본문 바로가기
프로그래밍/TensorFlow2

Sequential API로 간단한 CNN 구현해 보기

by 세인트 워터멜론 2020. 7. 17.

TensorFlow2에서 제공하는 모델 구현 API는 크게 3가지 종류가 있다. 신경망 레이어를 순차적으로 쌓아 나가는 방식의 Sequential API, 레이어를 함수형태로 정의하는 Functional API, 그리고 클래스 형으로 모델을 만들 수 있는 Model Subclassing API다.

 

 

 

Sequential API는 간단한 모델을 쉽게 구축할 수 있으며, 빈 깡통 모델을 만들어 놓고 순차적으로 레이어를 추가하거나 한꺼번에 순차적인 모델을 구축할 수 있다.

Functional API는 복잡한 모델을 구축할 때 유리하며 ResNet과 같이 순차적이지 않은 모델도 구축할 수 있다.

Model Subclassing API는 자유도가 제일 높은 모델 구축 방법으로서 사용자 자신의 방법으로 신경망을 만들고 학습시킬 수 있다.

그러면 Sequential API로 간단한 CNN모델을 만들고 MNIST 숫자를 분류해 보자. 프로그래밍 언어를 처음 배울 때 "Hello, World!" 를 출력하는 것으로 시작하는 것처럼 딥러닝을 처음 배울 때는 MNIST 숫자 분류부터 시작한다.

텐서플로2에서는 MNIST 데이터셋도 쉽게 다운로드할 수 있다.

 

mnist = tf.keras.datasets.mnist
(x_train, y_train), (x_test, y_test) = mnist.load_data()

# adjusting to 0 ~ 1.0
x_train = x_train / 255.0
x_test = x_test / 255.0

 

x_train와 x_test에는 MNIST 숫자 그림이 y_train와 y_test에는 라벨이 저장된다. MNIST 숫자 이미지를 몇 개 출력해 보면 다음과 같다.

 

 

x_train와 x_test의 사이즈를 보면,

 

print(x_train.shape, x_test.shape)

 

(60000, 28, 28) (10000, 28, 28)이다. 각각 데이터 개수가 6만개, 1만개이며 사이즈가 \( 28 \times 28 \) 인 흑백 이미지다. CNN의 컨볼루션 레이어는 채널을 가진 데이터형을 받기 때문에 shape를 바꿔준다.

 

x_train = x_train.reshape(-1,28,28,1)
x_test = x_test.reshape(-1,28,28,1)

 

그러면 이미지 사이즈는 \( 28 \times 28 \times 1 \) 이 된다.

만들고자 하는 CNN 모델은 다음과 같이 간단한 것이다.

 

 

3개의 컨볼루션 레이어와 2개의 완전연결(fully connected) 레이어로 구성되어 있다. 첫 번째 컨볼루션 레이어의 파라미터 개수는 \( (3 \times 3 \times 1+1) \times 16=160 \)개, 두 번째 컨볼루션 레이어의 파라미터 개수는 \( (3 \times 3 \times 16+1) \times 32=4640 \)개, 세 번째 컨볼루션 레이어의 파라미터 개수는 \( (3 \times 3 \times 32+1) \times 64=18496 \)개, 첫 번째 완전연결 레이어의 파라미터 개수는 \( (3 \times 3 \times 64+1) \times 32=18464 \)개, 두 번째 완전연결 레이어의 파라미터 개수는 \( (32+1) \times 10=330 \)개로서 총 42,090개다.

Sequential API를 사용하여 모델을 만들면 다음과 같다.

 

model = tf.keras.Sequential([
    tf.keras.layers.Conv2D(input_shape=(28,28,1), kernel_size=(3,3), filters=16, activation='relu'),
    tf.keras.layers.MaxPooling2D((2,2)),
    tf.keras.layers.Conv2D(kernel_size=(3,3), filters=32, activation='relu'),
    tf.keras.layers.MaxPooling2D((2,2)),
    tf.keras.layers.Conv2D(kernel_size=(3,3), filters=64, activation='relu'),
    tf.keras.layers.Flatten(),
    tf.keras.layers.Dense(32, activation='relu'),
    tf.keras.layers.Dense(10, activation='softmax')
])

 

Functional API를 사용하여 모델을 만들면 다음과 같다. Sequential API에서는 첫 번째 레이어에 input_shape를 정해줘야 한다.

 

input_shape = (28,28,1)
img_input = tf.keras.layers.Input(shape=input_shape)
h1 = tf.keras.layers.Conv2D(kernel_size=(3,3), filters=16, activation='relu')(img_input)
h1_pool = tf.keras.layers.MaxPooling2D((2,2))(h1)
h2 = tf.keras.layers.Conv2D(kernel_size=(3,3), filters=32, activation='relu')(h1_pool)
h2_pool = tf.keras.layers.MaxPooling2D((2,2))(h2)
h3 = tf.keras.layers.Conv2D(kernel_size=(3,3), filters=64, activation='relu')(h2_pool)
h3_flat = tf.keras.layers.Flatten()(h3)
h4 = tf.keras.layers.Dense(32, activation='relu')(h3_flat)
predictions = tf.keras.layers.Dense(10, activation='softmax')(h4)

