Google早已成爲全球最成功的互聯網搜索引擎,在Google出現以前,曾出現過許多通用或專業領域搜索引擎。Google最終能擊敗全部競爭對手,很大程度上是由於它解決了困擾前輩們的最大難題:對搜索結果按重要性排序。而解決這個問題的算法就是PageRank。絕不誇張的說,是PageRank算法成就了Google今天的地位。html
從本質上說,搜索引擎是一個資料檢索系統,搜索引擎擁有一個資料庫(具體到這裏就是互聯網頁面),用戶提交一個檢索條件(例如關鍵詞),搜索引擎返回符合查詢條件的資料列表。node
理論上檢索條件能夠很是複雜,爲了簡單起見,咱們不妨設檢索條件是一至多個以空格分隔的詞,而其表達的語義是同時含有這些詞的資料(等價於布爾代數的邏輯與)。例如,提交「littlehann 博客」,意思就是「給我既含有‘littlehann’又含有‘博客’詞語的頁面」,如下是Google對這條關鍵詞的搜索結果:python
固然,實際上如今的搜索引擎都是有分詞機制的,例如若是以「littlehann的博客」爲關鍵詞,搜索引擎會自動將其分解爲「littlehann 的 博客」三個詞,而「的」做爲中止詞(Stop Word)會被過濾掉。git
創建一個搜索引擎的核心問題就是如下幾個:github
1. 創建資料庫; 2. 創建一種數據結構,根據關鍵詞找到含有這個詞的頁面; 3. 將結果按照重要程度排序後呈現給用戶;
這個問題通常是經過一種叫爬蟲(Spider)的特殊程序實現的(專業領域搜索引擎例如某個學術會議的論文檢索系統可能直接從數據庫創建資料庫)。web
簡單來講,爬蟲就是從一個頁面出發(例如新浪首頁),經過HTTP協議通訊獲取這個頁面的全部內容,把這個頁面url和內容記錄下來(記錄到資料庫),而後分析頁面中的連接,再去分別獲取這些連接鏈向頁面的內容,記錄到資料庫後再分析這個頁面的連接。算法
上述過程不斷重複,就能夠將整個互聯網的頁面所有獲取下來(固然這是理想狀況,要求整個Web是一個強連通(Strongly Connected),而且全部頁面的robots協議容許爬蟲抓取頁面,爲了簡單,咱們仍然假設Web是一個強連通圖,且不考慮robots協議)。sql
抽象來看,能夠將資料庫看作一個巨大的key-value結構,key是頁面url,value是頁面內容。數據庫
這個問題是經過一種叫倒排索引(inverted index)的數據結構實現的。安全
抽象來講倒排索引也是一組key-value結構,key是關鍵詞,value是一個頁面編號集合(假設資料庫中每一個頁面有惟一編號),表示這些頁面含有這個關鍵詞。
搜索引擎獲取「littlehann 博客」查詢條件,將其分爲「littlehann」和「博客」兩個詞。
而後分別從倒排索引中找到「littlehann」所對應的集合,假設是{1, 3, 6, 8, 11, 15};
「博客」對應的集合是{1, 6, 10, 11, 12, 17, 20, 22},
將兩個集合作交運算(intersection),結果是{1, 6, 11}。即尋找同時出現了這2個詞的頁面。
最後,從資料庫中找出一、六、11對應的頁面返回給用戶就能夠了。
上面兩個問題解決後,咱們很天然會想到,Web頁面數量很是巨大,因此一個檢索的結果條目數量也很是多,例如上面「littlehann 博客」的檢索返回了上萬條條結果。用戶不可能從如此衆多的結果中一一查找對本身有用的信息。
因此,一個好的搜索引擎必須想辦法將「質量」較高的頁面排在前面。
其實直觀上也能夠感受出,在使用搜索引擎時,咱們並不太關心頁面是否夠全(上百萬的結果,全不全有什麼區別?並且實際上搜索引擎都是取top,並不會真的返回所有結果。),而很關心前一兩頁是否都是質量較高的頁面,是否能知足咱們的實際需求。
所以,對搜索結果按重要性合理的排序就成爲搜索引擎的最大核心問題。
1. 不評價 早期的搜索引擎直接按照某天然順序(例如時間順序或編號順序)返回結果。這在結果集比較少的狀況下還說得過去,可是一旦結果集變大,用戶叫苦連天,試想讓你從幾萬條質量良莠不齊的頁面中尋找須要的內容,簡直就是一場災難,這也註定這種方法不可能用於現代的通用搜索引擎。 2. 基於檢索詞的評價 後來,一些搜索引擎引入了基於檢索關鍵詞去評價搜索結構重要性的方法,實際上,這類方法如TF-IDF算法在現代搜索引擎中仍在使用。
早期一些搜索引擎基於相似的算法評價網頁重要性的。這種評價算法看似依據充分、實現直觀簡單,但卻很是容易受到一種叫「Term Spam」的攻擊。
其實從搜索引擎出現的那天起,spammer和搜索引擎反做弊的鬥法就沒有中止過。Spammer是這樣一羣人——試圖經過搜索引擎算法的漏洞來提升目標頁面(一般是一些廣告頁面、博彩或垃圾頁面)的重要性,使目標頁面在搜索結果中排名靠前。
如今假設Google單純使用關鍵詞佔比評價頁面重要性,而我想讓個人博客在搜索結果中排名更靠前(最好排第一)。
那麼我能夠這麼作:在頁面中加入一個隱藏的html元素(例如一個div),而後其內容是「littlehann」重複一萬次。這樣,搜索引擎在計算「littlehann 博客」的搜索結果時,個人博客關鍵詞佔比就會很是大(TF-IDF的公式決定了),從而作到排名靠前的效果。
更進一步,我甚至能夠干擾別的關鍵詞搜索結果,例如我知道如今歐洲盃很火熱,我就在我博客的隱藏div里加一萬個「歐洲盃」,當有用戶搜索歐洲盃時,個人博客就能出如今搜索結果較靠前的位置。這種行爲就叫作「Term Spam」。
早期搜索引擎深受這種做弊方法的困擾,加之基於關鍵詞的評價算法自己也不甚合理,所以常常是搜出一堆質量低下的結果,用戶體驗大大打了折扣。而Google正是在這種背景下,提出了PageRank算法,並申請了專利保護。此舉充分保護了當時相對弱小Google,也使得Google一舉成爲全球數一數二的搜索引擎。
Relevant Link:
http://blog.codinglabs.org/articles/intro-to-pagerank.html
1. 每個一個網頁自己具備必定的重要性,它的重要性是經過其餘網絡的連接到該網頁來評價的。其餘網頁連接到該網頁能夠形象地理解爲給這個網頁投票。 2. 一個網頁的連接會把該網頁的重要性傳遞到連接的網頁中,而一個網頁的重要性又必須經過連接它的網頁來肯定。這是一個互相依賴的遞歸過程。 3. 公平起見,一個網頁X若連接了m個網頁,那麼這m個網頁的每一個網頁接收到的來自網頁X的重要性是PR(X)/m。
PageRank算法的目標就是計算每個網頁的PageRank值,而後根據這個值的大小對網頁的重要性進行排序。
它的思想是模擬一個清閒的上網者,上網者首先隨機選擇一個網頁打開,而後在這個網頁上呆了幾分鐘後,跳轉到該網頁所指向的連接,這樣無所事事、漫無目的地在網頁上跳來跳去,PageRank就是估計這個清閒的上網者分佈在各個網頁上的機率。
在這個小節咱們以一個清閒上網者的視角來討論PageRank的算法過程,以便創建起一個感性的概念性認識,方便咱們記憶和攔截核心概念。
互聯網中的WWW網頁能夠看出是一個有向圖,其中網頁是結點。若是網頁A有連接到網頁B,則存在一條有向邊A->B。下面是一個簡單的示例:
這個例子中只有四個網頁。分別是A、B、C、D。這4個網頁分別擁有各自不一樣的「跳轉選擇選項」,清閒上網者在每一個網頁中,能夠往哪個網頁去進行下一跳,是由這個選項規定的。
若是當前在A網頁,那麼清閒的上網者將會各以1/3的機率跳轉到B、C、D,這裏的3表示A有3條出鏈。若是一個網頁有k條出鏈,那麼跳轉任意一個出鏈上的機率是1/k;
同理D到B、C的機率各爲1/2;
而B到C的機率爲0。
通常用轉移矩陣表示上網者的跳轉機率(注意,這個跳轉機率是在創建網絡圖的時候就肯定好的,後面不會再改變)。
若是用n表示網頁的數目,則轉移矩陣M是一個n*n的方陣(每個網頁均可能轉移到任意的網頁,包括它本身)。
若是網頁j 有 k 個出鏈,那麼對每個出鏈指向的網頁i,有M[i][j]=1/k(權重是等分的),而其餘網頁的M[i][j]=0(沒有出鏈就意味着不給那個網頁投票);
上面示例圖對應的轉移矩陣的轉置以下(注意,下面的矩陣是列向量的形式):
好了,如今咱們已經獲得了全部網頁的轉移矩陣,也即肯定了全部網頁各自的「跳轉選擇選項」。接下來要讓咱們的清閒上網者開始在網頁上不斷遊走,但願這個上網者經過不斷地遊走,給出一個最終的評估,對A、B、C、D這4個網頁的重要性權重給出一個數值結果。
根據最大熵原則,清閒上網者對這4個網頁的權重沒有任何先驗知識,因此假設每個網頁的機率都是相等的,即1/n。
因而初試的機率分佈就是一個全部值都爲1/n的n維列向量V0,用V0去右乘轉移矩陣M,就獲得了第一步以後上網者的機率分佈向量MV0。n x n)* (n x 1)依然獲得一個n x 1的矩陣。
M的第一行乘以 V0,表示累加全部網頁到網頁A的機率即獲得9/24;
M的第二行乘以 V0,表示累加全部網頁到網頁B的機率即獲得9/24;
M的第三行乘以 V0,表示累加全部網頁到網頁C的機率即獲得9/24;
M的第四行乘以 V0,表示累加全部網頁到網頁D的機率即獲得9/24;
這一輪結束後,上網者對各個網頁的權重值獲得了一次調整,從思想上很相似EM優化過程。
能夠把矩陣M和向量r相乘當作M的列以向量r爲權重進行線性組合,矩陣M同一列的不一樣行表明該節點向其餘節點的分發鏈接。
獲得了V1後,再用V1去右乘M獲得V2,一直下去,最終V會收斂,
即Vn=M * V(n-1)。
不斷的迭代,最終V = [3/9,2/9,2/9,2/9]'
這個[3/9,2/9,2/9,2/9]'就表明了上網者對這4個網頁權重的最終評價。顯然,這個權重評價是根據 M矩陣 的擬合而來的。
直觀上能夠這麼理解:這個清閒上網者看到轉移矩陣M,他在想,這個M矩陣就表明了當前整個網絡的拓樸結構,那麼這個拓樸結構背後必定隱含了某種規律,這個規律就是每一個網頁的權重。這個規則「支撐」着網絡成爲今天我看到的樣本。那我要努力去遊走,讓個人評價無限接近網絡背後的真實規律。恩,加油,我必定行的!
筆者思考:這種漸進收斂的思路,本質上體現了極大似然估計的思想,即從結果反推最有可能產生這個結果的模型參數。筆者建議讀者朋友翻出極大似然估計的書籍參照着學習,筆者也有一篇blog討論了極大似然估計的話題。
如今咱們從馬爾科夫過程的角度來看PageRank的訓練和收斂過程。關於markvo的討論,能夠參閱另外一篇blog。
假設咱們在上網的時候瀏覽頁面並選擇下一個頁面,這個過程與過去瀏覽過哪些頁面無關,而僅依賴於當前所在的頁面。這個假設前提符合馬爾科夫的有限狀態依賴假設。
咱們能夠把PageRank的這一選擇過程能夠認爲是一個有限狀態、離散時間的隨機過程,其狀態轉移規律可用Markov鏈描述。
在PageRank算法中,網頁拓樸間互相連接的鄰接矩陣,就對應了機率轉移矩陣。
能夠想象,在一個龐大的網絡中,鄰接矩陣是一個十分龐大有至關稀疏的方陣(用黑色表明1, 用白色表明0)。例以下圖:
矩陣中的的空行表明了沒有被其餘網頁連接過,可能表明是新網頁(例如新的新聞html頁面),或者是異常的惡意url。
定義矩陣G的「列和」與「行和」,在PageRank場景下,機率轉移矩陣的「行和」和「列和」是有明確含義的。
1. cj(列和) 是頁面j 的導出連接數目。也就是該頁面給其餘頁面的「投票」。固然,在PageRank中,列和是有明確約束的,即一個頁面能給其餘頁面投票的總權重和是1,不能超過1。 2. ri(行和) 是頁面 i 的導入連接數目。也就是該頁面收到的權重投票。
在討論馬爾科夫收斂問題前,咱們要對PageRank的迭代公式進行一個明肯定義。可是,在討論PageRank公式以前還要先討論兩個在實際中會遇到的問題:
,即Spider Traps問題(自循環節點),由於這個問題的存在,致使PageRank的迭代公式須要做出一些變形。
能夠預見,若是把真實的Web組織成轉移矩陣,那麼這將是一個極爲稀疏的矩陣。
從矩陣論知識能夠推斷,極度稀疏的轉移矩陣迭代相乘可能會使得向量v變得很是不平滑,即一些節點擁有很大的rank,而大多數節點rank值接近0。
而一種叫作Spider Traps節點的存在加重了這種不平滑。例以下圖:
D有外鏈因此不是Dead Ends,可是它只鏈向本身(注意鏈向本身也算外鏈,固然同時也是個內鏈)。這種節點叫作Spider Trap。
若是對這個圖進行計算,會發現D的rank愈來愈大趨近於1(由於每輪迭代它都只給本身投票),而其它節點rank值幾乎歸零。
所謂Dead Ends,就是這樣一類節點:它們不存在外鏈。看下面的圖:
注意這裏D頁面不存在外鏈,是一個Dead End。
在這個圖中,M第四列(D對應的那列)將全爲0。在沒有Dead Ends的狀況下,每次迭代後向量v各項的和始終保持爲1,而有了Dead Ends,迭代結果將最終歸零。
爲了克服這種因爲矩陣稀疏性、Spider Traps、以及Dead Ends帶來的問題,須要對PageRank計算方法進行一個平滑處理,具體作法是加入「隨機轉移機率」。
所謂隨機轉移,就是咱們認爲在任何一個頁面瀏覽的用戶都有可能以一個極小的機率瞬間轉移到另一個隨機頁面。
固然,這兩個頁面可能不存在超連接,隨機轉移只是爲了算法須要而強加的一種純數學意義的機率數字。
筆者思考:你們仔細體會這種作法的思想,它本質上就是一個結構化風險最小化思想。和在機器學習算法中加入正則項、懲罰項、剪枝;在深度學習中 Dropout 的核心思想都是一致的。咱們能夠這麼來理解,加入了隨機轉移機率後,每一個節點向其餘節點轉移的機率是否是更加傾向於「均等化」了,這就等於削弱了本來的網絡結構的先驗特性。
加入隨機機率轉移後,向量迭代公式變爲:
其中 β 每每被設置爲一個比較小的參數(0.2或更小),它的做用就是在本來模型基礎上加入懲罰因子;
e爲N維單位向量,加入e的緣由是這個公式的前半部分是向量,所以必須將β/N轉爲向量才能相加。
通過隨機轉移機率的修正後,整個計算就變得平滑,由於每次迭代的結果除了依賴轉移矩陣外,還依賴一個小几率的隨機機率轉移。
以該圖爲例:
原始轉移矩陣M爲:
設β爲0.2,則計算公式爲:
若是按這個公式迭代算下去,會發現Spider Traps的效應被抑制了,從而每一個頁面都擁有一個合理的pagerank。
同時,即便是出現了Dead Ends,由於隨機機率矩陣的存在,實際的M 也所以不存在爲0的行了。
問題獲得了完美的解決。
首先給每一個頁面賦予隨機的PR值,而後經過不斷地迭代PR值。當知足下面的不等式後迭代結束,得到全部頁面的PR值:
# -*- coding: utf-8 -*- from pygraph.classes.digraph import digraph class PRIterator: __doc__ = '''計算一張圖中的PR值''' def __init__(self, dg): self.damping_factor = 0.85 # 阻尼係數,即α self.max_iterations = 100 # 最大迭代次數 self.min_delta = 0.00001 # 肯定迭代是否結束的參數,即ϵ self.graph = dg def page_rank(self): # 先將圖中沒有出鏈的節點改成對全部節點都有出鏈 for node in self.graph.nodes(): if len(self.graph.neighbors(node)) == 0: for node2 in self.graph.nodes(): digraph.add_edge(self.graph, (node, node2)) nodes = self.graph.nodes() graph_size = len(nodes) if graph_size == 0: return {} # 給每一個節點賦予初始的PR值,第一輪的PR值是均等的,即 1/N page_rank = dict.fromkeys(nodes, 1.0 / graph_size) # 公式中的(1−α)/N部分 damping_value = (1.0 - self.damping_factor) / graph_size flag = False for i in range(self.max_iterations): change = 0 for node in nodes: rank = 0 # 遍歷全部「入射」的頁面 for incident_page in self.graph.incidents(node): # "入射"頁面的權重根據其出鏈個數均分,而後傳遞給當前頁面 rank += self.damping_factor * (page_rank[incident_page] / len(self.graph.neighbors(incident_page))) # 增長隨機機率轉移矩陣的部分 rank += damping_value change += abs(page_rank[node] - rank) # 絕對值 page_rank[node] = rank print("This is NO.%s iteration" % (i + 1)) print(page_rank) if change < self.min_delta: flag = True break if flag: print("finished in %s iterations!" % node) else: print("finished out of 100 iterations!") return page_rank if __name__ == '__main__': # 建立一個網絡拓樸圖 dg = digraph() dg.add_nodes(["A", "B", "C", "D", "E"]) dg.add_edge(("A", "B")) dg.add_edge(("A", "C")) dg.add_edge(("A", "D")) dg.add_edge(("B", "D")) dg.add_edge(("C", "E")) dg.add_edge(("D", "E")) dg.add_edge(("B", "E")) dg.add_edge(("E", "A")) # PRrank迭代計算 pr = PRIterator(dg) page_ranks = pr.page_rank() print("The final page rank is\n", page_ranks)
從結果上能夠看出兩個比較明顯的規律:
1. E節點的權重是最高的,由於E的入鏈最多,這很顯然; 2. A節點的權重次之,也很高,由於高權重E節點存在向A節點的入鏈;
咱們知道,當Markov鏈收斂時,必有:
相似地,當提到Markov鏈收斂時,必有:
Relevant Link:
http://www.cnblogs.com/fengfenggirl/p/pagerank-introduction.html https://www.letiantian.me/2014-06-10-pagerank/ https://wizardforcel.gitbooks.io/dm-algo-top10/content/pagerank.html https://blog.csdn.net/cannel_2020/article/details/7672042 https://blog.csdn.net/Young_Gy/article/details/70169649?utm_source=blogxgwz2 http://blog.codinglabs.org/articles/intro-to-pagerank.html https://blog.csdn.net/golden1314521/article/details/41597605 https://blog.csdn.net/rubinorth/article/details/52215036 https://blog.csdn.net/leadai/article/details/81230557
設 A = (aij) 是一個 n x n 的正矩陣:,該矩陣有如下幾個性質:
1. A 存在一個正實數的特徵值,叫作 Perron根 或者 Perron - Frobenius特徵值,使得其餘全部特徵值(包括複數特徵值)的規模都比它小;
2. 只對應一個特徵向量 v;
3. 所對應的特徵向量 v 的全部元素都爲正實數;
4. 之外的其餘特徵值所對應的特徵向量的元素至少有一個爲負數或者複數;
5.
6.
每一個矩陣元都大於0的矩陣稱之爲正矩陣;
每一個矩陣元都大於等於0的矩陣是非負矩陣(Nonnegative matrix)
素陣是指自身的某個次冪爲正矩陣(Positive matrix)的矩陣。設 A 爲一個 n x n 的方陣,若是存在正整數 k 使得矩陣知足:
那麼,稱矩陣 A 爲素矩陣。
隨機矩陣又叫作機率矩陣(probability matrix)、轉移矩陣(transition matrix)、馬爾科夫矩陣(markov matrix)等。
隨機矩陣一般表示左隨機矩陣(left stochastic matrix)。
若是方陣爲左隨機矩陣,則其知足如下條件:
即「列和」爲1
方陣A 是不可約的,當且僅當與矩陣A 對應的有向圖是強連通的。
有向圖 G = (V,E) 是強連通的當且僅當對每一節點對,存在 u 到 v 的路徑(不必定是直接相連)。
說狀態 i 是週期的,而且具備週期 k > 1,是指存在一個最小的正整數 k,使得從某狀態 i 出發又回到狀態 i 的全部路徑的長度都是 k 的整數倍。
若是一個狀態不是週期的或者 k = 1,那它就是非週期的。
若是一個馬爾柯夫鏈的全部狀態都是非週期的,那麼就說這個馬爾柯夫鏈是非週期的。
下圖所示,從狀態1 出發回到狀態1 的路徑只有一條,即 1-2-3-1,須要的轉移次數是3,因此這是一個週期爲3 的馬爾柯夫鏈。
咱們從排序聲望(rank prestige)的角度進一步闡述PageRank的思想:
1. 從一個網頁指向另外一個網頁的超連接是PageRank值的隱含式傳遞,網頁的PageRank值是由指向它的全部的網頁所傳遞過來的PageRank值總和決定的。這樣,網頁 i 的入鏈越多,它的PageRank值就越高,它獲得的聲望就越高。 2. 一個網頁指向多個其餘網頁,那麼它傳遞的聲望值就會被它所指向的多個網頁分享。也就是說,即便網頁 i 被一個PageRank值很高的網頁 j 所指向,可是若是網頁 i 的出鏈很是多,網頁 i 從網頁 j 獲得的聲望值可能所以被稀釋地也很小
咱們能夠把web網絡抽象成一個有向圖 G = (V,E),其中 V 是圖的節點集合(一個節點對應一個網頁),E 是圖的有向邊集合(有向邊對應超連接)。
設web上的網頁總數爲 n,即 n = | V |。上述四項能夠形式化爲:
,i = 1,2....,n
其中 P(i) 表示網頁 i 的PageRank值,是網頁 j 出鏈的數量,(j,i) 表示存在網頁 j 指向網頁 i 的超連接。
從數學的觀點看就是存在一個包含 n 個未知量的線性方程組,每一個網頁的權重都是一個未知量。
能夠用一個矩陣來表示,首先做一個符號的約定,用列向量 P 表示 n 個網頁的PageRank值,以下:
再用矩陣 A 表示有向圖的鄰接矩陣,並按以下規則未每條有向邊賦值:
例如以下鄰接矩陣 A:
咱們能夠獲得以下方程組:
咱們的任務是在已知矩陣 A 的條件下,求解向量 P。這個 P 是循環定義的,因此採用冪迭代方法求解 P。
咱們定義給定初值 ,定義
是通過第 n 次迭代獲得的 P 值,能夠形式化以下:
知足上述方程組的解就是
。
固然,也能夠用馬爾柯夫鏈(markov chain)進行建模,這時就能夠當作是markov chain的一個狀態(state),A 能夠表示狀態轉移矩陣(state transition matrix),這樣就能夠轉換成馬爾柯夫鏈的遍歷性和極限分佈問題。
是否收斂,取決於下面幾個條件是否成立:
1. 是否存在?
2. 若是極限存在,它是否與的選取有關?即收斂性是否初始值敏感?
3. 若是極限存在,而且與的選取無關,它做爲網頁排序的依據是否真的合理?
若是要知足前2個問題, 轉移矩陣A 必須知足如下3個條件:
1. 轉移矩陣A 必須是隨機矩陣; 隨機矩陣要求矩陣的每個行和都爲1,即不能出現dead end節點(不存在任何出鏈的節點),若是web網絡拓樸中存在dead end,則原始隨機矩陣的條件不能成立。 可是不要忘了,由於隨機機率轉移矩陣(心靈矩陣)的存在,實際的M不存在爲0的行,因此這第一個條件時知足的。 2. 轉移矩陣A 是不可約的; 一樣的道理,正常的web拓樸不必定能知足徹底強連通的條件(由於Dead Ends的存在),可是由於隨機機率轉移矩陣(心靈矩陣)的存在,這第二個條件也成立 3. 轉移矩陣A 是非週期的; 一樣由於隨機機率轉移矩陣(心靈矩陣)的存在,週期性的定義沒法知足,因此最終的轉移矩陣能夠說知足非週期性
上述的3個條件使得收斂性的前兩個條件獲得了知足。接下來還剩最後一個問題,即'網頁排序的依據是不是真的合理'。
這個問題筆者是這麼認爲的:
所謂的「重要」,其實要看咱們的目的是什麼。這就跟你買車同樣,有的人認爲性能重要,就會更看重性能方面的指標;有的人認爲顏值重要,就會更關注外觀相關的指標。
而 PageRank 的發明場景是互聯網網頁搜索排序,佩奇認爲網頁之間的互相連接程度體現了網頁的重要性,畢竟互聯網的本質就是萬物互聯,一個孤立存在的網頁會被認爲是沒有價值的,或者很不因特內的。
這又引伸出另外一個重要的問題,PageRank算法能夠直接移植到網絡安全攻防檢測領域嗎?先拋出一個觀點:要慎重!就算能夠,在大多數狀況下,也須要改造原始的算法公式。
實際上,這也是筆者在項目中遇到的最多的一個問題之一。不少很秀的算法,從原理上看,明明是能夠適用於網絡安全領域,可是當你真的移植到你的業務場景中後,會發現,結果並非和你預期中那麼完美。形成這種問題的根本緣由是什麼呢?
筆者認爲這是由於如今機器學習經典教材中的經典算法,雖說起來是通用算法,可是其實它們都是由於一些具體的場景被創造出來的,最適合的也是其當初被創造出來的場景。移植到其餘的問題領域後,最核心的假設前提可能改變了,算法是否能發揮出原來同樣驚豔的做用,也就須要打一個問號了。
因此在實際的項目中,咱們須要根據具體場景問題具體分析,對多個算法進行stacking組合,造成一個最合適的pipeline。甚至須要修改原始算法核心公式,爲具體問題定製化一個專用的算法。這樣纔有可能真正發揮出做用。
Relevant Link:
http://www.doc88.com/p-8018027982328.html
https://pan.baidu.com/s/11I8G8Wnc0W1u8RVHXDjeQA
每一行由 「帳戶 - 粉絲」組成。
# -*- coding: utf-8 -*- from pygraph.classes.digraph import digraph import sqlite3 class PRIterator: __doc__ = '''計算一張圖中的PR值''' def __init__(self, dg): self.damping_factor = 0.85 # 阻尼係數,即α self.max_iterations = 1000 # 最大迭代次數 self.min_delta = 0.00001 # 肯定迭代是否結束的參數,即ϵ self.graph = dg def page_rank(self): # 先將圖中沒有出鏈的節點改成對全部節點都有出鏈 for node in self.graph.nodes(): if len(self.graph.neighbors(node)) == 0: for node2 in self.graph.nodes(): digraph.add_edge(self.graph, (node, node2)) nodes = self.graph.nodes() graph_size = len(nodes) if graph_size == 0: return {} # 給每一個節點賦予初始的PR值,第一輪的PR值是均等的,即 1/N page_rank = dict.fromkeys(nodes, 1.0 / graph_size) # 公式中的(1−α)/N部分 damping_value = (1.0 - self.damping_factor) / graph_size flag = False for i in range(self.max_iterations): change = 0 for node in nodes: rank = 0 # 遍歷全部「入射」的頁面 for incident_page in self.graph.incidents(node): # "入射"頁面的權重根據其出鏈個數均分,而後傳遞給當前頁面 rank += self.damping_factor * (page_rank[incident_page] / len(self.graph.neighbors(incident_page))) # 增長隨機機率轉移矩陣的部分 rank += damping_value change += abs(page_rank[node] - rank) # 絕對值 page_rank[node] = rank print("This is NO.%s iteration" % (i + 1)) print(page_rank) if change < self.min_delta: flag = True break if flag: print("finished in %s iterations!" % node) else: print("finished out of 100 iterations!") return page_rank if __name__ == '__main__': # 建立一個網絡拓樸圖 dg = digraph() conn = sqlite3.connect('zhihu.db') c = conn.cursor() nodes = [] cursor = c.execute("SELECT DISTINCT user_url, followee_url FROM Following;") for row in cursor: #print row if row[0] not in nodes: nodes.append(row[0]) if row[1] not in nodes: nodes.append(row[1]) # 添加實體節點 dg.add_nodes(nodes) cursor = c.execute("SELECT DISTINCT user_url, followee_url FROM Following;") for row in cursor: user_url = str(row[0]) followee_url = str(row[1]) # 添加實體間link(邊) followee_url -> user_url #print "followee_url:{0} -> user_url:{1}".format(followee_url, user_url) dg.add_edge((followee_url, user_url)) conn.close() # PRrank迭代計算 pr = PRIterator(dg) page_ranks = pr.page_rank() with open("page_ranks.txt", 'w') as fp: fp.write(str(page_ranks)) print("The final page rank is\n", page_ranks)
使用大數據組件進行100輪訓練後,獲得的pagerank排序結果以下:
node weight chengbailao 0.01406879 wind 0.00608487 neaton 0.00568152 jixin 0.00488994 zeng-kai-87 0.004726 yskin 0.00370338 hou-ye-60 0.00326399 followstars 0.00305445 yu-chen-41-39 0.00276672 gmf8541 0.00273379 _zhao_xu_ 0.0026574 zhai-huo-18 0.0026528 xushiyuzhihu 0.0025693 yueyihe 0.00212099 peng 0.00210883 bing-hou-20 0.00208451 oogoo 0.00201606 liuya802 0.0018386 mengtoy 0.0018233 yvancao 0.00176921 tang-chen 0.00174643 guo-shu-86-30 0.00168231 bhuztez 0.00165525 wang-wen-ping 0.00164296 chenxix 0.00163695 melinywu 0.0016331 chen-chen-66-21 0.00161215 ihate 0.00156636 stephen-cheng 0.00152092 boxun 0.00148928 wang-wen-ping-27 0.00148886 lxjts 0.0014828 tan-ri-tian 0.00144296 wangxiaofeng 0.00143438 zhong-ye-zi-49 0.0014341 james-swineson 0.00141263 puloon 0.0013969 mym95 0.00139541 lie-feng-2 0.00138813 susus 0.00137226 gymitat 0.00136749 fang-wen-32 0.00135586 joyneop 0.0013496 xuzhihong 0.00134178 qi-yuan-yuan-52 0.00131502 wannian 0.00130875 qiao-yang-76-30 0.00130659 bettercallsaul 0.00129384 du-forever 0.00128617 yuningyichen 0.00128111 xiao-chu 0.00126434 chen-yin-dong 0.00124541 shen0101 0.00124078 liu-yi-han-46 0.00123879 403Forbidden 0.00123664 eodoso 0.00123578 shenpp 0.00123555 dian-qian-du-dian-jian 0.00122604 anyan 0.00121871 qianjin 0.00119378 guaguaguaguaguagua 0.00115795 xie-wei-you 0.00114612 lu-zheng-29-24 0.00113225 yang-hu-85 0.00113176 yuba100 0.00113136 jueshihaojian 0.00112986 whale 0.00111578 fashiontop 0.0011137 cklover 0.00110348 zhu-yixin-42 0.00110276 mcbuder 0.00110074 quiver 0.00110009 lewhwa 0.00110006 zuo-qing-96 0.00108733 niu-yue-lao-li-xiao-chang 0.00108432 patli 0.00108025 sapereaude 0.00107575 hipara 0.00107537 GilgameshK 0.00107137 zkaip 0.00107053 sddcreerf 0.00106716 ling-er-ding-dang 0.00106533 liqiang123 0.00104595 david-du 0.00103702 aguaithefreak 0.00101756 chong 0.00100256 miaomiaomiao 0.00099407 hu-qian-qiu 0.00098814 han-yan-hui 0.00097521 songtsee 0.00096843 sun-peng-70-45 0.00096207 johnsonwang 0.00095147 hu-bi-teng 0.0009501 deutsch-99 0.00092026 lubenyuan.com 0.00091791 shijun 0.00091377 chengyuan 0.00091036 gazhi-liu 0.0008935 luo-li-10 0.00088078
Relevant Link:
http://www.cnblogs.com/fengfenggirl/p/pagerank-cnblogs.html https://github.com/BigPeng/cnblogs-user-pagerank https://www.jianshu.com/p/60ffb949113f https://www.jianshu.com/p/3b2a1895a12d