图神经网络实战(12)——图同构网络(Graph Isomorphism Network, GIN)

news2024/10/4 13:55:38

图神经网络实战(12)——图同构网络

    • 0. 前言
    • 1. 图同构网络原理
    • 2. 构建 GIN 模型执行图分类
      • 2.1 图分类任务
      • 2.2 PROTEINS 数据集分析
      • 2.3 构建 GIN 实现图分类
      • 2.4 GCN 与 GIN 性能差异分析
    • 3. 提升模型性能
    • 小结
    • 系列链接

0. 前言

Weisfeiler-Leman (WL) 测试提供了一个理解图神经网络 (Graph Neural Networks, GNN) 表达能力的框架,利用该框架我们比较了不同的 GNN 层,在本节中,我们将利用 WL 测试结果尝试设计比图卷积网络 (Graph Convolutional Network, GCN)、图注意力网络 (Graph Attention Networks,GAT) 和 GraphSAGE 更强大的 GNN 架构——图同构网络 (Graph Isomorphism Network, GIN)。然后,使用 PyTorch Geometric 实现 GIN 架构,并执行图分类任务。我们将在 PROTEINS 数据集上实现 GIN 架构,将比较不同的图分类模型并分析结果。

为了验证这一想法,在下一节中,我们将根据这一思想构建。

1. 图同构网络原理

在 Weisfeiler-Leman (WL) 测试一节中,我们看到之前所介绍的图神经网络 (Graph Neural Networks, GNN) (包括图卷积网络 (Graph Convolutional Network, GCN)、图注意力网络 (Graph Attention Networks,GAT) 和 GraphSAGE 等)的表达能力不如 WL 测试,这暴露出一个问题,因为区分更多图结构的能力与最终嵌入的质量有关。在本节中,我们将把理论框架转化为一种新的 GNN 架构——图同构网络 (Graph Isomorphism Network, GIN)。
GIN2018 年由 Xu 等人提出,旨在具有与 WL 测试相同的表达能力。作者将对聚合的观察归纳为两个函数:

  • 聚合 (Aggregate): 函数 f f f 选择 GNN 考虑的邻居节点
  • 组合 (Combine): 函数 ϕ ϕ ϕ 将所选节点的嵌入结合起来,生成目标节点的新嵌入

节点嵌入可以表达为以下形式:
h i ′ = ϕ ( h i , f ( h j : j ∈ N i ) ) h'_i=ϕ(h_i,f({h_j:j\in \mathcal N_i})) hi=ϕ(hi,f(hj:jNi))
GCN 中,函数 f f f 会聚合节点 i i i 的每个邻居,而 ϕ ϕ ϕ 则使用均值聚合算子。在 GraphSAGE 中,邻居采样就是函数 f f f ϕ ϕ ϕ 具有三个不同选项,包括均值聚合算子、长短期记忆 (long short-term memory, LSTM) 聚合算子和池化聚合算子。
而在图同构网络 (Graph Isomorphism Network, GIN) 中,这些函数是必须是单射的。如下图所示,单射函数 (injective function) 将不同的输入映射到不同的输出,这正是我们想要区分图结构的原因。如果函数不是单射的,那么不同的输入将得到相同的输出。在这种情况下,嵌入就变得不那么有价值了,因为它们包含的信息会更少。

单射函数

GIN 在设计这两个函数时,只是对这两个函数进行了近似。在 GAT 层中,我们学习了自注意力权重。在 GIN 中,我们可以利用通用近似定理,用一个多层感知机 (Multilayer Perceptron, MLP) 学习这两个函数:
h i ′ = M L P ( ( 1 + ɛ ) ⋅ h i + ∑ j ∈ N i h j ) h'_i=MLP((1+ɛ)\cdot h_i+\sum_{j\in \mathcal N_i}h_j) hi=MLP((1+ɛ)hi+jNihj)
其中, ɛ ɛ ɛ 是一个可学习的参数或固定标量,表示目标节点的嵌入与其邻居的嵌入相比的重要性。同时,MLP 必须具有多个层来区分特定的图结构。
现在,我们已经介绍了一个与 WL 测试具有相同表达能力的 GNN,但在此基础上,我们还可以进一步改进,将 WL 测试推广为一系列更高级别的测试,称为 k-WL 测试。与考虑单个节点不同,k-WL 测试考虑的是 k 元组节点。这意味着它们是非局部的,因为它们可以查看相距更远的节点,这也是 (k + 1) -WL 测试比 k-WL 测试(其中 k ≥ 2 )能区分更多图结构的原因。
目前已经提出了几种基于 k-WL 测试的架构,如 Morris 等人提出的 k-GNN。虽然这些架构有助于我们更好地理解 GNN 的工作原理,但与 GNNGAT 等表达能力较弱的模型相比,它们在实际应用中往往表现不佳,但它们也有各自合适的应用场景,接下来,我们将 GIN 应用于图分类以发挥其性能。

