【CS224W】(task4/5)图嵌入表示学习(Deepwalk、Node2vec)更新中

本文是图表示学习综述,介绍了图嵌入表示学习的基本框架和编码器 - 解码器架构,衍生出DeepWalk、Node2Vec等基于随机游走的图嵌入方法。还讨论了嵌入整张图的方法,以及Node2vec在同质性和结构性间的权衡,最后给出代码实战和思考题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

note

  • Node embedding的基本框架:encoder+decoder

    • 最简单encoder是embedding-lookup表: E N C ( v ) = z v = Z ⋅ v \mathrm{ENC}(v)=z_v=Z \cdot v ENC(v)=zv=Zv
    • decoder是基于节点的相似度
    • 目标:maximize z v T z u \mathbf{z}_v^{\mathrm{T}} \mathbf{z}_u zvTzu(其中u和v是相似的节点对)
    • 注:Node2vec在节点分类方面表现更好;而其他方法在链路预测上效果更好,如random walk效率更高;
  • Node2vec:调整随机游走跳转概率,让 Graph Embedding 的结果在网络的同质性(Homophily)和结构性(Structural Equivalence)中进行权衡,其中BFS表达结构性,DFS表达同质性。

    • 计算随机游走概率
    • 从节点 u u u开始模拟 r r r条长度为 l l l的游走链路
    • 使用 Stochastic Gradient Descent 优化node2vec损失函数
  • graph embeddings:

    • 方法1:对应子图or图的节点embedding进行sum或avg计算;
    • 方法2:创建横跨子图的super-node
  • deepwalk:负采样后极大似然估计、利用Hierarchical Softmax二叉树加速。尽量增加采样次数,适当增加游走长度,来保证生成结果的稳定性和覆盖率。deepwalk矩阵分解形式的优化目标: log ⁡ ( vol ⁡ ( G ) ( 1 T ∑ r = 1 T ( D − 1 A ) r ) D − 1 ) − log ⁡ b \log \left(\operatorname{vol}(G)\left(\frac{1}{T} \sum_{r=1}^T\left(D^{-1} A\right)^r\right) D^{-1}\right)-\log b log(vol(G)(T1r=1T(D1A)r)D1)logb
    在这里插入图片描述

  • 经典的w2v也是使用skip-gram算法:w2v的研究中提出的模型结构、目标函数、负采样方法、负采样中的目标函数在后续研究重复使用和优化。所谓的预测,取周围词随机初始化的embedding,进行平均池化后与中心词embedding进行点积,进行softmax多分类的预测任务,类别是所有的候选词(可以通过负采样优化),然后反向传播更新周围词语中心词的embedding,通过不断迭代得到最终的每个词向量。

一、Node embedding: Encoder + Decoder

本讲是图表示学习综述,介绍了图嵌入(节点嵌入)表示学习的基本框架和编码器-解码器架构,将节点嵌入映射为低维、连续、稠密向量。向量空间的相似度反映了对应节点在原图上的相似度。在同一个随机游走序列中共同出现的节点,视为相似节点,从而构建类似Word2Vec的自监督学习场景。衍生出DeepWalk、Node2Vec等基于随机游走的图嵌入方法。

从数学上,随机游走方法和矩阵分解是等价的。

进而讨论嵌入整张图的方法,可以通过所有节点嵌入向量聚合、引入虚拟节点、匿名随机游走等方法实现。

在这里插入图片描述

  • embedding编码网络中的信息,可用于下游任务, 图表示学习使得省去特征工程。
    在这里插入图片描述
  • G G G,节点集 V V V,邻接矩阵 A A A(二维,这里化简,不考虑节点的特征等信息)
  • node embedding:将节点信息编码为space中的embedding,使得embedding的相似度计算(如cos点积计算等)近似于节点之间真实的相似度

在这里插入图片描述

1.1 embedding-lookup

  • 注意两点:
    在这里插入图片描述
  • shallow encoding:encoder仅为embedding-lookup表 ENC ⁡ ( v ) = z v = Z ⋅ v \operatorname{ENC}(v)=\mathbf{z}_v=\mathbf{Z} \cdot v ENC(v)=zv=Zv
    • Z ∈ R d × ∣ V ∣ \mathbf{Z} \in \mathbb{R}^{d \times|\mathcal{V}|} ZRd×V矩阵中,每列是对应的节点的embedding
    • v ∈ I ∣ V ∣ v \in \mathbb{I}^{|\mathcal{V}|} vIV 是单位矩阵
    • 方法:deepwalk、node2vec等
    • goal:对于相似节点(u, v),优化参数,使得 similarity ⁡ ( u , v ) ≈ z v T z u \operatorname{similarity}(u, v) \approx \mathbf{z}_v^{\mathrm{T}} \mathbf{z}_u similarity(u,v)zvTzu

