深度学习-图神经网络-超图概念及在Hyper-YOLO的应用(小白也看懂)

为什么需要超图?

在一个复杂系统中,某些节点(实体)之间的互动可能不是仅限于两个节点之间的关系,而是多个节点同时参与的更复杂的关系(超边)。简单说就是为了更好的描述事物之间的关系,为了方便数学建模。

超图表示为一个二元组:

G=(V,E)

其中 V 是有限顶点集合,E是有限超边集合。加权超图,增加了超边的权重:

G=(V,E,w)

表示超边e的权重,超边的权重是给每个超边分配一个实数值,以表示其重要性或影响力。

顶点集的大小被称为超图的阶数 order of the hypergraph,边集的大小被称为超图的大小 size of the hypergraph。

重要概念:

顶点的度d(v),指包含该顶点的所有超边的权重之和。

超边的度 ,就是该边包含的顶点个数:

相邻、关联

1、相邻,点对点:如果存在包含两个顶点的超边,则超图中的两个顶点是相邻的(adjacent)。

就是说两个顶点在同一个超边上,就是相邻。

边对边:如果{ x } 是超边,则 x与其自身相邻。

2、关联,边对边:两边相交关联的( incident),两超边的顶点交集非空。

点对边:, 称 v 与 e 关联。用于关联矩阵

超图的关联矩阵表示法

超图 G 除了用二元组或三元组外,还可以用关联矩阵来表示。

关联矩阵 A,用于表征顶点和超边之间的关联关系,形式如下:

  • 行表示顶点,列表示超边,元素值为1表示顶点在该超边中,0表示不在。
  • 或者行表示边,列表示顶点,一样,两种方式。

利用关联矩阵H,顶点的度可表示为:

超边的度可表示为:

超图邻接矩阵A:

其中,W表示权重对角矩阵,表示顶点度对角矩阵,对角线元素为每个顶点的度。

具体例子: 下面是一个 关联矩阵,3边5点。

对偶矩阵,即关联矩阵的转置(如下图就是上面矩阵转置)。也很容易看出,任何导出子超图的关联矩阵(H的子超图,部分超图)是H的关联矩阵的子矩阵。

超图邻接矩阵A:

定义:

这个矩阵是对称方阵(维度等于超图顶点数),它也是多重图的矩阵。一般关联矩阵用得比较多。这个定义可能有点抽象,直接记住计算公式

其中,W表示权重对角矩阵,表示顶点度对角矩阵(对角线元素为每个顶点的度)。例如上面超图的邻接矩阵,经过计算为:

拉普拉斯矩阵

超图的拉普拉斯矩阵定义为:

其中 是超边的度矩阵,对角线元素为每个超边的大小。

超图熵

代数上的超图熵(algebraic hypergraph entropy) :

空间域超图卷积

空间域卷积是通过顶点的邻接信息来聚合信息,通常是将邻接的信息聚合到中心节点。

其中, 表示第 层与第层的输入,D是顶点度矩阵(n阶方阵),B是超边的度矩阵(m阶方阵),是非线性激活函数,P表示第 层与第层的权重矩阵,是可学习的权重矩阵 。H 是 超图的关联矩阵(n个顶点,m条超边)。是超图的权重对角阵。

超图卷积的物理含义

超图卷积的物理含义可以理解为:在一个复杂系统中,某些节点(实体)之间的互动可能不是仅限于两个节点之间的关系,而是多个节点同时参与的更复杂的关系(超边)。超图卷积通过聚合这些高维关系的信息,为每个节点提供更丰富的特征表示,从而更好地捕捉系统中的复杂互动。

频域超图卷积

频域卷积是基于拉普拉斯算子进行频域分析,通常用于频率特征的提取。

公式

在频域,超图卷积可通过以下方式定义:

其中 U是拉普拉斯矩阵的特征向量,D是特征值矩阵。

HyperGCN 中的谱域图卷积通过利用超图的结构及其拉普拉斯矩阵来聚合特征信息,这为半监督学习提供了有效的方法。典型的谱域图卷积公式如下:

其中:

  • H是输入特征矩阵。
  • H′是输出特征矩阵。

进行基于谱域的图卷积的计算步骤如下:

什么是超图注意力?

