首页 > 其他分享 >图神经网络版本的Kolmogorov Arnold(KAN)代码实现和效果对比

图神经网络版本的Kolmogorov Arnold(KAN)代码实现和效果对比

时间:2024-07-07 15:53:07浏览次数:17  
标签:index torch self Kolmogorov mask KAN Arnold labels size

MLP是多层感知器(Multilayer Perceptron)的缩写,它是一种前馈人工神经网络。MLP由至少三层节点组成:一个输入层、一个或多个隐藏层以及一个输出层。每一层的节点都与下一层的每个节点相连,并且每个连接都有一个权重。MLP通过这些权重和节点的激活函数来学习输入数据的模式。

Kolmogorov-Arnold Networks(KAN) 是一种新型的神经网络架构,它受到Kolmogorov-Arnold表示定理的启发。与传统的多层感知器(MLP)不同,MLP在节点上使用固定的激活函数,而KAN在网络的边(即权重)上使用可学习的激活函数。KAN没有使用线性权重,而是将每个权重参数替换为一个参数化的单变量函数,通常是样条函数。

这种设计让KAN在准确性和可解释性方面相比于MLP有显著提升。理论上和实证上,KAN比MLP拥有更快的神经扩展规律。在可解释性方面,KAN可以直观地被可视化,并且能够容易地与人类用户交互。通过数学和物理中的两个例子,KAN被展示为有用的合作伙伴,帮助科学家(重新)发现数学和物理定律。

此外,KAN还提出了一些网络简化的方法,如符号化和修剪,以提供可解释的数学公式和优化网络架构。KAN的创新性在于它调整了Kolmogorov-Arnold表示定理的理念,并将其应用于当前的机器学习环境中,使用任意的网络架构并采用反向传播和修剪等技术,使得KAN比以往的研究更接近实际应用。

KAN的提出为深度学习模型提供了一种有前景的替代方案,这些模型目前严重依赖于MLP。


原文地址:图神经网络版本的Kolmogorov Arnold(KAN)代码实现和效果对比 (qq.com)
Kolmogorov Arnold Networks (KAN)最近作为MLP的替代而流行起来,KANs使用Kolmogorov-Arnold表示定理的属性,该定理允许神经网络的激活函数在边缘上执行,这使得激活函数“可学习”并改进它们。

目前我们看到有很多使用KAN替代MLP的实验,但是目前来说对于图神经网络来说还没有类似的实验,今天我们就来使用KAN创建一个图神经网络Graph Kolmogorov Arnold(GKAN),来测试下KAN是否可以在图神经网络方面有所作为。


首先声明GKAN类,它是一个图神经网络,用于捕获图数据集中的复杂模式。模型将计算Cora图数据集之间的关系,并训练节点分类模型。由于Cora数据集中的节点代表学术论文,边缘代表引用,因此该模型将根据论文引用检测到的模式对学术论文进行分组。

代码中最主要的是NaiveFourierKANLayer层。每个NaiveFourierKANLayer对特征进行傅里叶变换,捕获数据中的复杂模式,同时改进NaiveFourierKANLayer中的激活函数。序列中的最后一层是一个标准的线性层,它将隐藏的特征映射到由hidden_feat和out_feat定义的输出特征空间,降低特征的维数,使分类更容易。

在最后一个KAN层之后,线性层对特征进行处理以产生输出特征。结果输出使用log-softmax激活函数原始输出分数转换为用于分类的概率。

通过整合傅里叶变换,模型通过捕获数据中的高频成分和复杂模式而成为真正的KAN,同时使用基于傅里叶的转换,该转换是可学习的,并随着模型的训练而改进。

 class GKAN(torch.nn.Module):  
     def __init__(self, in_feat, hidden_feat, out_feat, grid_feat, num_layers, use_bias=False):  
         super().__init__()  
         self.num_layers = num_layers  
         self.lin_in = nn.Linear(in_feat, hidden_feat, bias=use_bias)  
         self.lins = torch.nn.ModuleList()  
         for i in range(num_layers):  
             self.lins.append(NaiveFourierKANLayer(hidden_feat, hidden_feat, grid_feat, addbias=use_bias))  
         self.lins.append(nn.Linear(hidden_feat, out_feat, bias=False))  
   
     def forward(self, x, adj):  
         x = self.lin_in(x)  
         for layer in self.lins[:self.num_layers - 1]:  
             x = layer(spmm(adj, x))  
         x = self.lins[-1](x)  
         return x.log_softmax(dim=-1)

