본문으로 바로가기

출처: https://www.slideshare.net/yongho/ss-79607172

 

문제의 유형에 따라 적절한 아키텍처

1. 스냅샷성 데이터 (Snapshot) -> CNN

이미지, 영상, 바둑

- AlexNet(구형), VGG(인기 많고 많이 씀, 믿고 쓰는 허브솔트같은 느낌), GoogLeNet(처음에는 인기 없다가 뒤에 Inception 버전 업되고 좀 쓰임), ResNet(레이어 매우 많고, 최근 자주 쓰임)

https://medium.com/analytics-vidhya/vggnet-convolutional-network-for-classification-and-detection-3543aaf61699

 

- 조각을 보고, 패턴을 익히고, 점점 멀리서 조합을 본다

 

2. 시퀀스성 데이터 (Sequence) -> RNN, LSTM

음성, 언어, 주식가격, 맥락

 

 


CNN

특정 패턴이 있는지 박스로 훑으며 특징을 찾는다

- 위아래선, 좌우선, 대각선, 질감, 모양 등의 여러가지 "조각" 필터로 해당 패턴이 그림 위에 있는지 확인

- 간단한 필터들이 쌓여가며 엄청나게 복잡한 필터를 스스로 만들어나가는 것

- 레고처럼 부품을 조립하여 더 복잡한 부품을 만드는 것으로 예시를 많이 든다.

 

용어 정리

1) Convolutional Layer

- Image와 Filter(주로 3*3)을 곱하고 더한 값으로 Feature Map을 만들 수 있다. 

- 이 때 FilterKernel이라고 부르기도 하며, 3*3을 Filter size 또는 Kernel size라고 한다.

- 곱하고 더하여 만들어진 숫자 matrix를 Feature Map이라고 부른다.

- Filter가 몇 개의 set로 구성되어있는지에 따라 Feature Map의 depth가 달라진다.

- ex) Image(1024,1024) & Filter(3,3)*64 -> Feature Map(1022, 1022, 64)

- 깊이를 더 깊이해서 특징을 더 잘 추출하도록 만드는 것이다.

 

- Image를 Filter로 스캔하며 도장처럼 찍을 때 Stride(보폭)을 지정할 수도 있다. 주로 한 칸씩 이동하며, 용량을 많이 줄이고 싶다면 두 칸으로 설정하기도 한다.

- Stride를 한 칸씩 이동시키며 학습시키는 것을 Window sliding이라고 한다.

 

https://wikidocs.net/64066

- Filter로 한 칸씩 이동하며 스캔하다보면 제일 모서리에 있는 픽셀들은 적은 Filter와 곱해지는 수가 적어 적게 가중치가 주어진다. 이것이 padding='valid'인 경우이며, 모서리에 있는 픽셀에도 동일하게 가중치를 주고 싶다면 또는 이미지의 사이즈를 유지하고 싶다면 Zero-padding을 설정해야 한다. 이는 padding='same'으로 설정 가능하다.

 

 

2) Pooling Layer

사이즈를 줄여가며 더욱 추상화해 나가는 과정

https://nico-curti.github.io/NumPyNet/NumPyNet/layers/maxpool_layer.html

- 이미지의 용량을 더 줄이고 싶거나 중요한(선명한) 정보만 활용하고 싶을 때, Pooling layer로 이미지를 절반으로 줄일 수 있다.

- 주로, Max-Pooling을 사용하지만, 경우에 따라 Average-Pooling으로 설정하기도 한다.

 

https://velog.io/@seongguk/AI-CNNConvolutional-Neural-Network-학습

 

 

3) FC 

Fully-Connected Neural Network = Dense

 

 


CNN 코드

0. Import Library

import tensorflow as tf
from tensorflow.keras import datasets, utils
from tensorflow.keras import models, layers, activations, initializers, losses, optimizers, metrics

import numpy as np
import pandas as pd

import os
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'

 

1. Prepare train & test data

'''Train & Test split'''
(train_data, train_label), (test_data, test_label) = datasets.mnist.load_data()

 

2. Data Preprocessing

'''Normalization'''
# CNN에서는 [28행 x 28열]을 [1행 x 784열]로 펼쳐주지 않아도 됨
# 다만 이미지의 채널에 해당하는 차원을 마지막 차원으로 새로이 추가해줍니다. (MNIST는 흑백 이미지이므로 채널 수를 1로 지정)
# Color Image라면 (60000, 28, 28, 3)
train_data = train_data.reshape(60000, 28, 28, 1) / 255.0 
test_data = test_data.reshape(10000, 28, 28, 1) / 255.0

'''One-hot Encoding'''
train_label = utils.to_categorical(train_label) # 0~9 -> one-hot vector
test_label = utils.to_categorical(test_label) # 0~9 -> one-hot vector

 

3. Build the model & Set the criterion

CNN 진행 4단계
1. Convolution (+ Batch Normalization)
2. Activation
3. (Max) Pooling
   * Repeat 1~3 for adding more hidden layers.
4. Connect the network to a fully-connected network (+ Dropout)
   * This fully-connected network makes the model can result in classification.

 

model = models.Sequential()

# Dnese layer라면
# model.add(layers.Flatten())
# model.add(layers.Dense(units=512, activation='relu'))
# model.add(layers.Dense(input_dim=28*28, units=512, activation='relu'))