1.2 节点相似的定义

  • 有边
  • 共享邻居
  • 有相似的structural roles
  • 随机游走random walk定义的节点相似度

1.3 unsupervised/self-supervised

无监督or自监督学习:不使用节点的标签和特征,直接得到节点的度量(如embedding)

二、Random Walk

2.1 notation

  • z u \mathbf{z}_u zu:我们想学习到的节点u embedding
  • P ( v ∣ z u ) P\left(v \mid \mathbf{z}_u\right) P(vzu),条件概率:已知节点u embedding,基于random walk的要访问节点v的概率
  • 通过非线性函数得到预测概率:
    • softmax函数将数据归一化为和为1的结果: σ ( z ) [ i ] = e z [ i ] ∑ j = 1 K e z [ j ] \sigma(\mathbf{z})[i]=\frac{e^{z[i]}}{\sum_{j=1}^K e^{z[j]}} σ(z)[i]=j=1Kez[j]ez[i]
    • sigmoid函数:转为(0, 1)范围内,公式为 S ( x ) = 1 1 + e − x S(x)=\frac{1}{1+e^{-x}} S(x)=1+ex1

2.2 Algorithm:DeepWalk

  • 采样得到多个随机游走序列,计算条件概率 P ( v ∣ z u ) P\left(v \mid \mathbf{z}_u\right) P(vzu),优化参数使得 similarity ⁡ ( u , v ) ≈ z v T z u \operatorname{similarity}(u, v) \approx \mathbf{z}_v^{\mathrm{T}} \mathbf{z}_u similarity(u,v)zvTzu,其中前者是真实中两个节点u和v的相似程度,后者是两个节点对应embedding的点积,近似于u和v节点共现在同一随机游走链路中的概率。
  • 优化embedding的log-likelihood目标函数(极大似然估计)如下,其中 N R ( u ) N_R(u) NR(u) 表示从 u u u 节点出发的随机游走序列的所有邻域节点:
    max ⁡ f ∑ u ∈ V log ⁡ P ( N R ( u ) ∣ z u ) \max _f \sum_{u \in V} \log \mathrm{P}\left(N_{\mathrm{R}}(u) \mid \mathbf{z}_u\right) fmaxuVlogP(NR(u)zu)

在这里插入图片描述

算法由两部分组成:

  • (1)随机游走序列生成器;
  • (2)向量更新。

随机游走:对图G均匀地随机采样一个节点 v i v_i vi,并作为random walk的根结点 W v i W_{v_{i}} Wvi,然后一直向周围邻居采样,直到达到最大路径长度 t t t
随机游走的长度没有限制,但是在实验中设置最大步长是固定的。
在这里插入图片描述

  • 输出:一个顶点表示矩阵 Φ \Phi Φ,大小为 ∣ V ∣ × d |V|\times d V×d
  • 第二行:构建Hierarchical Softmax
  • 第三行:对每个节点做 γ \gamma γ次随机游走
  • 第四行:打乱网络中的节点
  • 第五行:以每个节点为根结点生成长度为 t t t的随机游走
  • 第七行:根据生成的随机游走使用skip-gram模型利用梯度的方法对参数进行更新。

其中SkipGram参数更新的细节如下:
在这里插入图片描述

(1)SkipGram

SkipGram参数更新的细节如下:
在这里插入图片描述
SkipGram算法是语言模型中,最大化窗口 w w w中出现的词的概率的方法(梯度下降),外层for循环是对这个序列中的每个词进行操作,内层for循环是对每个词的窗口大小为 w w w的词序列进行操作。具体操作是用一个似然函数 J ( Φ ) J(\Phi) J(Φ)表示 Φ \Phi Φ,通过梯度下降(对 J ( Φ ) J(\Phi) J(Φ)求导)更新参数( α \alpha α是学习速率)。

从词向量学习的角度看,基于神经网络语言模型的预训练方法存在缺点:当对t时刻词进行预测时,模型只利用了历史词序列作为输入,而损失了与“未来”上下文之间的共现信息。于是大佬们提出更强的词向量预训练模型Word2Vec,其中包括CBOW(Continuous Bag-of-Words)模型以及Skip-gram模型。

(2)Hierarchical Softmax

在这里插入图片描述
在计算 Pr ⁡ ( u k ∣ Φ ( v i ) ) \Pr(u_k|\Phi(v_i)) Pr(uk∣Φ(vi)) 时,可以利用Hierarchical Softmax二叉树加速。作者将所有节点作为二叉树的叶子节点,就可以用从根节点到叶子节点的路径来表示每个节点。二叉树若有 ∣ V ∣ |V| V个叶子节点,则深度至多为 log ⁡ ∣ V ∣ \log|V| logV。这样就会有:
Pr ⁡ ( u k ∣ Φ ( v j ) ) = ∏ l = 1 ⌈ log ⁡ ∣ V ∣ ⌉ Pr ⁡ ( b l ∣ Φ ( v j ) ) \Pr(u_k|\Phi(v_j))=\prod_{l=1}^{\lceil\log|V|\rceil}\Pr(b_l|\Phi(v_j)) Pr(uk∣Φ(vj))=l=1logVPr(bl∣Φ(vj))其中 b 0 , b 1 , . . . , b ⌈ log ⁡ ∣ V ∣ ⌉ b_0, b_1, ..., b_{\lceil\log|V|\rceil} b0,b1,...,blogV是一系列二叉树中的非叶子节点。这样就可以用较少的分类器完成这个任务,将计算复杂度由 O ( ∣ V ∣ ) O(|V|) O(V)降低至 O ( log ⁡ ∣ V ∣ ) O(\log|V|) O(logV)。更进一步,还可以结合节点出现频率,使用霍夫曼编码,为更频繁出现的节点分配稍短的路径,再次降低计算复杂度。

(3)Optimization和负采样优化

模型参数集是 { Φ , T } \{\Phi, T\} {Φ,T},使用随机梯度下降算法 S G D SGD SGD(一次训练一个样本)进行优化参数。通过方向传播计算损失函数关于参数的偏导数,SGD的学习率初始设置为2.5%,然后随着训练过程中看到的顶点数量的增加而线性减少。

  • 目标:使对每个节点 u , N R ( u ) u, N_R(u) u,NR(u) 的节点和 z u z_u zu 靠近, 即 P ( N R ( u ) ∣ z u ) P\left(N_R(u) \mid z_u\right) P(NR(u)zu) 值大。
  • f : u → R d : f ( u ) = z u \mathrm{f}: \mathrm{u} \rightarrow \mathbb{R}^{\mathrm{d}}: \mathrm{f}(\mathrm{u})=\mathbf{z}_{\mathrm{u}} f:uRd:f(u)=zu
  • 优化embedding的log-likelihood目标函数:
    max ⁡ f ∑ u ∈ V log ⁡ P ( N R ( u ) ∣ z u ) \max _f \sum_{u \in V} \log \mathrm{P}\left(N_{\mathrm{R}}(u) \mid \mathbf{z}_u\right) fmaxuVlogP(NR(u)zu)
    在这里插入图片描述
    【负采样优化】
    但是由于求解上面目标函数的时间复杂度很高,需要 O ( ∣ V ∣ 2 ) \mathrm{O}\left(|\mathrm{V}|^2\right) O(V2),可以通过负采样优化该公式的分母,即不用所有节点作为归一化的负样本。
    在这里插入图片描述

负样本个数k的考虑因素:

  • 更高的k会使估计结果更鲁棒robust
  • 更高的k会使负样本上的偏差bias更高
  • 实践上的k常用值:5-20

2.3 代码实战

# DiGraph with 100 nodes and 4961 edges
import networkx as nx
import numpy as np
from tqdm import tqdm
from gensim.models import word2vec

def walkOneTime(g, start_node, walk_length):
    walk = [str(start_node)]  # 初始化游走序列
    for _ in range(walk_length):  # 最大长度范围内进行采样
        current_node = int(walk[-1])
        successors = list(g.successors(current_node)) # graph.successor: 获取当前节点的后继邻居
        if len(successors) > 0:
            next_node = np.random.choice(successors, 1)
            walk.extend([str(n) for n in next_node])
        else:
            break
    return walk

def getDeepwalkSeqs(g, walk_length, num_walks):
    seqs=[]
    for _ in tqdm(range(num_walks)):
        start_node = np.random.choice(g.nodes)
        w = walkOneTime(g,start_node, walk_length)
        seqs.append(w)
    return seqs

def deepwalk( g, dimensions = 10, walk_length = 80, num_walks = 10, min_count = 3 ):
    seqs = getDeepwalkSeqs(g, walk_length = walk_length, num_walks = num_walks)
    model = word2vec.Word2Vec(seqs, vector_size = dimensions, min_count = min_count)
    return model

if __name__ == '__main__':
	#快速随机生成一个有向图
    g = nx.fast_gnp_random_graph(n = 100, p = 0.5,directed = True) 
    model = deepwalk( g, dimensions = 10, walk_length = 20, num_walks = 100, min_count = 3 )
    # 观察与节点2最相近的三个节点
    print(model.wv.most_similar('2',topn=3))
    # 可以把emd储存下来以便下游任务使用
    model.wv.save_word2vec_format('e.emd')
    # 可以把模型储存下来以便下游任务使用
    model.save('m.model')
  • 先利用networkx随机生成二项式有向图(如下图所示)
  • walk_length是每条random walk链路的长度,共有num_walks条链路,通过随机游走得到的seqs送入到gensim.models.word2vec中训练w2v,保存训练得到的embedding和模型m.model
  • 得到与节点2最接近的3个节点:[('77', 0.8721016049385071), ('65', 0.8555149435997009), ('66', 0.8495140671730042)]

