【GNN 1】PyG实现图神经网络,完成节点分类任务,人话、保姆级教程

news2024/11/20 22:38:51

我们来做一个节点分类的任务,选择的数据集是Karate Club,Karate是空手道的意思,所以这就是一个空手道俱乐部的数据。

简而言之,这个数据集,包含34个节点,156条无向无权边,结点总共分为4类,此外,每个节点还有34个特征,也就是说还有34个指标来描述空手道俱乐部的每个成员。欸?特征数怎么和节点数一样,没错,就是one-hot编码。

下图是论文原图,颜色表示了类别。

数据集的详细说明

这个数据集是由社会学家Wayne W. Zachary在1977年的论文An Information Flow Model for Conflict and Fission in Small Groups中提出的,基于他对一个美国大学空手道俱乐部的观察和记录。这个数据集包含34个节点和78条边,每个节点代表一个空手道俱乐部的成员,每条边代表两个成员之间的社交关系。

在收集数据的过程中,俱乐部的管理员和教练之间发生了冲突,导致俱乐部分裂为两个社区,一半成员跟随教练,另一半成员跟随管理员或离开俱乐部。Zachary利用图的结构信息,成功地预测了除了一个成员之外的所有成员的类别。

Zaharu’s karateclub数据集还根据2017年的论文Semi-supervised Classification with Graph Convolutional Networks,给每个节点赋予了一个四分类的标签,表示它们属于哪个社区,这些标签是通过基于模块度的聚类方法得到的。

再说一遍数据集的本质:这个数据集,包含34个节点,156条无向无权边,结点总共分为4类,此外,每个节点还有34个特征,也就是说还有34个指标来描述空手道俱乐部的每个成员。欸?特征数怎么和节点数一样,没错,就是one-hot编码。

加载并探索数据集

torch_geometric.datasets里已经有了这个数据集,我们可以查看一下这个数据的一些信息。包括有几个图(有的数据集有很多张图,比如有好几个俱乐部,每个俱乐部是一张图,这里是只有一张图),结点/边特征个数,结点类别个数。

from torch_geometric.datasets import KarateClub

dataset = KarateClub()
print(f'Dataset: {dataset}:')
print('======================')
print(f'Number of graphs: {len(dataset)}')
print(f'Number of node features: {dataset.num_features}')
print(f'Number of classes: {dataset.num_classes}')
print(f'Number of edge features: {dataset.num_edge_features}')
Dataset: KarateClub():
======================
Number of graphs: 1
Number of node features: 34
Number of classes: 4
Number of edge features: 0

可以看到只有一张图,34个结点特征和4个类别。

这里需要明确的是,数据集是数据集,图是图,图是包含在数据集中的,接下来,我们拿出这个数据集的第一张图(也是唯一一张图),来看看这个图中的一些信息。包括图的节点个数、边的个数、结点的平均度、是否有孤立点、有没有自己连接自己等等。

data = dataset[0]  # Get the first graph object.

print(data)
print('==============================================================')

# Gather some statistics about the graph.
print(f'Number of nodes: {data.num_nodes}')
print(f'Number of edges: {data.num_edges}')
print(f'Average node degree: {(data.num_edges) / data.num_nodes:.2f}')
print(f'Number of training nodes: {data.train_mask.sum()}')
print(f'Training node label rate: {int(data.train_mask.sum()) / data.num_nodes:.2f}')
print(f'Contains isolated nodes: {data.has_isolated_nodes()}')
print(f'Contains self-loops: {data.has_self_loops()}')
print(f'Is undirected: {data.is_undirected()}')
Data(x=[34, 34], edge_index=[2, 156], y=[34], train_mask=[34])
==============================================================
Number of nodes: 34
Number of edges: 156
Average node degree: 4.59
Number of training nodes: 4
Training node label rate: 0.12
Contains isolated nodes: False
Contains self-loops: False
Is undirected: True

Data这个数据类型

我们来详细看看Data这个数据类型

PyTorch Geometric是一个用于图神经网络的PyTorch库,它可以处理各种类型的图数据。在PyTorch Geometric中,每个图都由一个Data对象表示,它包含了描述图结构的所有信息。Data对象可以像一个普通的Python字典一样使用,也可以提供一些分析图结构的有用功能。

