GCN原理回顾

news2025/1/26 15:49:24

Cora_dataset description

Cora数据集是一个常用的学术文献用网络数据集,用于研究学术文献分类和图网络分析等任务。
该数据集由机器学习领域的博士论文摘要组成,共计2708篇论文,涵盖了7个不同的学科领域。每篇论文都有一个唯一的ID,并且被分为以下7个类别之一:Case_Based、Genetic_Algorithms、Neural_Networks、Probabilistic_Methods、Reinforcement_Learning、Rule_Learning和Theory。

除了论文之间的引用关系外,Cora数据集还包含了每篇论文的词袋表示,即将每篇论文表示为一个词频向量(0-1嵌入,每行有多个1,非one-hot vector,feature of node)。这些词频向量表示了论文中出现的单词及其在该论文中的出现频率。

Cora数据集常用于图神经网络的研究和评估,可以用于学术文献分类、引文网络分析、节点嵌入等任务。

print cora

dataset = Planetoid("./tmp/Cora", name="Cora", transform=T.NormalizeFeatures())
num_nodes = dataset.data.num_nodes
# For num. edges see:
# - https://github.com/pyg-team/pytorch_geometric/issues/343
# - https://github.com/pyg-team/pytorch_geometric/issues/852

num_edges = dataset.data.num_edges // 2
train_len = dataset[0].train_mask.sum()
val_len = dataset[0].val_mask.sum()
test_len = dataset[0].test_mask.sum()
other_len = num_nodes - train_len - val_len - test_len
print(f"Dataset: {dataset.name}")
print(f"Num. nodes: {num_nodes} (train={train_len}, val={val_len}, test={test_len}, other={other_len})")
print(f"Num. edges: {num_edges}")
print(f"Num. node features: {dataset.num_node_features}")
print(f"Num. classes: {dataset.num_classes}")
print(f"Dataset len.: {dataset.len()}")

在这里插入图片描述

GCN原理与实现

卷积公式: f ∗ g = F − 1 ( F ( f ) ⋅ F ( g ) ) f*g=F^{-1}(F(f)·F(g)) fg=F1(F(f)F(g))
给定一个图信号x和一个卷积核, x ∗ g = U ( U T x ⊙ U T g ) = U ( U T x ⊙ g θ ) = D ~ − 0.5 A ~ D ~ − 0.5 X Θ x*g=U(U^Tx\odot U^Tg)=U(U^Tx\odot g_{\theta})=\widetilde D^{-0.5}\widetilde A\widetilde D^{-0.5}X\Theta xg=U(UTxUTg)=U(UTxgθ)=D 0.5A D 0.5XΘ
其中A为图的邻接矩阵,D为图的度数矩阵,
D ~ = D + γ I , A ~ = A + γ I \widetilde D=D+\gamma I,\widetilde A=A+\gamma I D =D+γI,A =A+γI,添加自环以缩小 λ \lambda λ(Laplace matrix)

1.computation of D ~ − 0.5 A ~ D ~ − 0.5 \widetilde D^{-0.5}\widetilde A\widetilde D^{-0.5} D 0.5A D 0.5

def gcn_norm(edge_index, edge_weight=None, num_nodes=None,
add_self_loops=True, flow="source_to_target", dtype=None):
fill_value = 1.
num_nodes = maybe_num_nodes(edge_index, num_nodes)
if add_self_loops: #添加自环
edge_index, edge_weight = add_remaining_self_loops(
edge_index, edge_weight, fill_value, num_nodes)
edge_weight = torch.ones((edge_index.size(1), ), dtype=dtype,
device=edge_index.device)
row, col = edge_index[0], edge_index[1]
idx = col
deg = scatter(edge_weight, idx, dim=0, dim_size=num_nodes, reduce='sum')
deg_inv_sqrt = deg.pow_(-0.5)
deg_inv_sqrt.masked_fill_(deg_inv_sqrt == float('inf'), 0)
edge_weight = deg_inv_sqrt[row] * edge_weight * deg_inv_sqrt[col]
return edge_index, edge_weight

代码解释

