1. 添加相似度后的矩阵更新方式研究
1.1 baseline
baseline 模型是 RSVD,也就是 SVD 矩阵分解添加正则化的损失函数如下:
矩阵更新公式如下:
1.2 添加相似度后的模型
添加相似度后的模型的损失函数:
矩阵更新公式如下:
1.3 代码分析
1.3.1 ALS 库的选择
目前能使用的 ALS 算法的 Python 工具库有两个:
- Spark.mllib
- implicit
那么先来分析一下 Spark.mllib 库
1.3.1.1 Spark.mllib
Spark.mllib
-
优点如下:
- 速度快
- 方便推荐
- 方便调参
- 方便输出不同指标的准度,如:MPA@K,P@K,NDCG@K,RMSE等
-
缺点如下:
- 目前算法接口只支持两个数据类型输入。分别是:spark.RDD 和 Dataframe
- 各个算法像一个黑盒子,只有少量参数可以调整,也许用起来简单,但很多时候并不能满足不同用户的使用需求
- 不方便调整内部的具体算法,如调整损失函数。
1.3.1.2 implicit
- implicit
- 优点如下:
- 速度快,使用 Cython 代码编写
- 比较方便推荐,有推荐接口
- 方便调参
- 缺点如下:
- 不方便调整内部的具体算法,如调整损失函数,需要一定的 Cython 基础
- 支持输出的准度指标较少,仅有 NDCG@K,P@K,MAP@K
- 源代码注释少,不方便二次修改
1.3.2 具体代码分析
由于 Spark.mllib 的源码无法修改,所以不分析了
下面是 implicit 库中的 _calculate_loss() 函数:
- 使用 Cython 语言编写,改写比较麻烦
- 源代码没有注释
- 每次调试只有在重新编译后才能使用,调试比较麻烦
- 代码中的具体分析在注释里面,目前还未解析完
def _calculate_loss(Cui, integral[:] indptr, integral[:] indices, float[:] data,
floating[:, :] X, floating[:, :] Y, float regularization,
int num_threads=0):
# Cui是评分矩阵
# indptr是每行的非零数据总数
# indices是每行的非零数据列数
# data是所有的非零数据
# 以上3个变量可以组成一个评分矩阵
# X是ALS的固定矩阵,用来求Y
# Y是ALS的固定矩阵,用来求X
# regularization是正则化项
# num_threads是迭代次数
# 转换数据类型
dtype = np.float64 if floating is double else np.float32
# 确定用户与项目数量
cdef int users = X.shape[0], N = X.shape[1], items = Y.shape[0], u, i, index, one = 1
# 暂定
cdef floating confidence, temp
cdef floating zero = 0.
# Y的平方
cdef floating[:, :] YtY = np.dot(np.transpose(Y), Y)
# 指针数据
cdef floating * r
# 初始化loss等变量
cdef double loss = 0, total_confidence = 0, item_norm = 0, user_norm = 0
with nogil, parallel(num_threads=num_threads):
r = malloc(sizeof(floating) * N)
try:
for u in prange(users, schedule='guided'):
# calculates (A.dot(Xu) - 2 * b).dot(Xu), without calculating A
temp = 1.0
symv("U", &N, &temp, &YtY[0, 0], &N, &X[u, 0], &one, &zero, r, &one)
# 计算每个用户
for index in range(indptr[u], indptr[u + 1]):
# i表示第几列
i = indices[index]
# confidence表示该用户的第i个数据
confidence = data[index]
if confidence > 0:
temp = -2 * confidence
else:
temp = 0
confidence = -1 * confidence
temp = temp + (confidence - 1) * dot(&N, &Y[i, 0], &one, &X[u, 0], &one)
axpy(&N, &temp, &Y[i, 0], &one, r, &one)
total_confidence += confidence
loss += confidence
loss += dot(&N, r, &one, &X[u, 0], &one)
user_norm += dot(&N, &X[u, 0], &one, &X[u, 0], &one)
for i in prange(items, schedule='guided'):
item_norm += dot(&N, &Y[i, 0], &one, &Y[i, 0], &one)
finally:
free(r)
loss += regularization * (item_norm + user_norm)
return loss / (total_confidence + Cui.shape[0] * Cui.shape[1] - Cui.nnz)
1.4 总结
想要添加用户相似度进 ALS 模型中的损失函数还需要不少时间
参考链接:Spark 相关知识