O que é ResNet? Construir ResNet do zero com Python

Conteúdo

Este artigo foi publicado como parte do Data Science Blogathon

Introdução

Ha habido una serie de avances en el campo del aprendizado profundo y la visión por computadora. Especialmente com a introdução de redes neurais convolucionais muito profundas, esses modelos ajudaram a obter resultados de ponta em problemas como reconhecimento e classificação de imagens.

Então, ao passar dos anos, as arquiteturas de aprendizagem profunda ficaram cada vez mais profundas (adicionando mais camadas) para resolver tarefas cada vez mais complexas, o que também ajudou a melhorar o desempenho das tarefas de classificação e reconhecimento e também a torná-las robustas.

Pero cuando seguimos agregando más capas a la neuronal vermelho, torna-se muito mais difícil de treinar e a precisão do modelo começa a saturar e depois também degrada. Aí vem a ResNet para nos resgatar daquele cenário e ajudar a resolver esse problema.

O que é ResNet?

Rede Residual (ResNet) é um dos famosos modelos de aprendizagem profunda introduzidos por Shaoqing Ren, Kaiming He, Jian Sun e Xiangyu Zhang em seu artigo. O documento foi nomeado “Aprendizagem residual profunda para reconhecimento de imagem”. [1] sobre 2015. O modelo ResNet é um dos modelos de aprendizado profundo mais populares e bem-sucedidos até agora.

Blocos residuais

O problema de treinar redes muito profundas foi amenizado com a introdução desses blocos residuais e o modelo ResNet é composto por esses blocos.

63071n1-8299125
Fonte: ‘Aprendizagem residual profunda para reconhecimento de imagem‘ papel

O problema de treinar redes muito profundas foi amenizado com a introdução desses blocos residuais e o modelo ResNet é composto por esses blocos.

No figura anterior, a primeira coisa que podemos notar é que há uma conexão direta que omite algumas camadas do modelo. Esta conexão é chamada “conexão de salto” e é o coração dos blocos residuais. A saída não é a mesma devido a esta conexão de salto. Sem a conexão de salto, a entrada 'X é multiplicada pelos pesos da camada, seguido pela adição de um termo de inclinação.

Luego viene la função de despertar, f () e obtemos a saída como H (x).

H (x) = f (wx + b) Oh (x) = f (x)

Agora, com a introdução de uma nova técnica de conexão de salto, a saída é H (x) muda para

H (x) = f (x) + x

Pero la dimensão de la entrada puede variar de la de la salida, lo que podría suceder con una capa convolucional o capas agrupadas. Portanto, este problema pode ser tratado com essas duas abordagens:

· Zero é preenchido com a conexão de salto para aumentar suas dimensões.

· Camadas convolucionais são adicionadas 1 × 1 na entrada para combinar com as dimensões. Em tal caso, a saída é:

H (x) = f (x) + w1.x

Aqui, um parâmetro extra w1 é adicionado enquanto nenhum parâmetro extra é adicionado ao usar a primeira abordagem.

Esta técnica de omisión de conexiones en ResNet resuelve el problema de la desaparición del gradiente en las CNN profundas al permitir una ruta de acceso direto alternativa para que fluya el gradiente. O que mais, a conexão de bypass ajuda se alguma camada prejudicar o desempenho da arquitetura, entonces se omitirá mediante la regularização.

Arquitectura de ResNet

Existe uma rede simples de 34 camadas na arquitetura que é inspirada pelo VGG-19 em que conexão de acesso direto ou conexões de salto são adicionadas. Essas conexões de salto ou blocos residuais, então, convertem a arquitetura para a rede residual, conforme mostrado na figura abaixo.

28984n2-1814961

Fonte: ‘Aprendizagem residual profunda para reconhecimento de imagem‘ papel

Usando ResNet com Keras:

Keras é uma biblioteca de aprendizado profundo de código aberto capaz de funcionar no TensorFlow. Os aplicativos Keras fornecem as seguintes versões do ResNet.

– ResNet50

– ResNet50V2

– ResNet101

– ResNet101V2

– ResNet152

– ResNet152V2

Vamos construir o ResNet do zero:

78567n3-1485933

Fonte: ‘Aprendizagem residual profunda para reconhecimento de imagem‘ papel

Vamos manter a imagem acima para referência e começar a construir a rede..

La arquitectura de ResNet usa los bloques CNN varias veces, así que creemos una clase para el bloque CNN, que toma canais de entrada y canais de salida. Feno un batchnorm2d después de cada capa de conv.

import torch
import torch.nn as nn
bloco de classe(nn.Module):
    def __init__(
        auto, in_channels, intermediate_channels, identity_downsample=Nenhum, passo=1
    ):
        super(bloquear, auto).__iniciar__()
        auto.expansão = 4
        self.conv1 = nn. Conv2d(
            in_channels, intermediate_channels, kernel_size=1, passo=1, preenchimento = 0, viés=Falso
        )
        auto.bn1 = nn. BatchNorm2d(intermediate_channels)
        self.conv2 = nn. Conv2d(
            intermediate_channels,
            intermediate_channels,
            kernel_size = 3,
            passo=passo,
            preenchimento=1,
            viés=Falso
        )
        auto.bn2 = nn. BatchNorm2d(intermediate_channels)
        self.conv3 = nn. Conv2d(
            intermediate_channels,
            intermediate_channels * auto.expansão,
            kernel_size=1,
            passo=1,
            preenchimento = 0,
            viés=Falso
        )
        auto.bn3 = nn. BatchNorm2d(intermediate_channels * auto.expansão)
        self.relu = nn. ReLU()
        self.identity_downsample = identity_downsample
        self.stride = stride

    def forward(auto, x):
        identidade = x.clone()

        x = self.conv1(x)
        x = self.bn1(x)
        x = self.relu(x)
        x = self.conv2(x)
        x = self.bn2(x)
        x = self.relu(x)
        x = self.conv3(x)
        x = self.bn3(x)

        se self.identity_downsample não é Nenhum:
            identidade = self.identity_downsample(identidade)

        x += identity
        x = self.relu(x)
        retornar x

