图相似度计算——SIMGnn源码解读

news2025/2/26 23:13:04

在运行代码的时候,需要首先指定参数,--histogram,表示使用直方图特征

 

1.数据集

         数据集我们使用的是AIDS数据集,为内置的数据集,整个数据集大约700张图,每个图少于10个点,每个点由29维的向量组成。标签为图和图的相似度。

 2.GCN图卷积特征提取

        对于输入数据,一次性输入两个图,标签为两个图之间的相似度。论文中,一个batch为128个图,如果这128个图共有1158个点,则输入向量的维度为[1158,29],然后,经过三层GCN进行特征提取。提取后,特征维度变为[1158,16]。此时我们得到了图的特征表达

3.点和点之间的注意力计算

计算不同batch点的分布

        to_dense_batch,将输入的特征向量转换为Batch*N*F的格式,在这个任务中,batch我们设置为128,N为各个图最大的节点数,在此任务为10个节点,F为特征数,经过GCN的提取为16.所以返回值为128*10*16,有些图可能少于10个点,用mask表示ture表示有10个点,False表示没有10个点

 获得直方图特征结果

        首先,我们需要根据提取到的特征向量,计算相似度评分。相似度评分用点乘表示,得到128*10*10的向量。128表示128张图,首先遍历每一张图,每张图的评分矩阵为10*10,经过sigmoid后得到最终的评分。用直方图统计出特征的分布,得到一个16维的向量,将这个向量进行归一化。此时,这个16维的向量表示两张图10个点的相似度特征。最终,所有图的相似度特征矩阵为128*16。

    def calculate_histogram(
        self, abstract_features_1, abstract_features_2, batch_1, batch_2
    ):
        """
        Calculate histogram from similarity matrix.
        :param abstract_features_1: Feature matrix for target graphs.
        :param abstract_features_2: Feature matrix for source graphs.
        :param batch_1: Batch vector for source graphs, which assigns each node to a specific example
        :param batch_1: Batch vector for target graphs, which assigns each node to a specific example
        :return hist: Histsogram of similarity scores.
        """
        #----------------------------------------------------------------#
        # to_dense_batch,将输入的特征向量转换为Batch*N*F的格式,在这个任务中,
        # batch我们设置为128,N为各个图最大的节点数,在此任务为10个节点,F为特征数,
        # 经过GCN的提取为16.所以返回值为128*10*16,有些图可能少于10个点,
        # 用mask表示ture表示有10个点,False表示没有点的地方
        #----------------------------------------------------------------#
        abstract_features_1, mask_1 = to_dense_batch(abstract_features_1, batch_1)
        abstract_features_2, mask_2 = to_dense_batch(abstract_features_2, batch_2)
        B1, N1, _ = abstract_features_1.size()
        B2, N2, _ = abstract_features_2.size()

        mask_1 = mask_1.view(B1, N1)
        mask_2 = mask_2.view(B2, N2)
        num_nodes = torch.max(mask_1.sum(dim=1), mask_2.sum(dim=1))

        # 用点乘表示得分,此时得分为128*10*10的矩阵
        scores = torch.matmul(
            abstract_features_1, abstract_features_2.permute([0, 2, 1])
        ).detach()
        hist_list = []
        # 最终,直方图特征结果为128*16维的向量
        for i, mat in enumerate(scores):
            # 用sigmoid对得分进行归一化
            mat = torch.sigmoid(mat[: num_nodes[i], : num_nodes[i]]).view(-1)
            # 用直方图进行统计,统计出得分的分布,得到16维的向量
            hist = torch.histc(mat, bins=self.args.bins)
            # 对统计结果进行归一化
            hist = hist / torch.sum(hist)
            hist = hist.view(1, -1)
            hist_list.append(hist)
        return torch.stack(hist_list).view(-1, self.args.bins)

 3.全局特征提取

        注意力机制 

        首先,用图中所有点的特征的平均作为图的全局特征表示。并使用16*16的矩阵进行特征映射。将x的每个点乘以全局特征获得注意力权重(逐元素相乘*)。此时,注意力权重包含每个点对于全局特征的贡献信息。此时,将x乘以注意力权重,即完成的注意力机制加权 

