从头开始建立和模拟一个图形神经网络的详细指南

157 阅读7分钟

图形神经网络是机器学习和深度学习领域中最新兴的技术之一。在许多研究工作中,我们可以看到这些网络在结果和速度方面的成功。图神经网络成功背后的一个主要原因是,它们使用图数据进行建模,而图数据可以由数据集的实体之间的结构关系组成。在这篇文章中,我们将学习如何通过从头开始建立和实现图神经网络来建立和进行建模。本文要涉及的主要内容如下。

目录

  1. 什么是图神经网络?
  2. 了解数据
    1. 下载数据集
    2. 可视化数据
    3. 制作图形数据
  3. 图形神经网络的实现
    1. 图形层
    2. 图形神经结点分类器
  4. 拟合模型
    1. 实例化GNN模型
    2. 定义训练数据
    3. 训练模型
    4. 可视化结果

让我们首先了解什么是图神经网络。

什么是图神经网络

在我们的一篇文章中,我们已经讨论过,可以在图形数据上操作的神经网络可以被认为是图形神经网络。使用图数据,任何神经网络都需要使用数据的顶点或节点来执行任务。比方说,我们正在使用任何GNN执行任何分类任务,那么该网络就需要对图数据的顶点或节点进行分类。在图形数据中,节点应该与它们的标签一起呈现,以便每个节点都能根据神经网络的标签进行分类。

由于在大多数数据集中,我们发现数据实体之间的结构关系,我们可以使用图神经网络来代替其他ML算法,并可以利用图数据建模的好处。图数据的好处可以在这里找到。

在这篇文章中,我们将使用Keras和TensorFlow库实现一个卷积图神经网络。在这个实现中,我们将尝试将图神经网络用于节点预测任务。

了解数据

使用图神经网络需要图数据。在这篇文章中,我们使用的是Cora数据集。这个数据集包括2708篇科学论文,这些论文已经被分为7类,有5429个链接。让我们开始用下载数据集来实现图神经网络建模。

下载数据集

import os
from tensorflow import keras
zip_file = keras.utils.get_file(
    fname="cora.tgz",
    origin="https://linqs-data.soe.ucsc.edu/public/lbc/cora.tgz",
    extract=True,
)
data_dir = os.path.join(os.path.dirname(zip_file), "cora")

输出:

由于该数据集包括两个文件

  1. cora.cites:包括引文记录
  2. cora.content:包括论文内容记录

我们可以在输出中看到我们有两个下载记录。

现在我们需要将引文数据转换成一个数据框:

import pandas as pd
citations_data = pd.read_csv(
    os.path.join(data_dir, "cora.cites"),
    sep="\t",
    header=None,
    names=["target", "source"],
)

将数据集描述为:

citations_data.describe()

输出:

在数据的描述中,我们可以看到,数据框有两个变量target和source,总数值的计数是5429。让我们把核心内容转换为数据框:

column_names = ["paper_id"] + [f"term_{idx}" for idx in range(1433)] + ["subject"]
papers_data = pd.read_csv(
    os.path.join(data_dir, "cora.content"), sep="\t", header=None, names=column_names,
)

将论文数据描述为:

print("Papers shape:", papers_data.shape)
papers_data.head()

输出:

在输出中,我们可以看到这个数据有2708行和1435列,其中有主题名称。现在我们需要为论文ID和主题列提供标签编码:

class_values = sorted(papers_data["subject"].unique())
class_idc = {name: id for id, name in enumerate(class_values)}
paper_idc = {name: idx for idx, name in enumerate(sorted(papers_data["paper_id"].unique()))}
 
