1. 텐서플로 라이트 : 안드로이드 기초(1)

첫 번째로 모델이라고 부를 수도 없는 모델을 안드로이드와 연동한다.
모델이 됐건 연산이 됐건
텐서플로 라이트 입장에선 다를 것이 없다.
너무 간단한 덧셈과 곱셈 연산을 통해
안드로이드로부터 플레이스 홀더 입력을 받아오는 것부터 해보자.

내용이 길어서 여러 개로 나누어서 작업한다.
여기서는 텐서플로 모델을 생성하고 tflite 파일로 변환하는 것까지 진행한다.

  1. PC에서 모델을 학습한다.
  2. 학습한 모델을 텐서플로 라이트 버전으로 변환한다.

안드로이드 앱을 구성하고 나면
최종적으로 아래와 같은 화면이 뜬다.
다양한 입력을 보여주기 위해 4가지 형태로 구성했다.
4개의 버튼 중에서 하나를 누르면 상단에 있는 텍스트뷰에 결과를 보여준다.


코드가 조금 긴데..
4가지 코드를 한번에 보여주기 때문에 그런 것뿐.. 하나하나는 가볍기 그지 없다.
model_common 함수를 중점적으로 봐야 하고
나머지 함수들에서는 입력과 출력이 어떻게 달라지는지를 봐야 한다.

import tensorflow as tf


# 이번 파일에서 공통으로 사용하는 함수.
# 컨버터 생성해서 파일로 저장. 다시 말해 모바일에서 사용할 수 있는 형태로 변환해서 저장.
def model_common(inputs, outputs, model_path):
# 텐서플로 API만을 사용해서 저장할 수 있음을 보여준다.
# 4가지 방법 중에서 가장 기본.
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())

# input_tensors: List of input tensors. Type and shape are computed using get_shape() and dtype.
# output_tensors: List of output tensors (only .name is used from this).
converter = tf.lite.TFLiteConverter.from_session(sess,
input_tensors=inputs,
output_tensors=outputs)
# 세션에 들어있는 모든 연산, 즉 모델 전체를 변환
# 반환값은 TFLite 형식의 Flatbuffer 또는 Graphviz 그래프
flat_data = converter.convert()

# 텍스트가 아니기 때문에 바이너리 형태로 저장. w(write), b(binary)
with open(model_path, 'wb') as f:
f.write(flat_data)


# 입력 1개, 출력 1개
def simple_model_1(model_path):
# 에러. 반드시 shape을 지정해야 함.
# x = tf.placeholder(tf.int32)

# 안드로이드에서 전달한 입력과 출력 변수가 플레이스 홀더와 연동
x = tf.placeholder(tf.int32, shape=[1])
out = x * 5

model_common([x], [out], model_path)

# 에러. 반드시 [] 형태로 전달해야 함.
# model_common(x, out, model_path)


# 입력 2개짜리 1개, 출력 1개
def simple_model_2(model_path):
x = tf.placeholder(tf.int32, shape=[2])
out = tf.reduce_sum(x * x)

model_common([x], [out], model_path)


# 입력 1개짜리 2개, 출력 1개
def simple_model_3(model_path):
# 에러. 반드시 shape을 지정해야 함.
# x1 = tf.placeholder(tf.int32, shape=[0])
# x2 = tf.placeholder(tf.int32, shape=[0])

x1 = tf.placeholder(tf.int32, shape=[1])
x2 = tf.placeholder(tf.int32, shape=[1])
out = tf.add(x1, x2)

# 입력에 2개 전달
model_common([x1, x2], [out], model_path)


# 입력 1개짜리 2개, 출력 1개짜리 2개
def simple_model_4(model_path):
x1 = tf.placeholder(tf.int32, shape=[1])
x2 = tf.placeholder(tf.int32, shape=[1])
out_1 = x1 + x2
out_2 = x1 * x2

# 입력에 2개, 출력에 2개 전달
model_common([x1, x2], [out_1, out_2], model_path)


simple_model_1('simple_1.tflite')
simple_model_2('simple_2.tflite')
simple_model_3('simple_3.tflite')
simple_model_4('simple_4.tflite')


tflite 파일로 변환하는 방법에는 4가지가 있는데
나는 쉬운 것만 쓰고 좀더 나을 수도 있겠지만 복잡해 보이는 방법은 사용하지 않을 것이다.

여기서는 가장 기본이 되는 텐서플로 코드를 직접 변환하는 방법을 사용했다.
세션에 들어있는 모든 데이터와 연산을 변환하게 되고 함수 이름은 from_session이다.
다른 방법이 궁금하면
도트(.)를 찍고 나면 어떤 방법이 있는지 이름을 통해 확인할 수 있다.

위의 코드를 실행하고 나면
현재 폴더에 tflite 파일 4개가 만들어진다.
생성된 모든 파일은 뒤에서 안드로이드 프로젝트로 복사해서 붙여넣는다.

PC 버전의 모델을 tflite 모델로 변환한다는 것은
모바일 버전에서 효율적으로 동작할 수 있도록 재구성하는 것이다.
PC 버전을 살짝 바꿔서 모바일 버전을 구현할 수도 있겠지만
그럴 경우 하드웨어 사양이 낮은 모바일에서는 결과 보기가 매우 힘들어진다.

convert 함수를 호출하면 모델을 FlatBuffer로 변환한다.
구글에서 게임 개발에 사용하기 위해 만들었는데
크로스 플랫폼에서 데이터를 효율적으로 처리하기 위한 라이브러리로 사용된다.