class AttentionModule(torch.nn.Module):
    """
    SimGNN Attention Module to make a pass on graph.
    """

    def __init__(self, args):
        """
        :param args: Arguments object.
        """
        super(AttentionModule, self).__init__()
        self.args = args
        self.setup_weights()
        self.init_parameters()

    def setup_weights(self):
        """
        Defining weights.
        """
        self.weight_matrix = torch.nn.Parameter(
            torch.Tensor(self.args.filters_3, self.args.filters_3)
        )

    def init_parameters(self):
        """
        Initializing weights.
        """
        torch.nn.init.xavier_uniform_(self.weight_matrix)

    def forward(self, x, batch, size=None):
        """
        Making a forward propagation pass to create a graph level representation.
        :param x: Result of the GNN.
        :param size: Dimension size for scatter_
        :param batch: Batch vector, which assigns each node to a specific example
        :return representation: A graph level representation matrix.
        """
        size = batch[-1].item() + 1 if size is None else size
        # 对每个点的特征取平均值得到图的全局特征
        mean = scatter_mean(x, batch, dim=0, dim_size=size)
        # 将全局特征乘以16*16的权重矩阵,得到重构后的全局特征
        transformed_global = torch.tanh(torch.mm(mean, self.weight_matrix))
        # x上的每一个点乘以重构后的全局特征得到注意力权重,*表示逐元素相乘,表示对每个点的特征进行加权
        coefs = torch.sigmoid((x * transformed_global[batch]).sum(dim=1))
        # x乘以注意力权重

         NTN模块

        NTN模块是用来探究两个向量之间的关系的,具体做法是,首先使用一组权重对向量1进行重构。本代码权重矩阵的维度为128*16*16,意思是在18个不同的维度评估两个向量之间的关系。重构后向量1的特征向量的维度为128,16,16。即蕴含了18种不同角度的特征向量。然后用这16种不同角度的特征向量分别于向量2做点积注意力,即计算向量的余弦相似度。此时,我们得到了16种关于向量1和向量2的关系的向量。(向量1和向量2在本算法中分别代表两个图的特征) 

        将两个图的全局特征向量进行拼接,并通过线性层进行特征映射,加入关系向量和偏置项。

 

class TensorNetworkModule(torch.nn.Module):
    """
    SimGNN Tensor Network module to calculate similarity vector.
    """

    def __init__(self, args):
        """
        :param args: Arguments object.
        """
        super(TensorNetworkModule, self).__init__()
        self.args = args
        self.setup_weights()
        self.init_parameters()

    def setup_weights(self):
        """
        Defining weights.
        """
        self.weight_matrix = torch.nn.Parameter(
            torch.Tensor(
                self.args.filters_3, self.args.filters_3, self.args.tensor_neurons
            )
        )
        self.weight_matrix_block = torch.nn.Parameter(
            torch.Tensor(self.args.tensor_neurons, 2 * self.args.filters_3)
        )
        self.bias = torch.nn.Parameter(torch.Tensor(self.args.tensor_neurons, 1))

    def init_parameters(self):
        """
        Initializing weights.
        """
        torch.nn.init.xavier_uniform_(self.weight_matrix)
        torch.nn.init.xavier_uniform_(self.weight_matrix_block)
        torch.nn.init.xavier_uniform_(self.bias)

    def forward(self, embedding_1, embedding_2):
        """
        Making a forward propagation pass to create a similarity vector.
        :param embedding_1: Result of the 1st embedding after attention.
        :param embedding_2: Result of the 2nd embedding after attention.
        :return scores: A similarity score vector.
        """
        batch_size = len(embedding_1)
        # self.weight_matrix原始输入的两个实体都是16维向量,现在用256维表示他们的某种关系
        # 重构后的score维度为128*256
        scoring = torch.matmul(
            embedding_1, self.weight_matrix.view(self.args.filters_3, -1)
        )
        # 128,16,16,第二个维度16可以理解为两个向量有16种不同的关系
        scoring = scoring.view(batch_size, self.args.filters_3, -1).permute([0, 2, 1]) #filters_3可以理解成找多少种关系
        # 乘以局部特征向量,相当于16种重构的embedding_1与embedding_2计算相似度特征
        scoring = torch.matmul(
            scoring, embedding_2.view(batch_size, self.args.filters_3, 1)
        ).view(batch_size, -1)
        # 拼接全局特征和局部特征
        combined_representation = torch.cat((embedding_1, embedding_2), 1)
        # 对拼接的特征进行特征映射
        block_scoring = torch.t(
            torch.mm(self.weight_matrix_block, torch.t(combined_representation))
        )
        # 特征融合
        scores = F.relu(scoring + block_scoring + self.bias.view(-1))
        return scores

