码到成功

卷积神经网络新姿势:除了余弦卷积,这几招也值得认真看一眼


这类东西最迷人的点不在于名字新,而在于它们通常都有一个共同特点:

- 改动不算夸张
- 很容易塞进现有 CNN
- 一旦有效,替换成本往往不高

所以它们特别适合拿来做实验。你不一定非得一把梭全网替换,但很适合在自己的模型里先试一层、两层,看看味道对不对。

## 这篇主要在看什么

从公开摘要里可以确认,这篇“其他”主要点到了两类很值得留意的思路:

- `SinReLU`
- `Swish`

它们的共性是:都在想办法让传统激活函数别那么“硬”,让网络在表达能力和训练行为上多一点回旋空间。


如果用一句话概括这篇的气质,大概就是:

`不是推翻 CNN,而是想把它常用的小零件再打磨一下。`

## 1. SinReLU:给 ReLU 加一点“波动感”

ReLU 之所以流行,是因为简单、直接、好训。

但它也有自己的脾气:

- 形状太硬
- 负半轴信息处理得很绝
- 对某些更细腻的模式变化不够灵活

`SinReLU` 这类思路,可以粗略理解成:

`在 ReLU 的门控逻辑之外,再给它一点正弦式的起伏感。`

你不用一上来就死背公式,先抓直觉就够了:

- 它还是保留了 ReLU 系的核心味道
- 但不想让激活曲线一直板着脸
- 希望通过一点周期性或波动性,把表达能力再抬一截

这种设计为什么会让人觉得有潜力?

- 一方面,它不像完全重新发明一个巨大模块
- 另一方面,它又不是纯粹的参数微调,而是真的在“形状”上做文章

对于卷积网络来说,激活函数的形状一旦变得更灵活,网络对局部模式、细小变化、复杂响应的表达,就有机会跟着变活一点。

当然,这类方法是不是一定比 ReLU 强,不存在绝对答案。更准确的说法是:

`它给了你一个值得试的方向。`

## 2. Swish:让输入自己给自己做一道“软门”

如果说 SinReLU 更像是在 ReLU 的基础上加一点波动感,那么 `Swish` 的气质就更偏“平滑门控”。

它最常见的写法是:

```text
f(x) = x * sigmoid(beta * x)
```

这个式子看起来很短,但味道很足。

它不像 ReLU 那样:

- 负数就直接拍平
- 正数就线性放行

而是给输入乘上一个 `sigmoid(beta * x)` 形成的软权重。于是:

- 较大的正值会被更明显地保留
- 一部分负值不会被立刻清零
- 整体曲线比 ReLU 更平滑


从使用体验上看,Swish 最讨喜的地方就在这里:

- 它没有复杂到吓人
- 又比普通 ReLU 多了点“弹性”

所以很多人第一次看到它时都会有一种感觉:

`这玩意儿,好像真的挺适合直接塞进现有网络里试试。`

## 3. 为什么这种“小改动”会让人上头

很多 CNN 新招并不是在 backbone 上狠狠干一刀,而是专挑“小但关键”的位置下手。

比如:

- 激活函数
- 卷积核的计算方式
- 通道之间的重新加权
- 局部和全局信息的混合方式

这类位置有个特点:

`它们虽然小,但几乎贯穿全网。`

所以你改一个激活函数,影响的并不是一层两层,而是整张网络的非线性表达方式。

这也是为什么很多看起来只是“小修小补”的论文,最后却能带来挺像样的提升。

## 4. 这类新姿势的价值,不只是精度涨没涨

很多人看新模块时,第一反应都是:

`那它到底涨了几点?`

这个问题当然重要,但还不够。

像 `SinReLU`、`Swish` 这种东西,真正有价值的地方通常还包括:

- 是否容易接进现有模型
- 是否容易训练
- 是否引入太多额外参数
- 推理成本有没有明显变重
- 对不同网络深度是否稳定

尤其是 `Swish` 这一类,优点就在于它非常像一个“替换成本不高”的升级件。对工程实践来说,这种东西的吸引力其实很大。

## 5. 如果你是做 CNN 实验的,应该怎么试

我的建议很简单:别一上来全网换血。

先从小规模替换开始更稳:

1. 选一个你已经跑熟的 baseline
2. 先只替换少数 block 里的激活函数
3. 保持学习率、batch size、训练轮数不变
4. 看 loss 曲线、收敛速度和验证集表现

这样你能更快判断:

- 是真的有效
- 还是只是训练波动

## 6. 在 Python 里试这种新招,门槛其实没那么高


### 6.1 在 TensorFlow / Keras 里试一个 Swish

```python
import tensorflow as tf
from tensorflow import keras


def swish(x, beta=1.0):
    return x * tf.sigmoid(beta * x)


inputs = keras.Input(shape=(32, 32, 3))
x = keras.layers.Conv2D(32, 3, padding="same")(inputs)
x = keras.layers.BatchNormalization()(x)
x = keras.layers.Activation(swish)(x)
x = keras.layers.GlobalAveragePooling2D()(x)
outputs = keras.layers.Dense(10, activation="softmax")(x)

model = keras.Model(inputs, outputs)
model.summary()
```

这类实验的核心不是代码多复杂,而是你能不能把变量控制住。

### 6.2 在 PyTorch 里直接写个 Swish 模块

```python
import torch
import torch.nn as nn


class Swish(nn.Module):
    def __init__(self, beta=1.0):
        super().__init__()
        self.beta = beta

    def forward(self, x):
        return x * torch.sigmoid(self.beta * x)


x = torch.randn(4, 64, 32, 32)
act = Swish(beta=1.0)
y = act(x)
print(x.shape, y.shape)
```

顺手提一句,在 PyTorch 里你也可以直接看看 `SiLU`,它和 Swish 非常接近:

```python
import torch.nn as nn

act = nn.SiLU()
```

如果你只是想快速做实验,`SiLU` 会很顺手。

### 6.3 一个简化版“SinReLU 风格”尝试

严格实现要看你采用的具体定义,但如果只是为了体验“ReLU + 周期扰动”的思路,可以先写一个玩具版本:

```python
import torch
import torch.nn as nn


class ToySinReLU(nn.Module):
    def __init__(self, alpha=0.1):
        super().__init__()
        self.alpha = alpha

    def forward(self, x):
        return torch.relu(x) + self.alpha * torch.sin(x)


x = torch.linspace(-4, 4, 9)
layer = ToySinReLU(alpha=0.05)
print(layer(x))
```

这个版本不是论文标准实现,但足够帮你理解那种“在 ReLU 基础上加一点波动感”的直觉。

## 7. 这类方法真正适合谁

如果你属于下面这几类人,这种“新姿势”很值得看:

- 手里已经有一个成熟 CNN baseline
- 想做低成本结构改造
- 不想一上来重写大网络
- 愿意用小实验换一些潜在提升

它不一定每次都能带来压倒性收益,但很适合拿来做:

- 模块替换实验
- 激活函数 ablation
- 轻量结构优化

## 8. 最后收个尾

这篇“其他”看起来像是在随手记几个新点子,其实很有代表性。

它传达出来的意思大概是:

`CNN 不只是 backbone 在进化,连最基础的激活函数和局部计算方式,也一直有人在认真折腾。`

其中:

- `SinReLU` 更像是在 ReLU 的表达形状上加活力
- `Swish` 更像是在平滑门控上做优化

它们未必能在所有场景里都赢,但绝对属于那种值得你放进实验清单里的东西。

说白了,很多模型提升并不是一拳把房子拆了重盖,而是把原来那些“默认值”一个个重新审视。激活函数这件事,恰好就是最典型的一项。