Skip to content

Commit 7457779

Browse files
authored
CIFAR 10 ExecuTorch model fine-tuning on the Android app (#41)
* Initialized project skeleton * Initialized the app UI in activity_main.xml. * Added the gradle build configurations * Added the image preprocessing methods in the ImageTransformations.kt file * Added the image extraction methods to extract files from the binary files in Cifar10ImageExtractor.kt file * Added the main app logic inside the MainActivity.kt file * Bug fix: Added missing package header in MainActivity.kt file * Bug fix: Updated the target SDK version to ver. 35 for compatibility with newest version of ExecuTorch aar * Removed .idea directory from the repository and updated .gitignore * Added the README file to the repo * Removed unused code from the MainActivity.kt file * Updated the README to include the reference to the CIFAR 10 example in the official ExecuTorch repo * Bug fix: Fixed the filename for the python script in the README * Refactored the code to follow current pattern of the repo * Refactored the code to maintain the current structure of the target repo * Addressed the comments for refactoring the README * Fixed the debug tag in the app * Refactored the README file * Removed unneeded image files from the repository. * Fixed the directory structure * Removed the preexisting files * Refactored and formatted the code * Added the test cases * Clean up: Removed unused theming files from the codebase * Removed the jar file from the repo * Removed the unused theming files * Refactored REDAME to include commands to generate the relevant files and copy the outputs to the destination. * Included the build config file in the README * Removed the check for CMake which is handled by ET installation script
1 parent 3649e1f commit 7457779

29 files changed

+2724
-0
lines changed

.gitignore

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,28 @@ build/
88
*.bin
99
*.model
1010
*.pte
11+
*.ptd
1112

1213
# Xcode
1314
xcuserdata/
1415
.build/
1516
.swiftpm/
1617
*.xcworkspace/
1718
*.xcframework/
19+
*.iml
20+
21+
.gradle
22+
/local.properties
23+
./idea
24+
/.idea/caches
25+
/.idea/libraries
26+
/.idea/modules.xml
27+
/.idea/workspace.xml
28+
/.idea/navEditor.xml
29+
/.idea/assetWizardSettings.xml
30+
.DS_Store
31+
/captures
32+
.externalNativeBuild
33+
.cxx
34+
local.properties
35+
*.aar
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
# ExecuTorch Model Fine-Tuning on Android App
2+
3+
In this tutorial, we will be fine-tuning a CIFAR 10 model on an android app using ExecuTorch.
4+
5+
## Environment Setup
6+
7+
### Android Environment Setup
8+
9+
Ensure that your development environment meets the following requirements:
10+
11+
1. Minimum JDK version: 17
12+
2. Target Android API level: 34 (`Android 14.0`)
13+
3. Required Android SDK components:
14+
- Android SDK Build-Tools 34
15+
- Android SDK Platform-Tools
16+
- Android NDK (Side by side)
17+
- Android SDK Command-line Tools (latest)
18+
4. Install Android Emulator or connect an Android device
19+
5. Ensure the following environment variables are set:
20+
- `JAVA_HOME`
21+
- `ANDROID_NDK`
22+
- `ANDROID_SDK`
23+
24+
**NOTE** For the updated steps for building the dependencies refer to the official repository over [here](https://github.com/pytorch/executorch/blob/main/extension/android/README.md).
25+
26+
### ExecuTorch Environment Setup
27+
28+
To ensure better management of Python environments and packages, it is recommended to use a Python environment management tool such as `conda`, `venv`, or `uv`. For this demonstration, we will use `uv` to set up the Python environment.
29+
30+
To install ExecuTorch in a `uv` Python environment use the following commands:
31+
32+
```bash
33+
$ git clone https://github.com/pytorch/executorch.git --recurse-submodules
34+
$ cd executorch
35+
36+
# [optional] Setup an environment
37+
$ uv venv --seed --prompt et --python 3.10
38+
$ source .venv/bin/activate
39+
40+
# Install build tools and ExecuTorch pip wheel
41+
$ ./install_executorch.sh
42+
43+
# Build ExecuTorch for Android. Output AAR: ./extension/android/executorch_android/build/outputs/aar/executorch_android-debug.aar
44+
$ ./scripts/build_android_library.sh
45+
46+
# Fetch the examples
47+
$ git clone [email protected]:pytorch-labs/executorch-examples.git
48+
49+
# Copy the ExecuTorch AAR into the libs directory
50+
$ mkdir -p executorch-examples/cifar/android/CifarETTrainingDemo/app/libs
51+
$ cp ./extension/android/executorch_android/build/outputs/aar/executorch_android-debug.aar executorch-examples/cifar/android/CifarETTrainingDemo/app/libs/executorch.aar
52+
53+
# Build the data and models
54+
$ mkdir -p executorch-examples/cifar/android/CifarETTrainingDemo/app/src/main/assets/
55+
$ python ./extension/training/examples/CIFAR/main.py --data-dir executorch-examples/cifar/android/CifarETTrainingDemo/app/src/main/assets/ --model-path executorch-examples/cifar/android/CifarETTrainingDemo/app/src/main/assets/cifar10_model.pth --pte-model-path executorch-examples/cifar/android/CifarETTrainingDemo/app/src/main/assets/cifar10_model.pte --split-pte-model-path executorch-examples/cifar/android/CifarETTrainingDemo/app/src/main/assets/cifar10_model_pte_only.pte --save-pt-json executorch-examples/cifar/android/CifarETTrainingDemo/app/src/main/assets/cifar10_pt.json --save-et-json executorch-examples/cifar/android/CifarETTrainingDemo/app/src/main/assets/cifar10_et.json --ptd-model-dir executorch-examples/cifar/android/CifarETTrainingDemo/app/src/main/assets/ --epochs 5 --fine-tune-epochs 10
56+
```
57+
58+
If you run into errors for sdk path, complete the above steps in the [Trouble Shooting](#trouble-shooting) section before proceeding.
59+
60+
## Creation of Android App
61+
62+
1. Start with a clone of this repository and open the project in Android Studio executorch-examples/cifar/android/CifarETTrainingDemo/app/build.gradle.kts.
63+
64+
2. Set the minimum SDK version to `API 34`.
65+
66+
3. Wait for the Gradle sync to complete.
67+
68+
4. Click on run to proceed: ![](./images/Pasted%20image%2020250709170837.png)
69+
70+
5. Click on the fine-tune button and the training begins for `150` epochs![[Pasted image 20250709171210.png]]
71+
72+
**Note:** The training parameters can be tweaked in `MainActivity.kt`
73+
74+
### Summary:
75+
76+
We trained the PyTorch model for `1 epoch` and exported the `.pte` and `.ptd` files. We started with training loss of `2.159132883071899`, training accuracy of `18%`, testing loss of `2.1072397136688235`, and testing accuracy of `29%`
77+
78+
```log
79+
2025-07-09 17:11:46.309 13277-13277 ExecuTorchApp com.example.democifar10 D Starting Epoch 1/150
80+
2025-07-09 17:11:46.309 13277-13277 ExecuTorchApp com.example.democifar10 D Total images to be trained: 500, Number of batches: 125
81+
2025-07-09 17:11:47.237 13277-13428 EGL_emulation com.example.democifar10 D app_time_stats: avg=7.23ms min=2.75ms max=25.70ms count=60
82+
2025-07-09 17:11:47.715 13277-13277 ExecuTorchApp com.example.democifar10 D Epoch [1/150] Loss: 2.159132883071899, Accuracy: 18%, Time: 1.41 s, Time per image: 2.81 ms
83+
2025-07-09 17:11:47.715 13277-13277 ExecuTorchApp com.example.democifar10 D Starting evaluation
84+
2025-07-09 17:11:47.715 13277-13277 ExecuTorchApp com.example.democifar10 D Evaluating model on 100 test images (25 batches) out of 100 total images
85+
2025-07-09 17:11:47.989 13277-13277 ExecuTorchApp com.example.democifar10 D Evaluation complete - Loss: 2.1072397136688235, Accuracy: 29.0%, Time: 0.27 s, Time per image: 2.73 ms
86+
```
87+
88+
We reached a training loss of `1.7837489886283875`, training accuracy of `35%`, testing loss of `2.0501016211509704`, and testing accuracy of `38%` after `150 epochs`
89+
90+
```log
91+
2025-07-09 17:13:59.889 13277-13277 ExecuTorchApp com.example.democifar10 D Starting Epoch 150/150
92+
2025-07-09 17:13:59.889 13277-13277 ExecuTorchApp com.example.democifar10 D Total images to be trained: 500, Number of batches: 125
93+
2025-07-09 17:14:00.666 13277-13277 ExecuTorchApp com.example.democifar10 D Epoch [150/150] Loss: 1.7837489886283875, Accuracy: 35%, Time: 0.78 s, Time per image: 1.56 ms
94+
2025-07-09 17:14:00.666 13277-13277 ExecuTorchApp com.example.democifar10 D Starting evaluation
95+
2025-07-09 17:14:00.666 13277-13277 ExecuTorchApp com.example.democifar10 D Evaluating model on 100 test images (25 batches) out of 100 total images
96+
2025-07-09 17:14:00.809 13277-13277 ExecuTorchApp com.example.democifar10 D Evaluation complete - Loss: 1.7236163129806519, Accuracy: 38%, Time: 0.67 s, Time per image: 1.35 ms
97+
```
98+
99+
### Trouble Shooting
100+
101+
Check if the `local.properties` file is present in the `extension/android` directory:
102+
103+
```bash
104+
$ cat ./extension/android/local.properties
105+
```
106+
107+
**NOTE:** If this file (`local.properties`) is missing, the build process will fail because the script can't retrieve the path for the android sdk from the env variables. If you encounter this issue, please follow the following steps:
108+
109+
```bash
110+
$ touch ./extension/android/local.properties
111+
$ vim ./extension/android/local.properties
112+
# Add the path to your sdk directory into this file like: sdk.dir=/Users/<USERNAME>/Library/Android/sdk
113+
```
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/build
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
plugins {
2+
alias(libs.plugins.android.application)
3+
alias(libs.plugins.kotlin.android)
4+
alias(libs.plugins.kotlin.compose)
5+
}
6+
7+
android {
8+
namespace = "com.example.democifar10"
9+
compileSdk = 35
10+
11+
defaultConfig {
12+
applicationId = "com.example.democifar10"
13+
minSdk = 35
14+
targetSdk = 35
15+
versionCode = 1
16+
versionName = "1.0"
17+
18+
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
19+
}
20+
21+
buildTypes {
22+
release {
23+
isMinifyEnabled = false
24+
proguardFiles(
25+
getDefaultProguardFile("proguard-android-optimize.txt"),
26+
"proguard-rules.pro"
27+
)
28+
}
29+
}
30+
compileOptions {
31+
sourceCompatibility = JavaVersion.VERSION_1_8
32+
targetCompatibility = JavaVersion.VERSION_1_8
33+
}
34+
kotlinOptions {
35+
jvmTarget = "1.8"
36+
}
37+
buildFeatures {
38+
compose = true
39+
}
40+
composeOptions {
41+
kotlinCompilerExtensionVersion = "1.5.1"
42+
}
43+
packaging {
44+
resources {
45+
excludes += "/META-INF/{AL2.0,LGPL2.1}"
46+
}
47+
}
48+
}
49+
50+
dependencies {
51+
52+
implementation(libs.androidx.core.ktx)
53+
implementation(libs.androidx.lifecycle.runtime.ktx)
54+
implementation(libs.androidx.activity.compose)
55+
implementation(platform(libs.androidx.compose.bom))
56+
implementation("androidx.appcompat:appcompat:1.6.1")
57+
implementation(libs.androidx.ui)
58+
implementation(libs.androidx.ui.graphics)
59+
implementation(libs.androidx.ui.tooling.preview)
60+
implementation(libs.androidx.material3)
61+
implementation(fileTree(mapOf("dir" to "libs", "include" to listOf("*.jar", "*.aar"))))
62+
// Add SoLoader dependency for PyTorch native library loading
63+
implementation("com.facebook.soloader:soloader:0.10.5")
64+
// Add Facebook JNI dependency required by executorch
65+
implementation("com.facebook.fbjni:fbjni:0.5.1")
66+
testImplementation(libs.junit)
67+
androidTestImplementation(libs.androidx.junit)
68+
androidTestImplementation(libs.androidx.espresso.core)
69+
androidTestImplementation(platform(libs.androidx.compose.bom))
70+
androidTestImplementation(libs.androidx.ui.test.junit4)
71+
debugImplementation(libs.androidx.ui.tooling)
72+
debugImplementation(libs.androidx.ui.test.manifest)
73+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# Add project specific ProGuard rules here.
2+
# You can control the set of applied configuration files using the
3+
# proguardFiles setting in build.gradle.
4+
#
5+
# For more details, see
6+
# http://developer.android.com/guide/developing/tools/proguard.html
7+
8+
# If your project uses WebView with JS, uncomment the following
9+
# and specify the fully qualified class name to the JavaScript interface
10+
# class:
11+
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12+
# public *;
13+
#}
14+
15+
# Uncomment this to preserve the line number information for
16+
# debugging stack traces.
17+
#-keepattributes SourceFile,LineNumberTable
18+
19+
# If you keep the line number information, uncomment this to
20+
# hide the original source file name.
21+
#-renamesourcefileattribute SourceFile

0 commit comments

Comments
 (0)