Litert conversion script?

#1
by geneing - opened

@mlboydaisuke could you please share you code for converting from pytorch to litert for this model. I'm interested in doing some additional optimization for pixel phones.

Hi @geneing — thanks for trying the model, and for the Pixel angle. That is where these graphs have the most room, so I am glad someone is looking at it.

The build script is now in the zoo, next to the sample app and the recipe notes:

To reproduce:

pip install torch==2.12.1 litert-torch==0.9.3 ai-edge-litert==2.1.6 ai-edge-quantizer==0.8.0 sentencepiece safetensors huggingface_hub scipy
git clone https://github.com/kyutai-labs/pocket-tts        # tested at 001cf6e
cd LiteRT-Models/pockettts
PYTHONPATH=/path/to/pocket-tts python scripts/build_pockettts.py all   # or one stage: flowlm | head | fused | dectx | deconly | assets | pipeline

It downloads the ungated kyutai/pocket-tts-without-voice-cloning weights and writes every file in this repo into scripts/out/. I re-ran it today: the outputs are byte-identical to the published files (sha256 of all 20 LFS files), and the parity numbers on the card are printed per stage. About 3 minutes on a Mac.

Two things measured on a Pixel 8a (Mali-G715, LiteRT 2.1.6) that may save you time:

  • The per-frame cost there is the packed-KV upload plus the dispatch count, not arithmetic: one frame was ~43 ms with the split graphs (11 ms KV write, 12 ms run(), 20 ms readback), and fusing the head into the step graph with one output tensor took 1.1 s off an 8 s utterance — that is why the app runs pt_flowlm_fused. The 25 MB KV round-trip per step is the largest remaining term.
  • pt_mimi_dec_tx compiles fully on that GPU, but its output is audibly degraded (alba voicing HNR 0.9 dB vs 2.8 dB on CPU, which matches the fp32 reference), and GpuOptions(precision = FP32) does not recover it — so the app runs that one graph on CPU. A force_gpu.txt containing dectx in the app's files dir puts it back on the GPU for experiments.

If something moves the Pixel numbers, I would be glad to hear about it — and glad to take a PR into the zoo.

Thank you very much!!! LiteRT and onnx conversion from pytorch can be very difficult to do correctly. There are too many places where small errors creep up.

@mlboydaisuke I did a bit of exploration with litert pockettts model on my pixel 10 phone (Tensor G5 processor). Very interesting results.

TLDR: for LM per step GPU synchronization cost is more than compute cost - it makes running on CPU faster. PowerVR GPU is slower than Mali. I was getting only about 0.8x RTF with LM on GPU!

I tried amortizing gpu sync cost by running multiple steps of LLM per invocation (4 and 8 steps). It improved the timing, but CPU was still faster.

I also tested running the model on G5 NPU. mimi_deconly will not compile for NPU - plugin crashed! LM ran almost as fast as on CPU. Big improvement with dec_tx running on NPU - fast and preserved quality.

Optimal configuration LM:CPU DECTX:NPU DEC:GPU. Here are some generated timing tables.

End-to-end per utterance (LM steps 28 prompt + 70 gen)

scenario lm / dectx / dec LM steps/inv RTF total ms LM in / run / read ms dec_tx ms seanet ms
gold_cpu CPU/CPU/CPU 1 0.81× 7292 143 / 1858 / 17 488 4634
lm_npu NPU/CPU/GPU 1 1.29× 4526 654 / 1777 / 55 547 1389
lm_npu_ms4 NPU/CPU/GPU 4 1.18× 4853 331 / 2442 / 79 523 1368
lm_npu_ms8 NPU/CPU/GPU 8 1.21× 4687 225 / 2390 / 33 547 1386
lm_npu_ms4_dectx_npu NPU/NPU/GPU 4 1.31× 4327 310 / 2362 / 67 117 1375
lm_cpu_dectx_npu CPU/NPU/GPU 1 1.53× 3677 164 / 1937 / 12 131 1362
lm_cpu_dec_gpu CPU/CPU/GPU 1 1.37× 4138 159 / 2027 / 11 489 1364
lm_cpu_all_gpu CPU/GPU/GPU 1 1.49× 3815 155 / 2028 / 13 142 1375
shipped_gpu_lm GPU/CPU/GPU 1 0.79× 7113 1195 / 115 / 3765 556 1350
lm_gpu_ms4 GPU/CPU/GPU 4 0.97× 5894 619 / 70 / 3181 546 1366
lm_gpu_ms8 GPU/CPU/GPU 8 1.01× 5596 424 / 64 / 2994 551 1385
lm_gpu32 GPU32/CPU/GPU 1 0.65× 8709 1287 / 129 / 5252 536 1353
all_gpu GPU/GPU/GPU 1 0.86× 6532 1134 / 122 / 3654 135 1357

Micro-benchmark, 128 LM frames. "copy-in" = host→input-tensor write; "readback+sync" = output read, which is where the blocking wait lands.

LM graph accel steps/invocation invocations MB in/inv copy-in ms/step compute ms/step readback+sync ms/step total ms/step ms/invocation
fused_fp16 CPU 1 128 25.2 1.4 19.0 0.15 20.5 20.5
fused_fp16 GPU 1 128 25.2 12.5 1.2 37.1 50.8 50.8
ms4_fp16 GPU 4 32 6.3 2.7 0.44 27.7 30.9 123.4
ms8_fp16 GPU 8 16 3.2 1.5 0.28 27.7 29.5 235.7
fused (fp32) GPU32 1 128 25.2 12.9 1.2 52.7 66.9 66.9
fused_fp16_g5 NPU 1 128 25.2 6.7 17.2 0.50 24.4 24.4
ms4_fp16_g5 NPU 4 32 6.3 2.1 26.9 0.55 29.5 118.0
ms8_fp16_g5 NPU 8 16 3.2 1.1 26.8 0.26 28.2 225.4

Decoder stages

dectx dec dectx ms seanet ms
CPU CPU 488 4634
CPU GPU 489 1364
GPU GPU 135–142 1357–1375
NPU GPU 117–131 1362–1375

Sign up or log in to comment