Pascal voc 데이터 셋을 이용하여 AutoMl EfficientDet 학습하기

2022. 1. 30. 18:46computervision/섹션 8. EfficientDet

저번 시간에는 단순히 AutoML EfficientDet 패키지를 inference 해보았다.

이번에는 Pascal voc dataset을 이용하여 AutoML efficientdet 패키지를 학습해보고 inference 까지 수행해보려 한다.

 

!git clone --depth 1 https://github.com/google/automl
!cd /content/automl/efficientdet; pip install -r requirements.txt
https://github.com/google/automl 에서 automl 패키지를 다운받아 사용했다.
 
import os
import sys
import tensorflow.compat.v1 as tf

sys.path.append('/content/automl/efficientdet')

import hparams_config
from tf2 import anchors
from model_inspect import ModelInspector

이후 필요한 라이브러리를 import한 후 sys의 경로에 efficientdet 폴더가 있는 위치를 추가 시켜준다. 이 작업을 해야 efficientdet폴더의 위치에서 필요한 모듈을 import 해 올 수 있다.

 

!wget http://pjreddie.com/media/files/VOCtrainval_06-Nov-2007.tar
!tar -xvf VOCtrainval_06-Nov-2007.tar > /dev/null 2>&1

이후 pascal voc 2007 dataset을 version을 다운받는다.

!mkdir -p /content/tfrecord/train
!mkdir -p /content/tfrecord/val

!cd /content/automl/efficientdet; PYTHONPATH="/content/automl/efficientdet:$PYTHONPATH" python dataset/create_pascal_tfrecord.py  \
    --data_dir=/content/VOCdevkit --year=VOC2007 --set=train --output_path=/content/tfrecord/train/pascal

!cd /content/automl/efficientdet; PYTHONPATH="/content/automl/efficientdet:$PYTHONPATH" python dataset/create_pascal_tfrecord.py  \
    --data_dir=/content/VOCdevkit --year=VOC2007 --set=val --output_path=/content/tfrecord/val/pascal

이후 다운받은 train, validation 데이터 셋을 tfrecord 형태로 변환을 거쳐야 한다.

tfrecord로 변환하는 패키지가 처음에 다운받은 efficientdet 폴더에 있기 때문에 별도의 작업 필요없이 create_pascal_tfrecord.py 모듈을 사용하면된다.

 

각각 train과 val 폴더를 생성후 create_pascal_tfrecord.py모듈을 사용하여 pascal_voc 형태의 데이터를 tfrecord형태의 데이터셋으로 변환 후 각각의 저장위치에 다운로드 받는다.

 

import os, sys 
from google.colab import drive 

drive.mount('/content/gdrive')

!ln -s /content/gdrive/My\ Drive/ /mydrive
!ls /mydrive
!mkdir -p /mydrive/model_trained

모델이 학습이 될때의 checkpoint 파일을 다운받을 경로를 구글드라이브로 설정했다.

 

이후 학습을 진행하기 위해서 config파일을 설정해주어야 한다.

config = hparams_config.get_detection_config('efficientdet-d0')

모델은 efficientdet-d0로 사용하였다.

 

class TRAIN_CFG:
  model_name = 'efficientdet-d0' 
  strategy = ''
  model_dir = '/mydrive/model_trained' 
  pretrained_ckpt = '/content/efficientdet-d0' 
  hparams = 'num_classes=20,moving_average_decay=0,mixed_precision=true'
  use_xla = False
  use_fake_data = False
  batch_size = 8
  eval_samples = 5000 
  steps_per_execution = 1 
  num_examples_per_epoch = 2500 
  num_epochs = 15 
  train_file_pattern = '/content/tfrecord/train/pascal-*.tfrecord'
  val_file_pattern = '/content/tfrecord/val/pascal-*.tfrecord'
  val_json_file = None
  mode = 'traineval' 
  
  num_cores = 2   
  tpu = None
  gcp_project = None
  tpu_zone = None
  eval_master = ''
  eval_name = None
  tf_random_seed = 2021
  profile = False
  debug = False

먼저 config파일에서 수정해야될 parmeter들을 class형식으로 지정해준다.

기존의 config파일에서 수정해야될 사항들이 적혀있다.

efficientdet의 모델명, 학습된 모델이 저장될 위치, evaluation image 데이터의 수, epoch 횟수..

 

from tf2.train import setup_model
import hparams_config

import utils
from tf2 import tfmot
from tf2 import train_lib
from tf2 import util_keras

config = hparams_config.get_detection_config(TRAIN_CFG.model_name)
config.override(TRAIN_CFG.hparams)

steps_per_epoch = TRAIN_CFG.num_examples_per_epoch // TRAIN_CFG.batch_size

if tf.config.list_physical_devices('GPU'):
  ds_strategy = tf.distribute.OneDeviceStrategy('device:GPU:0')
else:
  ds_strategy = tf.distribute.OneDeviceStrategy('device:CPU:0')

print(ds_strategy)

