Efficient-KAN 源码详解

Efficient-KAN源码链接

Efficient-KAN (GitHub)

改进细节

1.内存效率提升

KAN网络的原始实现的性能问题主要在于它需要扩展所有中间变量以执行不同的激活函数。对于具有in_features个输入和out_features个输出的层,原始实现需要将输入扩展为shape为(batch_size, out_features, in_features)的tensor以执行激活函数。然而,所有激活函数都是一组固定基函数(3阶B样条)的线性组合。鉴于此,拟将计算重新表述为不同的基函数激活输入,然后将它们线性组合。这种重新表述可以显著减少内存消耗,并使计算变得更加简单的矩阵乘法,自然地适用于前向和后向传递。

2.正则化方法的改变

稀疏化被认为对KAN的可解释性至关重要。作者提出了一种定义在输入样本上的L1正则化,它需要对**(batch_size, out_features, in_features)** tensor进行非线性操作,因此与重新表述不兼容。拟改为对权重进行L1正则化,这在NN中更为常见,并且与重新表述兼容。

3.激活函数缩放选项

除了可学习的激活函数(B样条),原始实现还包括对每个激活函数的可学习缩放 ( w s ) (w_s) (ws)。拟提供一个名为enable_standalone_scale_spline的选项,默认情况下为True,以包含此功能。禁用它会使模型更高效,但可能会影响结果。这需要更多实验验证。

4.参数初始化的改变

为了解决在MNIST数据集上的性能问题,该代码修改了参数的初始化方式,采用Kaiming初始化

KAN_fast.py解析

基本参数和类定义

import torch
import torch.nn.functional as F
import math

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)  # 将网格作为缓冲区注册

1. torch.arange(-spline_order, grid_size + spline_order + 1)
  • **torch.arange(start, end)**:生成一个从 startend-1 的整数序列(左闭右开区间)。
  • **-spline_order**:从负的 spline_order 开始。
  • **grid_size + spline_order + 1**:终止于 grid_size + spline_order(不包括 +1)。

这个序列的长度是 grid_size + 2 * spline_order + 1,用于涵盖所有需要的网格点,包括两端的扩展区域。

2. * h
  • 这一步将生成的整数序列乘以步长 h,将索引序列转换为实际的网格位置。
3. + grid_range[0]
  • 这一步将整个网格位置进行平移,使得网格的起始点与 grid_range[0] 对齐。

如果 grid_range[0] = -1,则每个位置都会减去 1

4. .expand(in_features, -1)
  • **.expand()**:将这个网格复制 in_features 次,以适应输入特征的维度。具体来说,它将原本的一维网格向量扩展成一个 in_features × (grid_size + 2 * spline_order + 1) 的二维张量。 其中每一行都是相同的网格向量。
5. .contiguous()
  • **.contiguous()**:确保扩展后的张量在内存中是连续存储的,方便后续的计算和操作。虽然在大多数情况下这个操作是可选的,但它可以提高计算效率并避免潜在的问题。

最终效果:

这段代码生成了一个二维张量 grid,它的形状为 [in_features, grid_size + 2 * spline_order + 1],其中每一行都是相同的、覆盖整个 grid_range 并适当扩展的网格点序列。这个网格用于模型中的 B 样条或其他基函数计算,使得模型可以在输入数据范围内执行灵活的插值和拟合操作。

初始化可训练参数

        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)
            )

1. self.base_weight = torch.nn.Parameter(torch.Tensor(out_features, in_features)) ( w b ) (w_b) (wb)

  • **torch.Tensor(out_features, in_features)**:创建一个形状为 (out_features, in_features) 的未初始化张量,用于存储基础线性层的权重。这个张量的元素初始时没有具体的数值,通常在后续的 reset_parameters() 方法中进行初始化。
  • **torch.nn.Parameter**:将这个张量封装成 torch.nn.Parameter 对象。这意味着这个张量会被视为模型的可训练参数,PyTorch 会自动将其包含在模型的参数列表中,并在反向传播时更新其值。
  • **self.base_weight**:这个属性存储的是基础线性变换的权重矩阵。这个矩阵将在前向传播过程中被用来对输入特征进行线性变换。

