文章目录
- 📖 课前导读
- 为什么神经网络必须要有激活函数?
- 学完这一课,你将能够:
- 一、知识原理:激活函数的本质与作用
- 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 一个优秀激活函数的特征
| 特征 | 为什么重要 |
|---|---|
| 非线性 | 如果不满足,多层网络≈单层线性模型,表达能力归零 |
| 可微性(几乎所有点) | 反向传播需要计算梯度,不可微点需要用次梯度近似 |
| 梯度值适中 | 梯度过大(爆炸)或过小(消失)都会导致训练困难 |
| 计算效率高 | 在大型模型中被调用亿万次,开销影响巨大 |
| 零中心(可选但有价值) | 有利于梯度更新路径更高效(避免锯齿形收敛) |
二、环境搭建与准备
importtorchimporttorch.nnasnnimporttorch.nn.functionalasFimportmatplotlib.pyplotaspltimportnumpyasnpprint(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恒为正,权重的更新路径呈锯齿状,降低收敛效率
- 梯度消失风险高:两边饱和区的导数极小,反向传播时梯度层层衰减
defsigmoid(x):return1/(1+torch.exp(-x))defsigmoid_derivative(x):s=sigmoid(x)returns*(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%的神经元死亡,严重降低模型的有效容量。
classReLU(nn.Module):defforward(self,x):returntorch.maximum(torch.zeros_like(x),x)defrelu_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家族速查表
| 函数 | 公式(x<0部分) | α取值 | 优点 | 缺点 |
|---|---|---|---|---|
| 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中使用SiLUsilu=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使用)classClassifier(nn.Module):def__init__(self,input_dim,num_classes):super().__init__()self.fc=nn.Linear(input_dim,num_classes)# 注意:这里不加Softmax,因为CrossEntropyLoss内部包含它defforward(self,x):logits=self.fc(x)# 原始输出值(未归一化的“得分”)returnlogits# 交给损失函数处理,不要手动加SoftmaxSoftmax和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+ 已内置Mishmish=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对比
写出两种错误的代码实现,解释它们为什么是错误的,以及正确的替代写法:
- 在网络的forward中加了Softmax,但又把输出传给了CrossEntropyLoss。
- 手动在最后一层做了Softmax,然后在优化循环里对输出直接用MSE Loss。
七、下一课预告
第8课我们将学习损失函数详解与深度学习损失选型,内容包括:
- 回归任务损失(MSE、L1、SmoothL1)
- 分类任务损失(交叉熵、NLLLoss、BCE)
- 自定义损失函数编写
- 多任务学习中的损失组合策略
- 训练中损失异常的排查思路
学完第8课,你将能根据不同任务精准选对损失函数,并解决训练中损失不下降、梯度消失/爆炸等常见难题。
附录:本章PyTorch激活函数API速查
| PyTorch模块 | 函数式版本 | 说明 |
|---|---|---|
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 从入门到精通》系列课程导航
去订阅
🌟 感谢您耐心阅读到这里!
💡 如果本文对您有所启发欢迎:
👍 点赞📌 收藏 📤 分享给更多需要的伙伴。
🗣️ 期待在评论区看到您的想法, 共同进步。
🔔 关注我,持续获取更多干货内容~
🤗 我们下篇文章见~