输出层: 

        最后将全局特征和局部特征进行拼接,并连接一层FC层输出结果

class SimGNN(torch.nn.Module):
    """
    SimGNN: A Neural Network Approach to Fast Graph Similarity Computation
    https://arxiv.org/abs/1808.05689
    """

    def __init__(self, args, number_of_labels):
        """
        :param args: Arguments object.
        :param number_of_labels: Number of node labels.
        """
        super(SimGNN, self).__init__()
        self.args = args
        self.number_labels = number_of_labels
        self.setup_layers()

    def calculate_bottleneck_features(self):
        """
        Deciding the shape of the bottleneck layer.
        """
        if self.args.histogram:
            self.feature_count = self.args.tensor_neurons + self.args.bins
        else:
            self.feature_count = self.args.tensor_neurons

    def setup_layers(self):
        """
        Creating the layers.
        """
        self.calculate_bottleneck_features()
        if self.args.gnn_operator == "gcn":
            self.convolution_1 = GCNConv(self.number_labels, self.args.filters_1)
            self.convolution_2 = GCNConv(self.args.filters_1, self.args.filters_2)
            self.convolution_3 = GCNConv(self.args.filters_2, self.args.filters_3)
        elif self.args.gnn_operator == "gin":
            nn1 = torch.nn.Sequential(
                torch.nn.Linear(self.number_labels, self.args.filters_1),
                torch.nn.ReLU(),
                torch.nn.Linear(self.args.filters_1, self.args.filters_1),
                torch.nn.BatchNorm1d(self.args.filters_1),
            )

            nn2 = torch.nn.Sequential(
                torch.nn.Linear(self.args.filters_1, self.args.filters_2),
                torch.nn.ReLU(),
                torch.nn.Linear(self.args.filters_2, self.args.filters_2),
                torch.nn.BatchNorm1d(self.args.filters_2),
            )

            nn3 = torch.nn.Sequential(
                torch.nn.Linear(self.args.filters_2, self.args.filters_3),
                torch.nn.ReLU(),
                torch.nn.Linear(self.args.filters_3, self.args.filters_3),
                torch.nn.BatchNorm1d(self.args.filters_3),
            )

            self.convolution_1 = GINConv(nn1, train_eps=True)
            self.convolution_2 = GINConv(nn2, train_eps=True)
            self.convolution_3 = GINConv(nn3, train_eps=True)
        else:
            raise NotImplementedError("Unknown GNN-Operator.")

        if self.args.diffpool:
            self.attention = DiffPool(self.args)
        else:
            self.attention = AttentionModule(self.args)

        self.tensor_network = TensorNetworkModule(self.args)
        self.fully_connected_first = torch.nn.Linear(
            self.feature_count, self.args.bottle_neck_neurons
        )
        self.scoring_layer = torch.nn.Linear(self.args.bottle_neck_neurons, 1)

    def calculate_histogram(
        self, abstract_features_1, abstract_features_2, batch_1, batch_2
    ):
        """
        Calculate histogram from similarity matrix.
        :param abstract_features_1: Feature matrix for target graphs.
        :param abstract_features_2: Feature matrix for source graphs.
        :param batch_1: Batch vector for source graphs, which assigns each node to a specific example
        :param batch_1: Batch vector for target graphs, which assigns each node to a specific example
        :return hist: Histsogram of similarity scores.
        """
        #----------------------------------------------------------------#
        # to_dense_batch,将输入的特征向量转换为Batch*N*F的格式,在这个任务中,
        # batch我们设置为128,N为各个图最大的节点数,在此任务为10个节点,F为特征数,
        # 经过GCN的提取为16.所以返回值为128*10*16,有些图可能少于10个点,
        # 用mask表示ture表示有10个点,False表示没有点的地方
        #----------------------------------------------------------------#
        abstract_features_1, mask_1 = to_dense_batch(abstract_features_1, batch_1)
        abstract_features_2, mask_2 = to_dense_batch(abstract_features_2, batch_2)
        B1, N1, _ = abstract_features_1.size()
        B2, N2, _ = abstract_features_2.size()

        mask_1 = mask_1.view(B1, N1)
        mask_2 = mask_2.view(B2, N2)
        num_nodes = torch.max(mask_1.sum(dim=1), mask_2.sum(dim=1))

        # 用点乘表示得分,此时得分为128*10*10的矩阵
        scores = torch.matmul(
            abstract_features_1, abstract_features_2.permute([0, 2, 1])
        ).detach()
        hist_list = []
        # 最终,直方图特征结果为128*16维的向量
        for i, mat in enumerate(scores):
            # 用sigmoid对得分进行归一化
            mat = torch.sigmoid(mat[: num_nodes[i], : num_nodes[i]]).view(-1)
            # 用直方图进行统计,统计出得分的分布,得到16维的向量
            hist = torch.histc(mat, bins=self.args.bins)
            # 对统计结果进行归一化
            hist = hist / torch.sum(hist)
            hist = hist.view(1, -1)
            hist_list.append(hist)
        return torch.stack(hist_list).view(-1, self.args.bins)

    def convolutional_pass(self, edge_index, features):
        """
        Making convolutional pass.
        :param edge_index: Edge indices.
        :param features: Feature matrix.
        :return features: Abstract feature matrix.
        """
        features = self.convolution_1(features, edge_index)
        features = F.relu(features)
        features = F.dropout(features, p=self.args.dropout, training=self.training)
        features = self.convolution_2(features, edge_index)
        features = F.relu(features)
        features = F.dropout(features, p=self.args.dropout, training=self.training)
        features = self.convolution_3(features, edge_index)
        return features

    def diffpool(self, abstract_features, edge_index, batch):
        """
        Making differentiable pooling.
        :param abstract_features: Node feature matrix.
        :param edge_index: Edge indices
        :param batch: Batch vector, which assigns each node to a specific example
        :return pooled_features: Graph feature matrix.
        """
        x, mask = to_dense_batch(abstract_features, batch)
        adj = to_dense_adj(edge_index, batch)
        return self.attention(x, adj, mask)

    def forward(self, data):
        """
        Forward pass with graphs.
        :param data: Data dictionary.
        :return score: Similarity score.
        """
        edge_index_1 = data["g1"].edge_index
        edge_index_2 = data["g2"].edge_index
        #---------------------------------------#
        # 输入:两张图,一个batch有128个图,一共有
        # 1168个点,每个点是29维向量,标签为这两个图之间的相似度
        #----------------------------------------#
        features_1 = data["g1"].x
        print(features_1.shape)
        features_2 = data["g2"].x
        batch_1 = (
            data["g1"].batch
            if hasattr(data["g1"], "batch")
            else torch.tensor((), dtype=torch.long).new_zeros(data["g1"].num_nodes)
        )
        batch_2 = (
            data["g2"].batch
            if hasattr(data["g2"], "batch")
            else torch.tensor((), dtype=torch.long).new_zeros(data["g2"].num_nodes)
        )
        # 三层GCN进行特征提取
        abstract_features_1 = self.convolutional_pass(edge_index_1, features_1)
        abstract_features_2 = self.convolutional_pass(edge_index_2, features_2)

        # 点的相似度特征,直方图特征提取
        if self.args.histogram:
            hist = self.calculate_histogram(
                abstract_features_1, abstract_features_2, batch_1, batch_2
            )

        if self.args.diffpool:
            pooled_features_1 = self.diffpool(
                abstract_features_1, edge_index_1, batch_1
            )
            pooled_features_2 = self.diffpool(
                abstract_features_2, edge_index_2, batch_2
            )
        else:
            pooled_features_1 = self.attention(abstract_features_1, batch_1)
            pooled_features_2 = self.attention(abstract_features_2, batch_2)

        # NTN层
        scores = self.tensor_network(pooled_features_1, pooled_features_2)
        # 将全局特征和点和点之间的特征进行拼接
        if self.args.histogram:
            scores = torch.cat((scores, hist), dim=1)

        # FC层
        scores = F.relu(self.fully_connected_first(scores))
        score = torch.sigmoid(self.scoring_layer(scores)).view(-1)
        return score

 

 

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

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

