【代码解读】LLGC

2024-09-05 19:20
文章标签 代码 解读 llgc

本文主要是介绍【代码解读】LLGC,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

对象创建:

model = LLGC(description.size(1), label.max().item()+1, args.drop_out, args.use_bias).to(device)

模型使用:

output = model(train_features)

LLGC:

# Lorentzian MODEL
class LLGC(nn.Module):def __init__(self, nfeat, nclass, drop_out, use_bias):super(LLGC, self).__init__()self.drop_out = drop_outself.use_bias = use_biasself.nclass = nclassself.c = torch.tensor([1.0]).to("cuda")self.manifold = getattr(manifolds_LLGC, "Lorentzian")()#创建了manifolds中的一个lorentzian类的对象,赋值给self.manifoldself.W = LorentzLinear(self.manifold, nfeat, nclass, self.c, self.drop_out, self.use_bias)def forward(self, x, batch_size):x_loren = self.manifold.normalize_input(x, self.c)#normalize_input操作内部带有对数映射,self.c为曲率。x_loren为对数映射后的结果x_w = self.W(x_loren)x_tan = self.manifold.log_map_zero(x_w, self.c)return x_tan[:batch_size]
  1. 欧式空间中的点到流形的映射
  2. 计算
  3. 流形映射到欧式空间

1. 欧式空间中的点到流形的映射

目标是使用self.manifold.normalize_input将欧式空间的目标特征x映射到流形上,返回x_loren

x_loren = self.manifold.normalize_input(x, self.c)

创建一个全零张量。
将全零张量和输入张量拼接,增加一个额外的维度。
调用exp_map_zero

    def normalize_input(self, x, c):# print('=====normalize original input===========')num_nodes = x.size(0)zeros = torch.zeros(num_nodes, 1, dtype=x.dtype, device=x.device)x_tan = torch.cat((zeros, x), dim=1)return self.exp_map_zero(x_tan, c)

创建流形上的基点

    def exp_map_zero(self, dp, c, is_res_normalize=True, is_dp_normalize=True):zeros = torch.zeros_like(dp)zeros[:, 0] = c ** 0.5return self.exp_map_x(zeros, dp, c, is_res_normalize, is_dp_normalize)

exp_map_x 方法通过指数映射将一个点 p 和切向量 dp 映射回洛伦兹流形上。
首先规范化切向量 dp。
然后计算其洛伦兹范数。
接着通过指数映射公式将其映射到流形上,并可选地对结果进行规范化。

    def exp_map_x(self, p, dp, c, is_res_normalize=True, is_dp_normalize=True):if is_dp_normalize:dp = self.normalize_tangent(p, dp, c)dp_lnorm = self.l_inner(dp, dp, keep_dim=True)dp_lnorm = torch.sqrt(torch.clamp(dp_lnorm + self.eps[p.dtype], 1e-6))dp_lnorm_cut = torch.clamp(dp_lnorm, max=50)sqrt_c = c ** 0.5res = (torch.cosh(dp_lnorm_cut / sqrt_c) * p) + sqrt_c * (torch.sinh(dp_lnorm_cut / sqrt_c) * dp / dp_lnorm)if is_res_normalize:res = self.normalize(res, c)return res

normalize_tangent 方法的目的是规范化洛伦兹流形上切向量,使其满足洛伦兹内积 <p, p_tan>_L = 0

    def normalize_tangent(self, p, p_tan, c):"""Normalize tangent vectors to place the vectors satisfies <p, p_tan>_L=0:param p: the tangent spaces at p. size:[nodes, feature]:param p_tan: the tangent vector in tangent space at p"""d = p_tan.size(1) - 1p_tail = p.narrow(1, 1, d)p_tan_tail = p_tan.narrow(1, 1, d)ptpt = torch.sum(p_tail * p_tan_tail, dim=1, keepdim=True)p_head = torch.sqrt(c + torch.sum(torch.pow(p_tail, 2), dim=1, keepdim=True) + self.eps[p.dtype])return torch.cat((ptpt / p_head, p_tan_tail), dim=1)

计算内积

    def l_inner(self, x, y, keep_dim=False):# input shape [node, features]d = x.size(-1) - 1xy = x * yxy = torch.cat((-xy.narrow(1, 0, 1), xy.narrow(1, 1, d)), dim=1)return torch.sum(xy, dim=1, keepdim=keep_dim)

