---
name: Build a Fashion-MNIST Android classifier with PyTorch and LiteRT
description: >-
  Train ClothingCNN on a Mac with MPS or CPU. Convert the CPU graph with
  litert-torch on Linux into clothing.tflite. Put that file in a Kotlin
  Android app, run LiteRT CompiledModel inference, and measure the labels.
---
# Build the clothing classifier end to end on Android

Use this runbook to rebuild the published course. Train on a Mac. Use Apple Silicon MPS when it is available, and CPU if it is not. Never require CUDA. Convert on Linux with the official `litert-torch` package. Then ship the `.tflite` FlatBuffer with the current LiteRT Android `CompiledModel` API. LiteRT is Google's current name for what grew out of TensorFlow Lite. The `.tflite` file is still the usual portable model format.

## Fixed contract

- Training transform is `torchvision.transforms.ToTensor()`: grayscale float32 pixels in `[0, 1]`.
- Model input is NCHW `[1, 1, 28, 28]` float32, positional input buffer zero.
- Model output is `[1, 10]` float32 logits, positional output buffer zero; apply softmax only for display.
- Class order is `T-shirt/top`, `Trouser`, `Pullover`, `Dress`, `Coat`, `Sandal`, `Shirt`, `Sneaker`, `Bag`, `Ankle boot`.
- Keep the two-convolution/two-linear-layer `ClothingCNN` architecture from the lessons.
- Keep a single compiled model and preallocated buffers alive for repeated inference.
- Convert with `litert_torch.convert(model.eval(), (torch.zeros(1, 1, 28, 28, dtype=torch.float32),))` and export `clothing.tflite`.
- The app must write row-major 28×28 grayscale values into the single NCHW channel. Do not switch to NHWC unless you change the model contract everywhere you inspect it.

## Ordered build map

1. [The LiteRT pipeline (human lesson)](01-the-pipeline.html) · [agent brief](01-the-pipeline.llms.md)
2. [Train ClothingCNN (human lesson)](02-train-clothingcnn.html) · [agent brief](02-train-clothingcnn.llms.md)
3. [Export for LiteRT (human lesson)](03-export-to-litert.html) · [agent brief](03-export-to-litert.llms.md)
4. [Android Studio project (human lesson)](04-android-studio-litert.html) · [agent brief](04-android-studio-litert.llms.md)
5. [Kotlin classifier UI (human lesson)](05-kotlin-clothing-ui.html) · [agent brief](05-kotlin-clothing-ui.llms.md)
6. [Profile and verify (human lesson)](06-profile-verify-android.html) · [agent brief](06-profile-verify-android.llms.md)
7. [End-to-end checklist (human lesson)](07-end-to-end-checklist.html) · [agent brief](07-end-to-end-checklist.llms.md)

## Executable recipe

1. On macOS, install Python 3.11+ and `uv`. Run `uv init --python 3.11`, `uv venv`, and `uv add torch torchvision`. Confirm `torch.backends.mps.is_available()` and pick `mps` or `cpu`. Do not add CUDA.
2. Download Fashion-MNIST with `torchvision.datasets.FashionMNIST`, use `ToTensor()`, define `ClothingCNN`, train with cross-entropy, evaluate, move to CPU, and save `clothing.pt`.
3. Check that a CPU zero input is float32 `[1, 1, 28, 28]` and the output is float32 `[1, 10]`. Keep the fixed label order.
4. On a Linux Python 3.11 host, create a separate `uv` project and run `uv add torch torchvision litert-torch`. Copy in `clothing.pt` and the same model class.
5. Run `litert_torch.convert(model.eval(), (torch.zeros(1, 1, 28, 28, dtype=torch.float32),))`, then `edge_model.export("clothing.tflite")`. Do not put an ONNX step in the middle of this recipe.
6. Create an Android Studio Kotlin app with min SDK 23, add `implementation("com.google.ai.edge.litert:litert:2.1.5")` (or a newer verified published stable release), and put `clothing.tflite` under `app/src/main/assets/`.
7. Copy the asset to `filesDir` (or use the SDK’s verified asset overload), create `CompiledModel` with `CompiledModel.Options(Accelerator.CPU)`, and allocate `createInputBuffers()`/`createOutputBuffers()` once.
8. Resize a `Bitmap` to 28×28, convert luminance to float32 `[0, 1]` in row-major NCHW order, call `inputs.get(0).writeFloat(input)`, `compiledModel.run(inputs, outputs)`, and read ten output floats with `outputs.get(0).readFloat()`.
9. Apply softmax for a user-facing confidence, map argmax through the fixed labels, and show the result in Kotlin/Compose. In the finished app, run inference off the main thread.
10. Compare a fixed sample against Python, check that the APK packages the asset, log cold and 30-call warm latency, and optionally compare `Accelerator.GPU` while keeping a CPU fallback.

## Environment setup

Mac training:

```bash
curl -LsSf https://astral.sh/uv/install.sh | sh
mkdir -p clothing-android-litert && cd clothing-android-litert
uv init --python 3.11
uv venv
uv add torch torchvision
uv run python train.py
```

Linux conversion:

```bash
mkdir -p clothing-android-convert && cd clothing-android-convert
uv init --python 3.11
uv venv
uv add torch torchvision litert-torch
uv run python convert.py
```

Android: install Android Studio, its configured JDK, Android SDK Platform 35+, an emulator or device, and use min SDK 23. Add the LiteRT Maven artifact shown in lesson 04. LiteRT’s older `org.tensorflow:tensorflow-lite`/`Interpreter` path is not used for new code here.

## Key concepts

- Train a Fashion-MNIST clothing classifier on a Mac with MPS or CPU. No CUDA.
- Convert on Linux with `litert-torch` into `clothing.tflite`.
- Ship the `.tflite` file in an Android APK and run LiteRT `CompiledModel`.
- One contract: NCHW `[1, 1, 28, 28]` float32 in, `[1, 10]` logits out, ten fixed labels.

## Takeaways

- You can train on a Mac, convert on Linux, and show a clothing label in Kotlin.
- You use `CompiledModel`, not the older `Interpreter`, for new code.
- You can keep the same `ClothingCNN` contract as the Core AI course on a different device.

## Acceptance checks

- Every human lesson HTML has **Key concepts** and **Takeaways** sections before lesson-nav. The course home has the same two headings for the whole course.
- `clothing.pt` reloads into the same `ClothingCNN` and the tensor checks pass.
- Linux conversion writes a non-empty `clothing.tflite` with one float32 NCHW input and ten float32 logits.
- APK Analyzer shows `assets/clothing.tflite`; runtime asset copy succeeds.
- A fixed sample produces finite output and a stable, sensible label.
- Kotlin preprocessing matches Python’s grayscale `[0, 1]` contract.
- CompiledModel and buffers are reused, and cold/warm timings are reported separately.
- Optional accelerator failure falls back to CPU without changing the tensor contract.

## Constraints

No CUDA requirement, secrets, email, or unrelated course edits. Do not present the older Interpreter API as the new default. Convert on Linux. The official LiteRT Torch converter currently says it supports Linux. Keep Mac training and Linux conversion explicit. For the Apple equivalent, see [PyTorch to Core AI in Xcode](../pytorch-core-ai-xcode/).