相关文章

多视角碰撞,探索 Serverless 企业落地更多可能性丨阿里云用户组厦门站

2022 年 9 月 24 日,阿里云在厦门举办了第 12 场阿里云用户组(AUG)活动,活动主题为“提效降本,Serverless 助力企业快速落地云原生”,吸引了众多技术从业者及企业管理者到场交流。 2009 年,伯克…

油气田工业控制系统现状

石油石化行业综述 石油石化行业分为上游、中游和下游。其中,上游从事的业务包括原油、天然气 的勘探、开发,中游主要是油气 的存储与运输,下游则涵盖炼油、化工、天然气加工等流程型业务及加油站零售等产品配送、销售型业务。通常情况下&…

常用 numpy 函数(长期更新)

文章目录np.where()np.zeros()np.zeros_like()np.divide()np.linalg.norm()np.uint8()np.clip()np.where() np.where有两种用法 np.where(condition,x,y) 当where内有三个参数时,第一个参数表示条件,当条件成立时where方法返回x,当条件不成…

超强功能WebSSH安装,解决Web远程SSH终端

项目地址:https://github.com/huashengdun/webssh 一个简单的 Web 应用程序,用作 ssh 客户端以连接到您的 ssh 服务器。它是用 Python 编写的,基于 tornado、paramiko 和 xterm.js。 特征: 支持SSH密码认证,包括空密…

