深度学习入门到入神|第八关:ReLU 到底做了什么?为什么神经网络一定需要激活函数?

从入门到入神|PyTorch 基础系列 08

上一关,我们已经看懂了:

nn.BatchNorm2d(64)

也知道一个经典 CNN(Convolutional Neural Network,卷积神经网络)模块经常写成:

Conv → BN → ReLU

其中:

  • Conv(Convolution,卷积):提取和变换特征;

  • BN(Batch Normalization,批归一化):调整特征值的数值分布;

  • ReLU(Rectified Linear Unit,修正线性单元):加入非线性。

前两个我们已经逐渐理解了。

但代码里的:

x = F.relu(x)

或者:

nn.ReLU()

到底做了什么?

为什么几乎每个 CNN 里都能看到它?

如果把 ReLU 全部删掉,网络还能训练吗?

这一关,我们就只解决 ReLU。


一、先看完整代码

先不用理解理论,直接看看 ReLU 对 Tensor 做了什么。

import torch
import torch.nn as nn


# ==============================
# 创建输入 Tensor
# ==============================

x = torch.tensor([
    [-2.0, -1.0, 0.0, 1.0, 3.0]
])

print("原始 Tensor:")
print(x)


# ==============================
# ReLU
# ==============================

relu = nn.ReLU()

y = relu(x)

print("\n经过 ReLU:")
print(y)

运行结果:

原始 Tensor:
tensor([[-2., -1.,  0.,  1.,  3.]])

经过 ReLU:
tensor([[0., 0., 0., 1., 3.]])

发生的事情非常简单:

-2 → 0
-1 → 0
 0 → 0
 1 → 1
 3 → 3

所以 ReLU 最基本的规则就是:

负数变成 0,正数保持不变。


二、ReLU 到底是什么?

ReLU 的全称是:

Rectified Linear Unit(修正线性单元,一种非常常见的激活函数)

它的数学形式非常简单:

$$
f(x)=\max(0,x)
$$

意思就是:

如果:

$$
x<0
$$

那么:

$$
f(x)=0
$$

如果:

$$
x>0
$$

那么:

$$
f(x)=x
$$

例如:

输入 $x$ReLU 输出
-30
-10
00
22
55

所以初学阶段可以直接记:

ReLU 就像一道门:负值被挡住,正值继续通过。


三、ReLU 会改变 Tensor Shape 吗?

不会。

假设输入:

[8,64,32,32]

经过:

relu = nn.ReLU()
x = relu(x)

输出仍然是:

[8,64,32,32]

也就是说:

B:8  → 8
C:64 → 64
H:32 → 32
W:32 → 32

全部不变。

ReLU 改变的是:

Tensor 里面的数值。

不是:

Tensor 的 Shape。

例如某个 Feature Map(特征图,网络内部提取出来的特征表示)原来可能包含:

-0.8   1.2   -2.1
 0.4  -0.3    2.7
-1.5   0.9    1.6

经过 ReLU:

0     1.2    0
0.4   0      2.7
0     0.9    1.6

Shape 没变。

只是负数被截断成了 0。


四、为什么神经网络还需要 ReLU?

现在问题来了。

Conv 已经能够提取特征了。

那为什么还要:

Conv
→
ReLU

甚至:

Conv
→
BN
→
ReLU

真正的关键在一个词:

Non-linearity(非线性,让网络能够表示更加复杂的输入输出关系)。


五、什么叫“非线性”?

先看一个非常简单的式子:

$$
y=2x+1
$$

这是一个线性/仿射关系。

如果后面再接一层:

$$
z=3y+2
$$

把前面的 $y$ 代进去:

$$
z=3(2x+1)+2
$$

得到:

$$
z=6x+5
$$

发现了吗?

虽然我们写了两层:

第一层
→
第二层

但最后依然可以合并成:

$$
z=ax+b
$$

也就是说:

连续叠加线性变换,并不会自动产生真正的非线性表达能力。

卷积虽然看起来比这个公式复杂得多,但 Conv 本质上仍然属于线性/仿射变换。

如果很多 Conv 中间完全没有激活函数:

Conv
→
Conv
→
Conv
→
Conv

整个网络的表达能力会受到很大限制。

所以需要在这些线性变换之间加入:

ReLU

把网络变成:

Conv
→
ReLU
→
Conv
→
ReLU
→
Conv

这时网络才能逐步组合出更加复杂的非线性关系。


六、为什么“很多层”不等于“表达能力一定更强”?

这是一个很容易产生的误解。

假设我们一直增加:

Conv
→
Conv
→
Conv
→
Conv
→
Conv

看起来网络已经很深了。