model = tf.keras.Model(inputs=img_input, outputs=predictions)

 

다음과 같이 빈 모델을 먼저 만들고 add 메소드를 이용하여 차례로 레이어를 추가해서 만들 수도 있다.

 

model = tf.keras.Sequential()
model.add(tf.keras.layers.Conv2D(input_shape=(28,28,1), kernel_size=(3,3), filters=16, activation='relu'))
model.add(tf.keras.layers.MaxPooling2D((2,2)))
model.add(tf.keras.layers.Conv2D(kernel_size=(3,3), filters=32, activation='relu'))
model.add(tf.keras.layers.MaxPooling2D((2,2)))
model.add(tf.keras.layers.Conv2D(kernel_size=(3,3), filters=64, activation='relu'))
model.add(tf.keras.layers.Flatten())
model.add(tf.keras.layers.Dense(32, activation='relu'))
model.add(tf.keras.layers.Dense(10, activation='softmax'))

 

모델의 구조를 출력해보면

 

model.summary()

 

다음과 같이 나온다.

 

 

모델을 컴파일하고 전체 데이터를 5번 사용하여(에폭 5) 학습하면,

 

model.compile(optimizer='adam',
              loss='sparse_categorical_crossentropy',
              metrics=['accuracy'])

history = model.fit(x_train, y_train, epochs=5, validation_split=0.25, verbose=2)

 

 

이 되고, 결과를 그림으로 그리면 다음과 같다.

 

plt.figure(figsize=(10,4))
plt.subplot(1,2,1)
plt.plot(history.history['loss'], 'b-', label='loss')
plt.plot(history.history['val_loss'], 'r-', label='val_loss')
plt.xlabel('epoch')
plt.legend()

plt.subplot(1,2,2)
plt.plot(history.history['accuracy'], 'g-', label='accuracy')
plt.plot(history.history['val_accuracy'], 'k-', label='val_accuracy')
plt.xlabel('epoch')
plt.legend()

plt.show()

 

 

학습된 모델을 테스트 데이터를 이용하여 평가해 보면,

 

test_loss, test_acc = model.evaluate(x_test, y_test, verbose=0)

print(test_acc)

 

0.9874로서 약 98.7%의 테스트 정확도가 나온다.

전체 코드는 다음과 같다.

 

import tensorflow as tf
import matplotlib.pyplot as plt

# load mnist data
mnist = tf.keras.datasets.mnist
(x_train, y_train), (x_test, y_test) = mnist.load_data()

# adjusting to 0 ~ 1.0
x_train = x_train / 255.0
x_test = x_test / 255.0

print(x_train.shape, x_test.shape)

# reshaping
x_train = x_train.reshape(-1,28,28,1)
x_test = x_test.reshape(-1,28,28,1)

print(x_train.shape, x_test.shape)

# plotting
for c in range(16):
    plt.subplot(4,4,c+1)
    plt.imshow(x_train[c].reshape(28,28), cmap='gray')
plt.show()

# model
model = tf.keras.Sequential([
    tf.keras.layers.Conv2D(input_shape=(28,28,1), kernel_size=(3,3), filters=16, activation='relu'),
    tf.keras.layers.MaxPooling2D((2,2)),
    tf.keras.layers.Conv2D(kernel_size=(3,3), filters=32, activation='relu'),
    tf.keras.layers.MaxPooling2D((2,2)),
    tf.keras.layers.Conv2D(kernel_size=(3,3), filters=64, activation='relu'),
    tf.keras.layers.Flatten(),
    tf.keras.layers.Dense(32, activation='relu'),
    tf.keras.layers.Dense(10, activation='softmax')
])

model.summary()

# compile and train
model.compile(optimizer='adam',
              loss='sparse_categorical_crossentropy',
              metrics=['accuracy'])

history = model.fit(x_train, y_train, epochs=5, validation_split=0.25, verbose=2)

plt.figure(figsize=(10,4))
plt.subplot(1,2,1)
plt.plot(history.history['loss'], 'b-', label='loss')
plt.plot(history.history['val_loss'], 'r-', label='val_loss')
plt.xlabel('epoch')
plt.legend()

plt.subplot(1,2,2)
plt.plot(history.history['accuracy'], 'g-', label='accuracy')
plt.plot(history.history['val_accuracy'], 'k-', label='val_accuracy')
plt.xlabel('epoch')
plt.legend()

plt.show()

# model evaluate
test_loss, test_acc = model.evaluate(x_test, y_test, verbose=0)

print(test_acc)

 

 

댓글