NaiveFourierKANLayer类实现了一个自定义的神经网络层,使用傅里叶特征(模型中的正弦和余弦变换是“激活函数”)来转换输入数据,增强模型捕获复杂模式的能力。

在init方法初始化关键参数,包括输入和输出尺寸,网格大小和可选的偏差项。gridsize影响输入数据转换成其傅立叶分量的精细程度,从而影响转换的细节和分辨率。

在forward方法中,输入张量x被重塑为二维张量。创建频率k的网格,重塑的输入xrshp用于计算余弦和正弦变换,以找到输入数据中的模式,从而产生两个张量c和s,表示输入的傅里叶特征。然后将这些张量连接并重塑以匹配后面计算需要的维度。

einsum函数用于在连接的傅立叶特征和傅立叶系数之间执行广义矩阵乘法,产生转换后的输出y。einsum函数中使用的字符串“dbik,djik->bj”是一个指示如何运行矩阵乘法的einsum字符串(在本例中为一般矩阵乘法)。矩阵乘法通过将变换后的输入数据投影到由傅里叶系数定义的新特征空间中,将输入数据的正弦和余弦变换组合成邻接矩阵。

fouriercoeffs参数是一个可学习的傅立叶系数张量,初始化为正态分布,并根据输入维度和网格大小进行缩放。傅里叶系数作为可调节的权重,决定了每个傅里叶分量对最终输出的影响程度,作为使该模型中的激活函数“可学习”的分量。在NaiveFourierKANLayer中,fouriercoeffs被列为参数,因此优化器将改进该变量。

最后,使用输出特征大小将输出y重塑回其原始维度并返回。

 class NaiveFourierKANLayer(nn.Module):  
     def __init__(self, inputdim, outdim, gridsize=300, addbias=True):  
         super(NaiveFourierKANLayer, self).__init__()  
         self.gridsize = gridsize  
         self.addbias = addbias  
         self.inputdim = inputdim  
         self.outdim = outdim  
   
         self.fouriercoeffs = nn.Parameter(torch.randn(2, outdim, inputdim, gridsize) /  
                                          (np.sqrt(inputdim) * np.sqrt(self.gridsize)))  
         if self.addbias:  
             self.bias = nn.Parameter(torch.zeros(1, outdim))  
   
     def forward(self, x):  
         xshp = x.shape  
         outshape = xshp[0:-1] + (self.outdim,)  
         x = x.view(-1, self.inputdim)  
         k = torch.reshape(torch.arange(1, self.gridsize + 1, device=x.device), (1, 1, 1, self.gridsize))  
         xrshp = x.view(x.shape[0], 1, x.shape[1], 1)  
         c = torch.cos(k * xrshp)  
         s = torch.sin(k * xrshp)  
         c = torch.reshape(c, (1, x.shape[0], x.shape[1], self.gridsize))  
         s = torch.reshape(s, (1, x.shape[0], x.shape[1], self.gridsize))  
         y = torch.einsum("dbik,djik->bj", torch.concat([c, s], axis=0), self.fouriercoeffs)  
         if self.addbias:  
             y += self.bias  
         y = y.view(outshape)  
         return y

train函数训练神经网络模型。它基于输入特征(feat)和邻接矩阵(adj)计算预测(out),使用标记数据(label和mask)计算损失和精度,使用反向传播更新模型的参数,并返回精度和损失值。

eval函数对训练好的模型求值。它在不更新模型的情况下计算输入特征和邻接矩阵的预测(pred),并返回预测的类标签。

Args类定义了各种配置参数,如文件路径,数据集名称,日志路径,辍学率,隐藏层大小,傅立叶基函数的大小,模型中的层数,训练轮数,早期停止标准,随机种子和学习率,等等

