본문 바로가기

AI

Intel Neural Compute Stick(Movidius)에서 Keras 모델 실행하기

임베디드 환경에서 AI 코드를 실행하려고 하다보니 속도에 대한 이슈가 있어서 찾아보던중 인텔이 개발한 NCS의 존재를 알게 되어 사용해보게 되었다.

 

임베디드 보드에서 NCSDK 설치는 다른 기관에서 진행해 주었는데 NCSDK가 제공하는 샘플이 아닌 직접 학습시킨 네트워크를 사용해보려고 하니 제약 사항도 많고 계속 error가 나거나 알 수 없는 결과들이 도출되어서 Intel NCSDK 포럼을 뒤져봐도 대부분의 질문들이 답을 얻지 못하고 끝난 상태였다.

 

하지만 다양한 곳에서 나타난 작은 실마리들을 붙잡으며 나아가 커스텀 네트워크 실행에 성공했고, 이 글은 그 험난한 여정을 마친 기록이다. (임베디드 보드 세팅은 타기관에서 진행해 주었기 때문에 나는 PC 상에서 커스텀 네트워크를 실행하는데 중점을 두었다)

 

※요점정리

  • DropOut Layer는 NCSDK에서 지원하지 않으니 모델 설계시에 제거 한다.
  • tensorflow의 operations 이름들을 추출해 둬야 한다.
  • mvNCCheck 를 이용하여 tensorflow operation을 순차적으로 검증해야 한다.
  • 너무 큰 사이즈의 연산은 세그멘테이션 에러를 일으킨다.
  • Flatten(혹은 Reshape)은 사실상 유명 무실하다 다른 기능을 이용하여 차원을 맞춰 주도록 하자(ex. MaxPooling)
  • mvNCCheck에서 -i 옵션으로 입력되는 이미지의 픽셀 값들은 기본적으로 255로 나눠지는 듯하다. -S 255 옵션을 사용하지 않도록 하자.

  • Intel Nerual Compute Stick (Movidius)

우선은 NCS를 구입을 하고(배송기간 약 3주), 우분투에 NCSDK를 설치 한다. PC의 경우 우분투 16.04 버전에서만 설치가 된다.

NCSDK 설치가 정상적으로 완료 되어야 mvNCCheck, mvNCComlie 등을 이용할 수 있다.

https://movidius.github.io/ncsdk/install.html

 

Basic Installation and Configuration

Documentation for the Intel® Movidius™ Neural Compute SDK and Intel® Movidius™ Neural Compute API.

movidius.github.io

 

  • Keras model 을 ncsdk용 graph로 변환하기

이 부분이 사실상 모든 문제의 근원이 되는 부분이다. 웹에서 다양한 방법으로 변환하는 방법들을 적어두었지만 그 사이에 발생하는 문제들에 대해선 전혀 언급이되어 있지 않아 파편화된 정보들을 모으고 모아서 스스로 해결 할 수 밖에 없었다.

 

이 과정은 Keras model 저장한 후 tensorflow 저장 방식으로 변환하고 이후에 이를 검증하면서 검증이 완료되면 graph를 만드는 방식으로 진행해야 한다.

 

  • Keras model 저장과 tensorflow 저장 방식으로 변환

우선 학습이 완료된 Keras의 모델을 저장해 준다. 이때 중요한 것중 하나가 NCSDK는 아직 DropOut Layer를 지원하지 않는다.

즉 애초에 모델을 설계 할 때 DropOut Layer를 제거해줘야 한다.

with open("model.json", "w") as file:
    file.write(model.to_json())
model.save_weights("weights.h5")

저장된 모델을 불러와서 tensorflow의 모델 저장 형태로 변환해 준다.

from keras.models import model_from_json
from keras import backend as K
import tensorflow as tf

model_file = "model.json"
weights_file = "weights.h5"

with open(model_file, "r") as file:
    config = file.read()

K.set_learning_phase(0)
model = model_from_json(config)
model.load_weights(weights_file)

saver = tf.train.Saver()
sess = K.get_session()
print(sess.graph.get_operations())
saver.save(sess, "./TF_Model/tf_model")

fw = tf.summary.FileWriter('logs', sess.graph)
fw.close()

이때 중요한 것이 print(sess.graph.get_operations()) 부분인데 여기서 출력된 tensorflow의 operation 이름을 기준으로 이후에 graph 생성을 검증해야 하기 때문이다.

위의 과정으로 생성된 tf_model.meta 파일을 mvNCCheck를 통해서 검증하게 된다.

참고 URL : https://www.dlology.com/blog/how-to-run-keras-model-on-movidius-neural-compute-stick/

 

How to run Keras model on Movidius neural compute stick | DLology

Posted by: Chengwei 9 months, 3 weeks ago (Comments) Movidius neural compute stick(NCS) along with some other hardware devices like UP AI Core, AIY vision bonnet and the recently revealed Google edge TPU are gradually bringing deep learning to resource-con

www.dlology.com

  • mvNCCheck를 이용한 tf_model.meta 검증

