본문 바로가기

ML & AI Theory

케라스 실습] tf.keras API를 사용한 다층 신경망 훈련 기본

반응형

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))

 

하이퍼파라미터를 전혀 튜닝하지 않은 매우 간단한 모델을 실습해 보았습니다.

반응형