当前位置: 首页 > news >正文

深度学习——常见问题与优化改进

文章目录

  • 一、过拟合
    • 1.1正则化
    • 1.2早停
  • 二、欠拟合
  • 三、梯度消失 & 梯度爆炸
    • 3.1激活函数
    • 3.2权重初始化
    • 3.3批归一化BN
    • 3.4残差连接(Residual Connection)【消失】
    • 3.5梯度裁剪(Gradient Clipping)【爆炸】
  • 四、学习率不合适
  • 五、数据不平衡
  • 六、泛化不足
  • 七、训练不稳定
  • 八、部署慢

一、过拟合

过拟合(Overfitting)是指模型在训练数据上表现得非常好,但在测试数据或者实际应用中表现很差。换句话说,模型“记住了训练数据”,却没有学到真正的规律。

原因:

  • 模型过于复杂,参数太多。
  • 训练数据量太少,样本不够代表整体。
  • 特征选择不合理,噪声太多。

解决方案:

  • 简化模型:减少层数或神经元数量。
  • 正则化(Regularization):在损失函数里加入额外约束,让模型参数不变得过大,从而降低过拟合风险。
    • L1/L2 正则
    • Dropout(随机丢弃神经元):在训练过程中随机“关闭”一部分神经元,防止模型过度依赖某些特征。
  • 增加训练数据:更多的数据能帮助模型学到真实规律。
  • 提前停止训练(Early Stopping):监控验证集损失,避免训练过久。

1.1正则化

在损失函数中加入额外约束,使模型主动 “放弃” 或 “弱化” 对预测贡献小的参数。从而让模型更“简单”,更能泛化到新数据。
总损失=原损失+λ∗正则项总损失=原损失 + λ * 正则项总损失=原损失+λ正则项其中,λ 控制惩罚强度,>=0。

方法原理效果特点
L1正则化正则化项=权重的绝对值之和部分参数 归零稀疏参数
L2正则化正则化项=权重的平方之和所有参数被压缩 至接近 0平滑参数

Dropout 随机让一部分神经元 “失效”(输出为 0),使模型无法依赖固定的神经元组合,被迫学习更广泛、更具代表性的特征。

在 PyTorch 中,L1 需手动加入损失,对于加了L1正则的神经网络,大部分深度学习框架自带的优化器训练获得不了稀疏解;如果稀疏解的模式是无规律的,这种稀疏意义不大。
L2 可通过 weight_decay,Dropout 内置在模型层中

import torch
import torch.nn as nn
# L1正则化
l1_norm = sum(p.abs().sum() for p in model.parameters())
loss = loss + lambda_l1 * l1_norm# L2正则化
optimizer = optim.Adam(model.parameters(), lr=0.001, weight_decay=0.001)# 定义一个带 Dropout 的神经网络
class Net(nn.Module):def __init__(self):super(Net, self).__init__()self.fc1 = nn.Linear(100, 200)  # 输入层→隐藏层1self.dropout1 = nn.Dropout(p=0.5)  # 隐藏层1的Dropout(50%丢弃率)self.fc2 = nn.Linear(200, 100)  # 隐藏层1→隐藏层2self.dropout2 = nn.Dropout(p=0.3)  # 隐藏层2的Dropout(30%丢弃率)self.fc3 = nn.Linear(100, 10)   # 隐藏层2→输出层(10分类)

1.2早停

在训练过程中,每个 epoch 计算验证集损失。如果验证集损失在 连续若干个 epoch 内没有改善,则停止训练。

  • patience(耐心值):允许验证集表现不提升的轮数。
  • delta:改善阈值,如果改善小于 delta,则认为没有改善。
  • restore_best_weights:训练结束后是否恢复最佳模型参数。
