MXNet 시작하기 (4) – 이미지 분류를 위한 학습 모델 사용하기 (Inception v3)

이전 글에서는 처음으로 신경망을 구축하고 학습하는 방법을 배웠습니다. 이제 좀 더 실질적인 문제 해결을 할 수 있는 사례를 살펴 보겠습니다.

우선 최근에 사용되는 딥러닝 학습 모델은 매우 복잡하다는 사실을 알고 계셔야 합니다. 수 백개의 레이어가 있으며 막대한 양의 데이터를 학습하는 데 며칠이 걸릴수 있으며, 이러한 모델을 만들고 조정하는 데는 많은 전문 지식이 필요합니다.

매우 다행인 것인 이러한 모델을 사용하는 것은 생각 보다 간단하며 몇 줄의 소스 코드만 가지고 할 수 있습니다. 이 글에서는 Inception v3이라는 이미지 분류를 위해 미리 학습된 모델을 살펴 볼 것입니다.

2015년 12월에 나온 Inception v3은 GoogleNet 모델(2014 ImageNet Challenge에서 우승 한 모델)을 발전 시킨 것입니다. 연구 논문의 세부 사항에 대해서는 언급하지 않겠지만, 결론적으로 Inception v3는 당시에 사용 가능한 최고의 학습 모델보다 15-25% 정확도가 높으며, 연산에 있어 6배 저렴하고 최소 20% 미만의 매개 변수를 사용합니다 (즉, 모델 사용에 필요한 RAM 사용량이 적습니다.)

딥러닝을 인기있는 모델로 끌어 올린 대단한 물건인데, 이를 MXNet으로 한번 작동시켜 봅시다.

MXNet Zoo 학습 모델
Model Zoo는 MXNet에서 손쉽게 사용할 수 있도록 미리 학습된 모델 모음입니다. 모델 정의, 모델 매개 변수 (즉, 뉴런 가중치) 및 사용 가이드 등으로 구성되어 있습니다.

여기서 ImageNet에 해당 하는 모델 정의와 매개 변수 파일을 다운로드하세요. (파일 이름을 변경해야 할 수도 있음). 첫 번째 파일을 열면 모든 레이어의 정의가 표시되어 있습니다. 두 번째 파일은 바이너리 파일입니다.

$ wget http://data.dmlc.ml/models/imagenet/inception-bn/Inception-BN-symbol.json
$ wget http://data.dmlc.ml/models/imagenet/inception-bn/Inception-BN-0126.params
$ mv Inception-BN-0126.params Inception-BN-0000.params

이 모델은 ImageNet 데이터 세트에서 학습되었으므로 해당 이미지 카테고리 (1000개)도 다운로드해야 합니다.

$ wget http://data.dmlc.ml/models/imagenet/synset.txt

$ wc -l synset.txt
 1000 synset.txt

$ head -5 synset.txt
 n01440764 tench, Tinca tinca
 n01443537 goldfish, Carassius auratus
 n01484850 great white shark, white shark, man-eater, man-eating shark, Carcharodon carcharias
 n01491361 tiger shark, Galeocerdo cuvieri
 n01494475 hammerhead, hammerhead shark

다운로드 다 받으셨나요? 그러면 이제 모델을 가져와서 작업을 시작해 봅시다!

모델 로딩하기
우리가 해야 할 일을 살펴 보겠습니다.

1. 저장 상태에서 모델 로딩하기 : MXNet에서는 이것을 체크 포인트라고 부릅니다.  우리는 입력 Symbol과 모델 매개 변수를 반환 받습니다.

import mxnet as mx
sym, arg_params, aux_params = mx.model.load_checkpoint('Inception-BN', 0)

2. 새로운 모듈을 생성하고 그것을 입력 심볼로 할당합니다. 모델을 어디에서 실행할지를 나타내는 컨텍스트 매개 변수를 지정할 수도 있습니다. 기본값은 cpu (0)이지만 gpu (0)를 사용하여 GPU에서 실행할 수도 있습니다.

mod = mx.mod.Module(symbol=sym)

3. 입력 심볼에 입력 데이터를 바인딩합니다. 이름은 네트워크의 입력 레이어에 있는 이름이기 때문에 ‘data’라고 합시다. (JSON 파일의 처음 몇 줄을보십시오). ‘data’의 크기를 1 x 3 x 224 x 224로 정의합니다. 당황하지 마세요 😉 ‘224 x 224’는 이미지 해상도로 모델 학습 방법입니다. ‘3’은 채널 수입니다: 빨강, 초록, 파랑​​(순서대로). ‘1’은 배치 크기입니다. 한 번에 하나의 이미지를 예측합니다.

mod.bind(for_training=False, data_shapes=[('data', (1,3,224,224))])

4. 모델 매개 변수를 설정합니다.

mod.set_params(arg_params, aux_params)

이게 전부입니다. 코드 네 줄! ㅋㅋ 이제 몇 가지 데이터를 밀어 넣고 무슨 일이 일어나는지 살펴 보겠습니다. (데이터 먼저 준비하구요.)

데이터 준비하기
사실 개발자라면 1970년대 부터 하던 일인데, 데이터베이스 부터 기계 학습, 딥러닝에 이르기까지 빠지지 않는 게 데이터 준비네요. 좀 귀찮은 일이긴 하지만 꼭 필요하니 시작해 봅시다.