最后还有设置函数index_to_mask和random_disassortative_splits将数据集划分为训练、验证和测试数据,以便每个阶段捕获来自Cora数据集的各种各样的类。random_disassortative_splits函数通过变换每个类中的索引并确保每个集合的指定比例来划分数据集。然后使用index_to_mask函数将这些索引转换为布尔掩码,以便对原始数据集进行索引。

 def train(args, feat, adj, label, mask, model, optimizer):  
     model.train()  
     optimizer.zero_grad()  
     out = model(feat, adj)  
     pred, true = out[mask], label[mask]  
     loss = F.nll_loss(pred, true)  
     acc = int((pred.argmax(dim=-1) == true).sum()) / int(mask.sum())  
     loss.backward()  
     optimizer.step()  
     return acc, loss.item()  
   
 @torch.no_grad()  
 def eval(args, feat, adj, model):  
     model.eval()  
     with torch.no_grad():  
         pred = model(feat, adj)  
     pred = pred.argmax(dim=-1)  
     return pred  
   
 class Args:  
     path = './data/'  
     name = 'Cora'  
     logger_path = 'logger/esm'  
     dropout = 0.0  
     hidden_size = 256  
     grid_size = 200  
     n_layers = 2  
     epochs = 1000  
     early_stopping = 100  
     seed = 42  
     lr = 5e-4  
 def index_to_mask(index, size):  
     mask = torch.zeros(size, dtype=torch.bool, device=index.device)  
     mask[index] = 1  
     return mask  
   
 def random_disassortative_splits(labels, num_classes, trn_percent=0.6, val_percent=0.2):  
     labels, num_classes = labels.cpu(), num_classes.cpu().numpy()  
     indices = []  
     for i in range(num_classes):  
         index = torch.nonzero((labels == i)).view(-1)  
         index = index[torch.randperm(index.size(0))]  
         indices.append(index)  
   
     percls_trn = int(round(trn_percent * (labels.size()[0] / num_classes)))  
     val_lb = int(round(val_percent * labels.size()[0]))  
     train_index = torch.cat([i[:percls_trn] for i in indices], dim=0)  
   
     rest_index = torch.cat([i[percls_trn:] for i in indices], dim=0)  
     rest_index = rest_index[torch.randperm(rest_index.size(0))]  
   
     train_mask = index_to_mask(train_index, size=labels.size()[0])  
     val_mask = index_to_mask(rest_index[:val_lb], size=labels.size()[0])  
     test_mask = index_to_mask(rest_index[val_lb:], size=labels.size()[0])  
   
     return train_mask, val_mask, test_mask

对于上述代码中的random_disassortative_splits进行分析:
这段代码是一个Python函数,用于创建随机的、非同配性(disassortative)的数据集划分,通常用于机器学习中的数据拆分。下面是对这段代码的详细语法分析:

  1. 函数定义

    def random_disassortative_splits(labels, num_classes, trn_percent=0.6, val_percent=0.2):
    

    这是一个名为random_disassortative_splits的函数,它接受四个参数:labels(标签),num_classes(类别数量),trn_percent(训练集所占百分比,默认为0.6),val_percent(验证集所占百分比,默认为0.2)。

  2. 参数处理

    labels, num_classes = labels.cpu(), num_classes.cpu().numpy()
    

    这里将labelsnum_classes转换为CPU张量和NumPy数组,以便进行后续操作。

  3. 初始化索引列表

    indices = []
    

    创建一个空列表indices,用于存储每个类别的索引。

  4. 类别索引分配

    for i in range(num_classes):
        index = torch.nonzero((labels == i)).view(-1)
        index = index[torch.randperm(index.size(0))]
        indices.append(index)
    

    对于每个类别,找到所有属于该类别的索引,然后随机打乱这些索引,并将结果添加到indices列表中。

  5. 计算训练集大小

    percls_trn = int(round(trn_percent * (labels.size()[0] / num_classes)))
    

    计算每个类别中训练集的大小。

  6. 计算验证集大小

    val_lb = int(round(val_percent * labels.size()[0]))
    

    计算整个数据集中验证集的大小。

  7. 创建训练集索引

    train_index = torch.cat([i[:percls_trn] for i in indices], dim=0)
    

    从每个类别的索引列表中取出训练集部分,并使用torch.cat将它们合并为一个张量。

  8. 创建剩余索引

    rest_index = torch.cat([i[percls_trn:] for i in indices], dim=0)
    rest_index = rest_index[torch.randperm(rest_index.size(0))]
    

    取出每个类别的非训练集部分,合并它们,并随机打乱。

  9. 创建验证集和测试集掩码

    train_mask = index_to_mask(train_index, size=labels.size()[0])
    val_mask = index_to_mask(rest_index[:val_lb], size=labels.size()[0])
    test_mask = index_to_mask(rest_index[val_lb:], size=labels.size()[0])
    

    使用index_to_mask函数(这个函数在代码中没有给出,可能是外部定义的)将索引转换为掩码。掩码是布尔张量,用于指示哪些数据点属于训练集、验证集或测试集。

  10. 返回结果

    return train_mask, val_mask, test_mask
    

    函数返回三个掩码,分别对应训练集、验证集和测试集。

这段代码的目的是为每个类别生成随机的训练集、验证集和测试集索引,以确保数据的非同配性,即不同类别的数据点在各个集合中分布均匀。

标签:index,torch,self,Kolmogorov,mask,KAN,Arnold,labels,size
From: https://www.cnblogs.com/jzzg/p/18288569

