[TFLite] 4(5). 모델 변환과 기기 배포

4 minute read


모델 변환과 기기 배포


1. 모델 변환


개발한 모델을 안드로이드에서 사용하려면 텐서플로 라이트 모델로 변환해야 합니다. 텐서플로 라이트는 케라스 모델, SavedModel, Concrete 함수를 각각 TFLite 모델로 변환할 수 있고, 각 포맷의 변환 함수가 tf.lite.TFLiteConverter에 작성되어 있습니다.

KakaoTalk_20210810_154107260

각 포맷의 변환 방법을 알아봅시다.


케라스 모델 변환


텐서플로의 tf.keras 모듈을 통해 케라스 모델을 바로 만들거나 SavedModel, HDF5 포맷으로 저장된 모델을 케라스 모델로 불러와서 텐서플로 라이트 모델로 변환할 수 있습니다.

# 다층 퍼셉트론 케라스 모델
import tensorflow as tf

mnist = tf.keras.datasets.mnist
(x_train, y_train), (x_test, y_test) = mnist.load_data()

x_train, x_test = x_train / 255.0, x_test / 255.0

mlp_model = tf.keras.models.Sequential([tf.keras.layers.Flatten(input_shape=(28,28)),
                                        tf.keras.layers.Dense(128, activation='relu'),
                                        tf.keras.layers.Dense(10, activation='softmax')
                                       ])

mlp_model.compile(optimizer='adam', loss='sparse_categorical_crossentropy', 
                  metrics=['accuracy'])
mlp_model.fit(x_train, y_train, epochs=5)


Epoch 1/5
1875/1875 [==============================] - 4s 2ms/step - loss: 0.2605 - accuracy: 0.9254
Epoch 2/5
1875/1875 [==============================] - 4s 2ms/step - loss: 0.1137 - accuracy: 0.9660
Epoch 3/5
1875/1875 [==============================] - 4s 2ms/step - loss: 0.0791 - accuracy: 0.9756
Epoch 4/5
1875/1875 [==============================] - 4s 2ms/step - loss: 0.0591 - accuracy: 0.9820
Epoch 5/5
1875/1875 [==============================] - 4s 2ms/step - loss: 0.0449 - accuracy: 0.9862





<tensorflow.python.keras.callbacks.History at 0x2898d613280>


이제 mlp_model을 텐서플로 라이트로 변환합니다.

tf.lite.TFLiteConverter.from_keras_model() 메서드로 변환기를 만들고 변환기의 convert() 메서드로 모델을 변환합니다.

converter = tf.lite.TFLiteConverter.from_keras_model(mlp_model)
tflite_model = converter.convert()
INFO:tensorflow:Assets written to: C:\Users\wjsdu\AppData\Local\Temp\tmpcidkictn\assets


변환한 모델을 파일로 저장하려면 아래와 같이 파일 출력 함수인 write() 함수를 이용합니다.

with open('./keras_model.tflite', 'wb') as f:
    f.write(tflite_model)

open() 함수에 지정한 대로 현재 경로에 keras_model.tflite 파일이 생성됩니다.

KakaoTalk_20210810_163928336



SavedModel 변환


1. 텐서플로 모델 저장 및 불러오기

텐서플로 모델을 저장하는 방법으로는 학습된 파라미터만 저장하는 방법과 모델 전체를 저장하는 방법이 있습니다.

파라미터만 저장하려면 체크포인트라는 것을 사용하고 모델 전체를 저장하려면 모델 아키텍처, 가중치, 컴파일 관련 설정 값, 옵티마이저를 모두 저장해야 하므로 SavedModel이나 HDF5 포맷을 사용합니다.

텐서플로 1에서는 HDF5, 텐서플로 2에서는 SavedModel을 권장합니다.

tf.saved_model.save(mlp_model, "./mlp_model/")
# 또는
mlp_model.save("./mlp_model/")

# HDF5 format
# mlp_model.save("./mlp_model.h5")

이미 저장된 모델이 있다면 아래와 같이 케라스 모델로 불러올 수 있습니다.

saved_model = tf.keras.models.load_model("./mlp_model/") 

# HDF5 format
# h5_model = tf.keras.models.load_model("./mlp_model.h5")

이렇게 불러온 모델을 모델 변환 메서드를 사용하면 바로 tflite 모델로 변환됩니다.

converter = tf.lite.TFLiteConverter.from_keras_model(saved_model)
tflite_model = converter.convert()


2. SavedModel 바로 변환

SavedModel의 경우 케라스 모델로 불러오지 않고 바로 tflite 파일로 변환이 가능하여, 텐서플로 라이트는 이 방법을 가장 추천합니다.

from_saved_model() 메서드를 사용합니다.

# SavedModel 변환
converter = tf.lite.TFLiteConverter.from_saved_model("./mlp_model/")
tflite_model = converter.convert()
# SavedModel 저장
with open('./saved_model.tflite', 'wb') as f:
    f.write(tflite_model)

KakaoTalk_20210810_163941196



Concrete 함수 변환


1. Concrete 함수

즉시 실행모드로 동작하는 파이썬 함수에 @tf.function 데코레이터를 붙이거나 모델과 함수를 tf.function() 메서드에 인자로 전달하면 자동으로 그래프 모드로 변환됩니다.

예를 들면 다음과 같습니다.

# 입력받은 데이터에 1을 더하는 케라스 레이어
class Inc(tf.keras.layers.Layer):
    def call(self, inputs):
        return inputs+1
    
inc = Inc()

위 코드를 다음 2가지 방법으로 그래프 모드로 바꿀 수 있습니다.

# 1. call 함수에 @tf.function 데코레이션
class Inc_Graph(tf.keras.layers.Layer):
    @tf.function
    def call(self, inputs):
        return inputs+1
    
inc_g = Inc_Graph()

# 2. tf.function() 메서드에 인스턴스 전달
inc_g2 = tf.function(inc)

텐서플로 그래프는 다형성을 갖춘 일반적인 파이썬 함수와 달리 정적인 데이터 타입과 형태가 필요하기 때문에 호출 시 전달 받은 파라미터의 타입과 형태에 맞는 Concrete 함수를 생성합니다.

이 Concrete 함수는 시그니처(파라미터의 타입과 형태) 별로 하나만 생성되어 재사용됩니다.

print(inc_g(tf.constant(4)))
print(inc_g2(tf.constant(4)))
tf.Tensor(5, shape=(), dtype=int32)
tf.Tensor(5, shape=(), dtype=int32)

시그니처 별 Concrete 함수는 get_concrete_function() 메서드에 시그니처를 입력하여 얻을 수 있습니다.

# 1. 데코레이터를 이용하여 그래프 모드로 변환한 함수의 Concrete 함수 획득
concrete_fun = inc_g.call.get_concrete_function(tf.TensorSpec(shape=(1,3), 
                                                             dtype=tf.float32))
print(concrete_fun(tf.constant([[1.0, 2.0, 3.0]])))

# 2. 그래프 모드로 변환한 클래스의 concrete 함수
concrete_fun = inc_g2.get_concrete_function(tf.TensorSpec(shape=(1,3), 
                                                         dtype=tf.float32))
print(concrete_fun(tf.constant([[1.0, 2.0, 3.0]])))
tf.Tensor([[2. 3. 4.]], shape=(1, 3), dtype=float32)
tf.Tensor([[2. 3. 4.]], shape=(1, 3), dtype=float32)

inc_g 레이어는 call() 메서드에만 데코레이터를 적용했기 때문에 모델의 call() 메서드에서 get_concrete_function() 메서드를 호출하여 Concrete 함수를 얻습니다.

한편 inc_g2 레이어는 클래스 전체를 그래프 모드로 변환했기 때문에 클래스에서 바로 get_concrete_function() 메서드를 호출하여 Concrete 함수를 얻습니다.

get_concrete_function() 함수의 인자로 shape와 dtype을 지정한 tf.TensorSpec을 전달하면 그 시그니처에 맞는 Concrete 함수를 반환합니다.

다음은 두 가지 경우에 모두 모델을 그래프 모드로 바꾸고 Concrete 함수를 얻는 방법을 구현한 코드입니다.

