5.1 이미지를 인식하는 원리, 데이터 전처리
- MNIST 데이터는 텐서플로 케라스의 API를 이용해 불러올 수 있음
- 불러온 이미지 데이터를 X로, 0~9 클래스를 y로 구분
- 7만 개 중 학습에 사용될 부분은 train, 테스트에 사용될 부분은 test라는 이름으로 불러옴
from tensorflow.keras.datasets import mnist
from tensorflow.keras.utils import to_categorical
import matplotlib.pyplot as plt
import sys
# MNIST 데이터셋을 불러와 학습셋과 테스트셋으로 저장
(X_train, y_train), (X_test, y_test) = mnist.load_data()
# 학습셋과 테스트셋이 각각 몇 개의 이미지로 되어 있는지 확인
print("학습셋 이미지 수 : %d 개" % (X_train.shape[0]))
print("테스트셋 이미지 수 : %d 개" % (X_test.shape[0]))
[output]
학습셋 이미지 수 : 60000 개
테스트셋 이미지 수 : 10000 개
- 맷플롯립과 imshow()를 이용하여 한 개의 이미지만 출력
- 모든 이미지가 X_train에 저장되어 있으므로 X_train[0]으로 첫 번째 이미지, cmap='Greys'으로 흑백 출력
# 첫 번째 이미지 확인
plt.imshow(X_train[0], cmap='Greys')
plt.show()
- 이미지는 28 x 28 = 784개의 픽셀로 각 픽셀은 0~255까지의 값으로 밝기의 정도를 나타냄
- 0~255의 숫자로 이루어진 28 x 28 행렬로 볼 수 있음
- sys.stdout.write() : print()와 다르게 줄 바꿈없이 바로 출력되는 함수
- sys.stdout.write("%-3s" % i) : i 값의 3자리를 확보하고 나머지는 오른쪽부터 공백으로 채움
# 이미지가 인식되는 원리
for x in X_train[0]:
for i in x:
sys.stdout.write("%-3s" % i)
sys.stdout.write('\n')
[output]
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 3 18 18 18 12613617526 1662552471270 0 0 0
0 0 0 0 0 0 0 0 30 36 94 15417025325325325325322517225324219564 0 0 0 0
0 0 0 0 0 0 0 49 23825325325325325325325325325193 82 82 56 39 0 0 0 0 0
0 0 0 0 0 0 0 18 2192532532532532531981822472410 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 80 15610725325320511 0 43 1540 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 14 1 15425390 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 1392531902 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 11 19025370 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 35 2412251601081 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 81 24025325311925 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 45 18625325315027 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 16 93 2522531870 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 24925324964 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 46 1301832532532072 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 39 1482292532532532501820 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 24 11422125325325325320178 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 23 66 21325325325325319881 2 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 18 17121925325325325319580 9 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 55 17222625325325325324413311 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 13625325325321213513216 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
- 즉, MNIST 데이터셋은 784개의 속성을 이용해 0~9의 클래스 열 개 중 하나를 맞히는 문제가 됨
- 2차원 배열을 784개의 1차원 배열로 바꾸기 위해 reshape() 이용
- 케라스는 데이터를 0~1값으로 변환해야 최적의 성능을 보이기 때문에 0~255 값을 정규화시켜줘야 함
- 0~9 값의 클래스를 원-핫 인코딩 시켜주기 위해 to_categorical() 이용
# 차원 변환
X_train = X_train.reshape(X_train.shape[0], 784)
X_train = X_train.astype('float64')
X_train = X_train / 255
X_test = X_test.reshape(X_test.shape[0], 784).astype('float64') / 255
# 클래스 값 확인
print("class : %d " % (y_train[0]))
# 바이너리화 과정(원-핫 인코딩)
y_train = to_categorical(y_train, 10)
y_test = to_categorical(y_test, 10)
print(y_train[0])
[output]
class : 5
[0. 0. 0. 0. 0. 1. 0. 0. 0. 0.]
5.2 딥러닝 기본 프레임 만들기
- 딥러닝을 실행하기 위한 프레임 설정
- 입력값(input_dim) 784개, 은닉층 512개, 출력이 10개인 모델
- 활성화 함수 relu, 출력층은 softmax, 오차함수 categorical_croessentropy, 최적화함수 adam
# 모델 구조 설정
model = Sequential()
model.add(Dense(512, input_dim=784, activation='relu'))
model.add(Dense(10, activation='softmax'))
model.summary()
# 모델 실행 환경 설정
model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])
[output]
Model: "sequential"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
dense (Dense) (None, 512) 401920
_________________________________________________________________
dense_1 (Dense) (None, 10) 5130
=================================================================
Total params: 407,050
Trainable params: 407,050
Non-trainable params: 0
__________________________________________________________
- 모델 성과 저장, 열 번 이상 모델 성능이 향상되지 않으면 자동으로 학습 중단
- 샘플 200개를 모두 30번 실행하게 설정, 테스트 셋으로 최종 모델의 성과를 측정해 그 값을 출력
# 모델 최적화
modelpath="./MNIST_MLP.hdf5"
checkpointer = ModelCheckpoint(filepath=modelpath, monitor='val_loss', verbose=1, save_best_only=True)
early_stopping_callback = EarlyStopping(monitor='val_loss', patience=10)
# 모델 실행
history = model.fit(X_train, y_train, validation_split=0.25, epochs=30, batch_size=200, verbose=0, callbacks=[early_stopping_callback,checkpointer])
# 테스트 정확도 출력
print("\n Test Accuracy: %.4f" % (model.evaluate(X_test, y_test)[1]))
[output]
Epoch 00001: val_loss improved from inf to 0.18529, saving model to ../data/model\MNIST_MLP.hdf5
Epoch 00002: val_loss improved from 0.18529 to 0.13521, saving model to ../data/model\MNIST_MLP.hdf5
...
Epoch 00022: val_loss did not improve from 0.08088
Epoch 00023: val_loss did not improve from 0.08088
313/313 [==============================] - 1s 1ms/step - loss: 0.0711 - accuracy: 0.9816
Test Accuracy: 0.9816
- 실행 결과를 통한 학습셋과 테스트셋의 오차를 그래프로 표현
# 검증셋과 학습셋의 오차 저장
y_vloss = history.history['val_loss']
y_loss = history.history['loss']
# 그래프로 표현
x_len = np.arange(len(y_loss))
plt.plot(x_len, y_vloss, marker='.', c="red", label='Testset_loss')
plt.plot(x_len, y_loss, marker='.', c="blue", label='Trainset_loss')
# 그래프에 그리드를 주고 레이블 표시
plt.legend(loc='upper right')
plt.grid()
plt.xlabel('epoch')
plt.ylabel('loss')
plt.show()
5.3 컨볼루션 신경망(CNN)
- 컨볼루션 신경망이란 입력된 이미지에서 다시 한번 특징을 추출하기 위해 커널(슬라이딩 윈도)을 도입하는 기법
- 가중치가 들어있는 2 x 2 커널을 왼쪽 윗칸부터 칸을 옮겨가며 적용하면서 원래 있던 값에 가중치를 곱함
- 이렇게 새로 만들어진 층을 컨볼루션(합성곱)이라고 함
- 컨볼루션층을 만들면 입력 데이터가 가진 특징을 대략적으로 추출해서 학습을 진행할 수 있음
- 케라스에서 컨볼루션 층을 추가하는 함수는 Conv2D()
- 첫 번째 인자 : 커널을 몇 개 적용할지 설정
- kernel_size = (행, 열): 커널의 크기 지정
- input_shape = (행, 열, 색상(3) 또는 흑백(1)) : 입력되는 값 형태
- activation : 사용할 활성화 함수
- 컨볼루션층 2개 추가
model = Sequential()
model.add(Conv2D(32, kernel_size=(3, 3), input_shape=(28, 28, 1), activation='relu'))
model.add(Conv2D(64, (3, 3), activation='relu'))
5.4 맥스 풀링, 드롭아웃, 플래튼
- 컨볼루션 층을 통해 이미지 특징을 도출한 결과가 복잡하면 다시 한번 축소해야 하는데 이 과정을 풀링(pooling) 또는 서브샘플링(subsampling)이라고 함
- 맥스 풀링(max pooling): 정해진 구역 안에서 최댓값을 뽑아 냄
- 평균 풀링(average pooling) : 평균값을 뽑아 냄
- 맥스 풀링은 MaxPooling2D() 함수를 사용, pool_size를 통해 풀링 창의 크기 설정
model.add(MaxPooling2D(pool_size=(2,2))
- 드롭아웃(drop out)은 은닉층에 배치된 노드 중 일부를 임의로 꺼주는 기법
- 랜덤하게 노드를 꺼주면 학습 데이터에 지나치게 치우쳐서 학습되는 과적합을 방지할 수 있음
model.add(Dropout(0.25)) #25%의 노드를 꺼줌
- 컨볼루션 층, 맥스 풀링은 주어진 이미지를 2차원 배열인 채로 다루기 때문에 Flatten()을 통해 1차원으로 바꿔줘야 함
model.add(Flatten())
5.5 컨볼루션 신경망 실행하기
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, Dropout, Flatten, Conv2D, MaxPooling2D
from tensorflow.keras.callbacks import ModelCheckpoint, EarlyStopping
from tensorflow.keras.datasets import mnist
from tensorflow.keras.utils import to_categorical
import matplotlib.pyplot as plt
import numpy as np
#데이터 불러오기
(X_train, y_train), (X_test, y_test) = mnist.load_data()
# 차원 변환, 정규화
#2D 컨볼루션을 위해 4차원의 배열이 필요, pixel 은 흑백이므로 1 입력
X_trian = X_train.reshape(X_train.shape[0], 28, 28, 1).astype('float32')/255
X_test = X_test.reshape(X_test.shape[0], 28, 28, 1).astype('float32')/255
#레이블 데이터 원-핫 인코딩
y_train = to_categorical(y_train)
y_test = to_categorical(y_test)
#컨볼루션 신경망 설정
model = Sequential()
model.add(Conv2D(32, kernel_size=(3,3), input_shape=(28,28,1),
activation='relu'))
model.add(Conv2D(64, (3,3), activation='relu'))
model.add(MaxPooling2D(pool_size=(2,2)))
model.add(Dropout(0.25))
model.add(Flatten())
model.add(Dense(128, activation='relu'))
model.add(Dropout(0.5))
model.add(Dense(10, activation='softmax'))
#모델의 실행 옵션 설정
model.compile(loss='categorical_crossentropy', optimizer='adam',
metrics=['accuracy'])
#모델 최적화
model_path = "./MNIST_CNN.hdf5"
checkpointer = ModelCheckpoint(filepath=model_path, monitor='val_loss',
verbose=1, save_best_only=True)
early_stopping_callback = EarlyStopping(monitor='val_loss', patience=10)
#모델 실행
history = model.fit(X_train, y_train, validation_split=0.25, epochs=30,
batch_size=200, verbose=0, callbacks=[early_stopping_callback,
checkpointer])
#테스트 정확도 출력
print("\n Test Accuracy: %.4f" % (model.evaluate(X_test,y_test)[1]))
[output]
Epoch 00001: val_loss improved from inf to 0.18529, saving model to ../data/model\MNIST_MLP.hdf5
Epoch 00002: val_loss improved from 0.18529 to 0.13521, saving model to ../data/model\MNIST_MLP.hdf5
Epoch 00003: val_loss improved from 0.13521 to 0.11078, saving model to ../data/model\MNIST_MLP.hdf5
Epoch 00004: val_loss improved from 0.11078 to 0.10850, saving model to ../data/model\MNIST_MLP.hdf5
Epoch 00005: val_loss improved from 0.10850 to 0.09555, saving model to ../data/model\MNIST_MLP.hdf5
Epoch 00006: val_loss improved from 0.09555 to 0.09161, saving model to ../data/model\MNIST_MLP.hdf5
Epoch 00007: val_loss improved from 0.09161 to 0.08949, saving model to ../data/model\MNIST_MLP.hdf5
Epoch 00008: val_loss improved from 0.08949 to 0.08235, saving model to ../data/model\MNIST_MLP.hdf5
Epoch 00009: val_loss did not improve from 0.08235
Epoch 00010: val_loss did not improve from 0.08235
Epoch 00011: val_loss did not improve from 0.08235
Epoch 00012: val_loss did not improve from 0.08235
Epoch 00013: val_loss improved from 0.08235 to 0.08088, saving model to ../data/model\MNIST_MLP.hdf5
Epoch 00014: val_loss did not improve from 0.08088
Epoch 00015: val_loss did not improve from 0.08088
Epoch 00016: val_loss did not improve from 0.08088
Epoch 00017: val_loss did not improve from 0.08088
Epoch 00018: val_loss did not improve from 0.08088
Epoch 00019: val_loss did not improve from 0.08088
Epoch 00020: val_loss did not improve from 0.08088
Epoch 00021: val_loss did not improve from 0.08088
Epoch 00022: val_loss did not improve from 0.08088
Epoch 00023: val_loss did not improve from 0.08088
313/313 [==============================] - 1s 1ms/step - loss: 0.0711 - accuracy: 0.9816
Test Accuracy: 0.9816
#검증셋과 학습셋의 오차 저장
y_vloss = history.history['val_loss']
y_loss = history.history['loss']
#그래프 표현
x_len = np.arange(len(y_loss))
plt.plot(x_len, y_vloss, marker='.', c='red', label='Testset_loss')
plt.plot(x_len, y_loss, marker='.', c='blue', label='Trainset_loss')
#그래프에 그리드, 레이블 표시
plt.legend(loc='upper right')
plt.grid()
plt.xlabel('epoch')
plt.ylabel('loss')
plt.show()
- 23번째 에포크에서 학습 중단, 4번째 에포크에서 베스트 모델 만듦
- 테스트 정확도 98.16% 로 향상
'데이터 > 딥러닝' 카테고리의 다른 글
[Code Review] Cartoon-StyleGAN: Fine-tuning StyleGAN2 for Cartoon Face Generation (0) | 2023.04.01 |
---|---|
[모두의 딥러닝] Chapter 5 | 딥러닝 활용하기 - 자연어처리(NLP) (0) | 2023.02.21 |
[모두의 딥러닝] Chapter 4 | 딥러닝 기본기 다지기 - 모델 성능 향상시키기 (0) | 2023.02.14 |
[모두의 딥러닝] Chapter 4 | 딥러닝 기본기 다지기 - 모델 성능 검증하기 (0) | 2023.02.14 |
[모두의 딥러닝] Chapter 4 | 딥러닝 기본기 다지기 - 모델 설계, 다중분류 문제 해결 (0) | 2023.02.13 |