
簡介這是一套基于MATLAB的社交網絡鏈路預測程序包主要面向數據挖掘、網絡科學及復雜網絡分析方向的研究生、科研人員和MATLAB開發者。壓縮包共含37個文件包括34個.m源碼腳本、2個.asv自動緩存文件和1個txt說明整體大小僅22KB結構緊湊便于直接閱讀和二次開發。源碼實現了共同鄰居、Jaccard系數、Adamic-Adar指數、資源分配、局部路徑、Katz等經典算法并配有訓練/測試集劃分與AUC評估函數可量化評價預測結果。借助程序附帶的示例數據用戶能快速跑通完整流程進而應用于好友推薦、社區發現、網絡演化分析等場景。已有2420人瀏覽學習適合作為課程設計、畢業論文或科研入門階段的參考實現有助于理解鏈路預測原理并掌握MATLAB算法設計思路。1. 鏈路預測社交網絡里“看似無關的人其實只差一次AUC”在社交網絡分析中鏈路預測要回答的問題很具體給定一張靜態網絡快照哪些尚未連邊的節點對最可能產生新連邊這聽起來像推薦系統但它的核心不是用戶行為日志而是網絡結構相似性。做推薦、風控或生物網絡研究的人都知道多數真實網絡是稀疏的觀測到的邊只是真實關系的一部分。這個名為 Code.rar 的 MATLAB 壓縮包恰好把鏈路預測里最常用的十幾條基線指標從 CN 到 SimRank 一次性實現了并附帶劃分訓練/測試集與 AUC 評估腳本。對于剛接觸復雜網絡的研究生它是最快的“跑通基線”的方案對于已經做過網絡分析的人它的意義在于可以用同一份鄰接矩陣橫向對比多種指標省去重復造輪子的時間。2. 相似性指標譜系從 CN 到 SimRank哪些值得放進 Matlab 工具箱鏈路預測算法雖多但大部分都能歸到“給每個未連邊節點對打分”這個框架里。分數越高預測存在連邊的概率越大。判斷一個節點對有多“像”常見做法是分成三類局部指標只看一階或二階鄰域擬局指標利用有限長度路徑全局指標則考慮整個網絡的拓撲信息。這個包里同時出現了 CN、AA、RA、PA、Jaccard、Salton、HPI、HDI、LHN、LHNII、ACT、SimRank、Katz、RWR、LRW、SRW、TSRWR基本覆蓋了這三類。2.1 局部相似性Common Neighbors 與它的加權變體局部指標是計算代價最低的一類。Common NeighborsCN直接統計兩個節點的共同鄰居數數學上就是鄰接矩陣二次冪的非對角元。Adamic-AdarAA在 CN 基礎上對每個共同鄰居取度數的對數倒數目的是降低高連接度“中心節點”的權重——因為一個節點連接了全網一半的人它的共同鄰居身份參考價值有限。Resource AllocationRA比 AA 更激進直接把 log 去掉用度數的倒數。PAPreferential Attachment則相反認為新連邊概率與兩端度的乘積成正比適合冪律分布明顯的社交網絡。實際項目里一般把 AA 和 RA 當作局部基線它們對稠密社區比較敏感。下面是一段可以直接在 MATLAB 命令行運行的 CN 計算利用了稀疏矩陣乘法function S cn_score(A) % A: n x n 鄰接矩陣無向無權 n size(A, 1); S A * A; % S(i,j) 是 i 和 j 的公共鄰居數 S S - diag(diag(S)); % 去掉對角線上的自身度數 end這段代碼里A*A本質上是把二階路徑數量累加diag(diag(S))取出對角線并構造對角矩陣減掉。因為A是對稱矩陣所以S也是對稱的如果輸入的A是有向圖這里算出的就不再是共同鄰居而是“共同出度目標”要注意區分。局部指標的計算量主要花在這兩次矩陣乘法上稀疏矩陣下規模到十萬節點也不太吃力。2.2 擬局與全局指標路徑、隨機游走與 SimRank 的代價當網絡直徑較大或者局部鄰居信息稀疏時局部指標會失效。此時可以往擬局指標走。Local PathLP在 CN 的基礎上疊加長度為 3 的路徑數包里的 LocalPath.m 就是在做這件事通常還會給一階項和二階項分別加權。Katz 則把任意長度路徑按照長度指數衰減求和衰減因子beta必須小于鄰接矩陣最大特征值的倒數否則級數發散。另一個被頻繁使用的是 SimRank如果兩個節點的鄰居相似那么它們自己也相似本質上是“遞歸的同構性度量”。SimRank 的計算通常需要迭代到收斂復雜度較高但結果對結構相似的節點對非常魯棒。全局指標里還有一類基于隨機游走的RWR 計算從源節點出發的隨機游走回到每個節點的概率分布LRW 是有限步隨機游走SRW 是平均隨機游走。這個包里的 RWR.m、LRW.m、SRW.m、TSRWR.m 都在這個框架下。全局方法的共同特點是“貴”對 n 個節點做一次完整預測往往需要 n 次矩陣迭代或線性方程求解實際網絡中常用來和局部指標做融合。2.3 指標選型數據稀疏度與計算規模怎么權衡選擇哪一類指標首先看網絡規模。百萬節點級別的社交網絡里全局指標幾乎跑不動SimRank 更是災難此時 CN/AA/RA/PA 是唯一現實選擇。網絡規模在萬節點以下、且對精度敏感時Katz 和 RWR 能把局部指標落后的一截補回來。還要看任務目標如果是預測缺失邊局部指標在稠密網絡上表現不錯如果是預測未來新增邊PA 往往因為長尾效應而占優。下面的表把包里的常見指標按類別和復雜度整理了一下方便跑試驗前快速定位指標類別核心思想計算復雜度參考CN, AA, RA, Salton, Jaccard, HDI, HPI, LHN局部共同鄰居及鄰居度加權O(m) 到 O(n^2)LocalPath, Katz, ACT擬局有限路徑數/全路徑衰減矩陣乘冪或偽逆SimRank, RWR, LRW, SRW全局遞歸相似度/隨機游走多次迭代收斂慢實際工作中我會先跑局部指標把 AUC 數據拿到手再根據網絡規模決定要不要花時間跑全局指標。這套代碼包的價值就在于此它把這么多指標放在同一個數據結構上切換成本很低。3. Code.rar 代碼解剖Main.m、DivideNet.m 與 CalcAUC.m 的分工壓縮包里 31 個 m 文件看起來很多但核心流程只有三步把邊表變成鄰接矩陣劃分訓練/測試集然后對每個指標算分并評估。熟悉了這個數據流后面替換自己的網絡數據就很容易。3.1 文件結構與數據流先看倉庫里的文件。下表列出的是會直接調用的關鍵腳本文件角色Main.m主流程加載數據、調指標、輸出結果test.m / test1.m調試與單元級別的測試腳本FormNet.m將邊列表或鄰接列表轉換為稀疏鄰接矩陣DivideNet.m把完整鄰接矩陣按比例拆成訓練集和測試集CalcAUC.m在測試集上計算 AUCCN.m / AA.m / RA.m / PA.m / Katz.m / RWR.m ...各相似性指標實現Copy_of_Main.m / Main.asv備份與自動保存版本從命名可以看出Main.m 是入口。asv文件是 MATLAB 編輯器自動備份和主腳本內容基本一致可以直接忽略。Copy_of_Main.m看起來是想保留一份對比實驗主程序類似項目里這么干很常見跑參數掃描時只要復制主腳本再改一行參數避免破壞穩定版本。數據流是這樣的nettest.txt原始邊表 →FormNet.m生成鄰接矩陣 A →DivideNet.m把 A 的邊拆成訓練集 A_train 和測試集 A_test → 各個指標函數在 A_train 上計算分數矩陣 S →CalcAUC.m用 S 結合 A_train 和 A_test 計算 AUC。3.2 核心函數解讀FormNet.m負責構造鄰接矩陣。常見的傳入參數有兩種一種是 n×2 的邊列表另一種是 n×n 的鄰接矩陣。邊列表到矩陣的轉換在 MATLAB 里可以用sparse一行實現function A FormNet(edges, n) % edges: m x 2 的邊列表節點編號從 1 開始 % n: 節點數 A sparse(edges(:,1), edges(:,2), 1, n, n); A double(A | A); % 轉置后取并集保證無向對稱 end如果輸入的邊列表帶有重邊sparse會自動累加權重。A | A把有向的鄰居關系變成無向對稱這在社交網絡里是默認操作你要是處理有向網絡這行要去掉。MATLAB 的稀疏矩陣在后續矩陣乘法中會自動調用稀疏算法這也是整個包能跑大圖的基礎。DivideNet.m做的是邊級劃分。很多初學者會把訓練/測試集按節點劃分這在鏈路預測里是錯的測試邊必須從完整邊集中抽出來但節點集合要保持不變否則會泄漏節點屬性。一個可靠的劃分方式是先記錄所有非對角邊的下標隨機打亂后按比例取前 80% 作為訓練集后 20% 作為測試集function [A_train, A_test] DivideNet(A, ratio, seed) % ratio: 訓練集占比默認 0.8 rng(seed, twister); n size(A, 1); [I, J] find(triu(A, 1)); % 只取上三角避免重復 m length(I); perm randperm(m); train_idx perm(1:round(ratio*m)); test_idx perm(round(ratio*m)1:end); A_train sparse(n, n); A_test sparse(n, n); for k train_idx A_train(I(k), J(k)) 1; A_train(J(k), I(k)) 1; end for k test_idx A_test(I(k), J(k)) 1; A_test(J(k), I(k)) 1; end end這里故意用triu(A, 1)取出上三角元素避免每條邊被處理兩次。rng(seed)讓隨機劃分可復現跑實驗時應該固定seed否則 AUC 的波動可能不是算法差異而是數據劃分差異。CalcAUC.m是評估模塊。AUC 統計的是從測試邊里隨機抽一條邊它的得分高于從不存在邊里隨機抽一條邊的概率。常見做法是采樣估計也可以用全量排序。小網絡可用后一種大網絡用采樣function auc CalcAUC(score, A_train, A_test, n_samples) % score: n x n 得分矩陣 % 未連邊樣本從 A_train A_test 的補集中抽樣 if nargin 4 n_samples 100000; end n size(score, 1); [I, J] find(triu(A_test, 1)); pos_pairs sub2ind([n n], I, J); [I, J] find(triu(A_train A_test, 1) 0); neg_pairs sub2ind([n n], I, J); pidx randi(length(pos_pairs), n_samples, 1); nidx randi(length(neg_pairs), n_samples, 1); spos score(pos_pairs(pidx)); sneg score(neg_pairs(nidx)); auc (sum(spos sneg) 0.5 * sum(spos sneg)) / n_samples; endsub2ind把行列下標轉成線性索引避免在二維矩陣上反復取值。triu(A_train A_test, 1) 0選中上三角中不是原始邊的位置A_train A_test正好補回完整邊集。注意這里score要對稱否則triu取到的上三角分數和實際預測方向不對應。3.3 測試腳本與備份文件test.m和test1.m的區別不大通常前者用內置小圖驗證函數正確性后者跑真實邊表。Copy_of_Main.m這個備份名提醒我們修改主流程前先復制一份避免把驗證過的腳本改壞。不少同學直接改 Main.m改到一半發現 AUC 計算有問題想回退卻找不到原始版本只能從 asv 里恢復。如果你打算在這個包上做二次開發第一件事就是把 Main.m 復制成 Main_backup.m然后基于 Copy_of_Main.m 改。這不算什么高深技巧但能省掉很多不必要的錯誤定位時間。4. 跑通一次鏈路預測從 nettest.txt 到 AUC下面以nettest.txt為例給出一套可以直接執行的流程。實際數據不一定是這個格式但下面的步驟可以照搬到自己的邊表上。4.1 準備輸入數據與鄰接矩陣包里的nettest.txt一般是每行一條邊的簡表格式類似1 2 1 3 2 4 3 4第一列是邊的一端第二列是另一端。節點編號必須是從 1 開始的連續整數如果不是先重映射。讀取時可以這樣寫edges load(nettest.txt); n max(max(edges)); A FormNet(edges, n);如果文件有表頭load會失敗改用readmatrix(nettest.txt, NumHeaderLines, 1)。建好A后建議先檢查規模與對稱性fprintf(節點數%d邊數%d\n, n, nnz(A)/2); assert(issymmetric(A), 鄰接矩陣不對稱);nnz(A)/2是因為無向矩陣中每條邊存了兩次。這一步能過濾掉大部分格式錯誤。注意如果nettest.txt是帶權重的邊列表FormNet里的sparse第三個參數需要改成權重列否則會丟失權重信息。4.2 劃分訓練集和測試集默認用 80% 邊訓練20% 邊測試rng(42); ratio 0.8; [A_train, A_test] DivideNet(A, ratio, 42);這里有兩個關鍵點。第一ratio不能太極端否則測試集樣本太少AUC 置信區間會很寬。第二固定隨機種子很關鍵。為了快速比較多種指標建議把“劃分數據”和“跑指標”分成兩個階段避免每次跑指標都重新打亂訓練集。如果包里的DivideNet不允許傳入種子可以像上一節那樣自行封裝。4.3 計算多指標得分得到A_train后所有指標函數接口基本一致S_cn CN(A_train); S_aa AA(A_train); S_ra RA(A_train); S_pa PA(A_train); S_katz Katz(A_train, 0.01); % beta 參數見下文這些函數返回的S是 n×n 矩陣S(i,j)是模型預測 i 到 j 存在連邊的得分。對于無向網絡S應當對稱如果某個指標函數返回的結果不對稱檢查它內部有沒有(SS)/2的對稱化步驟。Katz.m的beta不能隨便填。beta要小于鄰接矩陣最大特征值的倒數否則計算(I - beta*A)^(-1)會發散。處理時可以用max(abs(eigs(A_train, 1, largestabs)))探測譜半徑保守取譜半徑倒數的 0.5 倍。RWR 的restart概率一般在 0.7 到 0.9 之間越小越偏向全局傳播越大越集中在局部鄰居。指標關鍵參數推薦范圍Katzbeta小于譜半徑倒數建議取其 0.5 倍RWR / TSRWRrestart0.7 ~ 0.9LRW / SRW步長3 ~ 5LocalPath路徑長度權重長度 3 項權重取 0.01 量級運行完所有指標后得分矩陣會占不少內存建議及時把中間結果寫盤save(scores.mat, S_cn, S_aa, S_ra, S_pa, S_katz);4.4 用 AUC 比較預測精度打分只是中間產物最終都要用 AUC 衡量。對于小網絡可以直接做全量 AUC也可以用采樣方式auc_cn CalcAUC(S_cn, A_train, A_test, 100000); fprintf(CN AUC %.4f\n, auc_cn);如果CalcAUC內部是采樣實現建議把樣本數至少設到 100000AUC 的標準誤大約在 sqrt(0.25/n_samples) 級別十萬樣本對應約 0.0016 的誤差足夠判斷指標之間的差距。測試邊數量特別少時采樣會不穩定這種時候直接用秩和檢驗計算等效 AUCMATLAB 的ranksum可以做pos S_cn(find(A_test 1)); [I_neg, J_neg] find(A_train A_test 0); neg S_cn(sub2ind(size(S_cn), I_neg, J_neg)); % 對 pos 和 neg 做逐對比較即可得到與 CalcAUC 等價的 AUC注意A_train A_test 0找的是正樣本補集也就是原始網絡中不存在的邊。如果網絡有向這里要分別處理每個方向的非邊。5. 進階技巧多指標投票、隨機游走參數調節與驗證陷阱拿到基本 AUC 后下一步是讓結果更可信。5.1 多指標融合與秩平均社交網絡里不同指標擅長捕獲不同微觀結構CN 擅長三角形閉包PA 擅長捕捉 hub 節點的吸引力Katz 能利用長路徑。與其選一個指標不如把幾個指標的排序結果做秩平均。做法是先將每個指標得分矩陣轉成排名然后逐元素平均。一段可用的 MATLAB 代碼function S_ens rank_ensemble(S_list) % S_list: cell 數組每個元素是一個 n x n 得分矩陣 n size(S_list{1}, 1); S_ens zeros(n); for k 1:length(S_list) flat S_list{k}(:); % 展成列向量 r tiedrank(flat); % 全局秩處理并列 S_ens S_ens reshape(r, n, n); % 還原成矩陣 end S_ens S_ens / length(S_list); end秩平均能減少不同指標量綱差異帶來的影響避免某個指標分數天然數值大而主導投票。測試集合規模較小時直接秩平均比加權回歸更穩方差小。5.2 隨機游走參數重跑的約定RWR 的 restart 概率、Katz 的 beta、LRW 的步長都是關鍵參數。我的習慣是在文件名里帶上參數比如Katz_beta_0.01_AUC_0.7234.mat而不是直接覆蓋 scores.mat。這樣調參時可以回溯“之前那個 0.72 是哪組參數跑出來的”而不是靠記憶。每次改參數都要固定相同的 train/test 劃分否則換了數據劃分AUC 差別可能大于參數影響。5.3 驗證 AUC 是否被高估的簡單辦法鏈路預測最常見的錯誤是測試邊在訓練階段泄漏給了模型。例如A_train與A_test的并集如果參與了指標計算AUC 會虛高。驗證方法是把A_test里的邊隨機隱藏一批重新計算 AUC如果 AUC 波動很大說明模型對個別邊過度敏感。另一個方法是構造空模型把A做度保持的隨機重連在隨機網絡上重跑同樣的流程。隨機網絡上的 AUC 應該接近 0.5如果明顯高于 0.5就要檢查指標函數里是否無意識地訪問了A_test的信息或者代碼里是否把測試邊也放進了鄰接矩陣。本文還有配套的精品資源點擊獲取