다시 이음

딥러닝(10) - Convolutional Neural Networks(합성곱 신경망) 본문

AI 일별 공부 정리

딥러닝(10) - Convolutional Neural Networks(합성곱 신경망)

Taeho(Damon) 2021. 11. 1. 23:33

안녕하세요.

 

오늘은 자연어 처리를 지나 이미지 처리에 관한 신경망구조를 살펴보려고 합니다.

 

 

Convolutional Neural Networks

 

합성곱 신경망(Convolutional neural network, CNN)은 시각적 영상을 분석하는 데 사용되는 다층의 피드-포워드적인 인공신경망의 한 종류입니다.

 

사실 우리는 딥러닝을 배우면서 처음에 Fully Connected Layer를 통해서 이미지를 구분하는 방법에 대해서 알아본 적이 있습니다.

 

하지만, 사진 데이터로 전연결(FC, Fully Connected) 신경망을 학습시켜야 할 경우에, 3차원 사진 데이터를 1차원으로 평면화시켜야 합니다.

 

즉, Fully Connected Layer만으로 구성된 인공 신경망의 입력 데이터는 1차원(배열) 형태로 한정됩니다.

 

이렇게 사진 데이터를 평면화 시키는 과정에서 공간 정보가 손실될 수밖에 없습니다.

 

결과적으로 이미지 공간 정보 유실로 인한 정보 부족으로 인공 신경망이 특징을 추출 및 학습이 비효율적이고 정확도를 높이는데 한계가 있습니다.

 

이미지의 공간 정보를 유지한 상태로 학습이 가능한 모델이 바로 CNN(Convolutional Neural Network)입니다.

 

 

CNN(Convolutional Neural Network)은 이미지의 공간 정보를 유지하면서 인접 이미지와의 특징을 효과적으로 인식하고 강조하는 방식으로 이미지의 특징을 추출하는 부분이미지를 분류하는 부분으로 구성됩니다.

 

특징 추출 영역은 Filter를 사용하여 공유 파라미터 수를 최소화하면서 이미지의 특징을 찾는 합성곱층(Convolution layer)와 특징을 강화하고 모으는 풀링층(Pooling layer)로 구성됩니다.

 

1) CNN의 구조

 

- 합성곱층(Convolution layer)

합성곱 연산 과정

  • Filter(kernel) : 정사각 행렬로 이루어져있는 가중치들의 집합입니다.
  • Feature Map : 입력 데이터에 Filter를 연산하여 만들어진 합성곱의 합입니다.
  • Stride : 필터는 입력 데이터를 지정한 간격으로 순회하면서 합성곱을 계산하는 데 이 간격을 stride라고 합니다.
  • channel : 데이터의 차원, 컬러 이미지는 3차원, 흑백이미지는 1차원으로 이루어져 있습니다. 그외에도 합성곱층에서 n개의 필터가 있다면 출력데이터의 채널은 n개입니다.
  • Padding : 입력데이터에 filter가 적용되어 연산될 때 출력데이터(Feature Map)의 크기는 입력데이터 보다 작을 수 밖에 없습니다. 이렇게 되면 가장자리에 있는 입력데이터들은 연산되는 횟수가 적어 손실이 일어날 수도 있기때문에 외곽에 0으로 채워진 값들을 채워넣음으로써 정보 손실을 막을 수 있습니다.

 

- Pooling Layer

 

풀링 레이어는 컨볼류션 레이어의 출력 데이터를 입력으로 받아서 출력 데이터(Activation Map)의 크기를 줄이거나 특정 데이터를 강조하는 용도로 사용됩니다.

 

  • Max Pooling : 정사각 행렬의 특정 영역 안에 값의 최댓값만을 출력값으로 가진다.
  • Average Pooling : 정사각 행렬의 특정 영역의 평균을 구해서 출력값으로 가진다.

 

Pooling 시에 행렬의 크기는 줄어들지만, 채널의 수는 줄어들지 않습니다.

 

 

2) Convolution 레이어 출력 데이터 크기

출력 데이터 크기 수식

 

3) CNN의 구조(전체)

CNN의 전체 구조

그림을 보면서 하나씩 설명을 해보자면 위에서 살펴본대로 합성곱층(Convolution)과 Pooling층이 번갈아 나오면서 이미지의 특징을 추출하고, 뒤의 Fully Connected Layer를 통해 이미지를 분류하는 과정으로 이루어져 있습니다.

 

