1. 從Google網頁排序到PageRank算法
(1)谷歌網頁怎么排序?
- 先對搜索關鍵詞進行分詞,如“技術社區”分詞為“技術”和“社區”;
- 根據建立的倒排索引返回同時包含分詞后結果的網頁;
- 將返回的網頁相關性(類似上篇文章所講的文本相似度)網頁,相關性越高排名越靠前
(2)怎么處理垃圾網頁?
那么問題來了,假如有某個垃圾網頁中雖然也包含大量的查詢詞,但卻並非滿足用戶需要的文檔,因此,頁面本身的重要性在網頁排序中也起着很重要的作用。
(3)如何度量網頁本身的重要性?
實際上互聯網上的每一篇HTML文檔除了包含文本、圖片、視頻等信息外,還包含了大量的鏈接關系,利用這些鏈接關系,能夠發現某些重要的網頁,其中網頁是節點,網頁間的鏈接關系是邊。
如上圖,某網頁1鏈向網頁2,則可以認為網頁1覺得網頁2有鏈接價值,是比較重要的網頁。某網頁被指向的次數越多,則它的重要性越高;越是重要的網頁,所鏈接的網頁的重要性也越高。
通過下圖我們可以更形象地看出鏈向網頁E的鏈接遠遠大於鏈向網頁C的鏈接,但是網頁C的重要性卻大於網頁E。這是因為網頁C被網頁B所鏈接,而網頁B有很高的重要性。
(4)PageRank核心思想
PageRank對網頁的排序可以獨立於用戶搜索進行。如果一個網頁被很多其它網頁所鏈接,說明它受到普遍的承認和信賴,那么它的排名就高。這就是 Page Rank 的核心思想。當然 Google 的 Page Rank 算法實際上要復雜得多。比如說,對來自不同網頁的鏈接對待不同,本身網頁排名高的鏈接更可靠,於是給這些鏈接予較大的權重。
通俗理解,我們可以將互聯網中的網頁理解成我們現實中的每個人,人與人之間的聯系就類似於網頁與網頁之間聯系,一般人的社交影響力是跟其人脈的廣度與人脈的質量有關,網頁也同理,其重要性也跟網頁的被鏈的數量與質量有關。
具體參考:PageRank算法講解;PageRank算法--從原理到實現
2 PageRank的python實現
2.1 需求
利用PageRank隨機瀏覽模型求如下圖個網頁的PageRank值。
即網頁之間的關系如下表格:
| 鏈接源ID | 鏈接目標 ID |
|---|---|
| 1 | 2,3,4,5, 7 |
| 2 | 1 |
| 3 | 1,2 |
| 4 | 2,3,5 |
| 5 | 1,3,4,6 |
| 6 | 1,5 |
| 7 | 5 |
2.2 Python實現
"""
Created on Sun Jan 8 23:41:29 2017
@author: whenif
"""
import numpy as np
import networkx as nx
import matplotlib.pyplot as plt
def getGm(A):
'''
功能:求狀態轉移概率矩陣Gm
@A:網頁鏈接圖的鄰接矩陣
'''
Gm = []
for i in range(len(A)):
cnt = 0
for j in range(len(A[i])):
if A[i][j] != 0:
cnt += 1
tran_prob = 1/cnt#轉移概率
Gm_tmp = []
for j in range(len(A[i])):
Gm_tmp.append(tran_prob*A[i][j])
Gm.append(Gm_tmp)
Gm = np.transpose(Gm)
return Gm
def getBaseLev(N):
'''
功能:計算網頁所獲得的基本級別(1-P)*e/n
@N:網頁總個數
'''
P = 0.85
e = np.ones(N)
R = [ [(1-P)*i*1/N] for i in e ]
return R
def getPR(P,Gm,R,PR):
'''
功能:獲取PR值
@P:加權系數,通常取 0.85 左右,按照超鏈接進行瀏覽的概率
@Gm:狀態轉移概率矩陣
@R:網頁所獲得的基本級別
@PR:每個網頁節點的PageRank值
'''
#狀態轉移概率矩陣Gm與PR值相乘矩陣相乘
Gm_PR = np.dot(Gm,PR)
#矩陣乘以常數P
P_Gm_PR = P*Gm_PR
#矩陣相加
new_PR = P_Gm_PR+R #PR=P*Gm'PR+(1-d)*e/n PageRank算法的核心
return new_PR
def res_vis(A,PR):
'''
將計算出來的值進行可視化展示
@A:網頁鏈接圖的鄰接矩陣
@PR:每個網頁節點最終的PageRank值
'''
#G=nx.Graph()構造的是無向圖, G=nx.DiGraph()構造的是有向圖
#初始化有向圖,節點數為7,edge(邊)被創造的隨機概率
all_edges = []
for i in range(7):
for j in range(len(A)):
if A[i][j]==1:
all_edges.append([i+1,j+1])
#(1)初始化有向圖
G = nx.DiGraph()
#(2)添加節點
G.add_nodes_from(range(1,len(A)))
#(3)添加有向邊
G.add_edges_from(all_edges)
#(4)添加PR值
pr = {}
for i in range(len(PR)):
pr[i+1] = PR[i][0]
# (5)畫圖
layout = nx.spring_layout(G)
plt.figure(1)
nx.draw(G, pos=layout, node_size=[x * 6000 for x in pr.values()],
node_color='m',with_labels=True)
plt.show()
def main():
#初始化參數
N = 7 #網頁個數
P = 0.85 #一個加權系數,通常取 0.85 左右,按照超鏈接進行瀏覽的概率
#網頁鏈接圖的鄰接矩陣,每一列表示一個網頁的出度
A = np.array([[0,1,1,0,1,1,0],
[1,0,1,1,0,0,0],
[1,0,0,1,1,0,0],
[1,0,0,0,1,0,0],
[1,0,0,1,0,1,1],
[0,0,0,0,1,0,0],
[1,0,0,0,0,0,0]])
A = np.transpose(A) #轉置
#初始化PR值為0
new_PR = []
for i in range(N):
new_PR.append([0])
count = 0#迭代計數器
while True:
PR = new_PR
R = getBaseLev(N)
Gm = getGm(A)
new_PR = getPR(P,Gm,R,PR)
count = count +1
print("第 %s 輪迭代" % count)
print(str(round(new_PR[0][0],5))
+"\t" + str(round(new_PR[1][0],5))
+ "\t" + str(round(new_PR[2][0],5))
+ "\t" + str(round(new_PR[3][0],5))
+ "\t" + str(round(new_PR[4][0],5))
+ "\t" + str(round(new_PR[5][0],5))
+ "\t" + str(round(new_PR[6][0],5)))
#設置迭代條件
if ( round(PR[0][0],5)==round(new_PR[0][0],5)
and round(PR[1][0],5)==round(new_PR[1][0],5)
and round(PR[2][0],5)==round(new_PR[2][0],5)
and round(PR[3][0],5)==round(new_PR[3][0],5)
and round(PR[4][0],5)==round(new_PR[4][0],5)
and round(PR[5][0],5)==round(new_PR[5][0],5)
and round(PR[6][0],5)==round(new_PR[6][0],5)):
break
print("-------------------")
print("PageRank值已計算完成")
res_vis(A,new_PR)
if __name__ == '__main__':
main()
2.3 結果與分析
(1)迭代結果
第 1 輪迭代 0.02143 0.02143 0.02143 0.02143 0.02143 0.02143 0.02143 第 2 輪迭代 0.06241 0.04025 0.0357 0.02963 0.05846 0.02598 0.02507 ...... 第 57 輪迭代 0.28026 0.15875 0.13887 0.10821 0.18418 0.06057 0.06907 第 58 輪迭代 0.28026 0.15875 0.13887 0.10821 0.18418 0.06057 0.06907 ------------------- PageRank值已計算完成
(2)可視化結果
其中圓圈編號表示網頁ID,圓圈大小表示PR值大小,連線表示網頁之間的關系,有帶黑色箭頭表示出度方向。
(3)結果匯總
| 名次 | PageRank值 | 網頁ID | 發出鏈接ID | 被鏈接ID |
|---|---|---|---|---|
| 1 | 0.28026 | 1 | 2,3,4,5,7 | 2,3,5,6 |
| 2 | 0.18418 | 5 | 1,3,4,6 | 1,4,6,7 |
| 3 | 0.15875 | 2 | 1 | 1,3,4 |
| 4 | 0.13887 | 3 | 1,2 | 1,4,5 |
| 5 | 0.10821 | 4 | 2,3,5 | 1,5 |
| 6 | 0.06907 | 7 | 5 | 1 |
| 7 | 0.06057 | 6 | 1,5 | 5 |
(4)結果分析
- 被鏈接個數越多其PageRank值越大,當被鏈接個數相同則發出鏈接個數越多其PageRank值越大;
- ID=1的頁面的PageRank值是0.28026,占據全體接近三分之一,成為了第1位。從可視化圖與結果匯總表格可以看出,因為ID=1頁面是鏈出鏈接和鏈入鏈接最多的頁面,也可以理解它是最受歡迎的頁面。
同時需要注意的是在PageRank值排在第3位的ID=2頁面,被3個鏈接所鏈接,而只有面向ID=1頁面發出一個鏈接,因此(面向ID=1頁面的)鏈接就得到ID=2的所有的PageRank值。
附另一個簡單的PageRank程序:
from numpy import *
a = array([[0,1,1,0],
[1,0,0,1],
[1,0,0,1],
[1,1,0,0]],dtype = float) #dtype指定為float
def graphMove(a): #構造轉移矩陣
c = zeros((a.shape),dtype = float)
for i in range(a.shape[0]):
for j in range(a.shape[1]):
c[i][j] = a[i][j] / (a[:, j].sum()) #完成初始化分配
return c
def pageRank(p,m,v): #計算pageRank值
#判斷pr矩陣是否收斂,(v == p*dot(m,v) + (1-p)*v).all()判斷前后的pr矩陣是否相等,若相等則停止循環
while((v == p*dot(m,v) + (1-p)*v).all()==False):
v = p*dot(m,v) + (1-p)*v
return v
if __name__=="__main__":
M = graphMove(a)
pr = array([float(1)/M.shape[0] for _ in range(M.shape[0])]) #pr值的初始化
p = 0.8 #引入瀏覽當前網頁的概率為p,假設p=0.8
print pageRank(p,M,pr) # 計算pr值
3 應用場景
在數據分析我們經常需要從用戶的角度思考問題,如用戶購買路徑,用戶之所以沒產生購買,那么到底是在哪個環節出現了問題?基於用戶還有許許多多的分析問題,如流失用戶分析、流失用戶預警、用戶信用度分析等。
從基於用戶的分析我們可以延伸到用戶與信息、用戶與商品、用戶與用戶之間的分析,當然這三點對號入座的便分別是BAT的基因所在,其中人與人之間的分析即是社交關系分析,這也是PageRank適合的領域之一。在不同行業的應用場景不用,如以下應用場景:
- 微信、微博等應用的社交網絡分析,可以實現基於用戶的相似度的內容推薦、可以挖掘用戶的價值、用戶的社交影響力等;電商如京東等可利用用戶關系,在一定程度上協助風險控制(抓刷單等)。
- 在電信行業中利用交往圈數據可以得到用戶的社交影響力,從而在一定程度上可以協助垃圾短信等的治理;
- 文獻重要性研究(引用與被引用)
- ......
后記
數據分析與挖掘很多都是從人出發,逐漸延伸到人與人,甚至是人、人與人在時間與空間上的表現,其中人與人之間的關系可以說是很重要的一環,所以個人覺得PageRank還是有挺大的應用性,在工作中也是深有體會。當然文中只是舉了簡單的例子並實現,代碼可優化的地方應該不少,望各路小伙伴一起交流一起進步。
鏈接:http://www.jianshu.com/p/5657910d7df6