Windows系统配置CUDA编程环境

像配置一个简单的可以进行CUDA编程的Windows系统环境,分别需要CUDA以及Visual stdio。 注意,如果是新配置的电脑,一定要先安装visual stdio再安装CUDA,否则后面在VS中创建.cu文件时容易出现找不到模块的情况。 一、安装Visual st…

动态规划--(回文子串,最长回文子序列)

代码随想录day 57 动态规划模块 回文子串,最长回文子序列 文章目录1.leetcode 647. 回文子串1.1 详细思路及解题步骤1.2 Java版代码示例2.leetcode 516. 最长回文子序列2.1 详细思路及解题步骤2.2 Java版代码示例1.leetcode 647. 回文子串 1.1 详细思路及解题步骤 该题用动态规…

2023最新SSM计算机毕业设计选题大全(附源码+LW)之java杨佑川音乐播放器908v6

大部分步骤是 1.确定选题 选题的确定需要查阅大量的资料,要搞清楚自己大概想要研究的方向是什么。可以选择自己感兴趣的学科或者强势的学科进行研究,同时要多和毕业指导老师多交流,征求老师的意见和建议,最后确立选题。计算机专…

【K8S系列】第九讲:Kubernetes 之探针

目录 一、探针是什么 二、探针类型 2.1 livenessProbe 2.1.1 容器重启策略 2.2 readinessProbe 2.3 startupProbe 2.4 总结 2.5 探针示例 2.6 配置字段介绍 三、探测机制 3.1 HTTP GET探针 3.2 TCP套接字探针 3.3 Exec探针 Tips 一、探针是什么 探针:是由 kub…

OpenCV众筹了一款ROS2机器人rae,开源、功能强、上手简单。来瞅瞅~

