반응형

이 글은 www.analyticsvidhya.com의 [블로그]의 내용임.

Build your Own Object Detection Model using TensorFlow API

객체 탐지의 세계

    사람이 이미지를 볼 때, 몇초안에 관심의 대상을 인식하지만 기계의 경우는 아니다. 따라서 객체 탐지는 이미지내 객체 인스턴스를 찾는 컴퓨터 비전 문제이다.

    객체 탐지를 개발하기 위한 현재의 기술은 상당한 성능 개선과 실제 사용을 위한 개발에 도움이 되는 end-to-end 파이프라인에 집중된다.

    이 글에서는 텐서플로 API를 사용하여 객체 탐지 모델을 구축하는 방법을 살펴본다. 기본 사항에 대해서는 아래 링크를 참고하자.


Table of Contents

  1. 객체 탐지를 위한 일반적인 프레임워크
  2. API가 무엇이고 왜 필요한가?
  3. 텐서플로 객체 탐지 API

객체 탐지를 위한 일반적인 프레임워크

    일반적으로 객체 탐지 프레임워크 구축시 다음 세단계를 거친다.

  1. 첫째, 딥러닝 모델 또는 알고리즘은 전체 이미지에 걸쳐있는 대규모 바운딩박스 셋을 생성하기 위해 사용된다.


  1. 다음으로, 시각적 특징은 각 바운딩 박스에서 추출된다. 이 특징들이 평가되고 시각적 특징에 기초하여 박스에 어떤 객체가 있는지 여부를 결정한다.(즉, 객체분류 컴포넌트)


  1. 마지막 후처리 단계에서, 겹처진 박스를 하나의 바운딩 박스로 묶는다.(즉, NMS - Non-Maximum Supression)


    이제 첫번째 객체 탐지 프레임워크가 준비되었다.


API가 무엇이고 왜 필요한가?

    API는 Application Programming Interface의 약자로 설계에서부터 코드를 작성하지 않도록 일반적인 연산(operation)의 셋을 개발자에게 제공한다.

API를 각 음식에 대해 설명이 있는 음식 목록을 제공하는 메뉴판으로 생각해도 된다. 원하는 음식을 선택했들때, 음식점은 그 음식을 만들고 제공한다. 손님은 정확히 어떻게 음식점이 그 음식을 준비하는지 모르고 손님도 알 필요가 없다.

    어떤 의미에서 API는 시간을 굉장히 절약하게 한다. 또한 사용자에게 많은 경우 편리함을 제공한다. Facebook 사용자는 그들의 Facebook ID를 사용하는 많은 앱과 사이트에 로그인하기 위한 능력의 진가를 안다. 이것은 물론 Facebook API를 이용한다


텐서플로 객체 탐지 API

    텐스플로 객체탐지 API는 객체 탐지 문제를 해결하는 딥러닝 네트워크를 생성하기 위한 프레임워크이다.

    Model Zoo라고하는 프레임워크에는 사전 훈련된 모델이 이미 존재한다. 이 프레임워크는 COCO 데이터셋 그리고 Open Image 데이터셋에서 사전 훈련된 모델 모음도 포함한다. 이 모델들은 단지 이 데이터넷에서의 분류에 관심이 있다면, 추론을 위해 사용할 수 있다.

    이 프레임워크는 새로운 데이터셋에서 훈련할 때 모델을 초기화하는데 유용하다. 사전훈련된 모델에서 사용된 다양한 구조는 아래와 같다.




MobileNet-SSD

    SSD (Single Shot MultiBox Detector) 구조는 한번에 바운딩 박스 위치를 예측하고 그 위치내 객체를 분류하기 위해 학습하는 단일 합성곱 네트워크이다. 따라서, SSD는 단대단(end-to-end)으로 학습할 수 있다. SSD 네트워크는 기본 구조(이 경우 MobileNet)과 이후 몇가지 합성곱 레이어로 아래와 같이 구성된다.



    SSD는 바운딩 박스의 위치를 탐지하기 위해 특성 맵 위에서 동작한다. 각 특성맵 위치에서 k개의 바운딩 박스가 예측된다. 각 바운딩 박스는 아래 정보를 가진다.

  • 바운딩 박스 오프셋 위치인 4개 코너 좌표(cx, cy, w, h)
  • C 분류 가능성 (c1, c2, ..., cp)

    SSD는 박스의 모양을 예측하지 못한다. 오히려 박스가 어디있는지를 예측한다. k 바운딩 박스 각각은 미리 경정되어있는 모양을 갖는다. 그 모양은 실제 훈련 전에 설정된다. 예를 들어, 위 그림에서는 4개의 박스가 있다. 이는 k=4를 의미한다.

Loss in MobileNet-SSD

    최종으로 일치된 박스 셋으로 아래와 같이 손실을 계산할 수 있다.


L = 1/N (L class + L box)
  • N : 일치된 박스의 총 수
  • L class : 분류를 위한 softmax loss
  • L box : 일치된 박스의 오류(error)를 나타내는 L1 smooth loss

    L1 smooth loss는 특이값에 더 강한 L1 loss의 수정본이다.

