kan代码阅读

目录

工具

torch.linspace(a,b,n)

张量的克隆(复制)

mlp文件夹

导入准备

mlp类

定义to函数

get_act获取激活值

前向传播forward()

[@property 装饰器辅助获取权重](#@property 装饰器辅助获取权重)

torch.einsum语法

爱因斯坦求和约定

示例

点积

矩阵-向量乘法

矩阵乘法

转置和乘法

优点

注意事项

kan文件夹

init

kanlayer

[class kanlayer](#class kanlayer)

参数解释


工具

torch.linspace(a,b,n)

和np的一样,n(亦作steps)是点的个数

t = torch.linspace(2, 10, 5)
print(t)

输出:tensor([ 2., 4., 6., 8., 10.])

张量的克隆(复制)

torch.clonetorch.copy_ 在 PyTorch 中用于创建张量的副本,但它们在用法和目的上有所不同。

  1. torch.clone:

    • 返回一个新的张量,它是输入张量的一个深拷贝。
    • 新张量和原始张量在内存中是独立的,修改新张量不会影响原始张量,反之亦然。
    • 使用方法:++new_tensor = original_tensor.clone()++
  2. torch.copy_:

    • 将所有数据从源张量复制到目标张量,并原地修改目标张量。
    • 目标张量必须已经存在于内存中,且必须足够大以容纳源张量的数据。
    • 使用方法:++destination_tensor.copy_(source_tensor)++

下面是两者主要的区别:

  • 调用方式:clone() 是一个独立函数,而 copy_() 是一个原地(in-place)操作。
  • 目标张量:clone() 创建一个新的张量,而 copy_() 需要一个预先存在的目标张量,并在原地修改它。
  • 用途:clone() 常用于需要创建独立副本的场合,而 copy_() 常用于在原地更新张量的场合。

例如:

import torch

# 使用 clone
original = torch.tensor([1, 2, 3])
cloned = original.clone()
cloned[0] = 0
print(original)  # 输出: tensor([1, 2, 3])
print(cloned)    # 输出: tensor([0, 2, 3])

# 使用 copy_
destination = torch.zeros(3)
destination.copy_(original)
destination[0] = 0
print(original)  # 输出: tensor([1, 2, 3])
print(destination)  # 输出: tensor([0, 2, 3])

在上面的例子中,可以看到 clone() 创建了一个独立的新张量,而 copy_() 则将数据复制到了预先存在的 destination 张量中。

mlp文件夹

导入准备

from tqdm import tqdm
  • 这行代码从tqdm模块导入tqdm函数,它用于在Python循环中添加一个进度条,提供直观的进度显示。

    from .LBFGS import LBFGS

  • 这行代码从当前目录下的LBFGS模块导入LBFGS类,这通常是一个自定义的优化器类。

python 复制代码
seed = 0
  • 这行代码设置一个名为seed的变量,值为0,通常用于设置随机数生成器的种子,以确保结果的可重复性。

    torch.manual_seed(seed)

  • 这行代码将PyTorch的随机数生成器种子设置为seed变量的值,确保每次运行代码时,随机数生成器的结果是一致的。

mlp类

class MLP(nn.Module):
  • 这行代码定义了一个名为MLP的类,它继承自nn.Module。这是创建自定义神经网络模型的标准做法。

      def __init__(self, width, act='silu', save_act=True, seed=0, device='cpu'):
    
  • 这行代码定义了MLP类的构造函数__init__,它接受几个参数:width(网络的宽度,即每层的神经元数量),act(激活函数类型,默认为'silu'),save_act(是否保存激活值,默认为True),seed(随机数种子,默认为0),device(运行设备,默认为'cpu')。

          super(MLP, self).__init__()
    

所以,这行代码的作用是调用 nn.Module 类的构造函数,这是必要的,因为 MLP 是从 nn.Module 继承而来的,并且需要初始化父类中的属性和方法。在自定义PyTorch模型时,这是一个常见的做法,以确保所有的初始化步骤都被正确执行,例如注册参数和钩子等。

简而言之,super(MLP, self).__init__() 确保了 MLP 类能够继承并使用 nn.Module 类的所有功能和特性。

  • 这行代码调用父类nn.Module的构造函数,这是在自定义PyTorch模块中常见的做法。

  • 更进一步------

  • super(MLP, self):这部分代码是获取当前类 MLP 的父类(在这种情况下是 nn.Module)的引用,self 是当前类的实例。

  • .__init__():这部分代码是调用父类 nn.Module 的构造函数 __init__()

          torch.manual_seed(seed)
    
  • 这行代码再次设置随机数生成器的种子,以确保网络初始化的一致性。

          linears = []
          self.width = width
          self.depth = depth = len(width) - 1
    
  • 这三行代码初始化一个空列表linears用于存储线性层,并将width参数保存为实例变量。同时计算网络深度depth,即width列表的长度减1。

          for i in range(depth):
              linears.append(nn.Linear(width[i], width[i+1]))
    
  • 这行代码通过循环为网络添加线性层(全连接层 ),每个线性层的输入和输出尺寸分别由width列表中的连续两个元素指定。

          self.linears = nn.ModuleList(linears)
    
  • linears是由nn.linear()构成的列表

  • 这行代码将包含所有线性层的列表(上一行那个列表)转换为nn.ModuleList,这是PyTorch中用于存储模块列表的一种方式。

          self.act_fun = torch.nn.SiLU()
    
  • 这行代码创建一个SiLU激活函数,它是一种常用的激活函数。

          self.save_act = save_act
          self.acts = None
    
  • 这两行代码设置一个标志save_act,用于指示是否保存激活值 ,并初始化一个变量acts用于存储激活值。

          self.cache_data = None
    
  • 这行代码初始化一个变量cache_data**,它可能用于存储其他数据** ,但目前设置为None

          self.device = device
          self.to(device)
    
  • 这两行代码将网络的设备设置为指定的设备(如CPU或GPU),并将网络的所有参数和缓冲区移动到该设备上。

定义to函数

def to(self, device):
  • 定义了一个名为 to 的方法,它接受一个参数 device。这个方法用于将模型的所有参数和缓冲区移动到指定的设备上,这个设备可以是CPU或者GPU。

      super(MLP, self).to(device)
    
  • 使用 super() 调用父类(这里是 nn.Module)的 to 方法,并将当前实例 selfdevice 参数传递给它。这行代码的作用是将当前模型(MLP 的实例)的所有参数和缓冲区移动到指定的 device 上。

      self.device = device
    
  • 这行代码将 device 参数的值赋给实例变量 self.device。这样做是为了在后续的操作中可以引用这个设备,比如在模型的其他方法中使用这个设备来创建或操作张量。

      return self
    
  • 这行代码返回了 self,即当前模型的实例。这使得 to 方法可以被链式调用,也就是说,你可以像这样连续调用方法:model.to(device).train()

这段代码总体上是为了确保模型可以在不同的设备之间移动,并保持对当前设备引用的跟踪。这在处理多设备环境时非常有用,尤其是在使用GPU进行训练时。

get_act获取激活值

python 复制代码
   def get_act(self, x=None):
3 months ago

add swap method
        if isinstance(x, dict):
            x = x['train_input']
        if x == None:
            if self.cache_data != None:
                x = self.cache_data
            else:
                raise Exception("missing input data x")
        save_act = self.save_act
        self.save_act = True
        self.forward(x)
        self.save_act = save_act
def get_act(self, x=None):
  • 定义了一个名为 get_act 的方法,它接受一个可选参数 x。这个方法可能是用来获取神经网络在给定输入 x 下的激活值。

      if isinstance(x, dict):
          x = x['train_input']
    
  • 这行代码检查参数 x 是否是一个字典。如果是,它将 x 的值更新为 x 字典中键为 'train_input' 的值。这意味着如果输入是一个字典,它期望字典中包含一个键为 'train_input' 的条目,该条目是网络的实际输入。

      if x == None:
          if self.cache_data != None:
              x = self.cache_data
          else:
              raise Exception("missing input data x")
    
  • 这是一个条件判断,如果 xNone,它将检查 self.cache_data 是否不为 None。如果 self.cache_data 存在,则将 x 设置为 self.cache_data 的值。如果 self.cache_data 也是 None,则抛出一个异常,指出缺少输入数据 x

      save_act = self.save_act
    
  • 这行代码将 self.save_act 的当前值保存到局部变量 save_act 中。self.save_act 可能是一个布尔值,指示是否应该保存激活值。

      self.save_act = True
    
  • 这行代码将 self.save_act 设置为 True,确保在接下来的 forward 调用中保存激活值。

python

复制

    self.forward(x)
  • 调用 self.forward 方法,这通常是神经网络的前向传播方法,它使用输入 x 计算网络的输出。由于前面设置了 self.save_actTrue这个过程也会保存激活值。

      self.save_act = save_act
    
  • 这行代码将 self.save_act 的值恢复为最初保存的值 ,即 save_act 变量的值。这样,get_act 方法结束后,self.save_act 的值不会被永久改变。

整体上,这个方法是为了获取给定输入 x 的网络激活值,并且可以选择性地使用缓存的数据。如果输入数据缺失,它会抛出一个异常。这个方法还确保了在获取激活值之后,self.save_act 的值会被恢复到原来的状态。

前向传播forward()

如果只是要求传播完的x,只要两句话就好了

python 复制代码
def forward(self,x):
    for i in range(self.depth):
        x=self.linears[i](x)
    return x
    

但是,还有很多细节要考虑,比如是否保存激活值:

if self.save_act:
            act = x.clone()
            act_scale = torch.std(x, dim=0)
            wa_forward = act_scale[None, :] * self.linears[i].weight
            self.acts.append(act)
            if i > 0:
                self.acts_scale.append(act_scale)
            self.wa_forward.append(wa_forward)
  • 如果 self.save_actTrue,则克隆当前激活值 x,计算其标准差 act_scale,计算权重与激活值尺度的乘积 wa_forward,并将这些值添加到相应的列表中。

  • 具体算法------------ 表达式 wa_forward = act_scale[None, :] * self.linears[i].weight 是在PyTorch中常见的一种操作,用于在神经网络的前向传播过程中对权重进行调整。这里,act_scale 是一个张量,表示每个特征维度的标准差,而 self.linears[i].weight 是第 i 个线性层的权重。下面解释这个表达式的各个部分:

  • act_scale[None, :]

    • act_scale 是一个一维张量,假设它的形状是 (C,),其中 C 是特征的数量。
    • [None, :] 是一个索引操作,它将 act_scale 增加一个维度,使其形状变为 (1, C)。这样做的目的是为了确保在进行广播操作时,act_scale 可以与 self.linears[i].weight 的形状兼容。
  • self.linears[i].weight

    • self.linears[i] 是一个线性层(例如 nn.Linear),而 .weight 是这个线性层的权重张量。
    • 假设这个线性层的输入特征数量是 C,输出特征数量是 D,那么 self.linears[i].weight 的形状是 (D, C)
  • *

    • 这是逐元素乘法操作。在这里,它将形状为 (1, C)act_scale[None, :] 与形状为 (D, C)self.linears[i].weight 进行逐元素相乘。
  • 结果 wa_forward

    • 结果的形状将是 (D, C),与 self.linears[i].weight 的形状相同。
    • 这个操作的效果是调整第 i 个线性层的权重,使得每个输出特征维度的权重都乘以相应的特征标准差 act_scale

总括------

  • 如果 self.save_actTrue,则克隆当前激活值 x,计算其标准差 act_scale,计算权重与激活值尺度的乘积 wa_forward,并将这些值添加到相应的列表中。
  • 如果当前层不是最后一层(i < self.depth - 1),则应用激活函数 self.act_fun 到输出 x 上。这意味着在每一层线性层之后(除了最后一层),都会进行激活。
  • 如果当前层是最后一层,并且 self.save_actTrue,则计算最后一层激活值的标准差 act_scale 并保存。
复制代码
每经过一层线性层(除了最后一层)都会激活一次。这是深度学习中常见的模式,其中非线性激活函数被用于引入非线性,使得模型能够学习更复杂的函数。最后一层通常不使用激活函数,特别是在回归问题或某些特定的分类问题中,例如输出层的维度与目标维度相同的情况。

@property 装饰器辅助获取权重

在Python中,@property 装饰器用于将一个方法转换为属性访问,这样就可以像访问属性一样访问这个方法,而不需要使用传统的函数调用方式(即不带括号)。这在实现面向对象编程时非常有用,特别是在需要通过属性访问来获取或设置对象状态时。

@property 装饰器用于 w 方法:

@property
def w(self):
    return [self.linears[l].weight for l in range(self.depth)]

以下是 @property 装饰器的作用:

  1. 属性访问 :允许用户通过 self.w 而不是 self.w() 来访问权重列表,这使得代码更加简洁和直观。

  2. 只读属性 :在这个例子中,w 方法没有定义一个设置器(setter),这意味着它是一个只读属性。用户可以获取权重列表,但不能直接修改它。

  3. 隐藏实现细节 :通过将方法转换为属性,可以在不公开内部实现细节的情况下,提供对对象状态的访问。在这个例子中,用户不需要知道权重是如何被组织或计算的,他们只需要知道可以通过 self.w 获取它们。

下面是如何使用这个属性的一个例子:

# 假设有一个 MLP 实例叫做 mlp
weights = mlp.w  # 获取所有层的权重,不需要调用 mlp.w() 作为函数

在这个例子中,每次你访问 mlp.w,它都会返回一个列表,其中包含了 mlp 对象中所有线性层的权重。由于 w 是一个只读属性,你不能通过赋值来改变它,例如 mlp.w = something 是不允许的,除非你定义了一个相应的设置器方法。

1暂时还没看到在哪里用了

torch.einsum语法

torch.einsum 是PyTorch中一个强大的函数,它允许用户使用爱因斯坦求和约定(Einstein summation convention)来执行多维数组(张量)的运算。爱因斯坦求和约定是一种表示多维数组(张量)运算的方法,它通过省略求和符号和索引来简化数学表达式的书写。

下面是对 torch.einsum 函数的具体讲解:

torch.einsum(equation, *operands)
  • equation: 一个字符串,定义了操作的形式,包括输入和输出张量的索引以及它们之间的关系。
  • operands: 一个或多个张量,它们将根据 equation 中定义的规则进行运算。

爱因斯坦求和约定

在爱因斯坦求和约定中,如果一个索引在输入张量中出现两次,那么它就表示对该索引进行求和。如果索引在输出张量中出现,那么它就表示该索引是输出张量的一个维度。

示例

以下是一些使用 torch.einsum 的示例:

点积

计算两个一维张量的点积:

x = torch.randn(5)
y = torch.randn(5)
result = torch.einsum('i,i', x, y)  # 等价于 torch.dot(x, y)

在这个例子中,'i,i' 表示对索引 i 求和,xy 的对应元素相乘。

矩阵-向量乘法

计算矩阵和向量的乘积:

A = torch.randn(4, 5)
x = torch.randn(5)
result = torch.einsum('ik,k', A, x)  # 等价于 torch.mv(A, x)

这里,'ik,k' 表示对索引 k 求和,A 的每一行与 x 相乘。

矩阵乘法

计算两个矩阵的乘积:

A = torch.randn(4, 5)
B = torch.randn(5, 6)
result = torch.einsum('ik,kj', A, B)  # 等价于 torch.mm(A, B)

在这个例子中,'ik,kj' 表示对索引 k 求和,A 的行与 B 的列相乘。

转置和乘法

计算矩阵的转置与另一个矩阵的乘积:

A = torch.randn(4, 5)
B = torch.randn(4, 6)
result = torch.einsum('ji,jk', A, B)  # 等价于 torch.mm(A.t(), B)

这里,'ji,jk' 表示 A 被转置(通过交换索引 ij),然后与 B 相乘。

优点

  • 表达能力强:可以简洁地表示复杂的多维数组运算。
  • 性能优化:torch.einsum 在某些情况下可以比传统的矩阵乘法函数(如 torch.mmtorch.matmul)更高效,因为它允许PyTorch优化器选择最佳的执行路径。

注意事项

  • 使用 torch.einsum 时,需要确保输入张量的维度与求和约定中的索引相匹配。
  • 对于非常大的张量,使用 torch.einsum 可能不如直接使用专门的函数(如 torch.mmtorch.matmul)高效,因为后者通常经过了更深入的优化。

++91行++

kan文件夹

init

在Python中,`init.py` 文件是用于标识一个目录为Python包(package)的特殊文件。在这个图片中,虽然"init.py"这个名称出现在列表中,但通常它不会以这种方式显示为一个普通文件。相反,它会作为包内部的一个隐藏或特殊的文件存在。

如果你希望创建一个新的Python包,你应该在你的包目录下创建一个空的 `init.py` 文件。这样,Python就会把这个目录识别为一个可以导入的包。

只有三行

python 复制代码
from .MultKAN import *
from .utils import *
#torch.use_deterministic_algorithms(True)

这个文件可以非常简单,甚至可以为空,只要它的文件名恰好是 __init__.py 即可。然而,你也可以在其中放置一些特定的代码和行为,这些代码会在导入该包时自动执行。

kanlayer

class kanlayer

参数解释

in_dim: int

输入维度

out_dim: int

输出维度

num: int

网格间隔的数量

k: int

样条函数的分段多项式阶数

noise_scale: float

初始化时样条函数的尺度

coef: 2D torch.tensor

B样条基的系数

scale_base_mu: float

残差函数 b(x) 从 N(μ, σ^2) 中抽取的量级,(magnitude),μ = sigma_base_mu

scale_base_sigma: float

残差函数 b(x) 从 N(μ, σ^2) 中抽取的量级,μ = sigma_base_sigma

scale_sp: float

样条函数 spline(x) 的量级

base_fun: fun

残差函数 b(x)

mask: 1D torch.float
样条函数的掩码。将掩码中的某些元素设置为零意味着将相应的激活函数设置为零。

grid_eps: float ∈ [0,1]

在 update_grid_from_samples 中使用的超参数。当 grid_eps = 1 时,网格是均匀的;当 grid_eps = 0 时,网格使用样本的百分位数进行划分。0 < grid_eps < 1 在两个极端之间插值。

锁定的激活函数的ID

device: str

设备

下棋待续。

相关推荐
坠金3 分钟前
神经网络的常用layer
人工智能·深度学习·神经网络
jun77889520 分钟前
NLP自然语言处理中的Attention机制原理揭秘
人工智能·自然语言处理
长命百岁️20 分钟前
【想法】NLP的基石-Word Embedding
人工智能·自然语言处理·embedding
Zilliz Planet22 分钟前
GenAI 生态系统现状:不止大语言模型和向量数据库
数据库·人工智能·语言模型·自然语言处理
余~1853816280023 分钟前
矩阵系统源码搭建,OEM贴牌技术
网络·人工智能·线性代数·算法·矩阵
健忘的派大星1 小时前
什么是RAG,有哪些RAG引擎?看完这一篇你就知道了!!
人工智能·ai·语言模型·langchain·llm·agi·rag
weixin_466202781 小时前
第29周:实现mnist手写数字识别(Tensorflow实战第一周)
人工智能·python·tensorflow
安科瑞武陈燕WX172696036551 小时前
智慧用电监控装置:引领0.4kV安全用电新时代
大数据·人工智能·安全
代码猪猪傻瓜coding1 小时前
python mac vscode 脚本文件的运行
pytorch·python