1. 程式人生 > >PersonalRank:一種基於圖的推薦演算法

PersonalRank:一種基於圖的推薦演算法

上面的二部圖表示user A對item a和c感興趣,B對a b c d都感興趣,C對c和d感興趣。本文假設每條邊代表的感興趣程度是一樣的。

現在我們要為user A推薦item,實際上就是計算A對所有item的感興趣程度。在personal rank演算法中不區分user節點和item節點,這樣一來問題就轉化成:對節點A來說,節點A B C a b c d的重要度各是多少。重要度用PR來表示。

初始賦予 $PR(A)=1, PR(B)=PR(C)=PR(a)=PR(b)=PR(c)=PR(d)=0$,即對於A來說,他自身的重要度為滿分,其他節點的重要度均為0。

然後開始在圖上游走。每次都是從PR不為0的節點開始遊走,往前走一步。繼續遊走的概率是$\alpha$,停留在當前節點的概率是$1-\alpha$。

第一次遊走, 從A節點以各自50%的概率走到了a和c,這樣a和c就分得了A的部分重要度,$PR(a)=PR(c)=\alpha*PR(A)*0.5$。最後$PR(A)$變為$1-\alpha$。第一次遊走結束後PR不為0的節點有A a c。

第二次遊走,分別從節點A a c開始,往前走一步。這樣節點a分得A $\frac{1}{2}*\alpha$的重要度,節點c分得A $\frac{1}{2}*\alpha$的重要度,節點A分得a $\frac{1}{2}*\alpha$的重要度,節點A分得c $\frac{1}{3}*\alpha$的重要度,節點B分得a $\frac{1}{2}*\alpha$的重要度,節點B分得c $\frac{1}{3}*\alpha$的重要度,節點C分得c $\frac{1}{3}*\alpha$的重要度。最後$PR(A)$要加上$1-\alpha$

經過以上推演,可以概括成公式:

\begin{equation}PR(j)=\left\{\begin{matrix}\alpha*\sum_{i\in{in(j)}}\frac{PR(i)}{|out(i)|}\ \ \ \ if(j\ne{u})\\(1-\alpha)+\alpha*\sum_{i\in{in(j)}}\frac{PR(i)}{|out(i)|} \ \ \ \ if(j=u)\end{matrix}\right.\label{pr}\end{equation}

$u$是待推薦的使用者節點。

personalrank.py

#coding=utf-8
__author__ = 'orisun'

import time


def PersonalRank(G, alpha, root, max_step):
    rank = dict()
    rank = {x:0 for x in G.keys()}
    rank[root] = 1
    #開始迭代
    begin=time.time()
    for k in range(max_step):
        tmp = {x:0 for x in G.keys()}
        #取節點i和它的出邊尾節點集合ri
        for i, ri in G.items():
            #取節點i的出邊的尾節點j以及邊E(i,j)的權重wij, 邊的權重都為1,歸一化之後就上1/len(ri)
            for j, wij in ri.items():
                #i是j的其中一條入邊的首節點,因此需要遍歷圖找到j的入邊的首節點,
                #這個遍歷過程就是此處的2層for迴圈,一次遍歷就是一次遊走
                tmp[j] += alpha * rank[i] / (1.0 * len(ri))
        #我們每次遊走都是從root節點出發,因此root節點的權重需要加上(1 - alpha)
        tmp[root] += (1 - alpha)
        rank = tmp
    end=time.time()
    print 'use time', end - begin

    li = sorted(rank.items(), cmp=lambda x, y: cmp(x[1], y[1]), reverse=True)
    for ele in li:
        print "%s:%.3f, \t"%(ele[0], ele[1]),
    print
    
    return rank


if __name__ == '__main__' :
    alpha = 0.8
    G = {'A' : {'a' : 1, 'c' : 1},
         'B' : {'a' : 1, 'b' : 1, 'c':1, 'd':1},
         'C' : {'c' : 1, 'd' : 1},
         'a' : {'A' : 1, 'B' : 1},
         'b' : {'B' : 1},
         'c' : {'A' : 1, 'B' : 1, 'C':1},
         'd' : {'B' : 1, 'C' : 1}}

    PersonalRank(G, alpha, 'b', 50)     #從'b'節點開始遊走

輸出:

use time 0.00107598304749
B:0.312, b:0.262, c:0.115, a:0.089, d:0.089, A:0.066, C:0.066,

注意這裡有一個現象,上述程式碼從節點b開始遊走,最終算得的PR重要度最高的不是b,而是B。

personalrank經過多次的迭代遊走,使得各節點的重要度趨於穩定,實際上我們根據狀態轉移矩陣,經過一次矩陣運算就可以直接得到系統的穩態。公式\eqref{pr}的矩陣表示形式為:

\begin{equation}r=(1-\alpha)r_0+\alpha{M^T}\label{mat}\end{equation}

其中$r$是個n維向量,每個元素代表一個節點的PR重要度,$r_0$也是個n維向量,第i個位置上是1,其餘元素均為0,我們就是要為第i個節點進行推薦。$M$是n階轉移矩陣:

\begin{equation}M_{ij}=\left\{\begin{matrix}\frac{1}{|out(i)|}\ \ \ \ if(j\in{out(i)})\\0 \ \ \ \ else \end{matrix}\right.\end{equation}

按照矩陣乘法把\eqref{mat}展開就可以得到\eqref{pr}。

由\eqref{mat}可以得到兩種變形:

\begin{equation}(I-\alpha{M^T})r=(1-\alpha)r_0\label{eq}\end{equation}

\begin{equation}r=(I-\alpha{M^T})^{-1}(1-\alpha)r_0\label{inv}\end{equation}

 利用\eqref{eq},解一次線性方程組就查以得到$r$,對$r$中的各元素降序排列取出前K個就得到了節點i的推薦列表。實踐中係數矩陣$(I-\alpha{M^T})$是一個高度稀疏的矩陣,解這種線性方程流行的方法是GMRES。另外在scipy中提供了多種稀疏矩陣的儲存方法:coo,lil,dia,dok,csr,csc等,各有各的優缺點,dok可以快速的按下標訪問元素,csr和csc適合做矩陣的加法、乘法運算,lil省記憶體且按下標訪問元素也很快。更多內容參見scipy中的稀疏矩陣

由於我們只關心$r$中各元素的相對大小,並不關心其真實值,所以\eqref{inv}可以寫為

\begin{equation}r=(I-\alpha{M^T})^{-1}r_0\end{equation}

矩陣$(I-\alpha{M^T})^{-1}$乘以$r_0$相當於是取出矩陣的第i列,因此為節點i進行推薦時對矩陣$(I-\alpha{M^T})^{-1}$的第i列排序即可,所以求出矩陣$(I-\alpha{M^T})$的逆就得到了所有節點的推薦結果。

pr_matrix.py

# coding=utf-8
__author__ = 'orisun'

import numpy as np
from numpy.linalg import solve
import time
from scipy.sparse.linalg import gmres, lgmres
from scipy.sparse import csr_matrix

if __name__ == '__main__':
    alpha = 0.8
    vertex = ['A', 'B', 'C', 'a', 'b', 'c', 'd']
    M = np.matrix([[0,        0,        0,        0.5,    	0,        0.5,      0],
                   [0,        0,        0,        0.25,     0.25,     0.25,     0.25],
                   [0,        0,        0,        0,        0,        0.5,      0.5],
                   [0.5,	  0.5,    	0,        0,        0,        0,        0],
                   [0,        1.0,    	0,        0,        0,        0,        0],
                   [0.333,    0.333,    0.333,    0,        0,        0,        0],
                   [0,        0.5,    	0.5,      0,        0,        0,        0]])
    # print np.eye(n) - alpha * M.T
    r0 = np.matrix([[0], [0], [0], [0], [1], [0], [0]])  # 從'b'節點開始遊走
    n = M.shape[0]

    # 直接解線性方程法
    A = np.eye(n) - alpha * M.T
    b = (1 - alpha) * r0
    begin = time.time()
    r = solve(A, b)
    end = time.time()
    print 'use time', end - begin
    rank = {}
    for j in xrange(n):
        rank[vertex[j]] = r[j]
    li = sorted(rank.items(), cmp=lambda x, y: cmp(x[1], y[1]), reverse=True)
    for ele in li:
        print "%s:%.3f, \t" % (ele[0], ele[1]),
    print

    # 採用CSR法對稀疏矩陣進行壓縮儲存,然後解線性方程
    A =np.eye(n) - alpha * M.T
    b = (1 - alpha) * r0
    data = list()
    row_ind = list()
    col_ind = list()
    for row in xrange(n):
        for col in xrange(n):
            if(A[row, col] != 0):
                data.append(A[row, col])
                row_ind.append(row)
                col_ind.append(col)
    AA = csr_matrix((data, (row_ind, col_ind)), shape=(n, n))
    begin = time.time()
    # 係數矩陣很稀疏時採用gmres方法求解。解方程的速度比上面快了許多
    r = gmres(AA, b, tol=1e-08, maxiter=1)[0]
    # r = lgmres(AA, (1 - alpha) * r0, tol=1e-08,maxiter=1)[0]  #lgmres用來克服gmres有時候不收斂的問題,會在更少的迭代次數內收斂
    end = time.time()
    print 'use time', end - begin
    rank = {}
    for j in xrange(n):
        rank[vertex[j]] = r[j]
    li = sorted(rank.items(), cmp=lambda x, y: cmp(x[1], y[1]), reverse=True)
    for ele in li:
        print "%s:%.3f, \t" % (ele[0], ele[1]),
    print

    # 求逆矩陣法。跟gmres解方程的速度相當
    A = np.eye(n) - alpha * M.T
    b = (1 - alpha) * r0
    begin = time.time()
    r = A.I * b
    end = time.time()
    print 'use time', end - begin
    rank = {}
    for j in xrange(n):
        rank[vertex[j]] = r[j, 0]
    li = sorted(rank.items(), cmp=lambda x, y: cmp(x[1], y[1]), reverse=True)
    for ele in li:
        print "%s:%.3f, \t" % (ele[0], ele[1]),
    print

    # 實際上可以一次性計算出從任意節點開始遊走的PersonalRank結果。從總體上看,這種方法是最快的
    A = np.eye(n) - alpha * M.T
    begin = time.time()
    D = A.I
    end = time.time()
    print 'use time', end - begin
    for j in xrange(n):
        print vertex[j] + "\t",
        score = {}
        total = 0.0  # 用於歸一化
        for i in xrange(n):
            score[vertex[i]] = D[i, j]
            total += D[i, j]
        li = sorted(score.items(), cmp=lambda x,
                    y: cmp(x[1], y[1]), reverse=True)
        for ele in li:
            print "%s:%.3f, \t" % (ele[0], ele[1] / total),
        print

輸出:

use time 7.60555267334e-05
B:0.312, b:0.262, c:0.115, d:0.089, a:0.089, C:0.066, A:0.066,
use time 0.000385999679565
B:0.312, b:0.262, c:0.115, a:0.089, d:0.089, A:0.066, C:0.066,
use time 0.000133991241455
B:0.312, b:0.262, c:0.115, d:0.089, a:0.089, C:0.066, A:0.066,
use time 0.000274181365967
A A:0.314, c:0.189, B:0.166, a:0.159, C:0.076, d:0.063, b:0.033,
B B:0.390, c:0.144, d:0.111, a:0.111, C:0.083, A:0.083, b:0.078,
C C:0.314, c:0.189, B:0.166, d:0.159, A:0.076, a:0.063, b:0.033,
a a:0.308, B:0.222, A:0.159, c:0.133, d:0.070, C:0.063, b:0.044,
b B:0.312, b:0.262, c:0.115, d:0.089, a:0.089, C:0.066, A:0.066,
c c:0.340, B:0.192, C:0.126, A:0.126, d:0.089, a:0.089, b:0.038,
d d:0.308, B:0.222, C:0.159, c:0.133, a:0.070, A:0.063, b:0.044,

相關推薦

PersonalRank基於推薦演算法

上面的二部圖表示user A對item a和c感興趣,B對a b c d都感興趣,C對c和d感興趣。本文假設每條邊代表的感興趣程度是一樣的。 現在我們要為user A推薦item,實際上就是計算A對所有item的感興趣程度。在personal rank演算法中不區分user節點和item節點,這樣一來問

Attention+基於關注關係與多使用者行為的推薦演算法

劉夢娟, 王巍, 李楊曦,等. AttentionRank~+:一種基於關注關係與多使用者行為的圖推薦演算法[J]. 計算機學報, 2017(3). 基於Random Walk的TSPR演算法 該演算法採用一階Markov-chain計算遊走 概 率。方

【原始碼】NSGA - II基於進化演算法的多目標優化函式

NSGA-II是一種著名的多目標優化演算法。 NSGA-II is a very famous multi-objective optimization algorithm. 相應的函式為nsga_2(pop,gen)。 The function is nsga_2(pop,g

SCAN基於密度的社團發現演算法

Update: spark版本的實現在這裡。 說明:該實現參照了SCAN作者的另一篇論文 : Zhao, W., Martha, V., & Xu, X. (2013, March). PSCAN: a parallel Structural

【問底】伍藝基於Rsync演算法的資料庫備份方案設計

根據容災備份系統對備份類別的要求程度,資料庫備份系統可以分為資料級備份和應用級備份。資料備份是指建立一個異地的資料備份系統,該系統是對原本地系統關鍵應用資料實時複製。當出現故障時,可由異地資料系統迅速恢復本地資料從而保證業務的連續性。應用級備份比資料備份層次更高,即在異地建

[置頂]搜尋引擎-提示詞推薦演算法

      搜尋引擎可以說目前所有網際網路應用裡技術含量最高的一種。儘管應用形式比較簡單:使用者輸入查詢詞,搜尋引擎返回搜尋結果。但是,搜尋引擎需要達到的目標:更全、更快、更準。如何讓搜尋結果更準確始終是搜尋引擎的一大難題。   公司最近在開發某行業的垂直搜尋引

NeuralTalk基於Python+numpy使用語句描述影象的多模態遞迴神經網路的例程

NeuralTalk工程的流程如下: The pipeline for the project looks as follows: 輸入資料使用Amazon Mechanical Turk收集的影象和5組語句描述的資料集。 The input is a dataset of im

OCTMAP基於八叉樹的高效概率三維對映框架

摘要 三維模型提供了空間的體積表示,這對於包括飛行機器人和裝有機械手的機器人在內的各種機器人應用非常重要。在本文中,我們提出了一個開源框架來生成體積3D環境模型。我們的對映方法基於八叉樹,使用概率佔用估計。它明確地表示不僅佔用的空間,而且自由和未知的區域。此外,我們提出一種八叉樹地圖壓縮方法,以保持

深度理解跳躍連結串列基於概率選擇的平衡樹

跳躍連結串列:一種基於概率選擇的平衡樹 作者:林子 時間:2014年9月宣告:歡迎指出錯誤,轉載不要去掉出處 跳躍連結串列簡介             二叉樹是一種常見的資料結構。它支援包

聚類演算法推薦元學習的方法

摘要:元學習是一種技術,其目的在於理解什麼型別的演算法解決什麼型別的問題。相比之下,聚類是基於物件的相似性把一個數據集劃分幾個簇,不需要物件類標籤的先驗知識。本文提出了基於無標籤物件特徵的提取,使用元學習推薦出聚類演算法。基於將要被計算的聚類問題的特徵以及不同聚類演算法的排

推薦演算法基於演算法隨機遊走

ItemRank @@@Random-walk computation of similarities between nodes of a graph,with application tp collaborative recommendations

Magnostics Image-based Search of Interesting Matrix Views for Guided Network Exploration(基於網絡信息矩陣像的網絡探索方法)

希望 組合 cad 區分 加權 rest 結果 xpl ati 網絡、關系等數據變成如圖的鄰接矩陣時(紅色代表兩個節點也就是人,之間有聯系),但是得到的矩陣會因為順序的問題而出現不同的排列方式,在第一種中會發現因為有聚集的塊狀區域而很容易地把數據分為兩個部分,然後根據數據的

推薦系統(基於協同過濾演算法開發離線推薦

什麼是離線推薦 所謂的離線推薦其實就是根據使用者產生的行為日誌,後臺設定一個離線統計演算法和離線推薦演算法的任務來對這些行為日誌進行週期性的統計,統計過後的結果資料為前臺或者實時分析提供資料的支撐。離線推薦要求實時性不高。 離線推薦演算法之協同過濾 協同過濾其實就是藉助大量已

基於二叉樹的int32排序演算法

      之前寫一個庫時用到了字典樹,可以很方便地進行歸類,姓名放進去的時候就會對前部分的字元逐個歸類,從而在全域性深搜的時候得到的字串便是按字母排序過的有序表。        於是我突發奇想——0000、0100、0001

機器視覺課內實驗攝像機標定演算法的程式設計實現

機器視覺課內實驗:一種攝像機標定演算法的程式設計實現 一實驗目的 掌握攝像機標定方法的原理,採用一種攝像機標定演算法,程式設計實現攝像機內部引數和外部引數的估計。 二.標定原理 攝像機標定是指建立攝像機影象畫素位置與場景點位置之間的關係,其途徑是根據攝像機

資料許可權設計——基於EntityFramework的資料許可權設計方案設計思路

 前言:“我們有一個訂單列表,希望能夠根據當前登陸的不同使用者看到不同型別的訂單資料”、“我們希望不同的使用者能看到不同時間段的掃描報表資料”、“我們系統需要不同使用者檢視不同的生產報表列”。諸如此類,最近經常收到專案上面的客戶提出的這種問題,即所謂的“資料許可權”,經過開會討論決定:在目前的開發框架上面搭建

聚類(clustering)無指導的學習演算法

       聚類是一種無監督的學習的結果,聚類的結果就是產生一組集合,集合中的物件與同集合中的物件彼此相似,與其他集合的物件相異。聚類演算法是推薦給初學者的演算法,因為該演算法不僅十分簡單,而且還足夠靈活以面對大多數問題都能給出合理的結果。        

g2o優化的C++框架

轉載自:http://blog.csdn.net/u012700322/article/details/52857244 原文發表於IEEE InternationalConference on Robotics and Automation (ICRA), Shan

K近鄰(k-Nearest Neighbor,KNN)演算法基於例項的學習方法

1. 基於例項的學習演算法 0x1:資料探勘的一些相關知識脈絡 本文是一篇介紹K近鄰資料探勘演算法的文章,而所謂資料探勘,就是討論如何在資料中尋找模式的一門學科。 其實人類的科學技術發展的歷史,就一直伴隨著資料探勘,人們一直在試圖中資料中尋找模式, 獵人在動物遷徙的行為中尋找模式 農夫在莊稼的生

分析比特幣網絡去中心化、點對點的網絡架構

比特幣 區塊鏈 比特幣采用了基於互聯網的點對點(P2P:peer-to-peer)分布式網絡架構。比特幣網絡可以認為是按照比特幣P2P協議運行的一系列節點的集合。本文來分析下比特幣網絡,了解它跟傳統中心化網絡的區別,以及比特幣網絡是如何發現相鄰節點的。中心化網絡為了更好的理解P2P網絡,我們先來看看傳