2025年最好创新的KAN网络模型python代码全合集

什么是KAN网络?

KAN网络,全称为Kolmogorov-Arnold Networks,是一种新型的神经网络架构,其设计灵感来源于Kolmogorov-Arnold表示定理。该定理表明,多元连续函数可以表示为有限数量的单变量连续函数和二元加法运算的复合。KAN网络正是基于这一理论,旨在通过在网络边缘(即权重)上引入可学习的激活函数,来提高模型的灵活性和表达能力,同时保持模型的解释性。

原理

KAN网络由多个KAN层组成,每个层都包含可学习的参数(如B样条函数的系数)。在正向传播过程中,输入数据通过各层时,首先与可学习的激活函数进行运算得到输出值;然后通过反向传播算法更新各层的参数以最小化损失函数。由于所有操作都是可微分的,因此可以使用反向传播进行训练。

权重与激活函数的创新:

传统MLP(多层感知器)中的权重是简单的线性系数,而KAN网络中的权重被可学习的单变量函数(如B样条函数)所替代。

这些可学习的激活函数位于网络的边上,而不是节点(神经元)上,使得每个权重都是一个独立的函数,从而提高了网络的灵活性和表达能力。

节点与边的角色:

在KAN网络中,节点主要负责简单的加法运算,而不包含非线性激活函数。非线性激活函数被移到了边上,即作为权重函数的组成部分。

这种设计减少了网络中的非线性元素数量,提高了计算效率,并保持了模型的可解释性。

B样条表示:

B样条是一种数学上具有良好性质的函数族,它可以被看作是一系列基础函数的线性组合。

在KAN网络中,B样条函数被用作可学习的激活函数,通过调整B样条的系数和节点位置,可以灵活地逼近各种复杂的函数形式。

特点

可学习的激活函数:KAN网络将可学习的激活函数应用于权重上,而不是像传统多层感知器(MLP)那样在节点(神经元)上使用固定的激活函数。这种设计使得KAN网络能够更灵活、更接近Kolmogorov-Arnold表示定理地处理和学习输入数据的复杂关系。

无线性权重矩阵:KAN网络没有传统的线性权重矩阵,每个权重参数都由一种可学习的一维样条函数(如B样条)替代。节点仅对输入信号进行求和,不进行任何非线性处理,非线性处理通过可学习的激活函数完成。

解释性:KAN网络具有很好的解释性,其可视化和交互性使得模型的行为和结果更容易被人类用户理解。这对于科学研究和实际应用中的模型验证和调整具有重要意义。

精度与效率:实验结果显示,KAN网络在数据拟合、偏微分方程求解等任务中可以达到或超过MLPs的准确度。同时,KAN网络具有更快的神经缩放定律,即随着模型规模的增加,性能提升速度更快。

广泛的应用前景:KAN网络不仅适用于函数拟合和偏微分方程求解等传统任务,还在时间序列预测、图结构学习、卷积神经网络改进等领域展现出巨大潜力。通过与其他模型的结合,KAN网络可以构建出更强大的预测和分析模型。

KAN结构

         于此,笔者整理了KAN网络模型的python代码全合集,该代码质量优异,注释清晰,配备了KAN网络的原文解读和使用说明,适合新手学习,并且在此基础上进行二创,适合所有科研人员。包括:KAN、CNN-KAN、CNN-LSTM-KAN、LSTM-KAN、TCN-KAN、LSTM-KAN、Transformer-KAN,全家桶

值得注意:

1、使用数据集来源:西安市天气数据,采样频率为1h/次,预测对象为PM2.5浓度。

2、先运行data_visualization.py,做数据集的划分,确定预测变量;再运行KAN.py,获得预测结果。

3、相关运行环境放置再requirements.txt中,根据需求进行配置。

4、运行结果保存在KAN预测、kan真实值的csv文件中。

5、图片保存在对应文件夹中。

有兴趣的朋友欢迎,关注笔者主页信息,进一步 交流学习,获取资料。