1、超图的注意力目标是学习一个动态关联矩阵。获得能够更好地揭示顶点间内在关系地动态转移矩阵。

2、要使在H上用注意力机制模块,必须假定边和顶点是可比的。这取决于超图如何构造。

3、例如可以将中心节点和k个最近邻节点共同形成一个超边。当节点和超边可以比较时,可以得到超图的注意力机制。

超图分类,如:

空超图 Empty hypergraph:没有边的超图;

非简单超图 Non-simple (or multiple) hypergraph :允许有环 loops(有单个顶点的超边)或重复边的超图;

简单超图 Simple hypergraph :不包含循环和重复边的超图;

k一致超图 k-uniform hypergraph:每条超边都正好包含 k 个顶点的超图;

d正则超图 d-regular hypergraph:每个顶点的度数都是 𝑑 的超图;

无环超图 Acyclic hypergraph:不包含任何环的超图。

超图的特性

1、每个超图都有一个对应的对偶超图。对偶超图就是通过将原超图的顶点和边进行交换。

如超图的关联矩阵是一个表示顶点和边之间关系的布尔矩阵,与它的转置对偶。

2、边的交集有大小

在普通图中,所有边的大小都是2,但在超图中,边的大小可以是任意数值。

3、边可以嵌套

4、超图与二分图(bipartite graph)有着一对一的对应关系。超图的布尔关联矩阵也可以看作二元关系的特征矩阵。

其他概念或属性

  1. 空超边(Empty Hyperedges)

如果关联矩阵中的某一列全部为零,这表示该超边是空的,也就是没有任何顶点与它关联。空超边在一些复杂的超图中是允许存在的。

  1. 孤立顶点(Isolated Vertices)

如果关联矩阵中的某一行全部为零,这表示该顶点是孤立的,也就是该顶点没有连接到任何超边。

  1. 多重超图(Multihypergraphs)

允许重复的超边,这意味着在关联矩阵中,可能会有重复的列(表示相同的超边重复出现)。

  1. 自环(Self-Loops)

自环表示图中的某条边可以连接到自己。在通常的图结构中,边通常是连接两个不同的顶点,而在允许自环的结构中,边可以连接到同一个顶点,这是一种特殊的连接方式。

  1. 方向(Direction)

有些图中的边可以被赋予方向性,也就是边的连接有输入和输出的顶点。在这种情况下,某些顶点被视为"输入",而另一些顶点则是"输出"。这种定向连接在网络模型中经常使用,比如表示信息流、交通流等。

  1. 顺序(Order)

在某些超图中,顶点可能具有特定的顺序。这种顺序可能是全序或部分序。对于图而言,这相当于定向边的概念,但在超图中,顺序的复杂性更高,因为超边可以连接多个顶点。

  1. 属性(Attributes)

一般来说,我们可以在图或超图的顶点、边或"关联关系"上附加属性。这些属性可以是不同的数据类型,例如权重、标签、标识符、类型、字符串等。

8、**步行(walk)**是指一系列相邻边的序列 W=〈e0,e1,...,eN〉,其中每对相邻边是相交的。步行的长度 N 是步行中边的数量。

9、s-步行是指在超图中两条边之间的最短路径,它的特点是需要更宽的连接。具体来说,两个顶点或超边之间的 s-距离 是它们之间最短 s-步行 的长度。

10、子超图 subhypergraph 是去掉某些顶点的超图。

11、部分超图 partial hypergraph是去掉一些边的超图。

12、如果每个顶点的度都为k,则超图H是k-正则的。一致超图的对偶是正则的,反之亦然。

此外还有**分段超图 section hypergraphs,**盖夫曼图 Gaifman graph),对称超图,横切面等概念。

同构

超图同态 homomorphism是指从一个超图的顶点集到另一个超图的顶点集的映射,如此使得每条边映射到另一条边。

超图已被广泛用于机器学习中,常作为一种数据结构或一种正则化属性分类器 classifier regularization。 这些应用包括推荐系统 recommender system (社团作为超边)、图像检索 image retrieval(相关性作为超边) 、和生物信息学(生物、化学分子间相互作用作为超边)。

比较典型的超图机器学习方法包括:超图谱聚类法 spectral clustering(用拉普拉斯超图 hypergraph Laplacian 扩展光谱图理论 spectral graph theory)和超图半监督学习 semi-supervised learning(通过引入超图结构来对结果进行限定)。对于大尺寸的超图,可以使用Apache Spark构建的分布式框架。

简单的超图定义代码

python 复制代码
from typing import List
import numpy as np
import os
import json

class Vertex:
    def __init__(self, name: str = None, weight: float = None):
        self.name = name
        self.weight = weight

class Edge:
    def __init__(self, name: str = None, weight: float = None, vertices: List[Vertex] = None):
        """
        :param name: 名字
        :param weight: 权重
        :param vertices: 顶点集合
        """
        self.name = name
        self.weight = weight
        self.vertices = vertices

class Hypergraph(object):
    def __init__(self, vertices: List[Vertex] = None, edges: List[Edge] = None):
        self.vertices = vertices
        self.edges = edges
        if vertices is None or edges is None:
            self.num_vertices = 0
            self.num_edges = 0
            self.incident_matrix = None
            self.vertices_degree = None
            self.edges_degree = None
            self.vertex_degree_diagonal_matrix = None
            self.edge_degree_diagonal_matrix = None
        else:
            self.num_vertices = len(vertices)
            self.num_edges = len(edges)
            self.incident_matrix = self.calculate_incident_matrix()
            self.vertices_degree = self.calculate_vertex_degree()
            self.edges_degree = self.calculate_edge_degree()
            self.vertex_degree_diagonal_matrix = self.calculate_diagonal_matrix_of_vertex_degree()
            self.edge_degree_diagonal_matrix = self.calculate_diagonal_matrix_of_edge_degree()

    def init_hypergraph_from_files(self, dataset_dir: str):
        self.vertices, self.edges = hypergraph_construction(dataset_dir)
        self.num_vertices = len(self.vertices)
        self.num_edges = len(self.edges)
        self.incident_matrix = self.calculate_incident_matrix()
        self.vertices_degree = self.calculate_vertex_degree()
        self.edges_degree = self.calculate_edge_degree()
        self.vertex_degree_diagonal_matrix = self.calculate_diagonal_matrix_of_vertex_degree()
        self.edge_degree_diagonal_matrix = self.calculate_diagonal_matrix_of_edge_degree()

    def calculate_incident_matrix(self):
        """
        Calculate the incident matrix of the hypergraph.
        :return: The incident matrix of the hypergraph.
        """
        incident_matrix = np.zeros(shape=(self.num_vertices, self.num_edges), dtype=int)
        for i in range(self.num_vertices):
            vertex = self.vertices[i]
            for j in range(self.num_edges):
                edge = self.edges[j]
                if vertex in edge.vertices:
                    incident_matrix[i, j] = 1

        return incident_matrix

    def calculate_vertex_degree(self):
        """
        Calculate the degree of vertices in the hypergraph.
        :return: The degree of vertices in the hypergraph.
        """
        edge_weights = np.zeros(shape=(self.num_edges,), dtype=np.float64)
        for i in range(self.num_edges):
            edge = self.edges[i]
            edge_weights[i] = edge.weight

        edge_weights = edge_weights.reshape(-1, 1)
        vertex_degree_array = np.dot(self.incident_matrix, edge_weights)

        return vertex_degree_array.reshape(vertex_degree_array.size, )

    def calculate_edge_degree(self):
        """
        Calculate the degree of edges in the hypergraph.
        :return: The degree of edges in the hypergraph.
        """
        edges_degree = self.incident_matrix.sum(axis=0)
        return edges_degree

    def calculate_diagonal_matrix_of_vertex_degree(self):
        """
        Create a diagonal matrix with the degrees of vertex as the diagonal elements.
        :return: The diagonal matrix.
        """
        return np.diag(self.vertices_degree)

    def calculate_diagonal_matrix_of_edge_degree(self):
        """
        Create a diagonal matrix with the degrees of edge as the diagonal elements.
        :return: The diagonal matrix.
        """
        return np.diag(self.edges_degree)


