EfficientNet B0의 Keeras 모델을 ONX 모델로 변환하여 추론

개시하다


"불합리한 이미지 분류기를 만들어 보다"시리즈에서 Keeras와 EfficientNetB0을 사용하여 이미지 분류기를 실현했다.
나는 그 이미지 분류 모델을 ONX 모델로 바꾸어 추론하고 싶다.

ONX가 뭐예요?


온NX(Open Neural Network Exchange)는 페이스북, 마이크로소프트가 주도해 머신러닝 프레임워크를 상호 운용하는 프로그램이다.자세한 내용은 먼저 구글에서 확인하세요.
  • 공식: ONNX
  • Wikipedia: Open Neural Network Exchange
  • 왜 ONX 모형으로 바꿨어요?


    개인적으로 가장 큰 이유는'ONXRuntime의 추론이 빠르다'는 것이지만, 추론할 때Kers, PyTorch 등 서로 다른 기계 학습 프레임워크를 사용해 학습하는 모델을 통일적으로 처리할 수 있다는 기쁨도 있다.

    모형을 바꾸는 두 가지 방법


    대략적인 조사만 하면 Kers 모델을 ONX 모델로 변환하는 방법은 다음과 같은 두 가지가 있다.
  • tf2onnx로 변환 ← 추천!
  • keras2onnx로 변환 ← 실패
  • 전자가 압도적으로 추천하다.후자를 배우기 위해서도 해 보았지만 상당히 어려웠다.비록 마침내 전환되었지만 추론은 실패했다.
    또 이 글은 학습을 하지 않고 전환, 추론만 하고 학습 후 전환하는 순서도 같다.

    컨디션


    어떤 변환, 추론이든 다음과 같은 환경에서 집행된다.Docker 내에서 실행되며 GPU는 사용되지 않습니다.
  • 하드웨어:
  • CPU: AMD Ryzen 3700X(8코어/16 스레드)
  • 스토리지: 64GB
  • GPU: GeForce GTX 1070(스토리지 8GB)
  • 소프트웨어:
  • OS: Ubuntu 20.04.2 LTS
  • Docker: 19.03.8
  • NVIDIA 드라이브: 460.39
  • tf2 onx로 변환→성공


    tf2onnx는 TensorFolow 모델을 ONX 모델로 변환하는 도구입니다.
    Kers에서 모델을 만든 후 TensorFolow Saved Model 형식으로 모델을 저장하면 이 도구로 변환할 수 있습니다.
    Kers H5 형식은 사용할 수 없습니다.

    Docker 이미지 만들기


    사용한 경우Dockerfilerequirements.txt는 다음과 같다.
    Dockerfile
    FROM nvidia/cuda:11.0.3-cudnn8-devel-ubuntu20.04
    RUN apt-get update \
      && DEBIAN_FRONTEND=noninteractive apt-get install --yes --no-install-recommends \
        build-essential \
        ca-certificates \
        python3-dev \
        python3-pip \
        python3-setuptools \
        tzdata \
      && rm --recursive --force /var/lib/apt/lists/*
    RUN python3 -m pip install --upgrade pip setuptools
    WORKDIR /opt/app
    COPY requirements.txt ./
    RUN python3 -m pip install --requirement requirements.txt
    ENV LANG C.UTF-8
    ENV TZ Asia/Tokyo
    
    requirements.txt
    onnxruntime==1.7.0
    tensorflow-hub==0.11.0
    tensorflow==2.4.1
    tf2onnx==1.8.4
    

    모델 생성


    TensorFlow Hub에서 학습한 EfficientNet B0를 그대로 저장하여 모델 파일을 생성합니다.
    원래 공부를 했는데 이번에는 전환 전후를 통해 학습 전의 추론 결과를 비교하여 전환의 성공 여부를 판단한다.
    save_model.py
    #!/usr/bin/env python3
    
    import tensorflow as tf
    import tensorflow_hub as hub
    
    model = tf.keras.Sequential(
        [
            hub.KerasLayer(
                "https://tfhub.dev/tensorflow/efficientnet/b0/feature-vector/1",
                trainable=False,
            ),
            tf.keras.layers.Dense(1, activation="sigmoid"),
        ]
    )
    model.build([None, 224, 224, 3])
    model.summary()
    model.save("efficientnet-b0")
    
    실행 예는 다음과 같다.성공하면 efficientnet-b0 디렉터리를 생성합니다.
    $ ./save_model.py
    Model: "sequential"
    _________________________________________________________________
    Layer (type)                 Output Shape              Param #
    =================================================================
    keras_layer (KerasLayer)     (None, 1280)              4049564
    _________________________________________________________________
    dense (Dense)                (None, 1)                 1281
    =================================================================
    Total params: 4,050,845
    Trainable params: 1,281
    Non-trainable params: 4,049,564
    _________________________________________________________________
    2021-04-23 00:07:12.365759: W tensorflow/python/util/util.cc:348] Sets are not currently considered sequences, but this may change in the future, so consider avoiding using them.
    
    $ ls efficientnet-b0
    assets  saved_model.pb  variables
    

    Keeras로 추론하다


    원스 모델로 전환하기 전에 케어스의 추론 결과를 확인해 보자.
    여기에 흑백 두 장의 그림에 대해 추론을 진행한다.
    predict_keras.py
    #!/usr/bin/env python3
    
    import numpy as np
    import tensorflow as tf
    
    model = tf.keras.models.load_model("efficientnet-b0")
    
    images = np.array(
        [
            np.zeros((224, 224, 3), dtype=np.float32),
            np.ones((224, 224, 3), dtype=np.float32),
        ]
    )
    
    results = model.predict(images)
    print(results)
    
    실행 예는 다음과 같다.모든 조합 층은 무작위 수로 초기화되어 있기 때문에 모델을 저장할 때마다 값이 달라질 수 있음을 주의하십시오.
    $ ./predict_keras.py
    WARNING:tensorflow:No training configuration found in save file, so the model was *not* compiled. Compile it manually.
    [[0.5295639]
     [0.5148043]]
    

    변환 모델

    tf2onnx를 사용하여 모델을 변환합니다.
    convert.sh
    #!/bin/bash
    python3 -m tf2onnx.convert --saved-model efficientnet-b0 --output efficientnet-b0.onnx
    
    실행 예는 다음과 같다.몇 개의 경고를 내보냈지만 이번에는 무시했다.
    $ ./convert.sh
    /usr/lib/python3.8/runpy.py:127: RuntimeWarning: 'tf2onnx.convert' found in sys.modules after import of package 'tf2onnx', but prior to execution of 'tf2onnx.convert'; this may result in unpredictable behaviour
      warn(RuntimeWarning(msg))
    2021-04-23 00:14:01,125 - WARNING - '--tag' not specified for saved_model. Using --tag serve
    2021-04-23 00:14:06,873 - INFO - Signatures found in model: [serving_default].
    2021-04-23 00:14:06,873 - WARNING - '--signature_def' not specified, using first signature: serving_default
    2021-04-23 00:14:06,873 - INFO - Output names: ['dense']
    WARNING:tensorflow:From /usr/local/lib/python3.8/dist-packages/tf2onnx/tf_loader.py:557: extract_sub_graph (from tensorflow.python.framework.graph_util_impl) is deprecated and will be removed in a future version.
    Instructions for updating:
    Use `tf.compat.v1.graph_util.extract_sub_graph`
    2021-04-23 00:14:09,553 - WARNING - From /usr/local/lib/python3.8/dist-packages/tf2onnx/tf_loader.py:557: extract_sub_graph (from tensorflow.python.framework.graph_util_impl) is deprecated and will be removed in a future version.
    Instructions for updating:
    Use `tf.compat.v1.graph_util.extract_sub_graph`
    2021-04-23 00:14:10,275 - INFO - Using tensorflow=2.4.1, onnx=1.9.0, tf2onnx=1.8.4/cd55bf
    2021-04-23 00:14:10,275 - INFO - Using opset <onnx, 9>
    2021-04-23 00:14:11,053 - INFO - Computed 0 values for constant folding
    2021-04-23 00:14:13,763 - INFO - Optimizing ONNX model
    2021-04-23 00:14:17,027 - INFO - After optimization: BatchNormalization -42 (49->7), Const -240 (442->202), Identity -926 (926->0), Squeeze -16 (16->0), Transpose -275 (276->1), Unsqueeze -64 (64->0)
    2021-04-23 00:14:17,056 - INFO -
    2021-04-23 00:14:17,057 - INFO - Successfully converted TensorFlow model efficientnet-b0 to ONNX
    2021-04-23 00:14:17,057 - INFO - Model inputs: ['keras_layer_input:0']
    2021-04-23 00:14:17,057 - INFO - Model outputs: ['dense']
    2021-04-23 00:14:17,057 - INFO - ONNX model is saved at efficientnet-b0.onnx
    

    원스로 추론하다


    변환된 ONX 모델을 사용하여 추론합니다.
    predict_onnx.py
    #!/usr/bin/env python3
    
    import numpy as np
    import onnxruntime
    
    session = onnxruntime.InferenceSession("efficientnet-b0.onnx")
    
    images = np.array(
        [
            np.zeros((224, 224, 3), dtype=np.float32),
            np.ones((224, 224, 3), dtype=np.float32),
        ]
    )
    
    results = session.run(["dense"], {"keras_layer_input:0": images})
    print(results)
    
    실행 예는 다음과 같다.
    $ ./predict_onnx.py
    [array([[0.52956396],
           [0.5148051 ]], dtype=float32)]
    
    케어스 모델의 추론 결과와 엄격하게 일치하지 않지만 소수점 5위까지 일치하기 때문에 문제가 없는 것 같습니다.

    keras2 onx로 변환 → 실패


    다음은 keras2onnx로 변환해 보세요.tf2onnx의 전환은 단번에 성공했지만 keras2onnx의 전환은 상당히 어렵다.
    참고는 다음과 같습니다.
  • keras2onnx는 TensorFlowv2입니다.4 대응 없음, TensorFlowv2.대응(2021년 4월 23일 기준, v1.7.0)
  • TensorFlow v2.4로 생성된 모델은 TensorFlowv2입니다.2 못 읽어서 공부도 v2.2에서 진행해야 합니다.
  • TensorFlow v2.2를 사용하려면 CUDA 10.1/cuDN7 대신 CUDA 11.0/cuDN8을 사용해야 합니다.
  • Docker 이미지 만들기


    사용한 경우Dockerfilerequirements.txt는 다음과 같다.
    CUDA, TensorFolow의 버전 등과 tf2onnx의 상황은 다르다.
    Dockerfile
    FROM nvidia/cuda:10.1-cudnn7-devel-ubuntu18.04
    RUN apt-get update \
      && DEBIAN_FRONTEND=noninteractive apt-get install --yes --no-install-recommends \
        build-essential \
        ca-certificates \
        python3-dev \
        python3-pip \
        python3-setuptools \
        tzdata \
      && rm --recursive --force /var/lib/apt/lists/*
    RUN python3 -m pip install --upgrade pip setuptools
    WORKDIR /opt/app
    COPY requirements.txt ./
    RUN python3 -m pip install --requirement requirements.txt
    ENV LANG C.UTF-8
    ENV TZ Asia/Tokyo
    
    requirements.txt
    keras2onnx==1.7.0
    onnxruntime==1.7.0
    tensorflow-hub==0.11.0
    tensorflow==2.2.1
    

    모델 생성


    기본 단계는 tf2onnx와 같지만 왜fitpredict를 호출하지 않으면 모델을 저장할 때 오류가 발생합니다.
    save_model.py
    #!/usr/bin/env python3
    
    import numpy as np
    import tensorflow as tf
    import tensorflow_hub as hub
    
    model = tf.keras.Sequential(
        [
            hub.KerasLayer(
                "https://tfhub.dev/tensorflow/efficientnet/b0/feature-vector/1",
                trainable=False,
            ),
            tf.keras.layers.Dense(1, activation="sigmoid"),
        ]
    )
    model.build([None, 224, 224, 3])
    model.summary()
    model.predict(np.zeros((1, 224, 224, 3), dtype=np.float32))
    model.save("efficientnet-b0")
    
    실행 예는 다음과 같습니다.
    $ ./save_model.py
    Model: "sequential"
    _________________________________________________________________
    Layer (type)                 Output Shape              Param #
    =================================================================
    keras_layer (KerasLayer)     multiple                  4049564
    _________________________________________________________________
    dense (Dense)                multiple                  1281
    =================================================================
    Total params: 4,050,845
    Trainable params: 1,281
    Non-trainable params: 4,049,564
    _________________________________________________________________
    2021-04-23 00:35:08.379870: W tensorflow/python/util/util.cc:329] Sets are not currently considered sequences, but this may change in the future, so consider avoiding using them.
    WARNING:tensorflow:From /usr/local/lib/python3.6/dist-packages/tensorflow/python/ops/resource_variable_ops.py:1817: calling BaseResourceVariable.__init__ (from tensorflow.python.ops.resource_variable_ops) with constraint is deprecated and will be removed in a future version.
    Instructions for updating:
    If using Keras pass *_constraint arguments to layers.
    WARNING:tensorflow:From /usr/local/lib/python3.6/dist-packages/tensorflow/python/ops/resource_variable_ops.py:1817: calling BaseResourceVariable.__init__ (from tensorflow.python.ops.resource_variable_ops) with constraint is deprecated and will be removed in a future version.
    Instructions for updating:
    If using Keras pass *_constraint arguments to layers.
    

    Keeras로 추론하다


    소스 코드가 tf2onnx와 같기 때문에 생략합니다.
    실행 예는 다음과 같다.
    $ ./predict_keras.py
    WARNING:tensorflow:No training configuration found in save file, so the model was *not* compiled. Compile it manually.
    [[0.41439614]
     [0.43379608]]
    

    변환 모델

    keras2onnx를 사용하여 모델을 변환합니다.
    convert.py
    #!/usr/bin/env python3
    
    import keras2onnx
    import onnx
    import tensorflow as tf
    
    model = tf.keras.models.load_model("efficientnet-b0")
    onnx_model = keras2onnx.convert_keras(model, "efficientnet-b0")
    onnx.save_model(onnx_model, "efficientnet-b0.onnx")
    
    실행 예는 다음과 같다.
    $ ./convert.py
    WARNING:tensorflow:No training configuration found in save file, so the model was *not* compiled. Compile it manually.
    tf executing eager_mode: True
    tf.keras model eager_mode: False
    2021-04-23 00:41:39.446674: W tensorflow/python/util/util.cc:329] Sets are not currently considered sequences, but this may change in the future, so consider avoiding using them.
    WARN: No corresponding ONNX op matches the tf.op node sequential/keras_layer/StatefulPartitionedCall/StatefulPartitionedCall/StatefulPartitionedCall/StatefulPartitionedCall/StatefulPartitionedCall/tf_op_layer_BroadcastTo_1/PartitionedCall/BroadcastTo_1 of type BroadcastTo
          The generated ONNX model needs run with the custom op supports.
    The ONNX operator number change on the optimization: 4007 -> 492
    

    원스로 추론하다


    소스 코드가 tf2onnx와 같기 때문에 생략합니다.
    실행 예는 다음과 같다.
    $ ./predict_onnx.py
    Traceback (most recent call last):
      File "./predict_onnx.py", line 6, in <module>
        session = onnxruntime.InferenceSession("efficientnet-b0.onnx")
      File "/usr/local/lib/python3.6/dist-packages/onnxruntime/capi/onnxruntime_inference_collection.py", line 280, in __init__
        self._create_inference_session(providers, provider_options)
      File "/usr/local/lib/python3.6/dist-packages/onnxruntime/capi/onnxruntime_inference_collection.py", line 307, in _create_inference_session
        sess = C.InferenceSession(session_options, self._model_path, True, self._read_config_from_model)
    onnxruntime.capi.onnxruntime_pybind11_state.Fail: [ONNXRuntimeError] : 1 : FAIL : Load model from efficientnet-b0.onnx failed:Fatal error: BroadcastTo is not a registered function/op
    
    /오류가 발생했습니다.
    변환 시 정보와 같이 ONX에서 지원하지 않는 운영자BroadcastTo 때문일 수 있습니다.
    맞춤형 조작원을 추가하면 대응이 가능할 수 있지만 tf2onnx 전환이 성공해 조사가 중단됐다.

    결론

    tf2onnx 사용하세요.

    좋은 웹페이지 즐겨찾기