Life Logs && *Timeline

  • TensorFlow Lite

    
    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 기반 객체 탐지가 가능하다!