编辑:OAK中国 首发:oakchina.cn 喜欢的话,请多多👍⭐️✍ ▌前言 Hello,大家好,这里是OAK中国,我是助手君。 在2020年、2021年OpenCV分别在Kickstarter上众筹了两款OAK产品,均筹集…

设计模式——桥接模式

桥接(Bridge)模式 一、基本思想 当一个类内部具备两种或多种变化维度时,使用桥接模式可以解耦这些变化的维度,使高层代码架构稳定。 将抽象与实现分离,使它们可以独立变化。 用组合关系代替继承关系来实现&#xff0…

运维面试必问的中间件高频面试题

1. redis是单线程还是多线程? 这个问题已经被问过很多次了,从redis4.0开始引入多线程,redis 6.0 中,多线程主要用于网络 I/O 阶段,也就是接收命令和写回结果阶段,而在执行命令阶段,还是由单线程…

综述类论文_Machine Learning for Encrypted Malicious Traffic Detection(重要)

文章目录Machine Learning for Encrypted Malicious Traffic Detection: Approaches, Datasets and Comparative Study摘要存在的问题论文贡献1. 基于机器学习的加密流量检测模型的总体框架1.1 Research Target(研究目标)1.2 Traffic Dataset Collection…

Allegro给各种形式的板框导弧操作指导

Allegro给各种形式的板框导弧操作指导 Allegro可以给板框导弧,让加工出来的板框更加圆滑,具体操作步骤如下 板框是line形式的 选择Manufacture-Drafting-Fillet命令 在Options里面Radius输出导弧的半径,比如78.74 框选两个线段的部分 完成后的效果如下图 框选4个角落,…

Python基础学习

一、Python基础 1.Python介绍 2.发展史 3.Python 2 or 3? 4.安装 5.Hello World程序 6.变量 7.用户输入 8.模块初识 9. .pyc是个什么鬼? 10.数据类型初识 11.数据运算 12.表达式if ...else语句 13.表达式for 循环 14.break and continue 15.表达式while 循环…

最好的Python入门教材是哪本?这本书当之无愧

1.门槛低 适合编程零基础上手 《Python编程 从入门到实践(第二版)》的作者埃里克马瑟斯(Eric Matthes)是一名高中科学和数学老师,现居住在阿拉斯加,在当地讲授 Python 入门课程。他从 5 岁开始就一直在编写…

带你深度解析虚幻引擎4的照明和阴影知识

照明是渲染的重要组成部分。有静态光和动态光,它们往往很重并且需要大量计算。今天就让赞奇云工作站带领小伙伴们来学习一下虚幻引擎4中的光照和阴影的知识。 静态照明 静态光在编辑器中预先计算并保存在光照贴图中。 〇:良好的性能和质量&#xff08…

[go学习笔记.第十五章.反射,常量] 1.反射的基本介绍以及实践

一.反射的引入以及基本介绍 1.看两个问题 (1).对于结构体的序列化和反序列化,看一段代码 package mainimport("fmt" "encoding/josn" )type Monster struct {Name string json:"monsterName"Age int json:"monsterAge&quo…

Android商城开发----点击左侧分类列表右侧更新对应列表内容

Android商城开发----点击左侧分类列表右侧更新对应列表内容 目录Android商城开发----点击左侧分类列表右侧更新对应列表内容一、首先说布局:二、主要说一下,布局完成后实现点击左侧类别时,右侧展现对应类的商品列表。主要思想:源代…

Vue3响应系统的实现(二)

前言 继上一篇文章,我们已经能够实现一个简单的响应系统了,但是仍然存在很多缺陷,本篇文章将具体叙述一下存在的缺陷以及如何解决,最后实现一个较为完善的响应式系统 解决副作用函数硬编码问题 从上一篇文章中我们不难发现响应…

概率 | 【提神醒脑】重难点专题突破 自用笔记

本文总结参考于 kira 2023概率提神醒脑技巧班 中 —— 重难点专题。 笔记均为自用整理。加油!ヾ(◍∇◍)ノ゙ 一研为定! 一、条件均匀 / 指数 / 二项…分布 -------------------------------------------------------------------------------------------------------------…