图机器学习(GML)&图神经网络(GNN)原理和代码实现(前置学习系列二)

Posted 汀、

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了图机器学习(GML)&图神经网络(GNN)原理和代码实现(前置学习系列二)相关的知识,希望对你有一定的参考价值。

项目链接:https://aistudio.baidu.com/aistudio/projectdetail/4990947?contributionType=1
欢迎fork欢迎三连!文章篇幅有限,部分程序出图不一一展示,详情进入项目链接即可

图机器学习(GML)&图神经网络(GNN)原理和代码实现(PGL)[前置学习系列二]

上一个项目对图相关基础知识进行了详细讲述,下面进图GML

networkx :NetworkX 是一个 Python 包,用于创建、操作和研究复杂网络的结构、动力学和功能
https://networkx.org/documentation/stable/reference/algorithms/index.html

import numpy as np
import random
import networkx as nx
from IPython.display import Image
import matplotlib.pyplot as plt
from sklearn.metrics import accuracy_score
from sklearn.metrics import roc_curve
from sklearn.metrics import roc_auc_score

1. 图机器学习GML

图学习的主要任务

图学习中包含三种主要的任务:

  • 链接预测(Link prediction)
  • 节点标记预测(Node labeling)
  • 图嵌入(Graph Embedding)

1.1链接预测(Link prediction)

在链接预测中,给定图G,我们的目标是预测新边。例如,当图未被完全观察时,或者当新客户加入平台(例如,新的LinkedIn用户)时,预测未来关系或缺失边是很有用的。

详细阐述一下就是:

GNN链接预测任务,即预测图中两个节点之间的边是否存在。在Social Recommendation,Knowledge Graph Completion等应用中都需要进行链接预测。模型实现上是将链接预测任务看成一个二分类任务:

  1. 将图中存在的边作为正样本;
  2. 负采样一些图中不存在的边作为负样本;
  3. 将正样例和负样例合并划分为训练集和测试集;
  4. 可以采用二分类模型的评估指标来评估模型的效果,

例如:AUC值在一些场景下例如大规模推荐系统或信息检索,模型需要评估top-k预测结果的准确性,因此对于链接预测任务还需要一些其他的评估指标来衡量模型最终效果:

  1. MR(MeanRank)
  2. MRR(Mean Reciprocal Rank)
  3. Hit@n

MR, MRR, Hit@n指标含义:假设整个图谱中共n个实体,评估前先进行如下操作:

(1)将一个正确的三元组(h,r,t)中的头实体h或者尾实体t,依次替换成整个图谱中的其他所有实体,这样会产生n个三元组;

(2)对(1)中产生的n个三元组分别计算其能量值,例如在TransE中计算h+r-t的值,这样n个三元组分别对应自己的能量值;

(3)对上述n个三元组按照能量值进行升序排序,记录每个三元组排序后的序号;

(4)对所有正确的三元组都进行上述三步操作MR指标:将整个图谱中每个正确三元组的能量值排序后的序号取平均得到的值;

  • MRR指标:将整个图谱每个正确三元组的能量排序后的序号倒数取平均得到的值;
  • Hit@n指标:整个图谱正确三元组的能量排序后序号小于n的三元组所占的比例。因此对于链接预测任务来说,MR指标越小,模型效果越好;
  • MRR和Hit@n指标越大,模型效果越好。接下来本文将在Cora引文数据集上,预测两篇论文之间是否存在引用关系或被引用关系

新LinkedIn用户的链接预测只是给出它可能认识的人的建议。

在链路预测中,我们只是尝试在节点对之间建立相似性度量,并链接最相似的节点。现在的问题是识别和计算正确的相似性分数!

为了说明图中不同链路的相似性差异,让我们通过下面这个图来解释:

N ( i ) N(i) N(i)是节点 i i i的一组邻居。在上图中,节点 i i i j j j的邻居可以表示为:

i i i的邻居:

1.1.1 相似度分数

我们可以根据它们的邻居为这两个节点建立几个相似度分数。

  • 公共邻居: S ( i , j ) = ∣ N ( i ) ∩ N ( j ) ∣ S(i,j) = \\mid N(i) \\cap N(j) \\mid S(i,j)=∣N(i)N(j),即公共邻居的数量。在此示例中,分数将为2,因为它们仅共享2个公共邻居。

  • Jaccard系数: S ( i , j ) = ∣ N ( i ) ∩ N ( j ) ∣ ∣ N ( i ) ∪ N ( j ) ∣ S(i,j) = \\frac \\mid N(i) \\cap N(j) \\mid \\mid N(i) \\cup N(j) \\mid S(i,j)=N(i)N(j)N(i)N(j),标准化的共同邻居版本。

