如何用Neo4j和Scikit-Learn做機器學習任務?| 附超詳細分步教程

  • 2019 年 12 月 10 日
  • 筆記

作者 | Mark Needham

譯者 | Tianyu、Shawnice

編輯 | Jane

出品 | AI科技大本營(ID:rgznai100)

圖演算法不是一個新興技術領域,在開源庫中已經有很多功能強大的演算法實現。近兩年,業內的學者與科學家都在積極探索可以彌補深度學習不可解釋性,無法進行因果推斷的這個缺陷,而圖神經網路(GNN)成為備受關注和期待的「寵兒」。隨著學界和業界越來越關注GNN,各種新工作不斷被提出,基於圖神經網路的框架隨之產生,如大家現在都已經熟悉的DGL,兩大深度學習框架PyTorch和TensorFlow中也開始支援相應的功能,大家對圖(Graph)、圖計算、圖資料庫、圖機器學習等研究的關注度越發高漲。

基於圖數據的優秀性質,吸引越來越多的企業在基於圖數據的機器學習任務中開始投入研究與使用,將圖數據與機器學習演算法結合,彌補演算法缺陷,賦予新一代圖資料庫新的使命。有不少企業內部自研圖資料庫與圖分析計算平台,但是可直接使用的開源或成熟工具並不完善,對沒有能力自研的企業來說,基於圖數據的機器學習該怎麼做?工程師在自己的研究中有什麼可行的嘗試方法?

今天的文章中,通過大家都非常熟悉的兩個工具——圖資料庫 Neo4J和Scikit-Learning 提供一種解決思路。我們將以構建一個機器學習分類器任務為例,從基礎背景知識、演算法原理到演算法程式碼實現進行全面的講解與指導。

資料庫 Neo4J

資料庫 Neo4J 是一種圖形資料庫,目前幾個主流圖資料庫有 TigerGraph、Neo4j、Amazon Neptune、JanusGraph和ArangoDB,近年來,Neo4J一直位列圖資料庫排行榜榜首,隨著這幾年知識圖譜的火熱發展,讓資料庫 Neo4J受到廣泛關注。

Neo4J 主要基於Cypher語言,基於Graph Algorithm 實現圖分析演算法。獲取安裝Neo4j Desktop也非常容易,只需一鍵。

Neo4j Desktop 地址: https://neo4j.com/download/

這裡再給大家推薦主要基於 Neo4J實現的案例演算法書《Graph Algorithms》,其作者 Amy Holder 和 Mark Needham也是 Neo4j的員工。

在線閱讀地址: https://neo4j.com/docs/graph-algorithms/current/

圖資料庫對於分析異構數據點之間的關係特別的有用,例如防欺詐或Facebook的好友關係圖,以在社交網路關係的預測任務為例,複雜的(社交)網路一個最重要的基本構成是鏈接,在社交關係網路中基於已有節點和鏈接構成的網路資訊,預測潛在關係,這背後一個核心的演算法就是鏈路預測演算法。這也是我們今天文章中的核心演算法,Neo4J圖演算法庫支援了多種鏈路預測演算法,在初識Neo4J 後,我們就開始步入鏈路預測演算法的學習,以及如何將數據導入Neo4J中,通過Scikit-Learning與鏈路預測演算法,搭建機器學習預測任務模型。

鏈路預測演算法

(一)什麼是鏈路預測?

鏈路預測已經被提出很多年了。2004年,由 Jon Kleinberg 和 David Liben-Nowell 發表相關論文之後,鏈路預測才被普及開來。他們的論文為《The Link Prediction Problem for Social Networks》

論文地址: https://www.cs.cornell.edu/home/kleinber/link-pred.pdf

隨後,Kleinberg 和 Liben-Nowell 提出從社交網路的角度來解決鏈路預測問題,如下所述:

若給定一個社交網路的快照,我們能預測出該網路中的成員在未來可能出現哪些新的關係嗎?我們可以把這個問題看作鏈路預測問題,然後對網路中各節點的相似度進行分析,從而得出預測鏈路的方法。