Data对象可以有多个属性,每个属性都是一个PyTorch张量。例如,上面的代码中,Data对象有四个属性:

  • edge_index:一个2 x 156的张量,表示图中的边的连接关系。每一列是一个边的起点和终点的索引,例如第一列[0, 1]表示从节点0到节点1的一条边。
  • x:一个34 x 34的张量,表示图中的节点的特征。每一行是一个节点的34维特征向量,例如第一行[-1]表示节点0的特征。
  • y:一个34维的张量,表示图中的节点的标签。每个元素是一个节点的类别,例如第一个元素0表示节点0属于类别0。
  • train_mask:一个34维的布尔张量,表示哪些节点的标签是已知的。每个元素是True或False,例如第一个元素True表示节点0的标签是已知的。

注意了,Data(x=[34, 34], edge_index=[2, 156], y=[34], train_mask=[34])这里面的数字,都是表示维度。

这个Data对象表示了一个有34个节点,156条边的图,其中只有4个节点的标签是已知的,其余的节点的标签需要通过图神经网络来推断。

Data对象还提供了一些实用函数,用于推断图的一些基本属性。例如,我们可以通过以下方式来判断图是否有孤立的节点(没有任何边相连的节点),是否有自环(从一个节点到自己的边),或者是否是无向图(每条边都是双向的):

  • data.isolated_nodes():返回一个布尔张量,表示哪些节点是孤立的。
  • data.contains_self_loops():返回一个布尔值,表示图是否有自环。
  • data.is_undirected():返回一个布尔值,表示图是否是无向的。

coo format

在这里我还想补充要给知识点,也就是coo format,这是用来表示图的一种方式,本质上是表示稀疏矩阵的一种方式,也就是用三个向量分别表示,行、列、值,请看下面详细介绍:

coo format是一种稀疏矩阵的存储格式,它只记录矩阵中非零元素的位置和值。coo format的全称是coordinate format,意思是坐标格式,因为它用两个坐标来表示每个非零元素的行和列索引。例如,一个4 x 4的矩阵:

[ 0 3 0 1 1 0 2 0 0 1 0 0 1 0 0 0 ] \begin{bmatrix} 0 & 3 & 0 & 1 \\\\ 1 & 0 & 2 & 0 \\\\ 0 & 1 & 0 & 0 \\\\ 1 & 0 & 0 & 0 \end{bmatrix} 0101301002001000

可以用coo format表示为:

row = [ 0 , 0 , 1 , 1 , 2 , 3 ] col = [ 1 , 3 , 0 , 2 , 1 , 0 ] data = [ 3 , 1 , 1 , 2 , 1 , 1 ] \begin{aligned} &\text{row} = [0, 0, 1, 1, 2, 3] \\\\ &\text{col} = [1, 3, 0, 2, 1, 0] \\\\ &\text{data} = [3, 1, 1, 2, 1, 1] \end{aligned} row=[0,0,1,1,2,3]col=[1,3,0,2,1,0]data=[3,1,1,2,1,1]

其中,row和col是两个长度为非零元素个数的数组,分别存储每个非零元素的行和列索引。data是一个同样长度的数组,存储每个非零元素的值。这样,就可以用三个数组来代替一个完整的矩阵,节省了空间和计算资源。

在图神经网络领域,coo format可以用来表示图的邻接矩阵,即图中每两个节点之间是否有边相连的矩阵。由于图的邻接矩阵通常是稀疏的,即大部分节点之间没有边相连,所以用coo format可以有效地压缩和处理图的结构信息。一些图神经网络的库,如PyTorch Geometric,就支持用coo format来输入图的邻接矩阵。

可视化一下图

接下来我们可视化一下这个图,看看和我们上面展示真实的图是不是一样的。

可视化,我们使用的networkx这个包,首先我们需要把Data转换成networkx支持的图结构。这里是使用的to_networkx函数。

# Helper function for visualization.
%matplotlib inline
import torch
import networkx as nx
import matplotlib.pyplot as plt

