最小推荐系统: SimRank快速近似计算

北城余情 提交于 2020-08-16 07:33:48

上一篇中提到,SimRank的计算复杂度非常高,在实际部署中是大不可能计算其真实值的,也没有必要。对于其近似值的计算来说,有很多计算方法。

在实际中,由条目-用户关系构建消费关系图 G=(V,E) ,对应的顶点数量 n (条目数加用户数)。 由此可以构成一个 n\times n 邻接矩阵 A , 其中 A_{ii}=0 . 并可以计算出SimRank矩阵 S . 显然地,矩阵 S的对角线元素都是1,即 S_{ii}=1 . 我们把邻接矩阵 A按列进行归一化:

A_{i,j} =\begin{equation} \left\{              \begin{array}{lr}              \frac{1}{ \left| A_\left( j \right) \right|} &   &(i,j)\in E,  &\\              0&   &(i,j)\notin E.              \end{array} \right. \end{equation} , 其中 \left| A_\left( j \right) \right| = \sum_{i=1}^{n}{A_{i,j}},  j=1,...,n .

那么,按照SimRank的定义 S=\left( c\mathbf{A}^\mathsf{T} S \mathbf{A} \right)  \vee \mathbf{I} , 其中 \vee 表示按相同位置取大值,即 \left( A \vee B \right)_{i,j} :=max\left\{ A_{i,j}, B_{i,j} \right\} , c 是人工定义的权重常数. 该定义保证S_{ii}=1. 由于实际中 G 是无向图,有预设 S 是实对称阵,即 S_{ij}=S_{ji} . 同样地,也有 A 是实对称阵。 \mathbf{A}^\mathsf{T} S \mathbf{A} 总是可以对角化。

由 定义可知, \left( c\mathbf{A}^\mathsf{T} S \mathbf{A} \right)  \vee \mathbf{I}c\mathbf{A}^\mathsf{T} S \mathbf{A} 仅在对角线上有元素不同。因此总是存在对角线矩阵 D:=S-c\left(\mathbf{A}^\mathsf{T} S \mathbf{A} \right) , 这里我们可以称 D对角修正矩阵[1]. 也就有

S=c\left( \mathbf{A}^\mathsf{T} S \mathbf{A} \right) +D .

把上式的左边不断代入右边:

S=D+c \mathbf{A}^\mathsf{T} D \mathbf{A} +c^2 \mathbf{A}^{\mathsf{T}2} D \mathbf{A}^2 +\cdot\cdot\cdot (1)

也就是说,SimRank矩阵的计算问题可以转换为对角修正矩阵的计算问题。对应地,对于SimRank中的矩阵元素 s\left( u,v \right) , 有

\begin{equation}  \begin{split} s\left( u,v \right)  = &e_{u}^\mathsf{T} Se_v \\ =& e_{u}^\mathsf{T} De_v +c\left(  \mathbf{A}e_u \right)^\mathsf{T}D \mathbf{A}e_u +c^2\left(  \mathbf{A}^2e_u \right)^\mathsf{T}D \mathbf{A}^2e_u +\cdot\cdot\cdot \end{split}  \end{equation}

由于 c 是介于0到1之间的常量,所以上式是一个无穷收敛级数. 如果定义 s^{\left( T \right)}\left( u,v \right) 为上式加到第 T 项的值,也就是

\begin{equation}  \begin{split} s^{\left( T \right)}\left( u,v \right)  :=& e_{u}^\mathsf{T} De_v +c\left(  \mathbf{A}e_u \right)^\mathsf{T}D \mathbf{A}e_u  +\cdot\cdot\cdot\\ &+ c^{\left( T-1 \right)}\left(  \mathbf{A}^{\left( T-1 \right)}e_u \right)^\mathsf{T}D \mathbf{A}^{\left( T-1 \right)}e_u  \end{split}  \end{equation}

于是其误差范围:

0 \leq s\left( u,v \right)  - s^{\left( T \right)}\left( u,v \right) \leq \frac{c^T}{1-c} . (2)

这对迭代次数与对应的误差范围给出了明确的关系。

由于 c 是常量,如果定义 W=\sqrt{c}A , 则有

S=W^\mathsf{T} SW+D (3)

而根据定义, S= W^\mathsf{T} S W\vee \mathbf{I}=W^\mathsf{T} SW - diag\left( W^\mathsf{T} SW \right) + I[2], 所以 D = - diag\left( W^\mathsf{T} SW \right) + I .

到这里,对角修正矩阵 D 的计算还是跟 S 的计算差不多复杂度。

假定 D 是给定的, 对于已知的 W , S\left( D \right) 是下式的解:

S\left( D \right) = W^\mathsf{T} S\left( D \right) W +D (4)

上式是离散lyapunov方程,可以使用GMRES[3]进行近似求解。 对应地,定义线性算子F\left( D \right) = D+ diag\left( W^\mathsf{T} S\left( D \right) W \right) .

定义 vec\left( \cdot \right) 为矩阵拉直运算, v 为一个 长度为n的向量  \begin{equation}  \left[  \begin{array}{ccc}      v_{1}  \\      v_{2}  \\ \cdot \\      v_{n}   \end{array}  \right]          \end{equation} , D\left( v \right) =  \begin{equation}  \left[  \begin{array}{ccc}      v_{1} &0&\cdot &0   \\      0&v_{2} &\cdot &0  \\     \cdot &\cdot &\cdot &\cdot \\ 0&0 &\cdot &     v_{n}   \end{array}  \right]          \end{equation}

P 为算子使 Pv=vec\left( D\left( v \right) \right) , 那么[4]

\begin{equation}  \begin{split} F=& I + P^\mathsf{T}\left( W^\mathsf{T}\otimes W^\mathsf{T} \right)\left( I-W^\mathsf{T}\otimes W^\mathsf{T} \right)^{-1}P\\  &=  P^\mathsf{T} \left( I-W^\mathsf{T}\otimes W^\mathsf{T} \right)^{-1}P  \end{split}  \end{equation}

其中 \otimes 为Kronecker积. Pn^2\times n^2 单位矩阵的子矩阵. 矩阵 F 的条件数

\kappa \left( F \right) \le \frac{2\left( 1+c \right)}{\left( 1-c \right)^2} , 这说明 F\left( D \right) 的给定误差估计是存在的。

结合(1)式与(4)式:

S= \sum_{k=0}^{\infty}{\left( W^\mathsf{T}  \right)^k}DW^k =  \sum_{k=0}^{K}{\left( W^\mathsf{T}  \right)^k}DW^k +R_K = S_K +R_K . 根据(2)式, \| R_K\|_1 \le c^K . 这给定了迭代的误差范围。

D 的GMRES求解:

global W
def matvec(x, num_iter=10, threshold=1e-4):
    # approximates matvec for GMRES method
    d = x.copy()
    XS = scipy.sparse.csr_matrix(scipy.sparse.diags(x, 0))
    for _ in range(num_iter):
        XS = W.T.dot(XS).dot(W)
        cond = abs(XS.data) > threshold
        not_cond = np.logical_not(cond)
        XS.data[not_cond] = 0.0
        XS.eliminate_zeros()
        if XS.data.shape[0] == 0:
            break
        d = d + XS.diagonal()
    return d

def dSolver(y, matvec, call=my_callback):
    #GMRES method
    n = max(y.shape)
    print('Solving key linear system...')
    LA = scipy.sparse.linalg.LinearOperator((n, n), matvec=matvec, dtype=np.float64)
    y = y.astype(np.int)
    d, info = scipy.sparse.linalg.gmres(LA, y, x0=y, callback=call)
    print('Solving key linear system... Done')
    return d, info

得到 D 之后,对query=[x]求所有元素与之的SimRank估计值:

global w
def getSimrank(W, d, x, num_items=20):
    #get simrank of querys
    n = W.shape[0]
    D = scipy.sparse.spdiags(d, 0, n, n)
    s = D.dot(x)
    for i in range(num_items):
        wx = W.dot(x)
        dwx = D.dot(wx)
        for k in range(i + 1):
            wtdwx = W.T.dot(dwx)
            dwx = wtdwx
        s = s + dwx
        x = wx
    return s

以上方法把SimRank矩阵的计算复杂度从O\left( n^4 \right)削减到 O\left( n^2 \right) . 其中预计算的 D 阵估计 计算复杂度为 O\left( n \right) . 同时内存开销也得到大幅削减。

参考

  1. ^Mitsuru Kusumoto, Takanori Maehara, & Ken-ichi Kawarabayashi. (2014). Scalable similarity search for SimRank. ACM.
  2. ^Yu, W. , Lin, X. , Zhang, W. , Pei, J. , & Mccann, J. A. . (2019). Simrank*: effective and scalable pairwise similarity search based on graph topology. Vldb Journal, 28(3), 401-426.
  3. ^Saad, Y. , & Schultz, M. H. . (2006). Gmres: a generalized minimal residual algorithm for solving nonsymmetric linear systems.
  4. ^Li, C., Han, J., He, G., Jin, X., Sun, Y., Yu, Y., & Wu, T. (2010). Fast computation of SimRank for static and dynamic information networks. extending database technology.
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!