Resnet#
Deep Residual Learning from Image Recognition
Abstract
Deeper neural networks are more difficult to train. We present a residual learning framework to ease the training of networks that are substantially deeper than those used previously(vgg). We explicitly reformulate the layers as learning residual functions with reference to the layer inputs, instead of learning unreferenced functions. We provide comprehensive empirical evidence showing that these residual networks are easier to optimize, and can gain accuracy from considerably increased depth. On the ImageNet dataset we evaluate residual nets with a depth of up to 152 layers - 8รdeeper than VGG nets [41] but still having lower complexity. An ensemble of these residual nets achieves 3.57% error on the ImageNet testset. This result won the 1st place on theILSVRC 2015 classification task. We also present analysis on CIFAR-10 with 100 and 1000 layers.The depth of representations is of central importance for many visual recognition tasks. Solely due to our extremely deep representations, we obtain a 28% relative improvement on the COCO object detection dataset. Deep residual nets are foundations of our submissions to ILSVRC& COCO 2015 competitions 1, where we also won the 1st places on the tasks of ImageNet detection, ImageNet localization, COCO detection, and COCO segmentation.
Deep Residual Learning for Image Recognition ์ด๋ ์ ๋ชฉ์ ๋ ผ๋ฌธ์ผ๋ก ์ฐ๋ฆฌ๊ฐ ๋ง์ด ๋ค์ด๋ณธ โresnetโ์ ๋ํด ๋์จ ๋ ผ๋ฌธ์ด๋ค. 2014๋ ์ vgg๋ ผ๋ฌธ์ด ๋์ค๊ณ ๋ฐ๋ก ๋ค์ ํด์ ๋์จ ๋ ผ๋ฌธ์ผ๋ก, ์ฌ์ค์ vgg์ ๊ตฌ์กฐ์ residual mapping์ด๋ผ๋ ์์ด๋์ด๋ง์ ์ถ๊ฐํ๊ณ ๋ imagenet classification์์ ๋์ ๋๋ ์ ์ ํฅ์์ ๋ณด์๋ค. resnet์ด๋ผ๋ ๋ ผ๋ฌธ์ ์์ด๋์ด ์์ฒด๊ฐ ์ฝ๋ค๋ ์ , ๊ทธ๋ฆฌ๊ณ ๊ทธ ์์ด๋์ด๊ฐ ๋น์์ ๋ชจ๋ธ์ ๊น์ด์ ๋ชจ๋ธ์ ์ฑ๋ฅ์ด ์ ํ์ ์ธ ์๊ด๊ด๊ณ๋ฅผ ์ด๋ฃจ์ง ๋ชปํ๋ ๋ฌธ์ (degradation problem)๋ฅผ ์ฌ์ด ๋ ผ๋ฆฌ๋ก ํด๊ฒฐํ๋ค๋ ์ ์์ ์ข์ ๋ ผ๋ฌธ์ผ๋ก ์ง๊ธ๊น์ง ํ๊ฐ๋๋ค. ์ฐ์ ์ resent์์ ํด๊ฒฐํ๊ณ ์ ํ๋ ๋ฌธ์ ๋ถํฐ ์ดํด๋ณด์.
๋ฌธ์ ์ค์ problem set-up#
vgg๋ฅผ ํตํด์ image classification์ด๋ผ๋ ๊ณผ์ ์์ ๋ง์ breakthrough๊ฐ ์์๋ค. cnn layer๋ฅผ ๊น์ด ์์์ผ๋ก์จ low/mid/high ์์ค์ feature๋ค์ ํตํฉํ๊ณ , ๋ ๊น์ด ์์์๋ก ๋ ๋ง์ feature๋ค์ data์์ ์ถ์ถํ ์ ์๊ฒ ๋ ๊ฒ์ด๋ค. ํ์ง๋ง ๋ฌด์กฐ๊ฑด ๊น์ด๋ฅผ ๋ง์ด ์๋๋ค๊ณ ๋์๋ ๊ฒ์ ์๋๋ค. ๋ฐ์ 2๊ฐ์ ๋ฌธ์ ๊ฐ ๊ทธ๊ฒ์ด๋ค.
1. vanishing/exploding gradients problem#
Is learning better networks as easy as stacking more layers?
โbro. of course not!
์ฒซ๋ฒ์งธ ๋ฌธ์ ๋ gradient๊ฐ ์์ค๋๊ฑฐ๋ ํญ๋ฐํด๋ฒ๋ฆฌ๋ ๋ฌธ์ ๋ค. layer๊ฐ ๋ช ์ธต ๋์ง ์๋ shallowํ network์์๋ ์ด๋ฌํ ๋ฌธ์ ๊ฐ ๋ํ๋์ง ์๊ฑฐ๋ ๊ฑฑ์ ์ด ํ์์์ ์ ๋ ์ด์ง๋ง, network๊ฐ ๊น์ด์ง๋ฉด gradient(๊ฒฝ์ฌ๋)๊ฐ too small or big for training to work effectively
ํ๊ฒ ๋๊ณ ์ด ๋ฌธ์ ๊ฐ vanishing exploding gradient ๋ฌธ์ ๋ค. sigmoid ํจ์๋ฅผ ์๊ฐํ๋ฉด ๋ฌธ์ ์ ๋ํด ์ด์ ํ๊ธฐ ์ฝ๋ค.
when n hidden layers use an activation like the sigmoid function, n small derivatives are multiplied together. Thus, the gradient decreases exponentially as we propagate down to the initial layers.
chain rule์ ๋ฐ๋ผ ๊ฐ layer์์์์ derivative๋ ๋คํธ์ํฌ๋ฅผ ๋ฐ๋ผ์ ๊ณฑํด์ง๊ณ , ๋ฐฉํฅ์ฑ์ ๋๋จ์์ ๋งจ ์ฒ์ layer๋ก ํฅํ๊ฒ ๋๋ค. ๋ค์์๋ถํฐ ์์ผ๋ก ํฅํ๋ back propagation์์ sigmoid๋ฅผ non-linear activaiton function์ผ๋ก ์ฌ์ฉํ๋ฉด, ์์ x๊ฐ(input)๋ค์ ์ ๋ถ 0์ ํ์์ด ๊ฐ๊น์์ง๊ธฐ ๋๋ฌธ์ ํ์ฑํ๊ฐ ์๋์ง ์๊ณ , ๊ณฑ์ ์ด ์งํ๋จ์ ๋ฐ๋ผ ์์ฃผ์์ฃผ ์์์ง๊ฒ๋๋ค. ์ด๋ ๊ณง ๋งจ ์๊น์ง ์ค๋ฉด gradient๊ฐ ์ฌ๋ผ์ง ๊ฒ ์ฒ๋ผ, ๊ทธ๋ฆฌ๊ณ ํ์ฑํ ์ญํ ์ ์ ๋๋ก ํ์ง ๋ชปํ๋ ํจ๊ณผ๋ฅผ ๋ํ๋ธ๋ค.
ํ์ง๋ง ์ด๋ฌํ ๋ฌธ์ ๋ ๋ ผ๋ฌธ์์์๋ ๋ง์ด ํด๊ฒฐ๋์๋ค๊ณ ๋งํ๋ค. ํ์ต ์์ฒด๊ฐ ์๋๋ ๋ฌธ์ ์ด๊ณ gradient๋ฅผ ์ด๋ฆฌ๋ ๊ฒ์ด ๋ฌธ์ ์์ผ๋ก nomarlized initialization, intermediate normalization layers ์ด ๋ ๋ฐฉ๋ฒ์์ ํด๊ฒฐ๋์๋ค๊ณ ๋ณธ๋ค. ๋ ผ๋ฌธ์์ ์ฃผ๋ก ๋ค๋ฃจ๊ณ ์ํ๊ณ ํด๊ฒฐํ๊ณ ์ถ์ ๋ฌธ์ ๋ 2๋ฒ์จฐ ๋ฌธ์ ์ด๋ค.
2. Degradation problem#
๊น์ network์ด ์๋ ด์ ์์ํ๋ค๊ณ ํด๋, degradation problem(์ฑ๋ฅ ์ ํ ๋ฌธ์ )๊ฐ ๋ํ๋ ์ ์๋ค๊ณ ๋งํ๋ค. ์ด ๋ฌธ์ ๋ gradient vanishing/exploding ๋ฌธ์ ๋ณด๋ค ์ข ๋ ๋์ ๋ฒ์์ ๋ฌธ์ ์ด๋ค. ์ด ๋ฌธ์ ์ ์ํฉ์์ network๋ ํ์ต๋ ๋๊ณ , gradient๋ ์ด์์๊ณ , accuracy score๊ฐ ์์น์ ํ๋๋ฐ, ์คํ๋ ค depth๊ฐ ๋ฎ์ network๋ณด๋ค depth๋ฅผ ๋์ธ network๊ฐ ์ ํ๋ ๋ฑ์ ํ๊ฐ์งํ์์ ๋ ๋์์ผ ํ๋๋ฐ ๊ทธ๋ ์ง ๋ชปํ๋ ํ์์ ๋งํ๋ค.
deeper is better๋ฅผ ํ๋์ ์์๋ง ๋ฃ์ผ๋ฉด ๊ฐ๋ฅํ๊ฒ ํ๋ ๊ฒ, ์ฆ degradation problem์ ํด๊ฒฐํ๋ ๊ฒ - ์ด ๋
ผ๋ฌธ์์๋ Redisual mapping
์ด๋ค.
Residual mapping, Identity mapping#
๊ธฐ์กด์ vgg์์์ mapping block์ \(H(\text{x})\)์ด๋ผ๊ณ ํ๋ค๋ฉด, ์ด๋ฐ block์ด 18๊ฐ์ ๋ ์ด์ด์ ธ ๋ถ์ด์๋ ํํ์๋ค. block ๋ด๋ถ์๋ cnn layer + relu layer + cnn layer + relu layer ๋ก ๊ตฌ์ฑ๋์ด ์๋ค.
resent์์๋ ์ด๋ฌํ ๊ตฌ์กฐ์์ block๋ง๋ค์ input(x) \(\to\) output(\(H(\text{x})\)=y) ๊ด๊ณ๋ฅผ ๋ถํดํ๋ค. input(x) + residual(F(x)) \(\to\) output(\(H(\text{x})\)=y). ๊ฒฐ๊ตญ ํ๋์ ๋ธ๋ญ ์์์ ํ์ตํด์ผํ๋ ๋ถ๋ถ์ \(H(\text{x}) - x = F(\text{x})\)๊ฐ ๋๋ ๊ฒ์ด๊ณ ์ด๊ฒ์ด Residual
์ด ๋๋ ๊ฒ์ด๋ค. ๊ทธ๋ฆฌ๊ณ input์ y=f(x)=x ์ฒ๋ผ input๊ฐ์ด output๊ณผ ๊ฐ์ ๊ฒ ์ฒ๋ผ mapping๋๋ ๋ถ๋ถ์์ผ๋ก identity mapping์ด๋ผ๊ณ ๋ถ๋ฆฐ๋ค.
F : residual function
๋ง์ฝ F๊ฐ single layer๋ผ๋ฉด : y = W_1 x + x ๊ฐ ๋ ์๋ ์๋ค.
F(x, {W_i})๋ multiple convolutional layers๋ฅผ ํํ
๊ธฐ์กด์ output์๋ค input์ ๋ํด์ฃผ๋ +
์ ๊ฐ๋
์ผ๋ก ์ดํดํ ์ ๋ ์๊ณ , ๊ธฐ์กด์ mapping์ ํด์ฒดํ๋ -
์ ๊ฐ๋
์ผ๋ก ์๊ฐํด๋ณผ ์ ๋ ์๋ค. -
์ ๊ฐ๋
์ผ๋ก ์ ๊ทผํ๋ค๋ฉด ๊ธฐ์กด์ optimize ํด ์ฃผ์ด์ผํ ๋ถ๋ถ์ด ์ค์ด๋ ๋ค๋ ๊ด์ ์ผ๋ก ์ ๊ทผํ ์ ์์ ๊ฒ์ด๊ณ , ์ด๊ฒ์ด ๋
ผ๋ฌธ์์ ๊ฐ์ ํ๊ณ ์ ๊ทผํ ์ง์ ์ด๋ค. identity mapping(x)๊ฐ ์ด๋ฏธ optimalํ๊ฒ mapping์ ์งํํด์๋ค๋ฉด ๋จ์ residual mapping(F(x))๋ง 0์ ๊ฐ๊น๊ฒ ๋ง๋ค๋ฉด ๋๋ค๋ ๊ฒ์ด๋ค. ๊ทธ๋ผ H(x)๊ฐ ๊ฒฐ๊ณผ์ ์ผ๋ก optimalํด์ง ๊ฒ์ด๊ณ output์ x๋ก ๋์ด์ ๋ค์ block์ input์ด ๋ ๊ฒ์ด๋ค.
The degradation problem suggests that the solvers might have difficulties in approximating identity mappingsby multiple nonlinear layers.
์ฌ๊ธฐ์ solver๋ optimization algorithm, back-propagation algorithm์ ๋งํ๋ค. gradient๋ฅผ ๊ณ์ฐํ์ฌ ๊ฐ์ค์น๋ฅผ ์ ๋ฐ์ดํธํ์ฌ ์์ค ํจ์๋ฅผ ์ต์ํํ๋ ๊ณผ์ ์ ๋งํ๋ค. ์ฆ ๊ธฐ์กด์ identity mapping์ด ์๋ network์์์ ์ต์ ํ ๊ณผ์ ์์์ degradation problem์ ๋น์ ํ ๋ ์ด์ด๋ฅผ ์ฌ๋ฌ ๊ฐ ํต๊ณผํ๋ฉด์ ์ ๋ ฅ๊ณผ ์ถ๋ ฅ์ด ๊ฐ์(identity mapping)์ ๊ฒฝ์ฐ์ ๋ํ ๋์ฒ๊ฐ ์ด๋ ค์ ์ง๋ค๋ ๊ฒ์ด๋ค. ๊ทธ๋ฆฌ๊ณ ์์ residual mapping๊ณผ identity mapping์ shortcut connection์ผ๋ก ๊ตฌํํจ์ผ๋ก์จ ๊น์ด์ง๋ network์์์ gradient ํ๋ฆ์ ๋ณด์กดํ ์ ์์๋ค๊ณ ๋งํ๋ค.
๋ฌผ๋ก identity mapping์ด optimalํ ๊ฒฝ์ฐ๋ ์ค์ ํ์ต๊ณผ์ ์์๋ ์ด๋ฃจ์ด ์ง์ง ์์ ์ ์๋ค๊ณ ๋ง๋ค. ํ์ง๋ง ๊ทธ๋ผ์๋ ๋ถ๊ตฌํ๊ณ ๊ธฐ์กด์ input๊ฐ์ ์ฐธ์กฐํ๋ ๊ฒ ๋ง์ผ๋ก๋ ํ์ต์ ๋์์ด ๋๋ค๊ณ ๋ ผ๋ฌธ์์๋ ๋งํ๋ค. ๋ค์ ๋ธ๋ก์ residual mapping์ด ์ด์ ๋ธ๋ก์ identity mapping์ ์ฐธ์กฐํ๋ ๊ฒ ๋ง์ผ๋ก๋ ํ์ต์ ์๋(?)์ด ์ ์ด์ง๋ค๊ณ ๋งํ๋ค.
shortcut connection == identity mapping?#
shortcut connection์ ์ ๋ ฅ๊ฐ์ ๋ค๋ก ๋๊ฒจ์ ๋ํด์ค๋ค. ์ด๊ฒ์๋ ์ข ๋ฅ๊ฐ ์๊ณ identity mapping์ 1๋ฒ์ผ๋ก ๊ทธ ์ข ๋ฅ์ค์ ํ๋๋ก ๋ณผ ์ ์์์ผ๋ก ์ ํํ๋ ์ฐจ์ด๊ฐ ์กด์ฌํ๋ค. input๊ณผ output์ dimension์ด ๋ฌ๋ผ์ง๋ฉด ๊ณ ๋ คํด์ผํ ๊ฒ์ด ๋ง์์ง๋ค.
Identity Shortcut Connection (ํต์ฌ): Identity Shortcut Connection์ ์ด์ ๋ ์ด์ด์ ์ถ๋ ฅ์ ํ์ฌ ๋ ์ด์ด์ ์ ๋ ฅ์ ์ง์ ๋ํด์ฃผ๋ ๋ฐฉ์์ .
Projection Shortcut Connection: Projection Shortcut Connection์ ์ด์ ๋ ์ด์ด์ ์ถ๋ ฅ์ ํ์ฌ ๋ ์ด์ด์ ์ ๋ ฅ์ ์ ํ ๋ณํ(projection)ํ์ฌ ํฌ๊ธฐ๋ ์ฐจ์์ ๋ง์ถ ํ ๋ํด์ฃผ๋ ๋ฐฉ์. ์ด๋ ์ฐจ์์ด ๋ค๋ฅธ ๊ฒฝ์ฐ์ ์ฌ์ฉ๋๋ฉฐ, ์ ํ ๋ณํ์ ํตํด ์ฐจ์ ์ผ์น๋ฅผ ์ ์งํ๊ณ ๊ทธ๋๋์ธํธ ํ๋ฆ์ ๋ณด์กดํ ์ ์๋ค.
Dimension Matching Shortcut Connection: Dimension Matching Shortcut Connection์ ์ด์ ๋ ์ด์ด์ ์ถ๋ ฅ๊ณผ ํ์ฌ ๋ ์ด์ด์ ์ ๋ ฅ์ ์ฐจ์์ด ๋ค๋ฅผ ๊ฒฝ์ฐ, ์ฐจ์์ ๋ง์ถ๊ธฐ ์ํด ์ถ๊ฐ์ ์ธ ์ฐ์ฐ์ ์ํํ๋ ๋ฐฉ์. ์ด๋ ์ฐจ์์ด ๋ค๋ฅธ ๊ฒฝ์ฐ์ ์ฌ์ฉ๋๋ฉฐ, ์ฐจ์์ ์ผ์น์์ผ ๊ทธ๋๋์ธํธ ํ๋ฆ์ ๋ณด์กดํ๊ณ ์ ๋ณด์ ์์ค์ ์ต์ํ.
Skip Connection: Skip Connection์ ์ด์ ๋ ์ด์ด์ ์ถ๋ ฅ์ ํ์ฌ ๋ ์ด์ด์ ์ ๋ ฅ์ผ๋ก ๋ฐ๋ก ์ ๋ฌํ๋ ๋ฐฉ์. Identity Shortcut Connection์ Skip Connection์ ํ ์ข ๋ฅ๋ก ๋ณผ ์ ์๋ค. Skip Connection์ ๋คํธ์ํฌ์ ์ฌ๋ฌ ๋ ์ด์ด๋ฅผ ๊ฑด๋๋ฐ์ด ๊ทธ๋๋์ธํธ ํ๋ฆ์ ๋ ์งง๊ฒ ๋ง๋ค์ด ์ค์ผ๋ก์จ ๊ทธ๋๋์ธํธ ์์ค ๋ฌธ์ ๋ฅผ ์ํ์ํค๊ณ , ์ ๋ณด์ ์์ค์ ์ค์ผ ์ ์๋ค.
์ด์ ์ ์ฐ๊ตฌ๋ค์์ shortcut connection์ โhighway networksโ์์ gating function์ผ๋ก ์ด์ฉ๋์๋ค๊ณ ํ๋ค. ์ด gates๋ค์ data-dependentํ๊ณ parameter๊ฐ ์์์ผ๋ฉฐ ๋ซํ ์ ์์๋ค๊ณ ํ๋ค. ํ์ง๋ง resnet์์์ shortcut connection์ parameter-free, never closed
๋ผ๊ณ ํ๋ค.
code#
# https://github.com/pytorch/vision/blob/main/torchvision/models/resnet.py
from functools import partial
from typing import Any, Callable, List, Optional, Type, Union
import numpy
import pandas
import torch
import torch.nn as nn
from torch import Tensor
def conv3x3(in_planes: int,
out_planes: int,
stride: int = 1,
groups:int = 1,
dilation: int = 1) -> nn.Conv2d:
"""3x3 convolution with padding"""
return nn.Conv2d(in_planes, out_planes,
kernel_size=3,
stride=stride,
padding=dilation,
groups=groups,
bias=False,
dilation=dilation,
)
def conv1x1(in_planes: int,
out_planes: int,
stride: int = 1,) -> nn.Conv2d:
"""1x1 convolution"""
return nn.Conv2d(in_planes, out_planes, kernel_size=1, stride=stride, bias=False)
class BasicBlock(nn.Module):
expansion: int = 1
def __init__(
self,
inplanes: int,
planes: int,
stride: int = 1,
downsample: Optional[nn.Module] = None,
groups: int = 1,
base_width: int = 64,
dilation: int = 1,
norm_layer: Optional[Callable[...,nn.Module]] = None,
) -> None:
super().__init__()
if norm_layer is None:
norm_layer = nn.BatchNorm2d
if groups != 1 or base_width != 64:
raise ValueError("BasicBlock only supports groups=1 and base_width=64")
if dilation > 1:
raise NotImplementedError("Dilation > 1 not supported in BasicBlock")
# Both self.conv1 and self.downsample layers downsample the input when stride != 1
self.conv1 = conv3x3(inplanes, planes, stride)
self.bn1 = norm_layer(planes)
self.relu = nn.ReLU(inplace=True)
self.conv2 = conv3x3(planes, planes)
self.bn2 = norm_layer(planes)
self.downsample = downsample
self.stride = stride
def forward(self, x: Tensor) -> Tensor:
identity = x
out = self.conv1(x)
out = self.bn1(out)
out = self.relu(out)
out = self.conv2(out)
out = self.bn2(out)
# residual function
if self.downsample is not None:
identity = self.downsample(x)
out += identity # identity mapping
out = self.relu(out)
return out
Implementation#
in training
he image is resized with its shorter side ran-domly sampled in for scale augmentation.A 224ร224 crop is randomly sampled from an image or its horizontal flip, with the per-pixel mean subtracted.
standard color augmentation
conv \(\to\) Batch Normalization \(\to\) non-linear activation f
plain ๋คํธ์ํฌ์์๋ ์ฌ์ฉ๋จ์ผ๋ก์จ ์คํ์์ฒด๊ฐ gradient vanishing problem ๋ณด๋ค๋ degradation problem์ ์ง์คํ๋๋ก ํจ.
ensures forward propagated signals to have non-zero variances.
์ ๋ ฅ ๋ฐ์ดํฐ์ ๋ถ์ฐ์ ์กฐ์ ํ์ฌ gradient์ ํฌ๊ธฐ๋ฅผ ์์ ํํ์ฌ gradient vanishing problem์ ์ํํ๋ฉฐ, ์์ ๋ณํ์๋ ๋ ๋ฏผ๊ฐํ ๊ฐ๊ฒ ํ ๋ชจ๋ธ์ ๋ง๋ค๊ณ , ๋น ๋ฅด๊ฒ ์๋ ดํ๋๋ก ๋๋ ์ญํ ์ ํ๋ค.
๋ํ backward propagated gradients(์ญ์ ํ๋ ์์คํจ์ ์ต์ํ ๊ฐ์ค์น ๋ฏธ๋ถ๊ฐ)๊ฐ ๊ฑด๊ฐํ๊ณ ์ ์ ํ ํฌ๊ธฐ๋ฅผ ์ ์งํ๋ ๋ฐ ๋์๋๋ค.
weitght initialization
SGD with mini-batch 256 size
0.1 lr with 10 error plateaus
60 x 10^4 iter
0.0001 weight decay, momentum 0.9
no dropout
in testing
standard 10-crop testing
fully convolutional form
average scores at multiple scales{224,256,384,480,640}
(A) zero-padding shortcuts are usedfor increasing dimensions, and all shortcuts are parameter-free
(B) projec-tion shortcuts are used for increasing dimensions, and othershortcuts are identity
ยฉ all shortcuts are projection
์์์ ๋ณด์ด๋ ABC๋ shortcut connection์ ์ด๋ป๊ฒ ๊ตฌ์ฑํ๋์ง๊ฐ ๋ค๋ฅด๊ณ , c๋ก ๊ฐ์๋ก ์ฑ๋ฅ์ ๋์์ก์ง๋ง, B๋ง์ผ๋ก๋ ์ ์๋ฏธํ ๋ฌธ์ ํด๊ฒฐ์ ๋ณด์์ผ๋ก ๋ชจ๋ shortcut์ด projection shortcut์ด ๋ ํ์๋ ์๋ค๊ณ ๋งํ๋ค. ํด๋น ๋ ผ๋ฌธ ์ดํ์ ๋์จ fishnet์ด๋ผ๋ ๋ ผ๋ฌธ์์๋ C์ ๋ฐฉ์์ ์ ๊ทน์ฑ์ฉํด์ resnet๋ณด๋ค ์ฑ๋ฅ์ ๋์๋ค.
Deeper Bottleneck Architectures#
18, 34 layers์์๋ 3x3,3x3๋ก 2๊ฐ์ conv layer๋ค์ ์์์ ๋ง๋ค์์๋ค๋ฉด, ๋ ๋์๊ฐ์ 50,101,152 layers๋ฅผ ์ํด์ 1x1,3x3,1x1 ๋ฅผ ํ๋์ ๋ธ๋ก์ผ๋ก ์ฌ์ฉํ๋ค. ์ด๊ฒ์ bottleneck block ์ด๋ผ๊ณ ๋ถ๋ฅธ๋ค.
(linear projection conv)1x1 ์ฒซ๋ฒ์งธ filter layer๋ input์ ์ฐจ์์ ์ค์ด๊ฑฐ๋ ๋๋ฆฌ๋๋ฐ(์ฐจ์์ ๋ง์ถ๋๋ฐ) ์ฌ์ฉ๋๋ค. ์ด๋ฅผ ํตํด ๊ณ์ฐ ๋น์ฉ์ ์ค์ด๊ณ , ๋ ์ ์์์ ํํฐ๋ฅผ ์ฌ์ฉํด์ ํน์ง์ ์ถ์ถํ๋ ๊ฒ์ด ๊ฐ๋ฅํ๋ค.์ด ๊ณผ์ ์์ ์ผ๋ถ ์ ๋ณด์ ์์ค์ด ๋ฐ์ํ ์๋ ์๋ค.
๋๋ฒ์งธ 3x3 filter layer๋ bottleneck
์ญํ ์ ์ค์ง์ ์ผ๋ก ํ๋ ๊ณต๊ฐ์ผ๋ก ์ฐจ์์ด ์ค์ด๋ ๋ค. ์ฐจ์์ด ์ค์ด๋ ๋ค๋ ๊ฒ์ feature ํน์ง์ ์ถ์ถํ๋ ๊ฒ์ด ์ ์ด์ง๋ค๋ ๊ฒ์ด๋ฉฐ, ๊ฐ์ค์น๊ฐ ํฐ feature์ ์ง์คํ๊ฒ ๋๋ค. ์ ์ฐจ์ ์ผ๋ก output size๊ฐ 112x112 \(\to\) 56x56 \(\to\) 28x28 \(\to\) 14x14 \(\to\) 7x7 ๋ก ์ค์ด๋ค๋ฉด์ cnn์ ๊ธฐ๋ณธ์ ์ธ ์ญํ (๊ณต๊ฐ์ ์ธ ํน์ง ํ์ต)์ ์ถฉ์คํ๊ฒ ๋๋ค.
๋ง์ง๋ง 1x1 filter layer๋ ์ค์ด๋ ์ฐจ์์ ๋ค์ ๋๋ ค์ฃผ๋ฉด์ ์ฐจ์์ ๋ณด์กดํ๋ค.
resnet50์ ๊ตฌ์กฐ๋๋ฅผ ์ฐพ์ ๊ฒ์ธ๋ฐ ๋ ผ๋ฌธ์์๋ stage๊ตฌ๋ถ์ด ์์๋๋ฐ ์ด๊ฒ์ ๊ตฌ๋ถ์ ํด์ batch norm๋ฑ์ ๋ค๋ฅด๊ฒ ์ฌ์ฉํ๊ณ ์๋ ๊ฒ์ผ๋ก ํํ๋๊ณ ์๋ค.
Code#
class Bottleneck(nn.Module):
expansion: int = 4
def __init__(
self,
inplanes: int,
planes: int,
stride: int = 1,
downsample: Optional[nn.Module] = None,
groups: int = 1,
base_width: int = 64,
dilation: int = 1,
norm_layer: Optional[Callable[..., nn.Module]] = None,
) -> None:
super().__init__()
if norm_layer is None:
norm_layer = nn.BatchNorm2d
width = int(planes * (base_width / 64.0)) * groups
self.conv1 = conv1x1(inplanes, width)
self.bn1 = norm_layer(width)
self.conv2 = conv3x3(width, width, stride, groups, dilation)
self.bn2 = norm_layer(width)
self.conv3 = conv1x1(width, planes * self.expansion)
self.bn3 = norm_layer(planes * self.expansion)
self.relu = nn.ReLU(inplace=True)
self.downsample = downsample
self.stride = stride
def forward(self, x: Tensor) -> Tensor:
identity = x
out = self.conv1(x)
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 is not None:
identity = self.downsample(x)
out += identity
out = self.relu(out)
return out
class ResNet(nn.Module):
def __init__(
self,
block: Type[Union[BasicBlock, Bottleneck]],
layers: List[int],
num_classes: int = 1000,
zero_init_residual: bool = False,
groups: int = 1,
width_per_group: int = 64,
replace_stride_with_dilation: Optional[List[bool]] = None,
norm_layer: Optional[Callable[..., nn.Module]] = None,
) -> None:
super().__init__()
# _log_api_usage_once(self)
if norm_layer is None:
norm_layer = nn.BatchNorm2d
self._norm_layer = norm_layer
self.inplanes = 64
self.dilation = 1
if replace_stride_with_dilation is None:
# each element in the tuple indicates if we should replace
# the 2x2 stride with a dilated convolution instead
replace_stride_with_dilation = [False, False, False]
if len(replace_stride_with_dilation) != 3:
raise ValueError(
"replace_stride_with_dilation should be None "
f"or a 3-element tuple, got {replace_stride_with_dilation}"
)
self.groups = groups
self.base_width = width_per_group
self.conv1 = nn.Conv2d(3, self.inplanes, kernel_size=7, stride=2, padding=3, bias=False)
self.bn1 = norm_layer(self.inplanes)
self.relu = nn.ReLU(inplace=True)
self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1)
self.layer1 = self._make_layer(block, 64, layers[0])
self.layer2 = self._make_layer(block, 128, layers[1], stride=2, dilate=replace_stride_with_dilation[0])
self.layer3 = self._make_layer(block, 256, layers[2], stride=2, dilate=replace_stride_with_dilation[1])
self.layer4 = self._make_layer(block, 512, layers[3], stride=2, dilate=replace_stride_with_dilation[2])
self.avgpool = nn.AdaptiveAvgPool2d((1, 1))
self.fc = nn.Linear(512 * block.expansion, num_classes)
for m in self.modules():
if isinstance(m, nn.Conv2d):
nn.init.kaiming_normal_(m.weight, mode="fan_out", nonlinearity="relu")
elif isinstance(m, (nn.BatchNorm2d, nn.GroupNorm)):
nn.init.constant_(m.weight, 1)
nn.init.constant_(m.bias, 0)
# Zero-initialize the last BN in each residual branch,
# so that the residual branch starts with zeros, and each residual block behaves like an identity.
# This improves the model by 0.2~0.3% according to https://arxiv.org/abs/1706.02677
if zero_init_residual:
for m in self.modules():
if isinstance(m, Bottleneck) and m.bn3.weight is not None:
nn.init.constant_(m.bn3.weight, 0) # type: ignore[arg-type]
elif isinstance(m, BasicBlock) and m.bn2.weight is not None:
nn.init.constant_(m.bn2.weight, 0) # type: ignore[arg-type]
def _make_layer(
self,
block: Type[Union[BasicBlock, Bottleneck]],
planes: int,
blocks: int,
stride: int = 1,
dilate: bool = False,
) -> nn.Sequential:
norm_layer = self._norm_layer
downsample = None
previous_dilation = self.dilation
if dilate:
self.dilation *= stride
stride = 1
if stride != 1 or self.inplanes != planes * block.expansion:
downsample = nn.Sequential(
conv1x1(self.inplanes, planes * block.expansion, stride),
norm_layer(planes * block.expansion),
)
layers = []
layers.append(
block(
self.inplanes, planes, stride, downsample, self.groups, self.base_width, previous_dilation, norm_layer
)
)
self.inplanes = planes * block.expansion
for _ in range(1, blocks):
layers.append(
block(
self.inplanes,
planes,
groups=self.groups,
base_width=self.base_width,
dilation=self.dilation,
norm_layer=norm_layer,
)
)
return nn.Sequential(*layers)
def _forward_impl(self, x: Tensor) -> Tensor:
# See note [TorchScript super()]
x = self.conv1(x)
x = self.bn1(x)
x = self.relu(x)
x = self.maxpool(x)
x = self.layer1(x)
x = self.layer2(x)
x = self.layer3(x)
x = self.layer4(x)
x = self.avgpool(x)
x = torch.flatten(x, 1)
x = self.fc(x)
return x
def forward(self, x: Tensor) -> Tensor:
return self._forward_impl(x)
temp= ResNet(BasicBlock, [2,2,2,2])
torchinfo#
from torchinfo import summary
summary(temp, input_size=(128,3,224,224))
==========================================================================================
Layer (type:depth-idx) Output Shape Param #
==========================================================================================
ResNet [128, 1000] --
โโConv2d: 1-1 [128, 64, 112, 112] 9,408
โโBatchNorm2d: 1-2 [128, 64, 112, 112] 128
โโReLU: 1-3 [128, 64, 112, 112] --
โโMaxPool2d: 1-4 [128, 64, 56, 56] --
โโSequential: 1-5 [128, 64, 56, 56] --
โ โโBasicBlock: 2-1 [128, 64, 56, 56] --
โ โ โโConv2d: 3-1 [128, 64, 56, 56] 36,864
โ โ โโBatchNorm2d: 3-2 [128, 64, 56, 56] 128
โ โ โโReLU: 3-3 [128, 64, 56, 56] --
โ โ โโConv2d: 3-4 [128, 64, 56, 56] 36,864
โ โ โโBatchNorm2d: 3-5 [128, 64, 56, 56] 128
โ โ โโReLU: 3-6 [128, 64, 56, 56] --
โ โโBasicBlock: 2-2 [128, 64, 56, 56] --
โ โ โโConv2d: 3-7 [128, 64, 56, 56] 36,864
โ โ โโBatchNorm2d: 3-8 [128, 64, 56, 56] 128
โ โ โโReLU: 3-9 [128, 64, 56, 56] --
โ โ โโConv2d: 3-10 [128, 64, 56, 56] 36,864
โ โ โโBatchNorm2d: 3-11 [128, 64, 56, 56] 128
โ โ โโReLU: 3-12 [128, 64, 56, 56] --
โโSequential: 1-6 [128, 128, 28, 28] --
โ โโBasicBlock: 2-3 [128, 128, 28, 28] --
โ โ โโConv2d: 3-13 [128, 128, 28, 28] 73,728
โ โ โโBatchNorm2d: 3-14 [128, 128, 28, 28] 256
โ โ โโReLU: 3-15 [128, 128, 28, 28] --
โ โ โโConv2d: 3-16 [128, 128, 28, 28] 147,456
โ โ โโBatchNorm2d: 3-17 [128, 128, 28, 28] 256
โ โ โโSequential: 3-18 [128, 128, 28, 28] 8,448
โ โ โโReLU: 3-19 [128, 128, 28, 28] --
โ โโBasicBlock: 2-4 [128, 128, 28, 28] --
โ โ โโConv2d: 3-20 [128, 128, 28, 28] 147,456
โ โ โโBatchNorm2d: 3-21 [128, 128, 28, 28] 256
โ โ โโReLU: 3-22 [128, 128, 28, 28] --
โ โ โโConv2d: 3-23 [128, 128, 28, 28] 147,456
โ โ โโBatchNorm2d: 3-24 [128, 128, 28, 28] 256
โ โ โโReLU: 3-25 [128, 128, 28, 28] --
โโSequential: 1-7 [128, 256, 14, 14] --
โ โโBasicBlock: 2-5 [128, 256, 14, 14] --
โ โ โโConv2d: 3-26 [128, 256, 14, 14] 294,912
โ โ โโBatchNorm2d: 3-27 [128, 256, 14, 14] 512
โ โ โโReLU: 3-28 [128, 256, 14, 14] --
โ โ โโConv2d: 3-29 [128, 256, 14, 14] 589,824
โ โ โโBatchNorm2d: 3-30 [128, 256, 14, 14] 512
โ โ โโSequential: 3-31 [128, 256, 14, 14] 33,280
โ โ โโReLU: 3-32 [128, 256, 14, 14] --
โ โโBasicBlock: 2-6 [128, 256, 14, 14] --
โ โ โโConv2d: 3-33 [128, 256, 14, 14] 589,824
โ โ โโBatchNorm2d: 3-34 [128, 256, 14, 14] 512
โ โ โโReLU: 3-35 [128, 256, 14, 14] --
โ โ โโConv2d: 3-36 [128, 256, 14, 14] 589,824
โ โ โโBatchNorm2d: 3-37 [128, 256, 14, 14] 512
โ โ โโReLU: 3-38 [128, 256, 14, 14] --
โโSequential: 1-8 [128, 512, 7, 7] --
โ โโBasicBlock: 2-7 [128, 512, 7, 7] --
โ โ โโConv2d: 3-39 [128, 512, 7, 7] 1,179,648
โ โ โโBatchNorm2d: 3-40 [128, 512, 7, 7] 1,024
โ โ โโReLU: 3-41 [128, 512, 7, 7] --
โ โ โโConv2d: 3-42 [128, 512, 7, 7] 2,359,296
โ โ โโBatchNorm2d: 3-43 [128, 512, 7, 7] 1,024
โ โ โโSequential: 3-44 [128, 512, 7, 7] 132,096
โ โ โโReLU: 3-45 [128, 512, 7, 7] --
โ โโBasicBlock: 2-8 [128, 512, 7, 7] --
โ โ โโConv2d: 3-46 [128, 512, 7, 7] 2,359,296
โ โ โโBatchNorm2d: 3-47 [128, 512, 7, 7] 1,024
โ โ โโReLU: 3-48 [128, 512, 7, 7] --
โ โ โโConv2d: 3-49 [128, 512, 7, 7] 2,359,296
โ โ โโBatchNorm2d: 3-50 [128, 512, 7, 7] 1,024
โ โ โโReLU: 3-51 [128, 512, 7, 7] --
โโAdaptiveAvgPool2d: 1-9 [128, 512, 1, 1] --
โโLinear: 1-10 [128, 1000] 513,000
==========================================================================================
Total params: 11,689,512
Trainable params: 11,689,512
Non-trainable params: 0
Total mult-adds (Units.GIGABYTES): 232.20
==========================================================================================
Input size (MB): 77.07
Forward/backward pass size (MB): 5087.67
Params size (MB): 46.76
Estimated Total Size (MB): 5211.49
==========================================================================================
torchinfo๋ ์์ฆ์ torchsummary, torchsummaryX๊ฐ update๋ฅผ ํ์ง์๋ ์ํฉ์์ ์ข์ ๋์์ด๋ค. ๋ฌผ๋ก ํ์ memory๋ฅผ ๊ณ์ฐํ๋๋ฐ ์๊ฐ์ด ๊ฝค๋ ๊ฑธ๋ฆฌ๋ ๊ฒ ๊ฐ์ง๋ง, GPU ๋ฉ๋ชจ๋ฆฌ๋ฅผ ๊ณ ๋ คํด์ ์ผ๋ง๋์ batch size๋ฅผ ๋ฏธ๋ฆฌ ์๊ฐํด๋ณด๋๋ฐ ์ข์ tool๋ก ๋ณด์ธ๋ค.
Netron#
# import torch.onnx
# params = temp.state_dict()
# dummy_data = torch.empty(1,3,224,224,dtype=torch.float32)
# torch.onnx.export(temp, dummy_data,'onnx_test.onnx')
================ Diagnostic Run torch.onnx.export version 2.0.0 ================
verbose: False, log level: Level.ERROR
======================= 0 NONE 0 NOTE 0 WARNING 0 ERROR ========================
์ฌ๊ธฐ์ W๋ weight๋ฅผ ๋ํ๋ด๊ณ
Result#
ILSVRCโ15#
2011 XRCE
2012 AlexNet(8 cnn 3 fc)
2013 ZFNet(alexnet๋ณด๋ค ์ข๋ ๊น๊ฒ, ๋ ์์ ํํฐ)
2014 GoogleNet(์ฌ๋ฌ ํํฐ ๋ณ๋ น ์ ์ฉ inception module) - VGG(๊ฐ๋จ alex ๋ณด๋ค deep)
2015 ResNet
2016 GoogleNet-v4
SENet(squeeze and excitation module channel๊ฐ์ ์์กด์ฑ ๊ฐ์กฐ)
Reference#
source - title