[Классификация изображений] Разорвите ResNet вручную — воспроизведите ResNet (Keras, Tensorflow 2.x)

искусственный интеллект

содержание

Резюме

Реализовать остаточный модуль

Реснет18, Реснет34

Реснет50, Реснет101, Реснет152


Резюме

ResNet (Residual Neural Network) был предложен четырьмя китайцами, включая Kaiming He из Исследовательского института Microsoft.С помощью ResNet Unit 152-слойная нейронная сеть была успешно обучена и выиграла чемпионат в соревновании ILSVRC2015 с частотой ошибок 3,57% на top5, хотя количество параметров меньше, чем у VGGNet, эффект очень заметен.

Новшество модели заключается в предложении идеи остаточного обучения, добавлении прямого канала подключения к сети и прямой передаче исходной входной информации на следующие слои, как показано на следующем рисунке:


Традиционные сверточные сети или полносвязные сети имеют более или менее проблемы, такие как потеря информации и потеря во время передачи информации, и в то же время градиенты исчезают или градиенты взрываются, делая глубокие сети неспособными к обучению. ResNet в определенной степени решает эту проблему.Прямая передача входной информации на выход защищает целостность информации.Вся сеть должна изучить только часть разницы между входом и выходом, что упрощает цели обучения и трудности. Сравнение VGGNet и ResNet показано на рисунке ниже. Самое большое отличие ResNet заключается в том, что существует множество обходных путей для прямого подключения входа к следующим слоям, эта структура также называется краткими или пропускными соединениями.

В структуре сети ResNet используются два остаточных модуля, один из которых представляет собой две сверточные сети 3 * 3, соединенные вместе как остаточный модуль, другой - 1 * 1, 3 * 3, 1 * 1. 3 сверточные сети объединены вместе как остаточный модуль. модуль. Как показано ниже:

ResNet имеет разные сетевые уровни, чаще всего используются 18-уровневый, 34-уровневый, 50-уровневый, 101-уровневый, 152-уровневый. Все они реализованы путем стекирования остаточных модулей, описанных выше. На рисунке ниже показаны различные модели ResNet.

Реализовать остаточный модуль

Первый остаточный модуль

# 第一个残差模块
class BasicBlock(layers.Layer):
    def __init__(self, filter_num, stride=1):
        super(BasicBlock, self).__init__()
        self.conv1 = layers.Conv2D(filter_num, (3, 3), strides=stride, padding='same')
        self.bn1 = layers.BatchNormalization()
        self.relu = layers.Activation('relu')
        self.conv2 = layers.Conv2D(filter_num, (3, 3), strides=1, padding='same')
        self.bn2 = layers.BatchNormalization()
        if stride != 1:
            self.downsample = Sequential()
            self.downsample.add(layers.Conv2D(filter_num, (1, 1), strides=stride))
        else:
            self.downsample = lambda x: x

    def call(self, input, training=None):
        out = self.conv1(input)
        out = self.bn1(out)
        out = self.relu(out)
        out = self.conv2(out)
        out = self.bn2(out)
        identity = self.downsample(input)
        output = layers.add([out, identity])
        output = tf.nn.relu(output)
        return output

второй остаточный модуль

 

# 第二个残差模块
class Block(layers.Layer):
    def __init__(self, filters, downsample=False, stride=1):
        super(Block, self).__init__()
        self.downsample = downsample
        self.conv1 = layers.Conv2D(filters, (1, 1), strides=stride, padding='same')
        self.bn1 = layers.BatchNormalization()
        self.relu = layers.Activation('relu')
        self.conv2 = layers.Conv2D(filters, (3, 3), strides=1, padding='same')
        self.bn2 = layers.BatchNormalization()
        self.conv3 = layers.Conv2D(4 * filters, (1, 1), strides=1, padding='same')
        self.bn3 = layers.BatchNormalization()
        if self.downsample:
            self.shortcut = Sequential()
            self.shortcut.add(layers.Conv2D(4 * filters, (1, 1), strides=stride))
            self.shortcut.add(layers.BatchNormalization(axis=3))

    def call(self, input, training=None):
        out = self.conv1(input)
        out = self.bn1(out)
        out = self.relu(out)
        out = self.conv2(out)
        out = self.bn2(out)
        out = self.relu(out)
        out = self.conv3(out)
        out = self.bn3(out)
        if self.downsample:
            shortcut = self.shortcut(input)
        else:
            shortcut = input
        output = layers.add([out, shortcut])
        output = tf.nn.relu(output)
        return output

Реснет18, Реснет34

import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers, Sequential


# 第一个残差模块
class BasicBlock(layers.Layer):
    def __init__(self, filter_num, stride=1):
        super(BasicBlock, self).__init__()
        self.conv1 = layers.Conv2D(filter_num, (3, 3), strides=stride, padding='same')
        self.bn1 = layers.BatchNormalization()
        self.relu = layers.Activation('relu')
        self.conv2 = layers.Conv2D(filter_num, (3, 3), strides=1, padding='same')
        self.bn2 = layers.BatchNormalization()
        if stride != 1:
            self.downsample = Sequential()
            self.downsample.add(layers.Conv2D(filter_num, (1, 1), strides=stride))
        else:
            self.downsample = lambda x: x

    def call(self, input, training=None):
        out = self.conv1(input)
        out = self.bn1(out)
        out = self.relu(out)
        out = self.conv2(out)
        out = self.bn2(out)
        identity = self.downsample(input)
        output = layers.add([out, identity])
        output = tf.nn.relu(output)
        return output


