Keras 10mins#

Tensorflow2 부터 Keras가 Core API 로 제공됩니다. 백엔드 엔진으로 Tensorflow를 쓰시는 분들은 Tensorflow2를 통해 Keras를 사용하시길 추천드립니다.

Keras는 Deep Learning 을 위한 배우고 사용하기 위한 API 를 지향하면서 다양한 백엔드 엔진에 대한 통일된 API 를 제공합니다. 몇 줄 안되는 코드로 Deep Learning 구조들을 쉽게 만들고 시험해 볼수 있습니다.

Deep Learning Framework Power Scores 2018

참조

from IPython.core.display import display, HTML
display(HTML("<style>.container { width:100% !important; }</style>"))

%matplotlib inline
import matplotlib.pyplot as plt

import numpy as np
np.set_printoptions(precision=5)

import pandas as pd
pd.set_option('display.max_rows', None) 
pd.set_option('display.max_columns', None) 
pd.set_option('display.max_colwidth', -1)
pd.options.display.float_format = '{:,.5f}'.format

print("pandas ver={}".format(pd.__version__))
print("numpy ver={}".format(np.__version__))
pandas ver=0.24.1
numpy ver=1.16.2

Keras 에서는 2가지 방식의 딥러닝 모델 구성 방식을 제공합니다.

  • Sequential Model

    • 스택처럼 쌓아서 만드는 단일 구조의 딥러닝 모델을 간편하게 설계할 수 있습니다.

  • Function API

    • 다양한 모델을 엮어 만드는 복잡한 구조의 딥러닝 모델을 설계할 수 있습니다.

    • multi-output models, directed acyclic graphs, 또는 shared layers를 가지는 모델들의 설계가 가능합니다.

MNIST 데이터셋을 2가지 방식으로 DNN을 구축해보면서 각 구현 방식의 차이점에 대해서 알아봅니다.

from keras.datasets import mnist

# MNIST 샘플 데이터
(np_train_xs, np_train_ys), (np_test_xs, np_test_ys) = mnist.load_data()

r, w, h = np_train_xs.shape
print("r={}, w={}, h={}".format(r, w, h))

# 전처리 - 28x28 2D 이미지를 DNN 모델링을 위해 784 1D 로 변환합니다. 
dim_x = w * h
np_train_xs = np_train_xs.reshape(-1, dim_x).astype('float32')
np_test_xs = np_test_xs.reshape(-1, dim_x).astype('float32')
print(np_train_xs.shape)
print(np_test_xs.shape)

# 전처리 - 0~1 사이로 데이터 Min Max Scaling
from sklearn import preprocessing

scaler = preprocessing.MinMaxScaler(feature_range=(0, 1))
np_train_xs = scaler.fit_transform(np_train_xs)
np_test_xs = scaler.transform(np_test_xs)

# 전처리 - 레이블을 One Hot 인코팅 
from keras.utils import np_utils

