목차
데이터 증강
[문제] CNN으로 개선되었긴 하지만 여전히 트레이닝 정확도가 검증 정확도보다 더 높음 ➡ 과적합 문제 남아있음
[해결책] 정제 데이터가 더 나은 예시를 제공
데이터세트의 다양성이 모델의 일반화에 도움이 됨
구현하기 전 읽어 볼 지식
데이터 증강 DATA AUGMENTATION
모델이 새로운 데이터를 살펴볼 때 좀 더 강력한 성능을 발휘하도록 가르치기 위해 우리는 데이터세트에서 크기와 편차를 프로그래밍 방식으로 늘리는 것
컴퓨터 비전 머신 러닝 모델에선 데이터를 증강하여 추가 예시를 제공 가능
- 다수의 머신 러닝 프레임워크는 이미 자체적으로 사진 수정을 위한 도구를 보유
Ex) 색조 변경
이렇게 증강을 통해
- 크기 증가: 모델이 트레이닝 중에 더 많은 이미지를 학습가능
- 편차 증가: 모델이 중요하지 않은 특징을 무시하고 분류에서 정말로 중요한 특징만 선택하도록 하여 더 나은 일반화가 가능
목표
- ASL 데이터세트 증강
- 증강된 데이터를 사용하여 개선된 모델 트레이닝
- 제대로 트레이닝된 모델을 디스크에 저장하여 배포에 사용
데이터 준비
수화 데이터세트는 csv 형식으로 되어있음.
- 상단에 레이블이 있는 행과 열로 이루어진 그리드
- 데이터를 로드하여 작업하기 위해 데이터 로드 및 조작을 위한 고성능 툴인 Pandas라는 라이브러리를 사용
- CSV 파일은 DataFrame이라는 형식으로 읽음
import pandas as pd
Pandas에는 csv 파일을 예상하고 DataFrame을 반환하는 read_csv 메서드가 존재
- 이를 통해 데이터를 불러 올 것임
👀 정답 코드 보기
# Load in our data from CSV files
train_df = pd.read_csv("data/asl_data/sign_mnist_train.csv")
valid_df = pd.read_csv("data/asl_data/sign_mnist_valid.csv")
레이블 추출
트레이닝 및 검증 레이블을 y_train 및 y_valid 변수에 저장하는 법
1️⃣ 해당 변수를 생성
2️⃣ 다음 레이블이 더 이상 필요없는 데이터프레임 원본에서 레이블을 삭제
👀 정답 코드 보기
# label 이라는 열이 정답열임
y_train = train_df['label']
y_valid = valid_df['label']
del train_df['label']
del valid_df['label']
이미지 추출
트레이닝 및 검증 이미지를 x_train 및 x_valid 변수에 저장
👀 정답 코드 보기
x_train = train_df.values
x_valid = valid_df.values
레이블 분류
keras.utils.to_categorical
메서드를 사용하여 여기에 인코딩할 값과 값을 인코딩할 범주 수를 전달해 범주 인코딩keras를 가져와 범주 수(24)를 설정해둠
import tensorflow.keras as keras
num_classes = 24
이 다음 코드를 짜봐라
- Categorically encode y_train and y_valid
👀 정답 코드 보기
# TODO: Categorically encode y_train and y_valid.
y_train = keras.utils.to_categorical(y_train, num_classes)
y_valid = keras.utils.to_categorical(y_valid, num_classes)
이미지데이터 정규화
데이터의 픽셀 값이 현재처럼 0~255가 아닌 0~1의 부동 소수점 값이 되어야함
보기전에 코드 직접 쳐보고 정말 정답 확인용으로 봐라
👀 정답 코드 보기
x_train = x_train / 255
x_valid = x_valid / 255
❤ 여기까진 MNIST 모델 구현과 같다 ❤
CNN을 위한 이미지 변환 reshaping
현재 데이터 셋의 상태를 확인해보자
x_train.shape, x_valid.shape
# ((27455, 784), (7172, 784))
현재 데이터 셋의 특징
- 서로 가까운 픽셀(위치 정보)에 대한 모든 정보를 가지고 있지 않음
- feature를 탐지하는 컨볼루션(convolution)(합성곱 계층) 적용 불가
[변환]
컨볼루션 적용을 위해 데이터 셋을 28x28픽셀 형식으로 변경
➡ 컨볼루션에서 인접한 픽셀끼리 그룹을 짓고 중요한 feature들을 탐지가능
[모델의 첫번 째 컨볼루션 레이어]
자세한 설명 보러가기
- 이미지의 높이
- 이미지 너비
- 채널의 숫자
우리의 이미지는 Grayscale이기 때문에 채널은 1의 값을 갖음
즉,
(27455, 784) ➡ (27455, 28, 28, 1)
(이미지 개수, 픽셀 개수) ➡ (이미지 개수, 특징맵 가로, 특징맵 세로, 채널 수)
동일하게 유지하기를 원하는 모든 차원에 대해 reshape을 진행하는 경우,
‘-1’ 을 통해 표현할 수 있으므로 다음과 같이 구성
x_train = x_train.reshape(-1,28,28,1)
x_valid = x_valid.reshape(-1,28,28,1)
바뀐 데이터 확인
x_train.shape
# (27455, 28, 28, 1)
x_valid.shape
# (7172, 28, 28, 1)
x_train.shape, x_valid.shape
# ((27455, 28, 28, 1), (7172, 28, 28, 1))
합성곱 모델 생성
아래의 그림을 구현할 것임
필요한건 줄테니 계층을 하나하나 구현해 보자!
- padding=”same” 이다
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import (
Dense,
Conv2D,
MaxPool2D,
Flatten,
Dropout,
BatchNormalization,
)
먼저 인스턴스 부터 생성해라
👀 정답 코드 보기
model = Sequential()
model.add(Conv2D(75, (3, 3), strides=1, padding="same", activation="relu", input_shape=(28, 28, 1)))
model.add(BatchNormalization())
model.add(MaxPool2D((2, 2), strides=2, padding="same"))
model.add(Conv2D(50, (3, 3), strides=1, padding="same", activation="relu"))
model.add(Dropout(0.2))
model.add(BatchNormalization())
model.add(MaxPool2D((2, 2), strides=2, padding="same"))
model.add(Conv2D(25, (3, 3), strides=1, padding="same", activation="relu"))
model.add(BatchNormalization())
model.add(MaxPool2D((2, 2), strides=2, padding="same"))
model.add(Flatten())
model.add(Dense(units=512, activation="relu"))
model.add(Dropout(0.3))
model.add(Dense(units=num_classes, activation="softmax"))
❤ 여기까진 CNN 모델 구현과 같다 ❤
데이터 증강
모델을 컴파일하기 전에 데이터 증강을 설정 필요
Keras
의 ImageDataGenerator
: 이미지 증강 클래스
- 이 클래스는 데이터 증강을 위한 일련의 옵션을 허용
- 과정 후반부에서 적합한 증강 전략을 선택 필요
from tensorflow.keras.preprocessing.image import ImageDataGenerator
옵션
featurewise_center
: Boolean. 입력 평균을 데이터 집합 위에 0으로 설정samplewise_center
: Boolean. 각 표본 평균을 0으로 설정featurewise_std_normalization
: Boolean. 입력을 데이터 집합(특징맵)의 표준으로 나눔samplewise_std_normalization
: Boolean. 각 입력을 표준으로 나눔zca_epsilon
: ZCA 미백용 엡실론. 기본값은 1e-6zca_whitening
: Boolean. ZCA 화이트닝rotation_range
: Int. 무작위 회전에 대한 도(ㅇ) 단위 범위width_shift_range
: Float, 이미지를 수평으로 임의로 이동(전체 너비의 대칭)height_shift_range
: Float, 이미지를 수직으로 임의로 이동(전체 높이의 대칭)brightness_range
: Tuple or list of two floats,밝기 이동 값 선택 범위shear_range
: Float, 전단 강도(도 단위로 시계 반대 방향으로 전단 각도)zoom_range
: Float or [lower, upper], 무작위 확대/축소 범위입니다. 부동 소수인 경우, [하위, 상한] = [1-dynam_range, 1+dynam_range].channel_shift_range
: Float, 무작위 채널 이동 범위fill_mode
: One of {“constant”, “nearest”, “reflect” or “wrap”}, “nearest”가 기본- 지정된 모드에 따라 입력 경계를 벗어나는 점이 채워짐
- constant’: kkkkkkkk[abcd]kkkkkkkk (cval=k)
- ‘nearest’: aaaaaaaa[abcd]dddddddd
- ‘reflect’: abcddcba[abcd]dcbaabcd
- ‘wrap’: abcdabcd[abcd]abcdabcd
cval
: Float or Int, 경계를 벗어나는 점에 사용되는 값horizontal_flip
: Boolean, 입력을 수평으로 랜덤하게 뒤집음vertical_flip
: Boolean. 입력을 수직으로 랜덤하게 뒤집음rescale
: 데이터에 제공된 값을 곱함preprocessing_function
: 각 입력에 적용될 함수 -> 이미지 크기 조정 및 증강 후에 실행dtype
: 생성된 어레이에 사용할 Dtype
여기선 설정을,
datagen = ImageDataGenerator(
rotation_range=10, # randomly rotate images in the range (degrees, 0 to 180)
zoom_range=0.1, # Randomly zoom image
width_shift_range=0.1, # randomly shift images horizontally (fraction of total width)
height_shift_range=0.1, # randomly shift images vertically (fraction of total height)
horizontal_flip=True, # randomly flip images horizontally
vertical_flip=False, # Don't randomly flip images vertically
)
이미지를 세로가 아닌 가로로 반전해야 하는 이유
- 손 이미지를 분류하려는 경우 손의 위아래가 바뀔 가능성은 적지만 왼손잡이로 전환될 수는 있기 때문
- matplotlib 라이브러리를 사용
배치크기
ImageDataGenerator
: 모델이 무작위 샘플에 대해 트레이닝할 수 있도록 데이터를 배치처리
import matplotlib.pyplot as plt
import numpy as np
batch_size = 32
img_iter = datagen.flow(x_train, y_train, batch_size=batch_size)
x, y = img_iter.next()
fig, ax = plt.subplots(nrows=4, ncols=8)
for i in range(batch_size):
image = x[i]
ax.flatten()[i].imshow(np.squeeze(image))
plt.show()
[배치조정, 증강 전]
[배치조정, 증강 후]
데이터 생성기에 맞추기
이제 생성기를 트레이닝 데이터세트에 맞춰야 함
인스턴스를 생성하여 트레이닝 데이터에 맞춤 -> 컴파일 하기위해
datagen.fit(x_train)
모델 컴파일
실제로 데이터를 사용하여 모델을 트레이닝하기 전에 수행해야 할 마지막 단계
- 손실함수:
categorical crossentropy
사용 - 모델의 정확도를 측정
model.compile(loss='categorical_crossentropy', metrics=['accuracy'])
증강으로 트레이닝
Keras에 이미지 데이터 생성기를 사용하는 경우 모델은 약간 다른 방식으로 트레이닝.
- 기존방식: 단순히 x_train 및 y_train 데이터세트를 모델에 전달
- 현재 방식: 생성기를 전달하여 생성기의 flow 메서드를 호출
- 그러면 이미지가 트레이닝을 위해 모델에 전달되기 직전에 실시간으로 증강되어 메모리에 위치
생성기(Generator): 무한한 데이터를 공급할 수 있음
- 생성기를 사용하여 데이터를 트레이닝할 때, 각 에포크가 얼마나 오랫동안 실행되어야 하는지를 명시적으로 설정 필요
- 그렇지 않으면 에포크가 무한히 실행되고 생성기는 모델을 제공하기 위해 증강된 이미지를 무한정으로 생성
steps_per_epoch
: 에포크가 얼마나 오랫동안 실행되어야 하는지를 명시적으로 설정steps * batch_size = number_of_images_trained in an epoch
: 단계 수를 증강되지 않은 데이터세트 크기를 batch_size(기본값 32)로 나눈 값과 동일하게 설정하는 것이기 때문
기존의 CNN보다 오래 걸리는 이유는 데이터가 증강되어 더 많아졌기 때문
model.fit(img_iter,
epochs=20,
steps_per_epoch=len(x_train)/batch_size, # Run same number of steps we would if we were not using a generator.
validation_data=(x_valid, y_valid))
결과 해석
먼저 결과를 보자
👀 결과 보기
Epoch 1/20
858/857 [==============================] - 8s 9ms/step - loss: 1.0688 - accuracy: 0.6604 - val_loss: 0.8469 - val_accuracy: 0.7082
Epoch 2/20
858/857 [==============================] - 7s 8ms/step - loss: 0.3064 - accuracy: 0.8960 - val_loss: 0.1397 - val_accuracy: 0.9522
Epoch 3/20
858/857 [==============================] - 7s 8ms/step - loss: 0.1952 - accuracy: 0.9349 - val_loss: 1.6423 - val_accuracy: 0.6775
Epoch 4/20
858/857 [==============================] - 7s 8ms/step - loss: 0.1501 - accuracy: 0.9516 - val_loss: 0.6556 - val_accuracy: 0.8296
Epoch 5/20
858/857 [==============================] - 7s 8ms/step - loss: 0.1286 - accuracy: 0.9588 - val_loss: 0.1129 - val_accuracy: 0.9618
Epoch 6/20
858/857 [==============================] - 7s 8ms/step - loss: 0.1134 - accuracy: 0.9637 - val_loss: 0.3497 - val_accuracy: 0.8993
Epoch 7/20
858/857 [==============================] - 7s 8ms/step - loss: 0.0995 - accuracy: 0.9700 - val_loss: 0.3377 - val_accuracy: 0.9060
Epoch 8/20
858/857 [==============================] - 7s 8ms/step - loss: 0.0917 - accuracy: 0.9709 - val_loss: 0.1426 - val_accuracy: 0.9470
Epoch 9/20
858/857 [==============================] - 7s 8ms/step - loss: 0.0834 - accuracy: 0.9748 - val_loss: 0.0934 - val_accuracy: 0.9663
Epoch 10/20
858/857 [==============================] - 7s 8ms/step - loss: 0.0796 - accuracy: 0.9764 - val_loss: 0.0393 - val_accuracy: 0.9883
Epoch 11/20
858/857 [==============================] - 7s 8ms/step - loss: 0.0740 - accuracy: 0.9779 - val_loss: 2.6428 - val_accuracy: 0.7280
Epoch 12/20
858/857 [==============================] - 7s 8ms/step - loss: 0.0779 - accuracy: 0.9785 - val_loss: 0.2394 - val_accuracy: 0.9361
Epoch 13/20
858/857 [==============================] - 7s 8ms/step - loss: 0.0652 - accuracy: 0.9801 - val_loss: 0.2099 - val_accuracy: 0.9516
Epoch 14/20
858/857 [==============================] - 7s 8ms/step - loss: 0.0587 - accuracy: 0.9829 - val_loss: 0.0415 - val_accuracy: 0.9851
Epoch 15/20
858/857 [==============================] - 7s 8ms/step - loss: 0.0603 - accuracy: 0.9826 - val_loss: 0.0218 - val_accuracy: 0.9930
Epoch 16/20
858/857 [==============================] - 7s 8ms/step - loss: 0.0593 - accuracy: 0.9831 - val_loss: 0.0142 - val_accuracy: 0.9962
Epoch 17/20
858/857 [==============================] - 7s 8ms/step - loss: 0.0578 - accuracy: 0.9835 - val_loss: 0.1707 - val_accuracy: 0.9587
Epoch 18/20
858/857 [==============================] - 7s 8ms/step - loss: 0.0508 - accuracy: 0.9849 - val_loss: 0.5059 - val_accuracy: 0.8712
Epoch 19/20
858/857 [==============================] - 7s 8ms/step - loss: 0.0573 - accuracy: 0.9844 - val_loss: 0.0666 - val_accuracy: 0.9806
Epoch 20/20
858/857 [==============================] - 7s 8ms/step - loss: 0.0528 - accuracy: 0.9860 - val_loss: 0.0086 - val_accuracy: 0.9962
<tensorflow.python.keras.callbacks.History at 0x7f37c461c9e8>
검증 정확도가 더 높고, 더 일관적임
CNN에 비해 과적합 상태를 보이지 않음
➡ 일반화가 개선되었고 새 데이터에 대한 예측도 개선
모델 저장
제대로 트레이닝된 모델을 얻었으니 이제 이를 이용해 새 이미지에 대한 추론을 수행 필요
만족스럽게 학습된 모델은 보통 디스크에 저장하
Keras에서 저장 메서드 제공
model.save('asl_model')
메모리
넘어가기 전에 다음 셀을 실행하여 GPU 메모리를 지우기
이는 다음 노트북으로 넘어가기 위한 필수 작업
import IPython
app = IPython.Application.instance()
app.kernel.do_shutdown(True)