【问题标题】:What is the idea behind using nn.Identity for residual learning?使用 nn.Identity 进行残差学习的想法是什么?
【发布时间】:2021-01-21 13:32:45
【问题描述】:

所以,我已经阅读了大约一半的原始 ResNet 论文,并且正在尝试弄清楚如何为表格数据制作我的版本。

我已经阅读了一些关于它在 PyTorch 中如何工作的博客文章,并且我看到大量使用 nn.Identity()。现在,该论文还经常使用术语身份映射。但是,它只是指以元素方式将层堆栈的输入添加到同一堆栈的输出。如果输入和输出维度不同,那么论文会讨论用零填充输入或使用矩阵W_s 将输入投影到不同的维度。

这是我在一篇博文中找到的残差块的抽象:


class ResidualBlock(nn.Module):
    def __init__(self, in_channels, out_channels, activation='relu'):
        super().__init__()
        self.in_channels, self.out_channels, self.activation = in_channels, out_channels, activation
        self.blocks = nn.Identity()
        self.shortcut = nn.Identity()   
    
    def forward(self, x):
        residual = x
        if self.should_apply_shortcut: residual = self.shortcut(x)
        x = self.blocks(x)
        x += residual
        return x
    
    @property
    def should_apply_shortcut(self):
        return self.in_channels != self.out_channels
    
block1 = ResidualBlock(4, 4)

还有我自己对虚拟张量的应用:

x = tensor([1, 1, 2, 2])
block1 = ResidualBlock(4, 4)
block2 = ResidualBlock(4, 6)
x = block1(x)
print(x)
x = block2(x)
print(x)

>>> tensor([2, 2, 4, 4])
>>> tensor([4, 4, 8, 8])

所以最后,x = nn.Identity(x) 我不确定它的用途,除了模仿原始论文中的数学术语。我确信情况并非如此,而且它有一些我还没有看到的隐藏用途。会是什么?

编辑这是另一个实现残差学习的例子,这次是在 Keras 中。它只是按照我上面的建议做的,只是保留输入的副本以添加到输出中:

def residual_block(x: Tensor, downsample: bool, filters: int,                                        kernel_size: int = 3) -> Tensor:
    y = Conv2D(kernel_size=kernel_size,
               strides= (1 if not downsample else 2),
               filters=filters,
               padding="same")(x)
    y = relu_bn(y)
    y = Conv2D(kernel_size=kernel_size,
               strides=1,
               filters=filters,
               padding="same")(y)

    if downsample:
        x = Conv2D(kernel_size=1,
                   strides=2,
                   filters=filters,
                   padding="same")(x)
    out = Add()([x, y])
    out = relu_bn(out)
    return out

【问题讨论】:

标签: python neural-network pytorch deep-residual-networks


【解决方案1】:

使用 nn.Identity 进行残差学习的想法是什么?

没有(几乎,见帖子末尾),nn.Identity 所做的只是转发给它的输入(基本上是no-op)。

PyTorch repo issue 所示,您在评论中链接这个想法首先被拒绝,后来由于其他用途被合并到 PyTorch 中(参见理由in this PR)。这个理由没有连接到 ResNet 块本身,请参阅答案的结尾。

ResNet 实现

我能想到的最简单的带有投影的通用版本就是这样的:

class Residual(torch.nn.Module):
    def __init__(self, module: torch.nn.Module, projection: torch.nn.Module = None):
        super().__init__()
        self.module = module
        self.projection = projection

    def forward(self, inputs):
        output = self.module(inputs)
        if self.projection is not None:
            inputs = self.projection(inputs)
        return output + inputs

您可以将两个堆叠卷积之类的东西作为module 传递,并添加1x1 卷积(带有填充或跨步或其他东西)作为投影模块。

对于tabular 数据,您可以将其用作module(假设您的输入具有50 功能):

torch.nn.Sequential(
    torch.nn.Linear(50, 50),
    torch.nn.ReLU(),
    torch.nn.Linear(50, 50),
    torch.nn.ReLU(),
    torch.nn.Linear(50, 50),
)

基本上,您只需将input 添加到某个模块的输出即可。

行为的基本原理nn.Identity

构建神经网络(然后再阅读)可能更容易,例如批量规范(取自上述 PR):

batch_norm = nn.BatchNorm2d
if dont_use_batch_norm:
    batch_norm = Identity

现在您可以轻松地将它与nn.Sequential 一起使用:

nn.Sequential(
    ...
    batch_norm(N, momentum=0.05),
    ...
)

在打印网络时,它始终具有相同数量的子模块(BatchNormIdentity),这也使整个 IMO 变得更加顺畅。

here 提到的另一个用例可能是删除现有神经网络的一部分:

net = tv.models.alexnet(pretrained=True)
# Assume net has two parts
# features and classifier
net.classifier = Identity()

现在,您可以运行net(input),而不是运行net.features(input),这样其他人也可能更容易阅读。

【讨论】:

  • 完美,这是我在发现 GitHub 问题后得出的确切结论。谢谢你解释得这么好。
猜你喜欢
  • 2018-04-11
  • 1970-01-01
  • 2011-06-04
  • 1970-01-01
  • 2016-06-09
  • 2017-04-27
  • 1970-01-01
  • 1970-01-01
  • 2010-11-04
相关资源
最近更新 更多