CNN 이미지 처리 실전 (1)
이제 이론편에서 공부한 내용을 실전에 적용해 보겠습니다.
1. 데이터 로드
텐서플로우에 있는 fashion_mnist 라는 데이터를 받아오도록 하겠습니다.
from tensorflow.keras.datasets import fashion_mnist
간략히 설명드리자면 fashion minist 데이터셋은 패션 이미지들의 모음으로 이루어져 있습니다.
이미지의 기본 크기는 28*28 이며, 이미지의 개수는 70000개입니다.
(train_x, train_y), (test_x, test_y) = fashion_mnist.load_data()
2. 데이터 형태 확인
자 그러면 받을 데이터들을 train_x, train_y, test_x, test_y 로 나누어서 받아줄 겁니다.
train_x, train_y의 데이터는 기출문제와 기출문제 정답이라고 생각하시면 이해에 도움이 되실거고
test_x, test_y의 데이터는 실제 시험과 실제 시험 정답이라고 비유적으로 생각해보겠습니다!
train_x.shape, train_y.shape
((60000, 28, 28), (60000,))
test_x.shape, test_y.shape
((10000, 28, 28), (10000,))
다음 모양들을 살펴보면 28*28 인 train_x 데이터가 60000장, train_y 데이터가 1차원으로 60000장인 것을 볼 수 있고,
test_x 는 28*28인 크기 10000장, test_y 데이터가 1차원으로 10000장이 있는 것을 확인할 수 있습니다.
그렇다면 실제 자료가 어떻게 이루어져 있는지 확인해보겠습니다.
train_y[:10]
# array([9, 0, 0, 3, 0, 2, 7, 2, 5, 5], dtype=uint8)
y데이터를 10개정도 끄집어 내어 확인해보니 이미 라벨링 되어 있는 값이 있습니다.
이 값들은 각 숫자에 따라 정해진 종류가 있음을 의미합니다 (분류 계층 기억하시죠?)
3. 데이터 전처리
데이터들에 대해 살펴보았다면 이제는 데이터를 처리하는 방법에 대해 알아보겠습니다.
이것은 텐서플로우의 고유 특성인데, 학습 데이터의 모양을 3차원으로 맞춰주어야만 합니다.
그래서 reshape을 통해서 데이터의 차원을 변경해주겠습니다.
train_x = train_x.reshape((60000,28,28,1))
test_x = test_x.reshape((-1,28,28,1))
이렇게 train_x, test_x 값만 변경을 진행시켜주면 됩니다.
여기에서 같은 크기였지만 하나는 60000을 사용하고 다른 하나는 -1을 사용하였죠? 결론을 말씀드리자면 둘의 결과는 같습니다! -1 은 '나머지'라고 생각하시면 됩니다.
28*28*1 로 3차원 데이터로 변경하였으니 60000장을 어떻게 해야할 지 고민이 있을 때, 나머지 개수를 -1로 표현하여 자동으로 개수를 맞출 수 있습니다. 지금과 같은 경우에는 크게 중요하지 않지만, 복잡한 reshape이 발생할 경우 셈을 어떻게 할지 고민이 많아 -1을 자주 사용하게 될 것입니다!
이 reshape의 과정은 데이터의 전처리 라고 표현합니다. 이외에도 one-hot encoding label encoding 등 많은 전처리 과정이 있습니다. 해당 내용들은 다음 시간에 천천히 설명드리겠습니다 :)
4. 레이어 생성
전처리 과정이 끝났다면 이제는 이론편에서 보았던 layer를 쌓는 방법에 대해 살펴보겠습니다.
우선은 model과 layer 정보를 가져오도록 하겠습니다.
from tensorflow.keras import models, layers
# from tensorflow.keras.models import Seuquential
# from tensorflow.keras.layers import Conv2D, MaxPool2D
보통은 1번째 줄 처럼 데이터를 한번에 가져올 수도 있지만, 2번째 3번째 줄처럼 특정 함수만 가져와서 사용할 수도 있습니다.
먼저 레이어를 쌓기 위해선 가장 큰 틀인 모델 객체가 있어야겠죠? 먼저 틀을 만들어주겠습니다.
model = models.Sequential()
그리고 이 틀 속에 레이어를 쌓아나가보겠습니다.
우선은 CNN처리에서 가장 중요한 Convolution layer를 쌓겠습니다.
함수는 layers.Conv2D 를 사용하고 이곳에서 들어갈 filter의 개수, kernel_의 크기, activation 함수, input_shape, name 등을 지정해 줄 수 있습니다.
여기에서 저희가 이론편에서 배우지 않은 내용들이 등장하고 있어 짚고 넘어가겠습니다!
가장 중요한 부분은 kernel과 filter 의 차이점을 이해하는 것입니다.
우선 비유를 먼저 하자면, kernel은 프린터, filter는 프린트된 종이로 보겠습니다.
kernel은 이론편에서서 설명했던 필터의 크기를 의미합니다. 이론편에서 필터가 움직이게 되면 1번 영역 2번영역 3번...해서 9번까지 연산이 이루어졌었죠? 이때 각 영역에 대한 연산이 이루어진 결과는 filter 입니다.
통상적으로 kernel과 filter에 대해서 잘 구분하지 않고 사용하고 있어 다소 헷갈릴 수 있겠지만, 그래도 기본적인 개념은 꼭 알고 가셨으면 좋겠습니다!
그렇게 해서 kernel_size는 튜플형태로 넣어도 괜찮고(ex : (3,3) ), 정수( 3 ) 으로 입력해도 괜찮습니다.
activation 함수는 입력된 데이터의 연산을 출력할 경우 어떻게 출력할 지 판단을 해내는 역할을 한다고 먼저 이해해주시면 좋겠습니다. 자세한 함수의 종류는 나중에 따로 정리해서 말씀드리겠습니다.
input_shape은 레이어가 처음 들어갈 경우 통상적으로 입력을 해줍니다. 입력은 안해도 괜찮지만, 넣지 않을 경우 feature map의 크기를 추정할 수 없다는 단점이 있어 첫 레이어에는 input_shape을 입력해주면 좋습니다.
마지막으로 name 은 만든 레이어에 이름을 붙여주는 역할을 하고 있습니다!
이렇게 convolution layer를 쌓았다면 이 정보를 조금 더 압축하는 과정이 필요합니다.
바로 pooling이죠
이번에 저희는 영역당 최대값으로 축소시키는 maxpooling을 사용하겠습니다.
먼저 pool_size, 압축할 사이즈를 설정하고, 해당 레이어에 name을 붙여줄 수 있습니다.
이 방식으로 세 개의 레이어를 쌓겠습니다.
필터의 개수, kernel_size, 활성함수, pool_size는 임의로 설정하였습니다.
하지만 input_shape은 이미지 텐서의 차원을 맞추도록 (28,28,1)로 맞춰주어야합니다!
model.add(layers.Conv2D(filters = 32, kernel_size = 3, activation = 'relu',
input_shape = (28,28,1), name = 'block_1_conv'))
model.add(layers.MaxPool2D(pool_size = 2, name = 'block_1_pool'))
model.add(layers.Conv2D(filters = 64, kernel_size = 3, activation = 'relu',name = 'block_2_conv'))
model.add(layers.MaxPool2D(pool_size = 2, name = 'block_2_pool'))
model.add(layers.Conv2D(filters = 64, kernel_size = 3, activation = 'relu', name = 'block_3_conv'))
이렇게 레이어를 쌓아준 다음 바로 학습에 들어가면 좋겠지만..! 1차원으로 데이터의 모양을 변경시켜주어야 학습이 이루어집니다. 그러기 위해서 flatten() 이 사용됩니다!
그리고 1차원으로 변형되면 개수가 많기 때문에 Dense로 퍼셉트론의 개수를 조금 줄이고,
최종적으로 결과를 도출하기 위한 출력 결과를 10으로 설정하겠습니다.
model.add(layers.Flatten())
model.add(layers.Dense(units = 64, activation = 'relu')) # 64개의 퍼셉트론으로 줄이기
model.add(layers.Dense(units = 10, activation = 'softmax')) # 10개의 출력 퍼셉트론, 그리고 softmax
이렇게 쌓은 레이어의 결과가 정상적으로 추가되었는지 확인해보겠습니다.
model.summary()
Model: "sequential"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
block_1_conv (Conv2D) (None, 26, 26, 32) 320
_________________________________________________________________
block_1_pool (MaxPooling2D) (None, 13, 13, 32) 0
_________________________________________________________________
block_2_conv (Conv2D) (None, 11, 11, 64) 18496
_________________________________________________________________
block_2_pool (MaxPooling2D) (None, 5, 5, 64) 0
_________________________________________________________________
block_3_conv (Conv2D) (None, 3, 3, 64) 36928
_________________________________________________________________
flatten (Flatten) (None, 576) 0
_________________________________________________________________
dense (Dense) (None, 64) 36928
_________________________________________________________________
dense_1 (Dense) (None, 10) 650
=================================================================
Total params: 93,322
Trainable params: 93,322
Non-trainable params: 0
_________________________________________________________________
summery에 대한 내용은 다음 시간에 자세히 살펴보도록 하겠습니다.
5. 학습과 평가를 어떻게 할 지 정의하기
이렇게 모델을 만들고나면 compile을 통해 loss값, optimizer, metrics 값을 지정해줍니다.
loss는 손실함수를 의미합니다. 위의 네트워크(neural network) 에서 내놓는 결과와 실제 결과와의 차이를 정의하는 함수입니다. loss function 은 몇가지 종류를 가지고 있지만 어려운 수학이 많다보니 패스..
optimizer는 최적화 알고리즘입니다. loss function의 값이 최소값을 갖는 부분을 찾는 방법입니다. 이것도 여러 종류가 있는데 기회가 된다면 나중에 설명드리도록 하겠습니다 :)
마지막으로 metrics는 평가지표입니다. accuracy는 모델이 예측한 값과 실제 정답 중 실제로 모델이 얼마나 맞췄는지 측정하는 척도입니다. 이외에도 다양한 평가척도가 있습니다.
model.compile(loss = 'sparse_categorical_crossentropy',
optimizer = 'rmsprop',
metrics = ['accuracy'])
6. 마지막, 학습
학습을 위한 설정, x에 train데이터 y에 train 결과값을 먼저 집어넣습니다.
그리고 epochs : 몇번 반복실행할 지, batch_size 한번에 얼마의 크기로 학습할지
validation_split 검증 단계 비율은 얼마나 할 지로 구분해줍니다.
잘 이해가 안된다면 아래 비유를 통해서 다시 이해해보겠습니다.
epochs은 공부할 때 반복해서 기출문제를 공부하면 더 좋은 성과를 내듯이 기출문제를 몇 번 반복학습할 지 설정하는 것입니다.
batch_size 는 우리가 한 세트에 몇 문제를 풀 지를 정하는 것입니다.
마지막으로 validation은 training 파트를 쪼개 모델의 성능을 평가하기 위해 사용합니다. 일종의 모의고사죠!
training이 잘 되고 있는지 확인하는 역할이기에 학습에 큰 도움이 됩니다. 이번에 저희는 training 20% 를 validation으로 사용하겠습니다.
model.fit(x = train_x, y = train_y, epochs = 50, batch_size = 128,
validation_split = 0.2)
Epoch 1/50
375/375 [==============================] - 34s 6ms/step - loss: 0.7224 - accuracy: 0.7960 - val_loss: 0.3737 - val_accuracy: 0.8650
Epoch 50/50
375/375 [==============================] - 2s 5ms/step - loss: 0.1315 - accuracy: 0.9652 - val_loss: 0.8027 - val_accuracy: 0.8795
<tensorflow.python.keras.callbacks.History at 0x7f46607e53d0>
마지막으로 학습이 끝났으니 loss 값과 정확도를 측정하는 것으로 학습 결과를 확인함으로써 이미지 처리가 마무리 됩니다.
loss, accuracy = model.evaluate(x=test_x,y=test_y)
# 313/313 [==============================] - 1s 2ms/step - loss: 0.9179 - accuracy: 0.8733
결과를 보면 loss 값이 높게 나왔지만, 정확도는 87%라는 결과를 얻을 수 있었습니다.
이 수치들은 학습을 진행할 때마다 조금 씩 차이가 날 수도 있습니다..!
화려한 결과가 눈으로 와닿지는 않지만, 정확도 수치를 보며 뿌듯함만 느꼈으면,,