Data Analysis & ML/Deep Learning

[Deep Learning][딥러닝] CNN_MNIST분류 / 모델저장/ FunctionalAPI

YSY^ 2020. 11. 9. 18:42

Convolutional Neural Network 구현

import matplotlib.pyplot as plt
# 학습결과 그래프 함수
# loss 그래프
def loss_plot(history):
#     plt.figure(figsize=(10,7))
    plt.plot(history.history['loss'], label='Train loss')
    plt.plot(history.history['val_loss'], label='Validation loss')
    plt.title('Loss')
    plt.xlabel('Epoch')
    plt.ylabel('Loss')
    plt.legend()
    plt.show()
# accuracy 그래프
def accuracy_plot(history):
#     plt.figure(figsize=(10,7))
    plt.plot(history.history['accuracy'], label='Train accuracy')
    plt.plot(history.history['val_accuracy'], label='Validation accuracy')
    plt.title('accuracy')
    plt.xlabel('Epoch')
    plt.ylabel('accuracy')
    plt.legend()
    plt.show()

MNIST CNN 적용

import tensorflow as tf
import tensorflow.keras as keras
import tensorflow.keras.layers as layers
import numpy as np
np.random.seed(1)
tf.random.set_seed(1)

# 하이퍼파라미터 정의
learning_rate = 0.001
N_EPOCHS = 20
N_BATCH = 100
N_CLASS = 10

(train_images, train_labels), (test_images, test_labels)  = keras.datasets.mnist.load_data()

N_TRAIN = train_images.shape[0]
N_TEST = test_images.shape[0]

train_images.shape, test_images.shape
#(개수, height, width, channel)


# 전처리 - image-0 ~ 1사이로 조정
X_train = train_images/255
X_test = test_images/255
# X의 차원을 3차원->4차원으로 변경. (channel 추가)
# (60000,28,28) => (60000,28,28, 1)
X_train = X_train[..., np.newaxis]
X_test = X_test[..., np.newaxis]
X_train.shape, X_test.shape

# y를 one hot encoding
print(train_labels[:5])
y_train = keras.utils.to_categorical(train_labels, N_CLASS)
y_test = keras.utils.to_categorical(test_labels, N_CLASS)
print(y_train[:5])


# Dataset 구성
train_dataset = tf.data.Dataset.from_tensor_slices((X_train, y_train))\
                               .shuffle(N_TRAIN)\
                               .batch(N_BATCH, drop_remainder=True).repeat()

test_dataset = tf.data.Dataset.from_tensor_slices((X_test, y_test)).batch(N_BATCH)
# 모델 구성 CNN - Conv, Pooling , Dense
def create_model():
    model = keras.Sequential()
    # Feature Extraction -> Convolution + Pooling layer
    # Conv2D(Convolution Layer) + input_shape 지정(height, width, channel)
    model.add(layers.Conv2D(filters=32, #필터개수 (1개의 필터가 1개의 특성 찾는다.)
                            kernel_size=3, #필터의 크기. (3,3) height,width가 같은 경우 하나만 써준다.
                            padding='SAME',# 패딩방식-SAME,VALID (대소문자 상관없다.
                            strides=1,  #얼만큼씩 이동할지 지정. default: (1,1)
                            activation='relu',
                            input_shape=(28,28,1)))
    # MaxPooling Layer
    model.add(layers.MaxPool2D(pool_size=2,  #size: default=(2,2)
                               strides=2, #기본값: None=>pool size와 동일
                               padding='SAME'))

    model.add(layers.Conv2D(filters=64, kernel_size=3, padding='SAME', activation='relu'))
    model.add(layers.MaxPool2D(padding='SAME'))

    model.add(layers.Conv2D(filters=128, kernel_size=3, padding='SAME', activation='relu'))
    model.add(layers.MaxPool2D(padding='SAME'))
    # Classification Layers => Fully Connected Layers
    model.add(layers.Flatten())
    model.add(layers.Dense(256, activation='relu'))
    model.add(layers.Dropout(0.4))
    #출력 layer
    model.add(layers.Dense(N_CLASS, activation='softmax'))

    return model

