www.analyticsvidhya.com의 [글] 내용임
A Practical Implementation of the Faster R-CNN Algorithm for Object Detection (Part 2 – with Python codes)
Introduction
어떤 알고리즘을 객체 탐지 작업에 사용할까? (저자는) 적은 시간내에 가장 정확한 모델을 구축하기 위해 꽤 많은 시도를 했다. 여러 해커톤과 실제 데이터셋에 걸쳐 보통은 항상 RCNN 부류의 알고리즘으로 이어졌다.
(저자에게는) 이는 굉장히 유용한 프레임워크이다. 이 연재글의 목적은 다른 타입의 RCNN 알고리즘이 얼마나 유용한지를 보여주는 것이다.
이 글에서는 우선 part 1에서 보운 것을 간단히 요약하고 RCNN 부류의 더 빠른 알고리즘인 Faster RCNN을 구현할 것이다. [Part 1 링크]
Table of Contents
- 객체 탐지를 위한 다른 RCNN 부류에 대한 개요 (A Brief Overview of the Different R-CNN Algorithms for Object Detection)
- 문제 상태 이해하기 (Understanding the Problem Statement)
- 시스템 셋팅 (Setting up the System)
- Faster RCNN 구현 (Implementing Faster RCNN)
객체 탐지를 위한 다른 RCNN 분류에 대한 개요
RCNN 부류(RCNN, Fast RCNN, Faster RCNN)내 다른 알고리즘을 빠르게 훑어보자. 이는 이전에 보지 못한 이미지(새로운 데이터)에서 바운딩 박스를 예측할 때 이후 구현 부분에 토대가 된다.
RCNN은 선택적 검색을 사용하여 주어진 이미지에서 구역의 묶음(bunch)를 추출하고 이들 박스(구역)이 객체를 포함하는지를 점검한다. 첫번째로 구역을 추출하고 각 구역에 대해 CNN이 특정 특징을 추출하기 위해 사용된다. 마지막으로 이들 특징은 객체를 탐지하기 위해 사용된다. 불행히도 RCNN은 이 프로세스에 관계된 3개의 단계 때문에 다소 느리다.
Fast RCNN은 반면, (이미지로부터 추출된 구역을 전달하는 대신) 전체 이미지를 RoI(Region of Interest)를 생성하는 ConvNet에 전달한다. 또한 3개의 다른 모델(RCNN에서 본)을 사용하는 대신, 구역으로부터 특징 추출, 특징을 분류하고 바운딩 박스 반환하는 하나의 모델을 사용한다.
이 모든 단계는 동시에 처리된다. 따라서 RCNN에 비해 더 빠르다. Fast RCNN은 그러나 여전히 구역을 추출하기 위해 선택적 검색을 사용하여 대규모 데이터 셋에 적용할 때 충분히 빠르지 않다.
Faster RCNN은 선택적 검색을 RPN(Region Proposal Network)로 교체하여 이 문제를 해결했다. 우선 ConvNet을 사용하여 입력이미지에서 특성 맵을 추출하고 이를 객체 제안(object proposal)을 반환하는 RPN에 전달한다. 최종적으로 이들 맵은 분류되고 바운딩 박스가 예측된다.
아래는 이미지에서 객체를 탐지하기 위해 Faster RCNN이 따르는 절차를 요약한 것이다.
- 입력이미지를 가져와 이미지에 대한 특성맵을 반환하는 ConvNet에 전달한다.
- 이들 특성맵에 RNP을 적용하고 객제 제안을 얻는다.
- 모든 제안을 동일한 크기로 줄이기 위해 RoI 풀링 레이어를 적용한다.
- 최종적으로 이들 제안을 이미지에 대한 바운딩 박스 예측을 분류하기 위해 FCN(fully connected network)로 전달한다.
아래는 이들 알고리즘을 비교한 것이다.
CNN | 이미지를 여러개의 구역으로 나누고 각 구역을 다양한 분류로 분류한다. | - | 정확하게 예측하기 위해서는 많은 구역이 필요하고 따라서 높은 계산 시간이 필요하다. |
RCNN | 구역을 생성하기 위해 선택적 검색(selective search)를 사용한다. 각 이미지에서 약 2000개의 구역을 추출한다. | 40 ~ 50초 | 각 구역이 따로 CNN으로 전달되어 높은 계산 시간이 필요하다. 또한 예측을 위해 다른 세가지 모델을 사용한다. |
Fast RCNN | 각 이미지는 단지 한번 CNN에 전달되고 특성맺이 추출된다. 선택적 검색은 예측을 위해 이들 맵에서 사용된다. RCNN에서 사용된 세가지 모델을 모두 을 하나로 묶는다. | 2초 | 선택적 검색이 느리기 때문에 계산 시간이 여전히 높다. |
Faster RCNN | 선택적 검색 방법을 알고리즘을 훨씬 더 빠른게 하는 RPN으로 대체한다. | 0.2초 | 객체 제안(object proposal)이 시간이 걸리고 하나씩 차례로 다른 시스템이 동작하는 것이 있기 때문에 시스템의 성능은 이전 시스템이 어떻게 수행되느냐에 달려있다. |
문제 상태 이해하기
이 글에서는 건강관련 데이터셋으로 혈액 세포(Blood Cell) 검출 문제를 해결하는 것을 목표로 한다. 작업은 현미경 이미지를 읽어 각 이미지에서 적혈구(Red Blood Cells - RBCs), 백혈구(White Blood Cells - WBCs) 그리고 혈소판(platelet)을 탐지하는 것이다. 아래 그림은 최종 결과에 대한 예제이다.
이 데이터셋을 선택한 이유는 우리 혈액에서 적혈구, 백혈구, 혈소판의 밀도가 면역 시스템(immune system)과 헤모글로빈(hemoglobin)에 대한 많은 정보를 제공하기 때문이다. 이는 사람이 건강한지 아닌지 잠정적으로 확인할 수 있고 혈액에서 어떤 차이가 발견된다면, 진단을 위한 빠른 조치를 할 수 있게 한다.
현미경으로 샘픙을 찾는 것은 지루한 절차이다. 여기서 딥러닝이 중요한 역할을 한다. 딥러닝은 인상적인 정확도로 현미경 이미지에서 혈액 세포를 분류하고 탐지한다.
이 글에서 사용된 전체 혈액 세포 탐지 데이터셋은 [여기]에서 다운로드 가능하다. 이 글에서는 목적에 맞도록 데이터를 아래와 같이 약간 수정하였다.
- 바운딩 박스는 원래의 .xml 포멧에서 .csv 포멧으로 변환하였다.
- 또한 전체 데이터셋에서 임의로 이미지를 선택하여 훈련과 테스트셋을 분리하여 생성하였다.
Setting up the System
모델을 구현하기 전에 아래와 같이 필요한 프레임워크와 라이브러리가 올바르게 설치되어 있는지 확인한다.
- pandas
- matplotlib
- tensorflow
- keras - 2.0.3
- numpy
- opencv-python
- sklearn
- h5py
만약 아나콘다(Anaconda) 또는 쥬피터 노트북(jupyter notebook)이 설치되어 있다면 위의 목록 대부분은 이미 설치되어 있을 것이다. requirement.txt 파일을 다운로드 받아 아래와 같이 설치할 수도 있다.
pip install -r requirement.txt
데이터 탐험
가지고 있는 데이터를 우선 탐색하는 것은 좋은 생각이다. 이는 숨겨진 패턴을 찾을 뿐 아니라, 작업하고 있는 것에 대한 가칬는 전반적인 통찰력을 얻을 수 있도록 한다. 아래는 전체 데이터셋이외에 생성한 3개의 파일이다.
- train_images
: 모델을 훈련하기 위해 사용할 이미지. 이 디렉토리에 각 분류(class) 및 실제 바운딩 박스를 포함한다. - test_images
: 훈련된 모델로 예측값을 만들기 위해 사용할 이미지. 이 데이터셋은 분류와 바운딩 박스를 포함하지 않는다. - train.csv
: 각 이미지에 대한 이름, 분류(class) 그리고 바운딩 박스 좌표를 포함. 이 데이터는 하나의 이미지가 하나 이상의 객체를 포함할 수 있기 때문에 하나의 이미지에 대해 여러줄로 나타날 수 있다.
.csv파일을 읽어 처음 몇줄을 출력해 보면 아래와 같다.
# importing required libraries
import pandas as pd
import matplotlib.pyplot as plt
%matplotlib inline
from matplotlib import patches
# read the csv file using read_csv function of pandas
train = pd.read_csv(‘train.csv’)
train.head()
훈련 파일에는 아래와 같은 6개의 컬럼이 있다.
- image_names : 이미지의 이름을 포함한다.
- cell_type : 세포의 타입을 나타낸다.
- xmin : 이미지 왼쪽 아래 부분의 x 좌표
- xmax : 이미지 오른쪽 위 부분의 x 좌표
- ymin : 이미지 왼쪽 아래 부분의 y 좌표
- ymax : 이미지 오른쪽 위 부분의 y 좌표
작업할 이미지를 출력해 보면 아래와 같다.
# reading single image using imread function of matplotlib
image = plt.imread('images/1.jpg')
plt.imshow(image)
위 이미지가 혈액 세포의 이미지이다. 여기서 파란 부분은 WBCs, 약간 붉은 부분은 RBCs를 나타낸다. 훈련 셋에 얼마나 많은 이미지와 종류가 있는지 보자.(254장의 훈련 이미지가 있다.)
# Number of unique training images
train['image_names'].nunique()
# Number of classes
train['cell_type'].value_counts()
데이터셋은 RBC, WBD, paatelet의 3가지 종류의 세포를 갖는다. 마지막으로 객체 탐지를 거친 이미지가 어떻게 보일지 알아보자.
fig = plt.figure()
#add axes to the image
ax = fig.add_axes([0,0,1,1])
# read and plot the image
image = plt.imread('images/1.jpg')
plt.imshow(image)
# iterating over the image for different objects
for _,row in train[train.image_names == "1.jpg"].iterrows():
xmin = row.xmin
xmax = row.xmax
ymin = row.ymin
ymax = row.ymax
width = xmax - xmin
height = ymax - ymin
# assign different color to different classes of objects
if row.cell_type == 'RBC':
edgecolor = 'r'
ax.annotate('RBC', xy=(xmax-40,ymin+20))
elif row.cell_type == 'WBC':
edgecolor = 'b'
ax.annotate('WBC', xy=(xmax-40,ymin+20))
elif row.cell_type == 'Platelets':
edgecolor = 'g'
ax.annotate('Platelets', xy=(xmax-40,ymin+20))
# add bounding boxes to the image
rect = patches.Rectangle((xmin,ymin), width, height, edgecolor = edgecolor, facecolor = 'none')
ax.add_patch(rect)
다른 분류와 그것에 일치하는 바운딩 박스가 있다.
Faster RCNN 구현
모델 훈련과 테스트 이미지에서 예측을 하기 위해 keras_fcnn라이브러리를 사용한다.
git clone https://github.com/kbardool/keras-frcnn.git
복제된 저장소에 train_images, test_images, train.csv를 옮긴다. 새로운 데이터셋에서 모델을 훈련하기 위해, 입력 포멧은 아래와 같아야 한다.
filepath,x1,y1,x2,y2,class_name
여기서,
- filepath : 훈련 이미지 경로
- x1 : 바운딩 박스에 대한 xmin
- y1 : 바운딩 박스에 대한 ymin
- x2 : 바운딩 박스에 대한 xmax
- y2 : 바운딩 박스에 대한 ymax
- class_name : 해당 바운딩 박스내 분류명(class name)
.csv 포멧을 위에서 설명한 것과 동일한 포멧인 .txt 파일로 변환해야 한다. 새로운 데이터프레임(Dataframe)을 만들고 포멧에 맞춰 모든 값을 채운 후 .txt파일로 저장한다.
data = pd.DataFrame()
data['format'] = train['image_names']
# as the images are in train_images folder, add train_images before the image name
for i in range(data.shape[0]):
data['format'][i] = 'train_images/' + data['format'][i]
# add xmin, ymin, xmax, ymax and class as per the format required
for i in range(data.shape[0]):
data['format'][i] = data['format'][i] + ',' + str(train['xmin'][i]) + ',' + str(train['ymin'][i]) + ',' + str(train['xmax'][i]) + ',' + str(train['ymax'][i]) + ',' + train['cell_type'][i]
data.to_csv('annotate.txt', header=None, index=None, sep=' ')
다음으로 train_frcnn.py파일로 모델을 훈련한다.
cd keras-frcnn
python train_frcnn.py -o simple -p annotate.txt
데이터의 크기때문에 모델 훈련시간이 좀 걸린다. GPU를 사용하면 더 빨리 훈련할 수 있다. 아니면 훈련 반복 횟수(epoch)를 줄여 볼 수도 있다. 훈련 반복 횟수를 줄이기 위해서는 train_frcnn.py파일의 num_epochs 파라미터를 적절하게 바꾸면 된다.
매번 모델이 개선되는 것을 확인할 수 있고 특정 회차(epoch)의 가중치는 동일 디렉토리에 model_frcnn.hdf5로 저장된다. 이 가중치는 테스트셋에서 예측할 때 사용된다. 약 500회에 걸쳐 훈련된 모델의 가중치를 [다운로드(전체)]받을 수 있다.
[다운로드(분할01)]
[다운로드(분할02)]
[다운로드(분할03)]
[다운로드(분할04)]
[다운로드(분할05)]
[다운로드(분할06)]
[다운로드(분할07)]
[다운로드(분할08)]
[다운로드(분할09)]
[다운로드(분할10)]
모델이 훈련되었고 가중치가 설정되었다. keras_frcnn은 새로운 이미지에 대한 예측을 하고 새로운 디렉토리에 이를 저장한다. 이를 위해 test_frcnn.py파일에서 두가지를 수정해야 한다.
- 파일의 맨 마지막 주석을 제거한다.
cv2.imwrite(‘./results_imgs/{}.png’.format(idx),img)
- 두번째와 세번째 마지막 라인을 주석처리 한다.
# cv2.imshow(‘img’, img) # cv2.waitKey(0)
아래와 같이 새로운 이미지에 대한 예측을 만든다.
python test_frcnn.py -p test_images
마지막으로 탐지된 객체를 포함하는 이미지는 results_imgs디렉토리에 저장된다. 아래는 Faster RCNN이 예측한 예제이다.