type
status
date
slug
category
password
tags
This blog records the process and issues solved during Deploying a pytorch model on an Android Phone. The code are uploaded on Github:
mobilenet-v2-android
wenyanglyu โข Updated Feb 15, 2025
Introduction
The first step is to choose the Right Pathway for Your Android ML Deployment, here is a list for different pathways. TFLite is chosen in this project.
Deployment Pathway | Best For | Pros | Cons |
TFLite (TensorFlow Lite) | Small, efficient models | Fast, optimized for mobile | Needs conversion |
PyTorch Mobile | PyTorch-based models | No conversion needed | Larger size |
ONNX Runtime | Cross-platform models | Works with TF & PyTorch | Needs ONNX conversion |
MLKit (Google Firebase) | Pre-trained ML models | No training needed | Limited customization |
Core ML | iOS + Android apps | Optimized for Apple devices | Needs conversion for Android |
This is a list of using different models while choosing TFLite deployed on Android. PyTorch is chosen to convert to TFLite in this project.
Model Type | Conversion Pathway | Output Format |
TensorFlow / Keras | SavedModel โ TFLiteConverter | .tflite โ
|
PyTorch | PyTorch โ ONNX โ TensorFlow โ TFLite | .tflite โ
|
Scikit-learn / XGBoost | Sklearn โ ONNX โ TensorFlow โ TFLite | .tflite โ
|
Unsupported TF Operations | Must replace layers with TFLite-compatible ops | .tflite ๐จ |
This project aims to deploy a pre-trained PyTorch ML model on Android, allowing for a quick implementation of the PyTorch โ ONNX โ TensorFlow โ TFLite conversion pathway and Android deployment.
Once the pipeline is established, it can be used to deploy custom ML models or other pre-trained models efficiently.
1 Python - Virtual Environment & Dependency Management
1.1 Setting Up a Virtual Environment (venv
)
To isolate dependencies and avoid conflicts, create and activate a virtual environment:
1.2 Issues & Key Takeaways
Issue 1.2.1: ModuleNotFoundError: No module named 'keras.src.engine'
TensorFlow Addons (
TFA
) only supports tf.keras
, but because standalone Keras (3.x) is installed, Python prioritizes it over tf.keras
TFA
tries to load keras.src.engine
, which does not exist in tf.keras
.Solution: Remove standalone Keras to force TFA to use tf.keras
Key Takeaway: Using tested low version is better than using latest versions
Issue 1.2.2: Dependency Conflicts with TensorFlow, TFA, and TFP
By default,
pip
installs the latest version of each package, without checking compatibility. This led to incompatible versions being installed.Solution: Install Compatible Versions Explicitly
Key Takeaway: I took too long too solve this issue, so I saved the versions in to file requirements.txt
pip freeze > requirements.txt
, then I wonโt suffer for the next project.Issue 1.2.3: typing-extensions
Conflict Between TensorFlow and PyTorch
I tested the requirements.txt in a new venv and failed to install all package because of the following dependency conflict:
Solution: These versions cannot be installed simultaneously because torch requires a higher version typing_extensions while tensorflow requires a very low one. So I Split Dependencies into Separate
requirements.txt
files and install them sequentially:Key Takeaway: If dependencies have conflicting requirements, install them in separate stages. This solution is tested working in new venv.
2 Python - Model Preparation
2.1 Model Selection
I tried several github models and it has different issues, before I lost my patience, I found this MobileNetV2 model from PyTorch.
MobileNetV2 is a light weight classificaiton model of 1000 classes.
The official link has given detialed guide such as the input of MobileNetV2 (3x224x224) and normalization requirement. https://pytorch.org/hub/pytorch_vision_mobilenet_v2/
2.1 Model Test
The first step is to test the model and evaluate its performance, giving me a clear expectation of its capabilities and how it will function on my Android phone.
Well, it works well, the classification is very accurate after testing several images.
3 Python - Model Conversion
3.1 Converting to TFLite
coremltools
converts models into MobileNetV2.mlmodel
for iOS, while TFLite
converts TensorFlow models into .tflite
format for Android.ONNX serves as a bridge between PyTorch and TensorFlow.
- ONNX (Open Neural Network Exchange) is an open-source format for representing machine learning models.
- ONNX enables ML model interoperability across PyTorch, TensorFlow, and other frameworks.
- ONNX is the best option when moving a model from PyTorch to TensorFlow Lite (for Android)
The reason for using TFLite for Android is TensorFlow models are too large and inefficient for mobile deployment.
4 Android - Building the Mobile App
I am using Android Studio for deploying and testing the application on a Samsung phone, with the converted TFLite machine learning model.
The project started by creating an empty Android project targeting Android 12 (API level 31). Below is a directory list of modified files:
4.1 Summary of Modified Files and Their Functions
Most of the project files we donโt need to touch, and they are configured and linked with each other perfectly.
4.1.1 AndroidManifest.xml
(Application Configuration)
Configures necessary permissions for file access, as the app selects images from storage for classification.
4.1.2 app/build.gradle.kts
(Dependencies)
- Added dependencies for TensorFlow Lite and Android UI components. There are serveral build.gradle.kts files, modify the one under app folder.
4.1.3 MainActivity.kt
(App Logic & Model Inference)
- Handles image selection from the gallery.
- Loads and initializes the TFLite model.
- Runs image preprocessing and inference on the selected image.
- Uses
imagenet_classes.txt
text-based label mapping.
4.1.4 activity_main.xml
(XML-Based User Interface)
- Created manually as it was not originally present in the project.
It defines the visual structure of the screen for
MainActivity.kt
.- When
MainActivity.kt
is launched, it loads this XML file using:setContentView(R.layout.activity_main)
ImageView
for selected image.Button
to classify the image.TextView
to show the classification result.
This means the UI elements in
activity_main.xml
will be displayed when the app starts.4.1.5 assets/
(Model and Labels)
- Added manually to store necessary assets.
mobilenet_v2.tflite
: The TensorFlow Lite model for image classification.
imagenet_classes.txt
: Label mappings for classification results.
4.2 Key Configurations in MainActivity.kt
The MainActivity.kt is the most important file in the project, the functions include Image Selection, loading TFLite model, pre-process the selected image, and run inference then display result.
4.2.1 UI Setup & Image Selection
- Users can select an image from their gallery by tapping on an
ImageView
.
- The selected image is then displayed in the UI for classification.
Process Overview:
- The app registers an Activity Result to handle image selection.
- When an image is chosen, it is stored in
selectedBitmap
and displayed in theImageView
.
4.2.2 Loading the TensorFlow Lite Model
- Loads
mobilenet_v2.tflite
from assets.
- Configures TensorFlow Lite Interpreter with specific options.
Process Overview:
- The model is loaded into a
tflite
interpreter for running inference.
- The settings ensure compatibility across different devices without GPU acceleration conflicts.
4.2.3 Preprocessing the Image for Inference
- Converts the selected image into a ByteBuffer that matches the input format of MobileNetV2.
- Normalizes pixel values using ImageNet mean & standard deviation values.
Process Overview:
- The image is resized to 224x224 pixels.
- Normalization applies ImageNet preprocessing values.
- The formatted ByteBuffer is ready for inference in TensorFlow Lite.
4.2.4 Running Inference & Displaying Results
- Runs inference asynchronously.
- Identifies the highest confidence prediction and displays it.
Process Overview:
- Runs inference on a background thread (
Dispatchers.IO
).
- Finds the class with the highest confidence score.
- Displays the predicted label in the UI.
4.3 Android Forecasting Error Troubleshooting
The same image produces correct predictions in Python, but incorrect predictions in Android. I tested different images on my Samsung phone, the results are far from correct.
4.3.1 Model Verification
The original MobileNetV2 model was converted using a standard pipeline:
- MobileNetV2 (verified in Python)
- Converted to ONNX
- Converted to TFLite
- Deployed TFLite on Android โ Incorrect predictions
I verified that TFLite runs correctly in Python, so the issue is not related to model conversion.
The image preprocessing on Android appeared correct, the input data shape matched expectations, and the output values had the correct dimensionsโyet the forecast was inaccurate. This raised the question of whether the same model could produce different results on different platforms. However, given that this conversion pipeline is a well-established method, any discrepancies should be minimal.
4.3.2 Input and Output Comparison
I got stuck here. I consulted an expert in the field, who emphasized that the model's input and output should be exactly the same across platforms. Following this advice, I systematically verified each step of the image preprocessing pipeline.
I tested the same image on both Python and Android:
- The first 10 preprocessed input values were printed in both environments.
- The first 10 output values were also compared.
The results showed that input & output values in Android were both different from Python. So the problem is obviously coming from the input.
4.3.3 Comparison of Image Preprocessing in Python vs. Android
The preprocessing steps for both implementations were compared to identify discrepancies.
Step | Python (Debug Script) | Kotlin (Android Implementation) | Match? |
Image Resizing | image.resize((224, 224), Image.BILINEAR) | Bitmap.createScaledBitmap(bitmap, 224, 224, true) | โ
Yes |
Convert to Float | np.array(image, dtype=np.float32) / 255.0 | ((pixel shr X and 0xFF) / 255.0f) | โ
Yes |
Standardization | (image - mean) / std | ((pixel_component / 255.0f) - mean) / std | โ
Yes |
Reorder Channels | np.transpose(image, (2, 0, 1)) (HWC โ CHW) | Missing explicit channel reordering | โ No |
Add Batch Dimension | np.expand_dims(image, axis=0) | Implicitly handled in ByteBuffer | โ
Yes |
The issue is Identified as missing Channel Reordering:
- The MobileNetV2 model expects input in CHW format (
(C, H, W)
, Channels First).
- Image is loaded in HWC format (
(H, W, C)
, Channels Last).
Python explicitly converts HWC โ CHW using
np.transpose(image, (2, 0, 1)), but
Android did not include this step, leading to incorrect input tensors. After reordering the channel the input values are the same as shown below and the classification is correct. ISSUE SOLVED!Source | First 10 Preprocessed Input Values |
Android (Kotlin) | [2.2489083, 2.2489083, 2.2489083, ...] |
Python (NumPy) | [2.2489083, 2.2489083, 2.2489083, ...] |
5. Git Version Control
5.1 GitHub Login & Setup
Create a repo on Github and connect with Github.
5.2 Checking Repository & Status
Check previous uploaded files on github repo, lists all tracked files currently in the repository.
Remove Old Python and Android Folder.
5.3 Uploading Updated Python and Android Project Files
The new files are verified and reviewed in new environment. They are ready to be used for new user
Push Updates to Remote Repository and verify the change
5.4 Uploading README.md
6 Next Steps
The Android app's loading speed is reasonable but has noticeable delays, leaving room for improvement.
ย
- Author:wenyang
- URL:https://www.wenyang.xyz/article/mlAndroid
- Copyright:All articles in this blog, except for special statements, adopt BY-NC-SA agreement. Please indicate the source!