2. self.spline_weight = torch.nn.Parameter(torch.Tensor(out_features, in_features, grid_size + spline_order)) ( c i ) (c_i) (ci)

  • **torch.Tensor(out_features, in_features, grid_size + spline_order)**:创建一个形状为 (out_features, in_features, grid_size + spline_order) 的未初始化张量,用于存储分段多项式的权重。这些权重将用于 B 样条或其他类似方法的计算。
  • **torch.nn.Parameter**:同样地,将这个张量封装成 torch.nn.Parameter,使其成为模型的可训练参数。
  • **self.spline_weight**:这个属性存储的是与分段多项式相关的权重。这些权重决定了如何将输入特征映射到输出特征,特别是在使用 B 样条等非线性激活函数时。
为什么 spline_weight 的形状是 (out_features, in_features, grid_size + spline_order)
  • **out_features****in_features**:与 base_weight 类似,表示输出和输入的特征数量。
  • **grid_size + spline_order**:这个维度表示在 B 样条或其他分段多项式方法中,每个输入特征需要使用的基函数的数量。通过这些基函数的线性组合,可以生成灵活的非线性激活。

3. if enable_standalone_scale_spline:

  • 这个条件语句检查 enable_standalone_scale_spline 是否为 True。如果为 True,则会为每个分段多项式激活函数引入一个独立的缩放参数。

4. self.spline_scaler = torch.nn.Parameter(torch.Tensor(out_features, in_features)) ( w s ) (w_s) (ws)

  • **torch.Tensor(out_features, in_features)**:创建一个形状为 (out_features, in_features) 的张量,用于存储独立的分段多项式缩放参数。
  • **torch.nn.Parameter**:将张量封装成 torch.nn.Parameter,使其成为可训练参数。
  • **self.spline_scaler**:这个属性存储的是分段多项式的缩放参数。每个 spline_weight 都有一个对应的缩放参数,可以单独调整其幅度,从而提供更大的灵活性。

其他实例属性

        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()  # 重置参数

