单样本实现注意力机制和邻域采样的GCN

邻域采样

import numpy as np


def sampling(src_nodes, sample_num, neighbor_table):
    '''
    根据源节点采样指定数量的邻居节点,注意使用的是有放回的采样;
    某个节点的邻居节点数量少于采样数量时,采样结果出现重复的节点
    :param src_nodes {list, ndarray}: 源节点列表
    :param sample_num {int}: 需要采样的节点数
    :param neighbor_table {dict}: 节点到其邻居节点的映射表
    :return: 采样结果构成的列表
    '''

    results = []
    for sid in src_nodes:
        # 从节点的邻居忠进行有放回的采样
        res = np.random.choice(neighbor_table[sid], size=(sample_num, ))
        results.append(res)
    return np.asarray(results).flatten()

def multihop_sampling(src_nodes, sample_nums, neighbor_table):
    '''
    根据源节点进行多阶采样
    :param src_nodes {list, np.ndarray}: 源节点id
    :param sample_nums {list of int}: 每一阶需要采样的个数
    :param neighbor_table {dict}: 节点到其邻居节点的映射
    :return [list of ndarray]: 每一阶的采样结果
    '''

    sampling_result = [src_nodes]
    for k, hopk_num in enumerate(sample_nums):
        hopk_result = sampling(sampling_result[k], hopk_num, neighbor_table)
        sampling_result.append(hopk_result)
    return sampling_result

GCN_ATT

import torch.nn as nn
import torch
import torch.nn.init as init
import scipy.sparse as sp
import numpy as np
import torch.nn.functional as F
import torch.optim as optim
from random import shuffle

class GraphConvolutionLayerWithAttention(nn.Module):
    '''定义一个图卷积层'''
    def __init__(self, nodes_num, input_dim, output_dim, use_bias = True):
        '''
        节点输入特征维度
        :param input_dim: int
        输出特征维度
        :param output_dim: int
        是否使用偏置项
        :param use_bias: bool, optional
        '''
        # 构造函数
        # 按照框架继承了nn.Module类,就必须首先调用父类的构造函数
        super(GraphConvolutionLayerWithAttention, self).__init__()
        # 声明网络结构和参数
        self.input = input_dim
        self.labels = output_dim
        self.use_bias = use_bias
        self.weight = nn.Parameter(torch.Tensor(input_dim, output_dim))
        self.attention = nn.Parameter(torch.Tensor(nodes_num, nodes_num))

        if self.use_bias:
            self.bias = nn.Parameter(torch.Tensor(output_dim))
        else:
            self.register_parameter('bias', None)
        self.reset_parameters()

    def reset_parameters(self):
        # 正态分布初始化
        init.kaiming_uniform_(self.weight)
        init.kaiming_uniform_(self.attention)
        if self.use_bias:
            # 零值初始化
            init.zeros_(self.bias)

    def forward(self, adjacency, input_feature):
        '''
        邻接矩阵
        邻接矩阵是稀疏矩阵,因此在计算时使用稀疏矩阵乘法。
        :param adjacency: torch.sparse.FloatTensor
        输入特征
        :param input_feature: torch.Tensor
        :return:
        '''
        # 前向传播
        # 矩阵乘法用torch.mm哈达玛积用torch.mul或*
        support = torch.mm(input_feature, self.weight)
        output = torch.mm(self.attention * adjacency, support)
        #output = torch.sparse.mm(adjacency, support)
        if self.use_bias:
            output += self.bias
        return output



class GCN_ATT(nn.Module):
    def __init__(self, BatchNodesNum, input_dim):
        super(GCN_ATT, self).__init__()
        self.BatchNodesNum = BatchNodesNum
        self.gcn1 = GraphConvolutionLayerWithAttention(BatchNodesNum, input_dim, 16)
        self.gcn2 = GraphConvolutionLayerWithAttention(BatchNodesNum, 16, 2)

    def forward(self, adjacency, feature):
        h = F.relu(self.gcn1(adjacency, feature))
        logits = self.gcn2(adjacency, h)
        return logits[0] #以第一行作为本次Batch的中心目标node

def LoadByIndices(url, Indices):
    # 逐个按照idx加载数据文件,存入BatchSamples并返回。
    BatchSamples = []
    for idx in Indices:
        file = url + str(idx) + '.npy'
        BatchSamples.append(np.load(file))
    return BatchSamples