N = 0인 경우, loss = 0으로 설정된다.

MobileNet

    MobileNet은 인수분해된 합성곱의 형태인 깊이 분할 합성곱(depthwise separable conv)에 기초한다. 표준 합성곱(standard conv)를 깊이 합성곱(depthwise conv)와 점 합성곱(pointwise conv)로 불리는1 X 1 합성곱으로 인수분해한다.

    MobileNet에서 깊이 합성곱은 각각의 입력 채널에 대해 단일 필터를 적용한다. 그리고 점 합성곱이 깊이 합성곱의 출력을 합치기 위해 1 X 1 합성곱을 적용한다.

    표준 합성곱은 한 단계로 입력을 출력의 새로운 셋으로 필터링하고 묶는다. 깊이 분할 합성곱은 이를 두 레이어로 나눈다. - 필터링을 위한 분할 레이어와 묶음을 위한 분할 레이어. 이러한 인수분해(factorization은 계산과 모델 크기를 크게 감소시키는 효과가 있다.)




How to load the model?

    아래부터는 객체 탐지를 쉽게 시각화 하기 위한 단계별 순서이다.

Install the Model

Windows 10 , Anaconda 환경 기준으로 작성

  • Window에서 pycocotools 설치

    1. Visual C++ 2015 Build Tools 다운로드 및 기본값(default)로 설치
    2. C:\Program Files (x86)\Microsoft Visual C++ Build Tools 폴더로 이동하여 vcbuildtools_msbuild.bat 파일 실행
    3. Anaconda에서 conda install Cython
    4. Anaconda에서 pip install git+https://github.com/philferriere/cocoapi.git#egg=pycocotools^&subdirectory=PythonAPI 실행. (powershell에서는 오류 발생함.)
  • object_detection package 설치

    1. 패키지를 다운로드할 디렉토리로 이동 : <아나콘다 설치위치>/pkgs
    2. 다운로드

      git clone --depth 1 https://github.com/tensorflow/models
    3. 설치 : <아나콘다 설치위치>\pkgs\models\research 로 이동

      python setup.py install
    4. 확인 : python 실행 후 아래 모듈 import시 오류가 없어야 함.

      import object_detection
  • 필요 라이브러리 import

    
      import numpy as np
      import os
      import pathlib
      import six.moves.urllib as urllib
      import sys
      import tarfile
      import tensorflow as tf
      import zipfile
    
      from collections import defaultdict
      from io import StringIO
      from matplotlib import pyplot as plt
      from PIL import Image
      from IPython.display import display
    
      # Object_detection module
      from object_detection.utils import ops as utils_ops
      from object_detection.utils import label_map_util
      from object_detection.utils import visualization_utils as vis_util
    

from object_detection.utils import ops as utils_ops 실행시

ModuleNotFoundError: No module named 'tf_slim' 오류가 발생하면

pip install tf_slim 실행

  • 모델 준비

    1. Loader

      
      def load_model(model_name):
       base_url = 'http://download.tensorflow.org/models/object_detection/'
       model_file = model_name + '.tar.gz'
       model_dir = tf.keras.utils.get_file(fname=model_name, origin=base_url + model_file, untar=True)
       model_dir = pathlib.Path(model_dir)/"saved_model"
       model = tf.saved_model.load(str(model_dir))
       model = model.signatures['serving_default']
       return model
      
    2. Loading label map

      Label map은 합성곱 네트워크가 5를 예측했들 때, 이것이 비행기라는 것을 알 수 있도록 인덱스를 분류명과 매핑한다.

      
      # List of the strings that is used to add correct label for each box.
      PATH_TO_LABELS = '<아나콘다 설치경로>/pkgs/models/research/object_detection/data/mscoco_label_map.pbtxt'
      category_index = label_map_util.create_category_index_from_labelmap(PATH_TO_LABELS, use_display_name=True)
      

      간단하게 하기 위해, 여기서는 2개의 이미지만 테스트 한다.

      
      # If you want to test the code with your images, just add path to the images to the TEST_IMAGE_PATHS.
      PATH_TO_TEST_IMAGES_DIR = pathlib.Path('<아나콘다 설치경로>/pkgs/models/research/object_detection/test_images')
      TEST_IMAGE_PATHS = sorted(list(PATH_TO_TEST_IMAGES_DIR.glob("*.jpg")))
      TEST_IMAGE_PATHS
      
    3. TensorFlow API를 사용한 객체 탐지 모델

      • 객체 탐지 모델 로드

        
        model_name = 'ssd_mobilenet_v1_coco_2017_11_17'
        detection_model = load_model(model_name)
        # 모델의 입력 확인 (int8 타입의 3색 이미지의 배치)
        print(detection_model.inputs)
        detection_model.output_dtypes
        
      • 모델을 호출하고 출력을 정리하기 위한 wrapper 함수 추가

        
        def run_inference_for_single_image(model, image):
          image = np.asarray(image)
          # The input needs to be a tensor, convert it using `tf.convert_to_tensor`.
          input_tensor = tf.convert_to_tensor(image)
          # The model expects a batch of images, so add an axis with `tf.newaxis`.
          input_tensor = input_tensor[tf.newaxis,...]
          # Run inference
          output_dict = model(input_tensor)
        
          # All outputs are batches tensors.
          # Convert to numpy arrays, and take index [0] to remove the batch dimension.
          # We're only interested in the first num_detections.
          num_detections = int(output_dict.pop('num_detections'))
          output_dict = {key:value[0, :num_detections].numpy() 
                          for key,value in output_dict.items()}
          output_dict['num_detections'] = num_detections
          # detection_classes should be ints.
          output_dict['detection_classes'] = output_dict['detection_classes'].astype(np.int64)
        
          # Handle models with masks:
          if 'detection_masks' in output_dict:
              # Reframe the the bbox mask to the image size.
              detection_masks_reframed = utils_ops.reframe_box_masks_to_image_masks(
                      output_dict['detection_masks'], output_dict['detection_boxes'],
                      image.shape[0], image.shape[1])      
              detection_masks_reframed = tf.cast(detection_masks_reframed > 0.5,
                                              tf.uint8)
              output_dict['detection_masks_reframed'] = detection_masks_reframed.numpy()
        
          return output_dict
        
      • 각각의 이미지에서 실행하고 결과보기

                
        def show_inference(model, image_path):
          # the array based representation of the image will be used later in order to prepare the
          # result image with boxes and labels on it.
          image_np = np.array(Image.open(image_path))
          # Actual detection.
          output_dict = run_inference_for_single_image(model, image_np)
          # Visualization of the results of a detection.
          vis_util.visualize_boxes_and_labels_on_image_array(
              image_np,
              output_dict['detection_boxes'],
              output_dict['detection_classes'],
              output_dict['detection_scores'],
              category_index,
              instance_masks=output_dict.get('detection_masks_reframed', None),
              use_normalized_coordinates=True,
              line_thickness=8)
        
          display(Image.fromarray(image_np))
        
        for image_path in TEST_IMAGE_PATHS:
          show_inference(detection_model, image_path)
        


