PageRank算法的思想


基於勝率矩陣的PageRank排序

 

  在做博弈模型評估的時候,遇到一個問題是如何評價多個模型的優劣。例如我有訓練好的三個圍棋模型A,B,C,兩兩之間對打之后有一個勝負關系,如何對這三個模型進行排序呢?通常對於人類選手這種水平有波動的情形,棋類比賽通常計算選手Elo得分按分值排序,足球籃球等通過聯賽積分或勝場進行排序,但對於固定不變的AI模型,我認為用類似PageRank的方式計算更方便也更加准確。

  這篇文章先從問題來源講起,再講解PageRank算法的思想,最后編程實現排序方法並指出一些需要注意的地方。

目錄

一、問題來源

  現在,深度強化學習更多的用在博弈模型的訓練當中,比如圍棋的AlphaZero,星際爭霸的AlphaStar,DOTA的OpenAI FIVE。比如我們已經訓練好了三個模型A,B,C,並且可以相互對打很多局,我們需要一個方法排出誰第一,誰第二。之前NeurIPS2019多智能體競賽設計的排序方法就存在明顯的bug,出現了A能勝過B,且A對C的勝率高於B對C的勝率,最后算出的排名卻是B更靠前。主辦方也承認了計算方式有缺陷並表示會在之后的比賽中修正,但是當前排名維持不變。

  那為什么成熟的Elo值計算方式沒有用在這類模型評估上面呢?Elo值通常用在圍棋、象棋等棋類排名上,電子競技例如英雄聯盟等也可以認為是類似Elo的積分方式。這類問題的特點是

  • 可通過一對一比賽得到一局的勝負關系,但和相同對手的對局次數有限,很難得到穩定的勝率關系。
  • 玩家水平並非固定不變,可隨環境、狀態等因素波動(臨場發揮),也可因長期訓練/荒廢而提升/下降(絕對實力)。

  我們需要根據這種1v1(or 5v5)的每一局的勝負關系,給出所有玩家的即時能力大小排序。由於每個人的水平都會因為身體因素、年齡因素等產生波動,這和一個固定的模型是不一樣的。而Elo可以根據每一局的實時對局結果立即更新當前排名,對棋類、競技體育等的時效性需求非常適合,也可以較為准確的反應玩家的當前水平排名。雖然它也不是絕對的准確,不過已經是針對這類需求很好的排序方法了。

  回過頭來,對於已經訓練好的AI模型,它的能力不會發生變化,並且我們可以通過足夠多的測試得到兩兩之間的准確勝率關系,這種情況下我們如果強行套Elo的算法一局一局挑選對手對打,更新Elo值,再挑對手對打,再更新Elo值,就會顯得沒有必要(因為我們並不關心每一局后的實時排名)而且很麻煩,再者如果中途有一個新加入的模型需要從0開始評估,要想得到較為穩定的排名關系就會顯得更加麻煩。

  而PageRank的方法可以充分利用模型之間容易得到的穩定勝負關系,用矩陣迭代的方式計算出最終排名,簡單且准確。

二、PageRank算法

算法思想

  PageRank算法是Google發明用來做網頁排序的,依據網頁之間的鏈接關系對網頁重要度進行排序。其主要設計思想如下

  • (1) 每個網頁的初始重要程度相同,比如a=1,b=1,c=1,...a=1,b=1,c=1,...
  • (2) 如果許多網頁b,c,d...b,c,d...指向某個網頁aa,則網頁aa很重要
  • (3) 如果某個重要的網頁aa指向某個網頁bb,則網頁bb因為aa很重要也會獲得更高的重要度。

  這個想法其實和paper的引用有相似之處,每一篇新paper剛發表,很難評價其質量,可以粗略認為paper質量都一樣;如果有一篇paper被引用很多,那么這篇paper肯定質量比較好;如果某偏很好的paper引用了另一篇paper,那這篇被引用的paper也理應質量不錯。

  基於這三點主要思想,我們假定有a,b,c,d四個網址,其鏈接關系如圖所示

  首先根據思想(1),假定每個網頁的初始重要度相同,比如都是1,則有重要度向量x=(1,1,1,1)x=(1,1,1,1)。

  接下來我們根據思想(2)、(3)計算每個網頁被指向后的重要度變化。令Ti,jTi,j表示網頁jj是否指向網頁ii,則有

 