在这里插入图片描述

2.4 小结

在这里插入图片描述

三、在同质性和结构性间权衡:Node2vec

3.1 同质性和结构性

网络的“同质性”指的是距离相近节点的 Embedding 应该尽量近似,如图 3 所示,节点 u 与其相连的节点 s1、s2、s3、s4的 Embedding 表达应该是接近的,这就是网络“同质性”的体现。在电商网站中,同质性的物品很可能是同品类、同属性,或者经常被一同购买的物品。

结构性”指的是结构上相似的节点的 Embedding 应该尽量接近,比如图 3 中节点 u 和节点 s6都是各自局域网络的中心节点,它们在结构上相似,所以它们的 Embedding 表达也应该近似,这就是“结构性”的体现。在电商网站中,结构性相似的物品一般是各品类的爆款、最佳凑单商品等拥有类似趋势或者结构性属性的物品。
在这里插入图片描述

图3 网络的BFS和DFS示意图

在这里插入图片描述

3.2 如何表达结构性和同质性

Graph Embedding 的结果究竟是怎么表达结构性和同质性的呢?

  • 首先,为了使 Graph Embedding 的结果能够表达网络的“结构性”,在随机游走的过程中,我们需要让游走的过程更倾向于 BFS(Breadth First Search,宽度优先搜索),因为 BFS 会更多地在当前节点的邻域中进行游走遍历,相当于对当前节点周边的网络结构进行一次“微观扫描”。(当前节点是“局部中心节点”,还是“边缘节点”,亦或是“连接性节点”,其生成的序列包含的节点数量和顺序必然是不同的,从而让最终的 Embedding 抓取到更多结构性信息。)

  • 而为了表达“同质性”,随机游走要更倾向于 DFS(Depth First Search,深度优先搜索)才行,因为 DFS 更有可能通过多次跳转,游走到远方的节点上。但无论怎样,DFS 的游走更大概率会在一个大的集团内部进行,这就使得一个集团或者社区内部节点的 Embedding 更为相似,从而更多地表达网络的“同质性”。

在这里插入图片描述
Node2vec 算法控制 BFS 和 DFS 的倾向性:

  • 通过节点间的跳转概率来控制跳转的倾向性。图 4 所示为 Node2vec 算法从节点 t 跳转到节点 v 后,再从节点 v 跳转到周围各点的跳转概率。这里,你要注意这几个节点的特点。比如,节点 t 是随机游走上一步访问的节点,节点 v 是当前访问的节点,节点 x1、x2、x3是与 v 相连的非 t 节点,但节点 x1还与节点 t 相连,这些不同的特点决定了随机游走时下一次跳转的概率。

在这里插入图片描述

图4 Node2vec的跳转概率

这些概率还可以用具体的公式来表示,从当前节点 v 跳转到下一个节点 x 的概率 π v x = α p q ( t , x ) ⋅ ω v x \pi_{v x}=\alpha_{p q}(t, x) \cdot \omega_{v x} πvx=αpq(t,x)ωvx其中 Wvx 是边 vx 的原始权重, α p q ( t , x ) \alpha_{p q}(t, x) αpq(t,x) 是 Node2vec 定义的一个跳转权重。到底是倾向于 DFS 还是 BFS,主要就与这个跳转权重的定义有关了:

α p q ( t , x ) = { 1 p  如果  d t x = 0 1  如果  d t x = 1 1 q  如果  d t x = 2 \alpha_{p q(t, x)=} \begin{cases}\frac{1}{p} & \text { 如果 } d_{t x}=0 \\ 1 & \text { 如果 } d_{t x}=1 \\ \frac{1}{q} & \text { 如果 } d_{t x}=2\end{cases} αpq(t,x)= p11q1 如果 dtx=0 如果 dtx=1 如果 dtx=2

α p q ( t , x ) \alpha_{p q}(t, x) αpq(t,x)里的 d t x d_{tx} dtx 是指节点 t 到节点 x 的距离,比如节点 x1其实是与节点 t 直接相连的,所以这个距离 d t x d_{tx} dtx就是 1,节点 t 到节点 t 自己的距离 d t t d_{tt} dtt就是 0,而 x2、x3这些不与 t 相连的节点, d t x d_{tx} dtx就是 2。

此外, α p q ( t , x ) \alpha_{p q}(t, x) αpq(t,x) 中的参数 p 和 q 共同控制着随机游走的倾向性。参数 p 被称为返回参数(Return Parameter),p 越小,随机游走回节点 t 的可能性越大,Node2vec 就更注重表达网络的结构性。参数 q 被称为进出参数(In-out Parameter),q 越小,随机游走到远方节点的可能性越大,Node2vec 更注重表达网络的同质性。

反之,当前节点更可能在附近节点游走。可以自己尝试给 p 和 q 设置不同大小的值,算一算从 v 跳转到 t、x1、x2和 x3的跳转概率。这样应该就能理解刚才所说的随机游走倾向性的问题啦。

3.3 实验证实+代码例子

Node2vec 这种灵活表达同质性和结构性的特点也得到了实验的证实,可以通过调整 p 和 q 参数让它产生不同的 Embedding 结果。

  • 图 5 上就是 Node2vec 更注重同质性的体现,从中可以看到,距离相近的节点颜色更为接近;
  • 图 5 下则是更注重结构性的体现,其中结构特点相近的节点的颜色更为接近。

在这里插入图片描述

图5 Node2vec实验结果

Node2vec 所体现的网络的同质性和结构性,在推荐系统中都是非常重要的特征表达。由于 Node2vec 的这种灵活性,以及发掘不同图特征的能力,可以把不同 Node2vec 生成的偏向“结构性”的 Embedding 结果,以及偏向“同质性”的 Embedding 结果共同输入后续深度学习网络,以保留物品的不同图特征信息。

【代码例子】通过Node2vec找到和节点2最接近的3个节点

import networkx as nx
from node2vec import Node2Vec

graph = nx.fast_gnp_random_graph(n=100, p=0.5)#快速随机生成一个无向图
node2vec = Node2Vec ( graph, dimensions=64, walk_length=30, num_walks=100, p=0.3,q=0.7,workers=4)#初始化模型
model = node2vec.fit()#训练模型
print(model.wv.most_similar('2',topn=3))# 观察与节点2最相近的三个节点
'''
[('43', 0.5867125988006592), ('41', 0.5798742175102234), ('33', 0.5246706008911133)]
'''

四、Embedding Entire Graphs

目标:得到子图或整图的embedding
在这里插入图片描述

  • 方法1:对对应区域的节点emb进行sum或avg操作
  • 方法2:使用一个”假节点“,学习该节点的emb,并且作为图emb;其中该虚拟接地那和子图or全图相连。
    • Li Y, Tarlow D, Brockschmidt M, et al. Gated graph sequence neural networks[J]. arXiv preprint arXiv:1511.05493, 2015.

五、代码实战

5.1 维基百科词条图嵌入可视化

# !/usr/bin/python
# -*- coding: utf-8 -*-
import networkx as nx # 图数据挖掘
from gensim.models import Word2Vec
import pandas as pd
import numpy as np
import random # 随机数
from tqdm import tqdm # 进度条
import matplotlib.pyplot as plt
from sklearn.decomposition import PCA
from sklearn.manifold import TSNE
# %matplotlib inline
plt.rcParams['font.sans-serif']=['SimHei']  # 用来正常显示中文标签
plt.rcParams['axes.unicode_minus']=False  # 用来正常显示负号

file_path = "/home/andy/torch_rechub_0830/CS224W_GNN/DeepWalk/DeepWalk代码实战-维基百科词条图嵌入可视化"
df = pd.read_csv("seealsology-data.tsv", sep = "\t")

# 1. 构图
G = nx.from_pandas_edgelist(df, "source", "target", edge_attr=True, create_using=nx.Graph())
# 可视化
# plt.figure(figsize=(15,14))
# nx.draw(G)
# plt.show()

# 2. 生成随机游走序列的函数
def get_randomwalk(node, path_length):
    '''
    输入起始节点和路径长度,生成随机游走节点序列
    '''

    random_walk = [node]

    for i in range(path_length - 1):
        # 汇总邻接节点
        temp = list(G.neighbors(node))
        temp = list(set(temp) - set(random_walk))
        if len(temp) == 0:
            break
        # 从邻接节点中随机选择下一个节点
        random_node = random.choice(temp)
        random_walk.append(random_node)
        node = random_node

    return random_walk

all_nodes = list(G.nodes())
# ex: 举例
# get_randomwalk('random forest', 5)

# 3. 正式生成所有随机游走序列
gamma = 10 # 每个节点作为起始点生成随机游走序列个数
walk_length = 5 # 随机游走序列最大长度
random_walks = []

for n in tqdm(all_nodes): # 遍历每个节点
    for i in range(gamma): # 每个节点作为起始点生成gamma个随机游走序列
        random_walks.append(get_randomwalk(n, walk_length))

# 4. train过程
model = Word2Vec(vector_size=256, # Embedding维数
                 window=4, # 窗口宽度
                 sg=1, # Skip-Gram
                 hs=0, # 不加分层softmax
                 negative=10, # 负采样
                 alpha=0.03,  # 初始学习率
                 min_alpha=0.0007, # 最小学习率
                 seed=14 # 随机数种子
                )

# 用随机游走序列构建词汇表
model.build_vocab(random_walks, progress_per=2)

# 5. 正式train  # 训练(耗时1分钟左右)
model.train(random_walks, total_examples=model.corpus_count, epochs=50, report_delay=1)

# 6. 分析结果
# (1) 得到短语对应的embedding
example_node = model.wv.get_vector("random forest")
# (2) 得到当前短语最相近的embedding
like_node = model.wv.similar_by_word("random forest")

# 7. 处理所有embedding, 进行降维可视化
# (0) 可视化所有词条的embedding
X = model.wv.vectors
pca = PCA(n_components=2)
embed_2d = pca.fit_transform(X)
plt.figure(figsize=(14,14))
plt.scatter(embed_2d[:, 0], embed_2d[:, 1])
# 标题
name = "all 词条的 embedding"
plt.title(name, fontsize=30)
# 注意savefig需要在show之前运行
plt.savefig(name + '.jpg')
plt.show()


# (1) 可视化某个词条的二维embedding
term = 'computer vision'
term_256d = model.wv[term].reshape(1,-1)
# term_256d.shape # (1, 256)
term_2d = pca.transform(term_256d)
plt.figure(figsize=(14,14))
plt.scatter(embed_2d[:,0], embed_2d[:,1])
plt.scatter(term_2d[:,0],term_2d[:,1],c='r',s=200)
# 标题
name = term + " 词条的 embedding"
plt.title(name, fontsize=30)
# 注意savefig需要在show之前运行
plt.savefig(name + '.jpg')
plt.show()


# (2) 可视化某些词条的二维embedding(按照Pagerank重要度)
# 计算PageRank重要度
pagerank = nx.pagerank(G)
# 从高到低排序
node_importance = sorted(pagerank.items(), key=lambda x:x[1], reverse=True)
# 取最高的前n个节点
n = 30
terms_chosen = []
for each in node_importance[:n]:
    terms_chosen.append(each[0])
# 手动补充新节点
terms_chosen.extend(['computer vision','deep learning','convolutional neural network','convolution','natural-language processing','attention (machine learning)','support-vector machine','decision tree','random forest','computational imaging','machine vision','cognitive science','neuroscience','psychophysics','brain','visual cortex','visual neuroscience','cognitive model','finite difference','finite difference time domain','finite difference coefficients','finite difference methods for option pricing','iso 128','iso 10303'])
# 输入词条,输出词典中的索引号
term2index = model.wv.key_to_index
# 可视化全部词条和关键词条的二维Embedding
plt.figure(figsize=(14,14))
plt.scatter(embed_2d[:,0], embed_2d[:,1])
for item in terms_chosen:
    idx = term2index[item]
    plt.scatter(embed_2d[idx,0], embed_2d[idx,1],c='r',s=50)
    plt.annotate(item, xy=(embed_2d[idx,0], embed_2d[idx,1]),c='k',fontsize=12)
# 标题
name = " 按照Pagerank重要度排序的词条的 embedding"
plt.title(name, fontsize=30)
# 注意savefig需要在show之前运行
plt.savefig(name + '.jpg')
plt.show()


# (3) TSNE降维可视化
# 将Embedding用TSNE降维到2维
tsne = TSNE(n_components=2, n_iter=1000)
embed_2d = tsne.fit_transform(X)
plt.figure(figsize=(14,14))
plt.scatter(embed_2d[:, 0], embed_2d[:, 1])
# 标题
name = "通过TSNE降维二维的embedding"
plt.title(name, fontsize=30)
# 注意savefig需要在show之前运行
plt.savefig(name + '.jpg')
plt.show()


# (4)可视化全部词条和关键词条的二维Embedding
plt.figure(figsize=(14,14))
plt.scatter(embed_2d[:,0], embed_2d[:,1])

for item in terms_chosen:
    idx = term2index[item]
    plt.scatter(embed_2d[idx,0], embed_2d[idx,1],c='r',s=50)
    plt.annotate(item, xy=(embed_2d[idx,0], embed_2d[idx,1]),c='k',fontsize=12)
plt.show()


# 导出TSNE降维到二维之后的Embedding
terms_chosen_mask = np.zeros(X.shape[0])
for item in terms_chosen:
    idx = term2index[item]
    terms_chosen_mask[idx] = 1

df = pd.DataFrame()
df['X'] = embed_2d[:,0]
df['Y'] = embed_2d[:,1]
df['item'] = model.wv.index_to_key
df['pagerank'] = pagerank.values()
df['chosen'] = terms_chosen_mask
df.to_csv('tsne_vis_2d.csv',index=False)


# (5)可视化全部词条的三维Embedding
# 将Embedding用TSNE降维到2维
tsne = TSNE(n_components=3, n_iter=1000)
embed_3d = tsne.fit_transform(X)

### 导出TSNE降维到三维之后的Embedding
df = pd.DataFrame()
df['X'] = embed_3d[:,0]
df['Y'] = embed_3d[:,1]
df['Z'] = embed_3d[:,2]
df['item'] = model.wv.index_to_key
df['pagerank'] = pagerank.values()
df['chosen'] = terms_chosen_mask
df.to_csv('tsne_vis_3d.csv',index=False)
print("===============test===================") 

在这里插入图片描述

5.2 《悲惨世界》人物图嵌入

# !/usr/bin/python
# -*- coding: utf-8 -*-
import networkx as nx # 图数据挖掘
import numpy as np # 数据分析
import random # 随机数
import matplotlib.pyplot as plt
from node2vec import Node2Vec
from sklearn.cluster import KMeans
import numpy as np
# from node2vec.edges import HadamardEmbedder
from node2vec.edges import HadamardEmbedder
from sklearn.decomposition import PCA
plt.rcParams['font.sans-serif']=['SimHei']  # 用来正常显示中文标签
plt.rcParams['axes.unicode_minus']=False  # 用来正常显示负号

# 《悲惨世界》人物数据集
G = nx.les_miserables_graph()

# 可视化
plt.figure(figsize=(15,14))
pos = nx.spring_layout(G, seed=5)
# nx.draw(G, pos, with_labels=True)   # 版本问题 可能这句不行 可换下句
nx.draw_networkx(G, pos, with_labels = True)
name = "character picture"
plt.title(name, fontsize=30)
plt.savefig("悲惨世界人物可视化.jpg")
plt.show()

# 一、 Node2vec模型训练
# 设置node2vec参数
node2vec = Node2Vec(G,
                    dimensions=32,  # 嵌入维度
                    p=1,            # 回家参数
                    q=3,          # 外出参数
                    walk_length=10, # 随机游走最大长度
                    num_walks=600,  # 每个节点作为起始节点生成的随机游走个数
                    workers=4       # 并行线程数
                   )

# p=1, q=0.5, n_clusters=6。DFS深度优先搜索,挖掘同质社群
# p=1, q=2, n_clusters=3。BFS宽度优先搜索,挖掘节点的结构功能。

# 训练Node2Vec,参数文档见 gensim.models.Word2Vec
model = node2vec.fit(window=3,    # Skip-Gram窗口大小
                     min_count=1,  # 忽略出现次数低于此阈值的节点(词)
                     batch_words=4 # 每个线程处理的数据量
                    )
X = model.wv.vectors


# 二、 embedding聚类可视化
# # DBSCAN聚类
# from sklearn.cluster import DBSCAN
# cluster_labels = DBSCAN(eps=0.5, min_samples=6).fit(X).labels_
# print(cluster_labels)

# KMeans聚类
cluster_labels = KMeans(n_clusters=3).fit(X).labels_
print(cluster_labels)

# 三、将词汇表的节点顺序转为networkx的节点顺序
colors = []
nodes = list(G.nodes)
for node in nodes: # 按 networkx 的顺序遍历每个节点
    idx = model.wv.key_to_index[str(node)] # 获取这个节点在 embedding 中的索引号
    colors.append(cluster_labels[idx]) # 获取这个节点的聚类结果

# 四、 可视化聚类
plt.figure(figsize=(15,14))
pos = nx.spring_layout(G, seed=10)
# nx.draw(G, pos, node_color=colors, with_labels=True)
name = "node embedding cluster picture"
plt.title(name, fontsize=30)
nx.draw_networkx(G, pos, node_color=colors, with_labels=True)
plt.show()

# 五、embedding降维(PCA)
# 将Embedding用PCA降维到2维
pca = PCA(n_components=2)
embed_2d = pca.fit_transform(X)

# 将Embedding用TSNE降维到2维
# from sklearn.manifold import TSNE
# tsne = TSNE(n_components=2, n_iter=5000)
# embed_2d = tsne.fit_transform(X)
# plt.figure(figsize=(14,14))
plt.scatter(embed_2d[:, 0], embed_2d[:, 1])
name = "node embedding PCA picture"
plt.title(name, fontsize=30)
plt.show()

# 查看embedding
model.wv.get_vector('Napoleon').shape
# 查找 Napoleon 节点的相似节点
model.wv.most_similar('Napoleon')
# 查看任意两个节点的相似度
model.wv.similarity('Napoleon', 'Champtercier')

# 六、对Edge(连接)做Embedding
# Hadamard 二元操作符:两个 Embedding 对应元素相乘
edges_embs = HadamardEmbedder(keyed_vectors=model.wv)
# 查看 任意两个节点连接 的 Embedding
edges_embs[('Napoleon', 'Champtercier')]
# 计算所有 Edge 的 Embedding
edges_kv = edges_embs.as_keyed_vectors()
# 查看 关系与 某两个节点 最相似的 节点对
edges_kv.most_similar(str(('Bossuet', 'Valjean')))

print("=============test==============")

在这里插入图片描述
聚类结果:
在这里插入图片描述

附:思考题

机器学习中的“表示学习”是做什么的?为什么要做表示学习?

CS224W整门课程,都对哪些研究对象进行了嵌入编码的表示学习操作?

图嵌入有什么用?

图嵌入有哪几种技术方案?各有什么优劣?

如何理解图嵌入向量的“低维、连续、稠密”

如何衡量两个节点是否“相似”?

图嵌入中,Decoder为什么用两个向量的数量积?

如何理解图嵌入中的Shallow Encoder和Deep Encoder?有何区别?

随机游走序列包含了哪些信息?

图机器学习和自然语言处理存在怎样的对应关系?

简述DeepWalk算法原理

简述Node2Vec算法原理

除了DeepWalk和Node2Vec之外,还有哪些基于随机游走的图嵌入算法?

同济子豪兄论文精读视频中,DeepWalk和Node2Vec也留了不少思考题,去看看吧

你是否能想出更科学的随机游走策略?

基于随机游走的图嵌入方法,都可以被统一成什么样的数学形式?

重新思考:为什么要把图表示成矩阵的形式?

1. deepwalk

DeepWalk本质上是在解决什么问题?
DeepWalk和Word2Vec有哪些异同?
DeepWalk和Node2Vec有哪些异同?
DeepWalk的随机游走生成过程有哪些缺点?如何改进?
DeepWalk是否包含节点的类别和自身特征信息?
除了非标度网络和自然语言外,还有哪些分布服从幂律分布?
非标度网络和随机网络有什么区别?
如果两个节点隔得非常远,DeepWalk能否捕捉这样的关系?
DeepWalk为什么在稀疏标注场景下表现地更好?
除了对节点编码,DeepWalk能否对连接、子图、全图做编码?
随机游走的序列最大长度应设置为多少合适?
DeepWalk论文中对原生DeepWalk做了哪两种改进?
DeepWalk能否解决link prediction问题?为什么?

2. Node2vec

为什么DFS探索的是节点社群属性,BFS探索的是节点功能角色属性?
Node2Vec中的BFS和DFS,和大学计算机本科《数据结构与算法》课程中的BFS、DFS搜索有什么异同?
Node2Vec中的随机游走,是相对于上一个节点和当前节点,为什么不是相对于起始节点和当前节点?
论文实验结果中,为什么PPI蛋白质图数据集上,Node2Vec相比DeepWalk,性能没有显著提升?
连接如果带权重的话,如何影响有偏随机游走序列生成?
有向图和无向图,如何影响有偏随机游走序列生成?
为什么使用Alias Sampling?简述Alias Sampling的基本原理
Node2vec的BFS是否能用在分析自然语言中单词的角色(中枢单词、桥接单词)
除了DeepWalk和Node2Vec,还有哪些随机游走方法?
Node2Vec算法有哪些缺点?如何弥补?

附:时间安排

任务任务内容截止时间注意事项
2月11日开始
task1图机器学习导论2月14日周二完成
task2图的表示和特征工程2月15、16日周四完成
task3NetworkX工具包实践2月17、18日周六完成
task4图嵌入表示2月19、20日周一完成
task5deepwalk、Node2vec论文精读2月21、22、23、24日周五
task6PageRank2月25、26日周日
task7标签传播与节点分类2月27、28日周二
task8图神经网络基础3月1、2日周四
task9图神经网络的表示能力3月3日周五
task10图卷积神经网络GCN3月4日周六
task11图神经网络GraphSAGE3月5日周七
task12图神经网络GAT3月6日周一

Reference

[1] 传统图机器学习的特征工程-节点【斯坦福CS224W】
[2] cs224w(图机器学习)2021冬季课程学习笔记2: Traditional Methods for ML on Graphs
[3] NetworkX入门教程
[4] https://github.com/TommyZihao/zihao_course/tree/main/CS224W
[5] 斯坦福官方课程:https://web.stanford.edu/class/cs224w/
[6] 子豪兄github:https://github.com/TommyZihao/zihao_course
[7] 子豪:随机游走的艺术-图嵌入表示学习【斯坦福CS224W图机器学习】
[8] Graph Embedding-Node2vec总结
[9] nx.draw报错 ‘_AxesStack‘ object is not callable
[10] Embedding技术在推荐系统中的应用
[11] cs224w(图机器学习)2021冬季课程学习笔记3: Node Embeddings
[12] networkx官方文档:fast_gnp_random_graph
[13] 论文|Node2vec算法原理、代码实战和在wx朋友圈Lookalike的应用
[14] 关于Node2vec算法中Graph Embedding同质性和结构性的进一步探讨.王喆大佬

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

山顶夕景

小哥哥给我买个零食可好

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值