Resnet

随着网络深度的的增加,会出现梯度爆炸,梯度消失等情况,输入层,中间层数据的标准化很大程度上可以解决该问题。但是随着更深的网络收敛时,准确率饱和,然后迅速下降,这并不是应为过拟合引起的,是因为更多的层会导致更高的训练误差。如下所示,层数增加,误差反而增大。

实现原理

我们把右边的网络理解为左边浅层网络加深了三层(框起来的部分),假如我们希望右边的深层网络与左边的浅层网络持平,即是希望框起来的三层跟没加一样,也就是加的三层的输入等于输出。我们假设这三层的输入为x,输出为H(x), 那么深层网络与浅层网络表现持平的直观理解即是: H(x)=x, 这种让输出等于输入的方式,就是论文中提到的恒等映射(identity mapping)

所以ResNet的初衷,就是让网络拥有这种恒等映射的能力,能够在加深网络的时候,至少能保证深层网络的表现至少和浅层网络持平

Basic Blcok

残差网络单元

要使网络不退化,根本就是做到恒等映射,但此时拟合H(x)=x很困难,可以将网络设计为H(x)=F(x)+x,将恒等映射作为网络一部分,相当于学习一个残差函数F(x)=H(x)-x,只要F(x)=0即可。

F=w2(relu(w1)x)

代码示例:Tensorflow2.1.0

X输入到BasicBlock ,经过卷积后输出,此时size可能变化,在经过Identity与X短接,identity其实是一个1x1卷积控制短接成功,如下downsample。

class BaiscBlcok(layers.Layer):
    def __init__(self,filter_num,stride=1):
        super(BaiscBlcok,self).__init__()
        # BasicBlock 第一个卷积
        self.conv1 = layers.Conv2D(filter_num,(3,3),stride=stride,padding="same")
        self.bn1 = layers.BatchNormalization()
        self.relu = layers.Activation("relu")
        # BasicBlock 第二个卷积,relu并未出现,短接后出现,效果更好
        self.conv2 = layers.Conv2D(filter_num,(3,3),stride=1,padding="same")
        self.bn2 = layers.BatchNormalization()
        # 短接层Identity
        if stride != 1:
            self.downsample = Sequential()
            self.downsample.add(layers.Conv2D(filter_num,(1,1),stride=stride))
        else:
            self.downsample = lambda x:x

    def call(self, inputs, training=None):
        # 卷积1
        out = self.conv1(input)
        out = self.bn1(out)
        out = self.relu(out)
        # 卷积二
        out = self.conv2(out)
        out = self.bn2(out)

        # 短接
        identity = self.downsample(inputs)
        output = layers.add([out,identity])
        output = self.relu(output)
        return output

多个basic block组成残差网络的基本单元,即一个基本单元内可以有多个basic block。

Resnet18 = 1(预处理)+4*4+1(全连接),Resnet34,等等以此类推

class ResNet(keras.Model):  # Resnet18
    def __init__(self,layer_dims,num_classes=100):
        # [2,2,2,2]  四个res_block,每个包含2个basicblock
        super(ResNet, self).__init__()

        # 预处理层
        self.stem = Sequential([
            layers.Conv2D(64,(3,3),strides=(1,1)),
            layers.BatchNormalization(),
            layers.Activation("relu"),
            layers.MaxPool2D(pool_size=(2,2),strides=(1,1),padding="same") # 可以没有
                                ])
        # 中间
        self.layer1 = self.build_resblock(64,layer_dims[0])
        self.layer2 = self.build_resblock(128,layer_dims[1],stride=2) # 降维
        self.layer3 = self.build_resblock(256,layer_dims[2],stride=2)
        self.layer4 = self.build_resblock(512,layer_dims[3],stride=2)

        # 全连接
        self.avgpool = layers.GlobalAvgPool2D()  # 例:假设卷积输出【h,w,c】---》【6,6,3】 ----》【1,1,3】
        self.fc = layers.Dense(num_classes)

    def call(self, inputs, training=None,):

        x = self.stem(inputs)

        x = self.layer1(x)
        x = self.layer2(x)
        x = self.layer3(x)
        x = self.layer4(x)

        # [b,c]
        x = self.avgpool(x)
        # [b,num_classes]
        x = self.fc(x)

        return x

    def build_resblock(self,filter_num,blocks,stride=1):
        res_blocks = Sequential()

        # 可能有下采样功能
        res_blocks.add(BaiscBlcok(filter_num,stride))

        for _ in range(1,blocks):
            res_blocks.add(BaiscBlcok(filter_num,stride=1))
        return res_blocks

    @staticmethod
    def resnet18():
        return ResNet([2,2,2,2],num_classes=2)

if __name__ == '__main__':
    model = ResNet.resnet18()
    model.build(input_shape=(None,576,576,3))
    model.summary()
Resnet网络结构

Dean0731

海纳百川,有容乃大,壁立千仞,无欲则刚

相关推荐

发表评论