220420 MNIST 데이터 분류

김응주·2022년 4월 20일
0

핸즈온 머신러닝

목록 보기
1/1

MNIST 데이터 분류 문제

MNIST 데이터란?

고등학생과 미국 인구조사국 직원들이 손으로 쓴 70,000개의 작은 숫자 이미지를 모은 것

사이킷런에서 MNIST 데이터셋 불러오기

from sklearn.datasets import fetch_openml
mnist = fetch_openml('mnist_784', version = 1)
mnist.keys()

데이터셋 확인

mnist['data']

각 row가 784개의 column, pixel로 구성된 것을 보아 사이킷런에서 불러온 데이터는 28x28 크기인 것을 알 수 있습니다. 각각의 column값은 0(white)부터 255(black)까지 pixel의 강도를 의미합니다.

mnist['target']

target label은 각 이미지가 어떤 숫자인지를 나타냅니다. 예를 들어, 0번 row의 target label은 5이므로 0번 row는 5를 손글씨로 쓴 이미지입니다.

모델 생성에 앞서...

모델을 생성하기 전, 문제를 정확히 정의할 필요가 있습니다. 저는 간단히 아래와 같이 정의해보았습니다.

784개의 pixel data(column, 특성)를 학습해
각각의 손글씨 이미지(row)를
0, 1, 2, 3, 4, 5, 6, 7, 8, 9의 10개 중 하나로 분류(다중 분류)

저는 본 문제를 다중 분류기를 통해 해결하고자 하지만, 사실 이진 분류기를 통해서도 다중 분류 문제를 해결할 수 있습니다. 어떤 내용인지 자세히 살펴봅시다. 먼저 다중 분류기라고 한다면, 하나의 모델에서 0, 1, ... , 8, 9 까지 10개의 class를 모두 분류하는 것입니다.

이진 분류기를 통해 다중 분류 문제를 해결하는 방법은, class의 개수에 맞추어 이진 분류기를 생성하는 것입니다. 본 문제로 예를 들면, '0'만을 인식하는 모델, '1'만을 인식하는 모델, ... , '8'만을 인식하는 모델, '9'만을 인식하는 모델을 생성하는 방식으로 OvR(one-versus-the-rest), 또는 OvA(one-versus-all) 전략이라고 말합니다.

또 다른 방법은 두 개의 class만을 비교하는 방법입니다. '0'과 '1' 구별, '1'과 '2' 구별과 같은 방식입니다. OvO(one-versus-one)라고 말하는데, 명확한 장점이 있습니다. 각 모델의 훈련에 필요한 데이터의 양이 줄어든다는 것입니다. 본 문제에서 각 class 별로 7,000개의 row가 존재한다고 가정해봅시다(10 class x 7,000 row = 70,000 row). 다중 분류기의 학습을 위해서는 당연히 70,000개의 모든 row가 필요합니다. 반면, '0'과 '1'을 분류하는 모델을 만들 때에는 14,000개의 row만으로 충분합니다. 물론 그만큼 여러 개의 모델을 생성해야 하지만, SVM처럼 훈련 데이터셋의 크기에 민감한 일부 모델에서는 OvO와 같은 방법이 더 효율적입니다.

OvO 방법은 명확한 장점이 존재하는데, OvR 방법은 과연 다중 분류기에 비해 어떤 점이 좋을까 곰곰이 생각해봤습니다. 책에서도 장점에 대한 설명은 딱히 없었기 때문에... 비교하자면 여러 개의 모델 vs 하나의 모델인데, OvO 처럼 데이터의 크기에서 갖는 이점은 크게 없지 않나 싶습니다. 그나마 생각되는 부분은 target label과 나머지 label의 불균형 문제 해결을 위해, 훈련 과정에서 under sampling을 하게 되면 확실히 훈련 데이터의 크기가 약간 줄어들 수 있다 정도입니다. 물론 under sampling은 훈련 시에만 적용하고, 검증 시에는 적용하지 않습니다. 다른 생각은, 가정이지만, 각각의 모델을 별도로 관리할 수 있다 입니다. 예를 들면, '0'의 경우는 분류가 수월해서 적은 데이터셋으로도 높은 precision과 recall을 얻을 수 있지만, '1'의 경우는 '7'과 혼동하는 경우가 많아서 다른 모델과는 달리, '1'과 '7' data를 더 추가해서 훈련을 진행하는 등 모델을 별도로 관리, 훈련할 수 있다고 생각했습니다. 다중 분류기를 만든다면 하나의 모델에 계속해서 훈련 데이터를 투입하는 방식으로 진행하지만, OvR 방법이라면 각각의 모델 중 성능이 나쁜 모델에만 훈련데이터를 투입하면 되지 않을까 합니다.

모델 생성

keras를 통해 간단한 dnn 모델을 생성해봤습니다.

from tensorflow import keras

model = keras.models.Sequential()
model.add(keras.layers.Flatten(input_shape=[784]))
model.add(keras.layers.Dense(300, activation = 'relu'))
model.add(keras.layers.Dense(100, activation = 'relu'))
model.add(keras.layers.Dense(10, activation = 'softmax'))

model.compile(loss='categorical_crossentropy',
            optimizer='adam',
            metrics=['accuracy',
                     tf.keras.metrics.Precision(name='precision'),
                     tf.keras.metrics.Recall(name='recall')])

model.summary()

history = model.fit(X_train, y_train, epochs=10, batch_size=100, validation_split = 0.2, validation_data=(X_test, y_test))

간단한 dnn 모델을 통해 precision 0.98, recall 0.97, accuracy 0.98의 성능에 도달했습니다.

profile
딥러닝 공부!

0개의 댓글