後來,Jim Webber 博士在 GraphConnect San Francisco 2015 大會上介紹了圖演算法的發展歷程,他用圖理論講解了第二次世界大戰。

演講影片: https://youtu.be/kVHdMD-XT9s

除了預測世界大戰和社交網路中的朋友關係,我們還可能在什麼場景用到關係預測呢?我們可以預測恐怖組織成員之間的關係,生物網路中分子間的關係,引文網路中潛在的共同創作關係,對藝術家或藝術品的興趣等等,這些場景都可能用得上鏈路預測。

鏈路的預測都意味著對未來可能發生的行為進行預測,比如在一個引文網路中,我們是在對兩個人是否可能合作寫一篇論文進行預測。

(二)鏈路預測演算法

Kleinberg 和 Liben-Nowell 介紹了一系列可以用於鏈路預測的演算法,如下圖所示:

Kleinberg 和 Liben-Nowell 在論文中所介紹的演算法

這些方法都是計算一對節點的分數,該分數可看作為那些節點基於拓撲網路的「近似度」。兩個節點越相近,它們之間存在聯繫的可能性就越大。

下面我們來看看幾個評估標準,以便於我們理解演算法的原理。

(三)演算法評估標準

  • 1、共同鄰居數

最簡單的度量方法之一是計算共同鄰居數,對這個概念,Ahmad Sadraei 的解釋如下:

作為預測因子,共同鄰居數可以捕捉到擁有同一個朋友的兩個陌生人,而這兩個人可能會被這個朋友介紹認識(圖中出現一個閉合的三角形)。

這個度量標準計算了一對節點所共享的相同鄰居數目。如下圖所示,節點 A 和 D 有兩個共同鄰居(節點 B 和 C),而節點 A 和 E 只有一個共同鄰居(節點 B)。因此,我們認為節點 A 和 D 更相近,未來更有可能產生關聯。

  • 2、Adamic Adar(AA 指標)

早在2003年,Lada Adamic 和 Eytan Adar 在研究社交網路的預測問題時,提出了 Adamic Adar 演算法。AA 指標也考慮了共同鄰居的度資訊,但除了共同鄰居,還根據共同鄰居的節點的度給每個節點賦予一個權重,即度的對數分之一,然後把每個節點的所有共同鄰居的權重值相加,其和作為該節點對的相似度值。

節點的度指它的鄰居數,該演算法的初衷是:當圖中出現一個閉合的三角時,那些度數低的節點可能有更大的影響力。比如在一個社交網路中,有兩個人是被他們的共同好友介紹認識的,發生這種關聯的可能性和這個人還有多少對朋友有關。一個「朋友不多」的人更有可能介紹他的一對朋友認識。

  • 3、優先連接

對於圖演算法研究者來說,這應該是最常見的概念之一,最初由 Albert-László Barabási 和 Réka Albert 提出,當時他們正在進行有關無尺度網路的研究。該演算法的設計初衷是,一個節點擁有的關係越多,未來獲得更多關聯的可能性就越大。這是計算起來最簡單的度量標準,我們只需要計算每個節點的度數的乘積。

(四)鏈路預測 – Neo4j 圖演算法庫

目前,Neo4j 圖演算法庫涵蓋了6種鏈路預測演算法:Adamic Adar 演算法、共同鄰居演算法( Common Neighbors)、優先連接演算法(Preferential Attachment)、資源分配演算法(Resource Allocation)、共同社區演算法(Same Community)、總鄰居演算法(Total Neighbors)。

快速學習一下以下五種演算法的原理:

(1)Adamic Adar:計算共同鄰居的度數的對數分之一,並求和。

(2)優先連接演算法:計算每個節點的度數的乘積。

(3)資源分配演算法:計算共同鄰居的度數分之一,並求和。

(4)共同社區演算法:利用社區發現演算法,檢查兩個節點是否處於同一個社區。

(5)總鄰居演算法:計算兩個節點所擁有的不同鄰居的數目。

