Altiora Petamus
[EXPLORATION] 1. 간단한 이미지 분류기 구현 본문
이곳에 MNIIST와 가위, 바위, 보의 사진을 분류하는 간단한 인공지능을 구현하는 내용을 기록한다.
소개
데이터 준비 -> 딥러닝 네트워크 설계 -> 학습 -> 테스트(평가)의 순서대로 설명과 함께 코드를 살펴본다.
이 글에서는 keras(TensorFlow의 표준 API)를 이용한다.
본문
1. MNIST를 이용한 손글씨 분류기 구현 - 데이터 구조
MNIST는 숫자 손글씨에 대한 data를 가지고 있으며 60,000장의 Train set과 10,000장의 Test set으로 구성되어 있다.
각각의 image는 28 x 28의 크기로 구성되어 있다. Train set은 250명의 사용자가 직접 작성한 것이고 , test set은 또 다른 250명의 사용자가 작성한 data이다. 자세한 정보는 아래 링크에서 확인할 수 있다.
MNIST handwritten digit database, Yann LeCun, Corinna Cortes and Chris Burges
yann.lecun.com
딥러닝 구현에서 가장먼저 해야할 일은 이용할 data의 정보를 파악하는 것이다. data의 구조를 알아야 본격적으로 학습을 시키기 전에 전처리를 수행할 수 있기 때문이다. 따라서 먼저 MNIST의 data set의 구조를 살펴보자.
import tensorflow as tf
from tensorflow import keras
import numpy as np
import matplotlib.pyplot as plt
print(tf.__version__) # Tensorflow의 버전을 출력
mnist = keras.datasets.mnist
# MNIST 데이터를 로드. 다운로드하지 않았다면 다운로드까지 자동으로 진행됩니다.
(x_train, y_train), (x_test, y_test) = mnist.load_data()
print(len(x_train)) # x_train 배열의 크기를 출력
위 코드는 MNIST의 데이터를 가져와 각각의 변수에 대입하는 과정이다.
실행결과
2.2.0
60000
실행결과를 보면 알 수 있듯이, Train 데이터는 60,000개로 구성되어 있다.
Train set의 data를 시각적으로 표현해 보자.
plt.imshow(x_train[1],cmap=plt.cm.binary)
plt.show()
print(y_train[1])
실행결과

0
train set의 2번째 요소에는 손글씨 0이 담겨있고, 그에 해당하는 label 역시 0으로 출력됨을 확인했다.
train set의 데이터는 28 x 28크기의 이미지 여러장이 하나의 벡터로 변환되어 저장된 형태이고 이미지의 pixel값은 0 ~255의 값을 갖는다. 하지만 일반적으로 모델을 훈련시킬때, 입력 data의 값은 0 ~ 1 의 값으로 정규화 시켜주는 것이 좋다. 따라서 우리는 이 값을 255로 나누어 정규화를 해주어야 한다.
x_train_norm, x_test_norm = x_train / 255.0, x_test / 255.0
print('최소값:',np.min(x_train_norm), ' 최대값:',np.max(x_train_norm))
실행 결과
최소값: 0.0 최대값: 1.0
정규화와 matplotlib(시각화 도구) 에 대한 정보
2. MNIST를 이용한 손글씨 분류기 구현 - 네트워크 설계
데이터가 모두 준비되었으니 이제 딥러닝 네트워크를 만든다. 이번 예제에서는 keras의 Sequential API를 이용하여 네트워크를 구성한다. Sequential API는 비교적 간단하게 딥러닝 네트워크를 만들수 있는 방법이다. 구조를 생성한는 방법은 다음과 같다.
마지막 줄의 변수(아래 예시에서는 10)는 Class의 수 이고, 첫 줄의 마지막 변수는 입력이미지의 형태로 이 두 변수들은 데이터의 구조에 따라 변경된다. 이 두 변수를 제외한 다른 변수들은 모델의 향상을 위해 값을 변경하여 입력하는 것이 가능하다. 대개 2^n의 형태로 입력한다. ( 컴퓨터는 이진 연산을 실행하므로 더욱 안정적으로 작동하기 떄문이다. )

