当前位置: 首页 > article >正文

kan代码阅读

目录

工具

torch.linspace(a,b,n)

张量的克隆(复制)

mlp文件夹

导入准备

mlp类

定义to函数

get_act获取激活值

前向传播forward()

@property 装饰器辅助获取权重

torch.einsum语法

爱因斯坦求和约定

示例

点积

矩阵-向量乘法

矩阵乘法

转置和乘法

优点

注意事项

kan文件夹

__init__

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.clone 和 torch.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类,这通常是一个自定义的优化器类。

 

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 方法,并将当前实例 self 和 device 参数传递给它。这行代码的作用是将当前模型(MLP 的实例)的所有参数和缓冲区移动到指定的 device 上。

 

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

 

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

 

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

get_act获取激活值

   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")
  • 这是一个条件判断,如果 x 是 None,它将检查 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_act 为 True这个过程也会保存激活值。

 

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

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

前向传播forward()

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

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_act 为 True,则克隆当前激活值 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_act 为 True,则克隆当前激活值 x,计算其标准差 act_scale,计算权重与激活值尺度的乘积 wa_forward,并将这些值添加到相应的列表中。
  • 如果当前层不是最后一层(i < self.depth - 1),则应用激活函数 self.act_fun 到输出 x 上。这意味着在每一层线性层之后(除了最后一层),都会进行激活。
  • 如果当前层是最后一层,并且 self.save_act 为 True,则计算最后一层激活值的标准差 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 求和,x 和 y 的对应元素相乘。

矩阵-向量乘法

计算矩阵和向量的乘积:

 

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 被转置(通过交换索引 i 和 j),然后与 B 相乘。

优点

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

注意事项

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

91行

kan文件夹

__init__

在Python中,`__init__.py` 文件是用于标识一个目录为Python包(package)的特殊文件。在这个图片中,虽然“__init__.py”这个名称出现在列表中,但通常它不会以这种方式显示为一个普通文件。相反,它会作为包内部的一个隐藏或特殊的文件存在。
如果你希望创建一个新的Python包,你应该在你的包目录下创建一个空的 `__init__.py` 文件。这样,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
    设备
 

下棋待续。


http://www.kler.cn/a/374527.html

相关文章:

  • 使用 Python 的 BeautifulSoup 与 Flask/Flask-RESTful 集成进行数据爬取和 API 构建
  • node.js rc4加密/解密 不好用怎么办?
  • 记录一下方便的条件编译
  • 【C++滑动窗口】2653. 滑动子数组的美丽值|1785
  • 【python】极简教程14-文件
  • Golang反射原理
  • 账户和组管理
  • 若依框架部署到服务器刷新或者是退出登录出现404
  • Spring Boot2.x教程:(十)从Field injection is not recommended谈谈依赖注入
  • PVE修改Ubuntu虚拟机的硬盘大小
  • nginx 根路径同时代理 http ws sse 三种请求
  • WebGL 快速入门构建你的第一个 3D 应用
  • 在浏览器中运行 Puppeteer:解锁新能力
  • USB接口类型及引脚信号详解
  • java 实现对 word 文档中占位符进行替换
  • Spring学习笔记_13——@Autowired
  • 内网穿透技术选型PPTP(点对点隧道协议)和 FRP(Fast Reverse Proxy)
  • 手机功耗异常大数据看板建设
  • IMU技术引领骑行新体验
  • linux中网口测试
  • JVM—类加载器、双亲委派机制
  • git入门教程6:git基本版本控制
  • 中英文如何快速切换?小达人盘点10款翻译工具给你
  • 芯片技术创新,GPU 服务器厂家聚焦服务器性能新巅峰
  • Nuxt.js 应用中的 components:extend 事件钩子详解
  • CSS常用标签笔记