Kaiming初始化权重(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
<think>好的,用户现在想了解EfficientNet-B3的详细信息和使用方法。之前我已经详细回答了B0的结构和参数,现在需要扩展回答到B3。首先,我需要回顾一下EfficientNet的复合缩放方法,因为B3是B0的放大版本,通过调整深度、宽度和分辨率系数来实现的。 用户提到B3的模型结构、参数详解和使用教程,所以需要分这三个部分来组织内容。结构部分需要对比B0,指出B3在哪些层进行了扩展,比如重复次数和通道数的变化。参数部分需要包括参数量、FLOPs、输入分辨率的变化,以及复合缩放的具体系数。使用教程可能需要提供代码示例,比如加载预训练模型、调整输入尺寸、微调模型等。 要注意之前的回答中引用了引用[1]关于模型结构的部分,所以这里也需要适当引用,但用户提供的引用信息比较模糊,可能需要根据之前的回答结构来补充。例如,B0的复合缩放公式是基础,B3的φ值应该是1.2,根据论文中的信息,所以需要确认具体的系数。 另外,用户可能希望知道如何在实际项目中应用B3,比如迁移学习时的注意事项,数据增强的方法,以及如何调整模型结构。需要确保回答的实用性,提供可操作的步骤,比如代码示例和调参建议。 需要检查是否有遗漏的关键点,比如激活函数是否变化,SE模块是否有调整,或者是否有新的优化技术应用在B3上。根据EfficientNet论文,这些核心模块应该保持一致,只是缩放参数不同。 最后,确保回答结构清晰,分点明确,参数部分用表格或公式展示,代码示例正确,并且相关问题能够引导用户进一步探索。可能还需要比较B3和B0的性能差异,比如准确率和计算成本的权衡,帮助用户决定是否选择B3。</think>### EfficientNet-B3 模型详解与使用指南 EfficientNet-B3 是 EfficientNet 系列中规模中等的模型,通过**复合缩放(Compound Scaling)**策略对 B0 进行深度、宽度和分辨率协同扩展,在计算效率与精度间取得平衡。以下是其核心结构、参数解析及实践方法。 --- #### 一、模型结构升级(对比 B0) EfficientNet-B3 基于 B0 的骨架进行**统一比例缩放**,具体结构变化如下表所示: | Stage | B0 重复次数 | B3 重复次数 | B0 输出通道数 | B3 输出通道数 | B3 输入分辨率 | |-------|-------------|-------------|----------------|----------------|----------------| | 1 | 1 | 1 | 32 | 40 | 300x300 | | 2 | 1 | 1 | 16 | 24 | 150x150 | | 3 | 2 | 2 | 24 | 32 | 150x150 | | 4 | 2 | 3 | 40 | 56 | 75x75 | | 5 | 3 | 3 | 80 | 112 | 38x38 | | 6 | 3 | 4 | 112 | 160 | 19x19 | | 7 | 4 | 5 | 192 | 272 | 19x19 | | 8 | 1 | 2 | 320 | 448 | 10x10 | | 9 | 1 | 1 | 1280 | 1536 | 5x5 | **核心改进**: 1. **深度扩展**:关键阶段的 MBConv 模块重复次数增加(如 Stage 4 从 2 次增至 3 次)。 2. **宽度扩展**:通道数提升(如 Stage 1 从 32 增至 40),增强特征表达能力。 3. **分辨率提升**:输入尺寸从 224x224 扩展至 300x300,捕获更细粒度特征[^1]。 --- #### 二、参数详解 1. **基础性能指标**: - **参数量**:约 12.3 百万(12.3M),较 B0(5.3M)增加 132%。 - **FLOPs**:约 18.7 亿次浮点运算(1.87B),是 B0(0.39B)的约 4.8 倍。 - **Top-1 准确率**(ImageNet):约 81.6%,较 B0(77.1%)显著提升。 2. **复合缩放系数**: - **缩放因子**:$\phi = 1.2$(B0 中 $\phi=1$)[^1]。 - **计算公式**: $$ \text{Depth}: d = 1.2^{1.2} \approx 1.44, \quad \text{Width}: w = 1.1^{1.2} \approx 1.15, \quad \text{Resolution}: r = 1.15^{1.2} \approx 1.18 $$ 满足约束 $\alpha \cdot \beta^2 \cdot \gamma^2 \approx 2$。 3. **训练配置优化**: - **优化器**:推荐使用 **AdamW**(学习率衰减策略为余弦退火)。 - **正则化**:权重衰减(0.0001)、标签平滑(Label Smoothing=0.1)。 - **数据增强**:RandAugment(强度=15)、MixUp($\alpha=0.2$)。 --- #### 三、使用教程(基于 TensorFlow/Keras) **1. 加载预训练模型**: ```python from tensorflow.keras.applications import EfficientNetB3 # 加载 ImageNet 预训练权重(输入尺寸需匹配 300x300) model = EfficientNetB3(weights='imagenet', include_top=True, input_shape=(300, 300, 3)) model.summary() # 查看完整结构 ``` **2. 自定义输入尺寸与分类头**: ```python from tensorflow.keras.layers import Dense, GlobalAveragePooling2D from tensorflow.keras.models import Model # 加载基础模型(不包含顶层分类器) base_model = EfficientNetB3(weights='imagenet', include_top=False, input_shape=(256, 256, 3)) # 添加自定义分类头 x = GlobalAveragePooling2D()(base_model.output) x = Dense(1024, activation='swish')(x) predictions = Dense(10, activation='softmax')(x) # 假设任务为10分类 model = Model(inputs=base_model.input, outputs=predictions) ``` **3. 微调(Fine-tuning)策略**: ```python # 冻结所有卷积层 for layer in base_model.layers: layer.trainable = False # 先训练顶层分类器 model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy']) model.fit(train_dataset, epochs=10) # 解冻后部分层进行微调 for layer in base_model.layers[-20:]: # 微调最后20层 layer.trainable = True model.compile(optimizer=AdamW(learning_rate=1e-5), loss='categorical_crossentropy') model.fit(train_dataset, epochs=5) ``` --- #### 四、关键注意事项 1. **输入尺寸兼容性**:若修改 `input_shape`,需确保分辨率满足 $r = \lfloor 224 \cdot \gamma^\phi \rfloor$(B3 中默认 300x300)。 2. **显存优化**: - 使用 **混合精度训练**(`tf.keras.mixed_precision.set_global_policy('mixed_float16')`)。 - 降低批量大小(Batch Size)或启用梯度累积。 3. **部署优化**:通过 TensorRT 或 OpenVINO 转换模型,使用 INT8 量化进一步压缩[^2]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值