이 모델은 하나의 224 x 224 이미지의 빨강, 녹색 및 파란 채널을 유지하는 4차원 NDArray를 사용합니다. 우리는 인기있는 OpenCV 라이브러리를 사용하여 입력 이미지로 부터 데이터를 추출해서 NDArray에 넣을 것입니다. OpenCV를 설치하지 않은 경우 “pip install opencv-python”을 실행하면 대부분의 경우 충분합니다. 🙂

단계는 다음과 같습니다.

1. 이미지 읽기 : BGR 순서 (파란색, 녹색 및 빨간색)의 세 채널을 사용하여 (이미지 높이, 이미지 너비, 3) 모양의 수적으로 배열을 반환합니다.

img = cv2.imread(filename)

2. 이미지를 RGB로 변환합니다.

img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)

3. 이미지를 224 x 224 크기로 조정합니다.

img = cv2.resize(img, (224, 224,))

4. 배열을 (이미지 높이, 이미지 너비, 3)에서 (3, 이미지 높이, 이미지 너비)로 바꿉니다.

img = np.swapaxes(img, 0, 2)
img = np.swapaxes(img, 1, 2)

5. 네 번째 차원을 추가하고 NDArray를 정의합니다.

img = img[np.newaxis, :]
array = mx.nd.array(img)
>>> print array.shape
 (1L, 3L, 224L, 224L)

자! 이제 실제 해볼까요? 아래에 이미지 파일이 하나 있습니다.


Input picture 448×336 (Source: metaltraveller.com)

이미지 처리가 끝나면 그림 크기가 조정되어 array[0]에 저장된 RGB 채널로 분할됩니다 (아래 그림을 생성하는 데 사용 된 코드가

array[0][0] : 224×224 red channel


array[0][1] : 224×224 green channel


array[0][2] : 224×224 blue channel

일괄 처리 크기가 1보다 크면, array[1]에 두 번째 이미지, array[2]에 세 번째 이미지 등이 나눠집니다. 이제 예측을 해볼까요?
예측하기
파트 3에서 Module 객체에서 데이터를 일괄적으로 모델에 공급해야 한다는 것을 기억하실 거에요. 일반적으로 데이터 반복기(Iterator)를 사용하는 것이 일반적인 방법입니다. (특히, NDArrayIter 객체 사용).

여기서 이미지 하나만 예측하고 싶습니다. 데이터 반복기를 사용할 수는 있겠지만 너무 오버하는 것이 될 수 있습니다. 대신 데이터 속성이 참조될 때 입력 NDArray를 반환하여, 가짜 반복기 역할을 할 Batch라는 이름의 튜플(tuple)을 만들 것입니다.

from collections import namedtuple
Batch = namedtuple('Batch', ['data'])

이제 “Batch”를 모델에 전달하고 예측할 수 있습니다.

mod.forward(Batch([array]))

모델은 1000 개 카테고리에 해당하는 1000개의 확률을 갖는 NDArray를 출력합니다. 일괄 처리 크기가 1이므로 한 줄 밖에 없습니다.

prob = mod.get_outputs()[0].asnumpy()

>>> prob.shape
 (1, 1000)

이것을 squeeze()를 사용하여 배열로 바꾼 후, argsort()를 사용하여 내림차순으로 정렬된 두 번째 배열 인덱스를 만듭니다.

prob = np.squeeze(prob)

>>> prob.shape
(1000,)
>> prob
[ 4.14978594e-08 1.31608676e-05 2.51907986e-05 2.24045834e-05
2.30327873e-06 3.40798979e-05 7.41563645e-06 3.04062659e-08 etc.

sortedprob = np.argsort(prob)[::-1]

>> sortedprob.shape
(1000,)

현재 모델에 따르면, 사진의 가장 가능성이 큰 카테고리는 #546이며 확률은 58%입니다.

>> sortedprob
[546 819 862 818 542 402 650 420 983 632 733 644 513 875 776 917 795
etc.
>> prob[546]
0.58039135

이 카테고리의 이름을 찾아 보겠습니다. synset.txt 파일을 사용하여 카테고리 목록을 작성하고, #546를 목록에서 찾을 수 있습니다.

synsetfile = open('synset.txt', 'r')
categorylist = []
for line in synsetfile:
categorylist.append(line.rstrip())

>>> categorylist[546]
'n03272010 electric guitar'

어떠세요? 전기 기타(electric quitar)라고 나오죠? 두번째를 볼까요?

>>> prob[819]
0.27168664
>>> categorylist[819]
'n04296562 stage

무대(stage)라고 나옵니다. 어떠세요? 그냥 됩니다. 우리는 이제 이미지 내의 분류를 위해 사전 학습된 모델을 사용하는 방법을 사용해 보았습니다. 코드는 고작 4줄이었고 주로 했던 일은 데이터 준비였습니다.

맨 아래 전체 코드가 있습니다. 다음에는 이미지 분석을 위해 좀 더 잘 학습된 모델인 VGG16과 ResNet-152를 살펴 보도록 하겠습습니다. 잘 따라 와 주세요~

다음 글: MXNet 시작하기 (5) – VGG16 및 ResNet-152 학습 모델 사용하기

연재 순서

코드 전체 보기

여러분의 생각