2. 构建 GIN 模型执行图分类

我们可以直接实现用于节点分类的图同构网络 (Graph Isomorphism Network, GIN)模型,但 GIN 架构对于执行图分类任务更加有效。在本节中,我们将了解如何使用全局池化技术将节点嵌入转化为图嵌入。然后,将这些技术应用于 PROTEINS 数据集,并对比 GIN 和图卷积网络 (Graph Convolutional Network, GCN)在图分类任务中的性能差异。

2.1 图分类任务

图分类是基于图神经网络 (Graph Neural Networks, GNN) 生成的节点嵌入进行的,但与节点层面的任务不同,图分类需要关注图数据的全局信息,需要对全局的信息进行融合学习,在图中通常采用全局池化( global pooling,也称图读出机制,graph-level readout function)来提取全局信息。三种简单的实现方法如下:

  • 全局均值池化 (Mean global pooling): 通过对图中每个节点的嵌入取平均值,得到图嵌入 h G h_G hG
    h G = 1 N ∑ i = 0 N h i h_G=\frac 1N\sum_{i=0}^Nh_i hG=N1i=0Nhi
  • 全局最大池化 (Max global pooling): 通过选择每个节点维度的最高值,得到图嵌入 h G h_G hG
    h G = m a x i = 0 N ( h i ) h_G=max_{i=0}^N(h_i) hG=maxi=0N(hi)
  • 全局求和池化 (Sum global pooling):通过对图中每个节点的嵌入求和,得到图嵌入 h G h_G hG
    h G = ∑ i = 0 N h i h_G=\sum_{i=0}^Nh_i hG=i=0Nhi

根据 Weisfeiler-Leman (WL) 测试可知,求和全局池化严格来说比其他两种池化技术更具表达能力。同时,要考虑所有结构信息,就必须考虑 GNN 每一层产生的嵌入,将 GNN k k k 个层中每层产生的节点嵌入求和后串联起来:
h G = ∑ i = 0 N h i 0 ∣ ∣ ⋯ ∣ ∣ ∑ i = 0 N h i k h_G=\sum_{i=0}^Nh_i^0||\cdots ||\sum_{i=0}^Nh_i^k hG=i=0Nhi0∣∣∣∣i=0Nhik
这种方法通过串联将求和运算符的表达能力与每层中存储的信息优雅的结合在一起。

2.2 PROTEINS 数据集分析

接下来,在 PROTEINS 数据集上使用图读出机制实现 GIN 模型。PROTEINS 数据集包含 1,113 个表示蛋白质的图,其中每个节点都是一个氨基酸。当两个节点之间的距离小于 0.6 纳米时,它们之间会有一条边相连。该数据集的目标是将每个蛋白质分类为酶或非酶,即二分类问题,酶可作为催化剂加速细胞内的化学反应,例如,脂肪酶可以帮助消化食物等,蛋白质的三维结构示例如下:

蛋白质三维结构

接下来,使用 PyTorch Geometric (PyG) 在 PROTEINS 数据集上构建 GIN 模型。

(1) 首先,使用 PyTorch GeometricTUDataset 类导入 PROTEINS 数据集并打印相关信息:

from torch_geometric.datasets import TUDataset

dataset = TUDataset(root='.', name='PROTEINS').shuffle()

# Print information about the dataset
print(f'Dataset: {dataset}')
print('-----------------------')
print(f'Number of graphs: {len(dataset)}')
print(f'Number of nodes: {dataset[0].x.shape[0]}')
print(f'Number of features: {dataset.num_features}')
print(f'Number of classes: {dataset.num_classes}')
'''
Dataset: PROTEINS(1113)
-----------------------
Number of graphs: 1113
Number of nodes: 28
Number of features: 3
Number of classes: 2
'''

