基於深度學習的圖書管理推薦系統(附python程式碼)

轻松学编程發表於2024-03-31

基於深度學習的圖書管理推薦系統

1、效果圖

demo點我跳轉

1/1 [==============================] - 0s 270ms/step
[13 11  4 19 16 18  8  6  9  0]
[0.1780757  0.17474999 0.17390694 0.17207369 0.17157653 0.16824844
 0.1668652  0.16665359 0.16656876 0.16519257]
keras_recommended_book_ids深度學習推薦列表 [9137, 10548, 1, 10546, 2, 1024, 10, 10550, 7, 512]

2、演算法原理

​ 使用Keras框架實現一個簡單的深度學習推薦演算法。Keras是建立在Python之上的高階神經網路API。Keras提供了一種簡單、快速的方式來構建和訓練深度學習模型。

​ 根據使用者對書籍的評分表,使用Emmbeding深度學習訓練得到一個模型,預測使用者可能評分高的書籍,並把前5本推薦給使用者。

Emmbeding是從離散物件(如書籍 ID)到連續值向量的對映。
這可用於查詢離散物件之間的相似性。
Emmbeding向量是低維的,並在訓練網路時得到更新。
設計一個模型,將使用者id作為使用者向量,物品id作為物品向量。
分別Emmbeding兩個向量,再Concat連線起來,最後加上3個全連線層構成模型,進行訓練。
使用adam最佳化器,用均方差mse來衡量預測評分與真實評分之間的誤差

流程圖:

3、演算法流程

1、從資料庫中讀取評分表資訊並轉成二維陣列
2、資料預處理,把使用者id,物品id對映成順序字典
3、統計使用者數量、物品數量
4、劃分訓練集與測試集
5、構建Embedding模型並進行資料訓練得到模型
6、呼叫模型預測評分高的物品並推薦給使用者

4、主體程式碼

# -*- coding: utf-8 -*-

"""
@contact: 微信 1257309054
@file: recommend_keras.py
@time: 2024/3/30 16:21
@author: LDC
使用Keras框架實現一個深度學習推薦演算法
"""

import os
import django
from django.conf import settings

os.environ["DJANGO_SETTINGS_MODULE"] = "book_manager.settings"
django.setup()

import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import pymysql
from sklearn.model_selection import train_test_split
import warnings

warnings.filterwarnings('ignore')

from book.models import UserSelectTypes, LikeRecommendBook, Book, RateBook
from keras.layers import Input, Embedding, Flatten, Dot, Dense, Concatenate, Dropout
from keras.models import Model

from keras.models import load_model


def get_select_tag_book(user_id, book_id=None):
    # 獲取使用者註冊時選擇的書籍類別各返回10門書籍
    category_ids = []
    us = UserSelectTypes.objects.get(user_id=user_id)
    for category in us.category.all():
        category_ids.append(category.id)
    unlike_book_ids = [d['book_id'] for d in
                       LikeRecommendBook.objects.filter(user_id=user_id, is_like=0).values('book_id')]
    if book_id and book_id not in unlike_book_ids:
        unlike_book_ids.append(book_id)
    book_list = Book.objects.filter(tags__in=category_ids).exclude(id__in=unlike_book_ids).distinct().order_by(
        "-like_num")[:10]
    return book_list


def get_data():
    '''
    從資料庫獲取資料
    '''
    conn = pymysql.connect(host=settings.DATABASE_HOST,
                           user=settings.DATABASE_USER,
                           password=settings.DATABASE_PASS,
                           database=settings.DATABASE_NAME,
                           charset='utf8mb4',
                           use_unicode=True)

    sql_cmd = 'SELECT book_id, user_id,mark FROM rate_book'
    dataset = pd.read_sql(sql=sql_cmd, con=conn)
    conn.close()  # 使用完後記得關掉

    return dataset


def preprocessing(dataset):
    '''
    資料預處理
    '''
    book_val_counts = dataset.book_id.value_counts()
    book_map_dict = {}
    for i in range(len(book_val_counts)):
        book_map_dict[book_val_counts.index[i]] = i

    # print(map_dict)
    dataset["book_id"] = dataset["book_id"].map(book_map_dict)

    user_id_val_counts = dataset.user_id.value_counts()
    # 對映字典
    user_id_map_dict = {}
    for i in range(len(user_id_val_counts)):
        user_id_map_dict[user_id_val_counts.index[i]] = i
    # 將User_ID對映到一串字典
    dataset["user_id"] = dataset["user_id"].map(user_id_map_dict)

    return dataset, book_map_dict, user_id_map_dict