class ResNet(keras.Model):
    def __init__(self, layer_dims, num_classes=10):
        super(ResNet, self).__init__()
        # 预处理层
        self.padding = keras.layers.ZeroPadding2D((3, 3))
        self.stem = Sequential([
            layers.Conv2D(64, (7, 7), strides=(2, 2)),
            layers.BatchNormalization(),
            layers.Activation('relu'),
            layers.MaxPool2D(pool_size=(3, 3), strides=(2, 2), padding='same')
        ])
        # resblock
        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.GlobalAveragePooling2D()
        # 全连接层
        self.fc = layers.Dense(num_classes, activation=tf.keras.activations.softmax)

    def call(self, input, training=None):
        x=self.padding(input)
        x = self.stem(x)
        x = self.layer1(x)
        x = self.layer2(x)
        x = self.layer3(x)
        x = self.layer4(x)
        # [b,c]
        x = self.avgpool(x)
        x = self.fc(x)
        return x

    def build_resblock(self, filter_num, blocks, stride=1):
        res_blocks = Sequential()
        res_blocks.add(BasicBlock(filter_num, stride))
        for pre in range(1, blocks):
            res_blocks.add(BasicBlock(filter_num, stride=1))
        return res_blocks


def ResNet34(num_classes=10):
    return ResNet([2, 2, 2, 2], num_classes=num_classes)


def ResNet34(num_classes=10):
    return ResNet([3, 4, 6, 3], num_classes=num_classes)


model = ResNet34(num_classes=1000)
model.build(input_shape=(1, 224, 224, 3))
print(model.summary())  # 统计网络参数

Реснет50, Реснет101, Реснет152

import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers, Sequential


# 第一个残差模块
class Block(layers.Layer):
    def __init__(self, filters, downsample=False, stride=1):
        super(Block, self).__init__()
        self.downsample = downsample
        self.conv1 = layers.Conv2D(filters, (1, 1), strides=stride, padding='same')
        self.bn1 = layers.BatchNormalization()
        self.relu = layers.Activation('relu')
        self.conv2 = layers.Conv2D(filters, (3, 3), strides=1, padding='same')
        self.bn2 = layers.BatchNormalization()
        self.conv3 = layers.Conv2D(4 * filters, (1, 1), strides=1, padding='same')
        self.bn3 = layers.BatchNormalization()
        if self.downsample:
            self.shortcut = Sequential()
            self.shortcut.add(layers.Conv2D(4 * filters, (1, 1), strides=stride))
            self.shortcut.add(layers.BatchNormalization(axis=3))

    def call(self, input, training=None):
        out = self.conv1(input)
        out = self.bn1(out)
        out = self.relu(out)
        out = self.conv2(out)
        out = self.bn2(out)
        out = self.relu(out)
        out = self.conv3(out)
        out = self.bn3(out)
        if self.downsample:
            shortcut = self.shortcut(input)
        else:
            shortcut = input
        output = layers.add([out, shortcut])
        output = tf.nn.relu(output)
        return output


class ResNet(keras.Model):
    def __init__(self, layer_dims, num_classes=10):
        super(ResNet, self).__init__()
        # 预处理层
        self.padding = keras.layers.ZeroPadding2D((3, 3))
        self.stem = Sequential([
            layers.Conv2D(64, (7, 7), strides=(2, 2)),
            layers.BatchNormalization(),
            layers.Activation('relu'),
            layers.MaxPool2D(pool_size=(3, 3), strides=(2, 2), padding='same')
        ])
        # resblock
        self.layer1 = self.build_resblock(64, layer_dims[0],stride=1)
        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.GlobalAveragePooling2D()
        # 全连接层
        self.fc = layers.Dense(num_classes, activation=tf.keras.activations.softmax)

    def call(self, input, training=None):
        x = self.padding(input)
        x = self.stem(x)
        x = self.layer1(x)
        x = self.layer2(x)
        x = self.layer3(x)
        x = self.layer4(x)
        # [b,c]
        x = self.avgpool(x)
        x = self.fc(x)
        return x

    def build_resblock(self, filter_num, blocks, stride=1):
        res_blocks = Sequential()
        if stride != 1 or filter_num * 4 != 64:
            res_blocks.add(Block(filter_num, downsample=True,stride=stride))
        for pre in range(1, blocks):
            res_blocks.add(Block(filter_num, stride=1))
        return res_blocks


def ResNet50(num_classes=10):
    return ResNet([3, 4, 6, 3], num_classes=num_classes)


def ResNet101(num_classes=10):
    return ResNet([3, 4, 23, 3], num_classes=num_classes)

def ResNet152(num_classes=10):
    return ResNet([3, 8, 36, 3], num_classes=num_classes)

model = ResNet50(num_classes=1000)
model.build(input_shape=(1, 224, 224, 3))
print(model.summary())  # 统计网络参数

результат операции: