ResNet

ResNet Paper

Github code for resnet

简单画了一个resnet101模型:

resnet

这里介绍一下: 首先这个模型的输入随机截取是[224, 224, 3]的ImageNet图片,如果是cifar10数据的话,模型是不太一样的。

  • conv1:卷积核大小是[7, 7, 3, 64]输出是64维。卷积时采用步长stride=2,所以conv1输出是[112, 112, 64]
  • 每一次卷积操作肯定都伴随着BN和ReLu,一个标准的卷积操作可以是conv-BN-ReLu或者BN-ReLu-Conv这样方式。
  • conv1之后是一个max pooling把特征大小缩小一半
  • conv2开始就有Residual_block的概念了(一个弯箭头起点到终点部分),一个Residual_block有三个卷积单元组成:
    1. 输入[56, 56, 64]先经过一个[1, 1, 64, 64]的卷积操作,在经过一个[3, 3, 64, 64]的卷积操作,最后在接一个[1, 1, 64, 256]大小的卷积操作。这里卷积步长都是1,Padding=’SAME’保证Residual_block输出的特征图大小是不变的,即输入一个特征大小是[56, 56],输出大小任然是[56, 56]只是维度增加了而已。
    2. 上面三个卷积操作结束后还要将第一个[1,1,64,64]卷积操作的输入,即[56, 56, 64]大小加到后面第三个卷积操作的输出[56, 56, 256]上。但是这里如果维度是一样的话就直接把两个三维矩阵相加。但是如果维度是不一样的所以就要进行一些简单的维度变换操作:

      A 一个是在将[56, 56, 64]大小的输入经过一个卷积核大小为[1, 1, 64, 256]的卷积操作,这样相当于是一个线性变换过程,这样输出特征就也是[56, 56, 64],就可以和第三个卷积操作输出相加了。

      B 第二种是补零操作:因为Residual_block输入是[56, 56, 64],经过三个卷积输出是[56, 56, 256],差了192个维度,这些维度全补零,即对[56, 56, 64]增加[56, 56, 192]个0是的其维度是[56,56,256]。然后再相加即可。

  • 对于conv2卷积时特征图大小不变,只有输出了才做一个[2, 2],stride=2的max pooling。后面几个conv一样
  • conv3的4X 表示有4个一样的Residual_block模块,连接方式和conv2一样,后面conv4,conv5意思一样。
  • conv5输出是[7, 7, 2048],然后对每一个[7, 7]大小做一个average pool,变为一个[2048]的向量,然后做一个全连接变为[1000]的向量,因为ImageNet有1000类而已,如果只做10分类,这里fc输出是[10]大小就好。最后就是一样的softmax输出了。

我是在cifar10上跑的,ImageNet太慢了。cifar10的话Residual_block里面是两个卷积核大小是[3, 3]的卷积操作。也没有每一个conv之后的max pooling操作,是在每一个conv第一个卷积操作时将stride改成2来代替pooling,将特征图减小一半。

cifar10模型:

resnet_cifar10

当输入是cifar10是,卷积部分分为三块,总共卷积层有6n+2层。比如我是50层cnn,那么n=8. 一共分为三个大卷积块和一个全连接。所有的卷积核大小都是[3, 3]

  • 第一个卷积块有2n+1层第一层卷积核大小是[3, 3, 3, 16],输出是[32, 32, 16]的特征图,然后送给一个residual block.这里一个residual block由两个3\*3卷积层和一个前向特征加到后面(弯箭头部分)部分组成。第一个卷积为[32, 32, 16, 16*k]大小的卷积核,这里stride=2(特征图大小减半是通过第一次卷积时改变步长来降低的)。16*k是输出特征维度,k取正整数,具体参考Wide Residual Networks.然后第二个卷积大小是[3, 3, 16k, 16k]。之后是将前向的特征加过来。由于前面维度是[32, 32, 16],经过2个3*3卷积后是[32, 32, 16k]所以要和resnet101一样要经过一个[1, 1, 16, 16k]卷积操作或者直接补零。(写代码的时候发现补零要好一点)。加完之后就是重复n - 1个residual block,只不过这里卷积步长都是1了,不用变,而且弯相加部分因为维度也都一样了所以也不用操作就直接相加即可。这样第一个大卷积一共1+2+2*(n-1)=2n+1层。
  • 第二和第三个大卷积和上面几乎一样。只要注意维度分别变成[32k]和[64k],还有就是每一个大卷积模块的第一个卷积操作stride设为2即可。
  • 输出是一个[8, 8, 64k]的特征,我们对前两维做一个8*8的average pooling,得到[64k]向量。在进行最后一次全连接,得到cifar10的10分类结果。

resnet cifar10在实际些代码中发现需要一些技巧才能达到论文中准确率,我的Github上一个跑到0.93的模型:

resnet_cifar10_better

这里每一个residual block后面没有relu 并且不是每一个卷积都有bn。具体见github代码