概念
很多网站都使用推荐系统预测用户喜欢的内容。以电影资讯网站为例,假设电影有多个特征,那么根据用户对电影的打分,我们可以预测用户可能喜欢那些类型的电影,这就是基于内容的推荐系统。这种优化过程和线性回归类似。
但是,在网站建设初期,我们没有精力为所有的电影设定特征或分类,没有内容就无法进行推荐,这时需要使用协同过滤算法进行目标优化,同时优化电影特征和用户喜好。
使用协同过滤算法的流程为:
- 随机初始化特征值矩阵和权值矩阵(或者称用户偏好)。
- 使用梯度下降法优化损失函数。
- 使用偏好向量和特征向量预测用户评价。
当获得电影的特征向量后,可以通过计算差值来比较两个电影的相似度,从而用于推荐系统。
TensorFlow实现
首先从文件读入数据,其中Y数组包含用户从一到五的评分,R数组包含二进制值指示器,表明用户是否对电影进行了评分。
data = scio.loadmat('ex8_movies.mat')
Y = tf.Variable(data['Y'], dtype=np.float64)
R = tf.Variable(data['R'], dtype=np.float64)
带正则化的损失函数计算方法如下:
learning_rate = 1.5 # 自定义
error = tf.multiply((tf.matmul(X, tf.transpose(Theta))) - Y, R) # 计算误差
basic = (1. / 2) * tf.reduce_sum(tf.square(error))
# 正则化
regular_theta = ((learning_rate / 2) * tf.reduce_sum(tf.square(Theta)))
regular_x = ((learning_rate / 2) * tf.reduce_sum(tf.square(X)))
j = basic + regular_theta + regular_x # 最终的损失函数
接下来,我们需要创建自己的电影评分模型,用这个模型生成个性化建议。“movie_ids.txt”文件存有一些电影名称和序号。把文件加载到字典中,加入一些样本评分。
movie_idx = {}
f = open('movie_ids.txt', encoding='UTF-8', errors='ignore') # 加入“ignore”防止读取时报错
for line in f:
info = line.split(' ')
info[-1] = info[-1][:-1] # 去掉分离字符串时遗留在最后的“\n”
movie_idx[int(info[0]) - 1] = ' '.join(info[1:]) # 从0开始计数
ratings = np.zeros((1682, 1)) # 使用NumPy方便赋值
# 添加评分
ratings[0] = 4
ratings[6] = 3
ratings[11] = 5
ratings[53] = 4
ratings[63] = 5
ratings[65] = 3
ratings[68] = 5
ratings[97] = 2
ratings[182] = 4
ratings[225] = 5
ratings[354] = 5
然后在数据集中添加自定义的评分向量。这里需要注意的一点是,“ratings != 0”这一句会将ratings转换为布尔型向量,而TensorFlow的concat方法无法拼接不同类型的向量,需要进行类型转换,NumPy的append方法会将布尔型(True, Flase)转为数字(1,0),所以没有这个问题。代码中也写了相应的NumPy实现。
Y = tf.concat([Y, ratings], axis=1)
R = tf.concat([R, (ratings != 0).astype(float)], axis=1)
# R = np.append(R, ratings != 0, axis=1)
最后训练协同过滤模型。为方便计算,均值标准化方面使用NumPy实现。
Y_mean = np.zeros([movies, 1]) # 电影评分均值矩阵
Y_norm = np.zeros([movies, users]) # 电影均值标准化矩阵
for i in range(movies):
idx = np.where(R[i, :] == 1)[0] # 为电影i评分的用户index
Y_mean[i] = np.mean(Y[i, idx]) # 电影i所有用户评分均值
Y_norm[i, idx] = Y[i, idx] - Y_mean[i] # 均值标准化
# 随机初始化X和Theta
X = tf.Variable(tf.random_uniform([movies, features], minval=0, maxval=1, dtype=np.float64))
Theta = tf.Variable(tf.random_uniform([users, features], minval=0, maxval=1, dtype=np.float64))
loss = cost(X, Theta, Y_norm, R, learning_rate)
train = tf.train.GradientDescentOptimizer(learning_rate).minimize(loss)
参数X和Theta经过训练后,计算并输出最高评分前10组。 因为数据是随机的,所以结果不定,其中一次运行的结果如下:
[[6.8931457 ]
[6.55168005]
[6.54106849]
[6.5401813 ]
[6.44245844]
[6.31349962]
[6.31135961]
[6.28386337]
[6.2115253 ]
[6.20582227]]
参考资料: Python机器学习的练习八:异常检测和推荐系统