
文章目录
-
- 📖 课前导读
-
- 为什么神经网络必须要有激活函数?
- 学完这一课,你将能够:
- 一、知识原理:激活函数的本质与作用
-
- 1.1 激活函数在神经元中的位置
- 1.2 一个优秀激活函数的特征
- 二、环境搭建与准备
- 三、代码实战:逐类解读主流激活函数
-
- 3.1 Sigmoid:开拓者的荣光与桎梏
- 3.2 Tanh(双曲正切):零中心的改进
- 3.3 ReLU及其“死亡神经元”困境
- 3.4 LeakyReLU / PReLU / RReLU:对抗死亡ReLU的三兄弟
- 3.5 ELU与SELU:用指数函数让负值平滑过渡
- 3.6 ReLU家族速查表
- 3.7 SiLU / Swish:自门控平滑激活
- 3.8 GELU:Transformer的首选激活函数
- 3.9 Softmax:让输出变成“概率分布”
- 3.10 Mish:2019年的“完美主义者”
- 3.11 Gated Linear Unit(GLU)家族:大模型FFN的新标准
-
- 核心变体对比
- 3.12 激活函数选型终极决策树
- 3.13 不同任务激活函数选型速查表
- 3.14 梯度消失/死亡ReLU的预防与调试
- 四、难点解析:高频问题与陷阱
-
-
- 问题1:`RuntimeError: element 0 of tensors does not require grad`
- 问题2:在`CrossEntropyLoss`之前又多加了`nn.Softmax`
- 问题3:BN/Dropout和激活函数的顺序排错
- 问题4:怎么判断“死亡ReLU”正在影响训练?
-
- 五、课后总结
-
- 核心知识点速查表
- 检查清单
- 六、课后作业
-
-
- 作业1:激活函数绘图
- 作业2:死亡ReLU实验
- 作业3:任务选型判断题
- 作业4:Softmax vs CrossEntropy对比
-
- 七、下一课预告
- 🔗《精讲25课|PyTorch 从入门到精通》系列课程导航
📖 课前导读
为什么神经网络必须要有激活函数?
相信很多初学者都有这个疑问:为什么神经元加权求和后不能直接输出,非要加一个“激活函数”?
答案是:线性变换的复合仍然是线性变换。如果你把多个线性层叠起来,无论网络多深,最终都等价于一个单层线性模型,根本学不了XOR这样的简单非线性问题。激活函数存在的意义就是给神经网络注入非线性,让它有能力去拟合现实世界那些复杂的、弯弯曲曲的规律。
💡 生活类比:深度神经网络是一台发动机,数据是燃料,计算单元是气缸,而激活函数就是那个 “火花塞” 。它负责在合适的时机“点燃”信号,让信息在层与层之间有效传递。一个太弱的火花塞(如Sigmoid深层的饱和区)会让发动机熄火(梯度消失),一个不稳定的火花塞会让发动机爆震(梯度爆炸),而一个高效精准的火花塞,则是发动机平稳高效运行的关键。
学完这一课,你将能够:
- ✅ 理解9种主流激活函数的数学原理、图像和梯度特性
- ✅ 在PyTorch中正确使用不同的激活函数
- ✅ 解决死亡ReLU问题——知道什么时候该用LeakyReLU/PReLU
- ✅ 明白为什么Transformer用GELU/SiLU而不是ReLU
- ✅ 掌握不同任务(分类/回归/多模态)的激活函数选型策略
- ✅ 避开梯度消失、梯度爆炸等常见激活函数陷阱
一、知识原理:激活函数的本质与作用
1.1 激活函数在神经元中的位置
在每个神经元中,流程是这样的:输入信号 → 加权求和(加上偏置)→ 激活函数 → 输出到下一层。
如果用数学语言表达:output = activation(∑(w_i * x_i) + b)
关键事实:如果没有激活函数,无论网络有多少层,都只是线性模型的组合。线性模型的组合仍然是一个线性模型,根本无法拟合异或(XOR)这种最简单的非线性可分问题。激活函数之所以是“激活”的,就是它赋予了网络“开关”和“放大”信号的能力——让网络不仅知道“要不要传递”,还能以非线性的方式对信号进行变换。
1.2 一个优秀激活函数的特征
| 非线性 | 如果不满足,多层网络≈单层线性模型,表达能力归零 |
| 可微性(几乎所有点) | 反向传播需要计算梯度,不可微点需要用次梯度近似 |
| 梯度值适中 | 梯度过大(爆炸)或过小(消失)都会导致训练困难 |
| 计算效率高 | 在大型模型中被调用亿万次,开销影响巨大 |
| 零中心(可选但有价值) | 有利于梯度更新路径更高效(避免锯齿形收敛) |
二、环境搭建与准备
import torch
import torch.nn as nn
import torch.nn.functional as F
import matplotlib.pyplot as plt
import numpy as np
print(f"PyTorch版本: {torch.__version__}")
# 设置中文绘图(可选)
# plt.rcParams['font.sans-serif'] = ['SimHei']
# plt.rcParams['axes.unicode_minus'] = False
# 用于可视化的输入范围
x = torch.linspace(–5, 5, 200)
三、代码实战:逐类解读主流激活函数
3.1 Sigmoid:开拓者的荣光与桎梏
Sigmoid是所有激活函数中最“经典”的一个,曾被广泛用于神经网络的隐藏层。它的核心公式是:
sigmoid(x) = 1 / (1 + exp(-x))
它将任意实数x平滑地映射到(0, 1)区间,函数图像是一条光滑的S形曲线。它的特点是:
- 输出饱和:当x很大或很小时,输出被死死压在0或1的边界上
- 输出均值非0:梯度公式中x恒为正,权重的更新路径呈锯齿状,降低收敛效率
- 梯度消失风险高:两边饱和区的导数极小,反向传播时梯度层层衰减
def sigmoid(x):
return 1 / (1 + torch.exp(–x))
def sigmoid_derivative(x):
s = sigmoid(x)
return s * (1 – s)
y_sigmoid = sigmoid(x)
y_sigmoid_grad = sigmoid_derivative(x)
# 可视化
plt.figure(figsize=(12, 5))
plt.subplot(1, 2, 1)
plt.plot(x.numpy(), y_sigmoid.numpy(), 'b-', linewidth=2, label='Sigmoid')
plt.axhline(y=0, color='k', linestyle='-', alpha=0.3)
plt.axhline(y=1, color='k', linestyle='–', alpha=0.3)
plt.grid(True, alpha=0.3)
plt.legend()
plt.title('Sigmoid 函数')
plt.subplot(1, 2, 2)
plt.plot(x.numpy(), y_sigmoid_grad.numpy(), 'r-', linewidth=2, label='导数')
plt.axhline(y=0, color='k', linestyle='-', alpha=0.3)
plt.grid(True, alpha=0.3)
plt.legend()
plt.title('Sigmoid 导数')
plt.tight_layout()
plt.show()
何时使用:在现代深度学习中,Sigmoid几乎只在二分类输出层出现(配合交叉熵损失)。请不要在隐藏层使用Sigmoid,尤其是在网络层数较深的时候——它的饱和区会让深层网络的浅层梯度几乎消失,模型几乎无法训练。
# PyTorch中的使用方式
sigmoid_layer = nn.Sigmoid()
x_input = torch.randn(10)
print(sigmoid_layer(x_input)[:3])
# 函数式(推荐在forward中直接使用)
y = torch.sigmoid(x_input)
3.2 Tanh(双曲正切):零中心的改进
Tanh是Sigmoid经过平移和缩放后的“增强版”,把输出区间从(0,1)扩展到(-1,1),区间对称于原点。它的公式是:
tanh(x) = (exp(x) – exp(-x)) / (exp(x) + exp(-x))
- 输出零中心:让梯度方向更合理,避免权重的“锯齿形”更新路径
- 依然存在梯度饱和:当|x|很大时,导数依然压得很低,深层网络仍然存在梯度消失的风险
y_tanh = torch.tanh(x)
y_tanh_grad = 1 – y_tanh ** 2
# Tanh的导数和Sigmoid不同,它的峰值在x=0处最高
何时使用:在LSTM/GRU等门控循环网络里,Tanh被内置在单元结构中作为候选状态的激活函数。在普通全连接网络中,如果想用比Sigmoid更好但暂时不换ReLU,Tanh是一个过渡选择。
# PyTorch中的使用
tanh_layer = nn.Tanh()
y = tanh_layer(x_input)
3.3 ReLU及其“死亡神经元”困境
ReLU彻底改变了深度学习的面貌——它简单、快速、有效,公式简单得令人发指:
ReLU(x) = max(0, x)
它在正半轴的梯度恒定是1,在负半轴的梯度是0。这种稀疏性(只有少部分神经元被激活)带来了以下好处:计算效率极高(只判断正负,不用算指数);正区梯度恒定是1,有效缓解了Sigmoid/Tanh的梯度消失问题;同时产生稀疏表征,有助于解耦特征。
但是,它的致命缺陷是 “死亡ReLU” 问题:如果某个神经元的所有输入都是负的,它的输出恒为0,在反向传播中它的梯度也为0,权重的更新就永远停了。这导致该神经元“不可逆死亡”,再也没法被激活。在深度超过20层的网络中,未经特殊处理的ReLU可能导致30%–50%的神经元死亡,严重降低模型的有效容量。
class ReLU(nn.Module):
def forward(self, x):
return torch.maximum(torch.zeros_like(x), x)
def relu_derivative(x):
return (x > 0).float()
relu = nn.ReLU()
y_relu = relu(x)
y_relu_grad = relu_derivative(x)
可以看到ReLU的导数在x<0的区域是一条贴着零轴的平直线——梯度为0,这就是死亡神经元的根源。
典型场景:在图像分类、目标检测等视觉任务中,ReLU依然是默认首选。权重初始化用Kaiming(He)初始化,可以有效降低早期死亡ReLU的风险。
# 标准用法
relu_layer = nn.ReLU()
y = relu_layer(x_input)
# 函数式写法(更简洁)
y = F.relu(x_input)
3.4 LeakyReLU / PReLU / RReLU:对抗死亡ReLU的三兄弟
LeakyReLU的思想很直接:把负半轴的梯度从0改成一个极小的正斜率α,通常取α=0.01。公式变为:
LeakyReLU(x) = x if x > 0 else α*x
这就确保了即便输入是负的,神经元也能获得微弱的梯度更新,不会永久死亡。实验表明,α=0.01时神经元死亡率可降至5%以下。
LeakyReLU有两个“加强版”变体:
- PReLU(参数化ReLU):把负半轴的斜率α从固定的超参数变成一个可学习的参数,网络会在训练中自动学习这个斜率。在大规模图像分类任务中,PReLU较原始ReLU可提升约1.2%的Top-1准确率。
- RReLU(随机化ReLU):在训练时负半轴的斜率α在指定范围内随机采样,测试时取均值,有助于提升模型的泛化能力。
# LeakyReLU(PyTorch内置)
leaky_relu = nn.LeakyReLU(negative_slope=0.01)
y_leaky = leaky_relu(x)
# PReLU(可学习的参数,通常初始化为0.25)
prelu = nn.PReLU(num_parameters=1, init=0.25)
y_prelu = prelu(x)
# 对比ReLU和LeakyReLU的输出差异
test_x = torch.tensor([–2.0, –1.0, 0.0, 1.0, 2.0])
print(f"ReLU输出: {F.relu(test_x)}")
print(f"LeakyReLU输出: {F.leaky_relu(test_x, negative_slope=0.01)}")
print(f"PReLU输出: {prelu(test_x)}")
选型建议:不想引入额外参数时直接选LeakyReLU(默认α=0.01效果已经很不错了)。希望网络自动学习最优负斜率时用PReLU。RReLU在正则化要求较高的场景下是加分项。
3.5 ELU与SELU:用指数函数让负值平滑过渡
ELU(Exponential Linear Unit)用指数函数来替换负半轴的线性部分,使函数在x=0处保持光滑(连续且可导):
ELU(x) = x if x > 0 else α*(exp(x)-1)
它的最大特点是对噪声更鲁棒。SELU(Scaled ELU)是ELU的“缩放加强版”,关键在于它能让深层网络自动保持输出的均值为0、方差为1——在精心设计下,梯度既不会消失也不会爆炸,可以训练极深的网络,是一种“自归一化”的属性。
elu = nn.ELU(alpha=1.0)
y_elu = elu(x)
# SELU(alpha和scale参数已被预设,通常不必再改)
selu = nn.SELU()
y_selu = selu(x)
计算开销:ELU/SELU涉及指数运算,相比LeakyReLU开销高出约15%左右。如果你非常在意推理速度,可以优先考虑ReLU或LeakyReLU。
3.6 ReLU家族速查表
| ReLU | 0 | 固定=0 | 计算极快,稀疏性 | 死亡神经元 |
| LeakyReLU | α*x | 固定小值(0.01) | 解决死亡问题,计算快 | 需手动调α |
| PReLU | α*x | 可学习 | 更灵活,性能更佳 | 增加参数量 |
| RReLU | α*x(随机) | 训练随机/测试均值 | 自带正则化 | 引入随机性 |
| ELU | α*(e^x-1) | 固定(1.0) | 负值平滑,抗噪声 | 计算更重 |
| SELU | 缩放版ELU | 预设参数 | 自归一化 | 适用范围有限 |
3.7 SiLU / Swish:自门控平滑激活
SiLU(Sigmoid Linear Unit,也叫Swish)是一种非常现代的激活函数,来自Google的自动搜索发现,它在深层网络中往往能超过ReLU。
公式简洁优雅:SiLU(x) = x * σ(x),其中σ(x)是标准Sigmoid函数
它的核心机制是“自门控”:用一个与输入x相关的概率值(Sigmoid的输出)来调控原始信号的通过强度。它比ReLU更平滑,负半轴输出不为0,彻底从根源上解决了死亡神经元问题。同时,它在负区有一个小小的“凸起”,这种非单调性可能有助于网络捕捉更复杂的模式。
一个关键澄清:SiLU和Swish本质上是同一个函数。Google论文中曾带有一个可学习超参数β,但是大量实验表明β=1的效果最好,所以几乎所有深度学习框架实现的SiLU都直接固定为x * sigmoid(x)了。
# PyTorch中使用SiLU
silu = nn.SiLU()
y_silu = silu(x)
# 函数式写法
y_silu = F.silu(x)
何时使用:现代CNN架构(如EfficientNet)和大语言模型(如LLaMA)已经在广泛使用SiLU。如果你训练的模型深度在几十层以上,并且希望获得比ReLU更平滑的梯度传播,可以考虑SiLU。
3.8 GELU:Transformer的首选激活函数
GELU(Gaussian Error Linear Unit)的初衷很有意思——它来自随机正则化的数学期望形式。公式是:GELU(x) = x * Φ(x),这里Φ(x)是标准正态分布N(0,1)的累积分布函数(CDF)。它近似地起到了 “输入越大,越倾向于激活” 的概率门控效果。
GELU也是Transformer架构的前馈网络(FFN)中的标配。在BERT、GPT这些大语言模型和ViT视觉Transformer里,都是靠它来引入非线性的。
# PyTorch官方实现(自动处理近似精度)
gelu = nn.GELU()
y_gelu = gelu(x)
# 函数式用法
y_gelu = F.gelu(x)
# 如果你需要兼容旧版Transformer(如Hugging Face早期实现),可以用tanh近似
gelu_tanh = nn.GELU(approximate='tanh')
y_gelu_tanh = gelu_tanh(x)
性能优势:在NLP任务的GLUE基准上,GELU比ReLU普遍高出约0.5至1.2个百分点。在ViT的ImageNet任务上,GELU比Swish大概高出0.3的top-1准确率。这些平滑优势在处理连续的文本或语音数据时尤其明显。
3.9 Softmax:让输出变成“概率分布”
严格来说,Softmax不是激活函数,它是一种将向量映射成概率分布的归一化操作——在PyTorch的nn.CrossEntropyLoss内部,Softmax已经默认被包含了,所以你不必在最后一层手动加Softmax再接入损失函数。
公式是:Softmax(z_i) = exp(z_i) / Σ_j exp(z_j)
它的输出是一个和为1的概率分布:[p1, p2, …, pk],其中每个p_i>0,适合处理多分类问题。
# 多分类输出层(配合CrossEntropyLoss使用)
class Classifier(nn.Module):
def __init__(self, input_dim, num_classes):
super().__init__()
self.fc = nn.Linear(input_dim, num_classes)
# 注意:这里不加Softmax,因为CrossEntropyLoss内部包含它
def forward(self, x):
logits = self.fc(x) # 原始输出值(未归一化的“得分”)
return logits # 交给损失函数处理,不要手动加Softmax
Softmax和CE的内部关系:CrossEntropyLoss = LogSoftmax + NLLLoss。如果你在最后一层加了Softmax,又传给了CrossEntropyLoss,那就等于是重复做了一次指数归一化,模型会失去正确的数值稳定性。
3.10 Mish:2019年的“完美主义者”
Mish是2019年提出的,它结合了自门控机制和负半轴的平滑性,数学形式是:
Mish(x) = x * tanh(softplus(x)) = x * tanh(ln(1 + e^x))
它的优势是全区间光滑(处处可导)、允许负半轴有一个较小的非零输出、而且自带轻度的自正则化效果(输出被限制在一个比较温和的范围内)。在原始论文中,Mish在某些图像任务上比ReLU可以提升1-2个百分点。
主要代价:因为它要反复算指数、对数与tanh,计算开销是ReLU的几倍。在实际部署时,尤其在嵌入式设备上,这个计算负担是不可忽略的。
# PyTorch 1.9+ 已内置Mish
mish = nn.Mish()
y_mish = mish(x)
3.11 Gated Linear Unit(GLU)家族:大模型FFN的新标准
GLU(Gated Linear Unit)是一种门控线性单元,与普通激活函数最核心的区别在于:它不是简单地处理一个值,而是在FFN内并排设置两条线性变换路径,然后逐元素相乘。
其通用形式为:GLU(x) = (x * W1 + b1) * σ(x * W2 + b2),其中σ是Sigmoid门控。门控信号告诉网络哪些维度信息值得保留,哪些需要被削弱。
核心变体对比
| ReGLU | x_a * ReLU(x_b) | 较早期 |
| GeGLU | x_a * GELU(x_b) | 某些研究 |
| SwiGLU | x_a * SiLU(x_b) | PaLM、LLaMA、DeepSeek |
SwiGLU是当前最主流的选择,在PaLM、LLaMA等主流大模型中已经完全取代了传统ReLU和GELU的FFN结构。虽然它的参数量比标准FFN更多,但换来的是更强的表达力和更优的训练效率。
3.12 激活函数选型终极决策树
"""
激活函数选型决策树(思维导图版)
1. 是任务的输出层吗?
├── 是 → 多分类 → Softmax(由CrossEntropyLoss间接处理)
├── 是 → 二分类 → Sigmoid(一维输出,配合BCE Loss)
└── 是 → 回归 → 不加激活函数(纯线性输出)
2. 是Transformer/LLM架构吗?
├── 是 → GELU(GPT/BERT等经典Transformer)
│ └── 或 → SiLU / SwiGLU(PaLM/LLaMA等大模型趋势)
3. 是常规CNN/MLP隐藏层吗?
├── 追求极简与速度 → ReLU
├── 担心神经元死亡/深层训练 → LeakyReLU(PReLU)
└── 需要平滑负半轴 → ELU/SELU/Mish(如果算力充足)
"""
3.13 不同任务激活函数选型速查表
| 图像分类(CNN) | ReLU / LeakyReLU / SiLU | Softmax(多分类) | ReLU速度快,SiLU深层更平滑 |
| 目标检测 | ReLU / LeakyReLU | Sigmoid / Softmax | 检测头需独立输出类别+置信度 |
| 语义分割 | ReLU / ELU | Softmax(逐像素) | ELU对分割边界的噪声更鲁棒 |
| NLP(文本分类) | GELU / ReLU | Softmax | Transformer标配GELU,浅层模型用ReLU |
| 机器翻译/LM | GELU / SwiGLU | Softmax(词表大小) | SwiGLU是大模型新标准 |
| 时序预测(RNN/LSTM) | Tanh(内置) | 线性(回归) / Sigmoid(分类) | LSTM内部固定使用Tanh+Sigmoid |
| 生成模型(GAN/VAE) | LeakyReLU / Mish | Sigmoid(GAN判别器) | 防止死亡神经元,保持梯度稳定 |
| 强化学习 | ReLU / Tanh | 线性(连续动作)/ Softmax(离散动作) | Policy网络常用Tanh约束动作范围 |
3.14 梯度消失/死亡ReLU的预防与调试
死亡ReLU不是玄学,你完全可以用以下方法主动调试和预防:
- 监控神经元活动率:在训练过程中打印ReLU激活值的均值,如果均值长期接近于0,说明大量神经元失效了。可以中间穿插添加LeakyReLU来测试效果。
- 用合适的初始化:ReLU网络必须使用Kaiming(He)初始化,设置mode='fan_out'和nonlinearity='relu'。
- 梯度裁剪:如果训练初期Loss出现inf或nan,在反向传播后执行torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0)。
- 实验对比:同一组超参数下,用LeakyReLU/PReLU和ReLU分别跑一遍,若LeakyReLU的准确率明显更高,说明原始网络中死亡ReLU确实已经干扰了训练。
四、难点解析:高频问题与陷阱
问题1:RuntimeError: element 0 of tensors does not require grad
请检查输入数据是否被错误地转换成了整数类型(如torch.long)。SiLU/GELU等函数要求输入必须是浮点型(float32/float64)。改正方法:调用.float()把整数张量转成浮点。
问题2:在CrossEntropyLoss之前又多加了nn.Softmax
这是新手最常犯的错误之一。CrossEntropyLoss在内部其实做了LogSoftmax + NLLLoss的处理,如果你在外面手动又套了一个Softmax,梯度路径中就会连续叠加两次指数归一化,导致数值稳定性变差甚至退化到极低准确率。
问题3:BN/Dropout和激活函数的顺序排错
经典的最佳实践顺序是:Linear / Conv2d → BatchNorm → Activation → Dropout。
- BatchNorm放在激活函数之前(Pre-activation)是目前的主流做法,能让激活函数的输入分布更加稳定。
- Dropout放在激活函数之后,让它作用于被激活的特征,而不是原生的净输入。
问题4:怎么判断“死亡ReLU”正在影响训练?
如果你怀疑模型陷入了死亡ReLU的困境,可以定期打印各层权重梯度的标准差。如果中间层梯度出现大量接近于0的参数,而且训练损失几乎不再下降,说明ReLU死亡问题正在显现。换用LeakyReLU或PReLU再做一组对比实验,如果后者迅速开始收敛,验证了死亡ReLU的判断。
五、课后总结
核心知识点速查表
| Sigmoid | 1/(1+e^-x) | 平滑、可微、值域(0,1) | 梯度消失、非零中心 | 二分类输出层 |
| Tanh | (ex-e-x)/(ex+e-x) | 零中心 | 仍有梯度消失 | LSTM单元 |
| ReLU | max(0,x) | 计算快、稀疏、缓解梯度消失 | 死亡神经元 | CNN/MLP隐藏层(默认) |
| LeakyReLU | x if x>0 else αx | 解决死亡神经元 | 需调α | 深层网络 |
| PReLU | x if x>0 else αx(α可学习) | 最灵活 | 增加参数量 | 大型模型 |
| ELU | x if x>0 else α(e^x-1) | 负值平滑、抗噪声 | 计算更重 | 对噪声敏感的任务 |
| SiLU/Swish | x*σ(x) | 平滑、自门控、无死亡 | 比ReLU稍慢 | 深层网络、大模型 |
| GELU | x*Φ(x) | Transformer首选、性能优 | 计算开销中等 | Transformer/ViT |
| Softmax | ez_i/Σez_j | 概率分布 | 只能多分类输出 | 多分类输出层 |
| Mish | x*tanh(softplus(x)) | 自正则化、平滑 | 计算极重 | 高端视觉任务 |
| SwiGLU | x_a*SiLU(x_b) | 大模型新标准 | 参数量增加 | PaLM/LLaMA等 |
检查清单
- 能解释为什么神经网络必须使用非线性激活函数
- 理解ReLU死亡问题的根源与解决方案
- 知道Softmax应与CrossEntropyLoss配合使用,不在输出层重复添加
- 能根据任务类型(分类/回归/Transformer)选择合适的激活函数
- 会使用PyTorch的nn和F两种方式调用激活函数
- 能识别梯度消失的征兆并尝试调整激活函数
- 了解SwiGLU等门控激活函数的演进趋势
六、课后作业
作业1:激活函数绘图
使用torch.linspace(-10, 10, 200)绘制ReLU、LeakyReLU(α=0.01)、ELU(α=1)、SiLU、GELU在同一个坐标轴上的函数曲线,并标注其导数曲线(可用子图呈现)。
作业2:死亡ReLU实验
构建一个5层全连接网络(每层512个神经元,激活函数ReLU),在MNIST上训练10个epoch。在训练过程中hook住每一层ReLU层的激活值,统计每层激活值为0的比例。换用LeakyReLU(α=0.01)再训练一次,对比两组的测试准确率和神经元死亡率。
作业3:任务选型判断题
假设你接到以下任务,请选出一个最合适的隐藏层激活函数和输出层激活函数:
- (A)电影评论情感二分类(用LSTM)
- (B)CIFAR-10图像分类(用ResNet-18)
- (C)房价预测(回归)
- (D)自己实现一个迷你版GPT
作业4:Softmax vs CrossEntropy对比
写出两种错误的代码实现,解释它们为什么是错误的,以及正确的替代写法:
七、下一课预告
第8课我们将学习损失函数详解与深度学习损失选型,内容包括:
- 回归任务损失(MSE、L1、SmoothL1)
- 分类任务损失(交叉熵、NLLLoss、BCE)
- 自定义损失函数编写
- 多任务学习中的损失组合策略
- 训练中损失异常的排查思路
学完第8课,你将能根据不同任务精准选对损失函数,并解决训练中损失不下降、梯度消失/爆炸等常见难题。
附录:本章PyTorch激活函数API速查
| nn.Sigmoid() | torch.sigmoid() | 输出(0,1),饱和易消失 |
| nn.Tanh() | torch.tanh() | 输出(-1,1),零中心 |
| nn.ReLU() | F.relu() | 正区梯度1,负区0 |
| nn.LeakyReLU(negative_slope) | F.leaky_relu(x, negative_slope) | 负区保留小梯度 |
| nn.PReLU(num_parameters) | — | α可学习 |
| nn.ELU(alpha) | F.elu(x, alpha) | 负区指数过渡 |
| nn.SELU() | F.selu() | ELU的自归一化版本 |
| nn.SiLU() | F.silu() | 即Swish(β=1) |
| nn.GELU(approximate) | F.gelu(x, approximate) | Transformer标配 |
| nn.Softmax(dim) | F.softmax(x, dim) | 多分类输出,慎用于损失前 |
| nn.LogSoftmax(dim) | F.log_softmax(x, dim) | 数值稳定的log概率 |
| nn.Mish() | F.mish() | PyTorch 1.9+ |
🔗《精讲25课|PyTorch 从入门到精通》系列课程导航
去订阅
🌟 感谢您耐心阅读到这里! 💡 如果本文对您有所启发欢迎: 👍 点赞📌 收藏 📤 分享给更多需要的伙伴。 🗣️ 期待在评论区看到您的想法, 共同进步。 🔔 关注我,持续获取更多干货内容~ 🤗 我们下篇文章见~



