딥러닝 공부

골빈해커의 3분 딥러닝 텐서플로맛_ch.6 헬로 딥러닝 MNIST (텐서플로우 2.0)

Woonys 2021. 8. 1. 02:20
반응형

챕터 6에서는 딥러닝의 'Hello World!'라 할 수 있는 MNIST를 분류하는 작업에 대한 예제를 연습해본다. 긴 말 할 것 없이 빠르게 가보자.

 

6.1 MNIST 학습하기

import tensorflow as tf
import numpy as np

#MNIST dataset load: keras 패키지에 메소드로 내장되어 있어 바로 불러오기 가능!

(x_train, y_train), (x_test, y_test) = tf.keras.datasets.mnist.load_data()
#MNIST 데이터셋 정규화: 픽셀값이 0-255로 되어 있어 이를 0-1 범위 사이에 넣기 위해 정규화
x_train, x_test = x_train/255.0, x_test/255.0

train dataset의 shape을 확인해보면 총 60,000 장의 데이터셋이며 각 장은 28*28 픽셀로 구성되어 있다. (60,000*28*28)

그리고 각 데이터셋에 해당하는 라벨(정답)인 y_train의 데이터셋 역시 총 60,000장이며 1차원으로 구성되어 있다. (60,000,)

 

#train & label dataset shape

x_train.shape, y_train.shape
((60000, 28, 28), (60000,))

바로 모델을 만들어보자. 여기서 이전 챕터에서는 나오지 않았던 새로운 케라스 레이어인 Flatten이 등장한다. 위의 데이터는 28*28로, 각 데이터셋은 2차원 텐서이며 이를 묶은 전체 학습 데이터는 3차원 텐서다. 이를 신경망에 넣어서 행렬곱으로 계산하기 위해서는 차원이 맞아야 하는데, 신경망은 2차원 행렬이므로 차원을 낮춰야 한다. 이를 위해서 작업해주는 게 바로 Flatten이다. 말 그대로 28*28인 2차원 텐서인 각 데이터셋을 일렬로 펴서 1차원 텐서, 벡터로 바꿔주는 작업이다.

model = tf.keras.models.Sequential([
				#Flatten: 28*28 픽셀인 MNIST 데이터셋을 일렬(784,)로 펴주는 작업
                tf.keras.layers.Flatten(input_shape = (28,28)),
                tf.keras.layers.Dense(256, activation='relu', kernel_initializer = 'random_normal', bias_initializer='zeros'),
                tf.keras.layers.Dense(256, activation='relu', kernel_initializer = 'randon_normal', bias_initializer='zeros'),
                tf.keras.layers.Dense(10, activation='softmax')
])

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

model.fit(x_train, y_train, epochs = 100)
model.evaluate(x_test, y_test, verbose=2)

아웃풋은 아래와 같다. 이미지긴 한지라 시간이 이전보다는 조금 더 걸린다.

 

Epoch 1/100 1875/1875 [==============================] - 8s 4ms/step - loss: 0.2032 - accuracy: 0.9388 Epoch 2/100 1875/1875 [==============================] - 6s 3ms/step - loss: 0.0841 - accuracy: 0.9736 Epoch 3/100 1875/1875 [==============================] - 6s 3ms/step - loss: 0.0593 - accuracy: 0.9812

...

Epoch 100/100 1875/1875 [==============================] - 9s 5ms/step - loss: 0.0055 - accuracy: 0.9992 313/313 - 1s - loss: 0.3846 - accuracy: 0.9829

 

[0.38456249237060547, 0.9829000234603882] #evaluate 결과

 

여기서 epoch 뒤에 오는 숫자를 살펴보자.

 

Epoch 1/100 1875/1875
Epoch 2/100 1875/1875
Epoch 3/100 1875/1875

 

앞서 학습용 데이터셋의 장수는 6만 장이라고 했다. Epoch이라는 건 학습 횟수라고 하지 않았나? 그럼 저 1875는 대체 무슨 의미지?

 

자, Epoch의 정의부터 명확하게 가보자. 학습 횟수인 건 맞다. 근데 뭐에 대한 학습 횟수이냐?

 

딥러닝에서 epoch는 전체 트레이닝 셋이 신경망을 통과한 횟수 의미합니다. 예를 들어, 1-epoch는 전체 트레이닝 셋이 하나의 신경망에 적용되어 순전파와 역전파를 통해 신경망을 한 번 통과했다는 것을 의미합니다.

출처: 로스카츠의 AI 머신러닝

 

[딥러닝] 배치 사이즈(batch size) vs 에포크(epoch) vs 반복(iteration)의 차이

배치 사이즈(batch size) vs 에포크(epoch) vs 반복(iteration)의 차이

losskatsu.github.io

