• Keras深度学习实战——推荐系统数据编码


    0. 前言

    《自编码器详解》中,我们介绍了数据编码的必要性,同时以图像编码为例实现了自编码器 (AutoEncoder) 及其多种变体。推荐系统是利用客户和商品信息,根据用户的兴趣特点和购买行为,向用户推荐用户感兴趣的信息和商品。在本节中,我们将在电影评价相关的数据集中对用户和电影进行编码。

    1. 推荐系统数据编码的必要性

    为了了解推荐系统中数据编码的必要性,我们考虑对用户进行电影推荐的应用场景。与文本分析类似,如果我们要对每部电影/用户进行独热编码,由于有成千上万部电影,则最终将为每部电影提供一个数千维的向量编码。
    根据用户的观看习惯以较低的维度对用户进行编码,从而可以根据电影的相似性对电影进行分组,这将帮助我们挖掘用户更可能需要观看的电影。类似的思想也可以应用于电子商务推荐系统,以及向超市中的顾客推荐产品。

    2. 推荐系统数据编码

    在本文中,我们主要考虑对用户进行电影推荐的应用场景,在此类数据库中可能有数以百万计的用户和有成千上万的电影,我们无法对数据进行独热编码。在这种情况下,数据编码便会派上用场。推荐系统编码中使用的最流行的技术之一是矩阵分解。接下来,我们将了解其工作原理并为用户和电影生成嵌入。

    2.1 在推荐系统中对用户和电影进行编码

    对用户和电影进行编码的原理如下:在考虑用户对不同电影的偏好方面,如果两个用户喜欢的电影相似,则表示两个用户编码向量应该同样是相似的,按照同样的逻辑,如果两部电影相似,例如它们属于同一类型或具有类似的演员表,则表示两部电影应具有相似的编码向量。

    2.2 数据集介绍

    用于模型训练的数据集中包含用户信息以及他们对其观看过的电影评分信息,其中第一列表示用户编号,第二列表示电影编号,第三列表示用户对电影的评分,最后一列表示时间戳,下图中展示了下载完成的部分数据。

    数据集

    该数据集可从以下链接下载:https://pan.baidu.com/s/1yYQw6uuXVsj9PHsT68rx1w,提取码: ifjr

    2.3 用于推荐系统的编码策略

    在开始实现模型之前,我们首先介绍用于推荐系统的编码策略工作流程,以便根据用户观看的电影历史记录推荐新的电影:

    • 加载数据集,为用户和电影分配 ID
    • 将用户和电影转换为 32 维编码向量
    • 使用 Keras 中的函数式 API 计算电影和用户的 32 维向量的点积:
      • 如果有 1000000 个用户和 10000 个电影,则编码后的电影矩阵尺寸为 10000 × 32,而用户矩阵尺寸为 1000000 × 32 尺寸
      • 两者的点积的尺寸为 1000000 × 10000
    • 将点积后的输出展平并通过一个全连接层,然后连接到输出层,输出层使用线性激活函数,输出值的范围为 15,表示预测的用户对电影的评分
    • 拟合模型
    • 分别提取用户和电影的嵌入权重
    • 可以通过计算用户感兴趣的电影与数据集中其他电影的相似度来找到与给定电影相似的电影

    接下来,我们将实现在推荐系统中将用户和电影编码为嵌入向量。

    2.4 实现推荐系统编码模型

    (1) 首先,导入数据集与所需库:

    import numpy as np
    import pandas as pd
    from keras.layers import Input, Embedding, Dense, Dropout, merge, Flatten, dot
    from keras.models import Model
    from keras.optimizers import Adam
    
    column_names = ['User', 'Movies', 'rating', 'timestamp']
    ratings = pd.read_csv('u.data', sep='\t', names=column_names)
    
    print(ratings.head())
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    打印出的使用 head() 方法读取的数据集数据样本信息如下:

       User  Movies  rating  timestamp
    0   196     242       3  881250949
    1   186     302       3  891717742
    2    22     377       1  878887116
    3   244      51       2  880606923
    4   166     346       1  886397596
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    (2) 将用户和电影转换为分类变量,创建了两个新变量:User2Movies2,用于分类:

    ratings['User2']=ratings['User'].astype('category')
    ratings['Movies2']=ratings['Movies'].astype('category')
    
    • 1
    • 2

    (3) 为每个用户和电影分配唯一的 ID

    users = ratings.User.unique()
    movies = ratings.Movies.unique()
    
    print(len(users))
    print(len(movies))
    
    userid2idx = {o:i for i,o in enumerate(users)}
    moviesid2idx = {o:i for i,o in enumerate(movies)}
    idx2userid = {i:o for i,o in enumerate(users)}
    idx2moviesid = {i:o for i,o in enumerate(movies)}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    (4)ID 作为新列添加到原始表中:

    ratings['Movies2'] = ratings.Movies.apply(lambda x: moviesid2idx[x])
    ratings['User2'] = ratings.User.apply(lambda x: userid2idx[x])
    
    print(ratings.head())
    
    • 1
    • 2
    • 3
    • 4

    再次使用 head() 方法读取的添加新列后的数据集数据样本信息如下:

       User  Movies  rating  timestamp  User2  Movies2
    0   196     242       3  881250949      0        0
    1   186     302       3  891717742      1        1
    2    22     377       1  878887116      2        2
    3   244      51       2  880606923      3        3
    4   166     346       1  886397596      4        4
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    (5) 为每个用户 ID 和电影 ID 定义嵌入:

    n_users = ratings.User.nunique()
    n_movies = ratings.Movies.nunique()
    
    • 1
    • 2

    在以上代码中,提取来数据集中的不同用户和不同电影的总数。接下来,定义函数 embedding_input,该函数将一个 ID 作为输入并将其转换为一个嵌入向量,该向量的维数为 n_out,共有 n_in 个输入值:

    def embedding_input(name,n_in,n_out):
        inp = Input(shape=(1,),dtype='int64',name=name)
        return inp, Embedding(n_in,n_out,input_length=1)(inp)
    
    • 1
    • 2
    • 3

    接下来,为每个用户以及每个电影提取一个 32 维的编码向量。

    n_factors = 32
    user_in, u = embedding_input('user_in', n_users, n_factors)
    movie_in, a = embedding_input('article_in', n_movies, n_factors)
    
    • 1
    • 2
    • 3

    (6) 构建神经网络模型:

    import keras.backend as K
    def rmse(y_true,y_pred):
      score = K.sqrt(K.mean(K.pow(y_true - y_pred, 2)))
      return score
    
    x = dot([u,a], axes=1)
    x=Flatten()(x)
    x = Dense(500, activation='relu')(x)
    x = Dense(1)(x)
    model = Model([user_in, movie_in],x)
    adam = Adam(lr=0.01)
    model.compile(adam,loss='mse', metrics=[rmse])
    model.summary()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    模型的简要架构信息输出如下:

    Model: "functional_1"
    __________________________________________________________________________________________________
    Layer (type)                    Output Shape         Param #     Connected to                     
    ==================================================================================================
    user_in (InputLayer)            [(None, 1)]          0                                            
    __________________________________________________________________________________________________
    article_in (InputLayer)         [(None, 1)]          0                                            
    __________________________________________________________________________________________________
    embedding (Embedding)           (None, 1, 32)        30176       user_in[0][0]                    
    __________________________________________________________________________________________________
    embedding_1 (Embedding)         (None, 1, 32)        53824       article_in[0][0]                 
    __________________________________________________________________________________________________
    dot (Dot)                       (None, 32, 32)       0           embedding[0][0]                  
                                                                     embedding_1[0][0]                
    __________________________________________________________________________________________________
    flatten (Flatten)               (None, 1024)         0           dot[0][0]                        
    __________________________________________________________________________________________________
    dense (Dense)                   (None, 500)          512500      flatten[0][0]                    
    __________________________________________________________________________________________________
    dense_1 (Dense)                 (None, 1)            501         dense[0][0]                      
    ==================================================================================================
    Total params: 597,001
    Trainable params: 597,001
    Non-trainable params: 0
    __________________________________________________________________________________________________
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    (7) 拟合模型:

    model.fit([ratings.User2,ratings.Movies2], ratings.rating,
                epochs=50,
                batch_size=128)
    
    • 1
    • 2
    • 3

    (8) 提取每个用户或电影的向量:

    # Extracting user vectors
    model.get_weights()[0]
    # Extracting movie vectors
    model.get_weights()[1]
    
    • 1
    • 2
    • 3
    • 4

    (9) 最后,我们将验证相似的电影是否具有相似的嵌入。在计算嵌入之间的相似度时,我们通常使用余弦相似度进行度量。选择第 600 个电影,余弦相似度计算如下:

    from sklearn.metrics.pairwise import cosine_similarity
    print(np.argmax(cosine_similarity(model.get_weights()[1][600].reshape(1,-1),model.get_weights()[1][:600].reshape(600,32))))
    
    • 1
    • 2

    根据以上代码,我们可以计算出与第 600 个位置的电影最相似的 ID

    89
    
    • 1

    查看电影 ID 列表,可以直观地看出,与第 600 个电影最相似的电影确实是第 89 个电影。

    相关链接

    Keras深度学习实战(1)——神经网络基础与模型训练过程详解
    Keras深度学习实战(2)——使用Keras构建神经网络
    Keras深度学习实战(7)——卷积神经网络详解与实现
    Keras深度学习实战(16)——自编码器详解

  • 相关阅读:
    【国际化Intl】Flutter 国际化多语言实践
    微信小程序框架---详细教程
    手把手教程-自动抢单某知名外卖程序制作
    大二Web课程设计:HTML+CSS学校静态网页设计——南京师范大学泰州学院(11页)
    ajax封装:get封装方式、post封装方式
    LeetCode 241. 为运算表达式设计优先级(分治/记忆化递归/动态规划)
    快来了解一下5个超实用的WPS表格操作技巧!
    不是,有了这套IP地址管理开源系统谁还用Excel啊
    day49:QT day2,信号与槽、对话框
    使用Tensorboard碰到AttributeError: module ‘distutils‘ has no attribute ‘version‘
  • 原文地址:https://blog.csdn.net/LOVEmy134611/article/details/125941249