# Visualization function for NX graph or PyTorch tensor
def visualize(h, color, epoch=None, loss=None, accuracy=None):
    plt.figure(figsize=(7,7))
    plt.xticks([])
    plt.yticks([])

    if torch.is_tensor(h):
        h = h.detach().cpu().numpy()
        plt.scatter(h[:, 0], h[:, 1], s=140, c=color, cmap="Set2")
        if epoch is not None and loss is not None and accuracy['train'] is not None and accuracy['val'] is not None:
            plt.xlabel((f'Epoch: {epoch}, Loss: {loss.item():.4f} \n'
                       f'Training Accuracy: {accuracy["train"]*100:.2f}% \n'
                       f' Validation Accuracy: {accuracy["val"]*100:.2f}%'),
                       fontsize=16)
    else:
        nx.draw_networkx(h, pos=nx.spring_layout(h, seed=42), with_labels=False,
                         node_color=color, cmap="Set2")
    plt.show()
from torch_geometric.utils import to_networkx

G = to_networkx(data, to_undirected=True)
visualize(G, color=data.y)



虽然不一样,不过问题不大,和后续的分析没有关系。

用图卷积神经网络(GCN)对节点进行低维嵌入

接下来我们看看如何使用PyG(PyTorch Geometric)库来实现图神经网络(GNNs)。我们用最简单的GNN结构——GCN layer,它可以通过节点特征表示x和COO格式的edge_index来执行图卷积操作,从而更新节点的特征。

输入输出与模型本质

输入是什么?

节点特征表示x和COO格式的edge_index,也就是节点特征和节点间的连接关系的信息。

输出是什么?

GNN的输出目标是学习一个函数f,它可以将图中的每个节点及其特征向量映射到一个低维的嵌入向量,这个嵌入向量可以反映节点在图中的结构和语义信息,从而用于后续的任务,如节点分类、边预测或图回归等。

通俗理解:用一个向量来表示这个节点,这个由GCN layer组成的GNN就是为了学习到一个从节点特征和节点间的连接关系到节点的向量表示的一个方法。

下面我们来看看如何实现吧!注释非常详细!

模型的定义与训练,训练前后的比较

__init__forward是两个特殊的方法,用于定义一个PyTorch的神经网络模型,__init__是初始化方法,用于创建模型中需要用到的各种层和参数,forward是前向传播方法,用于定义模型的计算流程,即如何用层和参数来计算模型的输出。

三个图卷积层是GCN模型的核心部分,图卷积层是一种能够有效地处理图数据的神经网络层,它可以实现图数据的信息传递和聚合。每个图卷积层都可以收集每个节点的一跳(one hop)邻居(直接相连的节点)的信息,但是当我们把三个图卷积层叠加在一起时,我们就可以收集每个节点的三跳邻居(最多相隔三条边的节点)的信息,这样可以使得模型能够捕捉到更广泛的图结构信息。

GCNConv层还可以降低节点特征的维度,从34维降到2维( 34 → 4 → 4 → 2 34 \rightarrow 4 \rightarrow 4 \rightarrow 2 34442),也就是说,经过三个GCNConv层后,每个节点的特征会变成2维的向量,这个向量可以看作是节点在图卷积神经网络的嵌入空间中的表示,可以用于可视化或其他任务。每个GCNConv层后面还加了一个tanh函数,这是一种非线性激活函数,可以增加模型的表达能力,也可以避免梯度消失或爆炸的问题。

在三个图卷积层之后,我们还应用了一个线性变换层(torch.nn.Linear),这个层的作用是对每个节点的嵌入向量进行线性变换,得到每个类别的得分,然后可以用softmax函数计算每个类别的概率,这个层相当于一个分类器,可以将我们的节点映射到四个类别/社区之一。起到监督学习的作用。

我们的模型的输出有两部分,一部分是最后的分类器的输出,也就是每个节点的类别得分或概率,另一部分是最后的节点嵌入矩阵,也就是每个节点在图卷积神经网络的嵌入空间中的表示,这两部分都可以用于不同的目的。

import torch
from torch.nn import Linear
from torch_geometric.nn import GCNConv
# 从PyTorch Geometric库中导入GCNConv类,这是一个图卷积层,可以实现图数据的信息传递和聚合