交集是共同的邻居,并集是:

因此,Jaccard系数由粉红色与黄色的比率计算出:

值是 1 6 \\frac 1 6 61

  • Adamic-Adar指数: S ( i , j ) = ∑ k ∈ N ( i ) ∩ N ( j ) 1 log ⁡ ∣ N ( k ) ∣ S(i,j) = \\sum_k \\in N(i)\\cap N(j) \\frac 1 \\log \\mid N(k) \\mid S(i,j)=kN(i)N(j)logN(k)1。 对于节点i和j的每个公共邻居(common neighbor),我们将1除以该节点的邻居总数。这个概念是,当预测两个节点之间的连接时,与少量节点之间共享的元素相比,具有非常大的邻域的公共元素不太重要。
  • 优先依附(Preferential attachment): S ( i , j ) = ∣ N ( i ) ∣ ∗ ∣ N ( j ) ∣ S(i,j) = \\mid N(i) \\mid * \\mid N(j) \\mid S(i,j)=∣N(i)N(j)
  • 当社区信息可用时,我们也可以在社区信息中使用它们。

1.1.2 性能指标(Performance metrics)

我们如何进行链接预测的评估?我们必须隐藏节点对的子集,并根据上面定义的规则预测它们的链接。这相当于监督学习中的train/test的划分。
然后,我们评估密集图的正确预测的比例,或者使用稀疏图的标准曲线下的面积(AUC)。

参考链接:模型评估指标AUC和ROC:https://cloud.tencent.com/developer/article/1508882

1.1.3 代码实践

这里继续用空手道俱乐部图来举例:

使用在前两篇文中提及到的Karate图,并使用python来进行实现

n=34
m = 78
G_karate = nx.karate_club_graph()

pos = nx.spring_layout(G_karate)
nx.draw(G_karate, cmap = plt.get_cmap('rainbow'), with_labels=True, pos=pos)
# 我们首先把有关图的信息打印出来:
n = G_karate.number_of_nodes()
m = G_karate.number_of_edges()
print("Number of nodes : %d" % n)
print("Number of edges : %d" % m)
print("Number of connected components : %d" % nx.number_connected_components(G_karate))
plt.figure(figsize=(12,8))
nx.draw(G_karate, pos=pos)
plt.gca().collections[0].set_edgecolor("#000000")

# 现在,让删除一些连接,例如25%的边:
# Take a random sample of edges
edge_subset = random.sample(G_karate.edges(), int(0.25 * G_karate.number_of_edges()))

# remove some edges
G_karate_train = G_karate.copy()
G_karate_train.remove_edges_from(edge_subset)

# 绘制部分观察到的图,可以对比上图发现,去掉了一些边
plt.figure(figsize=(12,8))
nx.draw(G_karate_train, pos=pos)


# 可以打印我们删除的边数和剩余边数:
edge_subset_size = len(list(edge_subset))
print("Deleted : ", str(edge_subset_size))
print("Remaining : ", str((m - edge_subset_size)))

# Jaccard Coefficient
# 可以先使用Jaccard系数进行预测:
pred_jaccard = list(nx.jaccard_coefficient(G_karate_train))
score_jaccard, label_jaccard = zip(*[(s, (u,v) in edge_subset) for (u,v,s) in pred_jaccard])
# 打印前10组结果
print(pred_jaccard[0:10])
# 预测结果如下,其中第一个是节点,第二个是节点,最后一个是Jaccard分数(用来表示两个节点之间边预测的概率)
# Compute the ROC AUC Score
# 其中,FPR是False Positive Rate, TPR是True Positive Rate
fpr_jaccard, tpr_jaccard, _ = roc_curve(label_jaccard, score_jaccard)
auc_jaccard = roc_auc_score(label_jaccard, score_jaccard)
print(auc_jaccard)

