일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- KoBERT
- 서버
- 스파르타코딩클럽
- Phrase Sets
- Flask
- UbuntuServer
- 모델적응
- keras
- Custom Classes
- ubuntu
- jquery
- FastAPI
- AWS
- Transfer_Learning
- 과일종류예측
- Google Speech To Text
- model
- Kaggle
- 가장쉽게배우는머신러닝
- Django
- 우분투2004
- 4주차
- EC2
- Model Adaptations
- Ubuntu2004
- mnist
- Linux
- html
- KoBART
- ajax
- Today
- Total
영웅은 죽지 않는다
딥러닝 때려 부수기 - 합성곱 신경망(CNN), 캐글 데이터를 이용한 수화 알파벳(MNIST) 분류 모델 본문
딥러닝 때려 부수기 - 합성곱 신경망(CNN), 캐글 데이터를 이용한 수화 알파벳(MNIST) 분류 모델
YoungWoong Park 2021. 8. 18. 19:40
합성곱 신경망(Convolutional Neural Networks, CNN)이란
합성곱은 컴퓨터 비전(CV) 분야에서 주로 쓰이는 이미지 처리 방식입니다.
입력데이터와 필터의 각각의 요소를 서로 곱한 후 모두 더하면 출력값이 됩니다. 이를 합성곱이라고 하는데,
연구원들은 이 합성곱을 어떻게 딥러닝에 활용할 수 있을지 고민하다가 1998년 Yann LeCun 교수가 합성곱에 관한 논문을 발표하게 됩니다.
위와 같이 합성곱을 이용한 신경망 디자인을 합성곱 신경망(CNN) 이라고 명칭하였고, 특히 이미지 처리에서 높은 성능을 보이는 것을 증명했습니다. 이후 딥러닝의 전성기를 맞이했으며 얼굴 인식, 사물 인식 등 다양한 곳에 널리 사용되기 시작하였습니다.
합성곱 계층 (Convolutional Layer)
- Fliter, Strides, Padding
아래와 같이 5*5 크기의 입력값에서 3*3 크기의 특성맵(Feature Map)을 뽑아낼 수 있습니다. 필터(Fliter 또는 Kernel)를 한 칸씩 오른쪽으로 움직이며 합성곱 연산을 하는데, 이 때 이동하는 간격을 스트라이드(Stride)라고 합니다.
그러나 위와 같이 연산을 하게 되면 합성곱 연산의 출력값인 Feature Map의 크기가 줄어듭니다. 이런 현상을 방지하기 위해 패딩(Padding 또는 Margin)을 주어, Stride가 1일 때 입력값과 출력값의 크기를 같게끔 만들어 줄 수 있습니다.
위에서는 1개의 필터를 사용하여 연산을 하였고, 여러개의 필터를 이용하여 합성곱 신경망의 성능을 높일 수 있습니다.
위 그림에서 각각의 입력과 출력은 다음과 같습니다.
- 입력 이미지 크기 : (10, 10, 3)
- 필터의 크기 : (4, 4, 3)
- 필터의 개수 : 2
- 출력 특성 맵(Feature Map)의 크기 : (10, 10, 2)
CNN의 구성
- 합성곱 신경망은 합성곱 계층(Convolutional Layer)와 완전연결 계층(Dense Layer)를 함께 사용합니다.
- 합성곱 계층 + 활성화 함수 + 풀링을 반복하며 점점 작아지지만, 핵심적인 특성들을 뽑아내게 되고, 풀링 계층(Pooling Layer)은 Feature Map의 중요 부분을 추출하여 저장하는 역할을 하게 됩니다.
- 풀링 계층에서 Pooling의 방법에 따라 Max Pooling과 Average Pooling 두 가지가 있는데, 말 그대로 최대값으로 하느냐, 평균값으로 하느냐로 나뉩니다.
- 풀링 계층을 지나 완전연결 계층(Dense Layer, Fully Connected)와 연결이 되어야 하나, 풀링을 통과한 Feature Map은 2차원이고 Dense Layer는 1차원이기에 연산이 불가능합니다. 따라서 평탄화 계층(Flatten Layer)를 사용해 2차원을 1차원으로 펼치는 작업을 진행해야 합니다.
- 평탄화 계층을 통과하여 행렬 곱셈을 한 후, 동일하게 Dense(Fully Connected) + Activation function의 반복을 통해 노드의 개수를 점점 축소시키다가, 마지막에 Softmax 활성화 함수를 통과하고 출력층으로 결과를 나타내게 됩니다.
CNN의 활용
- 물체 인식(Object Detection) : Computer Vision에서 가장 중요한 기술이며, 이미지에서 정확히 사물 또는 사람을 인식하는 것을 뜻합니다.
- YOLO (You Only Look Once) : 현재 V5 버전까지 나왔으며, 속도가 빠르고 정확도가 높은 CV 알고리즘 입니다.
- 이미지 분할(Segmentation) : 각 Object에 속한 픽셀들을 분리하는 것을 나타냅니다. 이는 나누는 기준이 디테일 할수록 정교화된 성능을 가져야 합니다. 특정 기준(Class)에 따라 분리하거나 인물을 Focusing할 때 사용됩니다.
CNN의 종류
- AlexNet (2012) : 컴퓨터 비전 분야의 큰 대회인 ILSVRC에서 2012년도에 우승한 모델입니다. AlexNet은 의미있는 성능을 낸 첫 번째 합성곱 신경망이고, Dropout과 Image Augmentation 기법을 효과적으로 적용한 딥러닝 기법입니다.
- VGGNet (2014) : 이는 parameter의 개수가 많고 모델의 깊이가 deep한 기술로 유명합니다. 또한 전이 학습 등을 통해서 가장 먼저 테스트하는 모델로도 잘 알려져 있습니다. 간단한 방법론으로 좋은 성적을 낼 수 있습니다.
- GoogLeNet(=Inception V3) (2015) : 위에서 언급한 LeCun 교수가 구글에서 개발한 합성곱 신경망 구조입니다. 이는 VGGNet보다 구조가 복잡하여 널리 쓰이진 않았지만, 하나의 계층에서도 다양한 종류의 filter, pooling을 도입함으로써 개별 계층을 두텁게 확장시킬 수 있다는 아이디어인 Inception module 구조 면에서 주목을 받았습니다. 이 1*1 합성곱 계층 아이디어로 조금 더 다양한 특성을 모델이 찾을 수 있게 하고, 신경망이 깊어졌음에도 사용된 parameter를 절반까지 줄일 수 있었습니다.
- ResNet (2015) : AlexNet이 제안된 이후로 합성곱 신경망의 계층은 점점 깊어졌는데, 층이 깊어질수록 역전파(Backpropagation)의 기울기가 점점 사라져서 학습이 잘 되지 않는 문제가 발생했습니다. 이 문제점을 해결하고자 ResNet은 기울기가 잘 흐를 수 있도록 지름길(Shortcut)을 만들어주는 Residual block을 제시했습니다.
오른쪽 그림에서 F(x) = y - x 꼴로 표현해 입력과 출력 간의 학습 차이를 학습하도록 설계하였으며, 예를 들어 위조 지폐범은 더욱 정밀하게 지폐를 위조하고 경찰은 더욱 정밀하게 위조된 지폐를 찾아내는 작업에 비유할 수 있습니다. 이는 합성곱 신경망 모델 성능에 큰 영향을 끼치게 되었습니다.
CNN을 이용한 Sign Language MNIST(수화 알파벳)
지난 주차에서 수화 MNIST를 단순 딥러닝 모델(MLP)을 이용해 분류해본 것과 다르게, 이번에는 CNN을 이용해 분류해 보겠습니다.
- 데이터셋 다운로드 : https://www.kaggle.com/datamunge/sign-language-mnist
캐글에 로그인 후 내 프로필 -> Account -> API -> Create New API Token 에서 kaggle.json을 다운로드하여 username과 key값을 입력합니다.
import os
os.environ['KAGGLE_USERNAME'] = 'username' # username
os.environ['KAGGLE_KEY'] = 'key' # key
그 후 MNIST 데이터셋의 API Command를 Copy하여 데이터셋을 불러온 후 압축을 풉니다.
!kaggle datasets download -d datamunge/sign-language-mnist
!unzip sign-language-mnist.zip
- 필요한 패키지 Import
from tensorflow.keras.models import Model
from tensorflow.keras.layers import Input, Dense, Conv2D, MaxPooling2D, Flatten, Dropout
from tensorflow.keras.optimizers import Adam, SGD
from tensorflow.keras.preprocessing.image import ImageDataGenerator
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.preprocessing import OneHotEncoder
여기서 data augmentation을 위해 keras에서 ImageDataGenerator 모듈을 추가로 가져옵니다.
- 데이터셋 Load
train_df = pd.read_csv('sign_mnist_train.csv')
test_df = pd.read_csv('sign_mnist_test.csv')
print(train_df.head())
print(test_df.head())
- Label 분포 확인하기
plt.figure(figsize=(16, 10))
sns.countplot(train_df['label'])
plt.show()
여기서 예외처리가 발생하는데, A(0) ~ Z(25)의 총 26개의 알파벳 중에서
J(9)와 Z(25)는 움직이는 동작이 들어가므로 제외하게 됩니다.
그렇기에 총 24개의 Label이 있음을 확인할 수 있습니다. 골고루 분포되어 있네요.
여기까지는 이전에 MLP로 해결했던 코드와 같습니다.
- 전처리 : 입력과 출력 나누기
train_df = train_df.astype(np.float32)
x_train = train_df.drop(columns=['label'], axis=1).values
x_train = x_train.reshape((-1, 28, 28, 1)) # 3차원 데이터로 만듬 (배치사이즈, 28x28, gray scale 이미지)
y_train = train_df[['label']].values
test_df = test_df.astype(np.float32)
x_test = test_df.drop(columns=['label'], axis=1).values
x_test = x_test.reshape((-1, 28, 28, 1))
y_test = test_df[['label']].values
print(x_train.shape, y_train.shape)
print(x_test.shape, y_test.shape)
여기서는 이전과 다르게 3차원으로 나열해야 하기에 reshape하는 과정이 추가됩니다. -1은 데이터셋의 크기(batch size), (28, 28)은 이미지의 크기, 1은 gray scale을 의미합니다.
- 전처리 : 데이터 미리보기
index = 1
plt.title(str(y_train[index]))
plt.imshow(x_train[index].reshape((28, 28)), cmap='gray')
plt.show()
2차원의 이미지형태로 변환하게 되면 왼쪽 그림과 같이 나오게 됩니다. 6번(G)의 알파벳은 왼쪽 그림과 같은 이미지로 나타나야 하는 것을 알 수 있겠네요.
- 전처리 : Ont-Hot-Encoding
encoder = OneHotEncoder()
y_train = encoder.fit_transform(y_train).toarray()
y_test = encoder.fit_transform(y_test).toarray()
print(y_train.shape)
하지만 6이라는 값을 컴퓨터가 이해하기 힘들기 때문에, One-Hot-Encoding을 진행하여 크기가 1에서 24로 증가하였습니다.
( 6 ㅡ> [0,0,0,0,0,1,0,0,0,...,0] )
- 전처리 : 일반화하기
train_image_datagen = ImageDataGenerator(
rescale=1./255, # 일반화
)
# generator 과정
train_datagen = train_image_datagen.flow(
x=x_train,
y=y_train,
batch_size=256,
shuffle=True # 27,455개의 데이터 순서를 섞어줌
)
test_image_datagen = ImageDataGenerator(
rescale=1./255
)
test_datagen = test_image_datagen.flow(
x=x_test,
y=y_test,
batch_size=256,
shuffle=False # test data에서는 랜덤성을 주지 않는다.
)
index = 1
preview_img = train_datagen.__getitem__(0)[0][index]
preview_label = train_datagen.__getitem__(0)[1][index]
plt.imshow(preview_img.reshape((28, 28)))
plt.title(str(preview_label))
plt.show()
이미지 데이터는 픽셀이 0~255 사이의 정수로 되어 있기에, 이것을 255로 나누어 0~1 사이의 소수점 데이터(float32)로 바꾸고 일반화를 시키는 과정이 필요합니다. 여기서는 MLP 방법때와는 다르게 ImageDataGenerator()를 사용해 일반화를 해보았습니다.
shuffle하는 과정은 train 데이터에서만 진행하였는데, 이 과정을 넣게 되면 성능 향상에 도움이 된다고 합니다.
그 이후 __getitem__이라는 keras의 메서드를 통해 이미지를 나타내게 되며, 나타나는 이미지는 train 데이터가 shuffle되었기 때문에 실행할 때마다 다른 이미지가 나오게 됩니다.
- 네트워크 구성하기
input = Input(shape=(28, 28, 1))
hidden = Conv2D(filters=32, kernel_size=3, strides=1, padding='same', activation='relu')(input)
hidden = MaxPooling2D(pool_size=2, strides=2)(hidden)
hidden = Conv2D(filters=64, kernel_size=3, strides=1, padding='same', activation='relu')(hidden)
hidden = MaxPooling2D(pool_size=2, strides=2)(hidden)
hidden = Conv2D(filters=32, kernel_size=3, strides=1, padding='same', activation='relu')(hidden)
hidden = MaxPooling2D(pool_size=2, strides=2)(hidden)
hidden = Flatten()(hidden) # 1차원으로 펼쳐줌 (3*3*32=288)
hidden = Dense(512, activation='relu')(hidden)
hidden = Dropout(rate=0.3)(hidden) # overfitting을 피하기 위해 dropout, 30%읜 노드를 랜덤으로 탈락시킨다.
output = Dense(24, activation='softmax')(hidden)
model = Model(inputs=input, outputs=output)
model.compile(loss='categorical_crossentropy', optimizer=Adam(learning_rate=0.001), metrics=['acc'])
model.summary()
# parameter 개수가 deep neural networks보다 훨씬 줄어듬, dense layer에서 많은 부분을 차지함
hidden layer의 깊이가 굉장히 깊습니다. Convolution 2D 값을 넣어 CNN을 구성한 후, MaxPooling하여 차원을 줄여갑니다. 이 과정을 필터의 개수만을 변경해주어 반복해주고, Flatten()을 통해 1차원으로 변경해주어 Dense Layer와 맞춰줍니다.
그 후 overfitting을 피하기 위해 30%의 노드를 랜덤으로 dropout시킵니다. 다중 회귀이기에 activation function은 softmax, loss function은 categorical_crossentropy를 사용하고 정확도를 출력하는 모델을 만듭니다.
출력 결과를 보면, total parameter의 개수가 deep nuural networks보다 훨씬 줄어든 것을 알 수 있습니다. 그러나 Dense Layer에서 parameter가 많은 부분을 차지하는 것을 알 수 있는데, Dense Layer를 줄일 수록 parameter를 줄일 수 있을 것 같습니다.
- 학습시키기
history = model.fit(
train_datagen,
validation_data=test_datagen, # 검증 데이터를 넣어주면 한 epoch이 끝날때마다 자동으로 검증
epochs=20 # epochs 복수형으로 쓰기!
)
val_acc가 95%에 육박하는 것을 볼 수 있고, 이는 MLP에 비해 더 높은 성능을 보이게 됩니다.
이를 시각적으로 나타내면 아래와 같습니다.
- 학습 결과 그래프 보기
fig, axes = plt.subplots(1, 2, figsize=(20, 6))
axes[0].plot(history.history['loss'])
axes[0].plot(history.history['val_loss'])
axes[1].plot(history.history['acc'])
axes[1].plot(history.history['val_acc'])
- 이미지 증강기법 사용해보기
train_image_datagen = ImageDataGenerator( # image augmentation
rescale=1./255, # 일반화
rotation_range=10, # 랜덤하게 이미지를 회전 (단위: 도, 0-180)
zoom_range=0.1, # 랜덤하게 이미지 확대 (%)
width_shift_range=0.1, # 랜덤하게 이미지를 수평으로 이동 (%)
height_shift_range=0.1, # 랜덤하게 이미지를 수직으로 이동 (%)
)
train_datagen = train_image_datagen.flow(
x=x_train,
y=y_train,
batch_size=256,
shuffle=True
)
test_image_datagen = ImageDataGenerator(
rescale=1./255
)
test_datagen = test_image_datagen.flow(
x=x_test,
y=y_test,
batch_size=256,
shuffle=False
)
index = 1
preview_img = train_datagen.__getitem__(0)[0][index]
preview_label = train_datagen.__getitem__(0)[1][index]
plt.imshow(preview_img.reshape((28, 28)))
plt.title(str(preview_label))
plt.show()
- 네트워크 구성하기
input = Input(shape=(28, 28, 1))
hidden = Conv2D(filters=32, kernel_size=3, strides=1, padding='same', activation='relu')(input)
hidden = MaxPooling2D(pool_size=2, strides=2)(hidden)
hidden = Conv2D(filters=64, kernel_size=3, strides=1, padding='same', activation='relu')(hidden)
hidden = MaxPooling2D(pool_size=2, strides=2)(hidden)
hidden = Conv2D(filters=32, kernel_size=3, strides=1, padding='same', activation='relu')(hidden)
hidden = MaxPooling2D(pool_size=2, strides=2)(hidden)
hidden = Flatten()(hidden)
hidden = Dense(512, activation='relu')(hidden)
hidden = Dropout(rate=0.3)(hidden)
output = Dense(24, activation='softmax')(hidden)
model = Model(inputs=input, outputs=output)
model.compile(loss='categorical_crossentropy', optimizer=Adam(learning_rate=0.001), metrics=['acc'])
model.summary()
- 학습시키기
history = model.fit(
train_datagen,
validation_data=test_datagen, # 검증 데이터를 넣어주면 한 epoch이 끝날때마다 자동으로 검증
epochs=20 # epochs 복수형으로 쓰기!
위 결과에서, 이미지 증강 기법을 이용했을 때 더욱 성능이 좋은 결과를 낸다는 것을 알 수 있습니다.
이를 시각화하면 아래와 같습니다.
- 학습 결과 그래프 보기
fig, axes = plt.subplots(1, 2, figsize=(20, 6))
axes[0].plot(history.history['loss'])
axes[0].plot(history.history['val_loss'])
axes[1].plot(history.history['acc'])
axes[1].plot(history.history['val_acc'])
val_loss값이 더 낮고, acc는 더 높은 것을 볼 수 있습니다. epoch의 개수를 늘려가면서 실험해보면 좋을 것 같습니다.
'Programming > Machine Learning' 카테고리의 다른 글
[Google Cloud] Speech-To-Text의 모델 적응 기능 활용해 STT 성능 높이기 (0) | 2022.10.27 |
---|---|
딥러닝 때려 부수기 - 전이 학습(TL), 캐글 데이터를 이용한 과일 종류 예측 모델 (0) | 2021.08.19 |
딥러닝 때려 부수기 - 신경망 개념&스킬, 캐글 데이터를 이용한 XOR문제 해결 (0) | 2021.08.14 |
머신러닝 때려 부수기 - 캐글 데이터를 이용한 논리 회귀(Logistic Regression), 전처리(Preprocessing) (0) | 2021.08.01 |
머신러닝 때려 부수기 - 캐글 데이터를 이용한 선형 회귀 (Linear Regression) (0) | 2021.07.23 |