Mais tarde, cree una clase ResNet que tome la entrada de varios bloques, capas, canais de imagem y la cantidad de clases.

No seguinte código, la función '_make_layer’
crea las capas ResNet, que toma la entrada de bloques, el número de resíduos
bloques, canal de salida y zancadas.

classe ResNet(nn.Module):
    def __init__(auto, bloquear, camadas, image_channels, num_classes):
        super(ResNet, auto).__iniciar__()
        self.in_channels = 64
        self.conv1 = nn. Conv2d(image_channels, 64, kernel_size=7, passo=2, preenchimento=3, viés=Falso)
        auto.bn1 = nn. BatchNorm2d(64)
        self.relu = nn. ReLU()
        self.maxpool = nn. MaxPool2d(kernel_size = 3, passo=2, preenchimento=1)
# Essencialmente, toda a arquitetura ResNet está nestas 4 lines below
self.layer1 = self._make_layer(
bloquear, camadas[0], intermediate_channels=64, passo=1
)
auto.layer2 = self._make_layer(
bloquear, camadas[1], intermediate_channels=128, passo=2
)
auto.layer3 = self._make_layer(
bloquear, camadas[2], intermediate_channels=256, passo=2
)
auto.layer4 = self._make_layer(
bloquear, camadas[3], intermediate_channels=512, passo=2
)

self.avgpool = nn. AdaptiveAvgPool2d((1, 1))
self.fc = nn. Linear(512 * 4, num_classes)

def frente(auto, x):
x = self.conv1(x)
x = self.bn1(x)
x = self.relu(x)
x = self.maxpool(x)
x = auto.layer1(x)
x = auto.layer2(x)
x = auto.layer3(x)
x = auto.layer4(x)

x = self.avgpool(x)
x = x.remodelar(x.shape[0], -1)
x = self.fc(x)

return x

def _make_layer(auto, bloquear, num_residual_blocks, intermediate_channels, passo):
identity_downsample = None
layers = []

# Ou se metade do espaço de entrada para ex, 56x56 -> 28x28 (passo=2), ou mudanças de canais
# precisamos adaptar a Identidade (pular conexão) por isso será capaz de ser adicionado
# to the layer that's ahead


if stride != 1 ou self.in_channels != intermediate_channels * 4:
identity_downsample = nn. Sequencial(
nn. Conv2d(
self.in_channels,
intermediate_channels * 4,
kernel_size=1,
passo=passo,
viés=Falso
),
nn. BatchNorm2d(intermediate_channels * 4),
)

camadas.append(
bloquear(self.in_channels, intermediate_channels, identity_downsample, passo)
)

# O tamanho da expansão é sempre 4 para ResNet 50,101,152
self.in_channels = intermediate_channels * 4

# Por exemplo, para a primeira camada de resnet: 256 será mapeado para 64 como camada intermediária,
# em seguida, finalmente de volta para 256. Portanto, não é necessário diminuir a identidade, desde o passo = 1,
# e também a mesma quantidade de canais.
para eu no alcance(num_residual_blocks - 1):
camadas.append(bloquear(self.in_channels, intermediate_channels))

retorno nn. Sequencial (* capas)

Luego defina diferentes versões de ResNet

Para ResNet50, a sequência de camadas é [3, 4, 6, 3].

Para ResNet101, a sequência de camadas é [3, 4, 23, 3].

Para ResNet152, a sequência de camadas é [3, 8, 36, 3]. (Pedir ao Aprendizagem residual profunda para reconhecimento de imagem‘ papel)

def ResNet50(img_channel = 3, num_classes = 1000):
    retornar ResNet(bloquear, [3, 4, 6, 3], img_channel, num_classes)
def ResNet101(img_channel = 3, num_classes = 1000):
retornar ResNet(bloquear, [3, 4, 23, 3], img_channel, num_classes)


def ResNet152(img_channel = 3, num_classes = 1000):
retornar ResNet(bloquear, [3, 8, 36, 3], img_channel, num_classes)

Mais tarde, escreva um pequeno código de teste para verificar se o modelo está funcionando bem.

teste de def():
    net = ResNet101(img_channel = 3, num_classes = 1000)
    dispositivo = "milagres" se torch.cuda.is_available() outro "CPU"
    y = net(torch.randn(4, 3, 224, 224)).para(dispositivo)
    imprimir(y.size())
teste()

Para o caso de teste acima, a saída deve ser:

76052n4-1368070

O código completo pode ser acessado aqui:

https://github.com/BakingBrains/Deep_Learning_models_implementation_from-scratch_using_pytorch_/blob/main/ResNet_.py

[1]. Kaiming He, Xiangyu Zhang, Shaoqing Ren, Jian Sun: Aprendizagem profunda residual para reconhecimento de imagem, Dezembro de 2015, DOI: https://arxiv.org/abs/1512.03385

Obrigado.

Suas sugestões e dúvidas são bem-vindas aqui na seção de comentários. Obrigado por ler o meu artigo!

Assine a nossa newsletter

Nós não enviaremos SPAM para você. Nós odiamos isso tanto quanto você.

Datapeaker