Pytorch Pytork:剪辑激活的可学习阈值

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

使用可学习阈值剪辑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_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_max]),查看它们的梯度:计算所有这些梯度指向的平均方向
  • 对于低于阈值的所有正向激活,计算它们想要改变的方向的平均梯度
  • 这些平均梯度的总和决定了act_max的变化方向和幅度

  • 该代码有两个问题

  • 实现级别一是,您使用的是一个就地操作,通常不能很好地与autograd配合使用。而不是
  • 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”,则可以跟踪这些步骤。