# ๐ฆ TFHub ๋ชจ๋ธ์ Android์์ TensorFlow Lite Task API๋ก ์ฌ์ฉํ๋ ๋ฐฉ๋ฒ
TensorFlow Lite Task API๋ ๋งค์ฐ ํธ๋ฆฌํ ๊ณ ์์ค Vision API์ง๋ง, ์ฌ์ ์๊ตฌ์ฌํญ์ด ๊น๋ค๋กญ๋ค. ํนํ **Teachable Machine**์ด๋ **TensorFlow Hub**์์ ๋ค์ด๋ก๋ํ `.pb` ๋ชจ๋ธ์ ๋ฐ๋ก Android์์ ์ธ ์ ์๋ค.
์ด ํฌ์คํ
์์๋ `.pb` ๋ชจ๋ธ์ Android์์ Task API๋ก ์ฌ์ฉํ๊ธฐ๊น์ง์ **์ ์ฒด ์ ํ ํ๋ฆ**์ ์ ๋ฆฌํ๋ค.
---
## ๐ง ์ ์ ๋๋๊ฐ?
### โ Android TFLite Task API์์ ๋ฐ๋ก ์ฌ์ฉํ ์ ์๋ ์ด์
- ๋๋ถ๋ถ์ `.pb` ๋ชจ๋ธ์ **TFLite ํฌ๋งท์ด ์๋**
- ๋๋ `.tflite`๋ผ๊ณ ํด๋ **Metadata (NormalizationOptions ๋ฑ)** ๊ฐ ์์ด Task API๊ฐ ์คํจ
- ์๋ฌ ๋ฉ์์ง ์์:
Input tensor has type kTfLiteFloat32: it requires specifying NormalizationOptions metadata…
---
## โ
ํด๊ฒฐ ์์ ์์ฝ
1. **(ํ์์) `tfhub_module.pb` โ SavedModel ๋ณํ**
2. `SavedModel` โ `.tflite` ๋ณํ
3. `.tflite` โ Metadata ์ถ๊ฐ
4. Android Task API์์ ์ ์ ๋ก๋
---
## ๐ Step-by-Step
### 1. `tfhub_module.pb` โ `SavedModel` ๋ณํ
```python
import tensorflow as tf
graph_def = tf.compat.v1.GraphDef()
with tf.io.gfile.GFile("tfhub_module.pb", "rb") as f:
graph_def.ParseFromString(f.read())
with tf.Graph().as_default() as graph:
tf.import_graph_def(graph_def, name="")
for op in graph.get_operations():
print(op.name) # input/output ์ด๋ฆ ํ์ธ
@tf.function(input_signature=[tf.TensorSpec([1, 224, 224, 3], tf.float32)])
def model_fn(x):
return {"output": tf.import_graph_def(graph_def, input_map={"input": x}, return_elements=["output:0"])[0]}
concrete_func = model_fn.get_concrete_function()
tf.saved_model.save(concrete_func, "./saved_model")
2. SavedModel โ TFLite ๋ณํ
converter = tf.lite.TFLiteConverter.from_saved_model("./saved_model")
converter.optimizations = [tf.lite.Optimize.DEFAULT]
converter.target_spec.supported_types = [tf.float32]
tflite_model = converter.convert()
with open("model.tflite", "wb") as f:
f.write(tflite_model)
3. Metadata ์ถ๊ฐ (NormalizationOptions ๋ฑ)
from tflite_support.metadata_writers import image_classifier
from tflite_support.metadata_writers import writer_utils
MODEL_PATH = "model.tflite"
LABEL_FILE = "labels.txt" # ๊ฐ ์ค๋ง๋ค ํด๋์ค๋ช
SAVE_TO_PATH = "model_with_metadata.tflite"
writer = image_classifier.MetadataWriter.create_for_inference(
MODEL_PATH, [LABEL_FILE], norm_mean=[127.5], norm_std=[127.5]
)
writer_utils.save_file(writer.populate(), SAVE_TO_PATH)
- ์ด ๊ณผ์ ์ ๊ฑฐ์ณ์ผ GMS ๊ธฐ๋ฐ Task API์์ ์ ์ ๋ก๋๋จ
4. Android์์ ๋ก๋
val options = ObjectDetector.ObjectDetectorOptions.builder()
.setMaxResults(3)
.build()
val objectDetector = ObjectDetector.createFromFileAndOptions(
context,
"model_with_metadata.tflite",
options
)
๐งช ์๋ฌ๊ฐ ๋ ๋ ์ฒดํฌ ํฌ์ธํธ
| ์ฒดํฌ๋ฆฌ์คํธ | ์ค๋ช |
|---|---|
| Tensor ์ด๋ฆ | input:0, output:0 ๋ฑ ์ ํํ ์ด๋ฆ ํ์ |
| Tensor ํ์ | float32์ผ ๊ฒฝ์ฐ normalization metadata ํ์ |
| Label ํ์ผ | .tflite์ ํฌํจ๋์ด์ผ Task API๊ฐ ํด๋์ค๋ช
๋งคํ ๊ฐ๋ฅ |
| ์ ๋ ฅ ์คํ | ์ด๋ฏธ์ง shape์ [1, 224, 224, 3] ๋ฑ์ด ์ผ๋ฐ์ |
โ ๋ง๋ฌด๋ฆฌ
Task API๋ ํธ๋ฆฌํ์ง๋ง, ๋ชจ๋ธ ์ค๋น๊ฐ ๊น๋ค๋กญ๋ค.
์ด ๊ณผ์ ์ ์๋ํํ๋ ค๋ฉด CLI ๋๊ตฌ ๋์ Python ์คํฌ๋ฆฝํธ๋ฅผ ํ์ฉํ์.
ํนํ TFHub/Teachable Machine ๋ชจ๋ธ์ ๋ฐ๋์ metadata ์ถ๊ฐ ํ ์ฌ์ฉํด์ผ Android์์ ์ ๋๋ก ๋์ํ๋ค.
์ต์ข
์ฐ์ถ๋ฌผ: model_with_metadata.tflite
์ด ๋ชจ๋ธ๋ง ์์ผ๋ฉด Android ์ฑ์์ ๋ฐ๋ก GMS Task API ๊ธฐ๋ฐ ๊ฐ์ฒด ํ์ง๊ฐ ๊ฐ๋ฅํ๋ค!
๋ต๊ธ ๋จ๊ธฐ๊ธฐ