edge_index, edge_weight = add_remaining_self_loops(edge_index, edge_weight,fill_value, num_nodes)
D ~ = D + γ I , A ~ = A + γ I \widetilde D=D+\gamma I,\widetilde A=A+\gamma I D =D+γI,A =A+γI;
deg = scatter(edge_weight, idx, dim=0, dim_size=num_nodes, reduce='sum')
根据edge_weight和idx=edge_index[1]得到度数矩阵,deg=D

  • explantation:edge_weight是要放入的对角阵,

deg_inv_sqrt = deg.pow_(-0.5):require D − 0.5 D^{-0.5} D0.5
deg_inv_sqrt.masked_fill_(deg_inv_sqrt == float('inf'), 0)
由于D非对角元=0,其-0.5次幂=∞,需要转化为0,
edge_weight = deg_inv_sqrt[row] * edge_weight * deg_inv_sqrt[col]
输出归一化后的edge_index

2. PairNorm

在这里插入图片描述

3.GCNConv的实现如下(删改自torch_geometric.nn.GCNConv)

class myGCNConv2(MessagePassing):
    def __init__(self, in_channels: int, out_channels: int,
                 add_self_loops: bool = True,bias: bool = True):
        super().__init__()

        self.in_channels = in_channels
        self.out_channels = out_channels
        self.add_self_loops = add_self_loops

        self.lin = Linear(in_channels, out_channels, bias=False,
                          weight_initializer='glorot')

        if bias:
            self.bias = Parameter(torch.Tensor(out_channels))
        else:
            self.register_parameter('bias', None)

        self.reset_parameters()

    def reset_parameters(self):
        super().reset_parameters()
        self.lin.reset_parameters()         #卷积层
        zeros(self.bias)                    #偏置层

    def forward(self, x: Tensor, edge_index: Adj,
                edge_weight: OptTensor = None) -> Tensor:

        edge_index, edge_weight = gcn_norm(  # yapf: disable
            edge_index, edge_weight, x.size(self.node_dim),
            self.add_self_loops, self.flow, x.dtype)

        x = self.lin(x)

        # propagate_type: (x: Tensor, edge_weight: OptTensor)
        out = self.propagate(edge_index, x=x, edge_weight=edge_weight,
                             size=None)

        if self.bias is not None:
            out = out + self.bias

        return out

    def message(self, x_j: Tensor, edge_weight: OptTensor) -> Tensor:
        return x_j if edge_weight is None else edge_weight.view(-1, 1) * x_j

    def message_and_aggregate(self, adj_t: SparseTensor, x: Tensor) -> Tensor:
        return spmm(adj_t, x, reduce=self.aggr)

代码解释

x = self.lin(x) X ′ = X Θ , X ∈ R n ∗ d 1 , Θ ∈ R d 1 ∗ d 2 X'=X\Theta,X\in R^{n*d1},\Theta \in R^{d1*d2} X=XΘXRnd1ΘRd1d2,对X降维
out = self.propagate(edge_index, x=x, edge_weight=edge_weight,size=None)
out= A ′ X ′ = D ~ − 1 2 A ~ D ~ − 1 2 X Θ A'X'=\widetilde D^{-\frac 1 2}\widetilde A \widetilde D^{-\frac 1 2 } X \Theta AX=D 21A D 21XΘ
Converge {x1’,…,xn’} ,each of which be a sampled vector,into target form.

message&message_and_aggregate为MessagePassing.propagate的相关函数,
经测试,删除后,val acc下降,故予以保留

4.Net(GCN)的实现

class GCN(torch.nn.Module):
def __init__(
self,
num_node_features: int,
num_classes: int,
hidden_dim: int = 16,
dropout_rate: float = 0.5,
) -> None:
super().__init__()
self.dropout1 = torch.nn.Dropout(dropout_rate)
self.conv1 = myGCNConv2(num_node_features,
hidden_dim,add_self_loops=True)
self.relu = torch.nn.ReLU(inplace=True)
self.dropout2 = torch.nn.Dropout(dropout_rate)
self.conv2 = myGCNConv2(hidden_dim, num_classes,add_self_loops=True)
self.pn=PairNorm()
def forward(self, x: Tensor, edge_index: Tensor) -> torch.Tensor:
x = self.pn(x)
x = self.dropout1(x)
x = self.conv1(x, edge_index)
x = self.relu(x)
x = self.dropout2(x)
x = self.conv2(x, edge_index)
return x

代码解释

x = self.pn(x):对x作PairNorm处理,之后xi~N(0,s2),各节点特征范数大小平衡,作用不明显;
采用2层GCN卷积层,中间用relu激活,dropout避免过拟合