# Adamic-Adar
# 现在计算Adamic-Adar指数和对应的ROC-AUC分数
# Prediction using Adamic Adar 
pred_adamic = list(nx.adamic_adar_index(G_karate_train))
score_adamic, label_adamic = zip(*[(s, (u,v) in edge_subset) for (u,v,s) in pred_adamic])
print(pred_adamic[0:10])
# Compute the ROC AUC Score
fpr_adamic, tpr_adamic, _ = roc_curve(label_adamic, score_adamic)
auc_adamic = roc_auc_score(label_adamic, score_adamic)
print(auc_adamic)

# Compute the Preferential Attachment
# 同样,可以计算Preferential Attachment得分和对应的ROC-AUC分数

pred_pref = list(nx.preferential_attachment(G_karate_train))
score_pref, label_pref = zip(*[(s, (u,v) in edge_subset) for (u,v,s) in pred_pref])
print(pred_pref[0:10])
fpr_pref, tpr_pref, _ = roc_curve(label_pref, score_pref)
auc_pref = roc_auc_score(label_pref, score_pref)
print(auc_pref)
plt.figure(figsize=(12, 8))
plt.plot(fpr_jaccard, tpr_jaccard, label='Jaccard Coefficient - AUC %.2f' % auc_jaccard, linewidth=4)
plt.plot(fpr_adamic, tpr_adamic, label='Adamic-Adar - AUC %.2f' % auc_adamic, linewidth=4)
plt.plot(fpr_pref, tpr_pref, label='Preferential Attachment - AUC %.2f' % auc_pref, linewidth=4)
plt.plot([0, 1], [0, 1], 'k--')
plt.xlim([0.0, 1.0])
plt.ylim([0.0, 1.0])
plt.xlabel('False positive rate')
plt.ylabel('True positive rate')
plt.title("ROC AUC Curve")
plt.legend(loc='lower right')
plt.show() 

1.2 节点标记预测(Node labeling)

给定一个未标记某些节点的图,我们希望对这些节点的标签进行预测。这在某种意义上是一种半监督的学习问题。

处理这些问题的一种常见方法是假设图上有一定的平滑度。平滑度假设指出通过数据上的高密度区域的路径连接的点可能具有相似的标签。这是标签传播算法背后的主要假设。

标签传播算法(Label Propagation Algorithm,LPA)是一种快速算法,仅使用网络结构作为指导来发现图中的社区,而无需任何预定义的目标函数或关于社区的先验信息。

单个标签在密集连接的节点组中迅速占据主导地位,但是在穿过稀疏连接区域时会遇到问题。

半监督标签传播算法是如何工作?

首先,我们有一些数据: x 1 , . . . , x l , x l + 1 , . . . , x n ∈ R p x_1, ..., x_l, x_l+1, ..., x_n \\in R^p x1,...,xl,xl+1,...,xnRp,,以及前 l l l个点的标签: y 1 , . . . , y l ∈ 1... C y_1, ..., y_l \\in 1...C y1,...,yl1...C.

我们定义初始标签矩阵 Y ∈ R n × C Y \\in R^n \\times C YRn×C,如果 x i x_i xi具有标签 y i = j y_i=j yi=j Y i j = 1 Y_ij = 1 Yij=1,否则为0。

该算法将生成预测矩阵 F ∈ R n × C F \\in R^n \\times C FRn×C,我们将在下面详述。然后,我们通过查找最可能的标签来预测节点的标签:

Y i ^ = a r g m a x j F i , j \\hatY_i = argmax_j F_i,j Yi^=argmaxjFi,j

预测矩阵 F F F是什么?

预测矩阵是矩阵 F ⋆ F^\\star F,其最小化平滑度和准确度。因此,我们的结果在平滑性和准确性之间进行权衡。

问题的描述非常复杂,所以我将不会详细介绍。但是,解决方案是:

F ⋆ = ( ( 1 − α ) I + L s y m ) − 1 Y F^\\star = ( (1-\\alpha)I + L_sym)^-1 Y F=((1α)I+Lsym)1Y

其中:

  • 参数 α = 1 1 + μ \\alpha = \\frac 1 1+\\mu α=1+μ1
  • Y Y Y是给定的标签
  • L s y m L_sym Lsym是图的归一化拉普拉斯矩阵(Laplacian matrix)

如果您想进一步了解这个主题,请关注图函数的平滑度和流形正则化的概念。斯坦福有一套很好的标签图可以下载:https://snap.stanford.edu/data/