部分核心代码:

import torch
import torch.nn.functional as F
import math
 
"""
1.内存效率提升:原始实现需要扩展所有中间变量来执行不同的激活函数,而此代码中将计算重新制定为使用不同的基函数激活输入,
  然后线性组合它们。这种重新制定可以显著降低内存成本,并将计算变得更加高效。
2.正则化方法的改变:原始实现中使用的L1正则化需要对张量进行非线性操作,与重新制定的计算不兼容。
  因此,此代码中将L1正则化改为对权重的L1正则化,这更符合神经网络中常见的正则化方法,并且与重新制定的计算兼容。
3.激活函数缩放选项:原始实现中包括了每个激活函数的可学习缩放,但这个库提供了一个选项来禁用这个特性。
  禁用缩放可以使模型更加高效,但可能会影响结果。
4.参数初始化的改变:为了解决在MNIST数据集上的性能问题,此代码修改了参数的初始化方式,使用kaiming初始化。
"""
 
class KANLinear(torch.nn.Module):
    def __init__(
        self,
        in_features,
        out_features,
        grid_size=5,  # 网格大小,默认为 5
        spline_order=3, # 分段多项式的阶数,默认为 3
        scale_noise=0.1,  # 缩放噪声,默认为 0.1
        scale_base=1.0,   # 基础缩放,默认为 1.0
        scale_spline=1.0,    # 分段多项式的缩放,默认为 1.0
        enable_standalone_scale_spline=True,
        base_activation=torch.nn.SiLU,  # 基础激活函数,默认为 SiLU(Sigmoid Linear Unit)
        grid_eps=0.02,
        grid_range=[-1, 1],  # 网格范围,默认为 [-1, 1]
    ):
        super(KANLinear, self).__init__()
        self.in_features = in_features
        self.out_features = out_features
        self.grid_size = grid_size # 设置网格大小和分段多项式的阶数
        self.spline_order = spline_order
 
        h = (grid_range[1] - grid_range[0]) / grid_size   # 计算网格步长
        grid = ( # 生成网格
            (
                torch.arange(-spline_order, grid_size + spline_order + 1) * h
                + grid_range[0]
            )
            .expand(in_features, -1)
            .contiguous()
        )
        self.register_buffer("grid", grid)  # 将网格作为缓冲区注册
 
        self.base_weight = torch.nn.Parameter(torch.Tensor(out_features, in_features)) # 初始化基础权重和分段多项式权重
        self.spline_weight = torch.nn.Parameter(
            torch.Tensor(out_features, in_features, grid_size + spline_order)
        )
        if enable_standalone_scale_spline:  # 如果启用独立的分段多项式缩放,则初始化分段多项式缩放参数
            self.spline_scaler = torch.nn.Parameter(
                torch.Tensor(out_features, in_features)
            )
 
        self.scale_noise = scale_noise # 保存缩放噪声、基础缩放、分段多项式的缩放、是否启用独立的分段多项式缩放、基础激活函数和网格范围的容差
        self.scale_base = scale_base
        self.scale_spline = scale_spline
        self.enable_standalone_scale_spline = enable_standalone_scale_spline
        self.base_activation = base_activation()
        self.grid_eps = grid_eps
 
        self.reset_parameters()  # 重置参数
 
    def reset_parameters(self):
        torch.nn.init.kaiming_uniform_(self.base_weight, a=math.sqrt(5) * self.scale_base)# 使用 Kaiming 均匀初始化基础权重
        with torch.no_grad():
            noise = (# 生成缩放噪声
                (
                    torch.rand(self.grid_size + 1, self.in_features, self.out_features)
                    - 1 / 2
                )
                * self.scale_noise
                / self.grid_size
            )
            self.spline_weight.data.copy_( # 计算分段多项式权重
                (self.scale_spline if not self.enable_standalone_scale_spline else 1.0)
                * self.curve2coeff(
                    self.grid.T[self.spline_order : -self.spline_order],
                    noise,
                )
            )
            if self.enable_standalone_scale_spline:  # 如果启用独立的分段多项式缩放,则使用 Kaiming 均匀初始化分段多项式缩放参数
                # torch.nn.init.constant_(self.spline_scaler, self.scale_spline)
                torch.nn.init.kaiming_uniform_(self.spline_scaler, a=math.sqrt(5) * self.scale_spline)
 
    def b_splines(self, x: torch.Tensor):
        """
        Compute the B-spline bases for the given input tensor.
        Args:
            x (torch.Tensor): Input tensor of shape (batch_size, in_features).
        Returns:
            torch.Tensor: B-spline bases tensor of shape (batch_size, in_features, grid_size + spline_order).
        """
        """
        计算给定输入张量的 B-样条基函数。
        参数:
        x (torch.Tensor): 输入张量,形状为 (batch_size, in_features)。
        返回:
        torch.Tensor: B-样条基函数张量,形状为 (batch_size, in_features, grid_size + spline_order)。
        """
        assert x.dim() == 2 and x.size(1) == self.in_features
 
        grid: torch.Tensor = ( # 形状为 (in_features, grid_size + 2 * spline_order + 1)
            self.grid
        )  # (in_features, grid_size + 2 * spline_order + 1)
        x = x.unsqueeze(-1)
        bases = ((x >= grid[:, :-1]) & (x < grid[:, 1:])).to(x.dtype)
        for k in range(1, self.spline_order + 1):
            bases = (
                (x - grid[:, : -(k + 1)])
                / (grid[:, k:-1] - grid[:, : -(k + 1)])
                * bases[:, :, :-1]
            ) + (
                (grid[:, k + 1 :] - x)
                / (grid[:, k + 1 :] - grid[:, 1:(-k)])
                * bases[:, :, 1:]
            )
 
        assert bases.size() == (
            x.size(0),
            self.in_features,
            self.grid_size + self.spline_order,
        )
        return bases.contiguous()
 
    def curve2coeff(self, x: torch.Tensor, y: torch.Tensor):
        """
        Compute the coefficients of the curve that interpolates the given points.
        Args:
            x (torch.Tensor): Input tensor of shape (batch_size, in_features).
            y (torch.Tensor): Output tensor of shape (batch_size, in_features, out_features).
        Returns:
            torch.Tensor: Coefficients tensor of shape (out_features, in_features, grid_size + spline_order).
        """
        """
        计算插值给定点的曲线的系数。
        参数:
        x (torch.Tensor): 输入张量,形状为 (batch_size, in_features)。
        y (torch.Tensor): 输出张量,形状为 (batch_size, in_features, out_features)。
        返回:
        torch.Tensor: 系数张量,形状为 (out_features, in_features, grid_size + spline_order)。
        """
        assert x.dim() == 2 and x.size(1) == self.in_features
        assert y.size() == (x.size(0), self.in_features, self.out_features)
        # 计算 B-样条基函数
        A = self.b_splines(x).transpose(
            0, 1 # 形状为 (in_features, batch_size, grid_size + spline_order)
        )  # (in_features, batch_size, grid_size + spline_order)
        B = y.transpose(0, 1)  # (in_features, batch_size, out_features) # 形状为 (in_features, batch_size, out_features)
        solution = torch.linalg.lstsq(   # 使用最小二乘法求解线性方程组
            A, B
        ).solution  # (in_features, grid_size + spline_order, out_features)  # 形状为 (in_features, grid_size + spline_order, out_features)
        result = solution.permute( # 调整结果的维度顺序
            2, 0, 1
        )  # (out_features, in_features, grid_size + spline_order)
 
        assert result.size() == (
            self.out_features,
            self.in_features,
            self.grid_size + self.spline_order,
        )
        return result.contiguous()
 
    @property
    def scaled_spline_weight(self):
        """
        获取缩放后的分段多项式权重。
        返回:
        torch.Tensor: 缩放后的分段多项式权重张量,形状与 self.spline_weight 相同。
        """
        return self.spline_weight * (
            self.spline_scaler.unsqueeze(-1)
            if self.enable_standalone_scale_spline
            else 1.0
        )
 
    def forward(self, x: torch.Tensor): # 将输入数据通过模型的各个层,经过线性变换和激活函数处理,最终得到模型的输出结果
        """
        前向传播函数。
        参数:
        x (torch.Tensor): 输入张量,形状为 (batch_size, in_features)。
        返回:
        torch.Tensor: 输出张量,形状为 (batch_size, out_features)。
        """
        assert x.dim() == 2 and x.size(1) == self.in_features
 
        base_output = F.linear(self.base_activation(x), self.base_weight) # 计算基础线性层的输出
        spline_output = F.linear( # 计算分段多项式线性层的输出
            self.b_splines(x).view(x.size(0), -1),
            self.scaled_spline_weight.view(self.out_features, -1),
        )
        return base_output + spline_output  # 返回基础线性层输出和分段多项式线性层输出的和
 
    @torch.no_grad()
    # 更新网格。
    # 参数:
    # x (torch.Tensor): 输入张量,形状为 (batch_size, in_features)。
    # margin (float): 网格边缘空白的大小。默认为 0.01。
    # 根据输入数据 x 的分布情况来动态更新模型的网格,使得模型能够更好地适应输入数据的分布特点,从而提高模型的表达能力和泛化能力。
    def update_grid(self, x: torch.Tensor, margin=0.01):
        assert x.dim() == 2 and x.size(1) == self.in_features
        batch = x.size(0)
 
        splines = self.b_splines(x)  # (batch, in, coeff)  # 计算 B-样条基函数
        splines = splines.permute(1, 0, 2)  # (in, batch, coeff)  # 调整维度顺序为 (in, batch, coeff)
        orig_coeff = self.scaled_spline_weight  # (out, in, coeff)
        orig_coeff = orig_coeff.permute(1, 2, 0)  # (in, coeff, out)  # 调整维度顺序为 (in, coeff, out)
        unreduced_spline_output = torch.bmm(splines, orig_coeff)  # (in, batch, out)
        unreduced_spline_output = unreduced_spline_output.permute(
            1, 0, 2
        )  # (batch, in, out)
 
        # sort each channel individually to collect data distribution
        x_sorted = torch.sort(x, dim=0)[0] # 对每个通道单独排序以收集数据分布
        grid_adaptive = x_sorted[
            torch.linspace(
                0, batch - 1, self.grid_size + 1, dtype=torch.int64, device=x.device
            )
        ]
 
        uniform_step = (x_sorted[-1] - x_sorted[0] + 2 * margin) / self.grid_size
        grid_uniform = (
            torch.arange(
                self.grid_size + 1, dtype=torch.float32, device=x.device
            ).unsqueeze(1)
            * uniform_step
            + x_sorted[0]
            - margin
        )
 
        grid = self.grid_eps * grid_uniform + (1 - self.grid_eps) * grid_adaptive
        grid = torch.concatenate(
            [
                grid[:1]
                - uniform_step
                * torch.arange(self.spline_order, 0, -1, device=x.device).unsqueeze(1),
                grid,
                grid[-1:]
                + uniform_step
                * torch.arange(1, self.spline_order + 1, device=x.device).unsqueeze(1),
            ],
            dim=0,
        )
 
        self.grid.copy_(grid.T)   # 更新网格和分段多项式权重
        self.spline_weight.data.copy_(self.curve2coeff(x, unreduced_spline_output))
 
    def regularization_loss(self, regularize_activation=1.0, regularize_entropy=1.0):
        # 计算正则化损失,用于约束模型的参数,防止过拟合
        """
        Compute the regularization loss.
        This is a dumb simulation of the original L1 regularization as stated in the
        paper, since the original one requires computing absolutes and entropy from the
        expanded (batch, in_features, out_features) intermediate tensor, which is hidden
        behind the F.linear function if we want an memory efficient implementation.
        The L1 regularization is now computed as mean absolute value of the spline
        weights. The authors implementation also includes this term in addition to the
        sample-based regularization.
        """
        """
        计算正则化损失。
        这是对原始 L1 正则化的简单模拟,因为原始方法需要从扩展的(batch, in_features, out_features)中间张量计算绝对值和熵,
        而这个中间张量被 F.linear 函数隐藏起来,如果我们想要一个内存高效的实现。
        现在的 L1 正则化是计算分段多项式权重的平均绝对值。作者的实现也包括这一项,除了基于样本的正则化。
        参数:
        regularize_activation (float): 正则化激活项的权重,默认为 1.0。
        regularize_entropy (float): 正则化熵项的权重,默认为 1.0。
        返回:
        torch.Tensor: 正则化损失。
        """
        l1_fake = self.spline_weight.abs().mean(-1)
        regularization_loss_activation = l1_fake.sum()
        p = l1_fake / regularization_loss_activation
        regularization_loss_entropy = -torch.sum(p * p.log())
        return (
            regularize_activation * regularization_loss_activation
            + regularize_entropy * regularization_loss_entropy
        )
 
 