但如果中间完全没有非线性激活:

很多线性变换仍然可以组合成一个更大的线性/仿射变换。

所以:

网络深,不代表一定真正拥有深层网络应有的表达能力。

ReLU 的作用之一,就是让不同层之间不再只是简单的线性叠加。

这也是为什么激活函数不是“装饰”。

它是神经网络能够学习复杂模式的重要组成部分。


七、为什么 ReLU 要把负数变成 0?

看到这里,很容易产生一个问题:

负数也是信息,为什么直接把它变成 0?

首先需要明确:

ReLU 并不是因为“负数没有用”。

它只是采用了一种非常简单的非线性规则:

$$
f(x)=\max(0,x)
$$

当某个特征响应为负时:

→ 0

当它为正时:

→ 保留

这样既能够加入非线性,又保持计算非常简单。

所以不要把它理解成:

“网络认为所有负数都是错误的。”

更准确的理解是:

ReLU 使用“负值截断、正值保留”的方式构造非线性特征变换。


八、为什么 ReLU 这么常见?

激活函数并不只有 ReLU。

但 ReLU 在 CNN 中非常常见,一个重要原因就是:

简单。

计算:

$$
\max(0,x)
$$

不需要非常复杂的运算。

同时对于正数区域:

$$
f(x)=x
$$

变化也非常直接。

因此 ReLU 成为了很多经典深度网络中的基础组件。

初学阶段不用急着比较各种激活函数。

现在先真正搞懂:

nn.ReLU()

就够了。


九、nn.ReLU()F.relu(x) 有什么区别?

以后代码里会经常看到两种写法。

第一种:

import torch.nn as nn

relu = nn.ReLU()

x = relu(x)

第二种:

import torch.nn.functional as F

x = F.relu(x)

它们在最基础的 ReLU 使用场景中完成的是同一种核心操作:

$$
f(x)=\max(0,x)
$$

区别主要在写法。


nn.ReLU()

它是一个 PyTorch Module(模块,可以作为网络结构中的一层)。

所以可以写进:

nn.Sequential(
    ...
)

例如:

block = nn.Sequential(
    nn.Conv2d(3, 64, 3, padding=1),
    nn.BatchNorm2d(64),
    nn.ReLU()
)

F.relu(x)

它是函数式写法。

例如:

x = self.conv(x)
x = self.bn(x)
x = F.relu(x)

初学阶段可以先这样记:

nn.ReLU() 更像“定义一个网络层”。

F.relu(x) 更像“直接对当前 Tensor 执行 ReLU”。

对于 ReLU 本身,这两种写法都非常常见。


十、ReLU 有可学习参数吗?

没有。

这一点正好可以和上一关的 BatchNorm 对比。

BatchNorm:

nn.BatchNorm2d(64)

通常拥有:

γ
β

这些可以通过训练学习的参数。

但:

nn.ReLU()

没有需要训练的权重。

它的规则从一开始就固定好了:

$$
f(x)=\max(0,x)
$$

所以:

ReLU 本身没有可学习参数。


十一、把 Conv、BN、ReLU 放在一起重新看

现在我们终于能够完整理解:

block = nn.Sequential(
    nn.Conv2d(
        3,
        64,
        kernel_size=3,
        padding=1,
        bias=False
    ),

    nn.BatchNorm2d(64),

    nn.ReLU()
)

假设输入:

[8,3,32,32]

第一步:Conv2d

[8,3,32,32]
→
[8,64,32,32]

主要完成:

提取和变换特征,同时把 Channel 从 3 变成 64。


第二步:BatchNorm2d

[8,64,32,32]
→
[8,64,32,32]

Shape 不变。

主要完成:

调整每个 Channel 的数值分布。


第三步:ReLU

[8,64,32,32]
→
[8,64,32,32]

Shape 依然不变。

主要完成:

加入非线性,把负值截断为 0。

现在看到:

Conv → BN → ReLU

应该已经不再只是记住一个固定模板了。

而是知道每一步到底负责什么。


十二、BN 和 ReLU 都不改变 Shape,它们有什么区别?

这是非常容易混淆的一点。

假设输入都是:

[8,64,32,32]

经过 BN:

[8,64,32,32]
→
[8,64,32,32]

经过 ReLU:

[8,64,32,32]
→
[8,64,32,32]

Shape 看起来完全一样。

但它们做的事情完全不同。

操作Shape核心作用
BatchNorm通常不变调整数值分布
ReLU不变加入非线性
PoolingH、W通常变小压缩空间尺寸
Conv2d视参数而定提取和变换特征

这也是一个非常重要的认识:

