JuHyang
케라스 기초 본문
학습과정 이야기
-
같은 문제집이라도 사람마다 푸는 방식이 다르고 학습된 결과도 다릅니다. 딥러닝 모델의 학습도 비슷합니다. 케라스에서는 모델을 학습시킬 때 fit() 함수를 사용하는데, 그 인자에 따라 학습 과정 및 결과가 차이납니다. 학습과정이 어떤 방식으로 일어나는 지 살펴보도록 하겠습니다.
-
배치 사이즈와 에포크
-
케라스에서 만든 모델을 학습할 때는 fit() 함수를 사용합니다
-
model.fit(x, y, batch_size = 32, epochs = 10)
-
x : 입럭 데이터 / y : 라벨 값 / batch_size : 몇 개의 샘플로 가중치를 갱신할 것인지 지정 / epochs : 학습 반복 횟수
학습에 관련된 인자이므로 시험 공부하는 것에 비유를 해보겠습니다. 먼저 모의고사 1회분을 가지고 학습해봅시다. 이 1회분은 100문항이 있고, 해답지도 제공합니다. 문제를 푼 뒤 해답지와 맞춰보면서 학습이 이루어지기 때문에 해답지가 없으면 학습이 안 됩니다.
위의 주요 인자는 다음과 같이 비유할 수 있습니다
-
X : 100문항의 문제들 입니다.
-
y : 100문항의 답들 입니다.
-
batch_size ( 배치사이즈 )
-
배치사이즈는 몇 문항을 풀고 해답을 맞추는 지를 의미합니다. 100문항일 때, 배치사이즈가 100이면 전체를 다 풀고 난 뒤에 해답을 맞춰보는 것입니다. 우리가 해답을 맞춰볼 때 '아하, 이렇게 푸는구나'라고 느끼면서 하긋ㅂ하는 것처럼 모델도 이러한 과정을 토앻 가중치가 갱신됩니다
-
---- 문제를 푼 뒤 해답과 맞춰봐야 학습이 일어납니다. > 모델의 결과값과 주어진 라벨 값과의 오차를 줄이기 위해 , '역전파 (Backpropagation)' 알고리즘으로 가중치가 갱신됩니다.
-
전체 문제를 푼 뒤 해답을 맞추므로 이때 가중치 갱신은 한 번만 일어납니다.
-
배치 사이즈가 10이면 열 문제씩 풀어보고 해답을 맞춰보는 것입니다. 100문항을 10문제씩 나누어서 10번 해답을 맞추므로 가중치 갱신은 10번 일어납니다.
-
배치 사이즈가 1이면 한 문제 풀고 해답 맞춰보고 또 한 문제 풀고 맞춰보고 하는 것입니다. 한 문제를 풀 때마다 가중치 갱신이 일어나므로 횟수는 100번입니다.
-
100문제 다 풀고 해답을 맞히는 것과 1문제씩 풀고 해답을 맞히는 것은 어떤 차이가 있을까요 ? 언뜻 생각해서는 별 반 차이가 없어 보입니다. 하지만 모의고사 1회분에 비슷한 문항이 있다고 가정했을 때, 배치 사이즈가 100일 때는 다 풀어보고 해답을 맞춰보기 때문에 한 문제를 틀릴 경우 이후 유사 문제를 모두 틀릴 경우가 많습니다. 배치사이즈가 1인 경우에는 한 문제씩 풀어보고 해답을 맞춰보기 때문에 유사문제 중 첫 문제를 틀렸다고 하더라도 해답을 보면서 학습하게 되므로 나머지 문제는 맞추게 됩니다. 자 그럼 이 배치사이즈가 어떨 때 학습 효과가 좋을까요? 사람이 학습하는 것이랑 비슷하빈다. 100문항 다 풀고 해답과 맞추어보려면 문제가 무엇이었는지 다 기억을 해야 맞춰보면서 학습이 되겠죠 ? 기억력(용량)이 커야 합니다. 1문항씩 풀고 해답 맞추면 학습은 꼼꼼히 잘 되겠지만 시간이 너무 걸리겠죠 ? 그리고 해답지를 보다가 다음 문제의 답을 봐버리는 불상사가 생기겠죠.
-
---- 배치 사이즈가 작을수록 가중치 갱신이 자주 일어납니다.
-
epchos (에포크)
-
에포크는 모의고사 1회분을 몇번 풀어볼까 입니다. 즉 100문항의 문제들을 몇 번이나 반복해서 풀어보는 지 정하는 것입니다. 에포크가 20이면 모의고사 1회분을 20번 푸는 것입니다. 처음에는 같은 문제를 반복적으로 풀어보는 것이 무슨 효과가 있는 지 의문이 들었지만 우리가 같은 문제집을 여러 번 풀면서 점차 학습되듯이 모델도 같은 데이터셋으로 반복적으로 가중치를 갱신하면서 모델이 학습됩니다. 같은 문제라도 이전에 풀었을 때랑 지금 풀었을 때랑 학습상태(가중치)가 다르기 때문에 다시 학습이 일어납니다
-
---- 같은 문제집이라도 반복해서 풀면 학습이 일어납니다.
-
아래 그래프에서 세로축이 100문항 중 틀린 개수이고, 가로축이 모의고사 풀이 반복 횟수입니다. 풀이를 반복할수록 틀린 개수가 적어지는것을 보실 수 있습니다. 처음에는 틀린 개수가 확 적어지지만 반복이 늘어날수록 완만하게 틀린 개수가 줄어듭니다. 우리가 공부할 때도 낮은 점수에서는 공부를 조금 하면 점수가 확 오르지만, 높은 점수에서 1~2점 올리는 것이 쉽지 않은 것과 비슷할 수 있습니다.
-
모의고사 1회분을 20번 푸는 것과 서로 다른 모의고사 20회분을 1번 푸는 것과는 어떤 차이가 있을까요? 이것은 분야에 따라 데이터 특성에 따라 다를 것이라고 생각하빈다. 잡다한 문제를 많이 푸는 것보다 양질의 문제를 여러 번 푸는 것이 도움이 된다고 생각합니다. 피아노를 배울 때도 기본 곡을 반복적으로 학습하면 다양한 악보도 쉽게 보는 반면 이곡 저걱 연습하면 제대로 익히기 쉽지 않습니다. 이런 문제를 제외하고도 현실적으로 데이터를 구하기가 쉽지 않기 때문에 제한된 데이터셋으로 반복적으로 학습하는 것이 효율적입니다.
https://tykimos.github.io/2017/03/25/Fit_Talk/
학습과정 표시하기 (텐서보드 포함)
-
케라스로 딥러닝 모델 개발할 때, 가장 많이 보게 되는 것이 fit 함수가 화면에 찍어주는 로그입니다. 이 로그에 포함된 수치들은 학습이 제대로 되고 있는 지, 학습을 그만할 지 등 판단하는 중요한 척도가 됩니다. 수치 자체도 큰 의미가 있지만 수치들이 에포크마다 바뀌는 변화 추이를 보는 것이 중요하기 때문에 그래프로 표시하여 보는 것이 더 직관적입니다. 본 절에서는 케라스에서 제공하는 기능을 이용하는 방법, 텐서보드와 연동하여 보는 방법, 콜백 함수를 직접 만들어서 사용하는 방법에 대해서 알아 보겠습니다
-
히스토리 기능 사용하기
-
케라스에서 학습시킬 때 fit 함수를 사용합니다.이 함수의 반환 값으로 히스토리 객체를 얻을 수 있는데, 이 객체는 다음의 정보를 담고 있습니다.
-
매 에포크마다의 훈련 손실값 (loss)
-
매 에포크마다의 훈련 정확도 (acc)
-
매 에포크마다의 검증 손실값 (val_loss)
-
매 에포크 마다의 검증 정확도 (val_acc)
-
히스토리 기능은케라스의 모든 모델에 탑재되어 있으므로 별도의 설정없이 fit 함수의 반환으로 쉽게 얻을수 있습니다.사용법은 다음과 같습니다.
hist = model.fit(X_train, Y_train, epochs=1000, batch_size=10, validation_data=(X_val, Y_val))
print(hist.history['loss'])
print(hist.history['acc'])
print(hist.history['val_loss'])
print(hist.history['val_acc'])
-
수치들은 각 에포크마다 해당 값이 추가되므로 배열 형태로 저장되어 있습니다. 이러한 수치들을 매 에포크마다 변화되는추이를 그래프로 표시하여 비교하면서 보녛 가습 상태를 직관적으로 이해하기 쉽습니다. 아래 코드와 같이 matplotlib 패키지를 이용하면 하나의 그래프로 쉽게 표시할 수 있습니다
-
train_loss(노란색) : 훈련 손실값이며 x축은 에포크 수, 좌측 y축은 손실값을 나타냅니다.
-
val_loss(빨간색) : 검증 손실값이며 x축은 에포크 수, 좌측 y축은 손실값을 나타냅니다.
-
train_acc(파란색) : 훈련 정확도이며, x축은 에포크 수, 우측 y축은 정확도를 나타냅니다.
-
val_acc(녹색) : 검증 정확도이며, x축은 에포크 수, 우측 y축은 정확도를 나타냅니다.
import matplotlib.pyplot as plt
fig, loss_ax = plt.subplots()
acc_ax = loss_ax.twinx()
loss_ax.plot(hist.history['loss'], 'y', label='train loss')
loss_ax.plot(hist.history['val_loss'], 'r', label='val loss')
acc_ax.plot(hist.history['acc'], 'b', label='train acc')
acc_ax.plot(hist.history['val_acc'], 'g', label='val acc')
loss_ax.set_xlabel('epoch')
loss_ax.set_ylabel('loss')
acc_ax.set_ylabel('accuray')
loss_ax.legend(loc='upper left')
acc_ax.legend(loc='lower left')
plt.show()
-
손글씨 데이터셋인 MNIST를 다층 퍼셉트론 모델로 학습시키는 간단한 예제로 테스트 해보겠습니다. 전체 코드는 다음과 같습니다.
from keras.utils import np_utils
from keras.datasets import mnist
from keras.models import Sequential
from keras.layers import Dense, Activation
import numpy as np
np.random.seed(3)
# 1. 데이터셋 준비하기
# 훈련셋과 시험셋 로딩
(X_train, Y_train), (X_test, Y_test) = mnist.load_data()
# 훈련셋과 검증셋 분리
X_val = X_train[50000:]
Y_val = Y_train[50000:]
X_train = X_train[:50000]
Y_train = Y_train[:50000]
X_train = X_train.reshape(50000, 784).astype('float32') / 255.0
X_val = X_val.reshape(10000, 784).astype('float32') / 255.0
X_test = X_test.reshape(10000, 784).astype('float32') / 255.0
# 훈련셋, 검증셋 고르기
train_rand_idxs = np.random.choice(50000, 700)
val_rand_idxs = np.random.choice(10000, 300)
X_train = X_train[train_rand_idxs]
Y_train = Y_train[train_rand_idxs]
X_val = X_val[val_rand_idxs]
Y_val = Y_val[val_rand_idxs]
# 라벨링 전환
Y_train = np_utils.to_categorical(Y_train)
Y_val = np_utils.to_categorical(Y_val)
Y_test = np_utils.to_categorical(Y_test)
# 2. 모델 구성하기
model = Sequential()
model.add(Dense(units=2, input_dim=28*28, activation='relu'))
model.add(Dense(units=10, activation='softmax'))
# 3. 모델 엮기
model.compile(loss='categorical_crossentropy', optimizer='sgd', metrics=['accuracy'])
# 4. 모델 학습시키기
hist = model.fit(X_train, Y_train, epochs=1000, batch_size=10, validation_data=(X_val, Y_val))
# 5. 모델 학습 과정 표시하기
# %matplotlib inline
import matplotlib.pyplot as plt
fig, loss_ax = plt.subplots()
acc_ax = loss_ax.twinx()
loss_ax.plot(hist.history['loss'], 'y', label='train loss')
loss_ax.plot(hist.history['val_loss'], 'r', label='val loss')
acc_ax.plot(hist.history['acc'], 'b', label='train acc')
acc_ax.plot(hist.history['val_acc'], 'g', label='val acc')
loss_ax.set_xlabel('epoch')
loss_ax.set_ylabel('loss')
acc_ax.set_ylabel('accuray')
loss_ax.legend(loc='upper left')
acc_ax.legend(loc='lower left')
plt.show()
-
각 에포크에 대한 손실값, 정확도 추이를 보실 수가 있습니다. 검증셋의 손실값이 감소하다가 100번째 에포크에서다시 증가되는양상을보입니다. 과적합(overfitting)이 발생했다고 볼 수가 있습니다. 이 경우 100번째 에포크만 학습시킨 모델이 1000번째 에포크까지 학습한 모델보다 실제 테스트에서 더 좋은 결과가 나올 수 있습니다.
-
텐서 보드와 연동하기
-
텐서플로우에서는 텐서보드라는 훌륭한 학습 과정 모니터링 툴을 제공하고 있습니다. 텐서플로우 기반으로 케라스를 구동할 경우 이 텐서보드를 사용할 수 있습니다. 따라서 텐서보드를 이용하기 위해서는 먼저 백엔드를 케라스 설정파일 (keras json) 에서 텐서플로우로 지정해야 합니다. 단 경로는 설치 환경에 따라 차이날 수 있습니다.
C:\Users\{사용자이름}\.keras 안에서 keras.json 을 수정해야한다
{
"floatx": "float32",
"epsilon": 1e-07,
"backend": "tensorflow",
"image_data_format": "channels_last"
"backend" : "tensorflow" ## 추가된 부분 ##
}
-
여기에서 중요한 인자는 backend입니다. 이 항목이 tensorflow로 지정되어 있어야 합니다. 연동하는 방법은 간단합니다. TensorBoard라는 콜백함수를 생성한 뒤 fit 함수 인자로 넣어주기만 하면 됩니다. TensorBoard 콜백함수 생성 시 log_dir 인자에 경로를 넣어야 하는데, 이 경로에 텐서보드와 정보를 주고 받을 수 있는 파일이 생성됩니다.
tb_hist = keras.callbacks.TensorBoard(log_dir='./graph', histogram_freq=0, write_graph=True, write_images=True)
model.fit(X_train, Y_train, epochs=1000, batch_size=10, validation_data=(X_val, Y_val), callbacks=[tb_hist])
-
동일한 예제로 이번에는 텐서보드를 통해 학습 과정을 모니터링 해보겠습니다. 전체 코드는 다음과 같습니다.
from keras.utils import np_utils
from keras.datasets import mnist
from keras.models import Sequential
from keras.layers import Dense, Activation
import keras
import numpy as np
np.random.seed(3)
# 1. 데이터셋 준비하기
# 훈련셋과 시험셋 로딩
(X_train, Y_train), (X_test, Y_test) = mnist.load_data()
# 훈련셋과 검증셋 분리
X_val = X_train[50000:]
Y_val = Y_train[50000:]
X_train = X_train[:50000]
Y_train = Y_train[:50000]
X_train = X_train.reshape(50000, 784).astype('float32') / 255.0
X_val = X_val.reshape(10000, 784).astype('float32') / 255.0
X_test = X_test.reshape(10000, 784).astype('float32') / 255.0
# 훈련셋, 검증셋 고르기
train_rand_idxs = np.random.choice(50000, 700)
val_rand_idxs = np.random.choice(10000, 300)
X_train = X_train[train_rand_idxs]
Y_train = Y_train[train_rand_idxs]
X_val = X_val[val_rand_idxs]
Y_val = Y_val[val_rand_idxs]
# 라벨링 전환
Y_train = np_utils.to_categorical(Y_train)
Y_val = np_utils.to_categorical(Y_val)
Y_test = np_utils.to_categorical(Y_test)
# 2. 모델 구성하기
model = Sequential()
model.add(Dense(units=2, input_dim=28*28, activation='relu'))
model.add(Dense(units=10, activation='softmax'))
# 3. 모델 엮기
model.compile(loss='categorical_crossentropy', optimizer='sgd', metrics=['accuracy'])
# 4. 모델 학습시키기
tb_hist = keras.callbacks.TensorBoard(log_dir='./graph', histogram_freq=0, write_graph=True, write_images=True)
model.fit(X_train, Y_train, epochs=1000, batch_size=10, validation_data=(X_val, Y_val), callbacks=[tb_hist]) ## fit 부분 callBack 추가
-
TensorBoard 콜백함수 생성시 logdir 인자로지정한 로컬의 graph라는 폴더안을 보면 events로시작하는파일이 생성되는것을 확인 할 수 있습니다. 콘솔에서아래 명령으로 텐서보드를 실행합니다. 여기에서 주의할 사항은 -logdir 인자에는 graph폴더의 절대경로로 지정해야합니다.
-
>> tensorBoard --logdir={절대경로}
-
직접 콜백함수 만들어보기
-
기본적인 모델의 학습 상태 모니터링은 앞서소개한 히스토리 콜백함수나 텐서보드를 이용하면 되지만, 순환신경망 모델인 경우에는 fit 함수를 여러번 호출되기 때문에제대로 학습 상태를 볼 수가 없습니다. 먼저 순환 신경망 모델 코드를 살펴보겠습니다.
for epoch_idx in range(1000):
print ('epochs : ' + str(epoch_idx) )
hist = model.fit(train_X, train_Y, epochs=1, batch_size=1, verbose=2, shuffle=False) # 50 is X.shape[0]
model.reset_states()
-
매 에포크마다 히스토리 객체가 생성되어 매번 초기화 되기 때문에 에포크별로 추이를 볼 수 없습니다. 이 문제를 해결하기 위해 fit 함수를 여러번 호출하더라도 학습 상태가 유지될 수 있도록 콜백 함수를 정의해보겠습니다.
# 사용자 정의 히스토리 클래스 정의
class CustomHistory(keras.callbacks.Callback):
def init(self):
self.losses = []
self.vol_losses = []
self.accs = []
self.vol_accs = []
def on_epoch_end(self, batch, logs={}):
self.losses.append(logs.get('loss'))
self.vol_losses.append(logs.get('vol_loss'))
self.accs.append(logs.get('acc'))
self.vol_accs.append(logs.get('acc_loss'))
-
새로 만든 콜백함수를 이용해서 학습 상태를 모니터링 해보겠습니다. 이전 코드에서 fit 함수 내에서 1000번 에포크를 수행했던 부분을 한번 에포크를 수행하는 fit 함수를 천번 호출하는 식으로 수정했습니다. 참고로 fit 함수를 한번 호출해서 에포크를 여러번 수행하는 것과 fit 함수를 여러번 호출하는 것은 동일한 효과를얻을수 있습니다.
import keras
# 사용자 정의 히스토리 클래스 정의
class CustomHistory(keras.callbacks.Callback):
def init(self):
self.train_loss = []
self.val_loss = []
self.train_acc = []
self.val_acc = []
def on_epoch_end(self, batch, logs={}):
self.train_loss.append(logs.get('loss'))
self.val_loss.append(logs.get('val_loss'))
self.train_acc.append(logs.get('acc'))
self.val_acc.append(logs.get('val_acc'))
from keras.utils import np_utils
from keras.datasets import mnist
from keras.models import Sequential
from keras.layers import Dense, Activation
import keras
import numpy as np
np.random.seed(3)
# 1. 데이터셋 준비하기
# 훈련셋과 시험셋 로딩
(X_train, Y_train), (X_test, Y_test) = mnist.load_data()
# 훈련셋과 검증셋 분리
X_val = X_train[50000:]
Y_val = Y_train[50000:]
X_train = X_train[:50000]
Y_train = Y_train[:50000]
X_train = X_train.reshape(50000, 784).astype('float32') / 255.0
X_val = X_val.reshape(10000, 784).astype('float32') / 255.0
X_test = X_test.reshape(10000, 784).astype('float32') / 255.0
# 훈련셋, 검증셋 고르기
train_rand_idxs = np.random.choice(50000, 700)
val_rand_idxs = np.random.choice(10000, 300)
X_train = X_train[train_rand_idxs]
Y_train = Y_train[train_rand_idxs]
X_val = X_val[val_rand_idxs]
Y_val = Y_val[val_rand_idxs]
# 라벨링 전환
Y_train = np_utils.to_categorical(Y_train)
Y_val = np_utils.to_categorical(Y_val)
Y_test = np_utils.to_categorical(Y_test)
# 2. 모델 구성하기
model = Sequential()
model.add(Dense(units=2, input_dim=28*28, activation='relu'))
model.add(Dense(units=10, activation='softmax'))
# 3. 모델 엮기
model.compile(loss='categorical_crossentropy', optimizer='sgd', metrics=['accuracy'])
# 4. 모델 학습시키기
custom_hist = CustomHistory ()
custom_hist.init()
for i in range(1000):
print('epochs : ' + str(i))
hist = model.fit(X_train, Y_train, epochs=1, batch_size=10, validation_data=(X_val, Y_val), callbacks=[custom_hist]) ## fit 부분 callBack 추가
model.reset_states()
# 5. 모델 학습 과정 표시하기
import matplotlib.pyplot as plt
fig, loss_ax = plt.subplots()
acc_ax = loss_ax.twinx()
loss_ax.plot(custom_hist.train_loss, 'y', label='train loss')
loss_ax.plot(custom_hist.val_loss, 'r', label='val loss')
acc_ax.plot(custom_hist.train_acc, 'b', label='train acc')
acc_ax.plot(custom_hist.val_acc, 'g', label='val acc')
loss_ax.set_xlabel('epoch')
loss_ax.set_ylabel('loss')
acc_ax.set_ylabel('accuray')
loss_ax.legend(loc='upper left')
acc_ax.legend(loc='lower left')
plt.show()
-
학습 모니터링 결과는 첫본째 예제와유사하게 나옴을 확인할 수 있습니다.
https://tykimos.github.io/2017/07/09/Training_Monitoring/
학습 조기종료 시키기
-
앞서 '학습과정과 데이터셋 이아기'에서 과적합 이라는 것을 살펴 보았고, 이를 방지하기 이위해 조기 종료하는 시점에 대해서 알아보았습니다. 본 절에서는 케라스에서 제공하는 기능을 이용하여 학습 중에 어떻게 조기 종료 시킬 수 있는 지 알아보겠습니다.
-
과적합 되는 모델 살펴보기
-
먼저 과적합 되는 모델을 만들고 어떻게 학습이 되었는 지 살펴보겟습니다. 아래 코드에서 사용된 데이터 수, 배치사이즈 ,뉴런 수 등은 과적합 현상을 재현하기 위해 설정된것으로 실제 최적화 된수치는 아닙니다.
from keras.utils import np_utils
from keras.datasets import mnist
from keras.models import Sequential
from keras.layers import Dense, Activation
import numpy as np
np.random.seed(3)
# 1. 데이터셋 준비하기
# 훈련셋과 시험셋 로딩
(X_train, Y_train), (X_test, Y_test) = mnist.load_data()
# 훈련 셋과 검증셋 분리
X_val = X_train[50000:]
Y_val = Y_train[50000:]
X_train = X_train[:50000]
Y_train = Y_train[:50000]
X_train = X_train.reshape(50000, 784).astype('float32') / 255.0
X_val = X_val.reshape(10000, 784).astype('float32') / 255.0
X_test = X_test.reshape(10000, 784).astype('float32') / 255.0
# 훈련셋, 검증셋 고르기
train_rand_idxs = np.random.choice(50000, 700)
val_rand_idxs = np.random.choice(10000, 300)
X_train = X_train[train_rand_idxs]
Y_train = Y_train[train_rand_idxs]
X_val = X_val[val_rand_idxs]
Y_val = Y_val[val_rand_idxs]
# 라벨링 전환
Y_train = np_utils.to_categorical(Y_train)
Y_val = np_utils.to_categorical(Y_val)
Y_test = np_utils.to_categorical(Y_test)
# 2. 모델 구성하기
model = Sequential()
model.add(Dense (units = 2, input_dim = 28 * 28, activation = 'relu'))
model.add(Dense (units = 10, activation='softmax'))
# 3. 모델 엮기
model.compile (loss = 'categorical_crossentropy', optimizer = 'sgd', metrics = ['accuracy'])
# 4. 모델 학습시키기
hist = model.fit(X_train, Y_train, epochs = 3000, batch_size = 10, validation_data = (X_val, Y_val))
# 5. 모델 학습 과정 표시하기
import matplotlib.pyplot as plt
fig, loss_ax = plt.subplots()
acc_ax = loss_ax.twinx()
loss_ax.plot(hist.history['loss'], 'y', label='train loss')
loss_ax.plot(hist.history['val_loss'], 'r', label='val loss')
acc_ax.plot(hist.history['acc'], 'b', label='train acc')
acc_ax.plot(hist.history['val_acc'], 'g', label='val acc')
loss_ax.set_xlabel('epoch')
loss_ax.set_ylabel('loss')
acc_ax.set_ylabel('accuray')
loss_ax.legend(loc='upper left')
acc_ax.legend(loc='lower left')
plt.show()
-
val_loss를 보면 에포크 횟수가 많아질 수록 감소하다가 150 에포크 근처에서 다시 증가됨을 알 수 있습니다. 이때 과적합이 발생한 것입니다.
# 6. 모델 사용하기
loss_and_matrics = model.evaluate (X_test, Y_test, batch_size = 32)
print ('')
print ('loss : ' + str (loss_and_matrics))
print ('accuray : ' + str (loss_and_matrics[1]))
-
조기 종료 시키기
-
학습 조기 종료를 위해서는 'EarlyStopping'이라는 함수를 사용하며 더 이상개선의 여지가 없을 때 학습을 종료시키는 콜백함수입니다. 콜백함수는 어떤 함수를 수행 시 그 함수에서 내가 지정한 함수를 호출하는 것을 말하며, 여기서는 fit 함수에서 ElartStopping이라는 콜백함수가 학습과정 중에 매번 호출됩니다. 먼저 fit 함수에서 EarlyStopping 콜백함수를 지정하는 방법은 다음과 같습니다.
early_stopping = EarlyStopping()
model.fit(X_train, Y_train, nb_epoch=1000, callbacks=[early_stopping])
-
에포크를 1000으로 지정했더라도 학습 과정에서 EarlyStopping 콜백함수를 호출하여 해당 조건이 되면 학습을 조기 종료시킵니다. EarlyStopping 콜백함수에서 설정할 수 있는 인자는다음과같습니다.
keras.callbakcs.EarlyStopping (monitor = 'val_loss', min_delta = 0, patience = 0, verbose = 0, mode = 'auto')
-
monitor : 관찰하고자 하는항목입니다. 'val_loss' 나 'val_acc' 가 주로 사용됩니다.
-
min_delta : 개선되고 있다고 판단하기 위한 최소 변화량을 나타냅니다. 만약 변화량이 min_delta보다 적은 경우에는 개선이 없다고 판단합니다.
-
patience : 개선이 없다고 바로 종료하지 않고 개선이 없는 에포크를 얼마나 기다려 줄 것인가를 지정합니다. 만약 10이라고 지정하면 개선이 없는 에포크가 10번째 지속될 경우 학습을 종료합니다.
-
verbose : 얼마나 자세하게 정보를 표시할 것인가를 지정합니다. (0, 1, 2)
-
mode : 관찰 항목에 대해개선이없다고 판단하기 위한 기준을 지정합니다. 예를 들어 관찰항목이 'val_loss'인 경우에는감소되는것이 멈출 때 종료되어야 하므로, 'min'으로 설정됩니다
-
auto : 관찰하는이름에 따라 자동으로 지정합니다
-
min : 관찰하고 있는 항목이 감소되는 것을 멈출 때 종료합니다.
-
max : 관찰하고있는항목이 증가되는 것을 멈출 때 종료합니다.
위의 전체 코드에서
# 4. 모델 학습시키기
## EarlyStopping Callback 함수 추가
from keras.callbacks import EarlyStopping
early_stopping = EarlyStopping()
hist = model.fit(X_train, Y_train, epochs = 3000, batch_size = 10, validation_data = (X_val, Y_val), callbacks = [early_stopping])
4번만 수정
loss : 1.9110999320983886
accuray : 0.199
-
val_loss 값이 갑소되다가 증가되자마자 학습이 종료되었습니다. 하지만 이 모델은 좀 더 학습이 될 수 있는 모델임을 이미 알고 있습니다. val_loss 특성 상 증가 / 감소를 반복하므로 val_loss가 증가되는 시점에 바로 종료하지 말고 지속적으로 증가되는 시점에서 종료해보겠습니다. 이를 위해 EarlyStopping 콜백함수에서 patience 인자를 사용합니다.
early_stopping = EarlyStopping(patience = 20)
-
즉 증가가 되었더라도 20 에포크 동안은기다려보도록 지정했습니다.
loss : 1.84164284324646
accuray : 0.2717
-
모델의 정확도도 향삼됨을 확인할수있습니다.
https://tykimos.github.io/2017/07/09/Early_Stopping/
학습 모델 보기 / 저장하기 / 불러오기
-
몇시간 동안 ( 또는 며칠동안 ) 딥러닝 모델을 학습 시킨 후 만족할만한 결과를 얻었다면, 실무에 바로 적용시키고 싶으실 겁니다. 이 때 떠오르는 의문 중 하나가 "딥러닝 모델을 사용하려면 매번이렇게 몇시간 동안 학습 시켜야 되는 거야?" 입니다. 대답은 "아니오" 입니다. 딥러닝 모델을 학습시킨다는 의미는 딥러닝 모델이 가지고 있는 뉴런들의 가중치(wiehgt)를 조정한다는 의미이고, 우리는 모델구성과 가중치만 저장해 놓으면, 필요할 때 저장한 모델 구성과 가중치를 불러와서 사용하면 됩니다. 간단한 딥러닝 모델의구성 및 가중치를 저장 및불러오는 방법에 대해서 알아보겠습니다.
-
1. 간단한 모델 살펴보기
-
2. 실무에서의 딥러닝
-
3. 학습된 모델 저장하기
-
4. 모델 아키텍처 보기
-
5. 학습된 모델 불러오기
-
간단한 모델 살펴보기
-
아래는 MNIST 데이터셋 (손글씨)을 이용하여 숫자를 분류하는문제를 간단한 다층 퍼셉트론 모델의 소스코드 입니다. 이 코드에는 모델 구성부터 학습, 평가, 사용까지 포함하고 있습니다. 이를 위해 데이터 셋구성을 모두 갖추어서 훈련셋, 검증셋, 시험셋을 준비했습니다. 또한 훈련셋으로 학습한 모델을 임의의 시험셋으로 예측을 해보겠습니다
# 0. 사용할 모델 불러오기
from keras.utils import np_utils
from keras.datasets import mnist
from keras.models import Sequential
from keras.layers import Dense, Activation
import numpy as np
from numpy import argmax
# 1. 데이터셋 생성하기
# 훈련셋과 시험셋 불러오기
(x_train , y_train), (x_test, y_test) = mnist.load_data()
# 데이터셋 전처리
x_train = x_train.reshape(60000, 784).astype('float32') / 255.0
x_test = x_test.reshape(10000, 784).astype('float32') / 255.0
# 원핫 인코딩 (one-hot encoding)처리
y_train = np_utils.to_categorical(y_train)
y_test = np_utils.to_categorical(y_test)
# 훈련셋과 검증셋 분리
x_val = x_train[:42000] # 훈련셋의 30%를 검증셋으로 사용
x_train = x_train[42000:]
y_val = y_train[:42000] # 훈련셋의 30%를 검증셋으로 사용
y_train = y_train[42000:]
# 2. 모델구성하기
model = Sequential()
model.add(Dense (units = 64, input_dim = 28 * 28, activation="relu"))
model.add(Dense (units = 10, activation = 'softmax'))
# 3. 모델 학습과정 설정하기
model.compile(loss = 'categorical_crossentropy', optimizer = 'sgd', metrics = ['accuracy'])
# 4. 모델 학습시키기
model.fit (x_train, y_train, epochs = 5, batch_size = 32, validation_data = (x_val, y_val))
# 5. 모델 평가하기
loss_and_matrics = model.evaluate(x_test, y_test, batch_size = 32)
print ('')
print ('loss_and_matrics : ' + str (loss_and_matrics))
# 6. 모델 사용하기
xhat_idx = np.random.choice(x_test.shape[0], 5)
xhat = x_test[xhat_idx]
yhat = model.predict_classes(xhat)
for i in range(5) :
print ('True : ' + str (argmax (y_test[xhat_idx[i]])) + ', Predict : ' + str (yhat[i]))
-
이 코드에서 '5. 모델 평가하기' 까지가 학습을 하기 위한 과정이고, '6. 모델 사용하기' 이후 코드가 학습된 모델을 사용하는 부분입니다. 이 사이를 분리하여 별도의 모듈로 만들면 우리가 원하는 결과를 얻을 수 있습니다.
-
실무에서의 딥러닝 시스템
-
모듈을 분리하기 전에 실무에서의 딥러닝 시스템을 살펴보겠습니다. 도메인, 문제에 마다 다양한 구성이 있겠지만, 생각하는 딥러닝 시스템의 구성은 다음과 같습니다.
-
우리가 만들고자 하는 전체 시스템을 목표 시스템이라고 했을 때, 크게 '학습 segment'와 '판정 segment'로 나누어집니다. '학습 segment'는 학습을 위해, 학습 데이터를 얻기 위한 '학습용 센싱 element', 센싱 데이터에서 학습에 적합한 형태로 전처리를 수행하는 '데이터셋 생성 element', 그리고 데이터셋으로 딥러닝 모델을 학습시키는 '딥러닝 모델 학습 element'으로 나누어집니다. '판정 segment'는 실무 환경에서 수집되는 센서인 '판정용 센싱 element'과 학습된 딥러닝 모델을 이용해서 센싱 데이터를 판정하는 '딥러닝 모델 판정 element'으로 나누어집니다. 앞서 본 코드에는 딥러닝 모델 학습 element 와 딥러닝 모델 판정 element가 모두 포함되어 있습니다. 이 두가지 element를 분리해보겠습니다.
-
---- 딥러닝 시스템은 크게 학습 부분과 판정 부분으로 나누어진다.
-
학습된 모델 저장하기
-
모델은 크게 모델 아키텍처와 모델 가중치로 구성됩니다. 모델 아키텍처는 모델이 어떤 층으로 어떻게 샇여있는 지에 대한 모델 구성이 정의도어 있고, 모델 가중치는 처음에는 임의의 값으로 초기화 되어있지만, 훈련셋으로 학습하면서 갱신됩니다. 학습된 모델을 저장한다는 말은 '모델 아키텍처'와 '모델 가중치'를 저장한다는 말입니다. 케라스에서는 save() 함수 하나로 '모델 아키텍처'와 '모델 가중치'를 'h5'파일 형식으로 모두 저장할 수 있습니다.
from keras.models import load_model
model.save('mnist_mip_model.h5')
# 7. 모델 저장하기
from keras.models import load_model
model.save ('mnist_mip_model.h5')
-
'mnist_mip_model.h5' 라는 파일이 작업 디렉토리에 생성되었는 지 확인해봅니다. 예제에서는 424KB 로 생성되었습니다. 저장된 파일에는 다음의 정보가 담겨 있습니다.
-
나중에 모델을 재구성하기 위한 모델의 구성 정보
-
모델을 구성하는 각 뉴런들의 가중치
-
손실함수, 최적하기 등의 학습 설정
-
재학습을 할 수 있도록 마지막 학습 상태
-
모델 아키텍처 보기
-
model_to_dot() 함수를 통해 모델 아키텍처를 가시화 시킬 수 있습니다.model 객체를생성한 뒤라면 언제든지 아래 코드를 호출하여 모델 아키텍처를 블록 형태로 볼수 있습니다.
# 8. 모델 아키텍처 보기
from IPython.display import SVG
from keras.utils.vis_utils import model_to_dot
%matplotlib inline
SVG (model_to_dot(model, show_shapes = True).create(prog = 'dot', format = 'svg'))
-
학습된 모델 불러오기
-
'mnist_mip_model.h5'에는 모델 아키텍처와 학습된 모델 가중치가 저장되어있으니, 이를 불러와서 사용해봅니다. 코드 흐름은 다음과 같습니다
-
모델 불러오는 함수를 이용하여 앞서 저장한 모델 파일로부터 모델을 재형성합니다.
-
실제 데이터로 모델을 사용합니다. 이 때 주로 사용되는 함수가 predict() 함수이지만 Sequential 기반의 분류모델을 사용할 경우 좀더 편리하게 사용할 수 있도롥 predict_classes() 함수를 제공합니다. 이 함수를 이용하면 가장 확률이 높은 클래스 인덱스를 알려줍니다.
# 0. 사용할 패키지 불러오기
from keras.utils import np_utils
from keras.datasets import mnist
from keras.models import Sequential
from keras.layers import Dense, Activation
import numpy as np
from numpy import argmax
# 1. 실무에 사용할 데이터 준비하기
(x_train, y_train) , (x_test, y_test) = mnist.load_data()
x_test = x_test.reshape(10000, 784).astype('float32') / 255.0
y_test = np_utils.to_categorical(y_test)
xhat_idx = np.random.choice (x_test.shape[0], 5)
xhat = x_test[xhat_idx]
# 2. 모델 불러오기
from keras.models import load_model
model = load_model('mnist_mip_model.h5')
# 3. 모델 사용하기
yhat = model.predict_classes (xhat)
for i in range (5) :
print('True : ' + str(argmax(y_test[xhat_idx[i]])) + ', Predict : ' + str(yhat[i]))
-
파일로부터 모델 아키텍처와모델 가중치를 재구성한 모델의 결과가 잘 나오는 것을 확인할 수 있습니다
-
Q & A
-
Q1 ) 모델 아키텍처와 모델 가중치를 따로 저장 할 수는 없나요 ?
-
A1 ) 있습니다. 모델 아키텍처는 model.to_json() 함수와 model.to_yaml() 함수를 이용하면 json 혹은 yaml 형식의 파일로 저장할 수 있습니다. 가중치는 model.save_weights() 함수로 파일 경로를 인자로 입력하면 h5 형식의 가중치 파일이 생성됩니다. 따로 저장한 경우에는 구성 시에도 따로 해야 합니다. 모델 아키텍처를 먼저 구성한 뒤가중치를 불러와서 모델에 셋팅하면 됩니다.
from models import model_from_json
json_string = model.to_json() # 모델 아키텍처를 json 형식으로 저장
model = model_from_json(json_string) # json 파일에서 모델 아키텍처 재구성
from models import model_from_yaml
yaml_string = model.to_yaml() # 모델 아키텍처를 yaml 형식으로 저장
model = model_from_yaml (yaml_string) # yaml 파일에서 모델 아키텍처 재구성
-
Q2) predict_classes() 함수는 Sequential 기반 모델에서만 사용 가능한가요 ?
-
A2) 네 맞습니다. functional API 기반 모델은 다수개의 입출력으로 구성된 다양한 모델을 구성할 수 있기 때문에 예측함수의 출력 형태 또한다양합니다. 따라서클래스 인덱스를 알려주는 간단한 예측함수를 제공하지 않습니다.
https://tykimos.github.io/2017/06/10/Model_Save_Load/
다층 퍼셉트론 레이어 이야기
-
이번에는 케라스에서 사용되는 레이어(layer, 층) 개념에 대해서 알아봅니다. 케라스의 핵십 데이터 구조는 모델이고, 이 모델을 구성하는 것이 레이어입니다. 간단히 뉴런에 대해서 알아본 다음, 주요 레이어에 대해 기본 개념, 역할 등에 대해서 살펴보고, 레이어를 어떻게 쌓아서 모델을 만들 수 있는 지 알아봅니다. 기본적인 레이어 개념을 익히면 레고 쌓는 것 처럼 쉽게 구성할 수 있는데, 실제 레고로도 쌓아보겠습니다. 본 강좌에서는 다층 퍼셉트론 모델에서 사용되는 Dense 레이어에 대해서만 알아보겠습니다.
-
인간의 신경계를 모사한 뉴런 이야기
-
신경망에서 사용되는 뉴런은 인간의 신경계를 모사한 것입니다. 아래 왼쪽 그림이 인간의 뉴런이고, 오른쪽 그림이 이를 모델링 한 것입니다.
-
axon (축삭돌기) : 팔처럼 몸체에서 뻗어나와 다른 뉴런의 수상돌기와 연결됩니다.
-
dendrite (수상돌기) : 다른 뉴런의 축삭 돌기와 연결되며, 몸체에 나뭇가지 형태로 붙어 있습니다.
-
synapse (시냅스) : 축삭돌기와 수상돌기가 연결된 지점입니다. 여기서 한 뉴런이 다른 뉴런으로 신호가 전달됩니다.
-
하나의 뉴런은 여러 다른 뉴런의 축삭돌기와 연결되어 있는데, 연결된 시냅스의 강도가 연결된 뉴런들의 영향력에 의해 결정됩니다. 이러한 영향력의 합이 어떤 값을 초과하면 신호가 발생하여 축삭돌기를 통해서 다른 뉴런에게 신호가 전달되는 식입니다. 오른쪽 그림의 모델링과는 다음과 같이 매칭됩니다.
-
x0, x1, x2 : 입력되는 뉴런의 축삭돌기로부터 전달되는 신호의 양
-
w0, w1, w2 : 시냅스의 강도, 즉 입력되는 뉴런의 영향력을 나타냅니다.
-
w0x0 + w1x1 + w2x2 : 입력되는 신호의 양과 해당 신호의 시냅스 강도가 곱해진 값의 합계
-
f : 최종 합계가 다른 뉴런에게 전달되는 신호의 양을 결정짓는 규칙, 이를 활성화 함수라고 부릅니다.
-
세 개의 신호를 받아 하나의 신호를 전달하는 뉴런을 레고로 표현하면 다음과 같다. 녹색 블럭은 시냅스의 강도, 노란색과 빨간색 블럭은 연산자, 파란색 블럭은 활성화 함수를 나타냅니다.
-
만약 세개의 신호가 서로 다른 뉴런 ㄷ두 개에 전달된다고 한다면, 각 뉴런은 하나의 신호가 출력되므로, 총 두개의 신호가 출력됩니다. 이를 레고로 표현하면 다음과 같습니다.
-
위와 같은 표현이지만 이를 겹쳐 표현하면 아래와 같습니다. 다시말해 세 개의 신호를 받는 뉴런 두 개를 표현 한 것입니다. 여기서 유심히 봐야할 점은 시냅스의 강도 즉 녹색블럭의 개수입니다. 세 개의 신호가 뉴런 두개에 연결되므로 총 연결 경우의 수(3 * 2 = 6) 인 6개가됩니다.
-
입출력을 모두 연결해주는 Dense 레이어
-
Dense레이어는입력과 출력을 모두 연결해줍니다. 예를 들어 입력 뉴런이 4개, 출력 뉴런이 8개 있다면 총 연결 선은 32개 (4 * 8 = 32) 입니다. 각 연결선에는 가중치(weight)를 포함하고 있는데, 이 가중치가 나타내는 의미는 연결강도라고 보시면 됩니다. 현재 연결선이 32개이므로 가중치도 32개 입니다.
-
---- 가중치가 높을수록 해당 입력 뉴런이 출력 뉴런에 미치는 영향이 크고 낮을수록 미치는 영향이 적다.
-
예를 들어 성별을 판단하는 문제에 있어서, 출력 뉴런의 값이 성별을 의미하고, 입력 뉴런에 머리카락 길이, 키, 혈액형 등이 있다고 가정했을 때, 머리카락의 가중치가 가장 높고, 키의 가중치가 중간이고,혈핵형의 가중치가 가장 낮을 겁니다. 딥러닝 학습과정에서 이러한 가중치들이 조정됩니다.
-
이렇게 입력 뉴런과 출력 뉴런을 모두 연결한다고 해서 전결합층이라고 불리고, 케라스에서는 Dense 라는 클래스로 구현이 되어 잇습니다. 아래는 Dense 클래스 사용 예제 입니다
-
---- Dense (0, input_dim = 4, init = 'uniform', activation = 'relu')
-
주요 인자는 다음과 같습니다.
-
첫번째 인자 : 출력 뉴런의 수를 설정합니다.
-
input_dim : 입력 뉴런의 수를 설정합니다.
-
init : 가중치 초기화 방법 설정합니다.
-
'uniform' : 균일 분포
-
'normal' : 가우시안 분포
-
activation : 활성화 함수 설정합니다.
-
'linear' : 디폴트 값, 입력 뉴런과 가중치로 계산된 결과값이 그대로 출력으로 나옵니다.
-
'relu' : rectifier 함수, 은익층에주로 쓰입니다.
-
'sigmoid' : 시그모이드 함수, 이진 분류 문제에서 출력층에 주로 쓰입니다.
-
'softmax' : 소프트맥스 함수, 다중 클래스 분류 문제에서 출력층에 주로 쓰입니다.
-
Dense 레이어는 입력 뉴런수에 상관없이 출력 뉴런수를 자유롭게 설정할 수 있기 때문에 출력층에 많이 사용됩니다. 이진 분류문제에서는 0과 1을 나타내는 출력 뉴런이 하나만 있으면 되기 때문에 아래 코드처럼 출력 뉴런이 1개이고,입력 뉴런과 가중치를 계산한 값을 0에서 1사이로 표현할 수 있는 활성화 함수인 sigmoid를 사용합니다
-
---- Dense (1, input_dim = 3, activatino = 'sigmoid')
-
이를 레고로 표시하면 다음과 같습니다. 왼쪽 그림은 앞서 설명한 뉴런 상세 구조를 도식화 한 것이고, 오른쪽 그림은 이를 간단하게 도식화한 것이다. 왼쪽 그림에서 시냅스 강도가 녹색 블럭으로 표시되어 있다면, 중간 그림에서는 시냅스 강도가 연결선으로 표시되어 있고, 오른쪽 그림에서는 생략되어 있습니다. 생략되어 있더라도 입력 신호의 수와 출력 신호의 수만 알면 곱셈으로 쉽게 유추할 수 있습니다.
-
다중 클래스 분류문제에서는 클래스 수만큼 출력 뉴런이 필요합니다. 만약 세가지 종류로 분류한다면, 아래 코드처럼 출력 뉴런이 3개이고, 입력 뉴런과 가중치를 계산한 값을 각 클래스의 확률 개념으로 표현할 수 있는 활성화 함수인 softmax를 사용합니다.
-
---- Dense (3, input_dim = 4, activation = 'softmax')
-
이를 레고로 표시하면 다음과 같습니다. 입력 신호가 4개이고 출력 신호가 3개이므로 시냅스 강도의 개수는 12개입니다.
-
Dense 레이어는 보통 출력층 이전의 은닉층으로도 많이 쓰이고, 영상이 아닌 수치자료 입력시에는 입력층으로도 많이 쓰입니다. 이 때 활성화 함수로 'relu'가 주로 사용됩니다. 'relu'는 학습과정에서 역전파 시에 좋은 성능이 나는 것으로 알려져 있습니다.
-
---- Dense(4, input_dim = 6, activation = 'relu')
-
이를 레고로 표시하면 다음과 같습니다.
-
또한 입력층이 아닐 때에는 이전층의 출력 뉴런 수를 알 수 있기 때문에 input_dim 을 지정하지 않아도 됩니다. 아래 코드를 보면, 입력층에만 input_dim 을 정의하였고, 이후 층에서는 input_dim을 지정하지 않았습니다.
-
---- model.add(Dense (8, input_dim = 4, init = 'uniform', activation = 'relu'))
-
---- model.add(Dense (6, init = 'uniform', activation = 'relu'))
-
---- model.add(Dense (1, init = 'uniform', activatoin = 'sigmoid'))
-
이를 레고로 표시하면 다음과 같습니다. 왼쪽 그림은 Dense 레이어 세개를 도식화 한 것이고, 오른쪽 그림은 입력과 출력의 수에 맞게 연결하여 입력 신호가 인가되었을 때 출력 신호가 나오는 것 까지의 구성을 표시한 것입니다. 이제 레고 블럭만 봐도 입력값이 4이고 출력값이 0에서 1가지 범위를 가지는 값이 나올 수 있도록 설계된 구조임을 알 수 있습니다. 활성화 함수가 sigmoid 이기때문에 이진 분류에 적합합니다.
-
쌓앗던 레고를 실제 케라스로 구현해봅니다. 4개의 입력 값을 받아 이진 분류하는문제를 풀 수 있는 모델입니다.
from keras.models import Sequential
from keras.layers import Dense
model = Sequential()
model.add(Dense (8, input_dim = 4, init = 'uniform', activation = 'relu'))
model.add(Dense (6, init = 'uniform', activation = 'relu'))
model.add(Dense (1, init = 'uniform', activation = 'sigmoid'))
from IPython.display import SVG
from keras.utils.vis_utils import model_to_dot
SVG(model_to_dot(model, show_shapes=True).create(prog='dot', format='svg'))
https://tykimos.github.io/2017/01/27/MLP_Layer_Talk/
다층 퍼셉트론 모델 만들어보기
-
케라스를 이용하여 간단한 다층 퍼셉트론 모델을 만들어 보겠습니다. 다음과 같은 순서로 진행하겠습니다.
-
1. 문제 정의하기
-
2. 데이터 준비하기
-
3. 데이터셋 생성하기
-
4. 모델 구성하기
-
5. 모델 학습과정 설정하기
-
6. 모델 학습시키기
-
7. 모델 평가하기
-
문제 정의하기
-
다층 퍼셉트론 모델은 가장 기본적인 모델이라 대부분 문제에 적용할 수 있습니다. 본 예제에서 비교적 쉬운 이진 분류 문제를 적용해보고자 합니다. 이진 분류 예제에 적합한 데이터 셋은 8개 변수와 당뇨병 발병 유무가 기록된 '피마족 인디언 당뇨병 발병 데이터셋'이 있습니다. 이 데이터셋을 이용하여 8개 변수를 독립변수로 보고 당뇨병 발병 유무를 예측하는 이진 부류 문제로 정의해보겠습니다.
-
데이터 셋은 아래 링크에서 다운로드 받으실 수 있습니다
-
'피마족 인디언 당뇨병 발병 데이터셋'을 선정한 이유는 다음과 같습니다.
-
인스턴스 수와 속성 수가 예제로 사용하기에 적당합니다.
-
모든 특징이 정수 혹은 실수로 되어 있어서 별도의 전처리 과정이 필요 없습니다.
-
데이터셋을 준비하기에 앞서, 매번 실행 시마다 결과가 달라지디 않도록 랜덤 시드를 명시적으로 지정합니다. 이것을 하지 않으면 매번 실행 시 마다 동일 모델인데도 불구하고 다른 결과가 나오기 때문에, 연구개발 단계에서 파라미더 조정이나 데이터 셋에 따른 결과 차이를 보려면 랜덤 시드를 지정해 주는 것이 좋습니다.
import numpy as np
from keras.models import Sequential
from keras.layers import Dense
# 랜덤시드 고정시키기
np.random.seed(5)
-
데이터 준비하기
-
위 링크에서 'pima-indians-diabets.names'을 열어보면 데이터셋에 대한 설명이 포함되어 있습니다. 먼저 몇가지 주요 항목을 살펴보겠습니다.
-
인스턴스 수 : 768 개
-
속성 수 : 8 가지
-
클래스 수 : 2가지
-
8가지 속성 (1번 ~ 8번)과 결과 (9번)의 상세 내용은 다음과 같습니다.
-
1. 임신 횟수
-
2. 경구 포도당 내성 검사에서 2시간 동안의 혈장 포도당 농도
-
3. 이완기 혈압 (mm Hg)
-
4. 삼두근 피부 두겹 두께 (mm)
-
5. 2 시간 혈청 인슐링 (mi U/ml)
-
6. 체질량 지수
-
7. 당뇨 직계 가족력
-
8. 나이 (세)
-
9. 5년 이내 당뇨병 발병 여부
-
좀 더 살펴보면, 양성인 경우가 268개 (34.9%), 음성인 경우가 500개 (65.1%) 입니다. 즉 모델이 모두 음성이라고 판별을 한다 하더라도 65.1%의 기본 정확도 (baseline accuracy)를 달셩할 수 있습니다. 즉 우리의 모델이 65.1% 보다 낮으면 모두 음성이라고 판별하는 것보다 낮은 정확도를 가진다고 생각하시면 됩니다. 지금까지 개발된 알고리즘의 최대 정확도는 10-fold 교차 검증 (cross validation) 했을 때 77.7% 이라고 웹사이트에는 표기되어 있습니다
-
'pima-indians-diabets.data'이 실제 데이터 파일입니다. 열어보면 CSV 형태로 되어 있습니다. CSV는 값들이 쉼표로 분리된 텍스트 파일이며 메모장이나 엑셀에서 쉽게 확인할 수 있습니다.
-
속성별 간단한 통계 정보는 다음과 같습니다.
-
numpy 패키지에서 제공하는 loadtxt() 함수를 통해 데이터를 불러옵니다.
-
---- dataset = np.loadtxt ("./data/diabets.csv", delimiter=",")
-
데이터셋 생성하기
-
csv 형식의 파일은 numpy 패키지에서 제공하는 loadtxt() 함수로 직접 불러올 수 있습니다. 데이터셋에는 속성값과 판정결과가 모두 포함되어 있기 때문에 입력(속성값 8개)와 출력 (판정결과 1개) 변수로 분리합니다.
x_train = dataset[:700, 0:8]
y_train = dataset[:700, 8]
x_test = dataset[700:, 0:8]
y_test = dataset[700:, 8]
-
모델 구성하기
-
앞 강좌에서 배운 Desne 레이어만을 사용하여 다층 퍼셉트론 모델을 구성할 수 있습니다. 속성이 8개이기 때문에 입력 뉴런이 8개이고, 이진 분류이기 때문에 0~1 사이의 값을 나타내는 출력 뉴런이 1개입니다
-
첫번째 Dense 레이어는 은닉층 (hidden layer)으로 8개의 뉴런을 입력받아 12개의 뉴런을 출력합니다
-
두번째 Dense 레이어는 은닉층으로 12개의 뉴런을 입력받아 8개 뉴런을 출력합니다.
-
마지막 Desne 레이어는 출력 레이어로 8개의 뉴런을 입력받아 1개의 뉴런을 출력합니다
-
이 구성을 블록으로 표시해 봤습니다. 총 세 개의 Desne 레이어 블록으로 모델을 구성한 다음, 8개의 속성 값을 입력하면 1개의 출력값을 얻을 수 있는 구성입니다.
model = Sequential()
model.add(Dense(12, input_dim = 8, activation = "relu"))
model.add(Dense(8, activation = "relu"))
model.add(Dense(1, activation = "sigmoid"))
-
은익 레이어의 활성화 함수는 모두 'relu'를 사용하였고, 출력 레이어만 0과 1사이로 값이 출력될 수 있도록 활성화 함수를 'sigmoid'로 사용하였습니다. 0과 1사이의 실수값이 나오기 때문에 양성 클래스의 확률로 쉽게 매칭할 수 있습니다.
-
모델 학습과정 설정하기
-
모델을 정의했다면 모델을 손실함수와 최적화 알고리즘으로 엮어봅니다.
-
loss : 현재 가중치 세트를 평가하는 데 사용할 손실 함수입니다. 이진 클래스 문제이므로 'binary_crossentropy'으로 지정합니다.
-
optimizer : 최적의 가중치를 검색하는 데 사용되는 최적화 알고리즘으로 효율적인 경사 하강법 알고리즘 중 하나인 'adam'을 사용합니다
-
metrics : 평가 척도를 나타내며 분류 문제에서는 일반적으로 'accuracy'으로 지정합니다
-
model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])
-
모델 학습시키기
-
모델을 학습시키기 위해서 fit() 함수를 사용합니다.
-
첫번째 인자 : 입력 변수입니다. 8개의 속성 값을 답고 있는 X 를 입력합니다.
-
두번째 인자 : 출력 변수 즉 라벨값입니다. 결과 값을 담고 있는 Y를 입력합니다.
-
epochs : 전체 훈련 데이터셋에 대해 학습 반복 횟수를 지정합니다. 1500번을 반복적으로 학습시켜 보겠습니다
-
batch_size : 가중치를 업데이트 할 배치 크기를 의미하며, 64개로 지정했습니다.
-
model.fit(x_train, y_train, epochs=1500, batch_size=64)
-
모델 평가하기
-
시험셋으로 학습한 모델을 평가해봅니다.
-
scores = model.evaluate(x_test, y_test)
-
print("%s: %.2f%%" %(model.metrics_names[1], scores[1]*100))
https://tykimos.github.io/2017/02/04/MLP_Getting_Started/
컨볼루션 신경망 레이어 이야기
-
이번 강좌에서는 컨볼루션 신경망 모델에서 주로 사용되는 컨볼루션 (Convolution) 레이어, 맥스 풀링 (Max Pooling) 레이어, 플래튼(Flatten) 레이어에 대해서 알아보겠습니다. 각 레이어 별로 레이어 구성 및 역할에 대해서 알아보겠습니다.
-
필터로 특징을 뽑아주는 컨볼루션(Convolution) 레이어
-
케라스에서 제공되는 컨볼루션 레이어 종류에도 여러가지가 있으나 영상 처리에 주로 사용되는 Conv2D 레이어를 살펴보겠습니다. 레이어는 영상 인식에 주로 사용되며, 필터가 탑재되어 있습니다. 아래는 Conv2D 클래스 사용 예제 입니다.
-
---- Conv2D(32, (5, 5), padding = 'valid', input_shape = (28, 28, 1), activation = 'relu')
-
주요 인자는 다음과 같습니다.
-
첫번 째 인자 : 컨볼루션 필터의 수 입니다.
-
두번 째 인자 : 컨볼루션 커널의 (행, 열) 입니다
-
padding : 경계 처리 방법을 정의합니다.
-
'valid' : 유효한 영역만 출력이 됩니다. 따라서 출력 이미지 사이즌 입력 사이즈보다 작습니다
-
'same' : 출력 이미지 사이즈가 입력 이비지 사이즈와 동일합니다.
-
input_shape : 샘플 수를 제외한 입력 형태를 정의 합니다. 모델에서 첫 레이어일 때만 정의하면 됩니다.
-
(행, 열 ,채널 수)로 정의합니다. 흑백 영상인 경우에는 채널이 1이고, 컬러(RGB) 영상인 경우에는 채널을 3으로 설정합니다
-
activation : 활성화 함수를 설정합니다.
-
'linear' : 디폴트 값, 입력 뉴런과 가중치로 계산된 결과값이 그대로 출력으로 나옵니다.
-
'relu' : rectfilter 함수, 은익층에 주로 쓰입니다.
-
'sigmoid' : 시그모이드 함수,. 이진 분류 문제에서 출력층에 주로 쓰입니다.
-
'softmax' : 소프트맥스 함수, 다중 클래스 분류 문제에서 출력층에 주로 쓰입니다.
-
입력 형태는 다음과 같습니다
-
image_data_format이 'channels_first' 인 경우 (샘플 수, 채널 수, 행, 열)로 이루어진 4D 텐서입니다.
-
image_data_format이 'channels_last' 인 경우 (샘플 수, 행, 열, 필터 수)로 이루어진 4D 텐서입니다.
-
행과열 크기는 padding 이 'same'인 경우에는 입력 형태의 행과 열의 크기가 동일합니다
-
간단한 예제로 컨볼루션 레이어와 필터에 대해서 알아보겠습니다. 입력 이미지는 채널 수가 1, 너비가 3 픽셀, 높이가 3픽셀이고, 크기가 2 x 2 인 필터가 하나인 경우를 레이어로 표시하면 다음과 같습니다. 단 image_data_format 이 'channels_last'인 경우 입니다.
-
---- Conv2D (1, (2, 2), padding = 'valid', input_shape = (3, 3, 1))
-
이를 도식화 하면 다음과 같습니다.
-
필터는 가중치를 의미합니다. 하나의 필터가 입력 이미지를 순회하면서 적용된 결과값을 모으면 출력 이미지가 생성됩니다. 여기에는 두 가지 특성이 있습니다.
-
하나의 필터로 입력 이미지를 순회하기 때문에 순회할 때 적용되는 가중치는 모두 동일합니다. 이를 파라미터 공유라고 부릅니다. 이는 학습해야할 가중치 수를 현저하게 줄여줍니다.
-
출력에 영향을 미치는 영역이 지역적으로 제한되어 있습니다. 즉 그림에서 y~0~ 에 영향을 미치는 입력은 x~0~, x~1~, x~3~, x~4~ 으로 한정되어 있습니다. 이는 지역적인 특징을 잘 뽑아내게 되어 영상 인식에 적합하니다. 예를 들어 코를 볼때는 코 주변만 보고, 눈을 볼 때는 눈 주변만 보면서 학습 및 인식하는 것입니다.
-
가중치의 수
-
이를 Dense 레이어와 컨볼루션 레이어를 비교하면서 차이점을 알아보겠습니다. 영상도 결국에는 픽셀의 집합이므로 입력 뉴런이 9개 (3 x 3)이고, 출력 뉴런이 4개(2 x 2)인 Dense 레이어로 표현할 수 있습니다.
-
---- Dense (4, input_dim = 9)
-
가중치 즉 시냅스 강도는 녹색 블럭으로 표시되어있습니다. 컨볼루션 레이어에서는 가중치 4개로 구성된 크기가 2 x 2 인 필터를 적용하엿을 때의 뉴런 상세 구조는 다음과 같습니다.
-
필터가 지역적으로만 적용되어 출력 뉴런에 영향을 미치는 입력 뉴런이 제한적이므로 Dense 레이어와 비교했을 때, 가중치가 많이 줄어든 것을 보실 수 있습니다. 게다가 녹색 블럭 상단에 표시된 빨간색, 파란색, 분홍색, 노란색끼리는 모두 동일한 가중치 (파라미터 공유) 이므로 결국 사용되는 가중치는 4개입니다. 즉 Dense 레이어에서는 36개의 가중치가 사용되었지만, 컨볼루션 레이어에서는 필터의 크기인 4개의 가중치만을 사용합니다.
-
경계 처리 방법
-
이번에는 경계 처리 방법에 대해서 알아봅시다. 컨볼루션 레이어 설정 옵션에는 border_mode 가 있는데, 'valid'와 'smae'으로 설정할 수 있습니다. 이 둘의 차이는 아래 그림에서 확인할 수 있습니다.
-
'valid'인 경우에는 입력 이미지 영역에 맞게 필터를 적용하기 때문에 출력 이미지 크기가 입력 이미지 크기보다 작아집니다. 반면에 'same'은 출력 이미지와 입력 이미지 사이즈가 동일하도록 입력 이미지 경계에 빈 영역을 추가하여 필터를 적용합니다. 'same'으로 설정 시, 입력 이미지에 경계를 학습시키는 효과가 있습니다.
-
필터 수
-
다음은 필터의 개수에 대해서 알아봅니다. 입력 이미지가 단채널의 3 x 3 이고, 2 x 2 인 필터가 하나 있다면 다음과 같이 컨볼루션 레이어를 정의할 수 있습니다.
-
---- Conv2D (1, (2, 2), padding = 'same', input_shape = (3, 3, 1))
-
만약 여기서 사이즈가 2 x 2 인 필터를 3개 사용한다면 다음과 같이 정의할 수 있습니다.
-
---- Conv2D (3, (2, 2), padding = 'same', input_shape = (3, 3, 1))
-
여기서 살펴봐야할 것은 필터가 3개라서 출력 이미지도 필터 수에 따라 3개로 늘어났습니다. 총 가중치의 수는 3 x 2 x 2으로 12개입니다. 필터마다 고유한 특징을 뽑아 고유한 출력 이미지로 만들기 때문에 필터의 출력값을 더해서 하나의 이미지로 만들거나 그렇게 하지 않습니다. 필터에 대해 생소하신 분은 카메라 필터라고 생각하시면 됩니다. 스마트폰 카메라로 사진을 찍을 때 필터를 적용해볼 수 있는데, 적용되는 필터에 따라 다른 사진이 나옴을 알 수 있습니다.
-
뒤에서 각 레이어를 레고처럼 쌓아올리기 위해서 약식으로 표현하면 다음과 같습니다.
-
이 표현은 다음을 의미합니다.
-
입력 이미지 사이즈가 3 x 3 입니다.
-
2 x 2 커널을 가진 필터가 3개 입니다. 가중치는 총 12개 입니다
-
출력 이미지 사이즈가 3 x 3 이고 총 3개입니다. 이는 채널이 3개다 라고도 표현합니다.
-
다음은 입력 이미지의 채널이 여러 개인 경우를 살펴보겠습니다. 만약 입력 이미지의 채널이 3개이고 사이즈가 3 x 3 이고, 사이즈가 2 x 2 필터를 1개 사용한다면 다음과 같이 컨볼루션 레이어를 정의할 수 있습니다.
-
---- Conv2D (1, (2, 2), padding = 'same', input_shaep = (3, 3, 3))
-
필터 개수가 3개인 것처럼 보이지만 이는 입력 이미지에 따라 할당되는 커널이고, 각 커널의 계산 값이 결국 더해져서 출력 이미지 한 장을 만들어내므로 필터 개수는 1개입니다. 이는 Dense 레이어에서 입력 뉴런이 늘어나면 거기에 상응하는 시냅스가 늘어나서 가중치의 수가 늘어나는 것과 같은 원리입니다. 가중치는 2 x 2 x 3 이므로 총 12개 이지만 필터 수는 1개입니다. 이를 약식으로 표현하면 다음과 같습니다.
-
이 표현은 다음을 의미합니다
-
입력 이미지 사이즈가 3 x 3 이고 채널이 3개 입니다.
-
2 x 2 커널을 가진 필터가 1개 입니다. 채널마다 커널이 할당되어 총 가중치는 12개 입니다.
-
출력 이미지는 사이즈가 3 x 3 이고 채널이 1개 입니다.
-
마지막으로 입력 이미지의 사이즈가 3 x 3 이고 채널이 3개이고, 사이즈가 2 x 2 인 필터가 2개인 경우를 살펴보겠습니다
-
---- Conv2D (2, (2, 2), padding = 'same', input_shape = (3, 3, 3))
-
필터가 2개이므로 출력 이미지도 2개입니다. 약식 표현은 다음과 같습니다.
-
이 표현은 다음을 의미합니다.
-
입력 이미지 사이즈가 3 x 3 이고 채널이 3개입니다.
-
2 x 2 커널을 가진 필터가 2개입니다. 채널마다 커널이 할당되어 총 가중치는 3 x 2 x 2 x 2 로 24개입니다.
-
출력 이미지는 사이즈가 3 x 3 이고 채널이 2개 입니다.
-
사소한 변화를 무시해주는 맥스풀링 (Max Pooling) 레이어
-
컨볼루션 레이어의 출력 이미지에서 주요값만 뽑아 크기가 작은 출력 영상을 만듭니다. 이것은 지역적인 사소한 변화가 영향을 미치지 않도록 합니다.
-
---- MaxPolling2D(pool_size = (2, 2))
-
주요 인자는 다음과 같습니다.
-
pool_size : 수직, 수평 축소 비율을 지정합니다. (2, 2)이면 출력 영상 크기는 입력 영상 크기의 반으로 줄어듭니다.
-
예를 들어, 노란색 블럭은 풀 크기에 따라 나눈 경계를 표시합니다. 해당 풀에서 가장 큰 값을 선택하여 파란색 블럭으로 만들면, 그것이 출력 영상이 됩니다. 가장 오른쪽은 맥스풀링 레이어를 약식으로 표시한 것입니다.
-
이 레이어는 영상의 ㅈ가은 변화라던지 사소한 움직임이 특징을 추출할 때 크게 영향을 미치지 않도록 합니다. 영상 내에 특징이 세 개 있다고 가정했을 때, 아래 그림에서 첫 번째 영상을 기준으로 두 번째 영상은 오른쪽으로 이동하였고, 세 번째 영상은 약간 비틀어 졌고, 네 번째 영상은 조금 확대 되었지만, 맥스풀링한 결과는 모두 동일합니다. 얼굴 인식 문제를 예를 들면, 맥스 풀링의 역할은 사람마다 눈, 코, 입 위치가 조금씩 다른데 이러한 차이가 사람이라고 인식하는 데 있어서는 큰 영향을 미치지 않게 합니다.
-
영상을 일차원으로 바꿔주는 플래튼 (Flatten) 레이어
-
CNN 에서 컨볼루션 레이어나 맥스풀링 레이어를 반복적으로 거치면 주요 특징만 추출되고, 추출된 주요 특징은 전결합층에 전달되어 학습됩니다. 컨볼루션 레이어나 맥스풀링 레이어는 주로 2차원 자료를 다루짐나 전결합층에 전달하기 위해선 1차원 자료로 바꿔줘야 합니다. 이 때 사용되는 것이 플래튼 레이어 입니다. 사용 예시는 다음과 같습니다.
-
---- Flatten ()
-
이전 레이어의 출력 정보를 이용하여 입력 정보가 자동으로 설정되며, 출력 형태는 입력 형태애 따라 자동으로 계산되기 때문에 별도로 사용자가 파라미터를 지정해주지 않아도 됩니다. 크기가 3 x 3 인 영상을 1차원으로 변경했을 때는 도식화 하면 다음과 같습니다.
-
한 번 쌓아보기
-
지금까지 알아본 레이어를 이용해서 간단한 컨볼루션 신경망 모델을 만들어 보겠습니다. 먼저 간단한 문제를 정의해 봅시다. 손으로 삼각형, 사각형, 원을 그린 이미지가 있고 이미지 크기가 8 x 8이라고 가정해봅니다. 삼각형, 사각형, 원을 구분하는 3개의 클래스를 분류하는 문제이기 때문에 출력 벡터는 3개여야 합니다. 필요하다고 생각하는 레이어를 구성해 봤습니다.
-
컨볼루션 레이어 : 입력 이미지 크기 8 x 8, 입력 이미지 채널 1개, 필터 크기 3 x 3 , 필터 수 2개, 경계 타입 'same', 활성화 함수 'relu'
-
맥스 풀링 레이어 : 풀 크기 2 x 2
-
컨볼루션 레이어 : 입력 이미지 크기 4 x 4, 입력 이미지 채널 2개, 필터 크기 2 x 2, 필터 수 3개 경계 타입 'same', 활성화 함수 'relu'
-
맥스 풀링 레이어 : 풀 크기 2 x 2
-
플래튼 레이어
-
Dense 레이어 : 입력 뉴런 수 12개, 출력 뉴런 수 8개, 활성화 함수 'relu'
-
Dense 레이어 : 입력 뉴런 수 8개, 출력 뉴런 수 3개, 활성화 함수 'softmax'
-
모든 레이어 블럭이 준비되었으니 이를 조합해 봅니다. 입출력 크기만 맞으면 레고 끼우듯이 합치면 됩니다. 참고로 케라스 코드에서는 가장 첫번째 레이어를 제외하고는 입력 형태를 자동으로 계산하므로 이 부분은 신경쓰지 않아도 됩니다. 레이어를 조립하니 간단한 컨볼루션 모델이 생성되었습니다. 이 모델에 이미지를 입력하면, 삼각형, 사각형, 원을 나타내는 벡터가 출력됩니다.
import numpy
from keras.models import Sequential
from keras.layers import Dense, Flatten
from keras.layers.convolutional import Conv2D, MaxPooling2D
from keras.utils import np_utils
model = Sequential()
model.add(Conv2D (2, (3, 3), padding = 'same', input_shape = (8, 8, 1), activation = 'relu'))
model.add(MaxPooling2D (pool_size = (2, 2)))
model.add(Conv2D (3, (2, 2), padding = 'same', activation = 'relu'))
model.add(MaxPooling2D (pool_size = (2, 2)))
model.add(Flatten())
model.add(Dense (8, activation = 'relu'))
model.add(Dense (3, activation = 'softmax'))
https://tykimos.github.io/2017/01/27/CNN_Layer_Talk/
컨볼루션 신경망 모델 만들어보기
-
간단한 컨볼루션 신경망 모델을 만들어봅니다. 다음과 같은 순서로 진행하겠습니다
-
1. 문제 정의하기
-
2. 데이터 준비하기
-
3. 데이터셋 생성하기
-
4. 모델 구성하기
-
5. 모델 학습과정 설정하기
-
6. 모델 학습시키기
-
7. 모델 평가하기
-
문제 정의하기
-
좋은 예제와 그와 관련된 데이터 셋도 공개된 것이 많이 있지만, 직접 문제를 정의하고 데이터를 만들어 보는 것도 딥러닝을 처음 접하시는 분들에게는 크게 도움이 될 것 같습니다. 컨볼루션 신경망 모델에 적합한 문제는 이미지 기반의 분류입니다. 따라서 우리는 직접 손으로 삼각형, 사각형, 원을 그려 이미지로 저장한 다음 이를 분류하는 모델을 만들어 보겠습니다. 문제 형태와 입출력을 다음과 같이 정의해봅니다.
-
문제 형태 : 다중 클래스 분류
-
입력 : 손으로 그린 삼각형, 사각형, 원 이미지
-
출력 : 삼각형, 사각형, 원일 확률을 나타내는 벡터
-
가장 처음 필요한 패키지를 불러오고, 매번 실행 시마다 결과가 달라지지 않도록 랜덤 시드를 명시적으로 지정합니다.
-
데이터 준비하기
-
손으로 그린 삼각형, 사각형, 원 이미지를 만들기 위해서는 여러가지 방법이 있을 수 있다. 태블릿을 이용할 수도 있고, 종이에 그려서 사진으로 찍을 수도 있습니다.
-
http://tykimos.github.io/warehouse/2017-3-8_CNN_Getting_Started_handwriting_shape.zip 다운로드 가능
-
디렉토리
-
train
-
circle
-
rectangle
-
triangle
-
test
-
circle
-
rectangle
-
triangle
-
데이터셋 생성하기
-
케라스는 이미지 파일을 쉽게 학습시킬 수 있도록 ImageDataGenerator 클래스를 제공합니다. ImageDataGenerator 클래스는 데이터 증강(data argumentation)을 위해 막강한 기능을 제공하는데, 이 기능들은 다른 강좌에서 다루기로 하고, 본 강좌에서는 특정 폴더에 이미지를 분류 해 놓았을 때 이를 학습 시키기 위한 데이터셋으로 만들어 주는 기능을 사용해보겠습니다.
-
먼저 ImageDataGenerator 클래스를 이용하여 객체를 생성한 뒤 flow_from_directory() 함수를 호출하여 제네레이터 (generator)를 생성합니다. flow_from_directory() 함수의 주요 인자는 다음과 같습니다
-
첫번째 인자 : 이미지 경로를 지정합니다
-
target_size : 패치 이미지 크기를 지정합니다. 폴더에 있는 원본 이미지 크기가 다르더라도 target_size에 지정된 크기로 자동 조절됩니다.
-
batch_size : 배치 크기를 지정합니다
-
class_mode : 분류 방식에 대해서 지정합니다
-
categorical : 2D one -hot 부호화된 라벨이 반환됩니다.
-
binary : 1D 이진 라벨이 반환됩니다.
-
sparse : 1D 이진 라벨이 반환됩니다.
-
None : 라벨이 반환되지 않습니다.
-
본 예제에서는 패치 이미지 크기를 24 x 24 로 하였으니 target_size 도 (24, 24)로 셋팅하였습니다. 훈련 데이터 수가 클래스당 15개이니 배치 크기를 3으로 지정하여 총 5번 배치를 수해앟면 하나의 epoch가 수행될 수 잇도록 하였습니다. 다중 클래스 문제 이므로 cloass_mode 는 'categoricla'로 지정하였습니다. 그리고 제너레이터는 훈련용과 검증용으로 두 개를 만들었습니다.
-
모델 구성하기
-
영상 분류에 높은 성능을 보이고 있는 컨볼루션 신경망 모델을 구성해보겠습니다. 각 레이어들은 이전 강좌에서 살펴보았으므로 크게 어려움없이 구성할 수 있습니다.
-
컨볼루션 레이어 : 입력 이미지 크기 24 x 24, 입력 이미지 채널 3개, 필터 크기 3 x 3, 필터 수 32개, 활성화 함수 'relu'
-
컨볼루션 레이어 : 필터 크기 3 x 3, 필터 수 64개, 활성화 함수 'relu'
-
맥스풀링 레이어 : 풀 크기 2 x 2
-
플래튼 레이어
-
댄스 레이어 : 출력 뉴런 수 128개, 활성화 함수 'relu'
-
댄스 레이어 : 출력 뉴런 수 3개, 활성화 함수 'softmax'
-
모델 학습과정 설정하기
-
모델을 정의했다면 모델을 손실함수와 최적화 알고리즘으로 엮어봅니다.
-
loss : 현재 가중치 세트를 평가하는 데 사용한 손힐 함수 입니다. 다중 클래스 문제이므로 'categorical_crossentropy'으로 지정합니다.
-
optimizer : 최적의 가중치를 검색하는데 사용되는 최적화 알고리즘으로 효율적인 경사 하강법 알고리즘 중 하나인 'adam'을 사용합니다
-
metrics : 평가 척도를 나타내며 분류 문제에서는 일반적으로 'accuracy'으로 지정합니다.
-
모델 학습시키기
-
케라스에서는 모델을 학습시킬 때 주로 fit() 함수를 사용하지만 제네레이터로 생성된 배치로 학습시킬 경우에는 fit_generator() 함수를 사용합니다. 본 예제에서는 ImageDataGenerator 라는 제네레이터로 이미지를 다목 있는 배치로 학습시키기 때문에 fit_generator() 함수를 사용하겠습니다
-
첫번째 인자 : 훈련 데이터 셋을 제공할 제너레이터를 지정합니다 본 예제에서는 앞서 생성한 train_generator 으로 지정합니다.
-
steps_per_epoch : 한 epoch에 사용한 스탭 수를 지정합니다. 총 45개의 훈련 샘플이 있고 배치 사이즈가 3이므로 15 스텝으로 지정합니다.
-
epochs : 전체 훈련 데이터셋에 대해 학습 반복 횟수를 지정합니다. 100번을 반복적으로 학습시켜 보겠습니다.
-
validation_data : 검증 데이터셋을 제공할 제네레이터를 지정합니다. 본 예제에서는 앞서 생성한 validation_generator으로 지정합니다
-
validation_steps : 한 epoch 종료 시 마다 검증할 때 사용되는 검증 스텝 수를 지정합니다. 총 15개의 검증 샘플이 있고 배치사이즈가 3이므로 5스텝으로 지정합니다.
-
모델 평가하기
-
학습한 모델을 평가해봅니다. 제너레이터에서 제공되는 샘플로 평가할 떄는 evaluate_generator 함수를 사용합니다
-
모델 사용하기
-
모델 사용 시에 제네레이터에서 제공되는 샘플을 입력할 때는 predict_generator 함수를 사용합니다. 예측 결과는 클래스별 확률 벡터로 출력되며, 클래스에 해당하는 열을 알기 위해서는 제너레이터의 'class_indices'를 출력하면 해당 열의 클래스명을 알려줍니다
import numpy as np
from keras.models import Sequential
from keras.layers import Dense, Flatten
from keras.layers.convolutional import Conv2D, MaxPooling2D
from keras.preprocessing.image import ImageDataGenerator
# 랜덤 시드 고정시키기
np.random.seed(3)
# ImageDataGenerator 생성
train_datagen = ImageDataGenerator (rescale = 1./ 255)
train_generator = train_datagen.flow_from_directory(
'./data/train',
target_size = (24, 24),
batch_size = 3,
class_mode = "categorical"
)
test_datagen = ImageDataGenerator (rescale=1./255)
test_generator = train_datagen.flow_from_directory(
'./data/test',
target_size = (24, 24),
batch_size = 3,
class_mode = "categorical"
)
model = Sequential()
model.add(Conv2D(32, (3, 3), activation = "relu", input_shape = (24, 24, 3)))
model.add(Conv2D(64, (3, 3), activation = 'relu'))
model.add(MaxPooling2D(pool_size = (2, 2)))
model.add(Flatten())
model.add(Dense(128, activation = 'relu'))
model.add(Dense(3, activation = 'softmax'))
model.compile(loss = 'categorical_crossentropy', optimizer = 'adam', metrics = ['accuracy'])
model.fit_generator (
train_generator,
steps_per_epoch = 15,
epochs = 100,
validation_data = test_generator,
validation_steps = 5
)
print ("-- Evaluate --")
scores = model.evaluate_generator(test_generator, steps = 5)
print ("%s : %.2f%%" %(model.metrics_names[1], scores[1] * 100))
print ("-- Predict -- ")
output = model.predict_generator (test_generator, steps = 5)
np.set_printoptions(formatter={'float': lambda x: "{0:0.3f}".format(x)})
print (test_generator.class_indices)
print (output)
https://tykimos.github.io/2017/03/08/CNN_Getting_Started/
컨볼루션 신경망 모델을 위한 데이터 부풀리기
-
본 강좌에서는 컨볼루션 신경망 모델의 성능을 농피기 위한 방법 중 하나인 데이터 부풀리기에 대해서 알아보겠습니다. 훈련셋이 부족하거나 훈련셋이 시험셋의 특성을 충분히 반영하지 못할 때 이 방법을 상요하면 모델의 성능을 크게 향상시킬 수 있습니다. 케라스에서는 데이터 부풀리기를 위한 함수를 제공하기 때문에 파라미터 셋팅만으로 간단히 데이터 부풀리기를 할 수 있습니다.
-
1. 현실적인 문제
-
2. 기존모델 결과 보기
-
3. 데이터 부풀리기
-
4. 개선 모델 결과 보기
-
현실적인 문제
-
컨볼루션 신경망 모델 만들업괴 강좌에서 사용하였던 원, 사각형, 삼각형 데이터셋을 예제로 살펴보겠습니다. 구성은 훈련셋과 시험셋으로 되어 있는데, 아래 그림은 훈련셋 입니다.
-
그리고 아래 그림은 시험셋입니다. 훈련셋과 시험셋은 모두 한사람이 그린 것이라 거의 비슷합니다. 그래서 그런지 100% 정확도의 좋은 결과를 얻은듯 합니다.
-
도전 시험셋은 다른사람이 그려준 것이다.
-
막상 시험셋을 받아보니 자신감이 없어지면서 여러가지 생각이 듭니다.
-
아, 이것도 원, 사각형, 삼각형이구나
-
왜 이런 데이터를 진작에 학습시키지 않았을까 ?
-
새로 받은 시험셋 일부를 학습시켜볼까 ?
-
이렇게 간단한 문제도 개발과 현실이 차이가 이렇게 나는데, 실제 문제는 더 상황이 좋지 않겠지 ?
-
결국 하나의 결론에 도달합니다
-
---- 개발자가 시험셋을 만들면 안된다.
-
하지만 어떠한 문제에서도 미래에 들어올 데이터에 대해서는 알 수가 없기 때문에, 비슷한 상황일 것 같습니다. 먼저 도전 시험 셋으로 시험한 결과를 살펴본 뒤, 한정적인 훈련셋을 이용하여 최대한 발생할 수 있는 경우를 고려하여 훈련셋을 만드는 방법이 데이터 부풀리기에 대해서 알아보겠습니다
-
기존 모델 결과 보기
-
컨볼루션 신경망 모델 만들어보기 강좌에서 사용했던 모델을 그대로 가지고와서 도전 시험셋으로 결과를 알아본다. >> 40 %결과 :: 오버피팅이 제대로 된 모델이라고 볼 수 있다.
-
데이터 부풀리기
-
케라스에서는 ImageDataGenerator 함수를 통해서 데이터 부풀리기 기능을 제공합니다 keras.o 페이지를 보면 아래와 같은 옵션으로 데이터 부풀리기를 할 수 있습니다.
keras.preprocessing.image.ImageDataGenerator(featurewise_center=False,
samplewise_center=False,
featurewise_std_normalization=False,
samplewise_std_normalization=False,
zca_whitening=False,
rotation_range=0.,
width_shift_range=0.,
height_shift_range=0.,
shear_range=0.,
zoom_range=0.,
channel_shift_range=0.,
fill_mode='nearest',
cval=0.,
horizontal_flip=False,
vertical_flip=False,
rescale=None,
preprocessing_function=None,
data_format=K.image_data_format())
-
그럼 훈련셋 중 하나인 삼각형을 골라 데이터 부풀리기를 해보겠습니다. 원본이 되는 삼각형은 다음과 같습니다.
-
rotation_range = 90
-
지정된 각도 범위내에서 임의의 원본 이미지를 회전시킵니다. 단위는 도이며, 정수형입니다. 예를 들어 90이라면 0도에서 90도 사이에 임의의 각도로 회전시킵니다.
-
width_shift_range = 0.1
-
지정된 수평방향 이동 범위내에서 임의로 원본 이미지를 이동시킵니다. 수치는 전체 넓이의 비율(실수)로 나타냅니다. 예를 들어 0.1이고 전체 넓이가 100이면, 10픽셀 내외로 좌우 이동시킵니다.
-
height_shift_range = 0.1
-
지정된 수직 방향 이동 범위 내에서 임의로 원본 이미지를 이동시킵니다 .수치는 전체 높이의 비율 (실수)로 나타냅니다. 예를 들어 0.1이고 전체 높이가 100이면 10픽셀 내외로 상하 이동시킵니다.
-
shear_range = 0.5
-
밀림 강도 범위 내에서 임의의 원본이미지를 변형시킵니다. 수치는 시계 반대방향으로 밀림 강도를 라디안으로 나타냅니다. 예를 들어 0.5라면, 0.5 라디안 내외로 시계반대방향으로 번형시킵니다.
-
zoom_range = 0.3
-
지정된 확대/축소 범위 내에서 임의로 원본이미지를 확대 / 축소 합니다. "1-수치" 부터 "1 + 수치" 사이 범위로 확대 / 축소를 합니다. 예를 들어 0.3 이라면 0.7배에서 1.3배 크기 변화를 시킵니다.
-
horizontal_flip = True
-
수평방향으로 뒤집기를 합니다.
-
vertical_flip = True
-
수직 방향으로 뒤집기를 합니다.
-
아래 코드는 ImageDataGenerator 함수를 이용하여 지정된 파라미터로 원본이미지에 대해 데이터 부풀리기를 수행한 후 그 결과를 특정 폴더에 저장하는 코드입니다. 여러 파라미터를 사용하였기 때문에 이를 혼합하여 데이터 부풀리기를 수행합니다. 즉 확대 / 축소도 하고 좌우 이동도 지정하였다면, 축소하면서 좌로 이동된 이미지도 생성됩니다.
https://tykimos.github.io/2017/06/10/CNN_Data_Augmentation/
'Deep Learning > Keras' 카테고리의 다른 글
Machine Learning ( Keras) (0) | 2019.07.15 |
---|