梯度裁剪(Gradient Clipping)

在训练比较深或者循环神经网络模型的过程中,我们有可能发生梯度爆炸的情况,这样会导致我们模型训练无法收敛。 我们可以采取一个简单的策略来避免梯度的爆炸,那就是梯度截断 Clip, 将梯度约束在某一个区间之内,在训练的过程中,在优化器更新之前进行梯度截断操作!!!!! 注意这个方法只在训练的时候使用,在测试的时候验证和测试的时候不用。

整个流程简单总结如下:

  1. 加载训练数据和标签
  2. 模型输入输出
  3. 计算 loss 函数值
  4. loss 反向传播
  5. 梯度截断
  6. 优化器更新梯度参数
import torch.nn as nn
outputs = model(data)
loss= loss_fn(outputs, target)
loss.backward()
nn.utils.clip_grad_norm_(model.parameters(), max_norm=20, norm_type=2)
optimizer.step()
optimizer.zero_grad()

nn.utils.clip_grad_norm_ 输入是(NN 参数,最大梯度范数,范数类型 = 2) 一般默认为 L2 范数。

梯度累积

常规网络如下:

# 正常网络
optimizer.zero_grad()
for idx, (x, y) in enumerate(train_loader):
pred = model(x)
loss = criterion(pred, y) loss.backward()
optimizer.step()
optimizer.zero_grad() if (idx+1) % eval_steps == 0:
eval()

需要梯度累计时,每个 mini-batch 仍然正常前向传播以及反向传播,但是反向传播之后并不进行梯度清零,因为 PyTorch 中的 loss.backward() 执行的是梯度累加的操作,所以当我们调用 4 次 loss.backward() 后,这 4 个 mini-batch 的梯度都会累加起来。但是,我们需要的是一个平均的梯度,或者说平均的损失,所以我们应该将每次计算得到的 loss除以 accum_steps

# 梯度累积

accum_steps = 4
optimizer.zero_grad()
for idx, (x, y) in enumerate(train_loader):
pred = model(x)
loss = criterion(pred, y) # normlize loss to account for batch accumulation
loss = loss / accum_steps loss.backward() if (idx+1) % accum_steps == 0 or (idx+1) == len(train_loader):
optimizer.step()
optimizer.zero_grad()
if (idx+1) % eval_steps:
eval()

总的来说,梯度累加就是计算完每个 mini-batch 的梯度后不清零,而是做梯度的累加,当累加到一定的次数之后再更新网络参数,然后将梯度清零。通过这种延迟更新的手段,可以实现与采用大 batch_size 相近的效果

冻结某些层

在加载预训练模型的时候,我们有时想冻结前面几层,使其参数在训练过程中不发生变化。

我们需要先知道每一层的名字,通过如下代码打印:

net = Network()  # 获取自定义网络结构
for name, value in net.named_parameters():
print('name: {0},\t grad: {1}'.format(name, value.requires_grad))

假设前几层信息如下:

name: cnn.VGG_16.convolution1_1.weight,   grad: True
name: cnn.VGG_16.convolution1_1.bias, grad: True
name: cnn.VGG_16.convolution1_2.weight, grad: True
name: cnn.VGG_16.convolution1_2.bias, grad: True
name: cnn.VGG_16.convolution2_1.weight, grad: True
name: cnn.VGG_16.convolution2_1.bias, grad: True
name: cnn.VGG_16.convolution2_2.weight, grad: True
name: cnn.VGG_16.convolution2_2.bias, grad: True

后面的 True 表示该层的参数可训练,然后我们定义一个要冻结的层的列表:

no_grad = [
'cnn.VGG_16.convolution1_1.weight',
'cnn.VGG_16.convolution1_1.bias',
'cnn.VGG_16.convolution1_2.weight',
'cnn.VGG_16.convolution1_2.bias'
]

冻结方法如下:

# net = Net.CTPN()  # 获取网络结构
net = Network()
for name, value in net.named_parameters():
if name in no_grad:
value.requires_grad = False
else:
value.requires_grad = True