아래 코드는 Sequential API를 이용하여 MNIST data구조에 맞게 설정한 딥러닝 네트워크이다.
model=keras.models.Sequential()
model.add(keras.layers.Conv2D(16, (3,3), activation='relu', input_shape=(28,28,1)))
model.add(keras.layers.MaxPool2D(2,2))
model.add(keras.layers.Conv2D(32, (3,3), activation='relu'))
model.add(keras.layers.MaxPooling2D((2,2)))
model.add(keras.layers.Flatten())
model.add(keras.layers.Dense(32, activation='relu'))
model.add(keras.layers.Dense(10, activation='softmax'))
print('Model에 추가된 Layer 개수: ', len(model.layers))
model.summary()
class가 0~9까지 총 10개 이므로 마지막 단계에 10이 입력되어 있다. 마지막의 summary()는 설계한 네트워크의 구조를 시각적으로 차원을 이해하기 쉽게 표현하는 함수이다.
실행결과

하지만 여기서 input shape는 28x 28 x 1이다. 이전에 train의 구조를 살펴 보았을 때, 우리는 데이터가 28 x 28 인것을 확인했는데 왜 다를까? 데이터 전처리 과정을 덜 해준것이다. image data는 가로 세로의 pixel값만 고려할 것이 아니라 image의 channel의 정보도 고려하여 input 해주어야 한다. (RGB-3, Gray-scale -1로 조절 ) 우리가 준비한 데이터는 흑백의 이미지이기 때문에 마지막 차원의 값을 1로 설정해 주어야 한다.
print("Before Reshape - x_train_norm shape: {}".format(x_train_norm.shape))
print("Before Reshape - x_test_norm shape: {}".format(x_test_norm.shape))
x_train_reshaped=x_train_norm.reshape( -1, 28, 28, 1) # 데이터갯수에 -1을 쓰면 reshape시 자동계산됩니다.
x_test_reshaped=x_test_norm.reshape( -1, 28, 28, 1)
print("After Reshape - x_train_reshaped shape: {}".format(x_train_reshaped.shape))
print("After Reshape - x_test_reshaped shape: {}".format(x_test_reshaped.shape))
실행결과
Before Reshape - x_train_norm shape: (60000, 28, 28)
Before Reshape - x_test_norm shape: (10000, 28, 28)
After Reshape - x_train_reshaped shape: (60000, 28, 28, 1)
After Reshape - x_test_reshaped shape: (10000, 28, 28, 1)
위 코드에서 train 데이터의 shape를 알맞게 수정해 준다. format함수는 간단하게 설명하자면 {}안에 값을 넣어주는 역할을 한다.
.reshape()를 통해 data의 구조를 바꾸어 주는데 여기서 -1은 나머지 값들이 결정되면 이 값은 알아서 결정되게 하는 역할을 한다.
굳이 따로 계산해서 맞추어줄 필요가 없다는 것이다. 이렇게 학습을 시킬 준비가 모두 마무리 되었다.
3. MNIST를 이용한 손글씨 분류기 구현 - 네트워크 학습
이제 학습을 시켜보자.
model.compile(optimizer='adam',
loss='sparse_categorical_crossentropy',
metrics=['accuracy'])
model.fit(x_train_reshaped, y_train, epochs=10)
compile함수는 해당 모델에 대한 부가기능을 명시하는 함수라고 볼 수 있다. optimizer로 adam을 사용하고 loss function으로는 sparse categorical corossentropy를 , 마지막으로 metrics를 accuracy로 설정한다는 뜻이다. 이제 아래 fit함수를 이용하여 학습을 시킨다. 훈련을 할 때의 input데이터는 t_train_reshaped(정규화 및 reshaped된 data) 이다. epoch는 전체 train데이터를 학습시키는 횟수를 의미한다. 위 코드에서는 10번 학습을 진행한다.
실행결과
Epoch 1/10
1875/1875 [==============================] - 3s 2ms/step - loss: 0.1921 - accuracy: 0.9415
Epoch 2/10
1875/1875 [==============================] - 3s 1ms/step - loss: 0.0612 - accuracy: 0.9812
Epoch 3/10
1875/1875 [==============================] - 3s 1ms/step - loss: 0.0457 - accuracy: 0.9858
Epoch 4/10
1875/1875 [==============================] - 3s 1ms/step - loss: 0.0359 - accuracy: 0.9888
Epoch 5/10
1875/1875 [==============================] - 3s 1ms/step - loss: 0.0296 - accuracy: 0.9908
Epoch 6/10
1875/1875 [==============================] - 3s 2ms/step - loss: 0.0253 - accuracy: 0.9921
Epoch 7/10
1875/1875 [==============================] - 3s 2ms/step - loss: 0.0201 - accuracy: 0.9936
Epoch 8/10
1875/1875 [==============================] - 3s 2ms/step - loss: 0.0175 - accuracy: 0.9944
Epoch 9/10
1875/1875 [==============================] - 3s 1ms/step - loss: 0.0148 - accuracy: 0.9951
Epoch 10/10
1875/1875 [==============================] - 3s 1ms/step - loss: 0.0134 - accuracy: 0.9955
위와같이 학습이 정상적으로 진행된것을 확인할 수 있다. 오른쪽에 표시되는 loss 와 accuracy는 train데이터에 대한 정보이다.
4. MNIST를 이용한 손글씨 분류기 구현 - 결과 분석
이제 훈련된 모델을 이용하여 test data와 비교하여 그 성능을 확인해 본다.
test_loss, test_accuracy = model.evaluate(x_test_reshaped,y_test, verbose=2)
print("test_loss: {} ".format(test_loss))
print("test_accuracy: {}".format(test_accuracy))
실행결과
313/313 - 3s - loss: 0.0357 - accuracy: 0.9909
test_loss: 0.03572753444314003
test_accuracy: 0.9908999800682068
위와 같이 test모델에 대하여 99%라는 높은 정확도를 뽐낸다. 하지만 train모델에서의 정확도(0.9955)보다는 조금낮다. 이는 모델이 train과정에서 test데이터를 이용하지 않고 학습을 하였기 때문에 지극히 당연한 것이다.
결과에 대하여 좀 더 직관적으로 살펴보자.
predicted_result = model.predict(x_test_reshaped) # model이 추론한 확률값.
predicted_labels = np.argmax(predicted_result, axis=1)
idx=0 #1번째 x_test를 살펴보자.
print('model.predict() 결과 : ', predicted_result[idx])
print('model이 추론한 가장 가능성이 높은 결과 : ', predicted_labels[idx])
print('실제 데이터의 라벨 : ', y_test[idx])
plt.imshow(x_test[idx],cmap=plt.cm.binary)
plt.show()
실행결과
model.predict() 결과 : [7.2904304e-12 5.6847798e-09 4.0407198e-09 1.3121650e-05 2.2648113e-08
4.6416937e-09 1.8716123e-16 9.9997735e-01 1.0762369e-09 9.5278228e-06]
model이 추론한 가장 가능성이 높은 결과 : 7
실제 데이터의 라벨 : 7