'''1-Conv layer'''
model.add(layers.Conv2D(32, (3, 3), input_shape=(28, 28, 1))) # Filter의 갯수, Filter의 shape, X data point의 shape (이미지 "1장"에 해당)
# layers.Flatten 쓰면 마찬가지로 input_shape 입력할 필요 없음
# strides=1, padding='valid', kernel_initializer='glorot_uniform', kernel_regulizer=None (l1, l2) : default
model.add(layers.BatchNormalization())   # CNN에서는 성능을 많이 높임
model.add(layers.Activation('relu'))

'''2-Conv layer'''
model.add(layers.Conv2D(32, (3, 3)))
model.add(layers.BatchNormalization())
model.add(layers.Activation('relu'))

'''3-MaxPooling layer'''
model.add(layers.MaxPooling2D(pool_size=(2, 2)))   # 해도 되고 안해도 됨 (실험해보면서 성능 확인)

'''4-Conv layer'''
model.add(layers.Conv2D(64, (3, 3)))
model.add(layers.BatchNormalization())
model.add(layers.Activation('relu'))

'''5-Conv layer'''
model.add(layers.Conv2D(64, (3, 3)))
model.add(layers.BatchNormalization())
model.add(layers.Activation('relu'))

'''6-MaxPooling layer'''
model.add(layers.MaxPooling2D(pool_size=(2, 2)))

# 여기까지 pretrained model -> 전이학습(TL)


# Fully-connected network (전연결층)
# 보통 2단정도 쌓아주는 편
model.add(layers.Flatten()) # 전체 차원을 펼쳐줍니다. (1개의 행으로)
model.add(layers.Dense(512))
model.add(layers.BatchNormalization())
model.add(layers.Activation('relu'))
model.add(layers.Dropout(rate=0.3))

model.add(layers.Dense(10, activation='softmax'))

 

 

* 모델을 더 가시적으로 보기 위해

# model.summary()

# !pip install visualkeras==0.0.2
import visualkeras # https://github.com/paulgavrikov/visualkeras
from PIL import ImageFont

visualkeras.layered_view(model, legend=True, font=ImageFont.truetype("arial.ttf", 13)) # font is optional

 

 

model.compile

model.compile(optimizer=optimizers.Adam(), 
              loss=losses.categorical_crossentropy, 
              metrics=[metrics.categorical_accuracy])

 


+ Data Augmentation

from tensorflow.keras.preprocessing.image import ImageDataGenerator
# Albumentations (fast augmentations based on highly-optimized OpenCV library) <- 써봐도 좋음
# @ https://goo.gl/GYKK4y & https://j.mp/2J7Enej

gen_train = ImageDataGenerator(rotation_range=8,        # Degree range for random rotations.
                               shear_range=0.3,         # Shear Intensity (Shear angle in counter-clockwise direction in degrees)
                               width_shift_range=0.08,  # Fraction of total width
                               height_shift_range=0.08, # Fraction of total height
                               zoom_range=0.08)         # Range for random zoom ([lower, upper] = [1-zoom_range, 1+zoom_range])

train_generator = gen_train.flow(train_data, train_label, batch_size=64) # flow_from_directory() 활용 가능
# flow_from_directory() 메서드를 사용하면 이미지 데이터 폴더로부터 direct하게 데이터를 가져와 training에 활용 가능

gen_test = ImageDataGenerator() # Test data에는 Augmentation을 적용하지 않습니다.
test_generator = gen_test.flow(test_data, test_label, batch_size=64)

 

- flow_from_directory() 활용

# 참고용 (flow_from_directory() 사용한다면 폴더에서 이미지 불러올 수 있음)
from tensorflow.keras.preprocessing.image import ImageDataGenerator
 
# All images will be rescaled by 1./255
train_datagen = ImageDataGenerator(rescale = 1./255)
 
# Flow training images in batches of 128 using train_datagen generator
train_generator = train_datagen.flow_from_directory(
        'data/horse-or-human/',  # This is the source directory for training images
        target_size=(300, 300),  # 자동으로 resize
        batch_size=128,
        class_mode='binary')     # Since we use binary_crossentropy loss, we need binary labels
# y 클래스가 없음 (폴더에 따라 자동으로 0,1 클래스로 설정됨)

# Available "class_mode" : 
# - "categorical" : 2D one-hot encoded labels (사전에 one-hot encoding 진행한 경우)
# - "binary" : 1D binary labels
# - "sparse" : 1D integer labels (사전에 one-hot encoding 하지 않은 경우)

 

 

4. Train the model

# Data Augmentation하지 않는다면
# history = model.fit(train_data, train_label, batch_size=100, epochs=15, validation_data=(val_data, val_label))   
# validation data가 따로 있다면, 없다면 validation_split=0.2

history = model.fit(train_generator, 
                    steps_per_epoch=60000 // 64, # == number of batches
                    epochs=5, 
                    validation_data=test_generator, 
                    validation_steps=10000 // 64)

 

 

5. Test the model

result = model.evaluate(test_data, test_label, batch_size=100)
print('loss (cross-entropy) :', result[0])
print('test accuracy :', result[1])

 

모델 예측 결과값 vs 실제 정답 비교

# 모델 예측 결과
print(np.argmax(model.predict(test_data[:10]), axis=1))

# 실제 정답
print(np.argmax(test_label[:10], axis=1))

 

6. Visualize the result

val_acc = history.history['val_categorical_accuracy']
acc = history.history['categorical_accuracy']

import numpy as np
import matplotlib.pyplot as plt

x_len = np.arange(len(acc))
plt.plot(x_len, acc, marker='.', c='blue', label="Train-set Acc.")
plt.plot(x_len, val_acc, marker='.', c='red', label="Validation-set Acc.")

plt.legend(loc='lower right')
plt.grid()
plt.xlabel('epoch')
plt.ylabel('Accuracy')
plt.show()