이포스트는 이미지를 딥러닝으로 분류할 때 데이터 셋이 적고 통상적으로 퍼져있는 이미지 데이터셋을 사용하지 못할 때 유용한 방법에 대한 포스트입니다.

Introduction

일반적으로 말하는 Object Detection은 물체를 1.탐지 하고 2.구분 해주는 것을 의미합니다. 한 장의 사진에서 원하는 물체를 찾아서 분류까지 해주는 강력한 방법이지만 실제로 실무에 활용하려면 전제조건이 있어야합니다.

  1. 많은 이미지 데이터 셋을 보유하고 있거나
  2. 통상적으로 학습된 이미지 데이터 셋에 포함되어있다.

개인적으로 프로젝트를 진행하는 경우에 1번과 2번 모두 포함되지 않는 경우가 있습니다. 이러한 경우에 어떻게 내가 원하는 물체를 구분할 수 있을까 고찰했습니다.

고민 결과를 먼저 말씀 드리면, 분류를 원하는 물체를 Detection & Crop하고, Crop된 이미지를 딥러닝으로 학습시켜 모델링 하는 것입니다. 제가 진행중인 명품 시계 이미지 분류 Project를 예시로 설명 하겠습니다. (Project관련 자세한 내용은 따로 업로드 할 예정입니다.)

Detection & Crop

한 장의 이미지에서 제가 원하는 이미지를 Detection하기 위한 학습을 진행하는 것은 매우 어려운 일이고, 충분한 이미지 데이터 셋도 가지고 있어야 가능합니다.

명품 시계 이미지 분류도 마찬가지 입니다. 이전에 포스팅 했던 crawling을 활용하더라도, 대량의 이미지 데이터 셋을 모으기에는 턱없이 부족합니다. 따라서 제가 분류하고자 하는 물체의 상위 카테고리를 활용하면 어떨까 하는 생각이 들었습니다.

[STEP1] 잘 알려진 이미지 데이터 셋 확인

tensorflow hub나 pytorch에 있는 detection model을 확인해보면, 일반적으로 잘 알려진 이미지 데이터 셋을 활용하여 detection을 진행하고 있습니다.

저는 그 중에서 coco2017 데이터 셋을 활용한 detection model을 채택 했습니다. 채택 기준은 해당 모델이 시계를 Detection 해 주기 때문입니다.

coco2017 데이터 셋