Networkx直接实现标签传播:https://networkx.github.io/documentation/latest/reference/algorithms/generated/networkx.algorithms.community.label_propagation.label_propagation_communities.html

接下来我们用python来实现节点标签的预测。
为了给我们使用到的标签添加更多的特征,我们需要使用来自Facebook的真实数据。你可以再这里下载,然后放到facebook路径下。

Facebook 数据已通过将每个用户的 Facebook 内部 id 替换为新值来匿名化。此外,虽然已经提供了来自该数据集的特征向量,但对这些特征的解释却很模糊。例如,如果原始数据集可能包含特征“political=Democratic Party”,则新数据将仅包含“political=anonymized feature 1”。因此,使用匿名数据可以确定两个用户是否具有相同的政治派别,但不能确定他们各自的政治派别代表什么。

我已经把数据集放在目录里了,就不需要下载了

1.2.1 代码实现标签扩散

n = G_fb.number_of_nodes()
m = G_fb.number_of_edges()

print("Number of nodes: %d" % n)
print("Number of edges: %d" % m)
print("Number of connected components: %d" % nx.number_connected_components(G_fb))

# 我们把图数据显示出来:
mapping=dict(zip(G_fb.nodes(), range(n)))
nx.relabel_nodes(G_fb, mapping, copy=False)
pos = nx.spring_layout(G_fb)

plt.figure(figsize=(12,8))
nx.draw(G_fb, node_size=200, pos=pos)
plt.gca().collections[0].set_edgecolor("#000000")

with open('facebook/3980.featnames') as f:
    for i, l in enumerate(f):
        pass

n_feat = i+1

features = np.zeros((n, n_feat))
f = open('facebook/3980.feat', 'r')

for line in f:
    if line.split()[0] in mapping:
        node_id = mapping[line.split()[0]]
        features[node_id, :] = list(map(int, line.split()[1:]))

features = 2*features-1
feat_id = 6  #特征选择id,自行设置
labels = features[:, feat_id]

plt.figure(figsize=(12,8))
nx.draw(G_fb, cmap = plt.get_cmap('bwr'), nodelist=range(n), node_color = labels, node_size=200, pos=pos)
plt.gca().collections[0].set_edgecolor("#000000")
plt.show()
# 这个所选择的特征,在图中相对平滑,因此拥有较好的学习传播性能。
# 为了阐述节点标签预测是如何进行的,我们首先要删掉一些节点的标签,作为要预测的对象。这里我们只保留了30%的节点标签:
random.seed(5)
proportion_nodes = 0.3
labeled_nodes = random.sample(G_fb.nodes(), int(proportion_nodes * G_fb.number_of_nodes()))

known_labels = np.zeros(n)
known_labels[labeled_nodes] = labels[labeled_nodes]

plt.figure(figsize=(12,8))
nx.draw(G_fb, cmap = plt.get_cmap('bwr'), nodelist=range(n), node_color = known_labels, node_size=200, pos=pos)
plt.gca().collections[0].set_edgecolor("#000000") # set node border color to black
plt.show()

1.3 图嵌入(Graph Embedding)

嵌入的学习方式与 word2vec 的 skip-gram 嵌入的学习方式相同,使用的是 skip-gram 模型。问题是,我们如何为 Node2Vec 生成输入语料库?数据要复杂得多,即(非)定向、(非)加权、(a)循环……

为了生成语料库,我们使用随机游走采样策略:

在处理NLP或计算机视觉问题时,我们习惯在深度神经网络中对图像或文本进行嵌入(embedding)。到目前为止,我们所看到的图的一个局限性是没有向量特征。但是,我们可以学习图的嵌入!图有不同几个级别的嵌入:

  • 对图的组件进行嵌入(节点,边,特征…)(Node2Vec) node2vec是一个用于图表示学习的算法框架。给定任何图,它可以学习节点的连续特征表示,然后可以用于各种下游机器学习任务。

  • 对图的子图或整个图进行嵌入(Graph2Vec) Learning Distributed Representations of Graphs

学习图的分布式表示