def train_model():
    '''
    訓練模型
    '''
    dataset = get_data()  # 獲取資料
    dataset, book_map_dict, user_id_map_dict = preprocessing(dataset)  # 資料預處理
    n_users = len(dataset.user_id.unique())  # 統計使用者數量
    print('n_users', n_users)

    n_books = len(dataset.book_id.unique())  # 統計書籍數量
    print('n_books', n_books)

    # 劃分訓練集與測試集
    train, test = train_test_split(dataset, test_size=0.2, random_state=42)
    # 開始訓練
    # creating book embedding path
    book_input = Input(shape=[1], name="Book-Input")
    book_embedding = Embedding(n_books + 1, 5, name="Book-Embedding")(book_input)
    Dropout(0.2)
    book_vec = Flatten(name="Flatten-Books")(book_embedding)

    # creating user embedding path
    user_input = Input(shape=[1], name="User-Input")
    user_embedding = Embedding(n_users + 1, 5, name="User-Embedding")(user_input)
    Dropout(0.2)
    user_vec = Flatten(name="Flatten-Users")(user_embedding)

    # concatenate features
    conc = Concatenate()([book_vec, user_vec])

    # add fully-connected-layers
    fc1 = Dense(128, activation='relu')(conc)
    Dropout(0.2)
    fc2 = Dense(32, activation='relu')(fc1)
    out = Dense(1)(fc2)

    # Create model and compile it
    model2 = Model([user_input, book_input], out)
    model2.compile('adam', 'mean_squared_error')

    history = model2.fit([train.user_id, train.book_id], train.mark, epochs=10, verbose=1)
    model2.save('regression_model2.h5')
    loss = history.history['loss']  # 訓練集損失
    # 顯示損失影像
    plt.plot(loss, 'r')
    plt.title('Training loss')
    plt.xlabel("Epochs")
    plt.ylabel("Loss")
    plt.show()
    print('訓練完成')


def predict(user_id, dataset):
    '''
    將預測評分高的圖書推薦給該使用者user_id
    '''

    model2 = load_model('regression_model2.h5')

    '''
    先拿到所有的圖書索引ISBN,並去重成為book_data。
    再新增一個和book_data長度相等的使用者列表user,不過這裡的user列表中的元素全是1,
    因為我們要做的是:預測第1個使用者對所有圖書的評分,再將預測評分高的圖書推薦給該使用者。
    '''
    book_data = np.array(list(set(dataset.book_id)))
    user = np.array([user_id for i in range(len(book_data))])
    predictions = model2.predict([user, book_data])
    # 更換列->行
    predictions = np.array([a[0] for a in predictions])
    # 根據原array,取其中數值從大到小的索引,再只取前top10
    recommended_book_ids = (-predictions).argsort()[:10]
    print(recommended_book_ids)
    print(predictions[recommended_book_ids])
    return recommended_book_ids


def embedding_main(user_id, book_id=None, is_rec_list=False):
    '''
    1、獲取資料、資料預處理
    2、劃分訓練集與測試集
    3、訓練模型、模型評估
    4、預測
    user_id: 使用者id
    book_id: 使用者已經評分過的書籍id,需要在推薦列表中去除
    is_rec_list: 值為True:返回推薦[使用者-評分]列表,值為False:返回推薦的書籍列表
    '''
    dataset = get_data()  # 獲取資料
    # print(dataset.head())
    if user_id not in dataset.user_id.unique():
        # 使用者未進行評分則推薦註冊時選擇的圖書型別
        print('使用者未進行評分則推薦註冊時選擇的圖書型別')
        if is_rec_list:
            return []
        # 推薦列表為空,按使用者註冊時選擇的書籍類別各返回10門
        return get_select_tag_book(user_id, book_id)
    dataset, book_map_dict, user_id_map_dict = preprocessing(dataset)
    # user_id需要轉換為對映後的user_id傳到predict函式中
    predict_book_ids = predict(user_id_map_dict[user_id], dataset)  # 預測的書籍Id
    recommend_list = []  # 最後推薦的書籍id
    # 把對映的值轉為真正的書籍id
    for book_id in predict_book_ids:
        for k, v in book_map_dict.items():
            if book_id == v:
                recommend_list.append(k)
    print('keras_recommended_book_ids深度學習推薦列表', recommend_list)

    if not recommend_list:
        # 推薦列表為空,且is_rec_list: 值為True:返回推薦[使用者-評分]列表
        if is_rec_list:
            return []
        # 推薦列表為空,按使用者註冊時選擇的書籍類別
        return get_select_tag_book(user_id, book_id)
    if is_rec_list:
        # 推薦列表不為空,且且is_rec_list: 值為True:返回推薦[使用者-評分]列表
        return recommend_list

    # 過濾掉使用者反饋過不喜歡的書籍
    unlike_book_ids = [d['book_id'] for d in
                       LikeRecommendBook.objects.filter(user_id=user_id, is_like=0).values('book_id')]

    # 過濾掉使用者已評分的資料
    already_mark_ids = [d['book_id'] for d in RateBook.objects.filter(user_id=user_id).values('book_id')]
    unrecommend = list(set(unlike_book_ids + already_mark_ids))
    if book_id and book_id not in unrecommend:
        unrecommend.append(book_id)
    book_list = Book.objects.filter(id__in=recommend_list).exclude(id__in=unrecommend).distinct().order_by("-like_num")
    return book_list


if __name__ == '__main__':
    train_model() # 訓練模型
    embedding_main(2) # 呼叫模型

輸出:

相關文章