논문이나 대회에서 많이 사용되는 이미지 셋이고 다음과 같이 이루어져 있습니다. (https://cocodataset.org/)

▶ 학습(training) 데이터셋: 118,000장의 이미지
▶ 검증(validation) 데이터셋: 5,000장의 이미지
▶ 테스트(test) 데이터셋: 41,000장의 이미지

[STEP2] 모델 선택 및 detection & Crop

coco2017 데이터 셋이 “clock"을 분류해 주기 때문에 해당 데이터 셋으로 학습된 detection model을 찾다 서버를 따로 가지고 있지 않기 때문에 비교적 간단한 Faster R-CNN with Resnet-50 V1 모델을 선택했습니다.

저는 tessorflow를 통해 딥러닝을 진행하고 있기 때문에 tensorflow hub로 detection 모델을 불러왔고, 해상도는 1024*1024를 사용했습니다. 명품 시계를 분류하기 위해서는 해상도가 꽤 커야 디테일한 부분을 구분할 수 있을 거라고 생각했기 때문입니다.

해당 모델을 통해 시계를 Detection한 결과는 만족스러웠습니다. (약 98% 디텍션 성능)
다음은 제가 수집한 이미지에서 명품 시계를 분류해주는 예시입니다.
detection model을 통해 object를 분류하고, Crop한 이미지를 통해 이미지셋을 만들어줍니다.

Classification

새로운 시계 이미지 데이터 셋을 통해 어떤 명품 시계인지 분류하는 작업이 남았습니다.
제가 수집한 이미지 데이터 셋이 많이 않았기 때문에, 전이 학습을 활용해야 겠다고 생각했습니다.

전이학습(Transfer Learning)

기존에 학습된 모델의 fully-connected-layer(최상위층)만 학습 시키는 방법입니다.
학습 속도가 빠르면서 정교한 분류가 가능하기 때문에 이미지 데이터 셋이 적을 때 널리 사용되고 있는 방식입니다.

[STEP1] Image classification 모델 결정

전이학습에 사용할 모델을 결정하기 위해서 두가지를 고려 했습니다.

  1. 내 컴퓨터에서 학습할 수 있을 만큼 가벼운가
  2. 성능이 잘 나오는가

두가지를 고려하여 제가 선택한 모델은 MobileNetV2와 DenseNet121 모델입니다.
MobileNetV2는 매우 가볍다는 것이 장점이고, DesneNet121 모델은 정확도가 높은것이 장점입니다.

[STEP2] 모델 학습 및 결과

전이 학습을 위해서는 기존 모델을 불러오고, 가장 최상위 층만 수정하여 모델을 구성해야 합니다.

# base model
base_model = DenseNet121(weights="imagenet", include_top=False)

# Add custom classification layers on top
x = base_model.output
x = GlobalAveragePooling2D()(x)
x = Dense(128, activation="relu")(x)
predictions = Dense(len(train_data.class_indices), activation="softmax")(x)

# Create the model
model = Model(inputs=base_model.input, outputs=predictions)

# Freeze layers in the base model
for layer in base_model.layers:
    layer.trainable = False

# Compile the model
model.compile(
    optimizer=Adam(0.005), loss="categorical_crossentropy", metrics=["accuracy"]
)

최상위 층을 제외하고 나머지는 학습이 되지 않아야 하므로, 다른 층을 모두 동결 시켜주는 것이 매우 중요합니다.
이후 모델 학습을 진행하시면 됩니다.

모델 학습 결과

Epoch 1/50
2023-09-09 21:39:18.600254: W tensorflow/core/platform/profile_utils/cpu_utils.cc:128] Failed to get CPU frequency: 0 Hz
2023-09-09 21:39:20.846062: I tensorflow/core/grappler/optimizers/custom_graph_optimizer_registry.cc:113] Plugin optimizer for device_type GPU is enabled.
98/98 [==============================] - ETA: 0s - loss: 1.9716 - accuracy: 0.44372023-09-09 21:39:44.809292: I tensorflow/core/grappler/optimizers/custom_graph_optimizer_registry.cc:113] Plugin optimizer for device_type GPU is enabled.