def hypergraph_construction(dataset_dir: str) -> (List[Vertex], List[Edge]):
    vertices = []
    edges = []

    # 假设数据集包含一个 JSON文件,其中包含顶点和超边的信息
    #json_file_path = os.path.join(dataset_dir, 'hypergraph_data.json')

    with open(dataset_dir, 'r') as file:
        data = json.load(file)

        # 处理顶点
        if 'vertices' in data:
            for v in data['vertices']:
                vertex = Vertex(name=v['name'], weight=v.get('weight', 1.0))
                vertices.append(vertex)

                # 处理超边
        if 'edges' in data:
            for e in data['edges']:
                edge_vertices = []
                for v_name in e['vertices']:
                    vertex = next((v for v in vertices if v.name == v_name), None)
                    if vertex is not None:
                        edge_vertices.append(vertex)

                edge = Edge(name=e['name'], weight=e.get('weight', 1.0), vertices=edge_vertices)
                edges.append(edge)

    return vertices, edges

"""

{  
    "vertices": [  
        {"name": "v1", "weight": 1.0},  
        {"name": "v2", "weight": 0.5},  
        {"name": "v3", "weight": 1.5},  
        {"name": "v4", "weight": 1.0},  
        {"name": "v5", "weight": 2.0},  
        {"name": "v6", "weight": 1.0},  
        {"name": "v7", "weight": 0.8},  
        {"name": "v8", "weight": 1.0}  
    ],  
    "edges": [  
        {"name": "e1", "vertices": ["v1", "v2", "v3"], "weight": 1.0},  
        {"name": "e2", "vertices": ["v1", "v4"], "weight": 2.0},  
        {"name": "e3", "vertices": ["v2", "v3", "v5", "v6"], "weight": 1.5}  
    ]  
}

"""

if __name__ == '__main__':
   ma = Hypergraph()
   ma.init_hypergraph_from_files('hypergraph_data.json')

基于超图的跨级别和跨位置表示网络

Hyper-YOLO 框架中,主干网络被划分为五个离散阶段。这些阶段的特征图被表示为 {B1,B2,B3,B4,B5}。对五个基础特征进行通道级的拼接,合成跨级别视觉特征。使用距离阈值从每个特征点构建一个 ε-球,该 ε-球将作为超边,如下图所示。ε-球是一个超边,包含距离中心特征点在一定阈值内的所有特征点。

数学定义:

表示指定顶点 v的邻居顶点集。∥x−y∥是距离函数。

空间域超图卷积计算过程如下(添加了残差):

超图信息传递的矩阵表达式

其中 是两个邻居指示函数:

分别表示顶点度对角阵、超边对角度矩阵。

H 是超图关联矩阵, 是一个可训练参数。

基于超图的跨级别和跨位置表示网络(HyperC2Net)

其中 ∥∥ 表示矩阵拼接操作,Φ是融合函数。

Hyper-YOLO 框架

Hyper-YOLO 开源代码

延伸

超图相似度,相似度函数

正核和相似度(略)、 超图态射(略)、超图的分割(略)。

相关推荐
程序员非鱼44 分钟前
深度学习中常见的激活函数详解
人工智能·python·深度学习·神经网络·机器学习·激活函数
蒙娜丽宁2 小时前
【人工智能】自然语言生成的前沿探索:利用GPT-2和BERT实现自动文本生成与完形填空
人工智能·gpt·bert
早安&早安2 小时前
深入了解 NLTK:Python 的自然语言处理工具
人工智能·python·深度学习·自然语言处理
繁华落尽,寻一世真情2 小时前
大语言模型预训练、微调、RLHF
人工智能·语言模型·自然语言处理
赵大仁2 小时前
大语言模型的分层架构:高效建模的全新探索
人工智能·深度学习·神经网络·机器学习·自然语言处理·数据挖掘·数据分析
Noos_2 小时前
如何训练大型语言模型?
人工智能·语言模型·自然语言处理
早安&早安2 小时前
什么是NLP语言:一文详解
人工智能·自然语言处理
早安&早安2 小时前
NLP 基础理论和工具使用
人工智能·深度学习·自然语言处理
东临碣石822 小时前
如何通过腾讯云平台执行SFT微调大语言模型
人工智能·语言模型·自然语言处理
~Yogi2 小时前
NLP学习
人工智能·学习·自然语言处理