class KAN(torch.nn.Module): # 封装了一个KAN神经网络模型,可以用于对数据进行拟合和预测。
    def __init__(
        self,
        layers_hidden,
        grid_size=5,
        spline_order=3,
        scale_noise=0.1,
        scale_base=1.0,
        scale_spline=1.0,
        base_activation=torch.nn.SiLU,
        grid_eps=0.02,
        grid_range=[-1, 1],
    ):
        """
        初始化 KAN 模型。
        参数:
            layers_hidden (list): 包含每个隐藏层输入特征数量的列表。
            grid_size (int): 网格大小,默认为 5。
            spline_order (int): 分段多项式的阶数,默认为 3。
            scale_noise (float): 缩放噪声,默认为 0.1。
            scale_base (float): 基础缩放,默认为 1.0。
            scale_spline (float): 分段多项式的缩放,默认为 1.0。
            base_activation (torch.nn.Module): 基础激活函数,默认为 SiLU。
            grid_eps (float): 网格调整参数,默认为 0.02。
            grid_range (list): 网格范围,默认为 [-1, 1]。
        """
        super(KAN, self).__init__()
        self.grid_size = grid_size
        self.spline_order = spline_order
 
        self.layers = torch.nn.ModuleList()
        for in_features, out_features in zip(layers_hidden, layers_hidden[1:]):
            self.layers.append(
                KANLinear(
                    in_features,
                    out_features,
                    grid_size=grid_size,
                    spline_order=spline_order,
                    scale_noise=scale_noise,
                    scale_base=scale_base,
                    scale_spline=scale_spline,
                    base_activation=base_activation,
                    grid_eps=grid_eps,
                    grid_range=grid_range,
                )
            )
 
    def forward(self, x: torch.Tensor, update_grid=False): # 调用每个KANLinear层的forward方法,对输入数据进行前向传播计算输出。
        """
        前向传播函数。
        参数:
            x (torch.Tensor): 输入张量,形状为 (batch_size, in_features)。
            update_grid (bool): 是否更新网格。默认为 False。
        返回:
            torch.Tensor: 输出张量,形状为 (batch_size, out_features)。
        """
        for layer in self.layers:
            if update_grid:
                layer.update_grid(x)
            x = layer(x)
        return x
 
    def regularization_loss(self, regularize_activation=1.0, regularize_entropy=1.0):#计算正则化损失的方法,用于约束模型的参数,防止过拟合。
        """
        计算正则化损失。
        参数:
            regularize_activation (float): 正则化激活项的权重,默认为 1.0。
            regularize_entropy (float): 正则化熵项的权重,默认为 1.0。
        返回:
            torch.Tensor: 正则化损失。
        """
        return sum(
            layer.regularization_loss(regularize_activation, regularize_entropy)
            for layer in self.layers
        )

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值