梯度裁剪

引入

梯度裁剪(Gradient Clipping)是一种在深度学习中用于防止梯度爆炸的技术。原理是通过对梯度进行限制,将梯度的大小控制在一个合理的范围内,避免梯度出现过大的极端情况,从而保证模型训练的稳定性和有效性。

L2范数裁剪

设模型参数为\(\theta\),梯度为\(\nabla\theta\),阈值为\(c\),则

总梯度L2范数: \[ \mathrm{total\_norm} = ||\nabla\theta|| = \sqrt{\sum_{i}(\nabla\theta_i)^2} \] 其中,\(\nabla\theta_i\)是第\(i\)个参数的梯度。

\(\mathrm{total\_norm} > c\),则需要进行梯度裁剪,缩放梯度: \[ \begin{aligned} &\mathrm{scale} = \frac{c}{\mathrm{total\_norm}}\\ \\ &\nabla\theta_i\longleftarrow\nabla\theta_i\times\mathrm{scale} \end{aligned} \]\[ \nabla\theta' = \begin{cases} \displaystyle\frac{c}{||\nabla\theta||}\nabla\theta\quad & \mathrm{if}\ ||\nabla\theta||>c\\ \\ \nabla\theta&\mathrm{otherwise} \end{cases} \]

按范数裁剪是考虑了整个模型中的所有参数的梯度,将其当成一个整体,若这个整体的梯度太大,则对所有的梯度进行放缩,不论其中某个参数的梯度是大还是小。

按元素裁剪

对于梯度\(\nabla\theta\)中的每个元素,\(\nabla\theta_i\),如果\(\nabla\theta_i>c\),则将其设置为\(c\),如果\(\nabla\theta_i<-c\),则将其设置为\(-c\),数学表达式为: \[ \nabla\theta_i' = \begin{cases} c\quad\quad& \mathrm{if}\ \nabla\theta_i > c\\ \\ -c& \mathrm{if}\ \nabla\theta_i < -c\\ \\ \nabla\theta_i&\mathrm{otherwise} \end{cases} \] 按元素裁剪的方式是针对每个参数,若这个参数的梯度太大,则裁剪,否则维持这个参数的梯度。

PyTorch实现梯度裁剪

使用L2范数裁剪:

1
torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm)

将模型model的参数的L2范数限制在max_norm范围内,梯度裁剪需要用在模型backward()方法之后,也就是需要模型先反向传播生成梯度。

比如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import torch
import torch.nn as nn

model = nn.Sequential(
nn.Linear(10, 50),
nn.ReLU(),
nn.Linear(50, 2)
)

# 生成一些随机数据
inputs = torch.randn(32, 10)
targets = torch.randn(32, 2)

criterion = nn.MSELoss()
optimizer = optim.SGD(model.parameters(), lr=0.01)

# 前向传播
outputs = model(inputs)
loss = criterion(outputs, targets)

# 反向传播
optimizer.zero_grad()
loss.backward()

# 梯度裁剪
max_norm = 1.0 # 设定最大范数阈值
torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm) # 梯度裁剪

# 更新模型参数
optimizer.step()

使用元素裁剪:

1
torch.nn.utils.clip_grad_value_(model.parameters(), clip_value)

将模型model的参数的梯度的每个元素限制在[-clip_value, clip_value]范围内。

比如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import torch
import torch.nn as nn

model = nn.Sequential(
nn.Linear(10, 50),
nn.ReLU(),
nn.Linear(50, 2)
)

# 生成一些随机数据
inputs = torch.randn(32, 10)
targets = torch.randn(32, 2)

criterion = nn.MSELoss()
optimizer = optim.SGD(model.parameters(), lr=0.01)

# 前向传播
outputs = model(inputs)
loss = criterion(outputs, targets)

# 反向传播
optimizer.zero_grad()
loss.backward()

# 梯度裁剪
clip_value = 0.5
torch.nn.utils.clip_grad_value_(model.parameters(), clip_value) # 梯度裁剪

# 更新模型参数
optimizer.step()

梯度裁剪
https://blog.shinebook.net/2025/04/26/人工智能/理论基础/深度学习/梯度裁剪/
作者
X
发布于
2025年4月26日
许可协议