每日学习记录 2019-10-28

2019-10-28  本文已影响0人  hwang_zhic

1. 添加相似度后的矩阵更新方式研究

1.1 baseline

baseline 模型是 RSVD,也就是 SVD 矩阵分解添加正则化的损失函数如下:
\begin{aligned} L=& \min _{U, V} \frac{1}{2} \sum_{i=1}^{m} \sum_{j=1}^{n} I_{i j}\left(r_{i j}-\mathbf{u}_{i}^{T} \mathbf{v}_{j}\right)^{2} \\ &+\frac{\lambda_{1}}{2}\|U\|_{F}^{2}+\frac{\lambda_{2}}{2}\|V\|_{F}^{2} \end{aligned}
矩阵更新公式如下:
\begin{array}{l}{\mathbf{u}_{i} \leftarrow \mathbf{u}_{i}+\gamma_{1}\left(\Delta_{i j} \mathbf{v}_{j}-\lambda_{1} \mathbf{u}_{i}\right)} \\ {\mathbf{v}_{j} \leftarrow \mathbf{v}_{j}+\gamma_{2}\left(\Delta_{i j} \mathbf{u}_{i}-\lambda_{2} \mathbf{v}_{j}\right)}\end{array}

\Delta_{i j}=r_{i j}-\mathbf{u}_{i}^{T} \mathbf{v}_{j}

1.2 添加相似度后的模型

添加相似度后的模型的损失函数:
\begin{aligned} L=& \min _{U, V} \frac{1}{2} \sum_{i=1}^{m} \sum_{j=1}^{n} I_{i j}\left(r_{i j}-\mathbf{u}_{i}^{T} \mathbf{v}_{j}\right)^{2} \\ &+\frac{\alpha}{2} \sum_{i=1}^{m} \sum_{f \in \mathcal{F}+(i)} s_{i f}\left\|\mathbf{u}_{i}-\mathbf{u}_{f}\right\|_{F}^{2} \\ &+\frac{\lambda_{1}}{2}\|U\|_{F}^{2}+\frac{\lambda_{2}}{2}\|V\|_{F}^{2} \end{aligned}
矩阵更新公式如下:
\begin{aligned} \mathbf{u}_{i} \leftarrow \mathbf{u}_{i}+\gamma_{1}\left(\Delta_{i j} \mathbf{v}_{j}-\alpha\right.& \sum_{f \in \mathcal{F}+(i)} s_{if}\left(\mathbf{u}_{i}-\mathbf{u}_{f}\right) \\\left.-\alpha \sum_{g \in \mathcal{F}^{-}(i)} s_{i g}\left(\mathbf{u}_{i}-\mathbf{u}_{g}\right)-\lambda_{1} \mathbf{u}_{i}\right) &\end{aligned}

\mathbf{v}_{j} \quad \leftarrow \quad \mathbf{v}_{j}+\gamma_{2}\left(\Delta_{i j} \mathbf{u}_{i}-\lambda_{2} \mathbf{v}_{j}\right)

\Delta_{i j}=r_{i j}-\mathbf{u}_{i}^{T} \mathbf{v}_{j}

1.3 代码分析

1.3.1 ALS 库的选择

目前能使用的 ALS 算法的 Python 工具库有两个:

  1. Spark.mllib
  2. implicit

那么先来分析一下 Spark.mllib 库

1.3.1.1 Spark.mllib

1.3.1.2 implicit

1.3.2 具体代码分析

由于 Spark.mllib 的源码无法修改,所以不分析了

下面是 implicit 库中的 _calculate_loss() 函数:

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 = <floating *> 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 相关知识

上一篇 下一篇

猜你喜欢

热点阅读