class GCN(torch.nn.Module):
    def __init__(self):
        super().__init__()
        torch.manual_seed(1234)
        # 创建一个GCNConv层,命名为conv1,这个层的输入特征维度是dataset.num_features,
        # 也就是数据集中每个节点的特征数,输出特征维度是4,也就是经过这个层后,每个节点的特征会变成4维的向量。
        self.conv1 = GCNConv(dataset.num_features, 4)
        # 创建一个GCNConv层,命名为conv2,这个层的输入特征维度是4,
        # 也就是上一个层的输出特征维度,输出特征维度也是4,也就是经过这个层后,每个节点的特征仍然是4维的向量。
        self.conv2 = GCNConv(4, 4)
        # 创建一个GCNConv层,命名为conv3,这个层的输入特征维度是4,
        # 也就是上一个层的输出特征维度,输出特征维度是2,也就是经过这个层后,每个节点的特征会变成2维的向量,
        # 这个向量可以看作是节点在图卷积神经网络的嵌入空间中的表示。
        self.conv3 = GCNConv(4, 2)
        # 创建一个Linear层,命名为classifier,这个层的输入特征维度是2,
        # 也就是上一个层的输出特征维度,输出特征维度是dataset.num_classes,
        # 也就是数据集中的类别数,这个层的作用是对每个节点的嵌入向量进行线性变换,
        # 得到每个类别的得分,然后可以用softmax函数计算每个类别的概率。
        self.classifier = Linear(2, dataset.num_classes)

    def forward(self, x, edge_index):
        '''
        定义模型的前向传播方法,这里需要接收两个参数,
        x是节点特征矩阵,每一行是一个节点的特征向量,
        edge_index是边索引矩阵,每一列是一条边的两个端点的索引,
        这两个参数可以描述一个图的结构和属性。
        '''
        h = self.conv1(x, edge_index)
        # 调用conv1层,传入节点特征矩阵和边索引矩阵,得到经过第一次图卷积后的节点特征矩阵,命名为h。
        h = h.tanh()
        # 对h矩阵中的每个元素应用双曲正切函数,这是一种非线性激活函数,
        # 可以增加模型的表达能力,也可以避免梯度消失或爆炸的问题。
        h = self.conv2(h, edge_index)
        # 调用conv2层,传入经过第一次激活后的节点特征矩阵和边索引矩阵,
        # 得到经过第二次图卷积后的节点特征矩阵,仍然命名为h,覆盖原来的值。
        h = h.tanh()
        # 对h矩阵中的每个元素应用双曲正切函数,这是第二次激活。
        h = self.conv3(h, edge_index)
        h = h.tanh()  # Final GNN embedding space.

        # Apply a final (linear) classifier.
        # 调用classifier层,传入节点嵌入矩阵,得到每个节点的类别得分矩阵,
        # 命名为out,这个矩阵可以用于计算交叉熵损失或预测节点的类别。
        out = self.classifier(h)
        
        # 返回类别得分矩阵和节点嵌入矩阵
        return out, h
    
# 创建一个GCN类的实例,命名为model,这是一个图卷积神经网络的模型对象,可以用于训练或测试
model = GCN()
print(model)
GCN(
  (conv1): GCNConv(34, 4)
  (conv2): GCNConv(4, 4)
  (conv3): GCNConv(4, 2)
  (classifier): Linear(in_features=2, out_features=4, bias=True)
)

前面说了,我们的模型分为两部分,一部分是用图卷积进行图嵌入,得到节点的低维向量表示,另一部分则是一个分类器,起到监督学习的作用,根据标签来调整前面图卷积的效果。

我们为了能够看到GNN的训练的效果,我们可以比较这两个节点的嵌入向量:①不经过训练,只有图卷积嵌入得到的低维向量;②经过训练。

我们可以通过两种方法来评估,第一种是直接看四类样本的分布情况,由于我们最终是用2维向量进行表示的,所以很直观地可以用二维平面可视化;第二种是用嵌入向量构建分类器,如果用同样的分类器训练前和训练后的效果一样,那就说明图卷积没啥用,反之则可以说明还是有用的。

model = GCN()

_, h_no_train = model(data.x, data.edge_index)
print(f'Embedding shape: {list(h_no_train.shape)}')

visualize(h_no_train, color=data.y)
Embedding shape: [34, 2]

# 导入sklearn的模块
from sklearn.tree import DecisionTreeClassifier # 决策树分类器
from sklearn.model_selection import train_test_split # 数据切分
from sklearn.metrics import accuracy_score # 准确率评估
from sklearn.tree import plot_tree # 决策树绘图
import matplotlib.pyplot as plt # 画图工具