위 predict의 결과로 나온 list에서 index가 7인 값이 가장 높은 것을 확인할 수 있다. 따라서 이 모델이 예측하기를 "7일 것이다."라고 한 것이다. 실제 label과 비교를 해보면 정답인 것을 확인할 수 있다.
이번엔 틀린 경우에 대하여 살펴보자.
import random
wrong_predict_list=[]
for i, _ in enumerate(predicted_labels):
# i번째 test_labels과 y_test이 다른 경우만 모아 봅시다.
if predicted_labels[i] != y_test[i]:
wrong_predict_list.append(i)
# wrong_predict_list 에서 랜덤하게 k개만 뽑아봅시다.
samples = random.choices(population=wrong_predict_list, k=1)
# k값을 조정하여 표시하는 sample의 갯수 조절가능
for n in samples:
print("예측확률분포: " + str(predicted_result[n]))
print("라벨: " + str(y_test[n]) + ", 예측결과: " + str(predicted_labels[n]))
plt.imshow(x_test[n], cmap=plt.cm.binary)
plt.show()
실행결과
예측확률분포: [4.6775830e-01 1.1936965e-09 2.6022069e-06 1.1078778e-02 9.1925671e-08 5.2068669e-01 5.3305389e-06 4.3366125e-04 9.8027440e-06 2.4759742e-05] 라벨: 0, 예측결과: 5