(2)8:1:1 的比例将数据集拆分为训练集、验证集和测试集:

from torch_geometric.loader import DataLoader

# Create training, validation, and test sets
train_dataset = dataset[:int(len(dataset)*0.8)]
val_dataset   = dataset[int(len(dataset)*0.8):int(len(dataset)*0.9)]
test_dataset  = dataset[int(len(dataset)*0.9):]

print(f'Training set   = {len(train_dataset)} graphs')
print(f'Validation set = {len(val_dataset)} graphs')
print(f'Test set       = {len(test_dataset)} graphs')

训练集、验证集和测试集中图的数量输出如下:

Training set   = 890 graphs
Validation set = 111 graphs
Test set       = 112 graphs

(3) 使用批大小为 64DataLoader 对象将这些数据集合转换为批数据,即每批数据最多包含 64 个图:

train_loader = DataLoader(train_dataset, batch_size=64, shuffle=True)
val_loader   = DataLoader(val_dataset, batch_size=64, shuffle=True)
test_loader  = DataLoader(test_dataset, batch_size=64, shuffle=True)

(4) 打印每批数据的相关信息:

print('\nTrain loader:')
for i, batch in enumerate(train_loader):
    print(f' - Batch {i}: {batch}')

print('\nValidation loader:')
for i, batch in enumerate(val_loader):
    print(f' - Batch {i}: {batch}')

print('\nTest loader:')
for i, batch in enumerate(test_loader):
    print(f' - Batch {i}: {batch}')

批数据信息

2.3 构建 GIN 实现图分类

构建训练数据集后,开始实施 GIN 模型。首先需要考虑 GIN 层的架构,使用一个至少有两层的多层感知机 (Multilayer Perceptron, MLP) ,引入批归一化来标准化每个隐藏层的输入,用于稳定并加快训练速度。总体而言, GIN 架构如下所示:

GIN 架构

(1) 使用 PyTorch Geometric (PyG) 实现以上架构,作为对比,我们同时实现了 GCN 模型:

import torch
import torch.nn.functional as F
from torch.nn import Linear, Sequential, BatchNorm1d, ReLU, Dropout
from torch_geometric.nn import GCNConv, GINConv
from torch_geometric.nn import global_mean_pool, global_add_pool

class GCN(torch.nn.Module):
    """GCN"""
    def __init__(self, dim_h):
        super(GCN, self).__init__()
        self.conv1 = GCNConv(dataset.num_node_features, dim_h)
        self.conv2 = GCNConv(dim_h, dim_h)
        self.conv3 = GCNConv(dim_h, dim_h)
        self.lin = Linear(dim_h, dataset.num_classes)

    def forward(self, x, edge_index, batch):
        # Node embeddings 
        h = self.conv1(x, edge_index)
        h = h.relu()
        h = self.conv2(h, edge_index)
        h = h.relu()
        h = self.conv3(h, edge_index)

        # Graph-level readout
        hG = global_mean_pool(h, batch)

        # Classifier
        h = F.dropout(hG, p=0.5, training=self.training)
        h = self.lin(h)
        
        return F.log_softmax(h, dim=1)

class GIN(torch.nn.Module):
    """GIN"""
    def __init__(self, dim_h):
        super(GIN, self).__init__()
        self.conv1 = GINConv(
            Sequential(Linear(dataset.num_node_features, dim_h),
                       BatchNorm1d(dim_h), ReLU(),
                       Linear(dim_h, dim_h), ReLU()))
        self.conv2 = GINConv(
            Sequential(Linear(dim_h, dim_h), BatchNorm1d(dim_h), ReLU(),
                       Linear(dim_h, dim_h), ReLU()))
        self.conv3 = GINConv(
            Sequential(Linear(dim_h, dim_h), BatchNorm1d(dim_h), ReLU(),
                       Linear(dim_h, dim_h), ReLU()))

PyTorch Geometric 还内置了 GINE 层,GINEGIN 层的一种改进。与 GIN 相比,GINE 的主要改进在于能够在聚合过程中考虑边特征。由于 PROTEINS 数据集没有边特征,因此本节采用经典的 GIN 模型。