요즘에는 Layer를 많이 쌓음으로써 확실히 성능의 효과가 있다는 걸 확인할 수 있어서 더욱더 네트워크 layer를 늘리려는 움직임이 많다고 합니다.

 

이렇게 layer를 많이 쌓고 싶은 만큼 Filter(kernel)의 사이즈는 점점 축소하고 있는 추세인데요. 그 이유는 필터커널의 사이즈가 크면 그만큼 이미지의 사이즈가 금방 축소되기 때문에 네트워크의 깊이를 충분히 깊게 만들기 불가능하기 때문입니다.

 

 

4) Transfer Learning(전이학습)

 

저번에 배웠던 사전학습과 관련이 있는 방법입니다.

 

위에서 CNN의 구조를 볼 때 이미지의 특징을 추출하는 과정과 분류하는 과정이 따로 있다고 했는데요.

 

여기서 이미지의 특징을 추출하는 과정은 높은 정확도를 위해서는 최대한 많은 데이터를 사용하는 게 좋습니다.

 

그렇기 때문에 미리 사전학습이 되어있는 모델을 사용하여 분류하는 과정만 따로 붙여주어 사용하는 방식입니다.

 

 

전이학습을 사용할 때에 주의해야 할 점으로는 다운받아 사용하는 모델에는 가중치 업데이트가 일어나지 않도록 Freeze해주는 것이 좋습니다. 왜냐하면 동결해주지 않을 시 역전파 시에 해당 모델까지 업데이트가 진행되어 엄청난 시간이 소모되기 때문입니다.

 

주로 ResNet50, VGG, GoogLeNet 과같은 모델이 있습니다.

 

 

5) CNN 구성해보기

 

- 전이학습을 사용하지 않았을 때

import numpy as np
import tensorflow as tf
from tensorflow.keras.applications.resnet50 import ResNet50
from tensorflow.keras.preprocessing import image
from tensorflow.keras.applications.resnet50 import preprocess_input, decode_predictions
from tensorflow.keras.layers import Dense, GlobalAveragePooling2D
from tensorflow.keras.models import Model
from tensorflow.python.keras.models import Sequential
from tensorflow.python.keras.layers import Dense
from keras.preprocessing.image import ImageDataGenerator

#이미지 데이터 불러오기
train_dataset = tf.keras.preprocessing.image_dataset_from_directory("파일경로", 
                                                                    labels='inferred', 
                                                                    label_mode='categorical',
                                                                    batch_size=32,
                                                                    image_size=(224, 224))

val_dataset = tf.keras.preprocessing.image_dataset_from_directory("파일경로", 
                                                                  labels='inferred', 
                                                                  label_mode='categorical',
                                                                  batch_size=32,
                                                                  image_size=(224, 224))

#convolution,pooling 층 구성하기
num_classes = 2

model = tf.keras.Sequential([
  tf.keras.layers.experimental.preprocessing.Rescaling(1./255),
  tf.keras.layers.Conv2D(32, 3, activation='relu'),
  tf.keras.layers.MaxPooling2D(),
  tf.keras.layers.Conv2D(32, 3, activation='relu'),
  tf.keras.layers.MaxPooling2D(),
  tf.keras.layers.Conv2D(32, 3, activation='relu'),
  tf.keras.layers.MaxPooling2D(),
  tf.keras.layers.Flatten(),
  tf.keras.layers.Dense(128, activation='relu'),
  tf.keras.layers.Dense(num_classes)
])

# layer 확인하기
model.summary()

#모델 compile
model.compile(
  optimizer='adam',
  loss=tf.losses.BinaryCrossentropy(from_logits=True),
  metrics=['accuracy'])

#모델 학습하기
model.fit(
  train_dataset,
  validation_data=val_dataset,
  epochs=10
)

# 정확도 확인
test_loss, test_acc = model.evaluate(val_dataset, verbose=2)

 

- 전이학습을 사용(ResNet)

# 모델 제작
model = Sequential()
model.add(ResNet50(include_top = False, weights='imagenet'))
model.add(GlobalAveragePooling2D()) #2차원 데이터로 변환
model.add(Dense(1024, activation = 'relu'))
model.add(Dense(2, activation = 'sigmoid'))

# 이미 학습된 영역은 학습하지 않겠다고 설정하는 옵션 
model.layers[0].trainable = False

#compile
model.compile(optimizer = 'adam', loss = 'binary_crossentropy', metrics = 'accuracy')

model.fit(train_dataset,
  validation_data=val_dataset,
  epochs=10
)