def normalization(adjacency):
    '''计算 L = A+I'''
    # 构造单位阵
    adjacency += sp.eye(adjacency.shape[0])
    # tocoo将矩阵转化为scipy.coo_matrix稀疏矩阵
    return adjacency.tocoo()

# 超参数定义
learning_rate = 0.1
weight_decay = 5e-4
epochs = 200
# 模型定义,包括模型实例化、损失函数与优化器定义
device = "cuda" if torch.cuda.is_available() else "cpu"
model = GCN_ATT().to(device)
# 损失函数使用交叉熵
criterion = nn.CrossEntropyLoss().to(device)
# 优化器使用Adam
optimizer = optim.Adam(model.parameters(), lr=learning_rate, weight_decay=weight_decay)



# 加载数据,并转换为torch.Tensor
#dataset = CoraData().data

# 样本总数
AllSampleNum = 20000
train_per = 0.3
train_sample_num = int(AllSampleNum*train_per)
indices = np.random.permutation(AllSampleNum)
train_indices = indices[:train_sample_num]
test_indeces = indices[train_sample_num:]

# 样本加载地址
samples_url = 'D:\\Samples\\Sample'
adjacencys_url = 'D:\\Adjacencys\\Adjacencys'
y_url = 'D:\\y\\y'

def train():
    loss_history = []
    val_acc_history = []
    train_y = LoadByIndices(y_url, train_indices)
    test_y = LoadByIndices(y_url, test_indeces)
    for epoch in range(epochs):
        # 每个epoch开始时,对样本索引进行重新打散
        shuffle(train_indices)

        for idx in train_indices:
            tensor_x = LoadByIndices(samples_url, idx)[0]
            tensor_adjacency = LoadByIndices(adjacencys_url, idx)[0]
            normal_adjacency = normalization(tensor_adjacency)
            logits = model(normal_adjacency, tensor_x)# 前向传播
            loss = criterion(logits, train_y[idx])
            # 梯度归零
            optimizer.zero_grad()
            # 反向传播计算梯度
            loss.backward()
            # 更新参数
            optimizer.step()

        train_acc = test(train_indices, train_y) # 计算当前模型在训练集上的准确率
        val_acc = test(test_indeces, test_y) # 计算当前模型在验证集上的准确率
        # 记录训练过程中的损失值和准确率变化,用于画图
        loss_history.append(loss.item())
        val_acc_history.append(val_acc.item())
        print('Epoch {:03d}: Loss{:.4f}, TrainAcc{:.4}, ValAcc{:.4f}'.format(epoch, loss.item(), train_acc.item(), val_acc.item()))
    return loss_history, val_acc_history

def test(indices_now, y):
    model.eval()
    acc = []
    with torch.no_grad():
        for idx in indices_now:
            tensor_x = LoadByIndices(samples_url, idx)[0]
            tensor_adjacency = LoadByIndices(adjacencys_url, idx)[0]
            normal_adjacency = normalization(tensor_adjacency)
            logits = model(normal_adjacency, tensor_x)
            predict_y = logits.max(1)[1]
            acc.append(torch.eq(predict_y, y[idx]).float())
    accuracy = np.mean(acc)
    return accuracy
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
注意力机制是一种计算机视觉领域的概念和基本原理,它可以帮助模型在处理输入数据时,有效地关注和权不同位置或通道的信息,以提高模型的性能和效果。注意力机制可以分为不同类型,包括通道注意力、空间注意力、混合注意力等。 而自注意力机制注意力机制的一种形式,它主要用于处理序列数据。自注意力机制通过在序列的每个位置计算该位置与其他位置之间的相关性,并根据这些相关性为每个位置分配一个权重,以决定其对最终输出的贡献程度。这样可以使模型关注序列不同位置之间的依赖关系和重要性,从而提高模型对序列的建模能力和理解能力。 自注意力机制的意义在于它可以帮助模型更好地捕捉输入序列的上下文信息,尤其是长距离的依赖关系。通过引入自注意力机制模型可以在编码输入序列时,同时关注序列的所有位置,而不仅仅是局部邻域。这样可以提高模型的表示能力,使其能够更好地理解输入序列的语义和结构。同时,自注意力机制还可以帮助模型在处理输入序列时自动学习到不同位置的权重,而不需要手动设计规则或模板。这使得模型灵活和适应不同任务和数据的需求。因此,自注意力机制在自然语言处理和序列建模等领域得到了广泛的应用和研究。<span class="em">1</span><span class="em">2</span><span class="em">3</span><span class="em">4</span>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值