冻结后我们再打印每层的信息:

name: cnn.VGG_16.convolution1_1.weight,   grad: False
name: cnn.VGG_16.convolution1_1.bias, grad: False
name: cnn.VGG_16.convolution1_2.weight, grad: False
name: cnn.VGG_16.convolution1_2.bias, grad: False
name: cnn.VGG_16.convolution2_1.weight, grad: True
name: cnn.VGG_16.convolution2_1.bias, grad: True
name: cnn.VGG_16.convolution2_2.weight, grad: True
name: cnn.VGG_16.convolution2_2.bias, grad: True

可以看到前两层的 weight 和 bias 的 requires_grad 都为 False,表示它们不可训练。

最后在定义优化器时,只对 requires_grad 为 True 的层的参数进行更新。(这里用filter筛选只传入了requires_grad为True的参数,但如果直接传入全部参数应该也可以达到只训练未冻结层参数的效果)

optimizer = optim.Adam(filter(lambda p: p.requires_grad, net.parameters()), lr=0.01)

其他注意事项

  1. with torch.no_grad()或者@torch.no_grad()中的数据不需要计算梯度,也不会进行反向传播。不需要计算梯度的代码块(如验证测试)用 with torch.no_grad() 包含起来,节省显存
model.eval()
with torch.no_grad():
pass
@torch.no_grad()
def eval():
...
  1. model.eval() 和 torch.no_grad() 的区别在于,model.eval() 是将网络切换为测试状态,例如 BN 和dropout在训练和测试阶段使用不同的计算方法。torch.no_grad() 是关闭 PyTorch 张量的自动求导机制,以减少存储使用和加速计算,得到的结果无法进行 loss.backward()。

  2. model.zero_grad()会把整个模型的参数的梯度都归零, 而optimizer.zero_grad()只会把传入其中的参数的梯度归零.

  3. loss.backward() 前用 optimizer.zero_grad() 清除累积梯度。如果在循环里需要把optimizer.zero_grad()写在后面,那应该在循环外需要先调用一次optimizer.zero_grad()

  4. 查看网络中的梯度

params = list(model.named_parameters())
(name, param) = params[28]
print(name)
print(param.grad)
print('-------------------------------------------------')