papers_data["paper_id"] = papers_data["paper_id"].apply(lambda name: paper_idc[name])
citations_data["source"] = citations_data["source"].apply(lambda name: paper_idc[name])
citations_data["target"] = citations_data["target"].apply(lambda name: paper_idc[name])
papers_data["subject"] = papers_data["subject"].apply(lambda value: class_idc[value]

数据的可视化

让我们用以下几行代码来实现图形数据的可视化:

import networkx as nx
import matplotlib.pyplot as plt
plt.figure(figsize=(10, 10))
colors = papers_data["subject"].tolist()
cora_graph = nx.from_pandas_edgelist(citations_data.sample(n=1500))
subjects = list(papers_data[papers_data["paper_id"].isin(list(cora_graph.nodes))]["subject"])
nx.draw_spring(cora_graph, node_size=15, node_color=subjects)

输出:

在输出中,我们可以看到节点是图形的代表,节点的颜色代表数据中的不同主题。由于我们已经讨论过图形神经网络在图形数据上的工作,我们需要将这些数据帧转换成图形数据。

制作图形数据

在文章的这一部分,我们将看到如何将数据帧转换为图形数据。在这里,我们将看到一个制作图形数据的基本方法。一个基本的图形数据可以由以下元素组成。

  1. **节点特征。**这个元素在一个数组中表示节点的数量和特征的数量。我们在本文中使用的数据集有可以作为节点的论文信息,节点_特征是每篇论文的词-存在的二进制向量。
  2. **边缘。**这是一个由节点之间的链接组成的稀疏矩阵,代表两个维度上的边的数量。在我们的数据集中,链接是论文的引用。
  3. 边缘权重。 这是一个可选的元素,是一个数组。让我们来看看我们如何制作它们:
import tensorflow as tf
feature_names = set(papers.columns) - {"paper_id", "subject"}
feature_names

输出:

  • 边缘
edges = citations_data[["source", "target"]].to_numpy().T
print("Edges shape:", edges.shape)

输出:

  • 节点特征
node_features = tf.cast(
    papers_data.sort_values("paper_id")[feature_names].to_numpy(), dtype=tf.dtypes.float32
)
print("Nodes shape:", node_features.shape)

输出:

  • 边缘权重
edge_weights = tf.ones(shape=edges.shape[1])
print("Edges_weights shape:", edge_weights.shape)

输出:

现在我们可以创建一个由上述元素组成的图信息元组。

graph_info = (node_features, edges, edge_weights)

现在我们已经准备好使用上述基本元素组成的图形数据来训练一个图形神经网络。

实现图神经网络

正如本节所讨论的那样,我们将建立一个可以使用图形数据的网络。为此,我们需要制作一个可以在图形数据上工作的层。

图形层

在文章的这一部分,我们将讨论一个基本的图层需要执行的任务。由于代码的规模很大,我们不在此推送,但我们将讨论该层的任务和功能。我们可以在这里找到整个实现。让我们从第一个任务开始:

  • 这个任务是关于输入节点的准备,我们用一个前馈神经网络来实现。该网络将产生一个信息,以便对输入节点的表示进行处理。节点表示的形状将是[num_nodes, representation_dim]。
  • 下一个任务是关于使用边缘权重将节点提供的消息汇总到其邻居节点。在数学上,我们在这里使用包络不变的集合操作。这些操作为每个节点创建一个单一的聚合消息。聚合消息的形状将是[num_nodes, representation_dim]。
  • 下一个任务是关于节点表示的新状态的产生。在这个任务中,我们要把节点表示和聚合消息结合起来。基本上,如果组合是GRU类型的,那么节点表征和聚合消息可以被堆叠以创建一个序列并由GRU层处理。

为了执行这些任务,我们创建了一个图卷积层作为Keras层,由准备、聚合和更新函数组成。

图神经节点分类器

在制作完该层后,我们需要制作一个图神经节点分类器。这个分类器可以遵循以下过程:

  • 对节点特征进行预处理,生成节点表示
  • 应用图层
  • 对节点表征进行后处理,生成最终的节点表征
  • 使用softmax层来产生基于节点表示的预测

由于这部分的代码也很大,我们在此推送。我们可以在这里找到实现。在这些代码中,我们应用了两个图卷积层来对图数据进行建模。

拟合模型

现在让我们来拟合图形神经网络。

实例化GNN模型

hidden_units = [32, 32]
learning_rate = 0.01
dropout_rate = 0.5
num_epochs = 300
batch_size = 256
gnn_model = GNNNodeClassifier(
    graph_info=graph_info,
    num_classes=num_classes,
    hidden_units=hidden_units,
    dropout_rate=dropout_rate,
    name="gnn_model",
)
gnn_model.summary()

输出:

这里我们已经实例化了模型。

定义训练数据

x_train = train_data.paper_id.to_numpy()
y_train = train_data["subject"]

定义用于编译和拟合模型的函数

def run_experiment(model, x_train, y_train):
    # Compile the model.
    model.compile(
        optimizer=keras.optimizers.Adam(learning_rate),
        loss=keras.losses.SparseCategoricalCrossentropy(from_logits=True),
        metrics=[keras.metrics.SparseCategoricalAccuracy(name="acc")],
    )
    # Create an early stopping callback.
    early_stopping = keras.callbacks.EarlyStopping(
        monitor="val_acc", patience=50, restore_best_weights=True
    )
    # Fit the model.
    history = model.fit(
        x=x_train,
        y=y_train,
        epochs=num_epochs,
        batch_size=batch_size,
        validation_split=0.15,
        callbacks=[early_stopping],
    )
 
    return history

训练模型

history = run_experiment(gnn_model, x_train, y_train)

输出:

结果的可视化

损失

fig, ax1 = plt.subplots(1, figsize=(15, 5))
 
ax1.plot(history.history["loss"])
ax1.plot(history.history["val_loss"])
ax1.legend(["train", "test"], loc="upper right")
ax1.set_xlabel("Epochs")
ax1.set_ylabel("Loss")

输出:

准确度

fig, ax2 = plt.subplots(1, figsize=(15, 5))
ax2.plot(history.history["acc"])
ax2.plot(history.history["val_acc"])
ax2.legend(["train", "test"], loc="upper right")
ax2.set_xlabel("Epochs")
ax2.set_ylabel("Accuracy")
plt.show()

输出:

在上述输出中,我们可以看到该模型表现良好。在准确率部分,我们可以看到,该模型在训练和测试数据中的准确率分别为90%和80%。

最后的话

在这篇文章中,我们已经看到了我们如何将数据设计成图数据,以及我们如何实现一个图神经网络来处理图数据。深入来说,我们可以说我们已经实现了一个卷积图神经网络,它也可以处理具有连续属性的图数据。

参考资料