一般来说,我们对卷积或者全连接层的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_函数,在适当的位置更新,就可以达到限制范围的目的了。