(2) 要进行图分类,还需要对每一层中图上每个节点嵌入进行求和。换句话说,我们需要为每一层存储一个大小为 dim_h 的向量,本节中 dim_h3。在最终的线性层之前,添加一个大小为 3 * dim_h 的线性层,用于二分类 (data.num_classes = 2):

        self.lin1 = Linear(dim_h*3, dim_h*3)
        self.lin2 = Linear(dim_h*3, dataset.num_classes)

(3) 接下来,实现连接初始化层的逻辑。每一层都会产生不同的嵌入张量——h1h2h3。使用 global_add_pool() 函数对它们进行求和,然后使用 torch.cat() 将它们串联起来。这样,就得到了分类器的输入,类似一个带有 Dropout 层的普通神经网络:

    def forward(self, x, edge_index, batch):
        # Node embeddings 
        h1 = self.conv1(x, edge_index)
        h2 = self.conv2(h1, edge_index)
        h3 = self.conv3(h2, edge_index)

        # Graph-level readout
        h1 = global_add_pool(h1, batch)
        h2 = global_add_pool(h2, batch)
        h3 = global_add_pool(h3, batch)

        # Concatenate graph embeddings
        h = torch.cat((h1, h2, h3), dim=1)

        # Classifier
        h = self.lin1(h)
        h = h.relu()
        h = F.dropout(h, p=0.5, training=self.training)
        h = self.lin2(h)
        
        return F.log_softmax(h, dim=1)

(4) 使用批数据实现一个常规的训练循环,共训练 100epoch

def train(model, loader):
    criterion = torch.nn.CrossEntropyLoss()
    optimizer = torch.optim.Adam(model.parameters(), lr=0.01)
    epochs = 100

    model.train()
    for epoch in range(epochs+1):
        total_loss = 0
        acc = 0
        val_loss = 0
        val_acc = 0

        # Train on batches
        for data in loader:
            optimizer.zero_grad()
            out = model(data.x, data.edge_index, data.batch)
            loss = criterion(out, data.y)
            total_loss += loss / len(loader)
            acc += accuracy(out.argmax(dim=1), data.y) / len(loader)
            loss.backward()
            optimizer.step()

            # Validation
            val_loss, val_acc = test(model, val_loader)

(5)20epoch 打印一次训练和验证准确率,并返回训练后的模型:

        # Print metrics every 20 epochs
        if(epoch % 20 == 0):
            print(f'Epoch {epoch:>3} | Train Loss: {total_loss:.2f} | Train Acc: {acc*100:>5.2f}% | Val Loss: {val_loss:.2f} | Val Acc: {val_acc*100:.2f}%')
            
    return model

(6)test() 方法中也必须使用批处理,因为验证和测试加载器同样包含多个批数据:

@torch.no_grad()
def test(model, loader):
    criterion = torch.nn.CrossEntropyLoss()
    model.eval()
    loss = 0
    acc = 0

    for data in loader:
        out = model(data.x, data.edge_index, data.batch)
        loss += criterion(out, data.y) / len(loader)
        acc += accuracy(out.argmax(dim=1), data.y) / len(loader)

    return loss, acc

(7) 定义用于计算准确率的函数:

def accuracy(pred_y, y):
    """Calculate accuracy."""
    return ((pred_y == y).sum() / len(y)).item()

(8) 实例化并训练 GCNGIN 模型:

print('GCN training')
gcn = GCN(dim_h=32)
gcn = train(gcn, train_loader)
print('GIN training')
gin = GIN(dim_h=32)
gin = train(gin, train_loader)

模型训练过程

(9) 使用测试加载器测试训练后的模型:

test_loss, test_acc = test(gcn, test_loader)
print(f'GCN test Loss: {test_loss:.2f} | GCN test Acc: {test_acc*100:.2f}%')

test_loss, test_acc = test(gin, test_loader)
print(f'Gin test Loss: {test_loss:.2f} | Gin test Acc: {test_acc*100:.2f}%')

# GCN test Loss: 0.56 | GCN test Acc: 65.70%
# GIN test Loss: 0.46 | GIN test Acc: 78.12%

2.4 GCN 与 GIN 性能差异分析

