일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
- kt디지털인재
- Tableau
- 논문 리뷰
- object detection
- 딥러닝
- 데이터 증강
- Data Augmentation
- k-fold cross validation
- VGG16
- SRCNN
- 데이터과학
- 파이썬 구현
- 태블로
- VGG
- 태블로 실습
- 데이터 분석
- Semantic Segmentation
- super resolution
- 머신러닝
- 데이터 시각화
- ResNet
- 개 vs 고양이
- cnn
- 장학프로그램
- Computer Vision
- data analysis
- kt희망나눔재단
- sparse coding
- 논문리뷰
- Deep Learning
- Today
- Total
기억의 기록(나의 기기)
[ResNet] 예제를 통한 설명 및 함수형 API 구현하기 본문
ResNet - 설명과 함수형 API 구현
안녕하세요, 데이터과학을 전공하고 있는 황경하입니다.
오늘은 ResNet에 대한 이야기를 해보려 합니다. 이전 포스팅에 VGG16에 대한 이야기를 다뤘었는데, 최근에 이런 pretrained model들에 대해 공부하고 있어서 ResNet도 설명해 보겠습니다. 바로 가시죠!
코드: https://colab.research.google.com/drive/1vOegGrsifZbSzOH_wZvPWbER-bbjAFMW?usp=sharing
ResNet?
ResNet이라는 단어 자체가 생소하신 분들도 계실 것 같습니다. ResNet을 좀 풀어보자면, Residual Network(잔차 신경망)라고 생각하시면 됩니다. 뒤에서 왜 잔차라는 말이 쓰였는지 알아보겠습니다. 먼저, ResNet은 2015년에 ILSVRC 대회에서 우승했던 모델로 층이 무려 152층이 됩니다. 바로 직전 해였던 2014년에 우승했던 GoogleNet은 22층, 준우승했던 VGG16은 16층임을 감안한다면, 이는 정말 혁명이었죠. 또한, 인간의 눈이 5.1%의 오류율을 보이는데, ResNet은 3.6%의 오류율로 최초로 인공지능이 인간을 뛰어넘은 성능을 보여주었습니다. 그래서 더 많은 각광을 받게 되었죠. 그런데, 우리는 모두 층이 깊어진다면, Vanishing Gradient (기울기 소실) 문제가 발생함을 알고 있습니다. 활성화 함수를 Leaky Relu로 설정하더라도 층이 152층이라면, 이를 피할 수 없죠. 그렇다면, ResNet은 어떻게 이 문제를 극복했을까요?
Residual Connection (잔차 연결)
바로 이 잔차 연결층 덕분입니다. 간단히 말하면, 중간 중간에 하나의 과정을 뛰어넘은 어떤 값을 더해주는 것인데요. 그림을 보며 이해해 보겠습니다.
왼쪽부터 화살표를 따라가 보시면, 중간중간에 화살표가 합쳐지는 구간이 있음을 알 수 있습니다. 이 방법으로 기울기 소실 문제를 피할 수 있었던 것인데요, 어떠한 원리로 그런 것일까요? 이는 이 아키텍처의 역전파를 생각해 보면 이해할 수 있습니다. 우리가 층이 매우 깊은 신경망의 역전파를 생각해 보면, 합성함수의 미분에 따라서 앞의 층의 미분값이 작아지면 뒤에 층에 곱해지는 기울기 값이 작아져 뒤에 층은 거의 학습이 되지 않는 기울기 소실 문제가 발생할 수 있습니다. 이는 층이 깊어질수록 더 심하겠죠, 그런데, 위 그림의 구조처럼 앞에 값을 뒤에 더해주는 형식이라면 어떨까요? 역전파를 따라가 보면, 앞에 층의 미분값이 그대로 가는 가는 것이 아니라 한 번 저장된 후 그 값을 더해주게 됩니다. 이로 인해, 죽어가던 미분값이 다시 살아나게 되죠. 이와 같은 과정을 계속 반복하다 보면, 층을 깊게 쌓아도 기울기 소실 문제가 발생하지 않게 됩니다. 미리 모델의 구조를 plot_model을 통해 살펴보죠.
층이 너무 깊어서 모두 표현할 수는 없고, 하나의 블록만 표현하였습니다.
Data Load for Test ResNet
ResNet에 대한 설명을 했으니 본격적으로 모델링에 들어가죠. 먼저, ResNet 또한 VGG16과 같이 ImageNet 데이터셋으로 사전 학습된 모델입니다. 이에 VGG16 포스팅에서 사용했던 데이터 4장을 다운로드하고 정확도를 살펴보겠습니다.
- 모델을 불러오고, image를 zip파일로 다운로드한 후에 unzip 합니다.
from tensorflow import keras
import gdown, zipfile, os
import matplotlib.pyplot as plt
import matplotlib.image as image
resnet = keras.applications.resnet.ResNet152()
if not os.path.isfile('VGG16_test.zip'):
gdown.download(id='11LZAFSFVtDsdKdLcFR9E-MaDoar6C3R5', output='VGG16_test.zip')
VGG16_test = zipfile.ZipFile('VGG16_test.zip')
VGG16_test.extractall()
VGG16_test.close()
with open('imagenet_classes.txt') as f:
labels = [line.strip() for line in f.readlines()]
img = image.imread("golden.jpg")
plt.imshow(img)
plt.axis('off')
plt.show()
print(img.shape)
|
- 데이터를 ResNet에 맞게 (224,224) shape으로 바꾸어주고, 전처리한 후 모델에 넣어 예측 확률 분포를 out에 저장합니다.
import tensorflow as tf
import numpy as np
img = tf.image.resize(img,(224,224))
out=resnet(tf.keras.applications.resnet.preprocess_input(tf.expand_dims(img,axis=0)))
|
- 모델이 예상한 top5의 라벨과 예측 확률을 출력합니다.
top_5 = np.argsort(-out[0])[:5]
for idx in top_5:
print(f"{labels[idx]} : {out[0,idx]*100:.3f}%")
|
img를 로드할 때 Gold Retriever을 불러왔으니 잘 출력된 결과라고 할 수 있겠네요. 이제 이 ResNet을 직접 함수형 API로 구현해 봄으로써 잔차 연결에 대한 자세한 이해를 해보겠습니다.
Create ResNet from scratch
152층을 직접 다 구현하기는 너무 오랜 시간이 걸리고, 잔차 연결을 이해하기 위해선 3개 정도의 block이면 충분합니다. for문을 이용하여 3개의 block을 만들어줍니다. 기본적인 구조는 BatchNormalization → Relu → Convolution → .. → MaxPooling → GlobalAveragePooling → Affine → Sigmoid입니다.
- 개 vs 고양이 이미지는 (180,180,3) shape으로 dataset을 로드할 때 가지고 왔으니, input shape은 (180,180,3)이 됩니다. (데이터에 대한 설명은 이 글을 참고해주세요.)
- Fully Connected Layer에서는 기존 CNN 구현 코드에서 사용했던 Flatten층이 아닌 Global Average Pooling층을 사용해 줍니다. 효과는 똑같이 1차원으로 변경해 주는 것이지만, Global Average Pooling층은 채널별 평균으로 대체합니다.
plot_model을 통해 시각화하면 아래와 같습니다.
from keras.layers import BatchNormalization, Activation
from keras.layers import Conv2D, MaxPooling2D, add, GlobalAveragePooling2D, Dense
from keras.utils import plot_model
def build_model(input_shape, num_out):
inputs = keras.Input(shape=input_shape)
x = Conv2D(32, 3, activation="relu")(inputs)
for size in [32, 64, 128]:
residual = x
x = BatchNormalization()(x)
x = Activation("relu")(x)
x = Conv2D(size, 3, activation="relu", padding="same", use_bias=False)(x)
x = BatchNormalization()(x)
x = Activation("relu")(x)
x = Conv2D(size, 3, activation="relu", padding="same", use_bias=False)(x)
x = MaxPooling2D(2, padding="same")(x)
residual = Conv2D(size, 1, strides=2, use_bias=False)(residual)
x = add([x, residual])
x = GlobalAveragePooling2D()(x)
outputs = Dense(1, activation="sigmoid")(x)
model = keras.Model(inputs=inputs, outputs=outputs)
return model
model = build_model((180,180,3), 1)
plot_model(model, show_shapes = True, show_layer_activations = True)
|
보시다시피 위의 층의 결괏값이 저장되었다가 아래층에서 합쳐지는 것을 볼 수 있습니다. 이러한 아이디어로 기울기 소실 문제를 피할 수 있어 층을 깊게 쌓을 수 있는 ResNet이었습니다.
마치며..
오늘은 ResNet을 배워봤습니다. VGG16, ResNet, GoogleNet이 딥러닝 모델의 3 대장이라고 불렸다고 하던데, 두 개나 포스팅했네요. 다음에는 GoogleNet을 다뤄보려 했지만, GoogleNet을 개선한 Inception 모델이 있어 Inception 모델에 대한 설명으로 돌아오려 합니다! 오늘도 읽어주셔서 감사합니다.
'딥러닝' 카테고리의 다른 글
[Sparse Coding] 희소 코딩 기법에 대한 그림을 통한 쉬운 설명 (0) | 2024.07.12 |
---|---|
[Data Preprocessing] Duplicate Cleaner 소개 및 사용법 (1) | 2024.07.03 |
[VGG16] 예제를 통한 transfer learning, fine-tuning 설명 (0) | 2024.06.24 |
[Data Augmentation] 예제를 통한 데이터 증강 효과 설명 (0) | 2024.06.24 |
[Data Augmentation] 강아지, 고양이, MNIST 예시를 통한 설명 (0) | 2024.06.24 |