# 모델생성 및 컴파일
model = create_model()
model.compile(optimizer=keras.optimizers.Adam(learning_rate),
              loss='categorical_crossentropy',
              metrics=['accuracy'])
model.summary()

# 학습전에 모델 평가
model.evaluate(test_dataset)
# loss 가 np.log(클래스수) 보다 크면(많이 차이가 나면) 모델을 수정한다.
# [2.3034281730651855, 0.09740000218153]


np.log(N_CLASS) #2.302585092994046


steps_per_epoch = N_TRAIN//N_BATCH
validation_steps = int(np.ceil(N_TEST/N_BATCH))


history = model.fit(train_dataset, epochs=N_EPOCHS, steps_per_epoch=steps_per_epoch,
                    validation_data=test_dataset, validation_steps=validation_steps)

 

prediction error가 발생한 example 확인

# test set을 예측
test_pred = model.predict_classes(X_test)

# 실제 label과 예측 결과 비교
error_idx = np.where(np.not_equal(test_labels, test_pred))[0]
error_idx

# 틀린것들 label비교
for idx in error_idx[:10]:
    print((test_labels[idx], test_pred[idx]))


plt.figure(figsize=(30,10))
for i in range(10):
    idx = error_idx[i]
    plt.subplot(1,10, i+1)
    plt.imshow(test_images[idx], cmap='Greys')
    plt.xticks([])
    plt.yticks([])
plt.show()

사진

모델 저장

  1. 학습이 끝난 모델의 파라미터만 저장
  2. 모델 전체 저장
  3. Callback 함수를 이용해 학습시 가장 좋은 지표의 모델 저장

학습한 Weight (파라미터) 저장 및 불러오기

  • 가중치를 저장하여 나중에 재학습 없이 학습된 가중치를 사용할 수 있다.
  • 저장: model.save_weights("저장경로")
  • 불러오기: model.load_weights('불러올경로')
  • 저장형식
    • Tensorflow Checkpoint (기본방식)
    • HDF5
      • save_weights(.., save_format='h5')
import os
cur_dir = os.getcwd() #현재 작업디렉토리
ckpt_dir = 'checkpoints' #weight를 저장할 디렉토리
file_name = 'mnist_cnn_weights.ckpt' #저장파일명

dir = os.path.join(cur_dir, ckpt_dir)
os.makedirs(dir, exist_ok=True)

file_path = os.path.join(dir, file_name)
print(file_path)

# 모델의 weight들을 저장.
model.save_weights(file_path)
# 가중치 불러오기
# 모델 생성
new_model = create_model()
new_model.compile(optimizer=keras.optimizers.Adam(learning_rate),
                  loss='categorical_crossentropy',
                  metrics=['accuracy'])

new_model.evaluate(test_dataset) # loss: 2.3040 - accuracy: 0.0910

# 새 모델에 저장된 가중치 설정
new_model.load_weights(file_path)

new_model.evaluate(test_dataset) # loss: 0.0300 - accuracy: 0.9940

전체 모델 저장하고 불러오기

  • 저장: model.save('저장할디렉토리')
  • 불러오기: tf.keras.models.load_model('저장파일경로')
  • 저장 형식

HDF5 형식으로 저장

model_h5_dir = 'save_model/h5'
model_h5_path = os.path.join(cur_dir, model_h5_dir)
model_h5_file = os.path.join(model_h5_path, 'mnist_cnn_h5_model.h5')
os.makedirs(model_h5_path, exist_ok=True)


print(model_h5_file) #/content/save_model/h5/mnist_cnn_h5_model.h5

# 학습한 모델 저장. 모델구조+컴파일내용+weight 모두 저장.
model.save(model_h5_file, save_format='h5')
# 저장한 모델 불러오기
new_model2 = keras.models.load_model(model_h5_file)

new_model2.summary()

 