Inception-SSD

    Inception-SSD 모델의 구조는 위의 MobileNet-SSD와 비슷하다. 차이는 기본 모델이 인셉션 모델이라는 것이다. 인셉션 네트워크에 대해서는 [다음]을 참고하자.

How to load the model?

    단지 API의 탐지 부분에서 모델의 이름만 변경하면 된다.


model_name = 'ssd_inception_v1_coco_2017_11_17'
detection_model = load_model(model_name)

    그리고 위에서 구현했던 단계로 예측하면 된다.


Faster RCNN

    최신 객체 탐지 네트워크는 객체 위치를 가설화하기 위한 지역 제안 알고리즘(region proposal algorithms)에 기초한다. SPPnet과 Fast R-CNN같은 발전은 이들 탐지 네트워크긔 실행 시간을 줄어들어 지역 제안 계산이 병목현상으로 나타났다.

    Faster RCNN에서 합성곱 특성맵을 생성하기 위해 합성곱 신경망에 입력 이미지를 전달한다. 합성곱 특성맵으로부터, 제안된 구역을 구분하고 사각형으로 감싼다. 그리고 RoI(Region Of Interest layer) 풀링 레이어를 사용하여 fully connected layer로 전달될 수 있도록 고정 크기로 변환한다.

    RoI 특성 벡터로부터 제안된 지역의 분류와 바운딩 박스에 대한 옵셋(offset)값을 예측하기 위해 softmax를 사용한다.


    Faster R-CNN에 대한 좀 더 자세한 내용은 [다음]을 참고하자.

How to load the model?

    단지 API의 탐지 부분에서 모델의 이름만 변경하면 된다.


model_name = 'faster_rcnn_resnet101_coco'
detection_model = load_model(model_name)

    그리고 위에서 구현했던 단계로 예측하면 된다.


    보이는 바와 같이 MobileNet-SSD 모델보다 훨씬 잘 동작한다. 하지만 이에 따른 트래이드오프(tradeoff)가 있다. - MobileNet-SSD보다 훨씬 느리다. 이러한 것이 딥러닝과 컴퓨터 비젼 프로젝트를 위한 올바른 객체 탐지 모델을 선택할 때 결정해야하는 것이다..


Which Object Detection Model Should you Choose?

    특정 요구에 따라, 텐서플로 API로 올바른 모델을 선택할 수 있다. 만약 고해상도(high fps)에서 제공되는 비디오를 탐지하기 위해 고속으로 동작하는 모델이 필요하다면, single-shot detection(SSD) 네트워크가 가장 잘 동작한다. 이름에서처럼, SSD 네트워크는 한번에 모든 바운딩 박스를 결정한다. 따라서 어마어마하게 빠른 모델이다.

    그러나, SSD는 정확도의 비용으로 속도를 얻게된다. Faster R-CNN은 높은 정확도지만 속도가 늦다. 따라서 탐험하고 그 과정에서, 텐서플로 API가 할수 있는 강력함을 알게 될 것이다.

반응형

+ Recent posts