params = dict(
      profile=TRAIN_CFG.profile,
      mode = TRAIN_CFG.mode,
      model_name=TRAIN_CFG.model_name,
      steps_per_execution=TRAIN_CFG.steps_per_execution,
      num_epochs = TRAIN_CFG.num_epochs,
      model_dir=TRAIN_CFG.model_dir,
      steps_per_epoch=steps_per_epoch,
      strategy=TRAIN_CFG.strategy,
      batch_size=TRAIN_CFG.batch_size,
      tf_random_seed=TRAIN_CFG.tf_random_seed,
      debug=TRAIN_CFG.debug,
      val_json_file=TRAIN_CFG.val_json_file,
      eval_samples=TRAIN_CFG.eval_samples,
      num_shards=ds_strategy.num_replicas_in_sync
      )

config.override(params, True)

config.image_size = utils.parse_image_size(config.image_size)

필요한 라이브러리를 호출 후 위에서 class형태로 지정한 config설정들을 기존의 config파일에 override 시킨다.

이때 입력 image size를 tuple형태로 지정해 주어야 한다. tuple형태로 맞춰주기 위해 utils에 있는 parse_image_size함수를 이용하면 된다.

여기까지 진행하면 모델의 학습에 필요한 config설정이 완료되었다.

이제 모델을 생성후 train,val용 dataset을 생성하고 train을 진행하면 된다.

 

import utils
from tf2 import tfmot
from tf2 import train_lib
from tf2 import util_keras
MODEL = 'efficientdet-d0' 

def download(m):
  if m not in os.listdir():
    !wget https://storage.googleapis.com/cloud-tpu-checkpoints/efficientdet/coco/{m}.tar.gz
    !tar zxf {m}.tar.gz
  ckpt_path = os.path.join(os.getcwd(), m)
  return ckpt_path
  
  ckpt_path = download(MODEL)

이후 학습에 사용할 coco dataset으로 pretrained된 efficientdet d0 checkpoint파일을 다운로드 받는다.

from tf2 import train_lib
from tf2 import train

model = train_lib.EfficientDetNetTrain(config=config)
model = train.setup_model(model, config)

if TRAIN_CFG.pretrained_ckpt:
  ckpt_path = tf.train.latest_checkpoint(TRAIN_CFG.pretrained_ckpt)
  util_keras.restore_ckpt(
      model,
      ckpt_path,
      config.moving_average_decay,
      exclude_layers=['class_net'])
  
train.init_experimental(config)

model.summary()

위에서 다운받은 pretrained된 model은 coco dataset으로 학습되어져 있다. coco dataset과 지금 학습하고자 하는 pascal voc dataset의 최종 output layer의 개수가 다르므로 다운받은 pretrained 된 모델의 classification layer를 제외 시켜야한다.

 

이제 train, val용 dataset을 생성해야한다.

import dataloader

def get_dataset(is_training, config):
  file_pattern = (
    TRAIN_CFG.train_file_pattern
    if is_training else TRAIN_CFG.val_file_pattern)
  if not file_pattern:
    raise ValueError('No matching files.')

  return dataloader.InputReader(
    file_pattern,
    is_training=is_training,
    use_fake_data=TRAIN_CFG.use_fake_data,
    max_instances_per_image=config.max_instances_per_image,
    debug=TRAIN_CFG.debug)(
        config.as_dict())

이제 모델의 모델의 학습에 필요한 모든 과정이 끝났다.

from tf2 import train
import numpy as np

tr_steps_per_epoch = train_images_num//config.batch_size
val_steps_per_epoch = val_images_num//config.batch_size
print('tr_steps_per_epoch:', tr_steps_per_epoch, 'val_steps_per_epoch:', val_steps_per_epoch)

val_dataset = get_dataset(False, config) if 'eval' in config.mode else None

model.fit(
    get_dataset(True, config),
    epochs=15,
    steps_per_epoch=tr_steps_per_epoch ,
    callbacks=train_lib.get_callbacks(config.as_dict(), val_dataset),
    validation_data=val_dataset,
    validation_steps=val_steps_per_epoch)

tf.keras.backend.clear_session()

위의 과정에서 설정한 steps_per_epoch, num_epochs를 무시하고 다시 생성한다.

위에서 steps_per_epoch, num_epochs를 설정하지 않으면 정상적으로 코드가 동작하지 않았기 때문에 위에서 설정했지만 이 코드에서 다시 생성한 steps_per_epoch, num_epochs을 이용하여 모델의 학습이 이루어지게 된다.

 

steps_per_epoch는 (전체 학습데이터의 수/batch_size)로 표현할 수 있다.

모델이 epochs 15 학습을 맞춘후 Iou 0.5:0.95는 0.465 Iou 0.5는 0.715의 성능을 보여주었다.

하지만 여전히 small object에 대해서는 0.106이라는 낮은 성능을 보여준다. 이는 입력되는 input_img_size의 크기를 좀 더 키우거나, epoch수를 더 늘리거나, efficientdet 모델의 버전을 높은 버전을 사용되면 성능향상을 이끌어 낼 수 있지 않을까 생각해본다.

 

이후 inference 시각화 과정은 저번시간에 실습했던 내용이랑 같은 내용이다.

수행시간은 tensorflow v1의 static graph모드(non eager mode)로 실행했을 때 30ms가 나왔다.

앞서 수행했던 efficientdet v0 모델을 학습 하지 않고 바로 inference을 했을 때와 비슷한 결과가 나왔다.