深度学习经验

缝合模块

缝合模块:可以自已写一个test.py来检验这个模块(即插即用)有效性,利用torch.randn(N,C,H,W),其中输入即为我们想要插入我们想缝合模块前的模块输入利用print打印出其shape,然后把该模块(即插即用)实例化进行测试,再利用print打印经过该模块后的输出,查看输入输出shape是否一致,可行后即可开始对比试验其模块有效性

输出图像全黑/全白(梯度爆炸

修改策略(顺序):模型架构->模型超参数(调整学习率、损失函数、优化器)->网络初始化->使用BN->梯度裁剪->梯度累积

数学原理

模型

调整网络的深度和宽度,使其避免梯度爆炸是最管用的一招!

方法一

对于带残差的网络,可以调整res_scale使网络避免梯度消失/梯度爆炸,从而使网络预测出正确的图像
  在残差网络中,如果残差部分的权重过大或者网络在其他地方存在问题(例如梯度爆炸或消失),那么输出可能会偏离预期,导致像素值超出正常范围,从而在可视化时显示为纯黑图像
  当 res_scale 较小时,残差部分(res)被大幅缩减,因此在相加时,原始特征(x)占主导地位。这有助于网络更加稳定地学习,因为即使残差部分的学习出现问题,原始特征仍然可以被保留
  当 res_scale 设置为 1 时,残差部分不再缩减。这意味着残差部分和原始输入以完全相同的比重被加和。如果残差部分的学习不稳定或者梯度出现问题(如过大或过小),这可能会导致整个网络输出受到影响,从而导致输出图像变黑

方法二

尝试加relu或conv+relu或relu+conv+relu

加入卷积层(Conv)和非线性激活函数(如ReLU)可以帮助缓解梯度消失或梯度爆炸的问题,但并不能完全避免。以下是这些组件如何帮助的原因:

  1. 卷积层(Conv)

    • 参数共享和稀疏连接:卷积层在处理数据时有参数共享的特性,并且其连接是稀疏的。这意味着对于输入数据的小局部区域只计算一组参数,这减少了模型的复杂性和总的参数数量,从而降低了梯度爆炸的风险。
    • 局部感受野:卷积层的局部感受野能够捕捉输入数据的局部特征,使得梯度在反向传播时更加稳定。
  2. ReLU激活函数

    • 非饱和性:ReLU(Rectified Linear Unit)函数的公式是f(x) = max(0, x)。当输入x为正时,函数的导数为1,这意味着在正区间内,梯度不会随着网络深度的增加而衰减,这有助于减轻梯度消失的问题。
    • 线性特性:对于正输入,ReLU 是线性的,这可以帮助减少训练过程中的梯度问题。

然而,ReLU 函数在输入为负时梯度为零,这可能导致神经元“死亡”,即它们在训练过程中不再更新,这也是一个需要注意的问题。为了解决这个问题,后来又提出了Leaky ReLU、Parametric ReLU等变体。

此外,虽然使用ReLU可以在一定程度上减缓梯度消失的问题,但是它可能导致梯度爆炸,尤其是在网络权重初始化不当的情况下。因此,为了进一步缓解这些问题,通常会结合使用以下策略:

  • 权重初始化:如He初始化或Xavier初始化,可以帮助权重在初始时有适当的规模,避免梯度在开始时过大或过小。
  • 批量归一化(Batch Normalization):通过归一化层输入,可以减少内部协变量偏移,帮助避免梯度消失和爆炸。
  • 梯度剪切(Gradient Clipping):在优化过程中限制梯度的大小,防止梯度爆炸。

综上,卷积层和ReLU可以帮助构建更稳定的网络,但是要避免梯度相关问题,通常需要综合考虑网络设计、权重初始化和训练技巧。

模型超参数

学习率

使用较小的学习率或学习率调度策略

损失函数

L1(MAE)/L2(MSE)

L1损失(也称为绝对误差损失)和均方误差(MSE)损失是机器学习中常用的两种损失函数,它们各有优缺点:

L1损失(平均绝对误差损失)

L1损失函数定义为预测值和真实值之间差的绝对值之和:L1(y,y')=1/n∑|y - y'|

torch.nn.L1Loss(size_average=None, reduce=None, reduction='mean')
优点

1. 鲁棒性(Robustness):L1损失对于异常值(outliers)更加鲁棒。由于它不对误差进行平方,因此异常值对损失的影响较小。
2. 稀疏性(Sparsity):在某些情况下(如Lasso回归中),L1损失可以产生稀疏解,这有助于特征选择。

缺点

1. 计算不稳定:L1损失在误差为零时的导数不连续,这可能导致优化过程中的不稳定和难以找到准确的最小值。
2. 收敛速度:相比于MSE损失,L1损失的收敛速度可能更慢,特别是当误差接近于0时。

L2损失(均方误差损失)

MSE损失函数定义为预测值和真实值之间差的平方和的平均值:MSE(y,y')=1/n∑(y - y')²

torch.nn.MSELoss(size_average=None, reduce=None, reduction='mean')
优点

1. 数学性质:MSE损失函数在数学上易于处理,由于其连续的二阶导数,它使得优化算法(如梯度下降)更加高效。
2. 误差敏感性:MSE对于较大的误差更加敏感,这使得模型更加倾向于减少大的误差。

缺点

1. 对异常值敏感:MSE对异常值非常敏感。由于平方项的存在,大的误差值会对损失函数产生巨大的影响,可能导致模型过于关注这些异常值。
2. 收敛特性:虽然MSE通常能较快收敛,但在某些情况下,过于敏感的误差反应可能导致模型训练过程中的不稳定性。

L1、L2对比

1)L1损失函数的鲁棒性比L2强:

  • L2函数将真实标签y与网络输出y'之间的误差进行了放大(二者之间误差大于1时)或缩小(二者之间误差小于1时),使用L2函数为训练目标的模型会对这种类型的样本更加敏感,在优化过程中模型不断调整适应这些样本,而这些样本可能本身是一个异常值,模型对这些异常值的优化适应则会导致其训练方向偏离目标。

2)L2拥有比L1更光滑的曲线,更利于网络收敛:

  • L1曲线连续,但是在y−y'=0处不可导,且L1大部分情况下梯度是保持不变的,这不利于函数的收敛和模型的学习,所以在使用时,通常配以学习率衰减策略,逐渐降低学习率。但是,无论对于什么样的输入值,都有着稳定的梯度,不会导致梯度爆炸问题,具有较为稳健性的解。
  • L2的函数曲线光滑、连续,处处可导,便于使用梯度下降算法,且随着误差的减小,梯度也在减小,这有利于收敛,即使使用固定的学习速率,也能较快的收敛到最小值。