目的是将一个向量 p 规范化,以确保它位于双曲面上。
这个过程可以理解为确保该向量符合双曲空间的几何结构。

    def normalize(self, p, c):"""Normalize vector to confirm it is located on the hyperboloid:param p: [nodes, features(d + 1)]:param c: parameter of curvature"""d = p.size(-1) - 1narrowed = p.narrow(-1, 1, d)if self.max_norm:narrowed = torch.renorm(narrowed.view(-1, d), 2, 0, self.max_norm)first = c + torch.sum(torch.pow(narrowed, 2), dim=-1, keepdim=True)first = torch.sqrt(first)return torch.cat((first, narrowed), dim=1)

2. 计算

x_w = self.W(x_loren)

其中LorentzLinear的类定义如下:

class LorentzLinear(nn.Module):# Lorentz Hyperbolic Graph Neural Layerdef __init__(self, manifold, in_features, out_features, c, drop_out, use_bias):super(LorentzLinear, self).__init__()# print("LorentzLinear")self.manifold = manifoldself.in_features = in_featuresself.out_features = out_featuresself.c = cself.drop_out = drop_outself.use_bias = use_biasself.bias = nn.Parameter(torch.Tensor(out_features-1))   # -1 when use mine mat-vec multiplyself.weight = nn.Parameter(torch.Tensor(out_features - 1, in_features))  # -1, 0 when use mine mat-vec multiplyself.reset_parameters()def reset_parameters(self):init.xavier_uniform_(self.weight, gain=math.sqrt(2))init.constant_(self.bias, 0)def forward(self, x):drop_weight = F.dropout(self.weight, self.drop_out, training=self.training)mv = self.manifold.matvec_regular(drop_weight, x, self.bias, self.c, self.use_bias)return mv

dropout的输入可以是特征,也可以是权值矩阵。
总归返回的是,以概率p随机给元素置零之后的输入。
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述


对输入执行对数映射。
分割映射结果。
矩阵乘法(将 x_tail 和权重矩阵 m 进行矩阵乘法。注意,这里对 m 执行了转置操作,以确保维度匹配)。
拼接结果,恢复到原有的维度。
首先执行 normalize_tangent_zero,将数据归一化到洛伦兹流形的切空间。再通过 执行指数映射,将数据映射回洛伦兹流形。
检查 mx 中的元素是否为零,用零替换掉 mx 中满足某个条件的部分。

    def matvec_regular(self, m, x, b, c, use_bias):d = x.size(1) - 1x_tan = self.log_map_zero(x, c)x_head = x_tan.narrow(1, 0, 1)x_tail = x_tan.narrow(1, 1, d)mx = x_tail @ m.transpose(-1, -2)if use_bias:mx_b = mx + belse:mx_b = mxmx = torch.cat((x_head, mx_b), dim=1)mx = self.normalize_tangent_zero(mx, c)mx = self.exp_map_zero(mx, c)cond = (mx==0).prod(-1, keepdim=True, dtype=torch.uint8)res = torch.zeros(1, dtype=mx.dtype, device=mx.device)res = torch.where(cond, res, mx)return res
    def log_map_zero(self, y, c, is_tan_normalize=True):zeros = torch.zeros_like(y)zeros[:, 0] = c ** 0.5return self.log_map_x(zeros, y, c, is_tan_normalize)

对数映射的作用是将洛伦兹流形上的点投影到某个点 x 的切空间中(即欧几里得空间)。
通过内积调整 y,得到一个新的向量 tmp_vector。
计算 tmp_vector 的范数。
计算切向量 y_tan。
如果 is_tan_normalize 为真,则对计算得到的切向量 y_tan 进行归一化处理,确保它满足洛伦兹切空间的约束。

    def log_map_x(self, x, y, c, is_tan_normalize=True):"""Logarithmic map at x: project hyperboloid vectors to a tangent space at x:param x: vector on hyperboloid:param y: vector to project a tangent space at x:param normalize: whether normalize the y_tangent:return: y_tangent"""xy_distance = self.induced_distance(x, y, c)tmp_vector = y + self.l_inner(x, y, keep_dim=True) / c * xtmp_norm = torch.sqrt(self.l_inner(tmp_vector, tmp_vector) + self.eps[x.dtype])y_tan = xy_distance.unsqueeze(-1) / tmp_norm.unsqueeze(-1) * tmp_vectorif is_tan_normalize:y_tan = self.normalize_tangent(x, y_tan, c)return y_tan

这里通过 induced_distance 方法计算向量 x 和 y 在洛伦兹流形上的距离,这实际上是两点在洛伦兹空间的测地线距离。

    def induced_distance(self, x, y, c):xy_inner = self.l_inner(x, y)sqrt_c = c ** 0.5return sqrt_c * arcosh(-xy_inner / c + self.eps[x.dtype])

3. 流形映射到欧式空间