new_model2.evaluate(test_dataset) # loss: 0.0300 - accuracy: 0.9940

tensorflow SaveModel 형식으로 저장

#SaveModel 형식은 저장할 디렉토리까지 지정한다.
save_model_path = os.path.join(cur_dir, 'save_model/saved_model/mnist_cnn_sm')
print(save_model_path)

model.save(save_model_path)

# 모델 불러오기
new_model3 = keras.models.load_model(save_model_path)
new_model3.summary()

사진

new_model3.evaluate(test_dataset) # loss: 0.0300 - accuracy: 0.9940

Callback을 사용한 모델 저장 및 Early Stopping

  • callback은 학습시 특정 이벤트 발생시 호출되는 다양한 함수를 제공하여 자동화 처리를 지원한다. (cf: 프로그래밍의 콜백함수)
  • 다양한 콜백 클래스가 제공된다.
  • EarlyStopping: Validation set에 대한 평가지표가 더 이상 개선되지 않을 때 학습을 자동으로 멈춤
    • monitor: 모니터링할 평가지표 지정. (ex: accuracy)
    • patience: epoch 수 지정. validation 평가 지표가 개선이 안되더라도 지정한 epoch만큼 반복한다. 지정한 epoch만큼 반복 후에도 개선이 되지 않으면 중단한다.
  • ModelCheckpoint: 지정한 평가지표(예:validation loss)가 가장 좋을 때 모델과 weight를 저장하여 overfitting이 발생하기 전의 model을 나중에 불러들여 사용할 수 있음
    • save_best_only=True: monitoring 중인 measure를 기준으로 최적의 모형의 weight만 저장
  • callback 객체들을 리스트로 묶은 뒤 fit()의 callback 매개변수에 전달한다.
model = create_model()
model.compile(optimizer='adam', 
              loss='categorical_crossentropy',
              metrics=['accuracy'])

ckpt_path = os.path.join(cur_dir, 'callback_ckpt')
os.makedirs(ckpt_path, exist_ok=True)

ckpt_filepath = os.path.join(ckpt_path, 'mnist_cnn_{epoch}.ckpt')
print(ckpt_filepath)
# callback 생성
# epoch마다 학습한 weight들을 저장하는 callback
ckpt_callback = keras.callbacks.ModelCheckpoint(filepath=ckpt_filepath,
                                                save_weights_olny=True #True:weight만저장, False: 모델까지 저장.
                                                )
# early stopping callback
early_stop_callback = keras.callbacks.EarlyStopping(monitor='val_loss', patience=10)

history = model.fit(train_dataset, epochs=N_EPOCHS, steps_per_epoch=steps_per_epoch,
                    validation_data=test_dataset, validation_steps=validation_steps,
                    callbacks=[ckpt_callback, early_stop_callback])

Functional API

  • 함수형 API를 사용하여 모델을 구성
  • 각각의 레이어를 만든다.
  • 각 레이어를 생성할 때 이전레이어를 지정해서 모델을 만든다.
  • input과 output 레이어를 전달하여 모델을 생성한다.
import tensorflow as tf
import tensorflow.keras as keras
import tensorflow.keras.layers as layers

def create_model_fn():
    # Input 레이어
    input = layers.Input(shape=(28,28,1))
    conv1 = layers.Conv2D(filters=32, kernel_size=3, padding='SAME', activation='relu')(input)
    pool1 = layers.MaxPool2D(padding='SAME')(conv1)

    conv2 = layers.Conv2D(filters=64, kernel_size=3, padding='SAME', activation='relu')(pool1)
    pool2 = layers.MaxPool2D(padding='SAME')(conv2)

    flatten = layers.Flatten()(pool2)
    dense1 = layers.Dense(256, activation='relu')(flatten)
    dropout1 = layers.Dropout(0.5)(dense1)
    output = layers.Dense(10, activation='softmax')(dropout1)

    #모델 생성
    model = keras.Model(inputs=input, outputs=output)

    return model

model = create_model_fn()
model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy'])
model.summary()

728x90
반응형