总结

选择L1还是MSE损失函数取决于具体的应用场景和数据特性。如果数据中包含许多异常值,或者你希望模型具有稀疏特性,L1损失可能更合适。相反,如果你需要快速且稳定的收敛,并且数据中异常值不是主要问题,那么MSE损失可能是更好的选择。在实践中,有时也会结合这两种损失函数的优点,使用它们的组合或变体,如Huber损失,它在误差较小的时候像MSE,在误差较大时像L1损失。

优化器

Adam

网络初始化

1. 零初始化(Zero Initialization)

 - 解释:将所有权重设置为0。这种方法通常不推荐,因为它会导致网络在训练过程中无法打破对称性,从而使得学习无效。
 

     import torch.nn as nn

     def init_weights_zero(m):
         if type(m) == nn.Linear:
             nn.init.zeros_(m.weight)
             nn.init.zeros_(m.bias)

2. 随机初始化(Random Initialization)

 - 解释:将权重初始化为随机数。这有助于打破对称性,促进有效的梯度下降。

     def init_weights_random(m):
         if type(m) == nn.Linear:
             nn.init.uniform_(m.weight, -1, 1)
             nn.init.uniform_(m.bias, -1, 1)

3. Xavier/Glorot 初始化

 - 解释:为了保持输入和输出的方差一致,这种方法在初始化时考虑了前后层的节点数。特别适用于tanh激活函数。

     def init_weights_xavier(m):
         if type(m) == nn.Linear:
             nn.init.xavier_uniform_(m.weight)
             nn.init.zeros_(m.bias)

4. He 初始化

 - 解释:这种方法是为了适应ReLU激活函数的特性而设计的,它考虑了ReLU在正区间的非线性特性。

import torch.nn as nn
  
def init_weights_kaiming_uniform(m):
    if type(m) == nn.Linear:
        nn.init.kaiming_uniform_(m.weight, mode='fan_in', nonlinearity='relu')
        nn.init.zeros_(m.bias)