앞에서 알아낸 tensorflow의 operations들의 이름을 하나씩 -on 옵션에 대입해가면서 한단계씩 진행하면서 검증을 하도록 한다.(-in 옵션의 input layer는 고정하도록 한다.)

layer들을 하나씩 검토하면서 만나는 문제들을 원본 네트워크에서 수정해 나가야 한다(mvNCCheck에서 걸리는 문제들은 ncsdk에서 해결할 수 없는 문제들이기 때문에 원본 Keras 모델 자체를 수정해야 한다.)

 

다음은 mvNCCheck를 진행하면서 만났고 해결이 어려웠던 사례들이다.

  1. 세그멘테이션 에러(segmentation fault)
    • 한번에 너무 많은 weight 들을 연산하게 될 경우 발생하는 문제로 보인다.
    • 나의 경우 Flatten 이후에 Dense Layer의 MatMul 연산때 (10000 * 64 = 640000) 발생했다.
  2. Flatten or Reshape Layer 검증에서 발생하는 에러 (RuntimeWarning: invalid value encountered in greater)
    • NCSDK에서 Flatten Layer를 지원한다고는 하지만 버그가 있는 것 같다. (Reshape 또한)
    • 다차원의 형태를 변형 할 때 문제가 생기는것 같기 때문에 Flatten(or Reshape)전에 다른 Layer를 이용해서 해당 형태의 Shape로 변경 해둬야 한다. (ex MaxPooling), 일종의 편법이다.
    • 나의 경우 (1, 16, 12, 12)의 output을 갖는 conv2d Layer를 바로 Flatten에 넣었더니 에러가 발생하였다. 그리하여 conv2d Layer 뒤에 pool_size=12의 MaxPooling Layer를 추가 하여 (1, 16, 1, 1) Shape를 만들고 이를 Flatten Layer에 넣었더니 더이상 에러가 발생하지 않았다.
    • 이는 ncsdk의 examples중 tensorflow에 있는 인셉션V1의 Layer에서 얻게된 힌트이다.

3, 4차원의 값이 있던 Layer가 MaxPool Layer를 거친뒤에 3, 4차원의 값이 1로 바뀌어서 ReShape을 무사히 통과한다.

이런 과정을 거쳐 mvNCCheck를 이용해서 최종 출력층 -on 까지 검증이 완려되면 mvNCCompile을 진행해서 graph 파일을 생성하도록 한다.

 

mvNCCheck에서 -i 옵션을 이용하여 실제 테스트 이미지를 경로를 입력할 수 있는데 이때 내부에서 자동으로 해당 이미지 RGB들의 값을 255로 나누고 있는것으로 보이기 때문에 -S 255 옵션을 사용하면 엉뚱한 값이 나오기 때문에 조심해야 한다.

 

그리고 생성된 graph 파일은 다음과 같은 방법으로 검증할 수 있다.

from mvnc import mvncapi
import cv2
import numpy

# Get a list of available device identifiers
device_list = mvncapi.enumerate_devices()

# Initialize a Device
device = mvncapi.Device(device_list[0])

# Initialize the device and open communication
device.open()

# Load graph file data
GRAPH_FILEPATH = './graph'
with open(GRAPH_FILEPATH, mode='rb') as f:
    graph_buffer = f.read()

# Initialize a Graph object
graph = mvncapi.Graph('graph1')

# Allocate the graph to the device and create input and output Fifos with default arguments
input_fifo, output_fifo = graph.allocate_with_fifos(device, graph_buffer)

images = ["0000.png", "0001.png", "0002.png", "0003.png"]

for img in images:
	# Read an image from file
	tensor = cv2.imread(img)
	# Do pre-processing specific to this network model (resizing, subtracting network means, etc.)
	tensor = cv2.cvtColor(tensor, cv2.COLOR_BGR2RGB)
	tensor = tensor / 255.0

	tensor = tensor.astype(numpy.float32)

	# Write the tensor to the input_fifo and queue an inference
	graph.queue_inference_with_fifo_elem(input_fifo, output_fifo, tensor, 'user object')

	# Get the results from the output queue
	output, user_obj = output_fifo.read_elem()
	print(output, output.argmax())

# Clean up
input_fifo.destroy()
output_fifo.destroy()
graph.destroy()
device.close()
device.destroy()

 

위는 4개의 이미지를 순차적으로 검증하는 코드이다.

 

해당 네트워크를 학습 시킬 때에 RGB 순서에 각 픽셀을 255로 나눠서 0.0~1.0으로 하였기 때문에 위의 코드에서도 해당 내용이 이미지 전처리에 적용되어 있다.

 

위의 코드로 인공지능의 predict 결과 까지 확인하게 되면 Intel Neural Compute Stick(Movidius)의 NCSDK를 이용한 custom network를 실행하는 과정이 완료되게 된다.

 

P.S : mvNCProfile을 이용하면 전체 네트워크를 수행하는데 걸리는 자원의 소모량과 소요 시간등을 알 수 있다.