# 早停参数
patience = 10
best_val_loss = float('inf')
counter = 0
best_model_weights = None
# 训练循环
num_epochs = 100
for epoch in range(num_epochs):model.train()optimizer.zero_grad()outputs = model(X_train)loss = criterion(outputs, y_train)loss.backward()optimizer.step()# 验证集损失model.eval()with torch.no_grad():val_outputs = model(X_val)val_loss = criterion(val_outputs, y_val)print(f"Epoch {epoch+1} - Train Loss: {loss.item():.4f}, Val Loss: {val_loss.item():.4f}")# 早停逻辑if val_loss < best_val_loss:best_val_loss = val_losscounter = 0best_model_weights = model.state_dict()  # 保存最佳模型else:counter += 1if counter >= patience:print("Early stopping triggered!")break
# 恢复最佳模型
if best_model_weights is not None:model.load_state_dict(best_model_weights)

二、欠拟合

欠拟合(Underfitting)是指模型在训练数据上都表现不好,说明模型太简单,无法捕捉数据中的规律。

原因:

  • 模型复杂度不足(层数太少、参数太少)。
  • 特征太少或不够有代表性。
  • 训练不够,模型还没学会规律。

解决方案:

  • 增加模型复杂度:增加网络层数或神经元数量。
  • 增加特征:尝试更多有用的特征或者做特征工程。
  • 延长训练时间:训练更多轮(epochs)。
  • 减少正则化:过强的正则化也可能导致欠拟合。

三、梯度消失 & 梯度爆炸

梯度问题是深度学习训练时常见问题,发生在反向传播(Backpropagation)阶段。
梯度消失:梯度太小,导致权重更新几乎停滞,模型无法学习。
梯度爆炸:梯度太大,权重更新幅度过大,模型不收敛甚至发散。

原因:

  • 网络太深,连续的矩阵乘积导致梯度指数级衰减或增长。
  • 激活函数选择不当,如 sigmoid/tanh 在大输入下梯度趋近零。

解决方案:

  • 使用 ReLU 激活函数(不会导致梯度消失)。
  • 权重初始化:比如 Xavier 或 He 初始化,防止梯度过大或过小。
    • 若初始权重过大:前向传播时,信号经过经多层放大后可能溢出(如激活值趋于无穷大),反向传播时梯度也会急剧增大(梯度爆炸)。
    • 若初始权重过小:信号经多层传递后,信号会逐渐衰减至接近 0(梯度消失),导致浅层参数几乎无法更新。
  • 归一化方法:Batch Normalization、Layer Normalization
  • 梯度裁剪(Gradient Clipping):限制梯度最大值,避免爆炸。

3.1激活函数

激活函数用于神经网络中,每个神经元的输出经过激活函数才能引入非线性,使神经网络能够拟合复杂函数。

激活函数公式输出范围优点缺点PyTorch实现
Sigmoidσ(x)=11+e−x\sigma(x) = \frac{1}{1+e^{-x}}σ(x)=1+ex1(0,1)输出概率,常用于二分类梯度消失,输出非零均值nn.Sigmoid()
Tanhtanh⁡(x)=ex−e−xex+e−x\tanh(x) = \frac{e^x - e^{-x}}{e^x + e^{-x}}tanh(x)=ex+exexex(-1,1)输出零均值,梯度比 sigmoid 好梯度消失nn.Tanh()
ReLUReLU(x)=max⁡(0,x)\text{ReLU}(x) = \max(0, x)ReLU(x)=max(0,x)[0,∞)简单、高效,缓解梯度消失死神经元(负区间梯度为0)nn.ReLU()
Leaky ReLULeakyReLU(x)=max⁡(0.01x,x)\text{LeakyReLU}(x) = \max(0.01x, x)LeakyReLU(x)=max(0.01x,x)(-∞,∞)改进 ReLU,避免死神经元输出仍不零均值nn.LeakyReLU(0.01)
ELUELU(x)=xif x>0,α(ex−1)if x≤0\text{ELU}(x) = x \text{ if } x>0, \alpha(e^x-1) \text{ if } x≤0ELU(x)=x if x>0,α(ex1) if x0(-α,∞)平滑,负值接近零均值计算稍复杂nn.ELU(alpha=1.0)
SoftmaxSoftmax(xi)=exi∑jexj\text{Softmax}(x_i) = \frac{e^{x_i}}{\sum_j e^{x_j}}Softmax(xi)=jexjexi(0,1), ∑=1多分类输出概率仅用于输出层nn.Softmax(dim=1)
SwishSwish(x)=x∗σ(x)\text{Swish}(x) = x * \sigma(x)Swish(x)=xσ(x)(-∞,∞)平滑,表现优于 ReLU计算稍复杂x * torch.sigmoid(x)
GELUGELU(x)=x∗Φ(x)\text{GELU}(x) = x * \Phi(x)GELU(x)=xΦ(x)(Φ为标准正态CDF)(-∞,∞)Transformer 常用计算复杂nn.GELU()