現在來看一下如何使用庫中的共同鄰居函數,以之前提到的圖關係作為例子。

首先執行 Cypher 語句,在 Neo4j 中創建一個圖:

  UNWIND [["A", "C"], ["A", "B"], ["B", "D"],          ["B", "C"], ["B", "E"], ["C", "D"]] AS pair  MERGE (n1:Node {name: pair[0]})  MERGE (n2:Node {name: pair[1]})  MERGE (n1)-[:FRIENDS]-(n2)

然後用下面的函數來計算節點 A 和 D 的共同鄰居數:


neo4j> MATCH (a:Node {name: 'A'})         MATCH (d:Node {name: 'D'})         RETURN algo.linkprediction.commonNeighbors(a, d);  +-------------------------------------------+  | algo.linkprediction.commonNeighbors(a, d) |  +-------------------------------------------+  | 2.0                                       |  +-------------------------------------------+  1 row available after 97 ms, consumed after another 15 ms

這些節點有兩個共同鄰居,所以它們的得分為2。現在對節點 A 和 E 進行同樣的計算。因為它們只有一個共同鄰居,不出意外我們得到的分數應該為1。

  neo4j> MATCH (a:Node {name: 'A'})         MATCH (e:Node {name: 'E'})         RETURN algo.linkprediction.commonNeighbors(a, e);  +-------------------------------------------+  | algo.linkprediction.commonNeighbors(a, e) |  +-------------------------------------------+  | 1.0                                       |  +-------------------------------------------+    

如我們所料,得分確實為1。該函數默認的計算方式涵蓋任意的類型以及指向。我們也可以通過傳入特定的參數來進行計算:

  neo4j> WITH {direction: "BOTH", relationshipQuery: "FRIENDS"}         AS config         MATCH (a:Node {name: 'A'})         MATCH (e:Node {name: 'E'})         RETURN algo.linkprediction.commonNeighbors(a, e, config)         AS score;  +-------+  | score |  +-------+  | 1.0   |  +-------+    

為了確保得到準確的結果,我們再試試另一種演算法。

優先連接函數返回的是兩個節點度數的乘積。如果我們對節點 A 和 D 進行計算,會得到 2*2=4 的結果,因為節點 A 和 D 都有兩個鄰居。下面來試一試:

  neo4j> MATCH (a:Node {name: 'A'})         MATCH (d:Node {name: 'D'})         RETURN algo.linkprediction.preferentialAttachment(a, d)         AS score;  +-------+  | score |  +-------+  | 4.0   |  +-------+    

(五)鏈路預測所得的分數有何用?

現在我們已經了解有關鏈路預測和相似度指標的基本知識了,但還需要弄明白如何使用這些指標進行鏈路預測。有以下兩種方法:

  • 1、直接使用指標

我們可以直接使用由鏈路預測演算法得到的分數,即設置一個閾值,這樣就可以預測一對節點是否可能存在關係了。

在上面的例子中,我們可以設定每一對優先連接分數在3分以上的節點都可能存在關聯,而那些得分小於或等於3分的節點對則不存在關聯。

  • 2、有監督學習

我們可以把分數作為特徵去訓練一個二分類器,從而進行有監督學習。然後用這個二分類器去預測一對節點是否存在關聯。

參考閱讀文章《Link Prediction In Large-Scale Networks》中有對這兩種方法的詳細介紹: https://hackernoon.com/link-prediction-in-large-scale-networks-f836fcb05c88?gi=b86a42e1c8d4

在這個系列教程中,我們會重點介紹有監督學習的方法。

構建機器學習分類器

既然我們決定使用有監督學習的方法,那麼就需要考慮有關機器學習工作流的兩個問題:

(1)具體要使用什麼機器學習模型?

(2)如何將數據分成訓練集和測試集?

(一)機器學習模型

前面提到的鏈路預測指標都是對相似的數據進行計算,但如果選擇使用機器學習模型,意味著我們需要解決特徵間的關聯問題。

有些機器學習模型默認其處理的特徵都是相互獨立的。若一個模型得到的特徵不滿足該假設,則會導致預測結果的準確度很低。無論我們選擇什麼模型,都需要去除掉那些高度相關的特徵。

我們還可以選一個簡單方案,使用那些對特徵相關性不那麼敏感的模型。

一些集成方法是行得通的,因為他們對輸入數據沒有這樣的要求,比如梯度提升分類器(gradient boosting classifier)或者 隨機森林分類器(random forest classifier)。

(二)訓練集和測試集

比較棘手的問題是訓練集和測試集的切分,我們不能只進行隨機切分,因為這可能導致數據泄露。

當模型不小心用到訓練集以外的數據時,就會發生數據泄露。這在圖計算中很容易發生,因為訓練集中的節點可能與測試集中的節點存在關聯。

我們需要把圖切分成子圖作為訓練集和測試集。如果圖數據有時間這個概念,那我們的工作就容易多了,我們可以以某個時間點進行分割點,該時間點之前的數據作為訓練集,之後的數據作為測試集。

這仍然不是最好的解決方案,我們需要進行嘗試,確保訓練集和測試集中子圖的大致網路結構是相近的。一旦做好這一步,我們就擁有了由若干存在關聯的節點對所組成的訓練集和測試集。它們都屬於機器學習模型中的正樣本。

接下來看什麼是負樣本。

最簡單的情況是,全部節點對之間都不存在關聯。但問題是,很多場景中存在關係的節點對數目遠大於那些沒有關係的節點對。

負樣本的最大數目如下:


# negative examples = (# nodes)² - (# relationships) - (# nodes)

如果我們將訓練集中的全部負樣本都代入模型,就會導致嚴重的類別不均衡問題,即負樣本數遠大於正樣本數。

若基於這種不均衡數據集進行模型的訓練,只要我們預測任何節點對都不存在關聯,就可以得到非常不錯的準確度,但這當然不是我們想要的。

所以我們需要盡量減少負樣本的數目。有一種方法被多篇論文提及過,那就是選擇那些彼此間距相等的節點對。這種方法可以有效地減少負樣本數,雖然負樣本數仍然遠大於正樣本數。

為了解決樣本不均衡的問題,我們也可以對負樣本進行欠取樣,或者對正樣本進行過取樣。

(三)程式碼教程:鏈路預測實戰

基於上面對鏈路預測背景知識的學習,準備好實際數據集後,下面我們就開始實操教程,教程將完成一個判斷是否是論文合著者關係的機器學習預測模型。

  • 1、錄入引用資料庫

我們將使用來自DBLP引文網路的數據,其中包括來自各種學術來源的引文數據,這裡我們還要重點關注一些軟體開發會議上的數據。

通過運行以下Cypher語句來導入該數據子集。只要在Neo4j瀏覽器中啟用多語句編輯器,就可以一次全部運行。

// Create constraints  CREATE CONSTRAINT ON (a:Article) ASSERT a.index IS UNIQUE;  CREATE CONSTRAINT ON (a:Author) ASSERT a.name IS UNIQUE;  CREATE CONSTRAINT ON (v:Venue) ASSERT v.name IS UNIQUE;  // Import data from JSON files using the APOC library  CALL apoc.periodic.iterate(    'UNWIND ["dblp-ref-0.json", "dblp-ref-1.json", "dblp-ref-2.json", "dblp-ref-3.json"] AS file     CALL apoc.load.json("https://github.com/mneedham/link-prediction/raw/master/data/" + file)     YIELD value WITH value     RETURN value',    'MERGE (a:Article {index:value.id})     SET a += apoc.map.clean(value,["id","authors","references", "venue"],[0])     WITH a, value.authors as authors, value.references AS citations, value.venue AS venue     MERGE (v:Venue {name: venue})     MERGE (a)-[:VENUE]->(v)     FOREACH(author in authors |       MERGE (b:Author{name:author})       MERGE (a)-[:AUTHOR]->(b))     FOREACH(citation in citations |       MERGE (cited:Article {index:citation})       MERGE (a)-[:CITED]->(cited))',     {batchSize: 1000, iterateList: true});   

下圖是數據導入到Neo4j後的顯示:

  • 2、搭建共同作者圖

該數據集不包含描述他們的協作的作者之間的關係,但是我們可以根據查找多個人撰寫的文章來推斷他們。以下Cypher語句在至少撰寫過一篇文章的作者之間創建了CO_AUTHOR關係:

  MATCH (a1)<-[:AUTHOR]-(paper)-[:AUTHOR]->(a2:Author)  WITH a1, a2, paper  ORDER BY a1, paper.year  WITH a1, a2, collect(paper)[0].year AS year,       count(*) AS collaborations  MERGE (a1)-[coauthor:CO_AUTHOR {year: year}]-(a2)  SET coauthor.collaborations = collaborations;    

即使在多篇文章中進行過合作,我們也只能在合作的作者之間創建一種CO_AUTHOR關係。我們在這些關係上創建幾個屬性:

(1)年份屬性,指合作者們共同完成的第一篇文章的出版年份

(2)合作屬性,指作者們合作過多少篇文章

Neo4j 中的共同作者

現在已經有了合著者關係圖表,我們需要弄清楚如何預測作者之間未來合作的可能性,我們將構建一個二進位分類器來執行此操作,因此下一步是創建訓練圖和測試圖。

  • 3、訓練和測試數據集

根據上面的介紹,我們不能將數據隨機分為訓練數據集和測試數據集,因為如果不小心將訓練數據之外的數據用於創建模型,則可能會發生數據泄漏。這很容易發生在使用圖形的時候,因為訓練集中的節點對可能與測試集中的節點相連。

為了解決這個問題,我們需要將我們的圖分為訓練圖和測試子圖,幸運的是引文圖中包含我們可以分割的時間資訊。我們可以通過拆分特定年份的數據來創建訓練圖和測試圖。但是,我們應該分開哪一年呢?先來看看合作者共同合作的第一年的分布情況:

(每年的合作數分布圖)

看起來我們應該在2016年進行拆分,為我們的每個子圖提供合理數量的數據,將2005年之前開始的所有合著者作為訓練圖,2006年以後的則作為測試圖。

基於該年在圖表中創建明確的CO_AUTHOR_EARLY和CO_AUTHOR_LATE關係。以下程式碼將為我們創建這些關係:

  • 訓練子圖
  MATCH (a)-[r:CO_AUTHOR]->(b)  WHERE r.year < 2006  MERGE (a)-[:CO_AUTHOR_EARLY {year: r.year}]-(b);    
  • 測試子圖
  MATCH (a)-[r:CO_AUTHOR]->(b)  WHERE r.year >= 2006  MERGE (a)-[:CO_AUTHOR_LATE {year: r.year}]-(b);    

這樣分組使我們在2005年之前的早期圖表中有81,096個關係,在2006年之後的後期圖表中有74,128個關係,形成了52-48的比例。這個比例比通常測試中使用的比例高很多,但這沒關係。這些子圖中的關係將作為訓練和測試集中的正例,但我們也需要一些負例。使用否定示例可以讓我們的模型學習如何區分在它們之間鏈接節點和不在它們之間鏈接節點。

與鏈接預測問題一樣,否定示例比肯定的示例多得多。否定示例的最大數量等於:


# negative examples = (# nodes)² - (# relationships) - (# nodes)

即節點的平方數減去圖形所具有的關係再減去自身關係。

除了使用幾乎所有可能的配對以外,我們也將彼此之間相距2至3跳的節點進行配對,這將為我們提供更多可管理的數據。我們可以通過運行以下程式碼來生成和查詢配對:

  MATCH (author:Author)  WHERE (author)-[:CO_AUTHOR_EARLY]-()  MATCH (author)-[:CO_AUTHOR_EARLY*2..3]-(other)  WHERE not((author)-[:CO_AUTHOR_EARLY]-(other))  RETURN id(author) AS node1, id(other) AS node2

此查詢返回4,389,478個否定示和81,096個肯定示,這意味著否定示是肯定示的54倍之多。

但仍然存在很大的不平衡,這意味著用於預測每對節點鏈接的模型將非常不準確。為了解決這個問題,我們可以對正例進行升取樣或對負例進行降取樣,可以使用下取樣方法。

  • 4、Py2neo, pandas, scikit-learn

接下來我們使用py2neo,pandas和scikit-learn庫,全部基於Python語言,通過Pypi安裝:


pip install py2neo==4.1.3 pandas sklearn

(1)py2neo驅動程式使數據科學家能夠輕鬆地將Neo4j與Python數據科學生態系統中的工具相結合。我們將使用該庫對Neo4j執行Cypher查詢。

(2)pandas是BSD許可的開放源程式碼庫,為Python程式語言提供了高性能、易於使用的數據結構和數據分析工具。

(3)scikit-learn是一個非常受歡迎的機器學習庫。我們將使用該庫來構建我們的機器學習模型。

(Scikit-Learn workflow 拓展版,來源網路)

安裝完這些庫後,導入所需的程式包,並創建資料庫連接:

from py2neo import Graph  import pandas as pd    graph = Graph("bolt://localhost", auth=("neo4j", "neo4jPassword"))

  • 5、搭建我們的訓練和測試集

現在,我們可以編寫以下程式碼來創建測試數據框架,其中包含基於早期圖形的正例和負例:

# Find positive examples  train_existing_links = graph.run("""  MATCH (author:Author)-[:CO_AUTHOR_EARLY]->(other:Author)  RETURN id(author) AS node1, id(other) AS node2, 1 AS label  """).to_data_frame()  # Find negative examples  train_missing_links = graph.run("""  MATCH (author:Author)  WHERE (author)-[:CO_AUTHOR_EARLY]-()  MATCH (author)-[:CO_AUTHOR_EARLY*2..3]-(other)  WHERE not((author)-[:CO_AUTHOR_EARLY]-(other))  RETURN id(author) AS node1, id(other) AS node2, 0 AS label  """).to_data_frame()  # Remove duplicates  train_missing_links = train_missing_links.drop_duplicates()  # Down sample negative examples  train_missing_links = train_missing_links.sample(      n=len(train_existing_links))  # Create DataFrame from positive and negative examples  training_df = train_missing_links.append(      train_existing_links, ignore_index=True)  training_df['label'] = training_df['label'].astype('category')

一個測試數據集的例子

執行相同的操作來創建測試數據框架,但是這次僅考慮後期圖形中的關係:

# Find positive examples  test_existing_links = graph.run("""  MATCH (author:Author)-[:CO_AUTHOR_LATE]->(other:Author)  RETURN id(author) AS node1, id(other) AS node2, 1 AS label  """).to_data_frame()  # Find negative examples  test_missing_links = graph.run("""  MATCH (author:Author)  WHERE (author)-[:CO_AUTHOR_LATE]-()  MATCH (author)-[:CO_AUTHOR_LATE*2..3]-(other)  WHERE not((author)-[:CO_AUTHOR_LATE]-(other))  RETURN id(author) AS node1, id(other) AS node2, 0 AS label  """).to_data_frame()  # Remove duplicates  test_missing_links = test_missing_links.drop_duplicates()  # Down sample negative examples  test_missing_links = test_missing_links.sample(n=len(test_existing_links))  # Create DataFrame from positive and negative examples  test_df = test_missing_links.append(      test_existing_links, ignore_index=True)  test_df['label'] = test_df['label'].astype('category')

接下來,開始創建機器學習模型。

  • 6、選擇機器學習演算法

我們將創建一個隨機森林分類器,此方法非常適合數據集中包含強項和弱項的模型。儘管弱功能有時會有所幫助,但隨機森林方法可以確保我們不會創建過度擬合訓練數據的模型。使用以下程式碼創建模型:

from sklearn.ensemble import RandomForestClassifier    classifier = RandomForestClassifier(n_estimators=30, max_depth=10,                                      random_state=0)