ptorch常用代码梯度篇(梯度裁剪、梯度累积、冻结预训练层等)的更多相关文章

  1. Logistic 回归(sigmoid函数,手机的评价,梯度上升,批处理梯度,随机梯度,从疝气病症预测病马的死亡率

    (手机的颜色,大小,用户体验来加权统计总体的值)极大似然估计MLE 1.Logistic回归 Logistic regression (逻辑回归),是一种分类方法,用于二分类问题(即输出只有两种).如 ...

  2. 对数几率回归法(梯度下降法,随机梯度下降与牛顿法)与线性判别法(LDA)

    本文主要使用了对数几率回归法与线性判别法(LDA)对数据集(西瓜3.0)进行分类.其中在对数几率回归法中,求解最优权重W时,分别使用梯度下降法,随机梯度下降与牛顿法. 代码如下: #!/usr/bin ...

  3. 机器学习算法(优化)之一:梯度下降算法、随机梯度下降(应用于线性回归、Logistic回归等等)

    本文介绍了机器学习中基本的优化算法—梯度下降算法和随机梯度下降算法,以及实际应用到线性回归.Logistic回归.矩阵分解推荐算法等ML中. 梯度下降算法基本公式 常见的符号说明和损失函数 X :所有 ...

  4. NN优化方法对照:梯度下降、随机梯度下降和批量梯度下降

    1.前言 这几种方法呢都是在求最优解中常常出现的方法,主要是应用迭代的思想来逼近.在梯度下降算法中.都是环绕下面这个式子展开: 当中在上面的式子中hθ(x)代表.输入为x的时候的其当时θ參数下的输出值 ...

  5. 梯度下降法VS随机梯度下降法 (Python的实现)

    # -*- coding: cp936 -*- import numpy as np from scipy import stats import matplotlib.pyplot as plt # ...

  6. fluent当中的梯度宏和VOF梯度的获取【转载】

    1 FLUENT变量梯度宏 C_R_G C_P_G C_U_G C_V_G C_W_G C_T_G C_H_G C_YI_G C_R_RG C_P_RG C_U_RG C_V_RG C_W_RG C_ ...

  7. 机器学习(ML)十五之梯度下降和随机梯度下降

    梯度下降和随机梯度下降 梯度下降在深度学习中很少被直接使用,但理解梯度的意义以及沿着梯度反方向更新自变量可能降低目标函数值的原因是学习后续优化算法的基础.随后,将引出随机梯度下降(stochastic ...

  8. PyTorch常用代码段整理合集

    PyTorch常用代码段整理合集 转自:知乎 作者:张皓 众所周知,程序猿在写代码时通常会在网上搜索大量资料,其中大部分是代码段.然而,这项工作常常令人心累身疲,耗费大量时间.所以,今天小编转载了知乎 ...

  9. C#常用函数--通用篇

    C#常用函数→通用篇转载地址→http://www.cnblogs.com/superfang/archive/2008/07/02/1233706.html以前我都是"原文地址" ...

随机推荐

  1. 【wepy入门教程】48小时开发看美女微信小程序,万花阁

    说明:本文只做小程序的开发过程记录:小程序仅供学习参考,严禁用于商业及非法用途 准备 不管是做网站还是做小程序,只要是To C,就少不了做内容,因此第一步依然是数据准备,从网上找到两个网站: http ...

  2. js获取异步方法里面的数据

    这里介绍  五种方法(说白了本质 就三种) 1.callback回调函数  function getData(callback){ setTimeout(()=>{ let name = '哈哈 ...

  3. Leetcode541/151之String与char数组与StringBuffer

    String与char数组与StringBuffer 通常情况下遇到删除字符或者反转字符串时需要将String转为char数组或者StringBuffer String与char数组 char [] ...

  4. Linux安装Redis步骤和make遇到的坑

    Linux安装Redis服务步骤 1.获取redis资源 ​​​​​​​cd /usr/local wget https://mirrors.huaweicloud.com/redis/redis-6 ...

  5. Python入门-第一行代码到多行代码

    不管学啥语言,开始的第一行代码都是: print("hello word") 回车之后,就代表你正式进入代码的世界! 如果报错,恭喜你获得第一个书写bug,请检查单词拼写,双引号, ...

  6. Spring Boot 使用 Redis 共享 Session 代码示例

    参考资料 博客:spring boot + redis 实现session共享 1. 新建 Maven 工程 我新建 spring-boot-session-redis maven 工程 2. 引入 ...

  7. 安卓记账本开发学习day3

    今天发现了新的错误 首先是AS警告杀毒系统对编译运行有影响,我目前只开了火绒,所以只需要在火绒里将SDK文件夹设置为信任即可 第二个错是在编译测试运行的时候,虚拟机能够正常打开,也launch suc ...

  8. 2021.11.14 CF1583E Moment of Bloom(LCA+图上构造)

    2021.11.14 CF1583E Moment of Bloom(LCA+图上构造) https://www.luogu.com.cn/problem/CF1583E 题意: She does h ...

  9. 【远程文件浏览器】Unity+Lua开发调试利器

    Remote File Explorer是一个跨平台的远程文件浏览器,用户通过Unity Editor就能操作运行在手机上的游戏或是应用的的目录文件.比如当项目打包运行到设备上时,可通过Remote ...

  10. liunx对磁盘进行配额限制

    说明,我们给你一个分区进行磁盘配额进行限制 1.首先我们要进行那个分区进行限制,通过修改配置文件加上uquota ,然后再重启一下系统 二.quota 命令用于管理设备的磁盘容量配额,语法格式为 xf ...