def init_weights_kaiming_normal(m):
    if type(m) == nn.Linear:
        nn.init.kaiming_normal_(m.weight, mode='fan_in', nonlinearity='relu')
        nn.init.zeros_(m.bias)

5.正交初始化(Orthogonal Initialization)

 - 解释:通过使用正交矩阵作为权重,这种方法旨在保持梯度的稳定性,特别是在深层网络中。

     def init_weights_orthogonal(m):
         if type(m) == nn.Linear:
             nn.init.orthogonal_(m.weight)
             nn.init.zeros_(m.bias)

6. 稀疏初始化(Sparse Initialization)

- 解释:在这种方法中,权重矩阵的大部分元素被初始化为0,而少数元素被赋予随机值,以提高网络的稀疏性。

     def init_weights_sparse(m, sparsity=0.1):
         if type(m) == nn.Linear:
             nn.init.sparse_(m.weight, sparsity=sparsity)
             nn.init.zeros_(m.bias)

在PyTorch中,你可以通过将这些函数应用于模型的每一层来初始化网络,例如:

model = MyNeuralNetwork()
model.apply(init_weights_he)  # 使用He初始化

这些初始化方法是根据网络的具体需求和激活函数的特性来选择的。例如,对于使用ReLU激活函数的网络,He初始化通常是一个很好的选择。而对于使用tanh或sigmoid激活函数的网络,Xavier初始化可能更合适。

批归一化(Batch Normalization)

可以让优化空间平滑、稳定激活函数的输出,从而帮助减少梯度的波动

梯度裁剪

在优化器步骤之前对梯度进行裁剪,限制梯度更新的最大值

如果模型一开始就梯度爆炸了,那就别用梯度裁剪;如果模型运行了几个epoch后梯度爆炸,那么在对应最好的epoch后进行梯度裁剪。(自我感觉梯度裁剪比较鸡肋,能别用就别用,用了还降低模型效果)

梯度累计(gradient accmulation)

对于特别大的模型,可以在多个小批量上累积梯度,然后进行一次更新,这样可以模拟更大批量的稳定性,而不增加内存负担

特征进行非线性映射原因

1. 捕捉复杂模式:在很多现实世界的问题中,数据间的关系往往不是线性的。非线性映射允许模型能更好地捕捉这些复杂的、非直线性的关系,例如在图像识别、语音识别或自然语言处理等领域。

2. 增加模型的表达能力:通过非线性映射,模型可以学习到更加复杂的函数。这意味着它可以逼近更多类型的数据分布,从而在各种任务上表现得更好。

3. 解决线性不可分问题:在某些情况下,数据集在原始空间中是线性不可分的。通过将特征映射到高维空间,可以使得原本线性不可分的数据变得可分,如支持向量机(SVM)中的核技巧。

4. 深度学习中的作用:在深度学习中,非线性映射是至关重要的。它允许神经网络学习到从输入到输出的复杂映射。如果没有非线性激活函数,无论神经网络有多少层,它最终都只能表示线性函数。

总的来说,非线性映射是机器学习和深度学习中的一个关键概念,它使得模型能够处理更加复杂和多变的数据集。

常见的几种学习方式

监督学习:存在x->y对
半监督学习:存在一部分x->y对,和另一部分x,适用于标注数据稀疏或标注成本昂贵情况


半监督学习一般流程:labeled data、unlabeled data
1.先进行supervised learning 进行labeled data训练后给模型进行初始化
2.再送入大量unlabeled data和少量labeled data进行混合训练(semi-supervised learning strategy)
3.最后,可以再采取少量labeled data 进行精炼训练,以达到进一步提高网络性能


自监督学习:存在x->p(pseudo label)对,p一般由pretext task中数据的某些属性决定,适用于标注数据稀疏或标注成本昂贵情况。一般来讲,self-supervised learning先进行pretext task pre-training,后进行迁移学习到supervised learning进行fine-tuning
无监督学习:不存在pair对
强化学习:奖惩机制
迁移学习:对于数据集少的任务或与原任务相似的任务,用一些在大型数据集上预训练过的模型(如ImageNet)经过模型冻结(层冻结)技术来解决这些任务。迁移学习可以显著减少新任务的学习时间和所需的数据量

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值