参考:https://blog.csdn.net/qq_42691298/article/details/126590726

3.2权重初始化

# Xavier 初始化(均匀分布),适合 sigmoid/tanh
init.xavier_uniform_(layer.weight)  
# He 初始化(正态分布),适合 ReLU
init.kaiming_uniform_(layer.weight, nonlinearity='relu')  

3.3批归一化BN

A全连接层 —————激活函数——B全连接层
A全连接层——BN——激活函数——B全连接层
对每一层的输入进行标准化处理,使输入分布稳定在均值为 0、方差为 1 附近,从而在进入激活函数之前数据间的差距能变得更小。

class NetWithBN(nn.Module):def __init__(self):super().__init__()self.net = nn.Sequential(nn.Linear(784, 256),nn.BatchNorm1d(256),  # 添加BN层nn.ReLU(),nn.Linear(256, 10))

3.4残差连接(Residual Connection)【消失】

在某些层的输出上加上输入x,关键在于求导 “+1”

class ResidualBlock(nn.Module):def __init__(self, in_features):super().__init__()self.fc = nn.Linear(in_features, in_features)self.relu = nn.ReLU()def forward(self, x):out = self.fc(x)out = self.relu(out)out += x  # 残差连接return out

3.5梯度裁剪(Gradient Clipping)【爆炸】

当梯度的 “大小” 超过预设阈值时,按比例缩小梯度,使其控制在合理范围内,保证参数更新的稳定性。

optimizer = torch.optim.Adam(model.parameters(), lr=0.001)
for epoch in range(epochs):for inputs, targets in dataloader:optimizer.zero_grad()outputs = model(inputs)loss = criterion(outputs, targets)loss.backward()# 梯度裁剪(关键步骤)torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0)optimizer.step()

四、学习率不合适

学习率决定每次更新参数的步长。如果学习率不合适,会导致训练问题。

  • 学习率太大:训练不稳定,损失震荡或发散。
  • 学习率太小:收敛太慢,训练时间长甚至停滞。

当学习率过大时:降低并稳定学习率直接减小学习率:

  • 通常按比例缩小(如除以 10),例如从 0.1→0.01,0.001→0.0001。初次调整后观察损失是否趋于稳定(不再剧烈波动)。若损失仍波动,继续缩小(如再除以 5),直到损失呈现下降趋势。
  • 配合梯度裁剪:学习率过大可能伴随梯度爆炸,通过梯度裁剪(如设置 L2 范数阈值 1.0)限制梯度幅度,再适当降低学习率,可快速稳定训练。
  • 使用学习率预热(Warm-up):初始用极小的学习率(如目标学习率的 1/10),逐步增大至目标值(如前 5 个 epoch 线性增长),避免训练初期的剧烈震荡。
  • 适用场景:大型模型(如 Transformer)、大批次训练(如批大小 > 512)。

当学习率过小时:增大并加速收敛直接增大学习率:

  • 按比例放大(如乘以 5 或 10),例如从 0.0001→0.0005,0.01→0.05。观察损失是否下降加快,但需避免过大导致震荡。
  • 动态调整学习率(学习率调度):初期用较大学习率快速下降,后期减小学习率精细优化,
    • 分段衰减(Step Decay):每训练一定 epoch(如 30 轮),学习率乘以衰减系数(如 0.1)。
    • 指数衰减(Exponential Decay):学习率随 epoch 指数下降. lr=lr0⋅γepoch\text{lr} = \text{lr}_0 \cdot \gamma^{\text{epoch}}lr=lr0γepoch
    • 余弦退火(Cosine Annealing):学习率随 epoch 按余弦曲线周期性下降,适合需要精细收敛的场景。
    • 自适应调度(ReduceLROnPlateau):当验证集损失停止下降时,自动减小学习率(如乘以 0.5)。
  • 结合批大小调整:若增大批大小(如从 32→128),可按比例增大学习率(如从 0.01→0.04),保持参数更新幅度一致(线性缩放规则)。

五、数据不平衡

数据不平衡:训练数据中某些类别样本太少,导致模型偏向多的类别,分类效果差。

解决方案:

  • 重采样(Resampling):增加少数类别样本(过采样)或减少多数类别样本(欠采样)。
  • 类别权重(Class Weight):在损失函数中给少数类更高权重。
  • 使用 Focal Loss:专门关注难分类的样本。

六、泛化不足

模型在训练数据上表现很好,但在新数据上表现差。

原因:

  • 过拟合。
  • 数据分布变化(训练数据和实际应用数据差异大)。

解决方案:

  • 增加训练数据或做数据增强(Data Augmentation)。
  • 使用正则化。
  • 尝试迁移学习(Transfer Learning),用已有模型在新数据上微调。

七、训练不稳定

训练过程中损失波动大,模型难以收敛。

原因:

  • 学习率过大。
  • 网络结构不合理或初始化不当。
  • 数据噪声大或批量归一化问题。

解决方案:

  • 调小学习率,使用自适应优化器。
  • 使用 Batch Normalization,稳定各层输入分布。
  • 改善数据质量,清理异常值。

八、部署慢

模型训练完成后,在实际应用中运行缓慢,影响效率。

原因:

  • 模型太大,参数多。
  • 硬件性能不足。
  • 推理优化不足。

解决方案:

  • 模型压缩:剪枝(Pruning)、量化(Quantization)、知识蒸馏(Knowledge Distillation)。
  • 使用 GPU / TPU 或高性能硬件。
  • 使用高效推理框架,如 TensorRT、ONNX Runtime。
http://www.dtcms.com/a/334229.html

相关文章:

  • java中消息推送功能
  • Xiaothink-T6-0.15B混合架构模型深度解析
  • 3 种方式玩转网络继电器!W55MH32 实现网页 + 阿里云 + 本地控制互通
  • 架构调整决策
  • 超越Transformer:大模型架构创新的深度探索
  • 【计算机网络架构】混合型架构简介
  • Blackwell 和 Hopper 架构的 GPGPU 新功能全面综述
  • 【LeetCode每日一题】
  • Mac (三)如何设置环境变量
  • 从希格斯玻色子到 QPU:C++ 的跨维度征服
  • 代码随想录Day52:图论(孤岛的总面积、沉没孤岛、水流问题、建造最大岛屿)
  • 在ubuntu系统上离线安装jenkins的做法
  • 立体匹配中的稠密匹配和稀疏匹配
  • 8.16 pq
  • [系统架构设计师]系统质量属性与架构评估(八)
  • 解锁JavaScript性能优化:从理论到实战
  • 【完整源码+数据集+部署教程】太阳能面板污垢检测系统源码和数据集:改进yolo11-RVB-EMA
  • 地级市+省级气候政策不确定性指数(2000-2023年)-实证数据
  • ollama 自定义模型
  • imx6ull-驱动开发篇27——Linux阻塞和非阻塞 IO(上)
  • 【JS】认识并实现一个chrome扩展程序
  • 如何在 MacOS 上安装 SQL Server
  • MySQL完整重置密码流程(针对 macOS)
  • 硬核北京 | 2025世界机器人大会“破圈”,工业智能、康养科技…… 亦庄上演“机器人总动员”
  • Flink Sql 按分钟或日期统计数据量
  • 中本聪思想与Web3的困境:从理论到现实的跨越
  • 存算分离与云原生:数据平台的新基石
  • 基于Kubernetes亲和性与反亲和性的Pod调度优化实践指南
  • Linux上配置环境变量
  • 从频繁告警到平稳发布:服务冷启动 CPU 风暴优化实践01