梯度裁剪(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. 小程序web-view加载H5信息不全

    满足小程序的web-view标签跳转网页形式 配置小程序后台的web-view(业务域名) 可打开关联的公众号的文章 通常实现逻辑 页面加载的时候赋值于一个data对象的值,然后赋值到web-view ...

  2. PHP基于Thinkphp5的砍价活动相关设计

    近期我们公司项目里陆陆续续有很多为了招引新用户的活动推出,砍价的活动由我来负责,我们的项目是在微信浏览器里供用户浏览访问. 大概描述:进入砍价活动列表页选择有意向的商品,用户点击商品图片可以看到WEB ...

  3. 前端基础之CSS(1)

    1.css3的新特性有哪些 (1)CSS3选择器(基本.属性.伪类具体见下) (2)CSS3边框与圆角 圆角border-radius 属性:border-top-left-radius 左上角 bo ...

  4. 论文阅读-Clustering temporal disease networks to assist clinical decision support systems in visual analytics of comorbidity progression

    一.问题描述:      二.相关工作: 三.方法描述: 四.实验及结果

  5. 数据库查询中where和having的用法

    1.类型: "baiWhere"是一个约束声明,在查询数据库du的结果返回之前对数据库中zhi的查询条件进行约束dao,即在结果返回之前起作用,且where后面不能使用" ...

  6. C语言---魔方阵

    魔方阵的定义:在n*n的方阵中,每一行的和=每一列的和=对角线的和.(本文中涉及的n为大于3的奇数). 例如3*3的魔方阵为: 5*5的魔方阵为: 如何写魔方阵呢? 1.数字1位于第一行的正中间2.下 ...

  7. yum install mysql-community-server yum方式安装mysql(社区版实操)

    前言:rpm方式或者这种yum安装时比较简单的方式,但是不推荐,但是确实很着急的话,可以采用这种安装这种方式不利于后续对mysql的管理,如果是多实例或者是复杂的一些架构的话,还是推荐利用源码包编译方 ...

  8. 帝国CMS模板$GLOBALS[navclassid]用法详解

    帝国CMS模板程序扩展变量说明:通过这些变量可实现各种更复杂的显示格式. 一.列表/封面模板变量说明:(栏目页或专题页中使用) (一).当前栏目ID或专题ID:$GLOBALS[navclassid] ...

  9. 如何调试手机上的网页以及基于Cordova/Phonegap的Hybrid应用

    开发手机页面以及Hybird应用时,调试曾经是个老大难问题,不时需要用写log等方式曲线救国. 实际上,Chrome和Android(需要4.4+版本)已经提供了不亚于电脑版本的调试功能,只是看样子还 ...

  10. java class 文件格式解析

    前言 大约5年前,想研究javaassistant,cglib等字节码操作的相关类库,来对class进行增强,当要到要操作字节码的时候,发现无法继续下去了,只能放弃. 学习jvm字码,需要理解clas ...