社交網(wǎng)絡(luò)信息傳播模型:SI、SIR與IC模型原理與應(yīng)用)
1. 項目概述從社交網(wǎng)絡(luò)到信息傳播的量化洞察最近幾年無論是品牌營銷、輿情監(jiān)控還是產(chǎn)品冷啟動大家越來越關(guān)注一個核心問題一條信息比如一個熱點話題、一個產(chǎn)品功能、一則新聞是如何在人群中擴散開來的它的傳播路徑是怎樣的最終能觸達多少人這些問題本質(zhì)上就是社交網(wǎng)絡(luò)中的信息傳播問題。單純靠經(jīng)驗直覺去判斷往往不準(zhǔn)這時候就需要一些數(shù)學(xué)模型來幫忙了。這個項目就是帶你親手搭建幾個經(jīng)典的信息傳播模型并用Python把它們實現(xiàn)出來讓你能直觀地看到信息擴散的動態(tài)過程。簡單來說信息傳播模型就是一套數(shù)學(xué)規(guī)則用來模擬個體網(wǎng)絡(luò)中的節(jié)點在受到鄰居影響后其狀態(tài)比如從未知到知曉從知曉到傳播再到遺忘如何發(fā)生變化。通過計算機模擬成千上萬次這樣的個體互動我們就能預(yù)測整個網(wǎng)絡(luò)的宏觀傳播效果。這對于評估營銷活動的潛在影響力、預(yù)測輿情走勢、甚至分析傳染病擴散都有極大的參考價值。無論你是做數(shù)據(jù)分析、產(chǎn)品運營還是策略研究掌握這套方法都能讓你多一個強有力的量化分析工具。2. 核心模型原理與選型邏輯信息傳播模型有很多種選擇哪個取決于你想模擬的現(xiàn)實場景。這次我們重點實現(xiàn)三個最基礎(chǔ)、也最經(jīng)典的模型SI、SIR和IC模型。它們各有側(cè)重構(gòu)成了理解更復(fù)雜模型的基礎(chǔ)。2.1 SI模型最簡單的擴散起點SI模型是所有傳播模型的“始祖”它把人群分為兩類易感者Susceptible, S和感染者Infected, I。在信息傳播的語境下S代表還沒聽說過這條信息的人I代表已經(jīng)知道并會主動傳播這條信息的人。模型規(guī)則極其簡單一個I狀態(tài)的節(jié)點每次與它的S狀態(tài)鄰居接觸時都有一定的概率記為β將這個鄰居轉(zhuǎn)變?yōu)镮狀態(tài)。一旦變成I就永久保持這個狀態(tài)不會再變回S。這個模型模擬的是一種“一旦知曉永久傳播”的場景比如某些根深蒂固的觀念或者常識的普及。它的核心方程是微分方程形式dI/dt β * S * I / N其中N是總?cè)藬?shù)。這個方程描述了感染者數(shù)量隨時間增長的速率。選擇SI模型作為起點是因為它邏輯清晰參數(shù)少只有一個感染概率β非常適合用來理解傳播動力學(xué)的基本框架和編程實現(xiàn)的核心循環(huán)。注意SI模型預(yù)測的最終結(jié)果是所有人都被感染I→N這顯然不符合大多數(shù)信息傳播會飽和的現(xiàn)實。因此它更適用于理論教學(xué)和模擬傳播初期階段。2.2 SIR模型引入“免疫”與遺忘SIR模型在SI的基礎(chǔ)上增加了一個狀態(tài)移除者Recovered, R?,F(xiàn)在人群分為三類易感者S、感染者I、移除者R。新增的規(guī)則是感染者I在以概率β感染易感者S的同時自身還會以概率γ轉(zhuǎn)變?yōu)橐瞥逺。在信息傳播中R狀態(tài)可以理解為對信息“免疫”了。這個人可能已經(jīng)知道了信息但失去了傳播興趣比如覺得信息過時了或者徹底忘記了這條信息。關(guān)鍵點在于變成R后節(jié)點就不再參與后續(xù)的傳播過程既不會被感染也不會感染別人。這個模型引入了“恢復(fù)”機制使得傳播過程有了終結(jié)的可能最終網(wǎng)絡(luò)中的個體會穩(wěn)定在S、I、R三個狀態(tài)的不同比例上而不會全部變成I。其微分方程組為dS/dt -β * S * I / NdI/dt β * S * I / N - γ * IdR/dt γ * ISIR模型非常適合模擬像季節(jié)性新聞、短期營銷活動這類“熱一陣就過”的信息擴散也經(jīng)典地用于傳染病研究。參數(shù)β感染率和γ恢復(fù)率的比值 R0 β / γ是一個關(guān)鍵指標(biāo)基本決定了疫情能否爆發(fā)。2.3 IC模型獨立級聯(lián)與影響力最大化獨立級聯(lián)模型Independent Cascade, IC是另一個流派常用于社交網(wǎng)絡(luò)影響力傳播的研究。它與SIR的“連續(xù)時間”視角不同IC模型是“離散時間步”的。在IC模型中每個節(jié)點只有兩種狀態(tài)活躍Active和非活躍Inactive。活躍節(jié)點代表接受了信息并可能傳播的人類似I非活躍節(jié)點代表未知者類似S。模型從一小組初始活躍節(jié)點種子節(jié)點開始按輪次進行第0步種子節(jié)點被激活。第1步每個在第0步新激活的節(jié)點有一次機會去嘗試激活它的每個非活躍鄰居。對每個鄰居激活嘗試以概率p一個預(yù)設(shè)的傳播概率獨立成功或失敗。后續(xù)步驟只有在上一步新被激活的節(jié)點才能在當(dāng)前步嘗試激活其鄰居。如果一個節(jié)點激活嘗試失敗或者它在上一步已經(jīng)被激活但未成功激活任何鄰居它在后續(xù)步驟中將不再進行嘗試。這個過程一直持續(xù)到?jīng)]有新的節(jié)點被激活為止。IC模型的核心特點是“一次性嘗試”和“級聯(lián)失效”。它模擬了現(xiàn)實中的一種情況你第一次聽到某個消息時可能會轉(zhuǎn)發(fā)但如果這次沒轉(zhuǎn)以后大概率也不會再轉(zhuǎn)了。這個模型是解決“影響力最大化”問題如何選擇k個種子節(jié)點使最終激活的節(jié)點數(shù)最多的經(jīng)典基礎(chǔ)模型。3. 環(huán)境準(zhǔn)備與網(wǎng)絡(luò)數(shù)據(jù)構(gòu)建在寫代碼之前我們需要搭建好實驗環(huán)境并準(zhǔn)備好模擬的“舞臺”——社交網(wǎng)絡(luò)。3.1 Python環(huán)境與核心庫我強烈建議使用Anaconda來管理Python環(huán)境它能很好地處理科學(xué)計算庫的依賴。核心庫就三個NetworkX 這是Python中處理復(fù)雜網(wǎng)絡(luò)圖的“瑞士軍刀”。創(chuàng)建網(wǎng)絡(luò)、添加節(jié)點和邊、計算網(wǎng)絡(luò)屬性、畫圖全都靠它。NumPy 提供高效的數(shù)組運算和隨機數(shù)生成我們模擬概率事件比如以概率β感染離不開它。Matplotlib 用于可視化。我們要畫出網(wǎng)絡(luò)結(jié)構(gòu)圖以及傳播過程中各狀態(tài)人數(shù)隨時間變化的曲線。安裝非常簡單在終端或Anaconda Prompt里執(zhí)行pip install networkx numpy matplotlib3.2 構(gòu)建模擬社交網(wǎng)絡(luò)現(xiàn)實中的社交網(wǎng)絡(luò)數(shù)據(jù)獲取不易我們先用一個經(jīng)典的合成網(wǎng)絡(luò)模型來模擬——WS小世界網(wǎng)絡(luò)。它由Watts和Strogatz提出能生成具有較短平均路徑長度六度分隔和較高聚類系數(shù)朋友的朋友也是朋友的網(wǎng)絡(luò)這非常貼合真實社交網(wǎng)絡(luò)的特征。import networkx as nx import matplotlib.pyplot as plt def create_social_network(n100, k4, p0.1): 創(chuàng)建一個WS小世界網(wǎng)絡(luò)用于模擬社交網(wǎng)絡(luò)。 參數(shù): n: 網(wǎng)絡(luò)中的節(jié)點數(shù)人數(shù)默認100。 k: 每個節(jié)點初始連接的鄰居數(shù)必須是偶數(shù)默認4。 p: 每條邊被隨機重連的概率控制著網(wǎng)絡(luò)的“小世界”特性默認0.1。 返回: G: 一個NetworkX圖對象。 # 使用networkx的connected_watts_strogatz_graph函數(shù)確保生成的網(wǎng)絡(luò)是連通的。 G nx.connected_watts_strogatz_graph(nn, kk, pp) print(f網(wǎng)絡(luò)創(chuàng)建成功節(jié)點數(shù){G.number_of_nodes()} 邊數(shù){G.number_of_edges()}) print(f平均聚類系數(shù){nx.average_clustering(G):.3f} 平均最短路徑長度{nx.average_shortest_path_length(G):.3f}) return G # 創(chuàng)建一個示例網(wǎng)絡(luò) G create_social_network(n50, k4, p0.1) # 可視化這個網(wǎng)絡(luò) plt.figure(figsize(8, 6)) pos nx.spring_layout(G, seed42) # 使用spring布局算法讓圖看起來更均勻 nx.draw(G, pos, node_colorlightblue, node_size200, with_labelsFalse, edge_colorgray) plt.title(WS小世界網(wǎng)絡(luò)結(jié)構(gòu)模擬社交網(wǎng)絡(luò)) plt.show()實操心得p參數(shù)是個關(guān)鍵調(diào)節(jié)旋鈕。p0時網(wǎng)絡(luò)是規(guī)則環(huán)p1時接近隨機網(wǎng)絡(luò)。p在0.01到0.1之間時網(wǎng)絡(luò)能很好地兼具高聚類和短路徑的特性。初次實驗時節(jié)點數(shù)n不要設(shè)太大比如50-200否則可視化會一團糟模擬速度也慢。先在小網(wǎng)絡(luò)上調(diào)通邏輯。4. SI模型實現(xiàn)與模擬分析有了網(wǎng)絡(luò)我們就可以開始實現(xiàn)第一個模型了。SI模型的邏輯最直接是理解傳播模擬編程范式的最佳切入點。4.1 算法步驟詳解SI模型的模擬過程可以分解為以下清晰步驟初始化給網(wǎng)絡(luò)G中的每個節(jié)點添加一個屬性state初始值設(shè)為S易感。隨機選擇一定數(shù)量比如1個或幾個的節(jié)點作為初始感染者將其state屬性改為I。初始化一個列表I_counts用于記錄每一步時刻的感染者數(shù)量。模擬循環(huán)設(shè)定總模擬步數(shù)T。在每一步t a. 遍歷當(dāng)前所有狀態(tài)為I的節(jié)點。 b. 對于每個感染者節(jié)點i遍歷其所有鄰居節(jié)點j。 c. 如果鄰居j的狀態(tài)是S則生成一個[0,1)之間的隨機數(shù)。如果這個隨機數(shù)小于感染概率beta則將節(jié)點j的狀態(tài)改為I。 d.關(guān)鍵點為了避免在同一時間步內(nèi)新感染的節(jié)點又去感染別人這不符合離散時間步的假設(shè)我們需要準(zhǔn)備一個“待感染列表”。在本輪遍歷中只記錄哪些S節(jié)點被選中等所有感染者的傳播嘗試都檢查完畢后再統(tǒng)一更新這些節(jié)點的狀態(tài)為I。 e. 記錄當(dāng)前步結(jié)束后的感染者總數(shù)存入I_counts。終止與輸出循環(huán)結(jié)束后返回I_counts列表。我們還可以可視化網(wǎng)絡(luò)最終狀態(tài)和感染人數(shù)曲線。4.2 Python代碼實現(xiàn)與注釋import numpy as np def simulate_si_model(G, beta0.3, initial_infected1, T20): 在給定網(wǎng)絡(luò)G上模擬SI傳播模型。 參數(shù): G: NetworkX圖代表社交網(wǎng)絡(luò)。 beta: 感染概率范圍[0,1]。 initial_infected: 初始感染者數(shù)量。 T: 模擬的總時間步數(shù)。 返回: S_counts, I_counts: 列表記錄每一步的易感者和感染者數(shù)量。 G: 模擬結(jié)束后的網(wǎng)絡(luò)節(jié)點帶有最終的state屬性。 # 1. 初始化節(jié)點狀態(tài) nx.set_node_attributes(G, S, state) # 給所有節(jié)點添加初始狀態(tài)S all_nodes list(G.nodes()) # 隨機選擇初始感染者 infected_nodes np.random.choice(all_nodes, sizeinitial_infected, replaceFalse) for node in infected_nodes: G.nodes[node][state] I # 初始化計數(shù)器列表 S_counts [] I_counts [] # 2. 開始模擬循環(huán) for step in range(T): # 記錄本輪待感染的節(jié)點 nodes_to_infect [] # 獲取當(dāng)前所有感染者節(jié)點 current_infected [n for n, attr in G.nodes(dataTrue) if attr[state] I] # 遍歷每個感染者 for inf_node in current_infected: # 遍歷感染者的鄰居 for neighbor in G.neighbors(inf_node): if G.nodes[neighbor][state] S: # 以概率beta嘗試感染 if np.random.rand() beta: nodes_to_infect.append(neighbor) # 統(tǒng)一更新狀態(tài)將本輪被選中的易感者變?yōu)楦腥菊?for node in nodes_to_infect: G.nodes[node][state] I # 統(tǒng)計當(dāng)前狀態(tài)人數(shù) S_count sum(1 for _, attr in G.nodes(dataTrue) if attr[state] S) I_count sum(1 for _, attr in G.nodes(dataTrue) if attr[state] I) S_counts.append(S_count) I_counts.append(I_count) # 可選如果感染者已經(jīng)達到總?cè)藬?shù)可以提前終止循環(huán) if I_count G.number_of_nodes(): print(f在第{step1}步所有人均已感染。) # 補齊剩余步數(shù)的計數(shù)保持列表長度一致 S_counts.extend([0] * (T - step - 1)) I_counts.extend([G.number_of_nodes()] * (T - step - 1)) break return S_counts, I_counts, G # 運行模擬 S_counts, I_counts, G_final simulate_si_model(G, beta0.2, initial_infected2, T15) # 可視化結(jié)果 plt.figure(figsize(12, 4)) # 子圖1最終網(wǎng)絡(luò)狀態(tài) plt.subplot(1, 2, 1) node_colors [red if G_final.nodes[n][state] I else lightblue for n in G_final] nx.draw(G_final, pos, node_colornode_colors, node_size200, with_labelsFalse, edge_colorgray) plt.title(fSI模型模擬最終狀態(tài) (Beta{0.2})) # 子圖2人數(shù)隨時間變化曲線 plt.subplot(1, 2, 2) steps list(range(len(I_counts))) plt.plot(steps, I_counts, r-, label感染者 (I), linewidth2) plt.plot(steps, S_counts, b--, label易感者 (S), linewidth2) plt.xlabel(時間步) plt.ylabel(人數(shù)) plt.title(SI模型傳播動力學(xué)) plt.legend() plt.grid(True, alpha0.3) plt.tight_layout() plt.show()4.3 參數(shù)影響與結(jié)果分析運行上面的代碼你會看到一張圖。左圖是模擬結(jié)束后網(wǎng)絡(luò)的狀態(tài)紅色節(jié)點是感染者藍色是易感者在SI模型里如果模擬時間足夠長最終應(yīng)該全是紅色。右圖是兩條曲線展示了S和I人數(shù)隨時間的變化。這里有幾個關(guān)鍵點需要你動手嘗試和觀察感染概率Beta 這是最重要的參數(shù)。將beta從0.05調(diào)到0.5再運行。你會發(fā)現(xiàn)beta很小時紅色曲線I上升得非常緩慢可能直到模擬結(jié)束還有大量藍色節(jié)點。beta很大時紅色曲線幾乎垂直上升迅速感染所有人。beta實際上決定了傳播的“力度”。初始感染者位置與數(shù)量 我們代碼中是隨機選的。你可以嘗試修改代碼固定選擇網(wǎng)絡(luò)中度中心性最高的節(jié)點最活躍的人作為初始感染者看看傳播速度是否會加快。這引出了“影響力最大化”的雛形。網(wǎng)絡(luò)結(jié)構(gòu)的影響 我們用的是WS小世界網(wǎng)絡(luò)。你可以嘗試用nx.erdos_renyi_graph(n, p)生成一個隨機圖Erdos-Renyi模型或者用nx.barabasi_albert_graph(n, m)生成一個無標(biāo)度網(wǎng)絡(luò)Barabasi-Albert模型存在少數(shù)高度節(jié)點。在不同結(jié)構(gòu)的網(wǎng)絡(luò)上運行相同的SI模型傳播速度和最終范圍會有顯著差異。無標(biāo)度網(wǎng)絡(luò)中對高度節(jié)點的感染會引發(fā)爆炸式的傳播。踩坑記錄在模擬循環(huán)中最易犯的錯誤是“即時更新”。即在遍歷感染者鄰居時一旦發(fā)現(xiàn)某個S節(jié)點滿足感染條件立刻將其狀態(tài)改為I。這會導(dǎo)致這個在本輪剛被感染的節(jié)點在同一輪中又以其新身份“I”去感染其他鄰居造成傳播速度的嚴(yán)重高估。務(wù)必使用“待感染列表”進行緩沖更新。5. SIR模型實現(xiàn)與深度探索SIR模型引入了恢復(fù)機制更貼近現(xiàn)實。它的實現(xiàn)比SI稍復(fù)雜一點因為要管理三個狀態(tài)和兩個概率β和γ。5.1 算法流程與狀態(tài)管理SIR模擬的步驟框架與SI類似但狀態(tài)轉(zhuǎn)換邏輯變?yōu)槌跏蓟O(shè)置所有節(jié)點為S隨機選擇初始IR數(shù)量為0。為每個節(jié)點增加一個state屬性。模擬循環(huán)每一步 a.感染過程遍歷所有I節(jié)點對其每個S鄰居以概率β嘗試感染將成功的鄰居加入“新感染列表”。 b.恢復(fù)過程遍歷所有I節(jié)點包括上一步剛產(chǎn)生的每個節(jié)點以概率γ嘗試恢復(fù)將成功的節(jié)點加入“新恢復(fù)列表”。 c.狀態(tài)更新先統(tǒng)一將“新感染列表”中的節(jié)點狀態(tài)從S改為I。再統(tǒng)一將“新恢復(fù)列表”中的節(jié)點狀態(tài)從I改為R。這里有個重要順序問題必須先處理感染再處理恢復(fù)并且用列表緩沖。否則可能出現(xiàn)一個節(jié)點剛被感染又在同一步被恢復(fù)的邏輯矛盾。 d. 記錄S, I, R的數(shù)量。終止可以設(shè)定最大步數(shù)或者當(dāng)I的數(shù)量降為0時提前終止。5.2 代碼實現(xiàn)與關(guān)鍵參數(shù)R0def simulate_sir_model(G, beta0.3, gamma0.1, initial_infected2, T50): 在給定網(wǎng)絡(luò)G上模擬SIR傳播模型。 參數(shù): G: NetworkX圖。 beta: 感染概率。 gamma: 恢復(fù)概率。 initial_infected: 初始感染者數(shù)量。 T: 最大模擬步數(shù)。 返回: S_counts, I_counts, R_counts: 列表記錄每一步各狀態(tài)人數(shù)。 G: 模擬結(jié)束后的網(wǎng)絡(luò)。 # 初始化 nx.set_node_attributes(G, S, state) all_nodes list(G.nodes()) infected_nodes np.random.choice(all_nodes, sizeinitial_infected, replaceFalse) for node in infected_nodes: G.nodes[node][state] I S_counts, I_counts, R_counts [], [], [] for step in range(T): new_infections [] new_recoveries [] # 獲取當(dāng)前所有感染者 current_infected [n for n, attr in G.nodes(dataTrue) if attr[state] I] # 感染階段I節(jié)點嘗試感染S鄰居 for inf_node in current_infected: for neighbor in G.neighbors(inf_node): if G.nodes[neighbor][state] S: if np.random.rand() beta: new_infections.append(neighbor) # 恢復(fù)階段I節(jié)點嘗試恢復(fù)為R for inf_node in current_infected: if np.random.rand() gamma: new_recoveries.append(inf_node) # 狀態(tài)更新先感染后恢復(fù) for node in new_infections: if G.nodes[node][state] S: # 二次檢查防止?fàn)顟B(tài)沖突 G.nodes[node][state] I for node in new_recoveries: if G.nodes[node][state] I: # 二次檢查 G.nodes[node][state] R # 統(tǒng)計 S_count sum(1 for _, attr in G.nodes(dataTrue) if attr[state] S) I_count sum(1 for _, attr in G.nodes(dataTrue) if attr[state] I) R_count sum(1 for _, attr in G.nodes(dataTrue) if attr[state] R) S_counts.append(S_count) I_counts.append(I_count) R_counts.append(R_count) # 如果感染者清零提前結(jié)束 if I_count 0: print(f疫情在第{step1}步結(jié)束。) break return S_counts, I_counts, R_counts, G # 運行模擬 S_counts, I_counts, R_counts, G_final_sir simulate_sir_model(G.copy(), beta0.2, gamma0.05, initial_infected3, T80) # 可視化 plt.figure(figsize(12, 5)) steps list(range(len(S_counts))) plt.plot(steps, S_counts, b-, label易感者 (S), linewidth2) plt.plot(steps, I_counts, r-, label感染者 (I), linewidth2) plt.plot(steps, R_counts, g-, label移除者 (R), linewidth2) plt.xlabel(時間步) plt.ylabel(人數(shù)) plt.title(SIR模型傳播動力學(xué) (Beta0.2, Gamma0.05)) plt.legend() plt.grid(True, alpha0.3) plt.show()5.3 模擬實驗與現(xiàn)象觀察運行代碼后你會看到經(jīng)典的SIR曲線S藍色從高位逐漸下降I紅色先上升達到一個峰值然后下降至0R綠色從0開始最終累積到一個穩(wěn)定值?,F(xiàn)在我們來做個關(guān)鍵的實驗理解基本再生數(shù) R0的概念。在均勻混合的假設(shè)下R0 β / γ。它代表一個感染者在整個感染期內(nèi)平均能傳染多少個易感者。當(dāng) R0 1每個感染者平均能傳染超過1個人疫情會擴散I曲線會先上升。當(dāng) R0 1每個感染者平均傳染不到1個人疫情會逐漸消失I曲線單調(diào)下降。我們可以通過調(diào)整β和γ來驗證設(shè)置 R0 1例如beta0.3, gamma0.1 R03。運行模擬你會看到明顯的疫情爆發(fā)波形。設(shè)置 R0 1例如beta0.05, gamma0.1 R00.5。運行模擬你會發(fā)現(xiàn)I人數(shù)從開始就緩慢下降無法形成大規(guī)模傳播。實操心得在網(wǎng)絡(luò)模型中R0的計算比均勻混合模型復(fù)雜因為它還依賴于網(wǎng)絡(luò)的平均度連接數(shù)等拓撲性質(zhì)。一個近似的經(jīng)驗公式是 R0_network ≈ β / γ * 其中 是網(wǎng)絡(luò)的平均度。在我們的WS網(wǎng)絡(luò)n50, k4中平均度約為4。當(dāng)β/γ * 4 1時疫情更容易在網(wǎng)絡(luò)中持續(xù)傳播。你可以用這個經(jīng)驗去設(shè)計你的參數(shù)觀察現(xiàn)象。6. IC模型實現(xiàn)與影響力分析獨立級聯(lián)模型IC的模擬邏輯與前兩者有顯著區(qū)別它更關(guān)注離散的“嘗試”和“級聯(lián)”過程。6.1 離散級聯(lián)過程實現(xiàn)IC模型的核心是“輪次”和“僅新激活節(jié)點有傳播機會”。我們需要記錄每個節(jié)點是在哪一輪被激活的。def simulate_ic_model(G, p0.2, seedsNone, max_iter20): 在給定網(wǎng)絡(luò)G上模擬獨立級聯(lián)模型。 參數(shù): G: NetworkX圖。 p: 獨立激活概率。 seeds: 初始激活節(jié)點列表。如果為None則隨機選一個。 max_iter: 最大模擬輪次。 返回: active_nodes_by_round: 列表的列表記錄每一輪新激活的節(jié)點。 total_active: 列表記錄每一輪累計激活節(jié)點數(shù)。 G: 模擬結(jié)束后的網(wǎng)絡(luò)節(jié)點帶有‘a(chǎn)ctive_round’屬性-1表示未激活0表示激活輪次。 # 初始化所有節(jié)點未激活輪次標(biāo)記為-1 nx.set_node_attributes(G, -1, active_round) if seeds is None: seeds [np.random.choice(list(G.nodes()))] # 第0輪激活種子節(jié)點 round 0 newly_active seeds for node in newly_active: G.nodes[node][active_round] round # 記錄每一輪新激活的節(jié)點和累計激活數(shù) active_nodes_by_round [newly_active.copy()] total_active [len(newly_active)] # 開始級聯(lián) while round max_iter and newly_active: round 1 current_newly_active [] # 遍歷上一輪新激活的節(jié)點 for node in newly_active: # 遍歷其未激活的鄰居 for neighbor in G.neighbors(node): if G.nodes[neighbor][active_round] -1: # 未激活 # 以概率p嘗試激活每個鄰居只有一次被該節(jié)點嘗試的機會 if np.random.rand() p: current_newly_active.append(neighbor) # 去重一個節(jié)點可能被多個鄰居在同一輪嘗試激活 current_newly_active list(set(current_newly_active)) # 激活本輪成功的節(jié)點 for node in current_newly_active: G.nodes[node][active_round] round active_nodes_by_round.append(current_newly_active) total_active.append(total_active[-1] len(current_newly_active)) # 更新newly_active為當(dāng)前輪新激活的節(jié)點用于下一輪 newly_active current_newly_active # 如果提前結(jié)束補全記錄 while len(total_active) max_iter: active_nodes_by_round.append([]) total_active.append(total_active[-1]) return active_nodes_by_round, total_active, G # 運行模擬 seeds [0, 5] # 選擇節(jié)點0和5作為種子 active_rounds, total_active, G_final_ic simulate_ic_model(G.copy(), p0.15, seedsseeds, max_iter10) # 可視化激活過程 plt.figure(figsize(10, 4)) # 繪制累計激活曲線 plt.subplot(1, 2, 1) rounds list(range(len(total_active))) plt.plot(rounds, total_active, bo-, linewidth2, markersize6) plt.xlabel(傳播輪次) plt.ylabel(累計激活節(jié)點數(shù)) plt.title(IC模型累計激活曲線 (p0.15)) plt.grid(True, alpha0.3) # 繪制最終網(wǎng)絡(luò)狀態(tài)按激活輪次著色 plt.subplot(1, 2, 2) # 為不同輪次分配顏色 cmap plt.cm.viridis node_colors [] for n in G_final_ic.nodes(): r G_final_ic.nodes[n][active_round] if r -1: node_colors.append(lightgray) # 未激活 else: # 激活輪次越早顏色越深這里用輪次歸一化 node_colors.append(cmap(r / max(1, max(active_rounds)))) nx.draw(G_final_ic, pos, node_colornode_colors, node_size200, with_labelsFalse, edge_colorgray) plt.title(IC模型激活狀態(tài)顏色深淺代表激活輪次) plt.tight_layout() plt.show()6.2 種子節(jié)點選擇策略初探IC模型常用來研究“影響力最大化”給定一個預(yù)算k只能選k個種子節(jié)點如何選擇能使最終激活的節(jié)點總數(shù)最多這是一個NP難問題但有高效的啟發(fā)式算法。最著名的是貪心算法其核心思想是迭代地選擇能帶來最大邊際收益的節(jié)點。我們可以實現(xiàn)一個簡單的模擬貪心算法來感受一下初始化一個空種子集S。對于每一個不在S中的節(jié)點v計算如果將v加入S運行多次IC模擬后的平均激活節(jié)點數(shù)即邊際增益。選擇邊際增益最大的節(jié)點加入S。重復(fù)步驟2-3直到S包含k個節(jié)點。由于每次模擬都有隨機性我們需要對每個候選節(jié)點進行多次模擬比如100次取平均以獲得穩(wěn)定的收益估計。這個算法計算量很大O(knR*模擬時間)但對于理解思想足夠了。在實際研究中會使用更高效的算法如CELFCost-Effective Lazy Forward來優(yōu)化。def greedy_influence_maximization(G, k3, p0.1, iterations100): 一個簡單低效的貪心算法用于影響力最大化。 注意此函數(shù)僅用于演示原理在大網(wǎng)絡(luò)上效率極低。 seeds [] all_nodes set(G.nodes()) for i in range(k): print(f選擇第 {i1} 個種子...) best_node None best_influence -1 # 遍歷所有尚未被選為種子的節(jié)點 candidates all_nodes - set(seeds) for node in candidates: # 計算當(dāng)前種子集 候選節(jié)點 的影響力 current_seeds seeds [node] total_spread 0 # 多次模擬取平均 for _ in range(iterations): _, total_active, _ simulate_ic_model(G.copy(), pp, seedscurrent_seeds, max_iter20) total_spread total_active[-1] # 取最終激活數(shù) avg_spread total_spread / iterations # 計算邊際增益可選這里直接用總影響力 if avg_spread best_influence: best_influence avg_spread best_node node if best_node is not None: seeds.append(best_node) print(f 選中節(jié)點 {best_node}, 預(yù)估影響力 {best_influence:.1f}) return seeds # 注意在小網(wǎng)絡(luò)上運行因為計算量很大 small_G create_social_network(n30, k4, p0.1) selected_seeds greedy_influence_maximization(small_G, k3, p0.15, iterations50) print(f貪心算法選出的種子節(jié)點: {selected_seeds})運行這個代碼可能需要一點時間。它會輸出算法依次選擇的種子節(jié)點。你可以對比一下隨機選擇3個節(jié)點作為種子和用這個貪心算法選出的3個節(jié)點分別運行IC模型最終的激活規(guī)模是否有顯著差異。通常貪心算法會選擇那些處于網(wǎng)絡(luò)中心位置比如度中心性高、介數(shù)中心性高的節(jié)點。7. 模型對比、應(yīng)用場景與常見問題7.1 三大模型核心對比為了更清晰地理解這三個模型的區(qū)別和適用場景我整理了一個對比表格特性維度SI模型SIR模型IC模型核心狀態(tài)S易感 I感染S易感 I感染 R移除Active活躍 Inactive非活躍狀態(tài)轉(zhuǎn)換S → IS → I → RInactive → Active (一次性)關(guān)鍵參數(shù)β感染概率β感染概率 γ恢復(fù)概率p激活概率傳播機制感染者持續(xù)嘗試感染易感鄰居感染者以β感染以γ恢復(fù)新激活節(jié)點有一次機會以p激活鄰居時間視角連續(xù)/離散時間均可通常為連續(xù)時間微分方程離散近似也可離散輪次最終結(jié)局所有人感染 (I → N)部分人感染后移除穩(wěn)定在S, I0, R級聯(lián)停止部分人激活典型應(yīng)用理論教學(xué) 簡單擴散初期模擬傳染病研究 短期熱點信息傳播社交影響力最大化 口碑營銷 信息級聯(lián)7.2 模型選擇與場景適配在實際項目中選擇哪個模型取決于你要分析的具體問題如果你想研究一個長期存在的觀念或技術(shù)的普及過程并且假設(shè)人們一旦接受就不會“反悔”那么SI模型是一個簡化的起點。如果你想分析一次疫情爆發(fā)、一個短期熱點話題如爆款短視頻的傳播生命周期SIR模型是最佳選擇。你可以通過擬合真實數(shù)據(jù)如每日新增話題量來反推β和γ參數(shù)。如果你的目標(biāo)是做營銷策劃比如尋找最合適的“KOC”進行產(chǎn)品投放以最大化曝光那么IC模型及其相關(guān)的影響力最大化算法就是你的核心工具。你需要收集或構(gòu)建用戶間的社交關(guān)系圖關(guān)注、好友關(guān)系。7.3 常見問題與調(diào)試技巧在實現(xiàn)和運行這些模型時你可能會遇到以下典型問題傳播速度過快或過慢不符合預(yù)期檢查概率參數(shù)β、γ、p的值通常很小。在真實社交網(wǎng)絡(luò)中單次接觸的傳播概率很少超過0.1??梢詮?.01、0.05這樣的小值開始嘗試。檢查網(wǎng)絡(luò)密度用nx.density(G)查看你的網(wǎng)絡(luò)密度。一個完全圖所有節(jié)點兩兩相連的傳播速度會極快。WS小世界網(wǎng)絡(luò)的密度約為k/(n-1)相對稀疏。檢查初始感染者位置隨機選擇可能選到邊緣節(jié)點。嘗試固定選擇網(wǎng)絡(luò)中度數(shù)最高的節(jié)點作為初始感染者觀察傳播速度的變化。模擬結(jié)果波動很大每次運行都不一樣這是正常的因為感染/激活是概率事件。為了得到穩(wěn)定結(jié)論必須進行多次模擬取平均。例如對同一組參數(shù)和初始條件運行100次模擬然后繪制平均曲線和置信區(qū)間。def run_multiple_simulations(model_func, G, params, times100): results [] for _ in range(times): # 注意每次模擬要使用網(wǎng)絡(luò)的副本避免狀態(tài)污染 G_copy G.copy() result model_func(G_copy, **params) results.append(result) return results # 然后對results列表中的數(shù)據(jù)如最終的感染人數(shù)進行統(tǒng)計分析代碼運行太慢特別是對于大網(wǎng)絡(luò)或IC的貪心算法向量化操作在SI/SIR模型中遍歷所有感染者的鄰居是主要開銷。對于大型網(wǎng)絡(luò)可以考慮使用鄰接矩陣?yán)肗umPy的矩陣運算進行概率判斷但這會消耗更多內(nèi)存。減少模擬次數(shù)在調(diào)試階段減少網(wǎng)絡(luò)規(guī)模(n)、模擬步數(shù)(T)和重復(fù)次數(shù)(iterations)。使用更高效的算法庫對于真正的研究可以考慮使用專門優(yōu)化過的庫如NDlibNetwork Diffusion Library。如何將模型應(yīng)用到真實數(shù)據(jù)數(shù)據(jù)獲取真實的社交網(wǎng)絡(luò)數(shù)據(jù)可能來自API如Twitter, Weibo的粉絲關(guān)系、合作方脫敏數(shù)據(jù)、或公開數(shù)據(jù)集如Stanford Large Network Dataset Collection。網(wǎng)絡(luò)構(gòu)建將用戶視為節(jié)點關(guān)注/好友關(guān)系視為邊構(gòu)建有向或無向圖。參數(shù)估計這是最難也是最關(guān)鍵的一步。可以通過歷史數(shù)據(jù)如過去話題的傳播軌跡來擬合模型參數(shù)如β, γ。常用方法有極大似然估計MLE或基于模擬的方法如Approximate Bayesian Computation。模型驗證用一部分?jǐn)?shù)據(jù)訓(xùn)練集估計參數(shù)在另一部分?jǐn)?shù)據(jù)測試集上預(yù)測傳播范圍比較預(yù)測值與真實值的差異。實現(xiàn)這三個模型只是第一步它們像積木一樣可以組合、擴展成更復(fù)雜的模型如SIS感染后可再次易感、SEIR增加潛伏期、LT模型線性閾值模型等。理解這些基礎(chǔ)模型的每一個細節(jié)能讓你在面對更復(fù)雜的傳播現(xiàn)象時擁有拆解和建模的能力。