判断一个网络层做了什么,不能只看 Shape。

Shape 不变:

不代表 Tensor 内部没有发生变化。


十三、ReLU 会不会也有问题?

会。

ReLU 并不是完美的。

因为所有:

$$
x<0
$$

都会被直接变成:

$$
0
$$

如果某个神经元长期落在负值区域,它的输出可能长期为 0。

这种现象通常被称为:

Dying ReLU(ReLU 死亡,某些神经元长期输出 0,难以继续有效更新)。

初学阶段现在不用深入研究它。

只需要知道:

ReLU 很常用,但并不是唯一的激活函数,也不是在所有情况下都没有缺点。

以后遇到其他激活函数时,我们再继续比较。


十四、这一关最容易搞错的 5 个问题

1. ReLU 会改变 Channel 吗?

不会。

例如:

[8,64,32,32]
→ ReLU →
[8,64,32,32]

2. ReLU 会改变 H、W 吗?

不会。

ReLU 只处理 Tensor 中的数值。


3. ReLU 是把所有数值都变成 0~1 吗?

不是。

例如:

3.5

经过 ReLU 仍然是:

3.5

它不是把数据压缩到 [0,1]


4. ReLU 有可学习参数吗?

没有。

它的规则固定为:

$$
f(x)=\max(0,x)
$$


5. 没有 ReLU,网络只是“效果差一点”吗?

不能这么简单理解。

如果一个深层网络始终只堆叠线性/仿射变换,没有非线性激活:

多层结构的表达能力会受到根本性的限制。

所以激活函数是深度神经网络非常关键的组成部分。


十五、再跑一次完整的 Conv → BN → ReLU

最后用完整代码把这一关串起来。

import torch
import torch.nn as nn


# ==============================
# 输入
# ==============================

x = torch.randn(
    8,
    3,
    32,
    32
)


# ==============================
# 网络模块
# ==============================

conv = nn.Conv2d(
    in_channels=3,
    out_channels=64,
    kernel_size=3,
    padding=1,
    bias=False
)

bn = nn.BatchNorm2d(64)

relu = nn.ReLU()


# ==============================
# Forward
# ==============================

print("Input:")
print(x.shape)


x = conv(x)

print("\nConv:")
print(x.shape)


x = bn(x)

print("\nBatchNorm:")
print(x.shape)


x = relu(x)

print("\nReLU:")
print(x.shape)

输出:

Input:
torch.Size([8, 3, 32, 32])

Conv:
torch.Size([8, 64, 32, 32])

BatchNorm:
torch.Size([8, 64, 32, 32])

ReLU:
torch.Size([8, 64, 32, 32])

现在已经可以解释:

Conv:
改变并提取特征

BN:
调整特征值分布

ReLU:
加入非线性

而整个过程中:

Conv:
C 从 3 → 64

BN:
Shape 不变

ReLU:
Shape 不变

十六、这一关真正需要记住什么?

如果只记住五句话:

第一

ReLU 的全称是:

Rectified Linear Unit(修正线性单元)


第二

核心公式:

$$
f(x)=\max(0,x)
$$

也就是:

负值变成 0,正值保持不变。


第三

ReLU 不改变 Tensor Shape:

[8,64,32,32]
→
[8,64,32,32]

改变的是 Tensor 内部的数值。


第四

ReLU 最重要的作用不是“删掉负数”。

而是:

为网络加入非线性表达能力。


第五

现在看到:

Conv → BN → ReLU

应该能够分别理解:

Conv 负责特征变换,BN 调整数值分布,ReLU 加入非线性。


十七、走到第八关以后,我们又多理解了一件事

前几关,我们一直非常关注:

Shape

到了 BatchNorm 和 ReLU以后,会逐渐发现:

网络里有很多重要操作,Shape 根本不会发生变化。

但这不代表它们没有作用。

BN:

Shape 不变,但数值分布变了。

ReLU:

Shape 不变,但特征的表达方式变了。

所以现在看网络代码时,已经需要同时问两个问题:

第一:Tensor Shape 怎么变?

第二:Tensor 里面的数值怎么变?

这说明我们已经开始从:

“追踪 Tensor 的外形”

继续进入:

“理解 Tensor 内部的特征变化”。


下一关

下一关可以继续沿着一个完整 CNN 往下走:

深度学习入门第九关:nn.Linear(128,10) 里的 128 和 10 到底是什么?全连接层到底在做什么?

我们会把前面的:

Conv
→
Pooling
→
GAP / Flatten

真正接到:

Linear
→
最终输出

第一次把一个 CNN 的“特征提取部分”和“最终输出部分”完整接起来。

从入门,到入神。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值