- Kaiming He: 중국인 AI 전문가
1️⃣ResNet
❓ResNet
- 2015sus ILSVRC에서 우승을 차지한 모델
- Human error보다 낮은 성능을 보인 최초의 Image Classification Network
- shortcut skip connection을 도입한 모델
- 여러 버전 중 최대 152개의 layer를 쌓아 본격적으로 very deep neural network를 가능하게 한 모델
❗️기존 의문점
- Layer를 추가해 complexity(=depth상승) 높이는 게 항상 정답일까?
- 다른 방향으로 F의 범위를 넓힐 가능성은 없는가?
- 기존의 모델을 포함하면서 확장을 해나간다면, target function에 가까워질 확률이 커질 것
✔예시
F1: VGG11로 만들 수 있는 모델의 집합
- 만약 F1 바깥에 task를 완벽하게 수행하는 'f*'가 있다면?
F2: VGG13로 만들 수 있는 모델의 집합
- VGG13은 11보다 복잡도가 높으므로 범위가 늘어남
- target function에 조금 더 가까워진 것일 ‘수도’ 있다
F3: VGG16로 만들 수 있는 모델의 집합
- 만족스러운 모델 target function을 얻었다면, F3 안에 'f*'가 속하게 된 상태
- 원하는 target function을 포함하는 집합을 만드는 것이 딥러닝
🚫문제점
기존의 모델을 포함하면서 확장시킨다면 target function을 찾을 수 있을 것이다
- 이 개념을 구체화한 도구가 shorcut skip connection
f(x) = x
- 위와 같이 identity function라면, 초기 상태에서 기존 모델 출력에 변화가 없음
- identity function,항등함수: 입출력이 같은 함수
- 추가된 레이어가 identity function에서부터 학습이 시작되면, 기존 모델을 포함한 상태에서 표현 범위가 더 넓어지는 효과를 가진다
f(x) +x = x- f(x)가 0이어야 identity function이 되지 않을까
- 입력(정의역) x -> 출력(공역) x
- 입력 x가 x가 될 확률보다는 f(x)=0에 가까워질 확률이 더 크다
- path1: 기존 레이어를 통과한 값
- path2: 기존 레이어를 스킵하고 입력값을 그대로 내보내는 path
- 두 결과를 더해서 다음 relu로 보내주는 것
- layer 추가할 때 block을 추가할 때, identity function을 추가한 것
- 기존 모델을 그대로 유지한 상태에서 표현 범위가 넓어질 확률이 큼
❗️Residual Block
Residual = 잔차 = f(x) = H(x) - x
- path1: Convolutinal /ReLU / Convolutinal
- path2: identity function
- path1 + path2를 activation(ReLU)
- 같은 ReLU를 쓰지 않는 것이 중요
code
class ResidualBlock(nn.Module):
def __init__(self, in_channels, out_channels):
super(ResidualBlock, slef).__init__()
self.conv_path = nn.Sequential(
nn.Conv2d(in_channels=in_channels, out_channels=out_channels,
kernel_size=3, padding=1),
nn.ReLU(),
nn.Conv2d(in_channels=in_channels, out_channels=out_channels,
kernel_size=3, padding=1)
)
self.skip_path = nn.Identity()
#위의 Relu와는 다른 오브젝트로 선언해주어야 한다
self.out_act = nn.ReLU()
def forward(self, x):
x_conv = self.conv_path(x)
x_skip = self.skip_path(x)
x = x_conv + x_skip
x = self.out_act(x)
return x
- 원소별 더하기를 하기 위해 채널 수랑 데이터 수가 같아야 함
- pooling layer를 사용하지 않고 padding=1 , 이미지 값은 절반이 되고, 채널 수는 두 배가 되려면 stride는 2가 되어야한다
❗️Residual Block 에서의 Output Shape과 Parameter 수
- parameter 수 : (kernel size * kernel channel 수 * kernel 수) + bias
❗️Residual Block
class ResidualBlcokDown(nn.Module):
def __init__(self, in_channels, out_channels):
super(ResidualBlockDown, self).__init__()
self.path1 = nn.Sequential(
#왜 stride가 2??
nn.Conv2d(in_channels=in_channels, out_channels=out_channels, kernel_size=3, padding=1, stride=2),
nn.ReLU(),
nn.Conv2dd(in_channels=in_channels,
out_channels=out_channels, kernel_size=3, padding=1, stride=1)
)
self.path2 = nn.Conv2d(in_channels=in_channels, out_channels=out_channels, kernel_size=1, padding=1, stride=2))
self.out_act = nn.ReLU()
def forward(self, x):
path1 = self.path1(x)
path2 = self.path2(x)
x = path1 + path2
x = self.out_act(x)
return x
-
Residual Block Down
- path1에서 이미지 사이즈는 절반 / path2(skip path)에서 nn.Identity를 쓸 수 없음
- path1에서의 결과와 path2(skip path)에서 결과를 더해줄 때 shape 맞지 않음
-
skip path에서는 (1x1) kernel을 사용하는 nn.Conv2d를 사용해 pooling과 비슷한 효과를 얻을 수 있음
❗️ResNet34 Model 구현하기
class ResNet34(nn.Module):
def __init__(self, in_channels):
super(ResNet34, self).__init__()
self.conv1 = nn.Sequential(nn.Conv2d(in_channels=in_channels, out_channels=64, kernel_size=7, stride=2, padding=3),
nn.ReLU())
self.conv2_x = nn.Sequential(nn.MaxPool2d(kernel_size=3, stride=2, padding=1),
ResidualBlock(in_channels=64, out_channel=64),
ResidualBlock(in_channels=64, out_channel=64),
ResidualBlock(in_channels=64, out_channel=64)
)
self.conv3_x = nn.Sequential(
ResidualBlockDown(in_channels=64, out_channels=128),
ResidualBlock(in_channels=128, out_channel=128),
ResidualBlock(in_channels=128, out_channel=128),
ResidualBlock(in_channels=128, out_channel=128)
)
self.conv4_x = nn.Sequential(
ResidualBlockDown(in_channels=128, out_channels=256),
ResidualBlock(in_channels=256, out_channel=256),
ResidualBlock(in_channels=256, out_channel=256),
ResidualBlock(in_channels=256, out_channel=256),
ResidualBlock(in_channels=256, out_channel=256),
ResidualBlock(in_channels=256, out_channel=256),
)
self.conv5_x = nn.Sequential(
ResidualBlockDown(in_channels=256, out_channels=512),
ResidualBlock(in_channels=512, out_channel=512),
ResidualBlock(in_channels=512, out_channel=512)
)
self.out_act = nn.Sequential(nn.AvgPool2d(kernel_size=7, stride=1, padding=0),
nn.AvgPool2d(kernel_size=7, stride=7),
nn.Linear(in_features=512*1*1 , out_features=1000),
)
def forward(self, x):
x = self.conv1(x)
x = self.conv2_x(x)
x = self.conv3_x(x)
x = self.conv4_x(x)
x = self.conv5_x(x)
x = x.view(x.shape[0],-1)
x = self.out_act(x)
return x
'AI데이터 엔지니어, 새싹' 카테고리의 다른 글
73th_12_18(Mon)_Web Scrapping (1) | 2023.12.18 |
---|---|
62th_121_Overfitting (0) | 2023.12.04 |
60th_11_29(Wed)_GoogLeNet (0) | 2023.11.29 |
59th_11_28(Tue)_VGG Implementation (0) | 2023.11.29 |
58th_11_27(Mon)_Convolutinal Neural Networks (1) | 2023.11.27 |