x_tan = self.manifold.log_map_zero(x_w, self.c)

这篇关于【代码解读】LLGC的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1139842

相关文章

活用c4d官方开发文档查询代码

当你问AI助手比如豆包,如何用python禁止掉xpresso标签时候,它会提示到 这时候要用到两个东西。https://developers.maxon.net/论坛搜索和开发文档 比如这里我就在官方找到正确的id描述 然后我就把参数标签换过来

poj 1258 Agri-Net(最小生成树模板代码)

感觉用这题来当模板更适合。 题意就是给你邻接矩阵求最小生成树啦。~ prim代码:效率很高。172k...0ms。 #include<stdio.h>#include<algorithm>using namespace std;const int MaxN = 101;const int INF = 0x3f3f3f3f;int g[MaxN][MaxN];int n

MCU7.keil中build产生的hex文件解读

1.hex文件大致解读 闲来无事,查看了MCU6.用keil新建项目的hex文件 用FlexHex打开 给我的第一印象是:经过软件的解释之后,发现这些数据排列地十分整齐 :02000F0080FE71:03000000020003F8:0C000300787FE4F6D8FD75810702000F3D:00000001FF 把解释后的数据当作十六进制来观察 1.每一行数据

Java ArrayList扩容机制 (源码解读)

结论:初始长度为10,若所需长度小于1.5倍原长度,则按照1.5倍扩容。若不够用则按照所需长度扩容。 一. 明确类内部重要变量含义         1:数组默认长度         2:这是一个共享的空数组实例,用于明确创建长度为0时的ArrayList ,比如通过 new ArrayList<>(0),ArrayList 内部的数组 elementData 会指向这个 EMPTY_EL

计算机毕业设计 大学志愿填报系统 Java+SpringBoot+Vue 前后端分离 文档报告 代码讲解 安装调试

🍊作者:计算机编程-吉哥 🍊简介:专业从事JavaWeb程序开发,微信小程序开发,定制化项目、 源码、代码讲解、文档撰写、ppt制作。做自己喜欢的事,生活就是快乐的。 🍊心愿:点赞 👍 收藏 ⭐评论 📝 🍅 文末获取源码联系 👇🏻 精彩专栏推荐订阅 👇🏻 不然下次找不到哟~Java毕业设计项目~热门选题推荐《1000套》 目录 1.技术选型 2.开发工具 3.功能

代码随想录冲冲冲 Day39 动态规划Part7

198. 打家劫舍 dp数组的意义是在第i位的时候偷的最大钱数是多少 如果nums的size为0 总价值当然就是0 如果nums的size为1 总价值是nums[0] 遍历顺序就是从小到大遍历 之后是递推公式 对于dp[i]的最大价值来说有两种可能 1.偷第i个 那么最大价值就是dp[i-2]+nums[i] 2.不偷第i个 那么价值就是dp[i-1] 之后取这两个的最大值就是d

pip-tools:打造可重复、可控的 Python 开发环境,解决依赖关系,让代码更稳定

在 Python 开发中,管理依赖关系是一项繁琐且容易出错的任务。手动更新依赖版本、处理冲突、确保一致性等等,都可能让开发者感到头疼。而 pip-tools 为开发者提供了一套稳定可靠的解决方案。 什么是 pip-tools? pip-tools 是一组命令行工具,旨在简化 Python 依赖关系的管理,确保项目环境的稳定性和可重复性。它主要包含两个核心工具:pip-compile 和 pip

D4代码AC集

贪心问题解决的步骤: (局部贪心能导致全局贪心)    1.确定贪心策略    2.验证贪心策略是否正确 排队接水 #include<bits/stdc++.h>using namespace std;int main(){int w,n,a[32000];cin>>w>>n;for(int i=1;i<=n;i++){cin>>a[i];}sort(a+1,a+n+1);int i=1

Spring 源码解读:自定义实现Bean定义的注册与解析

引言 在Spring框架中,Bean的注册与解析是整个依赖注入流程的核心步骤。通过Bean定义,Spring容器知道如何创建、配置和管理每个Bean实例。本篇文章将通过实现一个简化版的Bean定义注册与解析机制,帮助你理解Spring框架背后的设计逻辑。我们还将对比Spring中的BeanDefinition和BeanDefinitionRegistry,以全面掌握Bean注册和解析的核心原理。

GPT系列之:GPT-1,GPT-2,GPT-3详细解读

一、GPT1 论文:Improving Language Understanding by Generative Pre-Training 链接:https://cdn.openai.com/research-covers/languageunsupervised/language_understanding_paper.pdf 启发点:生成loss和微调loss同时作用,让下游任务来适应预训