Ti,j={1,if  ji0,otherwiseTi,j={1,if  j→i0,otherwise

 

  其中i,j{a,b,c,d}i,j∈{a,b,c,d},且自己指向自己記為0,即T(i,i)=0T(i,i)=0。重要度的變化如下計算xi=xj×Ti,jxi=∑xj×Ti,j,這種方式也很直覺,就是把所有指向ii的網頁的當前重要度加起來,就是網頁ii的重要度。由於剛開始大家的初始重要度都是1,則從圖中的指向關系可以算出

 

x(a)=1×Ta,a+1×Ta,b+1×Ta,c+1×Ta,d  =1×0+1×0+1×1+1×1  =2x′(a)=1×Ta,a+1×Ta,b+1×Ta,c+1×Ta,d  =1×0+1×0+1×1+1×1  =2

 

  同理有x(b)=1;x(c)=3;x(d)=2x′(b)=1;x′(c)=3;x′(d)=2。這里有一個問題需要注意,大家的初始權值都為1,但是發出去的權重卻大於1,例如網頁aa指向了b,c,db,c,d三個網頁,它發出去的權值為3,這是不太合理的。一個簡單的修正方式是,令Ti,jTi,j中同一個網頁發出去的鏈接的和為1,從而每個Ti,jTi,j還表示發出去的權值,而不僅僅表示有無。即有

 

Ti,j{1k{a,b,c,d}Tk,j,if   jk0,otherwiseTi,j←{1∑k∈{a,b,c,d}Tk,j,if ∃  j→k0,otherwise

 

  此時,我們有Ti,a=13;Ti,b=12;Ti,c=1;Ti,d=12Ti,a=13;Ti,b=12;Ti,c=1;Ti,d=12。重新計算每個網頁的重要度有

 

x(a)=1×Ta,a+1×Ta,b+1×Ta,c+1×Ta,d  =1×0+1×0+1×1+1×12  =32x′(a)=1×Ta,a+1×Ta,b+1×Ta,c+1×Ta,d  =1×0+1×0+1×1+1×12  =32

 

  同理有x(b)=13;x(c)=43;x(d)=56x′(b)=13;x′(c)=43;x′(d)=56。我們繼續將重要度向量xx進行第二次迭代計算,有

 

x(a)=x(a)×Ta,a+x(b)×Ta,b+x(c)×Ta,c+x(d)×Ta,d  =32×0+13×0+43×1+56×12  =74x′′(a)=x′(a)×Ta,a+x′(b)×Ta,b+x′(c)×Ta,c+x′(d)×Ta,d  =32×0+13×0+43×1+56×12  =74

 

  同理有x(b)=12;x(c)=1312;x(d)=23x′′(b)=12;x′′(c)=1312;x′′(d)=23。將計算表示為矩陣形式,我們有

 

x=⎛⎝⎜⎜⎜1111⎞⎠⎟⎟⎟,T=⎛⎝⎜⎜⎜⎜⎜01313130012121000120120⎞⎠⎟⎟⎟⎟⎟x=(1111),T=(00112130001312012131200)

 

  那么前兩次迭代可以表示為

 

x=Tx;x=Tx=T2xx′=Tx;x′′=Tx′=T2x

 

  經過無窮次迭代x=Txx∞=T∞x收斂,xx∞每個分量的大小即為對應網頁的重要度大小。實際情況中,不必作無限次運算即可收斂。

  接下來的問題是:對於任意這樣的矩陣,是否都會收斂呢?如何判斷當前矩陣是否具有這種收斂性?下一步給出比較直觀的理解和判斷方法,忽略證明過程。

數學原理

  如果我們把這個問題看作一個馬氏(隨機)過程,那么四個網頁組成的向量xx其實就是四個狀態。我們不取權值1,而是歸一化為x=(14,14,14,14)x=(14,14,14,14),那么xx可以看做是該馬氏過程的初始狀態概率分布。矩陣TT就是一步的狀態轉移概率矩陣。我們的目標則是求該轉移矩陣的平穩分布,這個平穩分布是與初始狀態分布xx無關的,也就是說無論xx的取值是多少,最后算出來的xx∞都一樣。那么現在的問題是什么樣的TT可以保證平穩分布存在且唯一。這里我們給出結論並簡單解釋,不作數學證明,可參考馬氏鏈平穩分布存在與唯一性的簡潔證明與計算

  定理: 若馬氏鏈不可約且正常返,則平穩分布存在且唯一。

  • 不可約:通俗來說,就是每個狀態都可以通過一步或者多步轉移到達任意另一個狀態。
  • 正常返:可以理解為每個狀態在有限步轉移后再回到自己的概率為1。

    如下圖所示例子

  從圖中可以看出,aa可以通過abcda→b→c→d到達dd,而dd無法轉移到aa等狀態,所以這個轉移矩陣不可約。同理,當aa轉移到dd等狀態時,就再也無法回到aa,則該轉移矩陣非正常返。這種情況下我們無法得到唯一的分布。

  例如我們分別取初始分布為

 

x1=⎛⎝⎜⎜⎜⎜⎜⎜⎜⎜0.10.20.30.10.10.2⎞⎠⎟⎟⎟⎟⎟⎟⎟⎟,x2=⎛⎝⎜⎜⎜⎜⎜⎜⎜⎜0000.30.20.5⎞⎠⎟⎟⎟⎟⎟⎟⎟⎟x1=(0.10.20.30.10.10.2),x2=(0000.30.20.5)

 

  狀態轉移矩陣為

 

T=⎛⎝⎜⎜⎜⎜⎜⎜⎜⎜0100000010000.5000.50000000.50.5000001000010⎞⎠⎟⎟⎟⎟⎟⎟⎟⎟T=(000.5000100000010000000.50000000.5010000.510)

 

  則有

 

x1=Tx1=⎛⎝⎜⎜⎜⎜⎜⎜⎜⎜00000.450.55⎞⎠⎟⎟⎟⎟⎟⎟⎟⎟,x2=Tx2=⎛⎝⎜⎜⎜⎜⎜⎜⎜⎜00000.350.65⎞⎠⎟⎟⎟⎟⎟⎟⎟⎟x1∞=T∞x1=(00000.450.55),x2∞=T∞x2=(00000.350.65)

 

  顯然x1x2x1∞≠x2∞。

  到這里我們說明了收斂性的問題,但其實真正在運用的時候還會遇到一些實際問題。下面,我們回到需要真正解決的AI模型排序的問題,用代碼實現算法,並解決一些運用中遇到的實際問題。

三、實例分析

  通過前述方式構建勝率矩陣,我們可以算得平穩分布,但還有一些實際問題需要微調算法。

對角線取值

  在之前的網頁排序里,對角線的元素被取為0,如果在勝率矩陣中也取為0,會出現錯誤的排序。假如勝率矩陣為

    a b c a 0 0.2 0.9 b 0.8 0 1 c 0.1 0 0 

  其中aa對bb的勝率為0.2,aa對cc的勝率為0.9;而bb對aa的勝率為0.8,bb對cc的勝率為1。可以很容易的看出bb是最厲害的,aa次之,cc最弱。但如果我們直接套前面的計算方式,有

import numpy as np

T = np.matrix([[0 ,0.2,0.9], [0.8, 0 , 1 ], [0.1, 0 , 0 ]]) for i in range(T.shape[0]): # 歸一化為狀態轉移概率矩陣 T[:,i] = T[:,i]/np.sum(T[:,i]) X = np.matrix([1/3,1/3,1/3]) # 初始分布 X = X.T print(T) print(T**2000*X) 

  得到

T: 
[[0. 1. 0.47368421] [0.88888889 0. 0.52631579] [0.11111111 0. 0. ]] X: [[0.48579545] [0.46022727] [0.05397727]] 

  可以發現aa居然比bb的分值高,這顯然是不合理的。出現這個問題的原因在於,在將勝率矩陣轉化為概率矩陣時,歸一化的操作改變了bb指向aa的權值,直接從0.2拉到了1,使得bb把所有自身的重要度都貢獻給了aa。一個合理的解決辦法是將對角線取為0.5,表示自己對自己的勝率是五五開。這種方式可以防止某個概率在歸一化的過程中被不合理的放縮。此時勝率矩陣為:

    a b c a 0.5 0.2 0.9 b 0.8 0.5 1 c 0.1 0 0.5 

  計算得到

T:
[[0.35714286 0.28571429 0.375 ] [0.57142857 0.71428571 0.41666667] [0.07142857 0. 0.20833333]] X: [[0.31038506] [0.66161027] [0.02800467]] 

  可以看到,這個結果是合理的。同時這種方式還可以防止某一列出現全為0的情形。

構造不可約且正常返

  通常我們需要考慮到各種勝負關系的情況,來保證平穩分布存在且唯一。假如勝率矩陣為

    a b c a 0.5 1 1 b 0 0.5 0.3 c 0 0.7 0.5 

  可以看出aa對bb和cc的勝率都為1;而cc對bb的勝率為0.7。可以很容易的看出排序應該為a,c,ba,c,b。但計算得到的結果為:

T:
[[1. 0.45454545 0.55555556] [0. 0.22727273 0.16666667] [0. 0.31818182 0.27777778]] X: [[1.] [0.] [0.]] 

  可以發現bb和cc的排序無法區分。出現這個問題的原因在於aa是一個吸收態,只有指入沒有指出。可以通過一個權值很小的均勻的轉移矩陣進行微調。取

 

E=⎛⎝⎜⎜131313131313131313⎞⎠⎟⎟E=(131313131313131313)

 

  其中權重參數α=0.001α=0.001,則修正后的矩陣表示為S=(1α)×T+α×ES=(1−α)×T+α×E。這里的TT是歸一化為概率矩陣的TT。此時有矩陣SS對每個狀態都至少有一個小的轉移概率,即不存在吸收態。同時可以注意到TT並不滿足不可約且正常返的條件,但TT存在平穩分布,這說明了之前的定理條件是充分條件,而非必要條件。可以留意一下這點。最終有

T = np.matrix([[0.5, 1 , 1 ],
               [ 0 ,0.5,0.3],
               [ 0 ,0.7,0.5]])

for i in range(T.shape[0]): # 歸一化為狀態轉移概率矩陣 T[:,i] = T[:,i]/np.sum(T[:,i]) E = np.matrix(np.ones_like(T))/T.shape[0] alpha = 1e-3 S = (1-alpha)*T+alpha*E X = np.matrix([1/3,1/3,1/3]) # 初始分布 X = X.T print(S) print(S**2000*X) 

  得到

S:
[[9.99333333e-01 4.54424242e-01 5.55333333e-01] [3.33333333e-04 2.27378788e-01 1.66833333e-01] [3.33333333e-04 3.18196970e-01 2.77833333e-01]] X: [[9.98694573e-01] [5.86177258e-04] [7.19249506e-04]] 

  此結果合理,且可以看出aa遠遠強於c,bc,b。

完整代碼及示例

  最終代碼封裝為函數:

def pagerank(T): assert type(T) == np.matrix, 'please use np.matrix' for i in range(T.shape[0]): T[:,i] = T[:,i]/np.sum(T[:,i]) E = np.matrix(np.ones_like(T))/T.shape[0] alpha = 1e-3 S = (1-alpha)*T+alpha*E X = np.matrix([1]*T.shape[0])/T.shape[0] X = X.T score = S**200*X return score 

  我們給一個不太好肉眼判斷的勝率關系如下:

    a b c a 0.5 0.6 0.3 b 0.4 0.5 0.6 c 0.7 0.4 0.5 

  這里三個模型出現了相互克制的情形,即
abeatsbbeatscbeatsaa⟶beatsb⟶beatsc⟶beatsa,帶入函數:

score:
matrix([[0.30789762], [0.34109655], [0.35100582]]) 

  可得排序關系c,b,ac,b,a。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM