본문 바로가기
AI데이터 엔지니어, 새싹

61th_11_30(Thu)_ResNet

by Leetora 2023. 11. 30.
  • 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을 포함하는 집합을 만드는 것이 딥러닝

🚫문제점
스크린샷 2023-11-30 오전 10.26.00.jpg
스크린샷 2023-11-30 오전 10.20.08.jpg
기존의 모델을 포함하면서 확장시킨다면 target function을 찾을 수 있을 것이다

  • 이 개념을 구체화한 도구가 shorcut skip connection

f(x) = x

  • 위와 같이 identity function라면, 초기 상태에서 기존 모델 출력에 변화가 없음
    • identity function,항등함수: 입출력이 같은 함수
  • 추가된 레이어가 identity function에서부터 학습이 시작되면, 기존 모델을 포함한 상태에서 표현 범위가 더 넓어지는 효과를 가진다
  • 스크린샷 2023-11-30 오전 10.26.31.jpg
    f(x) +x = x
  • f(x)가 0이어야 identity function이 되지 않을까
  • 입력(정의역) x -> 출력(공역) x
  • 입력 x가 x가 될 확률보다는 f(x)=0에 가까워질 확률이 더 크다
    • path1: 기존 레이어를 통과한 값
    • path2: 기존 레이어를 스킵하고 입력값을 그대로 내보내는 path
    • 두 결과를 더해서 다음 relu로 보내주는 것

스크린샷 2023-11-30 오전 10.35.46.jpg

  • 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 수
스크린샷 2023-11-30 오후 6.32.57.jpg

  • 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