根据上一小节的结果可以看出,用简单的全局均值池( PyTorch Geometric 中使用 global_mean_pool() 实现)实现 GCN 执行图分类,在完全相同的设定下,进行 100 次实验的平均准确率为 72.72%(±0.73%)。这远低于 GIN 模型的平均准确率 77.57% (±1.77%)
据此,可以得出结论,GIN 架构比 GCN 更适合图分类任务。根据 WL 测试理论框架,这是因为 GCN不如 GIN 的表达能力强。换句话说,GINGCN 能区分更多的图结构,这也是它更准确的原因。可以通过可视化两种模型错误分类的图来验证这一假设。

(1) 导入 matplotlibnetworkx 库,用于绘制蛋白质结构:

import numpy as np
import matplotlib.pyplot as plt
import networkx as nx
from torch_geometric.utils import to_networkx

fig, ax = plt.subplots(4, 4)
fig.suptitle('GIN - Graph classification')

(2) 对于每个蛋白质,使用训练后的 GIN 获取最终分类结果。如果预测是正确的,将其绘制为绿色(否则绘制为红色):

for i, data in enumerate(dataset[-16:]):
    # Calculate color (green if correct, red otherwise)
    out = gin(data.x, data.edge_index, data.batch)
    color = "green" if out.argmax(dim=1) == data.y else "red"

(3) 为了方便起见,将蛋白质转换成 networkx 图,然后使用 nx.draw_networkx() 函数进行绘制:

    ix = np.unravel_index(i, ax.shape)
    ax[ix].axis('off')
    G = to_networkx(dataset[i], to_undirected=True)
    nx.draw_networkx(G,
                    pos=nx.spring_layout(G),
                    with_labels=False,
                    node_size=10,
                    node_color=color,
                    width=0.8,
                    ax=ax[ix])
plt.show()

GIN 模型分类结果的准确性如下所示:

GIN 模型性能

(4)GCN 模型重复以上过程:

fig, ax = plt.subplots(4, 4)
fig.suptitle('GCN - Graph classification')

for i, data in enumerate(dataset[-16:]):
    # Calculate color (green if correct, red otherwise)
    out = gcn(data.x, data.edge_index, data.batch)
    color = "green" if out.argmax(dim=1) == data.y else "red"

    # Plot graph
    ix = np.unravel_index(i, ax.shape)
    ax[ix].axis('off')
    G = to_networkx(dataset[i], to_undirected=True)
    nx.draw_networkx(G,
                    pos=nx.spring_layout(G),
                    with_labels=False,
                    node_size=10,
                    node_color=color,
                    width=0.8,
                    ax=ax[ix])
plt.show()

GCN分类结果

可以看到,GCN 模型将更多的图错误分类。要了解哪些图结构没有被充分捕捉,需要对 GIN 正确分类的每个蛋白质进行大量分析。但可以看到 GIN 也出现了不同的错误,这证明了这些模型可以互补。

3. 提升模型性能

在机器学习中,将出现不同错误的模型集成为一个更优秀的模型是一种常见技术。可以采用不同的方法,例如在最终分类的基础上训练第三个模型。为了简单起见,本节中我们将实现一种简单的模型平均技术:

(1) 首先,我们将模型设置为评估模式 (eval()),并定义变量用于存储准确率:

gcn.eval()
gin.eval()
acc_gcn = 0
acc_gin = 0
acc_ens = 0

(2) 得到每个模型的最终分类结果,然后将它们组合起来,作为集成模型的预测结果:

for data in test_loader:
    # Get classifications
    out_gcn = gcn(data.x, data.edge_index, data.batch)
    out_gin = gin(data.x, data.edge_index, data.batch)
    out_ens = (out_gcn + out_gin)/2

(3) 计算三个模型预测的准确率:

    # Calculate accuracy scores
    acc_gcn += accuracy(out_gcn.argmax(dim=1), data.y) / len(test_loader)
    acc_gin += accuracy(out_gin.argmax(dim=1), data.y) / len(test_loader)
    acc_ens += accuracy(out_ens.argmax(dim=1), data.y) / len(test_loader)

(4) 最后,打印模型的准确率:

# Print results
print(f'GCN accuracy:     {acc_gcn*100:.2f}%')
print(f'GIN accuracy:     {acc_gin*100:.2f}%')
print(f'GCN+GIN accuracy: {acc_ens*100:.2f}%')
'''
GCN accuracy:     73.70%
GIN accuracy:     78.91%
GCN+GIN accuracy: 79.43%
'''