# tf.function() 메서드를 이용한 그래프 모드 적용
mlp_model = tf.keras.models.Sequential([tf.keras.layers.Flatten(input_shape=(28,28)),
                                        tf.keras.layers.Dense(128, activation='relu'),
                                        tf.keras.layers.Dense(10, activation='softmax')
                                       ])

graph_model = tf.function(mlp_model)
concrete_func = graph_model.get_concrete_function(
                    tf.TensorSpec(shape=mlp_model.inputs[0].shape, 
                                  dtype=mlp_model.inputs[0].dtype))


# @tf.function 데코레이터를 이용한 그래프 모드 적용
class MLP_Model(tf.keras.Model):
    def __init__(self):
        super(MLP_Model, self).__init__()
        self.flatten = tf.keras.layers.Flatten()
        self.dense = tf.keras.layers.Dense(128, activation='relu')
        self.softmax = tf.keras.layers.Dense(10, activation='softmax')
    
    @tf.function
    def call(self, inputs):
        x = self.flatten(inputs)
        x = self.dense(x)
        return self.softmax(x)
    
    
mlp_model = MLP_Model()
concrete_func = mlp_model.call.get_concrete_function(
                    tf.TensorSpec(shape=mlp_model.inputs[0].shape, 
                                  dtype=mlp_model.inputs[0].dtype))


2. Concrete 함수 변환

Concrete 함수를 얻으면 from_concrete_functions() 함수를 이용해 TFLite 모델로 변환하고 저장할 수 있습니다.

converter = tf.lite.TFLiteConverter.from_concrete_functions([concrete_func])
tflite_model = converter.convert()

with open('./concrete_func_model.tflite', 'wb') as f:
    f.write(tflite_model)

KakaoTalk_20210810_163907112



CLI 환경에서의 모델 변환


SavedModel이나 HDF5처럼 파일로 저장된 모델을 파이썬 코드를 사용하지 않고 CLICommand Line Interface 환경에서 명령어를 사용하여 바로 TFLite 모델로 변환할 수 있습니다.

1. SavedModel

tflite_convert –saved_model_dir=__ --output_file=_<생성할 tflite="" 파일경로="">_

2. HDF5

tflite_convert –keras_model_file=<h5 파일경로> –output_file=_<생성할 tflite="" 파일경로="">_


텐서플로 허브의 TFLite 모델

텐서플로 허브에서는 tflite 형태로 변환이 완료된 모델도 제공합니다. 최근 경량 모델 중 이미지 분류에서 가장 성능이 좋다고 알려진 신경망은 EfficientNet입니다.



2. 기기 배포

모델을 개발하고 tflite 파일로 변환했다면 앱에서 활용할 수 있도록 안드로이드 스튜디오에 배포합니다.

  1. [app] 우클릭 - [New] - [Folder] - [Assets Folder] 를 클릭하여 assets 폴더를 하나 생성합니다.
    KakaoTalk_20210810_165050735

  2. assets 폴더 안에 tflite 파일를 가져다 놓습니다.
    KakaoTalk_20210810_165524861


위의 두 과정만으로 안드로이드 스튜디오에서 딥러닝 모델을 활용할 수 있습니다.



정리


  • 모델 변환에는 크게 3가지가 있습니다.
    • 케라스 모델 변환: tf.lite.TFLiteConverter.from_keras_model(모델).convert()
    • SavedModel 변환: tf.lite.TFLiteConverter.from_saved_model(폴더 경로명).convert()
    • Concrete 함수 변환: tf.lite.TFLiteConverter.from_concrete_functions([Concrete 함수, …]).convert()
    • 이외에도 파일로 저장된 모델의 경우 CLI 환경에서 바로 변환도 가능하고, 텐서플로 허브에서 이미 tflite 파일로 변환된 모델을 사용할 수도 있습니다.
  • 기기 배포는 [app] 우클릭 - [New] - [Folder] - [Assets Folder] 를 클릭하여 assets 폴더를 하나 생성한 후에 폴더 안에 tflite 파일을 가져다 놓으면 됩니다.

Categories:

Updated:

Leave a comment