tf.keras 고수준 API를 어떻게 신경망을 훈련하는지 알아보겠습니다.
1. 훈련 데이터 준비
import struct
import os
import numpy as np
import tensorflow as tf
def load_mnist(path, kind='train'):
"""`path`에서 MNIST 데이터 적재하기"""
labels_path = os.path.join(path, '%s-labels-idx1-ubyte/' % kind)
images_path = os.path.join(path, '%s-images-idx3-ubyte/' % kind)
labels_file = ('%s-labels.idx1-ubyte' % kind)
images_file = ('%s-images.idx3-ubyte' % kind)
print(labels_path)
with open(labels_path + labels_file, 'rb') as lbpath:
magic, n = struct.unpack('>II', lbpath.read(8))
labels = np.fromfile(lbpath, dtype=np.uint8)
with open(images_path + images_file, 'rb') as imgpath:
magic, num, rows, cols = struct.unpack(">IIII", imgpath.read(16))
images = np.fromfile(imgpath, dtype=np.uint8).reshape(len(labels), 784)
images = ((images / 255.) - .5) * 2
return images, labels
X_train, y_train = load_mnist('./data/mnist/', kind='train')
print('행: %d, 열: %d' %(X_train.shape[0], X_train.shape[1]))
X_test, y_test = load_mnist('./data/mnist/', kind='t10k')
print('행: %d, 열: %d' %(X_test.shape[0], X_test.shape[1]))
## 평균을 0으로 만들고 표준 편차로 나눕니다.
mean_vals = np.mean(X_train, axis=0)
std_val = np.std(X_train)
X_train_centered = (X_train - mean_vals)/std_val
X_test_centered = (X_test - mean_vals)/std_val
del X_train, X_test
print(X_train_centered.shape, y_train.shape)
print(X_test_centered.shape, y_test.shape)
일관된 겨로가를 만들기 위해 넘팡 난수 초깃값을 설정하겠습니다.
np.random.seed(123)
훈련 데이터를 준비하기 위해 클래스 레이블(0~9 사이 정수)을 원-핫 인코딩으로 변경하겠습니다. tf.keras에는 이를 위한 편리한 도구가 준비되어 있습니다.
y_train_onehot = tf.keras.utils.to_categorical(y_train)
print('처음 3개 레이블: ', y_train[:3])
print('처음 3개 레이블(원-핫): ', y_train_onehot[:3])
to_categorical 함수는 입력 텐서에서 가장 큰 정수를 찾아 원-핫 인코딩 크기를 결정합니다. 앞예제에서처럼 정수 0은 첫 번째 원소가 1이 되는 식입니다. num_classes 매개변수를 사용하여 입력의 최댓값보다 더 큰 원-핫 인코딩을 만들 수도 있습니다.
훈련 데이터가 성공적으로 준비되었습니다.
2. 피드포워드 신경망 구성
이제 신경망을 구현하겠습니다. 간단하게 세 개의 완전 연결된 층을 만들어 보겠습니다. 처음 두 개의 층은 하이퍼볼릭 탄젠트(tanh)활성화 함수를 가진 50개의 은닉 유닛으로 이루어집니다. 마지막 층은 열 개의 클래스 레이블에 해당하는 열 개의 은닉 유닛을 가집니다. 마지막 층은 각 클래스의 확률을 계산하기 위해 소프트맥스(softmax)함수를 사용합니다. 다음 코드에서처럼 tf.keras는 이런 작업을 매우 간단하게 처리할 수 있습니다.
Note: tanh와 softmax함수는 처음 등장한 활성화 함수입니다.
model = tf.keras.models.Sequential()
model.add(
tf.keras.layers.Dense(
units=50,
input_dim=X_train_centered.shape[1],
kernel_initializer='glorot_uniform',
bias_initializer='zeros',
activation='tanh'))
model.add(
tf.keras.layers.Dense(
units=50,
input_dim=50,
kernel_initializer='glorot_uniform',
bias_initializer='zeros',
activation='tanh'))
model.add(
tf.keras.layers.Dense(
units=y_train_onehot.shape[1],
input_dim=50,
kernel_initializer='glorot_uniform',
bias_initializer='zeros',
activation='softmax'))
먼저 Sequential 클래스를 사용하여 피드포워드 신경망을 구현하는 새로운 모델을 초기화합니다. 그다음 원하는 만큼 층을 추가할 수 있습니다. 처음 추가한 층은 입력층과 연결되기 때문에 input_dim 속성이 훈련 세트에 있는 특성(열) 개수와 일치해야 합니다. (이 신경망에서는 784개의 특성 또는 픽셀입니다.)
또한 두 개의 연속된 층에서 출력 유닛(units)과 입력 유닛(input_dim)이 일치해야 합니다. 앞 코드에서 두 개의 은닉층은 50개의 은익 유닛과 한 개의 절펴 유닛을 가집니다. 출력층의 유닛 개수는 고유한 클래스 레이블의 개수와 같아야 합니다. 원-핫 인코딩된 클래스 레이블 배열의 열 개수입니다.
Note : kernel_initializer='glorot_uniform'은 새로운 가중치 행렬 초기화 알고리즘입니다. 심층 신경망을 안정적으로 초기화하는 방법입니다. 절편은 일반적으로 0으로 초기화합니다. 사실 이 설정으 케라스의 기본값입니다.
model.summary()
지금까지 만든 모델 구조를 summary()메서드를 사용하여 일목요연하게 출력해 봅니다.
Model: "sequential"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
dense (Dense) (None, 50) 39250
_________________________________________________________________
dense_1 (Dense) (None, 50) 2550
_________________________________________________________________
dense_2 (Dense) (None, 10) 510
=================================================================
Total params: 42,310
Trainable params: 42,310
Non-trainable params: 0
_________________________________________________________________
3. 피드포워드 신경망 훈련
모델 구성을 마치면 훈련을 수행하기 전에 모델을 컴파일해야 합니다. 이 단계에서 최적화할 손실 함수를 정의하고 최적화에 사용할 경사 하강법 옵티마이저를 선택합니다. 여기서는 하강법 최적화를 선택하겠습니다.
또 에포크마다 학습률을 조절하기 위한 학습률 감쇠 상수와 모멘텀 값을 지정합니다. 마지막으로 비용 함수(또는 손실 함수)를 categorical_crossentropy로 설정합니다.
이진 크로스 엔트로피는 로지스틱 손실 함수의 기술적인 표현입니다. 범주형 크로스 엔트로피는 소프트맥스를 사용하여 다중 클래스 예측으로 일반화한 것입니다.
sgd_optimizer = tf.keras.optimizers.SGD(learning_rate=0.001, decay=1e-7, momentum=.9)
model.compile(optimizer=sgd_optimizer,loss='categorical_crossentropy')
history = model.fit(X_train_centered, y_train_onehot,batch_size=64, epochs=50,verbose=1,validation_split=0.1)
훈련하는 동안 비용 함수 값을 출력하는 기능은 아주 유용합니다. 훈련 도중에 비용이 감소하는지 여부를 빨리 확인해서 감소하지 않느나면 하이퍼파라미터를 튜닝하기 위해 알고리즘을 일찍 멈출 수 있습니다.
클래스 레이블을 예측하려면 predict_classes메서드를 사용하여 정수로 된 클래스 레이블을 얻을 수 있습니다.
y_train_pred = model.predict_classes(X_train_centered, verbose=0)
correct_preds = np.sum(y_train == y_train_pred, axis=0)
train_acc = correct_preds / y_train.shape[0]
print('처음 3개 예측: ', y_train_pred[:3])
print('훈련 정확도: %.2f%%' % (train_acc * 100))
y_test_pred = model.predict_classes(X_test_centered, verbose=0)
correct_preds = np.sum(y_test == y_test_pred, axis=0)
test_acc = correct_preds / y_test.shape[0]
print('테스트 정확도: %.2f%%' % (test_acc * 100))
하이퍼파라미터를 전혀 튜닝하지 않은 매우 간단한 모델을 실습해 보았습니다.
'ML & AI Theory' 카테고리의 다른 글
텐서플로의 계산 그래프 : 1.0 vs 2.x 버젼 차이 (0) | 2021.07.10 |
---|---|
텐서를 다차원 배열로 변환하기 (0) | 2021.07.10 |
텐서플로, 랭크와 텐서를 확인하는 방법 (0) | 2021.07.06 |
다층 신경망의 활성화 함수 : 시그모이드, 소프트맥스, 하이퍼볼릭탄젠트, 렐루함수 (0) | 2021.07.05 |
텐서플로우 #1] 텐서플로우의 시작과 배열 구조 다루기 (0) | 2021.07.03 |
RNN 실전실습 : 시퀀스 모델링을 위한 다층 RNN구현 : IMDB 영화리뷰 구현 (0) | 2021.06.28 |
RNN 지식 : 순환 신경망으로 시퀀스 데이터 모델링 위한 기본 지식 (0) | 2021.06.28 |
인공신경망관련 용어정리 : 퍼셉트론, MLP, Feed Forward, Back Propagation, 활성 함수, 시그모이드 함수, Gradient Descent Method (0) | 2021.06.28 |