相关文章

  • 图神经网络版本的Kolmogorov Arnold(KAN)代码实现和效果对比
    KolmogorovArnoldNetworks(KAN)最近作为MLP的替代而流行起来,KANs使用Kolmogorov-Arnold表示定理的属性,该定理允许神经网络的激活函数在边缘上执行,这使得激活函数“可学习”并改进它们。目前我们看到有很多使用KAN替代MLP的实验,但是目前来说对于图神经网络来说还没有类似的实验......
  • U-KAN环境搭建&推理测试
    ​引子U-Net的鼎鼎大名,我觉得无需我多言了。图像分割和扩散概率模型的基石。作者探索了KANs在改进视觉任务Backbone网络方面的未开发潜力。作者研究、修改并重新设计已建立的U-NetPipeline,通过在标记化的中间表示上整合专用的KAN层,称之为U-KAN。严格的医学图像分割基准测试验......
  • Pointnet++改进即插即用系列:全网首发FastKAN|即插即用,提升特征提取模块性能
    简介:1.该教程提供大量的首发改进的方式,降低上手难度,多种结构改进,助力寻找创新点!2.本篇文章对Pointnet++特征提取模块进行改进,加入FastKAN,提升性能。3.专栏持续更新,紧随最新的研究内容。目录1.理论介绍2.修改步骤2.1步骤一     2.2步骤二     2.3步......
  • KAN神经网络 | KAN函数拟合附代码
    首先,让我们快速概述一下KAN及其实现的理论:柯尔莫哥洛夫-阿诺德表示定理:我们跳过繁琐的公式和定义,只用一个简单的解释。KART指出,任何具有多个输入的连续函数都可以通过组合单个输入的简单函数(如正弦或平方)并将它们相加来创建。例如,多元函数f(x,y)=x*y。这可以写成:((......
  • KAN神经网络 | KAN和MLP比较
    首先,让我们快速概述一下KAN及其实现的理论:柯尔莫哥洛夫-阿诺德表示定理:我们跳过繁琐的公式和定义,只用一个简单的解释。KART指出,任何具有多个输入的连续函数都可以通过组合单个输入的简单函数(如正弦或平方)并将它们相加来创建。例如,多元函数f(x,y)=x*y。这可以写成:((......
  • KAN:使用 Microsoft 的 KubeAI Application Nucleus简化边缘 AI
    我们需要的是在Kubernetes上构建和管理边缘机器学习应用程序的一致方法,一种可以加快开发和交付速度的方法。这就是KAN的作用,即KubeAIApplicationNexus。正如介绍性博客文章所指出的那样,这个名字来自一个普通话动词,翻译为“观看”或“看”。KAN是一个开源项目,托管在GitHub......
  • Vulkan矩形绘制顺序小坑
    裁剪坐标不同:1.vulkan裁剪坐标Y朝下,所以下面矩形意义:staticstd::vector<Vertex>vertices={{{-0.5f,-0.5f,0},{1.0f,0.0f,0.0f}},//左上角{{0.5f,-0.5f,0},{0.0f,1.0f,0.0f}},//右上角{{0.5f,0.5f,0},{0.0f,0.0f,1.0f}},//右下角......
  • P5999 [CEOI2016] kangaroo
    题目大意求出有多少种排列\(p\)满足对于任意\(i\in(1,n)\)有\(p_i\)两侧的值同时大于或小于\(p_i\)。规定\(p_1=s,p_n=t\)。\(2\leqn\leq2\times10^3,1\leqs,t\leqn\)思路首先能看出是动态规划。但是如果纯区间动规的话不太好转移,因为无法使得两个区间拼接的部分......
  • KAN: Kolmogorov–Arnold Networks 学术论文全译
    KAN:Kolmogorov–ArnoldNetworks学术论文全译来源 https://zhuanlan.zhihu.com/p/696001648 KAN:Kolmogorov–ArnoldNetworks https://arxiv.org/pdf/2404.19756讨论Applicationaspects:WehavepresentedsomepreliminaryevidencesthatKANsaremoreeffective......
  • 号称能打败MLP的KAN到底行不行?数学核心原理全面解析
    前几天火爆的Kolmogorov-ArnoldNetworks是具有开创性,目前整个人工智能社区都只关注一件事LLM。我们很少看到有挑战人工智能基本原理的论文了,但这篇论文给了我们新的方向。mlp或多层感知位于AI架构的最底部,几乎是每个深度学习架构的一部分。而KAN直接挑战了这一基础,并且也挑战了......