Pytorch Pytork:剪辑激活的可学习阈值
使用可学习阈值剪辑ReLU激活的正确方法是什么?下面是我如何实现它的,但我不确定这是否正确:Pytorch Pytork:剪辑激活的可学习阈值,pytorch,Pytorch,使用可学习阈值剪辑ReLU激活的正确方法是什么?下面是我如何实现它的,但我不确定这是否正确: class Net(nn.Module): def __init__(self): super(Net, self).__init__() self.act_max = nn.Parameter(torch.Tensor([0]), requires_grad=True) self.conv1 = nn.Conv2d(3, 32, kernel
class Net(nn.Module):
def __init__(self):
super(Net, self).__init__()
self.act_max = nn.Parameter(torch.Tensor([0]), requires_grad=True)
self.conv1 = nn.Conv2d(3, 32, kernel_size=5)
self.conv2 = nn.Conv2d(32, 64, kernel_size=5)
self.pool = nn.MaxPool2d(2, 2)
self.relu = nn.ReLU()
self.linear = nn.Linear(64 * 5 * 5, 10)
def forward(self, input):
conv1 = self.conv1(input)
pool1 = self.pool(conv1)
relu1 = self.relu(pool1)
relu1[relu1 > self.act_max] = self.act_max
conv2 = self.conv2(relu1)
pool2 = self.pool(conv2)
relu2 = self.relu(pool2)
relu2 = relu2.view(relu2.size(0), -1)
linear = self.linear(relu2)
return linear
model = Net()
torch.nn.init.kaiming_normal_(model.parameters)
nn.init.constant(model.act_max, 1.0)
model = model.cuda()
optimizer = torch.optim.SGD(model.parameters(), lr=0.001)
for epoch in range(100):
for i in range(1000):
output = model(input)
loss = nn.CrossEntropyLoss()(output, label)
optimizer.zero_grad()
loss.backward()
optimizer.step()
model.act_max.data = model.act_max.data - 0.001 * model.act_max.grad.data
我不得不添加最后一行,因为没有它,由于某种原因,值不会更新
更新:我现在正在尝试一种基于激活梯度计算上限(act_max)的方法:
该代码有两个问题
relu1[relu1>self.act\u max]=self.act\u max
您应该使用一个不合适的操作,如
relu1=火炬。其中(relu1>self.act\u max,self.act\u max,relu1)
torch.where
)-model.act\u max.grad.data
只定义了一些元素,因为它们的值设置为model.act\u max
。但是这个梯度不知道为什么它们被设置为那个值。为了使事情更具体,让我们定义切断操作C(x,t)
,它定义x
是高于还是低于阈值t
C(x,t)=1如果x
并将剪切操作编写为产品
clip(x,t)=C(x,t)*x+(1-C(x,t))*t
然后,您可以看到阈值t
具有双重含义:它控制何时切断(在C
内)和控制切断上方的值(后面的t
)。因此,我们可以将操作概括为
clip(x,t1,t2)=C(x,t1)*x+(1-C(x,t1))*t2
您的操作的问题是,它仅可对t2
进行微分,而不能对t1
进行微分。您的解决方案将二者联系在一起,以便t1==t2
,但梯度下降仍然会像没有改变阈值一样,只改变阈值以上的值
因此,通常情况下,阈值操作可能没有学习到您希望它学习的值。这是在开发操作时要牢记的事情,但不能保证失败——事实上,如果考虑标准的代码> Relu < /代码>某个线性单元的偏置输出,我们会得到类似的图片。我们定义了切断操作H
H(x,t)=如果x>t,则为1,否则为0
和ReLU
as
ReLU(x+b,t)=(x+b)*H(x+b,t)=(x+b)*H(x,t-b)
我们可以再次概括到
ReLU(x,b,t)=(x+b)*H(x,t)
同样,我们只能学习b
,而t
隐含在b
之后。然而,这似乎是可行的:)嗨,很抱歉我没有早点确认你的答案。忙碌的一周。我被你的最后一句话弄糊涂了——什么似乎管用?我们不是在学习ReLU函数中的t-它是硬连线到零的。这是怎么回事?如果你只考虑代码> Relu,断线硬连接到零。如果您考虑“代码> Relu < /代码>跟随任何带有偏压的层(例如<代码>线性< /代码>),则上面有图片:“原始”输出<代码> x<代码>,偏颇的输出<代码> x+b>代码>阈值>代码> t<代码>。对于x+b
,code>t
硬连线为0,但对于原始输出,它只是b==-t
,但原则上允许任何t
。b
越高,x
的值越小,将通过ReLU
而不会被调零。“它似乎起作用了”,因为大多数CNN都有这个b==-t
约束,而且性能很好。哦,我明白你的意思了。但是,在您的示例中,每层有多个b值(对于卷积层,每个特征贴图都有不同的偏差),我想了解每层的单个阈值。我现在正在尝试测试一种方法,该方法根据所有激活想要改变的平均方向(请参阅问题中的更新)尝试推送阈值。实际上,我刚刚尝试了torch.where方法,它也不会更新值。正在计算渐变,但我仍然需要手动更新它们的值,就像使用我的方法一样。这对你有用吗?你做了更好的版本吗?如果你回答自己的问题并提到另一个答案,我想没有人会介意。不幸的是,在pytorch中还没有实现裁剪:/@MarineGalantin我使用了jatenaki建议的torch.where方法,我对所有层的阈值总和应用了L2惩罚,以平衡它们的增长。如果在中搜索“train\u act\u max”,则可以跟踪这些步骤。