Epoch 1: val_accuracy improved from -inf to 0.61769, saving model to ./epic_models/DN_TL_230909.h5
98/98 [==============================] - 34s 314ms/step - loss: 1.9716 - accuracy: 0.4437 - val_loss: 1.2182 - val_accuracy: 0.6177 - lr: 0.0010
Epoch 2/50
98/98 [==============================] - ETA: 0s - loss: 0.8980 - accuracy: 0.7374
Epoch 2: val_accuracy improved from 0.61769 to 0.78153, saving model to ./epic_models/DN_TL_230909.h5
98/98 [==============================] - 28s 282ms/step - loss: 0.8980 - accuracy: 0.7374 - val_loss: 0.7872 - val_accuracy: 0.7815 - lr: 0.0010
Epoch 3/50
98/98 [==============================] - ETA: 0s - loss: 0.5997 - accuracy: 0.8254
Epoch 3: val_accuracy improved from 0.78153 to 0.80624, saving model to ./epic_models/DN_TL_230909.h5
98/98 [==============================] - 27s 280ms/step - loss: 0.5997 - accuracy: 0.8254 - val_loss: 0.5976 - val_accuracy: 0.8062 - lr: 0.0010
Epoch 4/50
98/98 [==============================] - ETA: 0s - loss: 0.4694 - accuracy: 0.8507
Epoch 4: val_accuracy improved from 0.80624 to 0.84655, saving model to ./epic_models/DN_TL_230909.h5
98/98 [==============================] - 27s 280ms/step - loss: 0.4694 - accuracy: 0.8507 - val_loss: 0.5066 - val_accuracy: 0.8466 - lr: 0.0010
Epoch 5/50
98/98 [==============================] - ETA: 0s - loss: 0.3835 - accuracy: 0.8777
Epoch 5: val_accuracy did not improve from 0.84655
98/98 [==============================] - 27s 275ms/step - loss: 0.3835 - accuracy: 0.8777 - val_loss: 0.4445 - val_accuracy: 0.8466 - lr: 0.0010
Epoch 6/50
98/98 [==============================] - ETA: 0s - loss: 0.3204 - accuracy: 0.9024
Epoch 6: val_accuracy improved from 0.84655 to 0.86086, saving model to ./epic_models/DN_TL_230909.h5
98/98 [==============================] - 29s 296ms/step - loss: 0.3204 - accuracy: 0.9024 - val_loss: 0.4280 - val_accuracy: 0.8609 - lr: 0.0010
Epoch 7/50
98/98 [==============================] - ETA: 0s - loss: 0.3155 - accuracy: 0.8925
Epoch 7: val_accuracy improved from 0.86086 to 0.86736, saving model to ./epic_models/DN_TL_230909.h5
98/98 [==============================] - 34s 348ms/step - loss: 0.3155 - accuracy: 0.8925 - val_loss: 0.3817 - val_accuracy: 0.8674 - lr: 0.0010
Epoch 8/50
98/98 [==============================] - ETA: 0s - loss: 0.2488 - accuracy: 0.9220
Epoch 8: val_accuracy improved from 0.86736 to 0.87906, saving model to ./epic_models/DN_TL_230909.h5
98/98 [==============================] - 36s 369ms/step - loss: 0.2488 - accuracy: 0.9220 - val_loss: 0.3691 - val_accuracy: 0.8791 - lr: 0.0010
Epoch 9/50
98/98 [==============================] - ETA: 0s - loss: 0.2311 - accuracy: 0.9220
Epoch 9: val_accuracy did not improve from 0.87906
98/98 [==============================] - 35s 361ms/step - loss: 0.2311 - accuracy: 0.9220 - val_loss: 0.3567 - val_accuracy: 0.8778 - lr: 0.0010
Epoch 10/50
98/98 [==============================] - ETA: 0s - loss: 0.2048 - accuracy: 0.9345
Epoch 10: val_accuracy did not improve from 0.87906
98/98 [==============================] - 37s 377ms/step - loss: 0.2048 - accuracy: 0.9345 - val_loss: 0.3584 - val_accuracy: 0.8726 - lr: 0.0010
Epoch 11/50
98/98 [==============================] - ETA: 0s - loss: 0.1992 - accuracy: 0.9262
Epoch 11: val_accuracy did not improve from 0.87906
98/98 [==============================] - 38s 392ms/step - loss: 0.1992 - accuracy: 0.9262 - val_loss: 0.3665 - val_accuracy: 0.8752 - lr: 0.0010
Epoch 12/50
98/98 [==============================] - ETA: 0s - loss: 0.1740 - accuracy: 0.9454
Epoch 12: val_accuracy improved from 0.87906 to 0.88687, saving model to ./epic_models/DN_TL_230909.h5
98/98 [==============================] - 39s 399ms/step - loss: 0.1740 - accuracy: 0.9454 - val_loss: 0.3238 - val_accuracy: 0.8869 - lr: 0.0010
Epoch 13/50
98/98 [==============================] - ETA: 0s - loss: 0.1618 - accuracy: 0.9470
Epoch 13: val_accuracy improved from 0.88687 to 0.88817, saving model to ./epic_models/DN_TL_230909.h5
98/98 [==============================] - 39s 398ms/step - loss: 0.1618 - accuracy: 0.9470 - val_loss: 0.3310 - val_accuracy: 0.8882 - lr: 0.0010
Epoch 14/50
98/98 [==============================] - ETA: 0s - loss: 0.1627 - accuracy: 0.9467
Epoch 14: val_accuracy did not improve from 0.88817
98/98 [==============================] - 38s 392ms/step - loss: 0.1627 - accuracy: 0.9467 - val_loss: 0.3146 - val_accuracy: 0.8856 - lr: 0.0010
Epoch 15/50
98/98 [==============================] - ETA: 0s - loss: 0.1385 - accuracy: 0.9557
Epoch 15: val_accuracy did not improve from 0.88817
98/98 [==============================] - 40s 407ms/step - loss: 0.1385 - accuracy: 0.9557 - val_loss: 0.3460 - val_accuracy: 0.8817 - lr: 0.0010
Epoch 16/50
98/98 [==============================] - ETA: 0s - loss: 0.1364 - accuracy: 0.9560
Epoch 16: val_accuracy did not improve from 0.88817
98/98 [==============================] - 40s 408ms/step - loss: 0.1364 - accuracy: 0.9560 - val_loss: 0.3473 - val_accuracy: 0.8752 - lr: 0.0010
Epoch 17/50
98/98 [==============================] - ETA: 0s - loss: 0.1397 - accuracy: 0.9538
Epoch 17: val_accuracy did not improve from 0.88817
98/98 [==============================] - 40s 411ms/step - loss: 0.1397 - accuracy: 0.9538 - val_loss: 0.3221 - val_accuracy: 0.8856 - lr: 0.0010
Epoch 18/50
98/98 [==============================] - ETA: 0s - loss: 0.1196 - accuracy: 0.9650
Epoch 18: val_accuracy improved from 0.88817 to 0.89337, saving model to ./epic_models/DN_TL_230909.h5
98/98 [==============================] - 42s 429ms/step - loss: 0.1196 - accuracy: 0.9650 - val_loss: 0.2946 - val_accuracy: 0.8934 - lr: 0.0010
Epoch 19/50
98/98 [==============================] - ETA: 0s - loss: 0.1047 - accuracy: 0.9666
Epoch 19: val_accuracy improved from 0.89337 to 0.89857, saving model to ./epic_models/DN_TL_230909.h5
98/98 [==============================] - 43s 438ms/step - loss: 0.1047 - accuracy: 0.9666 - val_loss: 0.3140 - val_accuracy: 0.8986 - lr: 0.0010
Epoch 20/50
98/98 [==============================] - ETA: 0s - loss: 0.0964 - accuracy: 0.9717
Epoch 20: val_accuracy improved from 0.89857 to 0.91027, saving model to ./epic_models/DN_TL_230909.h5
98/98 [==============================] - 44s 448ms/step - loss: 0.0964 - accuracy: 0.9717 - val_loss: 0.2822 - val_accuracy: 0.9103 - lr: 0.0010
Epoch 21/50
98/98 [==============================] - ETA: 0s - loss: 0.0746 - accuracy: 0.9839
Epoch 21: val_accuracy did not improve from 0.91027
98/98 [==============================] - 45s 456ms/step - loss: 0.0746 - accuracy: 0.9839 - val_loss: 0.2864 - val_accuracy: 0.9012 - lr: 1.0000e-04
Epoch 22/50
98/98 [==============================] - ETA: 0s - loss: 0.0717 - accuracy: 0.9878
Epoch 22: val_accuracy did not improve from 0.91027
98/98 [==============================] - 46s 468ms/step - loss: 0.0717 - accuracy: 0.9878 - val_loss: 0.2854 - val_accuracy: 0.9038 - lr: 1.0000e-05
Epoch 23/50
98/98 [==============================] - ETA: 0s - loss: 0.0715 - accuracy: 0.9881
Epoch 23: val_accuracy did not improve from 0.91027
98/98 [==============================] - 47s 478ms/step - loss: 0.0715 - accuracy: 0.9881 - val_loss: 0.2853 - val_accuracy: 0.9038 - lr: 1.0000e-06
Epoch 24/50
98/98 [==============================] - ETA: 0s - loss: 0.0714 - accuracy: 0.9878
Epoch 24: val_accuracy did not improve from 0.91027
98/98 [==============================] - 50s 512ms/step - loss: 0.0714 - accuracy: 0.9878 - val_loss: 0.2853 - val_accuracy: 0.9038 - lr: 1.0000e-07
Epoch 25/50
98/98 [==============================] - ETA: 0s - loss: 0.0714 - accuracy: 0.9878
Epoch 25: val_accuracy did not improve from 0.91027
98/98 [==============================] - 48s 489ms/step - loss: 0.0714 - accuracy: 0.9878 - val_loss: 0.2853 - val_accuracy: 0.9038 - lr: 1.0000e-08

모델 학습 결과는 제 예상보다 매우 만족스러웠습니다. (F1-score 0.91 달성)

Conclusions

기존에 잘 학습된 detection 모델과 Classification 모델을 활용하여 아주 정확도 높은 이미지 분류 모델을 만들어 낼 수 있었습니다.

추가로 Augmentation과 Fine-tuning을 사용하여 정확도를 더 높혀 볼 예정입니다.

참고