위와같이 실제 정답은 0이지만 모델은 5라고 예측한 것을 확인할 수 있다. 모델을 학습시킨후 이러한 시각화 작업을 거쳐 모델을 다듬는 작업은 향후 모델의 성능을 높이는데 도움이 될 수 있다.
5. MNIST를 이용한 손글씨 분류기 구현 - 마무리 정리
아래 코드는 위 긴 설명의 코드들을 하나로 모아놓은 것이다.
import tensorflow as tf
from tensorflow import keras
import numpy as np
import matplotlib.pyplot as plt
print(tf.__version__) # Tensorflow의 버전을 출력
mnist = keras.datasets.mnist
# MNIST 데이터를 로드. 다운로드하지 않았다면 다운로드까지 자동으로 진행됩니다.
(x_train, y_train), (x_test, y_test) = mnist.load_data()
print(len(x_train)) # x_train 배열의 크기를 출력
#데이터 정규화
x_train_norm, x_test_norm = x_train / 255.0, x_test / 255.0
print('최소값:',np.min(x_train_norm), ' 최대값:',np.max(x_train_norm))
#네트워크 구성
model=keras.models.Sequential()
model.add(keras.layers.Conv2D(16, (3,3), activation='relu', input_shape=(28,28,1)))
model.add(keras.layers.MaxPool2D(2,2))
model.add(keras.layers.Conv2D(32, (3,3), activation='relu'))
model.add(keras.layers.MaxPooling2D((2,2)))
model.add(keras.layers.Flatten())
model.add(keras.layers.Dense(32, activation='relu'))
model.add(keras.layers.Dense(10, activation='softmax'))
print('Model에 추가된 Layer 개수: ', len(model.layers))
model.summary()
#reshape작업
print("Before Reshape - x_train_norm shape: {}".format(x_train_norm.shape))
print("Before Reshape - x_test_norm shape: {}".format(x_test_norm.shape))
x_train_reshaped=x_train_norm.reshape( -1, 28, 28, 1) # 데이터갯수에 -1을 쓰면 reshape시 자동계산됩니다.
x_test_reshaped=x_test_norm.reshape( -1, 28, 28, 1)
print("After Reshape - x_train_reshaped shape: {}".format(x_train_reshaped.shape))
print("After Reshape - x_test_reshaped shape: {}".format(x_test_reshaped.shape))
#compile , fit
model.compile(optimizer='adam',
loss='sparse_categorical_crossentropy',
metrics=['accuracy'])
model.fit(x_train_reshaped, y_train, epochs=10)
#result
test_loss, test_accuracy = model.evaluate(x_test_reshaped,y_test, verbose=2)
print("test_loss: {} ".format(test_loss))
print("test_accuracy: {}".format(test_accuracy))
함수의 세세한 설명은 아래에 함수설명을 참조한다. 각 코드의 블록들에 대한 설명은 주석으로 설명이 되어있다.
이 코드를 실행함으로써 MNIST 손글씨 분류기 모델이 성공적으로 학습되었음을 확인할 수 있다.
6. 가위 바위 보 분류기 만들기 - 데이터 준비
MNIST를 구현하면서 딥러닝의 전반적인 구조를 알 수 있었다. 이제 우리가 직접 마련한 데이터를 직접 구성한 네트워크에 넣어서 학습을 시켜보고 결과를 내보자. 먼저 가위 바위 보 사진을 마련하는 방법은 두가지가 있다. 첫번째는 직접 찍어서 마련하는 방법이 있고 두번째는 인터넷에서 누군가 배포해준 데이터를 활용하는 방법이다. 넷 상에서 데이터를 구하게 되면 자료도 비교적 많고 여러사람의 data를 이용하여 과적합에 방지할 수 있기에 두번째 방법을 추천한다.
이미지를 마련하면 이를 학습시키기 전에 전처리 작업을 해주어야 한다.
#Resize
from PIL import Image
import os, glob
k=["paper","scissor","rock"]
for i in k:# for 반복문에 paper scissor rock을 넣어서 자동적으로 Resize를 실행하게 한다.
image_dir_path = os.getenv("HOME") + "/user/rock_scissor_paper_train/"+i
print("이미지 디렉토리 경로: ", image_dir_path)
images=glob.glob(image_dir_path + "/*.jpg")
# 파일마다 모두 28x28 사이즈로 바꾸어 저장합니다.
target_size=(28,28)
for img in images:
old_img=Image.open(img)
new_img=old_img.resize(target_size,Image.ANTIALIAS)
new_img.save(img,"JPEG")
for i in k:
image_dir_path = os.getenv("HOME") + "/user/rock_scissor_paper_test/"+i
print("이미지 디렉토리 경로: ", image_dir_path)
images=glob.glob(image_dir_path + "/*.jpg")
# 파일마다 모두 28x28 사이즈로 바꾸어 저장합니다.
target_size=(28,28)
for img in images:
old_img=Image.open(img)
new_img=old_img.resize(target_size,Image.ANTIALIAS)
new_img.save(img,"JPEG")
print("모든 이미지 resize 완료!")
위 코드는 모든 사진의 크기를 28 x 28로 reshape하는 코드로 test set과 train set이 한 디렉토리안에 존재하고 서로 분리되어 있다는 가정하에 작성한 코드이다. 위 코드를 이용하여 전처리 작업이 완료되었다면 이제 네트워크를 구성하고 학습을 실행해 보자.
7. 가위 바위 보 분류기 만들기 - 학습 및 결과 도출
#Rock scissor paper
import tensorflow as tf
from tensorflow import keras
import numpy as np
import matplotlib.pyplot as plt
print(tf.__version__) # Tensorflow의 버전을 출력
def load_data_train(img_path):#train data를 load하는 함수
# 가위 : 0, 바위 : 1, 보 : 2
number_of_data=3000 # 총 3000장의 데이터를 사용한다. 이 값은 이미지데이터와 label데이터를 저장하는 행렬을 만드는데 사용된다.
img_size=28
color=3
#이미지 데이터와 라벨(가위 : 0, 바위 : 1, 보 : 2) 데이터를 담을 행렬(matrix) 영역을 생성합니다.
#np.zeros는 0으로 채워진 벡터 생성 -> reshape하여 3000 x 28 x 28 x 3 의 차원이 4인 행렬 생성
imgs=np.zeros(number_of_data*img_size*img_size*color,dtype=np.int32).reshape(number_of_data,img_size,img_size,color)
labels=np.zeros(number_of_data,dtype=np.int32)#labels는 차원 데이터를 담지 않기 때문에 벡터로 생성 (3000,)
idx=0
for file in glob.iglob(img_path+'/scissor/*.jpg'):
img = np.array(Image.open(file),dtype=np.int32)#Image 한장을 가져와 데이터화 하여 저장한다.
imgs[idx,:,:,:]=img # 데이터 영역에 이미지 행렬을 복사
labels[idx]=0 # 가위 : 0
idx=idx+1
for file in glob.iglob(img_path+'/rock/*.jpg'):
img = np.array(Image.open(file),dtype=np.int32)
imgs[idx,:,:,:]=img # 데이터 영역에 이미지 행렬을 복사
labels[idx]=1 # 바위 : 1
idx=idx+1
for file in glob.iglob(img_path+'/paper/*.jpg'):
img = np.array(Image.open(file),dtype=np.int32)
imgs[idx,:,:,:]=img # 데이터 영역에 이미지 행렬을 복사
labels[idx]=2 # 보 : 2
idx=idx+1
print("학습데이터(x_train)의 이미지 개수는",idx,"입니다.")
return imgs, labels
image_dir_path_train = os.getenv("HOME") + "/aiffel/rock_scissor_paper_train"
(x_train, y_train)=load_data_train(image_dir_path_train)
x_train_norm = x_train/255.0 # 입력은 0~1 사이의 값으로 정규화
print("x_train shape: {}".format(x_train.shape))
print("y_train shape: {}".format(y_train.shape))
def load_data_test(img_path):#test를 위한 데이터를 load하는 함수
# 가위 : 0, 바위 : 1, 보 : 2
number_of_data=300 # 300장의 image를 이용한다.
img_size=28
color=3
#이미지 데이터와 라벨(가위 : 0, 바위 : 1, 보 : 2) 데이터를 담을 행렬(matrix) 영역을 생성합니다.
imgs=np.zeros(number_of_data*img_size*img_size*color,dtype=np.int32).reshape(number_of_data,img_size,img_size,color)
labels=np.zeros(number_of_data,dtype=np.int32)
idx=0
for file in glob.iglob(img_path+'/scissor/*.jpg'):
img = np.array(Image.open(file),dtype=np.int32)
imgs[idx,:,:,:]=img # 데이터 영역에 이미지 행렬을 복사
labels[idx]=0 # 가위 : 0
idx=idx+1
for file in glob.iglob(img_path+'/rock/*.jpg'):
img = np.array(Image.open(file),dtype=np.int32)
imgs[idx,:,:,:]=img # 데이터 영역에 이미지 행렬을 복사
labels[idx]=1 # 바위 : 1
idx=idx+1
for file in glob.iglob(img_path+'/paper/*.jpg'):
img = np.array(Image.open(file),dtype=np.int32)
imgs[idx,:,:,:]=img # 데이터 영역에 이미지 행렬을 복사
labels[idx]=2 # 보 : 2
idx=idx+1
print("검사데이터(x_test)의 이미지 개수는",idx,"입니다.")
return imgs, labels
image_dir_path_test = os.getenv("HOME") + "/aiffel/rock_scissor_paper_test"
(x_test, y_test)=load_data_test(image_dir_path_test)
x_test_norm = x_test/255.0 # 입력은 0~1 사이의 값으로 정규화
print("x_test shape: {}".format(x_test.shape))
print("y_test shape: {}".format(y_test.shape))
print('최소값:',np.min(x_train_norm), ' 최대값:',np.max(x_train_norm))# 정규화한 데이터가 정상적인지 확인한다.
model=keras.models.Sequential()
model.add(keras.layers.Conv2D(32, (3,3), activation='relu', input_shape=(28,28,3)))
model.add(keras.layers.MaxPool2D(2,2))
model.add(keras.layers.Conv2D(64, (3,3), activation='relu'))
model.add(keras.layers.MaxPooling2D((2,2)))
model.add(keras.layers.Flatten())
model.add(keras.layers.Dense(32, activation='relu'))
model.add(keras.layers.Dense(3, activation='softmax'))
print('Model에 추가된 Layer 개수: ', len(model.layers))
model.summary()
model.compile(optimizer='adam',
loss='sparse_categorical_crossentropy',
metrics=['accuracy'])
model.fit(x_train_norm, y_train, epochs=25)
test_loss, test_accuracy = model.evaluate(x_test_norm,y_test, verbose=2)
print("test_loss: {} ".format(test_loss))
print("test_accuracy: {}".format(test_accuracy))
위 코드는 3000개의 train data와 300개의 test data를 사용한다는 전제하에 작성된 코드이다.
위 코드의 순서를 간략히 요약하자면 다음과 같다.
train data 불러오기 -> train data 정규화 -> test data 불러오기 -> test data정규화 -> 네트워크 튜닝 ->compile / fit -> evaluate
실행결과
2.2.0
학습데이터(x_train)의 이미지 개수는 3000 입니다.
x_train shape: (3000, 28, 28, 3)
y_train shape: (3000,)
검사데이터(x_test)의 이미지 개수는 300 입니다.
x_test shape: (300, 28, 28, 3)
y_test shape: (300,)
최소값: 0.0 최대값: 1.0
Model에 추가된 Layer 개수: 7
Model: "sequential_2"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
conv2d_4 (Conv2D) (None, 26, 26, 32) 896
_________________________________________________________________
max_pooling2d_4 (MaxPooling2 (None, 13, 13, 32) 0
_________________________________________________________________
conv2d_5 (Conv2D) (None, 11, 11, 64) 18496
_________________________________________________________________
max_pooling2d_5 (MaxPooling2 (None, 5, 5, 64) 0
_________________________________________________________________
flatten_2 (Flatten) (None, 1600) 0
_________________________________________________________________
dense_4 (Dense) (None, 32) 51232
_________________________________________________________________
dense_5 (Dense) (None, 3) 99
=================================================================
Total params: 70,723
Trainable params: 70,723
Non-trainable params: 0
_________________________________________________________________
Epoch 1/25
94/94 [==============================] - 7s 69ms/step - loss: 1.0950 - accuracy: 0.3797
Epoch 2/25
94/94 [==============================] - 0s 2ms/step - loss: 1.0176 - accuracy: 0.4817
Epoch 3/25
94/94 [==============================] - 0s 2ms/step - loss: 0.7740 - accuracy: 0.6693
Epoch 4/25
94/94 [==============================] - 0s 1ms/step - loss: 0.5995 - accuracy: 0.7567
Epoch 5/25
94/94 [==============================] - 0s 2ms/step - loss: 0.4672 - accuracy: 0.8223
Epoch 6/25
94/94 [==============================] - 0s 2ms/step - loss: 0.3768 - accuracy: 0.8650
Epoch 7/25
94/94 [==============================] - 0s 1ms/step - loss: 0.3182 - accuracy: 0.8920
Epoch 8/25
94/94 [==============================] - 0s 2ms/step - loss: 0.2484 - accuracy: 0.9180
Epoch 9/25
94/94 [==============================] - 0s 2ms/step - loss: 0.2177 - accuracy: 0.9210
Epoch 10/25
94/94 [==============================] - 0s 2ms/step - loss: 0.1751 - accuracy: 0.9403
Epoch 11/25
94/94 [==============================] - 0s 2ms/step - loss: 0.1539 - accuracy: 0.9463
Epoch 12/25
94/94 [==============================] - 0s 1ms/step - loss: 0.1230 - accuracy: 0.9597
Epoch 13/25
94/94 [==============================] - 0s 2ms/step - loss: 0.1104 - accuracy: 0.9643
Epoch 14/25
94/94 [==============================] - 0s 1ms/step - loss: 0.1165 - accuracy: 0.9607
Epoch 15/25
94/94 [==============================] - 0s 2ms/step - loss: 0.0803 - accuracy: 0.9760
Epoch 16/25
94/94 [==============================] - 0s 2ms/step - loss: 0.0811 - accuracy: 0.9793
Epoch 17/25
94/94 [==============================] - 0s 2ms/step - loss: 0.0620 - accuracy: 0.9863
Epoch 18/25
94/94 [==============================] - 0s 2ms/step - loss: 0.0603 - accuracy: 0.9820
Epoch 19/25
94/94 [==============================] - 0s 2ms/step - loss: 0.0568 - accuracy: 0.9847
Epoch 20/25
94/94 [==============================] - 0s 2ms/step - loss: 0.0489 - accuracy: 0.9890
Epoch 21/25
94/94 [==============================] - 0s 2ms/step - loss: 0.0422 - accuracy: 0.9900
Epoch 22/25
94/94 [==============================] - 0s 2ms/step - loss: 0.0338 - accuracy: 0.9940
Epoch 23/25
94/94 [==============================] - 0s 2ms/step - loss: 0.0265 - accuracy: 0.9953
Epoch 24/25
94/94 [==============================] - 0s 2ms/step - loss: 0.0661 - accuracy: 0.9787
Epoch 25/25
94/94 [==============================] - 0s 2ms/step - loss: 0.0217 - accuracy: 0.9973
10/10 - 3s - loss: 2.3859 - accuracy: 0.6533
test_loss: 2.3858959674835205
test_accuracy: 0.653333306312561
학습시킨 모델을 test데이터를 이용하여 결과를 도출하였을 때, 정확도는 65로 낮은 값이 나왔다. 하지만 이전에 300개로 train을 진행하였을 때 정확도가 33으로 나온것을 참고하면 데이터의 증가로 정확도가 크게 증가하였음을 알 수 있다.
참고로 아래 코드를 위 코드의 마지막 부분에 입력해주면 결과를 시각화그래프를 통해 확인할 수 있다.
print(results.history.keys())
# summarize history for accuracy
plt.plot(results.history['accuracy'])
plt.plot(results.history['val_accuracy'])
plt.title('model accuracy')
plt.ylabel('accuracy')
plt.xlabel('epoch')
plt.legend(['train', 'test'], loc='upper left')
plt.show()
# summarize history for loss
plt.plot(results.history['loss'])
plt.plot(results.history['val_loss'])
plt.title('model loss')
plt.ylabel('loss')
plt.xlabel('epoch')
plt.legend(['train', 'test'], loc='upper left')
plt.show()
실행결과


요약
MNIST와 가위 바위 보 사진을 이용하여 딥러닝의 데이터 준비 -> 딥러닝 네트워크 설계 -> 학습 -> 테스트(평가) 단계를 이해하고 실현하였다.
이용한 함수 설명
imshow(x_train, cmp=plt.cm.binary)
- 이미지를 시각화하여 출력하는 함수로 이미지의 형태가 binary형태라면 두번째 인자로 cmap에 binary를 입력하여 전달한다. (cmap = color map)
format()
- format은 문자열을 보다 쉽고 간단하게 표현하기 위한 함수로 str에 {} 의 위치에 format함수에 인자로 전달된 값을 넣어서 저장하거나 출력하게 한다.
reshape()
- reshape는 numpy 라이브러리의 함수로 주어진 배열의 구조를 재배열 하는 함수이다.
evaluate(x,y,verbose)
- 테스트 모드에서 모델의 손실 값과 측정항목 값을 반환한다. 여기서 verbose는 0,1,2의 값이 들어갈 수 있으며 0은 silent,
1은 progress bar , 2는 one line per epoch를 의미한다.
predict()
- input sample에 대한 output 예측을 생성한다.
argmax()
- numpy라이브러리의 함수로 다차원 배열의 경우에 차원에 따라 가장 큰 값의 인덱스들을 반환해 주는 함수
enumerate()
- 반복문 사용시 몇 번째 반복문인지 확인이 필요할 때 이용하며 인덱스 번호와 컬렉션의 원소를 tuple로 반환
random.choices(x,k)
- x에서 중복을 허락하면서 선택한 k크기의 요소 리스트를 반환
os.getenv()
- 환경변수를 얻는 함수
glob.glob() / glob.iglob()
- glob은 파일들의 리스트를 뽑을 때 사용하며 현재 디렉토리에서 인수로 전달된 str이 파일명에 포함된 파일을 불러온다.
이때, 재귀적으로 현재폴더의 모든 하위폴더까지 탐색하여 파일을 불러온다면 i를 붙여서 glob.iglob으로 사용한다.
img.resize()
- pillow 라이브러리의 함수로 사진의 사이즈를 변경할 때 사용
np.zeros(shape)
- 0으로 채워진 array 생성
np.array(img,dtype=np.int32)
img의 정보를 이용하여 array를 성성하는 것으로 그 형태는 dtype에 의하여 결정된다.
'SSAC X AIffel > EXPLORATION_SSAC' 카테고리의 다른 글
[EXPLORATION] 2-1. scikit-learn Model / Confusion Matrix (0) | 2021.02.19 |
---|---|
[EXPLORATION] 2. scikit-learn 내장 분류 모델 학습 (0) | 2021.01.23 |