推荐系统(第三课第二周)
文章目录
- 推荐系统(第三课第二周)
- 一、简单的推荐示例
- 二、协同过滤算法(标签是连续值)
- 三、协同过滤(标签为二元标签)
- 四、均值归一化
- 五、协同过滤的tensorflow实现
- (一)下面是使用tensorflow实现简单的的梯度下降功能的示例
- (二)使用tensorflow实现协同过滤
- 六、使用协同过滤来查找相关地物品
- (一)相似物品查找
- (二)、协同过滤的局限性
- 七、基于内容的过滤算法
- (一)协同过滤与基于内容的过滤算法之间的比较
- (二)基于内容的过滤算法
- (三)将基于内容的过滤算法应用到大型目录物品中去
- (四)基于内容的过滤算法的tensorflow实现
推荐系统(第三课第二周)
一、简单的推荐示例
下面是一个推荐电影的示例:
通过为每一位用户拟合最佳的参数w和b,再与想要预测的电影进行计算,得到该用户对该电影的预测评分。
通过用户对一组电影的评分数据,学习出用户的参数w和b,成本函数如下:
上述公式中求和时,对象是用户已经评分了的数据,后面加入了正则化项,简化上面的成本函数,就得到了下面的公式:
我们构建上面的推荐算法模型是基于我们拥有一些有关电影的数据,那当我们无法获取到有关电影的信息时,我们又应该如何做呢?
二、协同过滤算法(标签是连续值)
当我们已知每个用户的参数w和b,以及用户的评分数据,未知电影的相关特征,稍后会说明这些参数是如何得到的,通过这些数据,我们可以进行反推,推断出电影的特征值,示例如下:
反推的成本函数如下:
将上述我们讲到的两种成本函数进行合并,就会得到关于(w,b,x)的一个汇总成本函数,这个成本函数表示,我们现有的数据使只用用户的部分评分数据,电影的特征以及每个用户的参数我们一律不知,成本函数示例如下:
计算成本函数的示例代码如下:
# GRADED FUNCTION: cofi_cost_func
# UNQ_C1def cofi_cost_func(X, W, b, Y, R, lambda_):"""Returns the cost for the content-based filteringArgs:X (ndarray (num_movies,num_features)): matrix of item featuresW (ndarray (num_users,num_features)) : matrix of user parametersb (ndarray (1, num_users) : vector of user parametersY (ndarray (num_movies,num_users) : matrix of user ratings of moviesR (ndarray (num_movies,num_users) : matrix, where R(i, j) = 1 if the i-th movies was rated by the j-th userlambda_ (float): regularization parameterReturns:J (float) : Cost"""nm, nu = Y.shapeJ = 0nor = 0### START CODE HERE ### for j in range(nu):for i in range(nm):if(R[i][j]==1):J+=((np.dot(W[j],X[i])+b[0,j])-Y[i][j])**2# for k in range(W.shape[1]):# nor += W[j][k]**2+X[i][k]**2# J+=nor*lambda_J += lambda_*(np.sum(np.square(W))+np.sum(np.square(X)))J = J/2### END CODE HERE ### return J
计算成本函数使用矢量化版本:
def cofi_cost_func_v(X, W, b, Y, R, lambda_):"""Returns the cost for the content-based filteringVectorized for speed. Uses tensorflow operations to be compatible with custom training loop.Args:X (ndarray (num_movies,num_features)): matrix of item featuresW (ndarray (num_users,num_features)) : matrix of user parametersb (ndarray (1, num_users) : vector of user parametersY (ndarray (num_movies,num_users) : matrix of user ratings of moviesR (ndarray (num_movies,num_users) : matrix, where R(i, j) = 1 if the i-th movies was rated by the j-th userlambda_ (float): regularization parameterReturns:J (float) : Cost"""j = (tf.linalg.matmul(X, tf.transpose(W)) + b - Y)*RJ = 0.5 * tf.reduce_sum(j**2) + (lambda_/2) * (tf.reduce_sum(X**2) + tf.reduce_sum(W**2))return J
对上述成本函数实施梯度下降算法:
现在算法的参数变成了w,b,x;所以在进行梯度下降时,需要对这几个参数都要实施梯度下降。这个算法就叫做协同过滤算法。即多个用户对同一部电影进行了评分,这种协同反馈让我们去了解这部电影的特点,从而推测出该电影的特征,同样可以预测出其他用户对该电影的评分。
三、协同过滤(标签为二元标签)
将上述连续的评分数据标签修改为二元标签,具体数据如下:0表示不喜欢或者看到之后没有进行互动;1表示喜欢或者看到之后进行了互动,?表示暂时没有展示给用户。
二元标签意味着我们要将预测的结果改为逻辑值,需要使用逻辑回归,而不是线性回归,成本函数如下:
成本函数与标准的逻辑回归的成本函数一致。
四、均值归一化
如上所示,当有用户之前没有对任何电影进行评分,上述协同过滤算法会将该用户的w和b参数都拟合为0,那么最终预测出来的分数也会是0,这肯定是不正确的,如何解决这个问题呢?对评分数据进行均值归一化。
均值归一化,就是计算每一个电影的评分均值,让原来的评分减去均值,得到归一化之后的均值,示例如下:
这样处理之后,需要注意的是,在预测评分时,我们需要在预测结果后面加上一个均值,就不会出现刚才说到的所有某一个用户在没有评分数据的情况下,所有的预测评分都是0,而是变成了均值,也就是说预测用户的评分从均值开始变化,结果变得更加可信,实验证明,这样处理也会加快算法的运行速度。
我们刚才是将每一行数据进行均值归一化,我们也可以对每一列数据进行均值归一化,同样也是一种合理的做法。如果有一个电影还没有评分,归一化列;如果有用户还没有评分,归一化行。
五、协同过滤的tensorflow实现
(一)下面是使用tensorflow实现简单的的梯度下降功能的示例
使用的是Auto Diff模块实现梯度下降
(二)使用tensorflow实现协同过滤
示例如下:
我们为什么不使用密集层来实现协同过滤呢:原因是它不能很好地融入到tensorflow地密集层或者其他标准地神经网络层类型中去。
示例代码如下:
iterations = 200
lambda_ = 1
for iter in range(iterations):# Use TensorFlow’s GradientTape# to record the operations used to compute the cost with tf.GradientTape() as tape:# Compute the cost (forward pass included in cost)cost_value = cofi_cost_func_v(X, W, b, Ynorm, R, lambda_)# Use the gradient tape to automatically retrieve# the gradients of the trainable variables with respect to the lossgrads = tape.gradient( cost_value, [X,W,b] )# Run one step of gradient descent by updating# the value of the variables to minimize the loss.optimizer.apply_gradients( zip(grads, [X,W,b]) ) # 使用计算出来的导数更新参数# Log periodically.if iter % 20 == 0:print(f"Training loss at iteration {iter}: {cost_value:0.1f}")
六、使用协同过滤来查找相关地物品
(一)相似物品查找
假设xi是第i个商品的特征,如何找到与商品i相似的其他商品呢,这时候我们就会通过计算商品i和其他商品(比如说商品k)它们特征的距离,从而来找出与商品i最相似的商品k:
(二)、协同过滤的局限性
1、在处理冷启动问题时表现不佳
具体表现在:当你有一个很少用户评分的新项目或者有评分商品很少的新用户,协同过滤推荐的结果就不是很准确。
2、没有自然的方式使用侧面信息,有关项目或用户的附加信息
具体表现在当你有一些有关项目或者用户的信息时,这些信息可能会对推荐有帮助(用户的地域信息),但是协同过滤却没有使用到这些有用的信息。
七、基于内容的过滤算法
(一)协同过滤与基于内容的过滤算法之间的比较
协同过滤:基于与您给出相似的用户的评分向您推荐项目。
基于内容的过滤算法:基于用户的特征和项目的特征向您推荐项目。
(二)基于内容的过滤算法
基于内容的过滤算法需要用户的特征和物品的特征(里面也包含了一些评分信息),示例如下:
基于内容的过滤算法就是用来匹配用户和电影:
我们需要从用户信息中学习到代表用户偏好的向量,从物品信息中学习到代表物品偏好特征的向量,最后计算它们之间的匹配度,两个向量必须具有相同的维度才能够进行点积计算。
如何计算得到这些向量呢?
我们可以使用神经网络来得到代表用户和商品的特征向量,最后通过两个向量的点积或者是计算逻辑值得到最终某一用户对某一物品的预测值,示例如下:
将上述过程融合在一起,示例如下:
我们可以使用上述成本函数来拟合神经网络的最佳参数,从而使预测值与真实值更加近似。
通过上述模型,我们也可以用来查找相似的物品(也是通过计算代表两个物品的特征向量之间的距离):
计算平方距离的示例代码如下:
# GRADED_FUNCTION: sq_dist
# UNQ_C2
def sq_dist(a,b):"""Returns the squared distance between two vectorsArgs:a (ndarray (n,)): vector with n featuresb (ndarray (n,)): vector with n featuresReturns:d (float) : distance"""### START CODE HERE ### d = 0d = np.sum(np.square(a-b))### END CODE HERE ### return (d)
单独将关于物品的神经网络拿出来建立模型,用于得到每个物品的特征向量:
input_item_m = tf.keras.layers.Input(shape=(num_item_features,)) # input layer
vm_m = item_NN(input_item_m) # use the trained item_NN
vm_m = Lambda(lambda x: tf.nn.l2_normalize(x, axis=1))(vm_m) # incorporate normalization as was done in the original model
model_m = Model(input_item_m, vm_m)
model_m.summary()
对物品进行预测,得到物品的特征向量:
scaled_item_vecs = scalerItem.transform(item_vecs)
vms = model_m.predict(scaled_item_vecs[:,i_s:])
print(f"size of all predicted movie feature vectors: {vms.shape}")
(三)将基于内容的过滤算法应用到大型目录物品中去
在(二)中讲述的模型对于运行大型目录的物品非常耗费时间和资源,特别是在训练神经网络上,所以我们需要对模型进行优化,让模型能够应用到更加大型的目录的物品上去。
大部分模型为了解决这个问题,会把任务分成两部分:1、检索:从物品列表中找出与该用户最近看过的最相似的物品列表或者再补充一些其他与用户喜好相关的物品作为备选,并把它们加入列表,去除已经看过或者不感兴趣的;
2、排序:将用户特征向量和物品特征向量输入到神经网络中去,计算预测评分,进行排序,推荐前top_n;
这两个步骤需要做的决策之一就是在检索时,我们应该选择多少个物品作为候选,检索的项目数量越多,效果越好,但是算法的速度就会变慢,我们可以离线做一些相关的实验,看看增加检索的项目数量对提高相关性有多大作用,我们需要做一些平衡。
(四)基于内容的过滤算法的tensorflow实现
构建神经网络代码如下:
# GRADED_CELL
# UNQ_C1num_outputs = 32
tf.random.set_seed(1)
user_NN = tf.keras.models.Sequential([### START CODE HERE ### tf.keras.layers.Dense(256,activation='relu'), tf.keras.layers.Dense(128,activation='relu'), tf.keras.layers.Dense(num_outputs), ### END CODE HERE ###
])item_NN = tf.keras.models.Sequential([### START CODE HERE ### tf.keras.layers.Dense(256,activation='relu'), tf.keras.layers.Dense(128,activation='relu'), tf.keras.layers.Dense(num_outputs), ### END CODE HERE ###
])# create the user input and point to the base network
from tensorflow.keras.layers import Lambda# 用户分支
input_user = tf.keras.layers.Input(shape=(num_user_features,))
vu = user_NN(input_user)
vu = Lambda(lambda x: tf.nn.l2_normalize(x, axis=1))(vu)# 物品分支
input_item = tf.keras.layers.Input(shape=(num_item_features,))
vm = item_NN(input_item)
vm = Lambda(lambda x: tf.nn.l2_normalize(x, axis=1))(vm)# compute the dot product of the two vectors vu and vm
output = tf.keras.layers.Dot(axes=1)([vu, vm])# specify the inputs and output of the model
model = Model([input_user, input_item], output)model.summary()
定义损失函数和优化器:
tf.random.set_seed(1)
cost_fn = tf.keras.losses.MeanSquaredError()
opt = keras.optimizers.Adam(learning_rate=0.01)
model.compile(optimizer=opt,loss=cost_fn)
训练模型:
tf.random.set_seed(1)
model.fit([user_train[:, u_s:], item_train[:, i_s:]], ynorm_train, epochs=30)
点赞收藏哟!!!