n_class = 10
np_train_ys = np_utils.to_categorical(np_train_ys, n_class)
np_test_ys = np_utils.to_categorical(np_test_ys, n_class)
print(np_train_ys.shape)
print(np_train_ys[:3])
Using TensorFlow backend.
r=60000, w=28, h=28
(60000, 784)
(10000, 784)
(60000, 10)
[[0. 0. 0. 0. 0. 1. 0. 0. 0. 0.]
 [1. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
 [0. 0. 0. 0. 1. 0. 0. 0. 0. 0.]]
from keras import layers, models, optimizers

# Sequential Model를 사용
def dnn_seq(n_i, n_hs, n_o):
    m = models.Sequential()
    m.add(layers.Dense(n_i, activation='relu', input_shape=(n_i,)))
    for l, n_h in enumerate(n_hs):
        m.add(layers.Dense(n_h, activation='relu'))
    m.add(layers.Dense(n_o, activation = 'softmax'))
    sgd = optimizers.SGD(lr=0.01, decay=1e-6, momentum=0.9, nesterov=True)
    m.compile(optimizer=sgd, loss='categorical_crossentropy', metrics=['accuracy'])
    return m

class DnnSeq(models.Sequential):
    def __init__(self, n_i, n_hs, n_o):
        super().__init__()
        self.add(layers.Dense(n_i, activation='relu', input_shape=(n_i,)))
        for l, n_h in enumerate(n_hs):
            self.add(layers.Dense(n_h, activation='relu'))
        self.add(layers.Dense(n_o, activation = 'softmax'))
        sgd = optimizers.SGD(lr=0.01, decay=1e-6, momentum=0.9, nesterov=True)
        self.compile(optimizer=sgd, loss='categorical_crossentropy', metrics=['accuracy'])

# Function API를 사용
def dnn_func(n_i, n_hs, n_o):
    i = layers.Input(shape = (n_i,))
    for l, n_h in enumerate(n_hs):
        if l == 0:
            h = layers.Dense(n_h, activation = 'relu')(i)            
        else:
            h = layers.Dense(n_h, activation = 'relu')(h)    
    o = layers.Dense(n_o, activation = 'softmax')(h)
    m = models.Model(inputs=[i], outputs=[o])
    sgd = optimizers.SGD(lr=0.01, decay=1e-6, momentum=0.9, nesterov=True)
    m.compile(optimizer=sgd, loss='categorical_crossentropy', metrics=['accuracy'])
    return m

class DnnFunc(models.Model):
    def __init__(self, n_i, n_hs, n_o):
        i = layers.Input(shape = (n_i,))
        for l, n_h in enumerate(n_hs):
            if l == 0:
                h = layers.Dense(n_h, activation = 'relu')(i)            
            else:
                h = layers.Dense(n_h, activation = 'relu')(h)    
        o = layers.Dense(n_o, activation = 'softmax')(h)
        
        super().__init__(inputs=[i], outputs=[o])
        sgd = optimizers.SGD(lr=0.01, decay=1e-6, momentum=0.9, nesterov=True)
        self.compile(optimizer=sgd, loss='categorical_crossentropy', metrics=['accuracy'])
        
#model = dnn_seq(n_i = dim_x, n_hs = [384, 128, 64], n_o = n_class)
#model = DnnSeq(n_i = dim_x, n_hs = [384, 128, 64], n_o = n_class)
#model = dnn_func(n_i = dim_x, n_hs = [384, 128, 64], n_o = n_class)
model = DnnFunc(n_i = dim_x, n_hs = [384, 128, 64], n_o = n_class)

model.summary()
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
input_2 (InputLayer)         (None, 784)               0         
_________________________________________________________________
dense_5 (Dense)              (None, 384)               301440    
_________________________________________________________________
dense_6 (Dense)              (None, 128)               49280     
_________________________________________________________________
dense_7 (Dense)              (None, 64)                8256      
_________________________________________________________________
dense_8 (Dense)              (None, 10)                650       
=================================================================
Total params: 359,626
Trainable params: 359,626
Non-trainable params: 0
_________________________________________________________________
# 학습과 평가
history = model.fit(np_train_xs, np_train_ys, epochs=10, batch_size=100, validation_split=0.2)
eval = model.evaluate(np_test_xs, np_test_ys, batch_size=100)
print(eval)
WARNING:tensorflow:From /usr/local/lib/python3.5/dist-packages/tensorflow/python/ops/math_ops.py:3066: to_int32 (from tensorflow.python.ops.math_ops) is deprecated and will be removed in a future version.
Instructions for updating:
Use tf.cast instead.
Train on 48000 samples, validate on 12000 samples
Epoch 1/10
48000/48000 [==============================] - 2s 32us/step - loss: 0.4390 - acc: 0.8731 - val_loss: 0.1959 - val_acc: 0.9461
Epoch 2/10
48000/48000 [==============================] - 1s 28us/step - loss: 0.1683 - acc: 0.9508 - val_loss: 0.1449 - val_acc: 0.9585
Epoch 3/10
48000/48000 [==============================] - 1s 28us/step - loss: 0.1157 - acc: 0.9659 - val_loss: 0.1164 - val_acc: 0.9668
Epoch 4/10
48000/48000 [==============================] - 1s 28us/step - loss: 0.0860 - acc: 0.9745 - val_loss: 0.1134 - val_acc: 0.9660
Epoch 5/10
48000/48000 [==============================] - 1s 28us/step - loss: 0.0669 - acc: 0.9796 - val_loss: 0.0897 - val_acc: 0.9734
Epoch 6/10
48000/48000 [==============================] - 1s 28us/step - loss: 0.0509 - acc: 0.9854 - val_loss: 0.0908 - val_acc: 0.9717
Epoch 7/10
48000/48000 [==============================] - 1s 28us/step - loss: 0.0400 - acc: 0.9881 - val_loss: 0.0889 - val_acc: 0.9733
Epoch 8/10
48000/48000 [==============================] - 1s 28us/step - loss: 0.0320 - acc: 0.9910 - val_loss: 0.0808 - val_acc: 0.9758
Epoch 9/10
48000/48000 [==============================] - 1s 28us/step - loss: 0.0247 - acc: 0.9933 - val_loss: 0.0783 - val_acc: 0.9770
Epoch 10/10
48000/48000 [==============================] - 1s 28us/step - loss: 0.0189 - acc: 0.9949 - val_loss: 0.0767 - val_acc: 0.9792
10000/10000 [==============================] - 0s 9us/step
[0.07681204915992566, 0.9782000082731247]

Model Persistance#

Keras에서 학습한 모델을 저장하고 불러오는 방법을 알아봅니다.

참조: https://keras.io/getting-started/faq/#how-can-i-save-a-keras-model

전체 모델 저장하기/불러오기 (architecture + weights + optimizer state)#

모델을 하나의 HD5 파일 형식으로 하나의 파일에 저장하고 불러올 수 있습니다. 저장되는 정보는 아래와 같습니다.

  • the architecture of the model, allowing to re-create the model

  • the weights of the model

  • the training configuration (loss, optimizer)

  • the state of the optimizer, allowing to resume training exactly where you left off.

from keras.models import load_model

model.save('my_model.h5')  # creates a HDF5 file 'my_model.h5'
del model  # deletes the existing model

# returns a compiled model
# identical to the previous one
model = load_model('my_model.h5')

전체 모델을 저장하고/불러오기 위해서는 Keras의 기본 모델을 이용하여 모델을 생성해야 합니다. 코드의 가독성을 위해 Keras 모델을 상속하여 모델 클래스를 만들어 사용하는 경우는 모델의 가중치 값만을 저장하고 불러올 수 있습니다. 복잡한 모델의 경우는 모델의 구조를 저장하는 것이 유리하기때문에 기본 Keras 클래스를 통해 모델을 생성하는 것을 추천합니다.

모델 구조만 저장하기/불러오기#

JSON 또는 YAML 형태로 모델을 저장하고 불러올 수 있습니다.

from keras.models import model_from_json
from keras.models import model_from_yaml

# JSON
json_string = model.to_json()
model = model_from_json(json_string)

# YAML
yaml_string = model.to_yaml()
model = model_from_yaml(yaml_string)

모델 가중치만 저장하기/불러오기#

모델을 학습시킨 결과 가중치만 HDF5 형식으로 저장하고 불러올 수 있습니다. 저장된 가중치를 사용하기 위해서는 저장한 모델의 구조가 동일해야 합니다

model.save_weights('my_model_weights.h5')
model.load_weights('my_model_weights.h5')

Fine-tuning 또는 Transfer-learning 과 같이 불러온 가중치를 다른 모델에서 사용해야 할 경우에는 layer 의 이름을 통해 해당 layer 의 가중치만 불러오기도 가능합니다.

"""
Assuming the original model looks like this:
    model = Sequential()
    model.add(Dense(2, input_dim=3, name='dense_1'))
    model.add(Dense(3, name='dense_2'))
    ...
    model.save_weights(fname)
"""

# new model
model = Sequential()
model.add(Dense(2, input_dim=3, name='dense_1'))  # will be loaded
model.add(Dense(10, name='new_dense'))  # will not be loaded

# load weights from first model; will only affect the first layer, dense_1.
model.load_weights(fname, by_name=True)

모델에 custom layers (or other custom objects) 포함된 경우#

자신만의 모델을 위해 custom layer/object/function 를 작성한 경우, 모델을 불러올 때는 해당 정보를 모델에 넘겨 줘야 합니다.

from keras.models import load_model
# Assuming your model includes instance of an "AttentionLayer" class
model = load_model('my_model.h5', custom_objects={'AttentionLayer': AttentionLayer})

from keras.utils import CustomObjectScope
# Alternatively, you can use a custom object scope:
with CustomObjectScope({'AttentionLayer': AttentionLayer}):
    model = load_model('my_model.h5')

from keras.models import model_from_json
# Custom objects handling works the same way for load_model, model_from_json, model_from_yaml:
model = model_from_json(json_string, custom_objects={'AttentionLayer': AttentionLayer})