現在是時候設計一些用來訓練模型的特徵。特徵提取是一種將大量數據和屬性提取為一組具有代表性的數值(特徵)的方法。這些特徵會作為輸入的數據,以便我們區分學習任務的類別/值。

  • 7、生成鏈接預測特徵

使用鏈接預測功能生成一些特徵:

def apply_graphy_features(data, rel_type):      query = """      UNWIND $pairs AS pair      MATCH (p1) WHERE id(p1) = pair.node1      MATCH (p2) WHERE id(p2) = pair.node2      RETURN pair.node1 AS node1,             pair.node2 AS node2,             algo.linkprediction.commonNeighbors(                 p1, p2, {relationshipQuery: $relType}) AS cn,             algo.linkprediction.preferentialAttachment(                 p1, p2, {relationshipQuery: $relType}) AS pa,             algo.linkprediction.totalNeighbors(                 p1, p2, {relationshipQuery: $relType}) AS tn      """      pairs = [{"node1": pair[0], "node2": pair[1]}               for pair in data[["node1", "node2"]].values.tolist()]      params = {"pairs": pairs, "relType": rel_type}        features = graph.run(query, params).to_data_frame()      return pd.merge(data, features, on = ["node1", "node2"])    

此功能發起一個查詢,該查詢從提供的DataFrame中獲取配對的節點,並對每一對節點進行以下計算:共同鄰居(cn)、優先附件(pa)以及鄰居總數(tn)

如下所示,我們可以將其應用於我們的訓練並測試DataFrame:

training_df = apply_graphy_features(training_df, "CO_AUTHOR_EARLY")  test_df = apply_graphy_features(test_df, "CO_AUTHOR")

對於訓練數據框架,僅根據早期圖形來計算這些指標,而對於測試數據框架,將在整個圖形中進行計算。也可以使用整個圖形來計算這些功能,因為圖形的演變取決於所有時間,而不僅取決於2006年及以後的情況。

測試訓練集

使用以下程式碼訓練模型:

columns = ["cn", "pa", "tn"]    X = training_df[columns]  y = training_df["label"]  classifier.fit(X, y)

現在的模型已經經過訓練了,但還需要對它進行評估。

8、評估模型

我們將計算其準確性,準確性和召回率,計算方法可參考下圖,scikit-learn也內置了此功能,還可以得到模型中使用的每個特徵的重要性。

from sklearn.metrics import recall_score  from sklearn.metrics import precision_score  from sklearn.metrics import accuracy_score    def evaluate_model(predictions, actual):      accuracy = accuracy_score(actual, predictions)      precision = precision_score(actual, predictions)      recall = recall_score(actual, predictions)        metrics = ["accuracy", "precision", "recall"]      values = [accuracy, precision, recall]      return pd.DataFrame(data={'metric': metrics, 'value': values})  def feature_importance(columns, classifier):      features = list(zip(columns, classifier.feature_importances_))      sorted_features = sorted(features, key = lambda x: x[1]*-1)        keys = [value[0] for value in sorted_features]      values = [value[1] for value in sorted_features]      return pd.DataFrame(data={'feature': keys, 'value': values})

評估模型執行程式碼:

predictions = classifier.predict(test_df[columns])  y_test = test_df["label"]    evaluate_model(predictions, y_test) 

(準確率,精準度,召回度)

在各個方面的得分都很高。現在可以運行以下程式碼來查看哪個特徵扮演了最重要的角色:

feature_importance(columns, classifier)

(特徵重要度)

在上面我們可以看到,公共鄰居(cn)是模型中的主要支配特徵。共同鄰居意味著作者擁有的未閉合的協同者三角的數量的計數,因此數值這麼高並不奇怪。

接下來,添加一些從圖形演算法生成的新特徵。

  • 9、三角形與聚類係數

首先,在測試圖和訓練子圖上運行三角計數演算法。該演算法可返回每個節點形成的三角形數量以及每個節點的聚類係數。節點的聚類係數表示其鄰居也被連接的可能性。可以在Neo4j瀏覽器中運行以下Cypher查詢,以在訓練圖上運行此演算法:

CALL algo.triangleCount('Author', 'CO_AUTHOR_EARLY', {    write:true,    writeProperty:'trianglesTrain',    clusteringCoefficientProperty:'coefficientTrain'});

然後執行以下Cypher查詢以在測試圖上運行:

CALL algo.triangleCount('Author', 'CO_AUTHOR', {    write:true,    writeProperty:'trianglesTest',    clusteringCoefficientProperty:'coefficientTest'});

現在節點上有4個新屬性:三角訓練,係數訓練,三角測試和係數測試。現在,在以下功能的幫助下,將它們添加到我們的訓練和測試DataFrame中:

def apply_triangles_features(data,triangles_prop,coefficient_prop):      query = """      UNWIND $pairs AS pair      MATCH (p1) WHERE id(p1) = pair.node1      MATCH (p2) WHERE id(p2) = pair.node2      RETURN pair.node1 AS node1,      pair.node2 AS node2,      apoc.coll.min([p1[$triangles], p2[$triangles]]) AS minTriangles,      apoc.coll.max([p1[$triangles], p2[$triangles]]) AS maxTriangles,      apoc.coll.min([p1[$coefficient], p2[$coefficient]]) AS minCoeff,      apoc.coll.max([p1[$coefficient], p2[$coefficient]]) AS maxCoeff      """        pairs = [{"node1": pair[0], "node2": pair[1]}            for pair in data[["node1", "node2"]].values.tolist()]      params = {"pairs": pairs,                "triangles": triangles_prop,                "coefficient": coefficient_prop}        features = graph.run(query, params).to_data_frame()      return pd.merge(data, features, on = ["node1", "node2"])

這些參數與我們到目前為止使用的不同,它們不是特定於某個節點配對的,而是針對某個單一節點的參數。不能簡單地將這些值作為節點三角或節點係數添加到我們的DataFrame中,因為無法保證節點配對的順序,我們需要一種與順序無關的方法。這裡可以通過取平均值、值的乘積或通過計算最小值和最大值來實現此目的,如此處所示:

training_df = apply_triangles_features(training_df,    "trianglesTrain", "coefficientTrain")  test_df = apply_triangles_features(test_df,    "trianglesTest", "coefficientTest")

現在可以訓練與評估:

columns = [      "cn", "pa", "tn",      "minTriangles", "maxTriangles", "minCoeff", "maxCoeff"  ]  X = training_df[columns]  y = training_df["label"]  classifier.fit(X, y)  predictions = classifier.predict(test_df[columns])  y_test = test_df["label"]  display(evaluate_model(predictions, y_test))

(準確率,精準度,召回度)

這些特徵很有幫助!我們的每項參數都比初始模型提高了約4%。哪個特徵最重要?

display(feature_importance(columns, classifier)) 

(特徵重要度)

共同鄰居還是最具有影響力的特徵,但三角特徵的重要性也提升了不少。

這篇教程即將結束,基於整個工作流程,希望還可以激發大家更多的思考:

(1)還有其他可添加的特徵嗎?這些特徵能幫助我們創建更高準確性的模型嗎?也許其他社區檢測甚至中心演算法也可能會有所幫助?

(2)目前,圖形演算法庫中的鏈接預測演算法僅適用於單零件圖(兩個節點的標籤相同的圖),該演算法基於節點的拓撲;如果我們嘗試將其應用於具有不同標籤的節點(這些節點可能具有不同的拓撲),這就意味著此演算法無法很好地發揮作用,所以目前也在考慮添加適用於其他圖表的鏈接預測演算法的版本,也歡迎大家在Github上一起交流。

Github地址: https://github.com/neo4j-contrib 原文鏈接: https://medium.com/neo4j/link-prediction-with-neo4j-part-1-an-introduction-713aa779fd9 https://towardsdatascience.com/link-prediction-with-neo4j-part-2-predicting-co-authors-using-scikit-learn-78b42w356b44c

(*本文為AI科技大本營編譯文章,轉載請微信聯繫 1092722531)