从零实现电影评分预测SGD算法:偏置b导数计算结果异常排查
偏置b导数计算函数的逻辑错误排查与修正
当前代码运行结果远偏离预期的核心问题,全部出在参数语义错配、计算范围错配上:
- 参数传入的内容和函数内部计算逻辑完全不匹配:测试调用时给
user_id传的是整数索引312、item_id传的是整数索引98,函数内部却直接把这两个整数当成用户偏置、物品偏置值,参与正则项的求和计算。仅正则项部分就会算出2*0.01*312=6.24的偏差,从计算起点就和预期逻辑不一致。 - 残差计算范围完全错误:函数内部直接把全量的U矩阵(943行2列)和全量V矩阵(2行1681列)做矩阵乘法,得到覆盖943*1681所有位置的全量预测矩阵,后续把全矩阵的残差全部纳入求和计算,相当于把几万条无关样本的误差都累加到当前单个样本的导数结果里,数值自然会远大于预期值。
- 导数的计算逻辑没有匹配目标方程的单样本求导规则:
带L2正则的矩阵分解损失,对单个用户偏置求导时,只需要计算当前位置样本的残差,不需要累加全量矩阵的内容。正确的求导逻辑为:对单个样本$(u,i,r_{ui})$,残差$e_{ui} = r_{ui} - \mu - b_u - b_i - u_u \cdot v_i$,损失对$b_u$的导数为 $2\alpha b_u - 2e_{ui}$,和原代码写的公式结构一致,但取数范围完全错了。
具体修正方案
首先要补全函数缺失的参数:原函数没有传入当前计算位置的用户偏置值、物品偏置值,导致只能拿整数索引凑计算,这是结果偏差的核心来源。其次要把全矩阵运算改为定位单样本位置的向量运算:
- 用传入的
user_id索引,从全量U矩阵中取出对应用户的隐向量,不要拿整数索引当隐向量用 - 用传入的
item_id索引,从全量V矩阵中取出对应物品的隐向量(因为V矩阵是2行1681列的结构,每一列对应一个物品的隐向量,取列的时候要注意维度匹配) - 对两个单样本的隐向量做点积,得到当前样本的隐因子预测值,不要做全矩阵的点积运算
- 代入导数公式计算即可,不要对残差做全量求和
修正后的函数参考实现:
def derivative_db(user_id, item_id, rating, U, V, mu, alpha, user_bias_val, item_bias_val): # 定位当前样本对应的隐向量 user_vec = U[user_id] item_vec = V[:, item_id] # 计算当前样本的隐因子预测值 uv_pred = np.dot(user_vec, item_vec) # 计算当前样本的预测残差 residual = rating - mu - user_bias_val - item_bias_val - uv_pred # 代入L2正则损失的偏导公式 return 2 * alpha * user_bias_val - 2 * residual
测试调用时,只需要传入312号用户对应的实际偏置值、98号物品对应的实际偏置值,替换掉原调用里直接传整数索引当偏置值的写法,即可得到符合预期的-0.931计算结果。
内容的提问来源于stack exchange,提问作者bruce
相关产品推荐
相关产品推荐