在本节示例中,集成模型的预测结果优于其它两个模型,准确率为 79.43% (GCN73.70%GIN78.91%)。模型集成技术的准确率提升相当显著,为构建高性能模型提供了更多可能性。然而,这并不一定是普遍情况,即使在本例中,集成模型的表现也并不总是优于 GIN。可以用其他架构(如 Node2Vec )的嵌入来丰富集成模型,并观察是否能提高最终的准确率。

小结

图同构网络 (Graph Isomorphism Network, GIN) 架构受 WL 测试启发而设计的,其表达能力与 WL 测试相近,因此在严格意义上比 GCNGATGraphSAGE 更具表达能力。在本节中,将这一架构用于图分类任务,介绍了将节点嵌入融合到图嵌入中的不同方法,GIN 通过连接求和运算符和每个 GIN 层产生图嵌入,其性能明显优于通过 GCN 层获得的经典全局均值池化。最后,我们将两个模型的预测结果进行简单的集成,从而进一步提高了准确率。

系列链接

图神经网络实战(1)——图神经网络(Graph Neural Networks, GNN)基础
图神经网络实战(2)——图论基础
图神经网络实战(3)——基于DeepWalk创建节点表示
图神经网络实战(4)——基于Node2Vec改进嵌入质量
图神经网络实战(5)——常用图数据集
图神经网络实战(6)——使用PyTorch构建图神经网络
图神经网络实战(7)——图卷积网络(Graph Convolutional Network, GCN)详解与实现
图神经网络实战(8)——图注意力网络(Graph Attention Networks, GAT)
图神经网络实战(9)——GraphSAGE详解与实现
图神经网络实战(10)——归纳学习
图神经网络实战(11)——Weisfeiler-Leman测试

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

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

相关文章

simplicity studio 5 修改设备电压

工装板的soc额定输入电压为1.5v,而常态下ttl高电平为5v/3.3v,所以需要设定烧录程序时的设备电压。 确保连接设备,并且被识别。 进入管理员模式。 烧录.hex文件快捷方法。

基于springboot实现毕业论文管理系统项目【项目源码+论文说明】计算机毕业设计

基于springboot实现毕业论文管理系统演示 摘要 现代经济快节奏发展以及不断完善升级的信息化技术,让传统数据信息的管理升级为软件存储,归纳,集中处理数据信息的管理方式。本毕业论文管理系统就是在这样的大环境下诞生,其可以帮助…

Linux 35.5 + JetPack v5.1.3@ ego-planner编译安装

Linux 35.5 JetPack v5.1.3 ego-planner编译安装 1. 源由2. 编译&安装Step 1:依赖库安装Step 2:建立工程Step 3:编译工程Step 4:安装工程 3. 问题汇总3.1 planner/plan_env - OpenCV3.2 uav_simulator/local_sensing - CUDA优…

python元组容器

""" 元组 ---不可变的容器 """ # 不写逗号就是字符串 fruits (watermelon) print(type(fruits)) # 一元组需要加逗号 fruits (watermelon,) print(type(fruits))# 元组运算--重复运算 fruits (watermelon, apple, banana, peach, apple) pri…

UE5-人物角色动画蓝图

这里主要从零给角色创建移动的蓝图,包含多种状态 创建 首先在角色骨骼网格体上右键创建动画蓝图 进入,在AnimGraph界面创建一个状态机(stateMachine) Idle 进入状态机,拉出来创建一个newState,这里命名…

QML应用添加网络代理

在QML应用中我们可以通过QNetworkProxy和QNetworkAccessManager类给应用添加网络代理。QNetworkProxy是Qt网络模块中的一个类,用于配置网络请求的代理服务器。通过使用代理服务器,我们可以控制应用程序的网络流量,实现网络请求的转发、监视、和过滤等功能。代理服务器在很多…

【Linux】(一)——Linux基础和Linux命令基础语法

目录 Linux基础Linux发行版本Linux终端Linux命令 Linux基础 Linux,通常指的是GNU/Linux操作系统,这是一个开源且免费使用的类UNIX操作系统。它的核心组件——Linux内核,由林纳斯托瓦兹(Linus Torvalds)在1991年10月5日…

在应对流量攻击时,如何提高pcdn的可用性(贰)?

在应对流量攻击时,提高PCDN(Private Content Delivery Network)的可用性是一项关键任务。以下是一些建议措施: 一.优化资源分配与负载均衡: 监测各个节点的流量和负载情况,确保在流量攻击时能…

