一般来说,我们对卷积或者全连接层的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的时候就对其gradclamp函数进行限制。例如:

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.Moduleparameters()方法的实现,把想要限制的层提取出来即可。例如:

# 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_函数,在适当的位置更新,就可以达到限制范围的目的了。