- 0. 前言
- 1. 推荐系统数据编码的必要性
- 2. 推荐系统数据编码
- 2.1 在推荐系统中对用户和电影进行编码
- 2.2 数据集介绍
- 2.3 用于推荐系统的编码策略
- 2.4 实现推荐系统编码模型
- 相关链接
在《自编码器详解》中,我们介绍了数据编码的必要性,同时以图像编码为例实现了自编码器 (AutoEncoder) 及其多种变体。推荐系统是利用客户和商品信息,根据用户的兴趣特点和购买行为,向用户推荐用户感兴趣的信息和商品。在本节中,我们将在电影评价相关的数据集中对用户和电影进行编码。
1. 推荐系统数据编码的必要性为了了解推荐系统中数据编码的必要性,我们考虑对用户进行电影推荐的应用场景。与文本分析类似,如果我们要对每部电影/用户进行独热编码,由于有成千上万部电影,则最终将为每部电影提供一个数千维的向量编码。
根据用户的观看习惯以较低的维度对用户进行编码,从而可以根据电影的相似性对电影进行分组,这将帮助我们挖掘用户更可能需要观看的电影。类似的思想也可以应用于电子商务推荐系统,以及向超市中的顾客推荐产品。
在本文中,我们主要考虑对用户进行电影推荐的应用场景,在此类数据库中可能有数以百万计的用户和有成千上万的电影,我们无法对数据进行独热编码。在这种情况下,数据编码便会派上用场。推荐系统编码中使用的最流行的技术之一是矩阵分解。接下来,我们将了解其工作原理并为用户和电影生成嵌入。
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
- 将点积后的输出展平并通过一个全连接层,然后连接到输出层,输出层使用线性激活函数,输出值的范围为 1 到 5,表示预测的用户对电影的评分
- 拟合模型
- 分别提取用户和电影的嵌入权重
- 可以通过计算用户感兴趣的电影与数据集中其他电影的相似度来找到与给定电影相似的电影
接下来,我们将实现在推荐系统中将用户和电影编码为嵌入向量。
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())
打印出的使用 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
(2) 将用户和电影转换为分类变量,创建了两个新变量:User2 和 Movies2,用于分类:
ratings['User2']=ratings['User'].astype('category')
ratings['Movies2']=ratings['Movies'].astype('category')
(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)}
(4) 将 ID 作为新列添加到原始表中:
ratings['Movies2'] = ratings.Movies.apply(lambda x: moviesid2idx[x]) ratings['User2'] = ratings.User.apply(lambda x: userid2idx[x]) print(ratings.head())
再次使用 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
(5) 为每个用户 ID 和电影 ID 定义嵌入:
n_users = ratings.User.nunique() n_movies = ratings.Movies.nunique()
在以上代码中,提取来数据集中的不同用户和不同电影的总数。接下来,定义函数 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)
接下来,为每个用户以及每个电影提取一个 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)
(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()
模型的简要架构信息输出如下:
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
__________________________________________________________________________________________________
(7) 拟合模型:
model.fit([ratings.User2,ratings.Movies2], ratings.rating,
epochs=50,
batch_size=128)
(8) 提取每个用户或电影的向量:
# Extracting user vectors model.get_weights()[0] # Extracting movie vectors model.get_weights()[1]
(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))))
根据以上代码,我们可以计算出与第 600 个位置的电影最相似的 ID:
89
查看电影 ID 列表,可以直观地看出,与第 600 个电影最相似的电影确实是第 89 个电影。
相关链接Keras深度学习实战(1)——神经网络基础与模型训练过程详解
Keras深度学习实战(2)——使用Keras构建神经网络
Keras深度学习实战(7)——卷积神经网络详解与实现
Keras深度学习实战(16)——自编码器详解