【嵌入式DIY实例】-OLED显示网络时钟

OLED显示网络时钟 文章目录 OLED显示网络时钟1、硬件准备与接线2、代码实现在上一个ESP8266 NodeMCU文章中,我们用DS3231 RTC芯片和SSD1306 OLED制作了一个简单的实时时钟,时间和日期显示在SSD1306屏幕上,并且可以通过两个按钮进行设置。 在本中,我们将使用ESP 8266 NodeMC…

一种基于小波域的增强独立分量分析方法(MATLAB)

独立分量分析ICA是二十世纪九十年代发展起来的一种多元统计和计算技术,目的是用来分离或提取随机变量、观测数据或信号混合物中具有独立特性的隐藏分量。ICA可以看作是主分量分析PCA和因子分析FA的扩展。与PCA和FA相比,ICA是一种更强有力的技术。当PCA和…

护眼落地台灯十大品牌有必要买吗?十大落地台灯品牌推荐

护眼落地台灯十大品牌有必要买吗?家里的台灯照明范围有限,阅读面和房间的空间光线亮暗差大,眼睛需要同时适应亮和暗两种光线,频繁收缩,极易眼疲劳。再加上学习时间过长,眼睛得不到休息,从而引起…

八股文系列Redis

缓存穿透 缓存穿透指什么 缓存穿透是指查询⼀个在缓存和数据库中都不存在的数据。由于缓存没有这个数据,所以每次查询都会“穿透”缓存直接查询数据库,如果有⼤量此类查询,会给数据库带来极⼤的压⼒。 查询流程 解决方案 设置value为null …

2024-03 GESP C++ 二级试题及答案

2024-03 GESP C 二级试题及答案单选题(每题 2 分,共 30 分)第 1 题 下列流程图的输出结果是?( ) A. 优秀 B. 良好 C. 不及格 D. 没有输出 第 2 题 以下选项中不符合 C变量命名规则的是?( ) A.…

【基于 PyTorch 的 Python 深度学习】8 注意力机制(2):Transformer(上)

前言 文章性质:学习笔记 📖 学习资料:吴茂贵《 Python 深度学习基于 PyTorch ( 第 2 版 ) 》【ISBN】978-7-111-71880-2 主要内容:根据学习资料撰写的学习笔记,该篇主要介绍了 Transformer 的主要功能、总体架构、相关…

2.1 初识Windows程序

Windows程序设计是一种面向对象的编程。Windows操作系统以数据结构的形式定义了大量预定义的对象作为操作系统的数据类型。Windows动态链接库提供了各种各样的API接口函数供Windows应用程序调用。一个Windows应用程序是运行在Windows操作系统之上的。这些API接口函数的调用所实…

MKS AX7680 SERIES 电源使用说明手侧

MKS AX7680 SERIES 电源使用说明手侧

【Python报错】AttributeError: ‘NoneType‘ object has no attribute ‘xxx‘

成功解决“AttributeError: ‘NoneType’ object has no attribute ‘xxx’”错误的全面指南 一、引言 在Python编程中,AttributeError是一种常见的异常类型,它通常表示尝试访问对象没有的属性或方法。而当我们看到错误消息“AttributeError: ‘NoneTyp…

Face Forgery Detection by 3D Decomposition

文章目录 Face Forgery Detection by 3D Decomposition研究背景研究目标创新点方法提出问题研究过程技术贡献实验结果未来工作Face Forgery Detection by 3D Decomposition 会议:CVPR2021 作者: 研究背景 面部伪造引发关注传统面部伪造检测主要关注原始RGB图像

linux进程家族-管理子进程,确保进程族稳健运行

在Linux环境下,进程之间相互影响、相互依赖,就像一个大家庭一样。作为程序员,我们不仅需要创建子进程,更要熟练掌握监控和管理子进程的技能,确保整个进程族能稳健高效地运行。本文将为你揭开进程创建、监控子进程、处理…

【Vue】小黑记事本

文章目录 一、需求说明二、思路分析三、代码实现 一、需求说明 拆分基础组件渲染待办任务添加任务删除任务底部合计 和 清空功能持久化存储 二、思路分析 拆分基础组件 咱们可以把小黑记事本原有的结构拆成三部分内容:头部(TodoHeader)、列…