위 링크에서 설명한 것처럼 Epoch은 예제 기준으로 6만 장 데이터가 신경망을 왔다갔다(를 1번으로 정의)한 횟수를 의미한다. 그럼 저 1875는 무엇이냐? 6만장이 한 번에 신경망에 들어갔다 나오면 좋겠지만 데이터가 워낙 많으니 이걸 쪼개서 넣는다. 지금 같은 경우는 60000장은 한 번에 32장씩 1번 장부터 60000번 장까지 나눠놓으면 이것을 1875번씩 넣으면 전체 6만 장이 한 번 왔다갔다 하게 되는 셈이다. 이때 나눈 묶음(32장)을 하나의 미니 배치라고 부른다. 그리고 이 사이즈(32장의 장수)를 배치 사이즈라고 부른다.

 

배치 사이즈가 크면 클수록 한 번에 들어가는 양이 많아져서 한 번에 많은 양을 처리하니 속도가 빨라질 수 있지만 또 너무 많이 넣으면 한 번에 넣을 때 너무 오래, 그리고 비효율적으로 학습이 이뤄지니 적절한 배치 사이즈를 맞추는 게 필요하다.

 

현재 배치 사이즈는 32가 디폴트 값으로 되어 있다. 이를 100으로 바꿔서 학습시켜보자. 다른 내용은 위와 전부 동일하나 배치 사이즈를 100으로 입력해 학습을 돌린다.

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

model = tf.keras.models.Sequential([
                                    tf.keras.layers.Flatten(input_shape =(28, 28)),
                                    tf.keras.layers.Dense(256, activation ='relu', kernel_initializer='random_normal', bias_initializer='zeros'),
                                    tf.keras.layers.Dense(256, activation='relu', kernel_initializer='random_normal', bias_initializer='zeros'),
                                    tf.keras.layers.Dense(10, activation='softmax')
])

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

#배치 사이즈: model.fit에서 설정
model.fit(x_train, y_train, epochs=100, batch_size=100, validation_data=(x_test,y_test)) #batch size는 여기서 정의!)

Epoch 1/100 600/600 [==============================] - 4s 6ms/step - loss: 0.2424 - accuracy: 0.9290 - val_loss: 0.1113 - val_accuracy: 0.9676

Epoch 2/100 600/600 [==============================] - 4s 6ms/step - loss: 0.0926 - accuracy: 0.9714 - val_loss: 0.0916 - val_accuracy: 0.9706

Epoch 3/100 600/600 [==============================] - 4s 6ms/step - loss: 0.0604 - accuracy: 0.9812 - val_loss: 0.0726 - val_accuracy: 0.9780

...

Epoch 100/100 600/600 [==============================] - 4s 7ms/step - loss: 0.0043 - accuracy: 0.9989 - val_loss: 0.2144 - val_accuracy: 0.9817

 

위의 32 사이즈로 학습했을 때 epoch 당 걸린 시간을 비교해보면 배치 사이즈를 100으로 했을 때 약 2배 조금 안되게 빨라진 것을 볼 수 있다(7~8s vs. 4s)

 

이를 평가해보면 아래와 같다.

model.evaluate(x_test, y_test, verbose=2)

313/313 - 1s - loss: 0.2144 - accuracy: 0.9817

[0.2144445776939392, 0.9817000031471252]

 

6.2 드롭아웃

 

이번에는 재밌는 개념을 하나 배운다. 드롭아웃이라는 건데, 아무래도 이론적 배경은 따로 더 파봐야 할 것 같다. 이 책에서는 갼략한 배경과 코드 위주로만 설명한다.

 

드롭아웃을 말하기 전에 앞서 공부했던 내용 중 오버피팅에 대해 다시 떠올려보자. 오버피팅은 무엇인가? 

 

(x_train, y_train), (x_test, y_test) = tf.keras.datasets.mnist.load_data()
x_train, x_test = x_train/255.0, x_test/255.0 #정규화

model = tf.keras.models.Sequential([
			tf.keras.layers.Flatten(input_dim = (28, 28)),
            tf.keras.layers.Dense(256, activation = 'relu', kernel_initializer = 'random_normal', bias_initializer = 'zeros'),
            tf.keras.layers.Dropout(0.2),  #드롭아웃: 신경망 20%를 그때그때 off
            tf.keras.layers.Dense(256, activation = 'relu', kernel_initializer = 'random_normal', bias_initializer = 'zeros'),
            tf.keras.layers.Dropout(0.2) #드롭아웃은 적용하고 싶은 각 레이어 바로 다음에다가! & 해당되는 바로 전 레이어에만 드롭아웃 적용
            tf.keras.layers.Dense(10, activation = 'softmax')
])

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

model.fit(x_train, y_train, epochs = 100, batch_size = 100, validation_data = (x_test, y_test))
model.evaluate(x_test, y_test, verbose=2)
반응형