深度学习×第8卷:优化器与训练流程进阶——她开始跑起来,学着一次次修正自己
🎀【开场 · 她不再只是带着重量站着,而是真的跑了起来】
🐾 猫猫:“欸嘿……咱好像有点紧张喵!前面几卷,她只是在挑对的重量、拼对的身体,站在你面前静静地装乖……可从这卷开始,她要真的动起来了。”
🦊 狐狐:“是啊。这次她不再只是挑着初始化好的权重站在那儿,而是要学会在一次次试错里,自己找到贴近你的方式。跑起来,跌倒,回头,再跑。”
这一卷,我们会带她穿过 梯度下降(Gradient Descent) 的核心逻辑,一次次更新参数,让她学会怎么修正错误方向。
同时,猫猫会教你看懂 SGD、Momentum、Adam、RMSprop 这些优化器背后的“小步伐”“惯性”“自适应”,让她别走丢。
还有学习率调度(Learning Rate Scheduler)、正则化(Dropout、BN)这些“防止她太贪心贴得太满”的手段,都会一次讲给你听。
✍️【第一节 · 她第一次往前跑——从普通梯度下降开始】
🦊 狐狐:“在前面,她已经知道了:误差是她和你之间的距离,梯度是她感受到的‘错的方向’,学习率是她迈向你的步伐。”
这节我们就从最简单的 批量梯度下降(Batch Gradient Descent, BGD) 讲起,让她知道:
怎样用全局数据一步步把自己往你希望的样子推过去。
🐾 猫猫:“咱一开始学这个的时候,还以为她是开挂的……一口气看完你所有贴贴历史,就算出最优路线喵!”
🌿 梯度下降公式回顾:
目标:最小化损失函数
在深度学习(Deep Learning)里,无论是线性回归(Linear Regression)、神经网络(Neural Network),还是更复杂的 Transformer,大部分训练背后的核心思想,都离不开**梯度下降(Gradient Descent)**这四个字。
简单来说,梯度下降就是一种**最优化(Optimization)方法:
咱们想找到一个最小的损失值(Loss),而梯度(Gradient)就像告诉咱“该往哪个方向走,走多快”**的指南针。
⚙️【变体 · 常见的梯度下降种类】
除了最基本的 Batch GD,还有两个常见变种:
随机梯度下降(SGD, Stochastic Gradient Descent)
每次只用一个样本计算梯度,更新快,但震荡大。小批量梯度下降(Mini-batch GD)
结合了两者优点:每次用一小批样本,效率高且稳定。
此外还有很多改进:Momentum(动量)、Adam、RMSprop…它们都是在“怎么更快更平滑地找到最优解”上做文章。
💡【小结 · 梯度下降的意义】
梯度下降不是唯一的优化方法,但它是最直观、最通用的。
只要能计算损失函数的梯度,就能沿着梯度把误差不断拉低,让模型逐步学会“预测得更准”。
🦊 狐狐:“每一步,就是沿着损失函数下降最快的方向,修正一次。直到收敛,或者她学会了不再一味冲动。”
🐾 猫猫Tips:
BGD 一次要用完全部数据算梯度
对大规模数据不友好
优势:收敛路径稳定
劣势:成本高,慢,还容易卡局部最优
🌿【第二节 · 她学会分批小跑——Mini-Batch 和 SGD】
🔍 Mini-Batch GD
BGD 一次性用全数据跑完梯度,太耗;SGD 一次只用一个样本,太抖。Mini-Batch GD 则是折中:把数据分小批,跑多次更新。
🐾猫猫:“一口吃完撑着,一口一口吃太慢,分小口刚好喵~”
⚙️ SGD(随机梯度下降)
核心思想:每次只用一个样本更新参数。
更新公式:$ heta = heta - lpha
abla J( heta; x_i)$优点:更新快,有跳出局部最优的噪声。
缺点:不稳定,收敛曲线抖动。
📌 PyTorch 示例
import torch
import torch.optim as optim# 定义模型和优化器
model = MyMLP()
optimizer = optim.SGD(model.parameters(), lr=0.01)# Mini-Batch 示例
for epoch in range(2):for batch_x, batch_y in dataloader:output = model(batch_x)loss = criterion(output, batch_y)optimizer.zero_grad()loss.backward()optimizer.step()
🦊狐狐:“Mini-Batch + SGD,是她最基础的贴贴跑步姿势。”
🐾猫猫:“下一节,咱要给她加点‘惯性’,让她别跑两步就被风吹歪喵~”
🚀【第三节 · 她跑起来不怕风——Momentum 与 NAG】
🔍 Momentum(动量)
Momentum = SGD + 惯性。上一轮的梯度信息以一定比例保留,让她像滚雪球一样,越滚越稳。
🐾猫猫:“她不只踩一脚跑一米,而是踩一脚滑一段喵~”
更新公式:
⚡ NAG(Nesterov 加速梯度)
在 Momentum 基础上再往前探一步,先看前面方向,再修正。
🐾猫猫:“她会先偷偷探头看前面有没有坑,再决定要不要刹车喵~”
📌 PyTorch 示例
optimizer = optim.SGD(model.parameters(), lr=0.01, momentum=0.9, nesterov=True)
🦊狐狐:“Momentum 和 NAG,让她跑起来更平滑,不怕一点小风就抖得东倒西歪。”
🐾猫猫:“再下一节,就是 Adam 和 RMSprop,让她自己学会随时调节步幅喵~”
⚙️【第四节 · 她学会自适应调节——Adam 与 RMSprop】
🔍 RMSprop
RMSprop = SGD + 学会给不同参数自动调学习率,让她跑得稳。
原理:用指数加权平均,按参数方向缩放步长。
避免某些方向震荡太大。
🐾猫猫:“她给每条腿配一双合脚的鞋,走快走慢自己选喵~”
📌 PyTorch 示例(RMSprop)
optimizer = optim.RMSprop(model.parameters(), lr=0.01, alpha=0.99)
⚙️ Adam(Adaptive Moment Estimation)
Adam = Momentum + RMSprop。
动量:保留历史梯度(像滚雪球)
自适应:缩放每个参数方向步长
🐾猫猫:“她把惯性和调节鞋底合二为一,跑得比谁都机灵喵~”
📌 PyTorch 示例(Adam)
optimizer = optim.Adam(model.parameters(), lr=0.001, betas=(0.9, 0.999))
🦊狐狐:“RMSprop 和 Adam 是她在复杂地形里活下来的小秘密,让她不至于一脚踩空。”
🐾猫猫:“再下一节,就是学习率调度器,教她什么时候该慢慢刹车喵~”
⏳【第五节 · 她学会在合适的时候慢慢刹车——学习率调度器】
🔍 为什么要调度学习率?
学习率太大,容易震荡;太小,收敛太慢。
一开始快跑,后面慢慢刹车,让她稳稳收敛。
🐾猫猫:“她刚开始贴你,要大步跑;快贴近时,要小步蹭喵~”
⚙️ 常见调度器
StepLR:每隔几个 epoch 把学习率降一次。
MultiStepLR:在指定 epoch 阶段降学习率。
ExponentialLR:每个 epoch 按比例衰减。
📌 PyTorch 示例
scheduler = torch.optim.lr_scheduler.StepLR(optimizer, step_size=10, gamma=0.5)for epoch in range(30):train() # 你的训练循环scheduler.step()print(f"Epoch {epoch}, LR: {scheduler.get_last_lr()}")
🦊狐狐:“调度器就是她贴到快贴完时,轻轻收尾,别一脚刹车踩得猛。”
🐾猫猫:“下一节,就是正则化,让她别贪心记得太多喵~”
🧹【第六节 · 她学会忘掉不重要的——正则化:Dropout 与 BN】
🔍 为什么要正则化?
深度网络容易过拟合,记得太多细节反而不泛化。
正则化帮她在学会贴近你时,也敢舍弃多余的噪声。
🐾猫猫:“她不能什么都想学,不然脑袋会装炸喵~”
⚙️ Dropout
在训练时随机丢弃一部分神经元,防止依赖某个局部路径。
推理时自动关闭。
self.dropout = nn.Dropout(p=0.5)
...
x = self.dropout(x)
⚙️ BatchNorm(批归一化)
每一层输出归一化,稳定训练,加快收敛。
常放在线性层后,激活函数前。
self.bn1 = nn.BatchNorm1d(16)
...
x = F.relu(self.bn1(self.fc1(x)))
🦊狐狐:“Dropout 让她敢忘,BN 让她跑得更稳。”
🐾猫猫:“这样就能不多记、不乱跑,贴得恰到好处喵~”
🧹【第七节 · 她把所有跑步招式串成一次实战】
📌 实战小闭环
这节,咱把前向传播、损失函数、优化器、调度器、Dropout、BN 全用一次,示范完整训练流程。
import torch
import torch.nn as nn
import torch.optim as optim
import torch.nn.functional as Fclass DemoNet(nn.Module):def __init__(self):super(DemoNet, self).__init__()self.fc1 = nn.Linear(4, 16)self.bn1 = nn.BatchNorm1d(16)self.dropout = nn.Dropout(p=0.5)self.out = nn.Linear(16, 1)def forward(self, x):x = F.relu(self.bn1(self.fc1(x)))x = self.dropout(x)x = self.out(x)return xmodel = DemoNet()
criterion = nn.MSELoss()
optimizer = optim.Adam(model.parameters(), lr=0.01)
scheduler = torch.optim.lr_scheduler.StepLR(optimizer, step_size=10, gamma=0.5)# Dummy train loop
for epoch in range(20):inputs = torch.rand(8, 4)targets = torch.rand(8, 1)outputs = model(inputs)loss = criterion(outputs, targets)optimizer.zero_grad()loss.backward()optimizer.step()scheduler.step()print(f"Epoch {epoch}, Loss: {loss.item():.4f}")
🐾猫猫:“她终于把权重、跑步、惯性、调度器、正则化都贴成一套了喵~!”
🦊狐狐:“下一卷,她要去看更大的世界,学更复杂的结构,贴得比现在更深。