# 划分训练集和测试集,随机种子设为0,测试集比例设为0.2
X_train, X_test, y_train, y_test = train_test_split(h_no_train.detach().numpy(), data.y.detach().numpy(), random_state=10, test_size=0.2)

# 创建一个决策树分类器,使用默认参数,随机种子设为0
clf = DecisionTreeClassifier(random_state=0)

# 训练模型
clf.fit(X_train, y_train)

# 预测测试集
y_pred = clf.predict(X_test)

# 计算准确率
acc = accuracy_score(y_test, y_pred)
print("The accuracy score is:", acc)

# 可视化决策树
plt.figure(figsize=(10, 10)) # 设置画布大小
plot_tree(clf, feature_names=["f1", "f2", "f3", "f4"], class_names=["c1", "c2", "c3", "c4"], filled=True) # 绘制决策树,填充颜色,设置特征名和类别名
plt.show() # 显示图像

The accuracy score is: 0.7142857142857143

# 导入sklearn的模块
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import cross_val_score
from sklearn.metrics import precision_recall_curve, roc_curve, auc
from sklearn.model_selection import KFold

X = h_no_train.detach().numpy()
y = data.y.detach().numpy()

# 创建一个逻辑回归模型,使用默认参数,随机种子设为0
clf = LogisticRegression(random_state=0)

# 使用5折交叉验证,得到每折的准确率
acc_scores = cross_val_score(clf, X, y, cv=3, scoring='accuracy')

# 计算平均的准确率
acc_mean = acc_scores.mean()

# 打印平均的准确率
print("The average accuracy score is:", acc_mean)

# 导入matplotlib.pyplot模块
import matplotlib.pyplot as plt

# 设置柱状图的x轴坐标和高度
x = ["Fold 1", "Fold 2", "Fold 3"]
height = acc_scores

# 绘制柱状图
plt.bar(x, height, color="grey")

# 添加标题和坐标轴标签
plt.title("Cross Validation Scores")
plt.xlabel("Folds")
plt.ylabel("Accuracy")
plt.ylim(0, 1) # 设置y轴的范围为0~1

# 计算平均值
mean = sum(acc_scores) / len(acc_scores)

# 在图上显示平均值
plt.axhline(y=mean, color="red", linestyle="--")
plt.text(0.5, mean + 0.01, f"Mean = {mean:.2f}")

# 显示图形
plt.show()
The average accuracy score is: 0.35353535353535354

模型如何训练?

上面是训练前的,下面我们来看看训练后的。

要看训练后的,当然得知道怎么训练。

先看看这些关键的数据长什么样。

print(data.train_mask)
# print(data.train_mask.shape())
print(data.x)
# print(data.x.shape())
print(data.edge_index)
# print(data.edge_index.shape())

image-20240109113913742

下面谈谈模型训练几个关键的地方,注释也非常详细。

这里的data.train_mask,其实就是一个[ True, False, False,…]的一个用来索引的向量,里面有4个是True,所以out[data.train_mask]就是选了4个出来。

train_mask虽然叫train_mask,但我觉得是起到一个测试的作用:一是用来在训练过程中计算这四个节点(也是样本)的loss,并更新参数使得loss减少;二是在一次训练结束后用这四个样本来计算一下准确率,作为training accuracy。

再说一下validation accuracy,前面说了,更新参数是根据train_mask的,计算准确率也是train_mask,那么在全图上的表现如何呢?也就是在所有样本的表现如何呢?于是就把上面的out[data.train_mask]直接换成out了。

import time
from IPython.display import Javascript  # Restrict height of output cell.
display(Javascript('''google.colab.output.setIframeHeight(0, true, {maxHeight: 430})'''))

model = GCN()
# 定义交叉熵损失函数,用于计算模型的预测值和真实值之间的差异
criterion = torch.nn.CrossEntropyLoss()  # Define loss criterion.
# 定义 Adam 优化器,用于根据损失函数的梯度更新模型的参数,学习率为 0.01
optimizer = torch.optim.Adam(model.parameters(), lr=0.01)  # Define optimizer.

def train(data):
    '''
    这里的data.train_mask,其实就是一个[ True, False, False,......]的一个用来索引的向量
    里面有4个是True,所以out[data.train_mask]就是选了4个出来,虽然叫train_mask,但我觉得是起到一个测试的作用
    一是用来在训练过程中计算这四个节点(也是样本)的loss,并更新参数使得loss减少
    二是在一次训练结束后用这四个样本来计算一下准确率,作为training accuracy
    再说一下validation accuracy,前面说了,更新参数是根据train_mask的,
    计算准确率也是train_mask,那么在全图上的表现如何呢?也就是在所有样本的表现如何呢?
    于是就把上面的out[data.train_mask]直接换成out了。
    '''
    
    # 调用优化器的 zero_grad 方法,清除之前的梯度
    optimizer.zero_grad()  # Clear gradients.
    # 调用模型的 forward 方法,传入节点特征 data.x 和边索引 data.edge_index,得到模型的输出 out 和节点嵌入 h
    out, h = model(data.x, data.edge_index)  # Perform a single forward pass.
    # 调用损失函数,传入模型输出的训练节点部分 out[data.train_mask] 和真实标签的训练节点部分 data.y[data.train_mask],计算训练损失
    loss = criterion(out[data.train_mask], data.y[data.train_mask])  # Compute the loss solely based on the training nodes.
    # 调用损失的 backward 方法,求出损失函数对模型参数的梯度
    loss.backward()  # Derive gradients.
    # 调用优化器的 step 方法,根据梯度更新模型参数
    optimizer.step()  # Update parameters based on gradients.

    # 上面是训练部分,下面就是看训练的怎么样了
    # 定义一个字典,用于存储训练准确率和验证准确率
    accuracy = {}
    # Calculate training accuracy on our four examples
    predicted_classes = torch.argmax(out[data.train_mask], axis=1) # [0.6, 0.2, 0.7, 0.1] -> 2
    target_classes = data.y[data.train_mask]
    accuracy['train'] = torch.mean(
        torch.where(predicted_classes == target_classes, 1, 0).float())

    # Calculate validation accuracy on the whole graph
    predicted_classes = torch.argmax(out, axis=1)
    target_classes = data.y
    accuracy['val'] = torch.mean(
        torch.where(predicted_classes == target_classes, 1, 0).float())

    return loss, h, accuracy

# 定义一个循环,迭代 100 次
for epoch in range(100):
    loss, h, accuracy = train(data)
    # Visualize the node embeddings every 10 epochs
    if epoch % 10 == 0:
        visualize(h, color=data.y, epoch=epoch, loss=loss, accuracy=accuracy)
        time.sleep(0.3)

可以看到随着训练次数的增加四类点的分布也呈现了区域化的特征,说明训练是有用的。

# 导入sklearn的模块
from sklearn.tree import DecisionTreeClassifier # 决策树分类器
from sklearn.model_selection import train_test_split # 数据切分
from sklearn.metrics import accuracy_score # 准确率评估
from sklearn.tree import plot_tree # 决策树绘图
import matplotlib.pyplot as plt # 画图工具

# 划分训练集和测试集,随机种子设为0,测试集比例设为0.2
X_train, X_test, y_train, y_test = train_test_split(h.detach().numpy(), data.y.detach().numpy(), random_state=10, test_size=0.2)

# 创建一个决策树分类器,使用默认参数,随机种子设为0
clf = DecisionTreeClassifier(random_state=0)

# 训练模型
clf.fit(X_train, y_train)

# 预测测试集
y_pred = clf.predict(X_test)

# 计算准确率
acc = accuracy_score(y_test, y_pred)
print("The accuracy score is:", acc)

# 可视化决策树
plt.figure(figsize=(10, 10)) # 设置画布大小
plot_tree(clf, feature_names=["f1", "f2", "f3", "f4"], class_names=["c1", "c2", "c3", "c4"], filled=True) # 绘制决策树,填充颜色,设置特征名和类别名
plt.show() # 显示图像
The accuracy score is: 0.8571428571428571

# 导入sklearn的模块
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import cross_val_score
from sklearn.metrics import precision_recall_curve, roc_curve, auc
from sklearn.model_selection import KFold

X = h.detach().numpy()
y = data.y.detach().numpy()

