一般来说,我们对卷积或者全连接层的weights
是没有限制的,但是如果我们自定义层的话,有可能会需要对权重或者梯度值进行限制。
那么在PyTorch里怎么操作呢?
限制Gradient
参考Stackoverflow。
对Gradient
的限制分为限制其范数与限制其值的范围两种,在PyTorch
文档里有详细的描述。
限制范数
torch.nn.utils.clip_gradnorm(parameters, max_norm, norm_type=2)
这个方法用于剪切可迭代参数的梯度范数。
在所有梯度上一起计算范数,就好像它们被连接成单个矢量一样。 梯度是in_place
就地修改的。
参数parameters
可以是单一的tensor
,也可以是可迭代的Tensors
。
max_norm
用来指定其最大范数。norm_type
表示p_norm
的类型,可以是inf
表示无穷范数。
Example
Github
...
# Backward and optimize
model.zero_grad()
loss.backward()
clip_grad_norm_(model.parameters(), 0.5)
optimizer.step()
...
在backward
之后,step
之前,对model
的参数的梯度做范数限制。
这里对参数梯度的限制还有一种更灵活的实现方式,即通过对参数注册钩子(hook)
函数,在backward
的时候就对其grad
用clamp
函数进行限制。例如:
for p in model.parameters():
p.register_hook(lambda grad: torch.clamp(grad, -clip_value, clip_value))
这里的运用就相对灵活,只要我们在定义Model
的时候,为参数注册hook
函数即可。不需要在backward
后再做限制。
同时,使用clamp
方法,也使得我们可以更加灵活限制grad
的范围,比如0.1~0.9
,而不是在正负范围之间。
上述的model.parameters()
都可以参照nn.Module
中parameters()
方法的实现,把想要限制的层提取出来即可。例如:
# Define model
class SimpleNet(nn.Module):
def __init__(self, **args):
super(SimpleNet, self).__init__()
self.features = nn.Sequential([...])
self.classifier = nn.Sequential([...])
...
...
model = SimpleNet(...)
loss.backward()
clip_grad_norm_(model.classifier.parameters(), 0.5)
optimizer.step()
...
限制范围
torch.nn.utils.clip_gradvalue(parameters, clip_value)
这里范围的限制中clip_value
限定了gradient
允许的最大值。gradient
值被剪切到[-clip_value, clip_value]
之间。
其他和上述限制范数的同理。
限制weights
当我们自定义层的时候,对自定义层的weights
有一些要求,比如限制其始终非负,或限制其在某个范围内等。
对weights
的限定与对weights.grad
的限制类似。
例如,这里我们限制weights
范围在[_min, _max]
之间:
...
# Backward and optimize
model.zero_grad()
loss.backward()
optimizer.step()
for p in model.parameters():
p.data.clamp_(_min, _max)
...
这里clamp
操作要放在step()
之后,因为这个时候更新完weights
了,限制之后再进入下一个batch
,达到限制的目的。
总结
总的来说运用torch.nn.utils.clip_grad_value_
,torch.nn.utils.clip_grad_norm_
以及钩子函数配合torch.clamp_
函数,在适当的位置更新,就可以达到限制范围的目的了。