残差网络(ResNet) 讨论区


#1

http://zh.diveintodeeplearning.org/chapter_convolutional-neural-networks/resnet.html


于置顶 #2

#3

Resnet 中每个block是两个conv2D, 然后把输入和输出相加; Resnet的优势是, 训练时,梯度可以通过告诉通道传递回来, 这个在程序哪里有体现…?


#4

mxnet计算梯度,并回传是以nn.block的子类为单位的吗;就是net.add(resBlock), 所以以resBlock为单位传递梯度,无论这个block里有多少个conv层吗


#5

仔细看了代码和论文中的ResidualBlock,感觉和这一节中的图片对应不上.
16
应该是x->conv1->conv2->out, return(out+x)
但是按照图片:
46
应该是x->conv1->out1->conv2->out2, return (out2+out1)

当然这个图片也可以理解为第2个方框中包括conv1, conv2, 第1个方框仅仅只是一个输入前的处理.
感觉有点误导.


#6

ResNet沿用了VGG的那种全用3×3卷积,但在卷积和池化层之间加入了批量归一层来加速训练。每次跨层连接跨过两层卷积。这里我们定义一个这样的残差块。注意到如果输入的通道数和输出不一样时(same_shape=False),我们使用一个额外的1×1卷积来做通道变化,同时使用strides=2来把长宽减半。

在这里的same_shape是不是应该包括2重含义,第一是输入输出的channel数目发生了变化,第二是输入输出的图像size发生了变化.在代码中默认是当channel数目发生变化时,图像size直接减半的.是否会出现仅仅channel数目发生变化,而图像size不变?


#7

在ResidualBlock和ResidualWithBottleneckBlock中,若需要将图像size减半,应该在哪个conv2d layer中减半?查了一些资料都是在第一个conv2d减半的,是否有什么依据?


#8

在3x3的layer 用stride=2 效果比较好 不过原版是在1x1 用stride=2
pytorch 和mxnte的resnet都是在3x3 所以acc要比原版高。
在1x1 subsample的话实际上是丢掉了3/4的信息不是很有效率


#9

每次subsample feature map大小变为原来的1/4 同时channel x 2


#10

主要是想演示下high way jump的想法,没有具体到resnet的两个conv做一个jump的形式。

是的


#11

是这样的


#12

谢沐神, 只有你完美的理解了我的问题:joy:


#13

多谢沐神


#14

请教一下 课程中给的resnet_18这个层数18是按卷积的数加全连接么,b1(1)+ b2(4)+b3(4)+b4(4)+b5(4)+b6(1)=1+4+4+4+4+1=18 是这样么?如果是这样数,kaggle-gluon-cifar10.md里给的resnet又不止18层?望能解惑 感谢!
image


#15

我也有同样的疑问,包括后面的densenet121,是怎么算出来的。小伙伴们,谁能给个解答


#16

http://lanbing510.info/2017/08/21/ResNet-Keras.html

好像是这么算的
conv1: 1 个 conv
con2_x~con5_x: 每层4个 conv, 共16个 conv
最后一个全连接算1层


#17

想请教一下为什么教程里给的ResNet代码在全连接层之前不加一个Flatten层呢?
为什么没有Flatten


#18

因为Dense层默认自带flatten技能


#19

带权运算的层算“一层”。比如conv 和fc,其他的比如pool层,不进行权运算,不算一层。


#20

按照论文实现的Resnet50 (bottleneck), 请各位大神指点,运行应该是没有问题的,但似乎test loss似乎不是太好,比教程中resnet18的test loss大

class Residual(nn.Block):
def init(self, in_channels, out_channels, same_shape=True, is_chg_strides=True, **kwargs):
super(Residual, self).init(**kwargs)
self.same_shape = same_shape

    strides = 1 if same_shape else 2
    if not is_chg_strides: strides=1
    self.conv1 = nn.Conv2D(channels=in_channels, kernel_size=1, padding=0, strides=1)
    self.bn1 = nn.BatchNorm()
    self.conv2 = nn.Conv2D(channels=in_channels, kernel_size=3, padding=1, strides=strides)
    self.bn2 = nn.BatchNorm()
    self.conv3 = nn.Conv2D(channels=out_channels, kernel_size=1, padding=0, strides=1)
    self.bn3 = nn.BatchNorm()
    self.in_out = in_channels==out_channels
    if not self.same_shape:
        self.conv4 = nn.Conv2D(channels=out_channels, kernel_size=1, padding=0, strides=strides)

def forward(self, X):
    out = self.bn1(self.conv1(X))
    out = self.bn2(self.conv2(out))
    out = self.bn3(self.conv3(out))
    if not self.same_shape:
        X = self.conv4(X)
    return nd.relu(out+X)

class Resnet50(nn.Block):
def init(self, n_class, **kwargs):
super(Resnet50, self).init(**kwargs)

    with self.name_scope():
        # block1
        b1 = nn.Sequential()
        b1.add(
            nn.Conv2D(channels=64, kernel_size=7, strides=2)
        )
        # block2-block10
        b2 = nn.Sequential()
        b2.add(
            nn.MaxPool2D(pool_size=3, strides=2),
            Residual(in_channels=64, out_channels=256, is_chg_strides=False, same_shape=False),
            Residual(in_channels=64, out_channels=256),
            Residual(in_channels=64, out_channels=256)
        )

        # block11-block22
        b3 = nn.Sequential()
        b3.add(Residual(in_channels=256, out_channels=512, same_shape=False))
        for _ in range(3):
            b3.add(Residual(in_channels=128, out_channels=512))

        # block23-block40
        b4 = nn.Sequential()
        b4.add(Residual(in_channels=256, out_channels=1024, same_shape=False))
        for _ in range(5):
            b4.add(Residual(in_channels=256, out_channels=1024))

        # block41-block49
        b5 = nn.Sequential()
        b5.add(Residual(in_channels=512, out_channels=2048, same_shape=False))
        for _ in range(2):
            b5.add(Residual(in_channels=512, out_channels=2048))

        # block50
        b6 = nn.Sequential()
        b6.add(
            nn.AvgPool2D(pool_size=3),
            nn.Dense(n_class)
        )

        self.net = nn.Sequential()
        self.net.add(b1, b2, b3, b4, b5, b6)
        # self.net.add(b1, b2, b3, b6)
def forward(self, x):
    out = x
    for i, b in enumerate(self.net):
        out = b(out)
    return out