# 创建一个逻辑回归模型,使用默认参数,随机种子设为0
clf = LogisticRegression(random_state=0)

# 使用5折交叉验证,得到每折的准确率
acc_scores = cross_val_score(clf, X, y, cv=3, scoring='accuracy')

# 计算平均的准确率
acc_mean = acc_scores.mean()

# 打印平均的准确率
print("The average accuracy score is:", acc_mean)

# 导入matplotlib.pyplot模块
import matplotlib.pyplot as plt

# 设置柱状图的x轴坐标和高度
x = ["Fold 1", "Fold 2", "Fold 3"]
height = acc_scores

# 绘制柱状图
plt.bar(x, height, color="grey")

# 添加标题和坐标轴标签
plt.title("Cross Validation Scores")
plt.xlabel("Folds")
plt.ylabel("Accuracy")
plt.ylim(0, 1) # 设置y轴的范围为0~1
# 计算平均值
mean = sum(acc_scores) / len(acc_scores)

# 在图上显示平均值
plt.axhline(y=mean, color="red", linestyle="--")
plt.text(0.5, mean + 0.01, f"Mean = {mean:.2f}")

# 显示图形
plt.show()

The average accuracy score is: 0.8560606060606061

可以看到,经过训练后,这些点的二维分布(嵌入向量)比之前更加离散,也就是更容易将其分开,换句话说就是分类器可以更好地进行分类,这也就导致了分类器在训练后数据通常会比在训练前数据上表现得更好。

结语

图神经网络的学习道阻且长,但行则将至,动手写起代码来吧,我们一起努力!如果觉得还不错,可以点赞收藏哟!

欣赏一下苏轼的寒食帖

Calligraphy by Su Shi: A detail of The Cold Food Observance (寒食帖)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1370791.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

AI绘画:Midjournety的使用体验

今天的时间少,没有给大家做一些教程,就单纯分享使用体验,还不错,体验感很好。 后需如果有需要,我可以出一些教程类的视频。 下面是一组复刻fated的saber的一组提示词,效果相当不错。我后续会分享一些学习经…

uniapp自定义顶部导航并解决打包成apk后getMenuButtonBoundingClientRect方法失效问题

需求:要在app上的顶部导航提示哪里添加一些东西进去,用uniapp自带的肯定不行啊,所以自定义了所有的页面的顶部导航,之后自定义后用手机调试发现 uni.getMenuButtonBoundingClientRect()这个方法的top获取不到....网上找了很多种方…

基于 InternLM 和 LangChain 搭建你的知识库

基于 InternLM 和 LangChain 搭建你的知识库 大模型开发范式LLM的局限性:RAG 检索增强生成 LangChain简介构建向量数据库搭建知识库助手Web Demo部署环境配置下载 NLTK 相关资源下载本项目代码 大模型开发范式 LLM的局限性: 知识实效性受限&#xff1a…

模型\视图一般步骤:为什么经常要用“选择模型”QItemSelectionModel?

一、“使用视图”一般的步骤: //1.创建 模型(这里是数据模型!) tabModelnew QSqlTableModel(this,DB);//数据表 //2.设置 视图的模型(这里是数据模型!) ui->tableView->setModel(tabModel); 模型种类: QStringListModel…

vue element plus Typography 排版

我们对字体进行统一规范,力求在各个操作系统下都有最佳展示效果。 字体# 字号# LevelFont SizeDemoSupplementary text12px Extra SmallBuild with ElementBody (small)13px SmallBuild with ElementBody14px BaseBuild with ElementSmall Title16px MediumBuild w…

了解不同方式导入导出的速度之快

目录 一、用工具导出导入 Navicat(速度慢) 1.1、导入: 共耗时: 1.2、导出表 共耗时: 二、用命令语句导出导入 2.1、mysqldump速度快 导出表数据和表结构 共耗时: 只导出表结构 导入 共耗时&…

Linux 网络层收发包流程及 Netfilter 框架浅析

1. 前言 本文主要对 Linux 系统内核协议栈中网络层接收,发送以及转发数据包的流程进行简要介绍,同时对 Netfilter 数据包过滤框架的基本原理以及使用方式进行简单阐述。 内容如有理解错误而导致说明错误的地方,还请指正。如存在引用而没有添…

