Using the TPU training mode on Google Cloud, I trained an SSD MobileNet V1 FPN model to recognize two types of objects. The model trained without errors, and I was able to evaluate in TensorBoard. Following conversion to TensorFlow Lite and attempting to run the model in the demo application for object detection, the model fails with an error regarding a custom operation being unimplemented, despite TensorFlow's documentation stating that the operation is implemented in Lite builds.
I trained the model using a TPU on Google Cloud, and downloaded the model from the storage bucket.
Next, I exported the model using the latest revision of the object detection model (paths are generic on purpose):
python -m object_detection/export_tflite_ssd_graph \
--pipeline_config_path=$PATH_TO_CONFIG_FILE \
--trained_checkpoint=model.ckpt-$CHECKPOINT \
--output_directory=$OUTPUT_DIR \
--add_postprocessing_op=true
Next, I converted the model using the latest 1.12 tag from TensorFlow's Git repository (using Bazel 0.21 to avoid Bazel errors):
bazel run -c opt //tensorflow/contrib/lite/toco:toco \
--incompatible_package_name_is_a_function=false \
-- \
--input_file=$OUTPUT_DIR/tflite_graph.pb \
--output_file=$OUTPUT_DIR/detect.tflite \
--input_shapes=1,640,640,3 \
--input_arrays=normalized_input_image_tensor \
--output_arrays='TFLite_Detection_PostProcess','TFLite_Detection_PostProcess:1','TFLite_Detection_PostProcess:2','TFLite_Detection_PostProcess:3' \
--inference_type=FLOAT \
--allow_custom_ops
Next, I edited the Bazel build closure to include my model which I copied over to the application directory, and edited the DetectionActivity Java file to reference my model:
# out of context
assets = [
#"//tensorflow/contrib/lite/examples/android/app/src/main/assets:labels_mobilenet_quant_v1_224.txt",
#"@tflite_mobilenet//:mobilenet_quant_v1_224.tflite",
#"@tflite_conv_actions_frozen//:conv_actions_frozen.tflite",
#"//tensorflow/contrib/lite/examples/android/app/src/main/assets:conv_actions_labels.txt",
#"@tflite_mobilenet_ssd//:mobilenet_ssd.tflite",
"//tensorflow/contrib/lite/examples/android/app/src/main/assets:detect.tflite",
#"//tensorflow/contrib/lite/examples/android/app/src/main/assets:box_priors.txt",
"//tensorflow/contrib/lite/examples/android/app/src/main/assets:pascal_labels.txt",
],
# out of context
private static final int TF_OD_API_INPUT_SIZE = 640;
private static final boolean TF_OD_API_IS_QUANTIZED = false;
private static final String TF_OD_API_MODEL_FILE = "file:///android_asset/detect.tflite";
private static final String TF_OD_API_LABELS_FILE = "file:///android_asset/pascal_labels.txt";
Finally, I built and deployed the application using the following commands:
bazel build -c opt --config=android_arm64 --cxxopt='--std=c++11' "//tensorflow/contrib/lite/examples/android:tflite_demo"
adb install -r bazel-bin/tensorflow/contrib/lite/examples/android/tflite_demo.apk
All the code I modified was otherwise stock in the repository at https://github.com/tensorflow/models/tree/master/research/object_detection.
I expect the result to be a working application, demonstrable by building the application without making any modifications to the program (stock from the repository).
The actual result is the application crashes immediately upon launch with the below error message, captured with Logcat:
2019-02-09 16:38:28.229 32716-32716/? E/AndroidRuntime: FATAL EXCEPTION: main
Process: org.tensorflow.lite.demo, PID: 32716
java.lang.RuntimeException: java.lang.IllegalArgumentException: Internal error: Cannot create interpreter: Didn't find custom op for name 'ResizeNearestNeighbor' with version 1
Registration failed.
at org.tensorflow.demo.TFLiteObjectDetectionAPIModel.create(TFLiteObjectDetectionAPIModel.java:124)
at org.tensorflow.demo.DetectorActivity.onPreviewSizeChosen(DetectorActivity.java:110)
at org.tensorflow.demo.CameraActivity$5.onPreviewSizeChosen(CameraActivity.java:362)
at org.tensorflow.demo.CameraConnectionFragment.setUpCameraOutputs(CameraConnectionFragment.java:401)
at org.tensorflow.demo.CameraConnectionFragment.openCamera(CameraConnectionFragment.java:408)
at org.tensorflow.demo.CameraConnectionFragment.access$000(CameraConnectionFragment.java:64)
at org.tensorflow.demo.CameraConnectionFragment$1.onSurfaceTextureAvailable(CameraConnectionFragment.java:95)
at android.view.TextureView.getHardwareLayer(TextureView.java:390)
at android.view.TextureView.draw(TextureView.java:339)
at android.view.View.updateDisplayListIfDirty(View.java:18150)
at android.view.View.draw(View.java:18928)
at android.view.ViewGroup.drawChild(ViewGroup.java:4240)
at android.view.ViewGroup.dispatchDraw(ViewGroup.java:4026)
at android.view.View.updateDisplayListIfDirty(View.java:18141)
at android.view.View.draw(View.java:18928)
at android.view.ViewGroup.drawChild(ViewGroup.java:4240)
at android.view.ViewGroup.dispatchDraw(ViewGroup.java:4026)
at android.view.View.draw(View.java:19203)
at android.view.View.updateDisplayListIfDirty(View.java:18150)
at android.view.View.draw(View.java:18928)
at android.view.ViewGroup.drawChild(ViewGroup.java:4240)
at android.view.ViewGroup.dispatchDraw(ViewGroup.java:4026)
at android.view.View.updateDisplayListIfDirty(View.java:18141)
at android.view.View.draw(View.java:18928)
at android.view.ViewGroup.drawChild(ViewGroup.java:4240)
at android.view.ViewGroup.dispatchDraw(ViewGroup.java:4026)
at android.view.View.updateDisplayListIfDirty(View.java:18141)
at android.view.View.draw(View.java:18928)
at android.view.ViewGroup.drawChild(ViewGroup.java:4240)
at android.view.ViewGroup.dispatchDraw(ViewGroup.java:4026)
at android.view.View.draw(View.java:19203)
at com.android.internal.policy.DecorView.draw(DecorView.java:825)
at android.view.View.updateDisplayListIfDirty(View.java:18150)
at android.view.ThreadedRenderer.updateViewTreeDisplayList(ThreadedRenderer.java:669)
at android.view.ThreadedRenderer.updateRootDisplayList(ThreadedRenderer.java:675)
at android.view.ThreadedRenderer.draw(ThreadedRenderer.java:783)
at android.view.ViewRootImpl.draw(ViewRootImpl.java:3098)
at android.view.ViewRootImpl.performDraw(ViewRootImpl.java:2912)
at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:2465)
at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1453)
at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:6958)
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:911)
at android.view.Choreographer.doCallbacks(Choreographer.java:723)
at android.view.Choreographer.doFrame(Choreographer.java:658)
at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:897)
at android.os.Handler.handleCallback(Handler.java:790)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6626)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:811)
Caused by: java.lang.IllegalArgumentException: Internal error: Cannot create interpreter: Didn't find custom op for name 'ResizeNearestNeighbor' with version 1
Registration failed.
at org.tensorflow.lite.NativeInterpreterWrapper.createInterpreter(Native Method)
2019-02-09 16:38:28.229 32716-32716/? E/AndroidRuntime: at org.tensorflow.lite.NativeInterpreterWrapper.<init>(NativeInterpreterWrapper.java:70)
at org.tensorflow.lite.Interpreter.<init>(Interpreter.java:175)
at org.tensorflow.lite.Interpreter.<init>(Interpreter.java:163)
at org.tensorflow.demo.TFLiteObjectDetectionAPIModel.create(TFLiteObjectDetectionAPIModel.java:122)
... 51 more
Here are all of the resources I can provide to help debug this issue:
I found the solution to the issue. Using TensorFlow v1.13.0-rc1
appears to resolve the issue at this point.
This is because the ResizeNearestNeighbor
operation for TensorFlow Lite did not exist until v1.13, and I realize that my main mistake was looking at the documentation for v1.13 despite using v1.12.