DropEdge Realization的手动实现

  • idea
  1. 首先把有向图的边,转化为无向图的边,保存在single_edge_index中,实现时先用single_edge字
    典存储每条无向边(key-value 任意)1次,再把single_edge转化成无向图的边集索引(2-dim tensor
    array)
#single_edge_index
single_edge={}
for i in range(len(dataset.data.edge_index[0])):
    if(((dataset.data.edge_index[0][i],dataset.data.edge_index[1][i]) not in single_edge.items()) and 
        ((dataset.data.edge_index[1][i],dataset.data.edge_index[0][i]) not in single_edge.items())):
        single_edge[dataset.data.edge_index[0][i]]=dataset.data.edge_index[1][i]

single_edge_index=[[],[]]

for key,value in single_edge.items():
    single_edge_index[0].append(key)
    single_edge_index[1].append(value)        

single_edge_index=torch.tensor(single_edge_index)
  1. 再把无向边集舍去dropout_rate比例的部分,之后转成有向边集索引
def drop_edge(single_edge_index, dropout_rate):
    # 计算需要丢弃的边数
    num_edges = single_edge_index.shape[1]
    num_drop = int(num_edges * dropout_rate)

    # 随机选择要丢弃的边
    remain_indices = torch.randperm(num_edges)[num_drop:]
    remain_single_edges = single_edge_index[:, remain_indices]
    reverse_edges = torch.stack([remain_single_edges[1],remain_single_edges[0]],dim=0)
    remain_edges=torch.cat([remain_single_edges,reverse_edges],dim=1)

    return remain_edges

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

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

相关文章

桥接模式: 消息发送器设计

桥接模式是一种结构型设计模式,它将抽象部分与它的实现部分分离,使它们可以独立地变化。桥接模式通过将抽象和实现分离,可以让它们可以独立地变化,从而提高系统的灵活性和可扩展性。 在桥接模式中,有两个重要的概念&a…

JavaBoy假期如何学习项目?弯道块才是真的快!

至暗时刻 老话说的好,弯道快才是真的快,谁直线不会加油?每到假期都是在座的各位弯道超车的时候。转眼自己已经出来搬了快四年砖头了,偶尔访问下牛客发现行情真是一年不如一年。。。不由得回想起自己春招时候的经历。 回想起2020年…

数据分析-Pandas数据的直方图探查

数据分析-Pandas数据的直方图探查 数据分析和处理中,难免会遇到各种数据,那么数据呈现怎样的规律呢?不管金融数据,风控数据,营销数据等等,莫不如此。如何通过图示展示数据的规律? 数据表&…

【贪心算法】Leetcode 455.分发饼干 376. 摆动序列 53. 最大子数组和

【贪心算法】Leetcode 455 分发饼干 376. 摆动序列【规律很多】53. 最大子数组和 455 分发饼干局部最优推全局最优:尽量用大饼干去满足大胃口的小朋友 376. 摆动序列【规律很多】思想:注意考虑一个坡度留首尾两个点、平坡、首尾 53. 最大子数组和【好思想…

FreeRTOS学习笔记-基于stm32(1)任务基础知识

一、裸机与RTOS 我们使用的32板子是裸机,又称前后台系统。裸机有如下缺点: 1、实时性差。只能一步一步执行任务,比如在一个while循环中,要想执行上一个任务,就必须把下面的任务执行完,循环一遍后才能执行…

从0开始学习NEON(2)

1、前言 继上一个例子,本次继续来学习NEON,本次学习NEON中向量拼接的操作,主要应用在图像的padding中。 https://blog.csdn.net/weixin_42108183/article/details/136440707 2、案例 2.1 案例1 在某些情况下,需要取在每个向量…

轻松压缩照片大小:简单实用的方法

当您需要通过网络传输或共享照片时,较小的文件大小可以提高传输速度并减少带宽消耗。这适用于通过电子邮件、社交媒体、即时消息应用程序等发送照片的场景。为了解决这个问题,本文将介绍一些简单而有效的方法来压缩照片的大小,以便更方便地分…

python并发编程:IO模型

一 IO模型 二 network IO 再说一下IO发生时涉及的对象和步骤。对于一个network IO \(这里我们以read举例\),它会涉及到两个系统对象,一个是调用这个IO的process \(or thread\),另一个就是系统内核\(kernel\)。当一个read操作发生时&#xff…

面试经典150题——基本计算器

​A husband is a man of many miles. ——Unknown 1. 题目描述 2. 题目分析与解析 2.1 思路一——先算括号内的内容 这个题目其实就是编译原理中很小的一个模块了,基本思路还是通过栈来实现。题目的难点主要在: 其中括号优先级的处理,以…

Spring揭秘:ImportBeanDefinitionRegistrar应用场景及实现原理!

内容概念 ImportBeanDefinitionRegistrar接口提供了强大的动态注册Bean的能力,它允许开发者在Spring容器初始化时,灵活地根据特定条件或需求来添加或修改Bean定义,从而实现更为精细的控制和扩展性。这是构建可扩展框架、插件系统或处理复杂配…

请说说你对Vue模板编译的理解

Vue模板编译是Vue.js框架的核心之一,它负责将Vue模板转换成渲染函数,从而实现模板的解析和渲染。要深入了解Vue模板编译,我们需要从编译过程、作用、特点等方面进行详细解析。 1. Vue模板编译的作用 Vue模板编译的主要作用是将Vue模板字符串…

021—pandas 书单整理将同一种书整理在一起

前言 在办公自动化场景下,最常见的需求就是信息的整理,pandas 最擅长复杂数据逻辑的处理,能够让整理工作更加高效,同时不容易出错。今天的案例是将一个平铺的书单按品类进行整理,合并为一行。 需求: 将书按书名进行合…

【python高级编程教程】笔记(python教程、python进阶)第三节:(1)多态与鸭子类型(Polymorphism and Duck Typing)

参考文章1:【比刷剧还爽】清华大佬耗时128小时讲完的Python高级教程!全套200集!学不会退出IT界! 参考文章2:清华教授大力打造的Python高级核心技术!整整100集,强烈建议学习(Python3…

基于springboot的母婴商城

摘 要 现代经济快节奏发展以及不断完善升级的信息化技术,让传统数据信息的管理升级为软件存储,归纳,集中处理数据信息的管理方式。本母婴商城系统就是在这样的大环境下诞生,其可以帮助管理者在短时间内处理完毕庞大的数据信息&am…

Windows10安装Docker

使用 PowerShell 启用 Hyper-V 以管理员身份打开 PowerShell 控制台。 运行以下命令: PowerShell复制 Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Hyper-V -All如果无法找到此命令,请确保你以管理员身份运行 PowerShell。 安装…

Claude3深夜震撼发布!模型特点分析,附使用教程

Claude3深夜震撼发布!模型特点分析,附使用教程 引言 最新发布的Claude3引起了广泛关注,这次发布一举推出了三个不同类型的模型,分别是Claude 3 Haiku、Claude 3 Sonnet和Claude 3 Opus。每个模型都具有独特的特点和能力&#xff…

Objective-C blocks 概要

1.block的使用 1.1什么是block? Blocks是C语言的扩充功能:带有自动变量(局部变量)的匿名函数。 “带有自动变量”在Blocks中表现为“截取自动变量" “匿名函数”就是“不带名称的函数” 块,封装了函数调用及调用…

代码随想录算法训练营Day38 || leetCode 7509. 斐波那契数 || 70. 爬楼梯 || 746. 使用最小花费爬楼梯

动态规划和我们数电中学习的时序电路类似,某一时刻的状态不仅与当前时刻的输入有关,还与之前的状态有关,所以推导过程中我们需要模拟题目中的情况,来找到每一时刻状态间的关系。 做题思路如下 509. 斐波那契数 此题简单 状态方程…

【QT】创建第一个QT程序

下面的前7个可以先不看,直接从8开始看 1. 创建Qt程序 一个Qt程序的组成部分:应用程序类,窗口类应用程序类个数:有且只有一个QApplication a;如何查看类对应的模块:光标移动到类上,F1qmake模块的名字 2. …

Java开发从零开始,mysql面试题索引

前言 “金九银十”的秋招热潮已经开始了,经过7月8月这两个月的提前批,终于成功拿下了一些大厂的offer。小编经过这么多次的面试,这两天整理了一份面试清单分享给大家,希望能给大家一点帮助(java方向),觉得有帮助的同学…