怎样的摆渡系统,能实现安全可管控的跨网数据传输?

大数据时代,数据在流通与传输的过程中,更需要注意到数据的安全防护,护航数据价值。“让数据主宰一切的隐忧”,数字战争的时代,各国早已认识到网络安全愈发重要,数据也成为各国发展的重要武器。 出于安全性和…

vmware虚拟机内存异常占用问题一例

关键词 vmware esxi、hypervisor虚拟化平台内存模式 Guest virtual memory 一、问题现象 业务一台vmware虚拟机出现内存使用率告警,运维人员登录系统检查内存确实高水位状态 检查各进程使用内存不高,合计内存总数与使用率占用情况明显不匹配&#xf…

面试题:什么是雪花算法?啥原理?

SnowFlake 算法,是 Twitter 开源的分布式 ID 生成算法。 其核心思想就是:使用一个 64 bit 的 long 型的数字作为全局唯一 ID。在分布式系统中的应用十分广泛,且 ID 引入了时间戳,基本上保持自增的,后面的代码中有详细…

Bert-vits2最终版Bert-vits2-2.3云端训练和推理(Colab免费GPU算力平台)

对于深度学习初学者来说,JupyterNoteBook的脚本运行形式显然更加友好,依托Python语言的跨平台特性,JupyterNoteBook既可以在本地线下环境运行,也可以在线上服务器上运行。GoogleColab作为免费GPU算力平台的执牛耳者,更…

密钥管理机制如何进行工作

密钥管理机制是信息安全领域中一个至关重要的环节,其目标是确保密钥的安全传输、存储和使用,从而保障整个系统的安全性和可靠性。在实际工作中,密钥管理机制涉及到多个方面的技术和方法,下面将详细介绍其工作原理和过程。 密钥管理…

linux(ubuntu)中crontab定时器命令详解 以及windows中定时器

linux(ubuntu)中crontab定时器命令详解 crontab 是一个用于创建、编辑和管理用户的定时任务的命令,它可以让用户在指定的时间自动执行指定的命令或脚本。 基本语法 -e:编辑用户的 crontab 文件;-l:列出用…

万界星空科技家具制造业MES系统解决方案

家具行业专业化生产和信息化管理程度较低。随着企业规模的迅速扩大,家具行业中用现有的凭手工发放图纸、制作bom、发放工艺说明文件越来越难以适应现代家具行业、已经成为家具行业快速发展的瓶颈。必须通过实施万界星空科技MES生产管理软件,实现部门之间…

【51单片机】延时函数delay的坑——关于无符号整型数据for语句“x >= 0“变成死循环

请认真看看以下延时函数是否正确,并且指出错误:(考考C语言功底) void delay_ms(unsigned int xms) //delay x ms {unsigned int x,y;for(xxms;x>0;x--)for(y124;y>0;y--); }废话少说,上正确代码: v…

Vue3-watch的用法

watch简介 作用:监视数据的变化 (和 Vue2 中的 watch 作用一致) 特点: Vue3 中的 watch 只能监视以下四种数据 1.ref 定义的数据(又可以分 基本 和 对象 ) 2.reactive 定义的数据 3.函数返回一个值。(getter 函数) 4.一个包含上述内容的数组 我们在 Vue3 中使用 watch …

聊聊 Java 集合框架中的 ArrayList

其实 Java 集合框架也叫做容器,主要由两大接口派生而来,一个是 collection,主要存放对象的集合。另外一个是Map, 存储着键值对(两个对象)的映射表。 下面就来说说 List接口,List存储的元素是有序、可重复的。其下有三个…

Harbor配置同步规则删除不掉

【问题原因】 harbor上主从两个仓库,配置同步规则时,定时任务配置太频繁,导致规则修改,删除都失败。 【问题现象】 点击修改后保存,页面报internal server error的错。 【问题排查】 docker ps | grep harbor 查看…

解决VirtualBox rc=-1908的错误

日常虽然使用linux办公,但是还是用virtualbox安装了一个20G的Windows作为ps等不可替代软件的作业环境。 每次Linux滚动更新(尤其是内核更新)后,virtualbox经常会遇到趴窝的情况。经过多方查证,有时可以解决问题,有时又不行,并且网…