最近关于图结构化数据的表示学习的工作主要集中在学习图子结构(例如节点和子图)的分布式表示。然而,许多图分析任务(例如图分类和聚类)需要将整个图表示为固定长度的特征向量。虽然上述方法自然不具备学习这种表示的能力,但图内核仍然是获得它们的最有效方法。然而,这些图内核使用手工制作的特征(例如,最短路径、graphlet 等),因此受到泛化性差等问题的阻碍。为了解决这个限制,在这项工作中,我们提出了一个名为 graph2vec 的神经嵌入框架来学习任意大小图的数据驱动的分布式表示。图2vec’ s 嵌入是以无监督的方式学习的,并且与任务无关。因此,它们可以用于任何下游任务,例如图分类、聚类甚至播种监督表示学习方法。我们在几个基准和大型现实世界数据集上的实验表明,graph2vec 在分类和聚类精度方面比子结构表示学习方法有显着提高,并且可以与最先进的图内核竞争。

1.3.1. 节点嵌入(Node Embedding)

我们首先关注的是图组件的嵌入。有几种方法可以对节点或边进行嵌入。例如,DeepWalk【http://www.perozzi.net/projects/deepwalk/ 】 使用短随机游走来学习图中边的表示。我们将讨论Node2Vec,这篇论文由2016年斯坦福大学的Aditya Grover和Jure Leskovec发表。

作者说:“node2vec是一个用于图表示学习的算法框架。给定任何图,它可以学习节点的连续特征表示,然后可以用于各种下游机器学习任务。“

该模型通过使用随机游走,优化邻域保持目标来学习节点的低维表示。

Node2Vec的代码可以在GitHub上找到:
https://github.com/eliorc/node2vec

部分程序出图不一一展示,详情进入项目链接即可

2.图神经网络GNN

2.1GNN引言

图神经网络(Graph Neural Networks,GNN)综述链接:https://zhuanlan.zhihu.com/p/75307407?from_voters_page=true 译文

https://arxiv.org/pdf/1901.00596.pdf 原始文章 最新版本V4版本

近年来,深度学习彻底改变了许多机器学习任务,从图像分类和视频处理到语音识别和自然语言理解。这些任务中的数据通常在欧几里得空间中表示。然而,越来越多的应用程序将数据从非欧几里德域生成并表示为具有复杂关系和对象之间相互依赖关系的图。图数据的复杂性对现有的机器学习算法提出了重大挑战。最近,出现了许多关于扩展图数据深度学习方法的研究。在本次调查中,我们全面概述了数据挖掘和机器学习领域中的图神经网络 (GNN)。我们提出了一种新的分类法,将最先进的图神经网络分为四类,即循环图神经网络、卷积图神经网络、图自动编码器和时空图神经网络。我们进一步讨论了图神经网络在各个领域的应用,并总结了图神经网络的开源代码、基准数据集和模型评估。最后,我们在这个快速发展的领域提出了潜在的研究方向

神经网络最近的成功推动了模式识别和数据挖掘的研究。许多机器学习任务,如对象检测 [1]、[2]、机器翻译 [3]、[4] 和语音识别 [5],曾经严重依赖手工特征工程来提取信息特征集,最近已经由各种端到端深度学习范例彻底改变,例如卷积神经网络 (CNN) [6]、递归神经网络 (RNN) [7] 和自动编码器 [8]。深度学习在许多领域的成功部分归功于快速发展的计算资源(例如 GPU)、大训练数据的可用性以及深度学习从欧几里得数据(例如图像、文本、和视频)。以图像数据为例,我们可以将图像表示为欧几里得空间中的规则网格。卷积神经网络 (CNN) 能够利用图像数据的移位不变性、局部连通性和组合性 [9]。因此,CNN 可以提取与整个数据集共享的局部有意义的特征,用于各种图像分析。

虽然深度学习有效地捕获了欧几里得数据的隐藏模式,但越来越多的应用程序将数据以图形的形式表示。例如,在电子

以上是关于图机器学习(GML)&图神经网络(GNN)原理和代码实现(前置学习系列二)的主要内容,如果未能解决你的问题,请参考以下文章

《深入浅出图神经网络》GNN原理解析☄学习笔记神经网络基础

深入浅出图神经网络|GNN原理解析☄学习笔记神经网络基础

08-GNN 图机器学习之图神经网络

机器学习实战:GNN(图神经网络)加速器的FPGA解决方案

李宏毅机器学习|图神经网络Graph Nerual Networks(GNN)|学习笔记-part1

李宏毅机器学习|图神经网络Graph Nerual Networks(GNN)|学习笔记-part1