Skip to content

Commit 16e399f

Browse files
authored
Epoxrt more zipformer ctc models to qnn (#2921)
1 parent 94a040e commit 16e399f

5 files changed

Lines changed: 329 additions & 23 deletions

File tree

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
#!/usr/bin/env python3
2+
# Copyright 2025 Xiaomi Corp. (authors: Fangjun Kuang)
3+
4+
import json
5+
6+
from device_info import soc_info_dict
7+
from dataclasses import asdict, dataclass
8+
import itertools
9+
10+
11+
@dataclass
12+
class Config:
13+
soc: str # SM8850
14+
soc_id: int # 87
15+
arch: str # v81
16+
input_in_seconds: str
17+
model_name: str
18+
19+
20+
def main():
21+
22+
input_in_seconds = ["5", "8", "10", "13", "15", "18", "20", "23", "25", "28", "30"]
23+
model_name_list = ["20250703", "20251222"]
24+
25+
configs = []
26+
27+
for name, soc in soc_info_dict.items():
28+
for num_seconds, model_name in itertools.product(
29+
input_in_seconds, model_name_list
30+
):
31+
if model_name == "20251222":
32+
if num_seconds not in ["5"]:
33+
# TODO(fangjun): We only upload model-5-seconds.onnx right now
34+
continue
35+
36+
configs.append(
37+
Config(
38+
soc=name,
39+
soc_id=soc.model.value,
40+
arch=soc.info.arch.name,
41+
input_in_seconds=num_seconds,
42+
model_name=model_name,
43+
)
44+
)
45+
46+
ans = [asdict(c) for c in configs]
47+
48+
print(json.dumps({"include": ans}))
49+
50+
51+
if __name__ == "__main__":
52+
main()

.github/workflows/export-zipformer-ctc-to-qnn-20250703.yaml

Lines changed: 235 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,32 +3,58 @@ name: export-zipformer-ctc-to-qnn-20250703
33
on:
44
push:
55
branches:
6-
- qnn-zipformer-ctc-models
6+
- zipformer-qnn-model-2
77
workflow_dispatch:
88

99
concurrency:
1010
group: export-zipformer-ctc-to-qnn-20250703-${{ github.ref }}
1111
cancel-in-progress: true
1212

1313
jobs:
14+
generate_build_matrix:
15+
if: github.repository_owner == 'csukuangfj' || github.repository_owner == 'k2-fsa'
16+
# see https://github.com/pytorch/pytorch/pull/50633
17+
runs-on: ubuntu-latest
18+
outputs:
19+
matrix: ${{ steps.set-matrix.outputs.matrix }}
20+
steps:
21+
- uses: actions/checkout@v4
22+
with:
23+
fetch-depth: 0
24+
25+
- name: Generating build matrix
26+
id: set-matrix
27+
run: |
28+
# outputting for debugging purposes
29+
python3 .github/scripts/export-qnn/generate_zipformer.py
30+
MATRIX=$(python3 .github/scripts/export-qnn/generate_zipformer.py)
31+
32+
# deprecated
33+
# echo "::set-output name=matrix::${MATRIX}"
34+
echo "matrix=$MATRIX" >> $GITHUB_OUTPUT
35+
1436
export-zipformer-ctc-to-qnn-20250703:
37+
needs: generate_build_matrix
1538
if: github.repository_owner == 'k2-fsa' || github.repository_owner == 'csukuangfj'
16-
name: ${{ matrix.input_in_seconds }}
17-
runs-on: ${{ matrix.os }}
39+
name: ${{ matrix.model_name }} ${{ matrix.input_in_seconds }} ${{ matrix.soc }}
40+
runs-on: ubuntu-22.04
1841
strategy:
1942
fail-fast: false
2043
matrix:
21-
os: [ubuntu-22.04]
22-
python-version: ["3.10"]
23-
input_in_seconds: ["5", "8", "10", "13", "15", "18", "20", "23", "25", "28", "30"]
44+
${{ fromJson(needs.generate_build_matrix.outputs.matrix) }}
2445

2546
steps:
2647
- uses: actions/checkout@v4
2748

28-
- name: Setup Python ${{ matrix.python-version }}
49+
- name: Setup Python 3.10
2950
uses: actions/setup-python@v5
3051
with:
31-
python-version: ${{ matrix.python-version }}
52+
python-version: "3.10"
53+
54+
- name: Create directories
55+
shell: bash
56+
run: |
57+
mkdir so binary
3258
3359
- name: Display NDK HOME
3460
shell: bash
@@ -175,6 +201,7 @@ jobs:
175201
qnn-net-run --help
176202
177203
- name: Run ${{ matrix.input_in_seconds }}
204+
if: matrix.model_name == '20250703'
178205
shell: bash
179206
run: |
180207
source py310/bin/activate
@@ -185,6 +212,7 @@ jobs:
185212
186213
export PATH=${ANDROID_NDK_LATEST_HOME}:$PATH
187214
export LDFLAGS="-Wl,-z,max-page-size=16384"
215+
dir=$PWD
188216
189217
mkdir tmp
190218
@@ -244,8 +272,43 @@ jobs:
244272
245273
readelf -lW model_libs/*/lib*.so
246274
275+
echo "Generate context binary"
276+
277+
$dir/scripts/qnn/generate_config.py \
278+
--soc ${{ matrix.soc }} \
279+
--graph-name "model_${t}_seconds_quantized" \
280+
--output-dir ./my-config \
281+
--qnn-sdk-root $QNN_SDK_ROOT
282+
283+
ls -lh my-config
284+
285+
head -n 1000 my-config/*.json
286+
287+
$QNN_SDK_ROOT/bin/x86_64-linux-clang/qnn-context-binary-generator \
288+
--backend $QNN_SDK_ROOT/lib/x86_64-linux-clang/libQnnHtp.so \
289+
--model ./model_libs/x86_64-linux-clang/libmodel-$t-seconds-quantized.so \
290+
--output_dir ./binary \
291+
--binary_file model \
292+
--config_file ./my-config/htp_backend_extensions.json
293+
294+
ls -lh binary/
295+
247296
echo "collect results"
248297
298+
d=sherpa-onnx-qnn-${{ matrix.soc}}-binary-$t-seconds-zipformer-ctc-zh-2025-07-03-int8
299+
mkdir -p $d
300+
mkdir -p $d/test_wavs
301+
cp -v binary/model.bin $d/
302+
cp -v tokens.txt $d
303+
cp -v *.wav $d/test_wavs
304+
echo "num_frames=$num_frames" > $d/info.txt
305+
306+
ls -lh $d
307+
tar cjfv $d.tar.bz2 $d
308+
ls -lh *.tar.bz2
309+
rm -rf $d
310+
mv *.tar.bz2 ../binary/
311+
249312
for p in x86_64-linux-clang aarch64-android; do
250313
if [[ $p == x86_64-linux-clang ]]; then
251314
d=sherpa-onnx-qnn-$t-seconds-zipformer-ctc-zh-2025-07-03-int8-linux-x64
@@ -275,29 +338,188 @@ jobs:
275338
echo "----show---"
276339
ls -lh *.tar.bz2
277340
278-
mv *.tar.bz2 ../
341+
mv *.tar.bz2 ../so
342+
343+
- name: Run ${{ matrix.input_in_seconds }}
344+
if: matrix.model_name == '20251222'
345+
shell: bash
346+
run: |
347+
source py310/bin/activate
348+
349+
pushd qairt/2.33.0.250327/bin
350+
source envsetup.sh
351+
popd
352+
353+
export PATH=${ANDROID_NDK_LATEST_HOME}:$PATH
354+
export LDFLAGS="-Wl,-z,max-page-size=16384"
355+
dir=$PWD
356+
357+
mkdir tmp
358+
359+
cd tmp
360+
361+
t=${{ matrix.input_in_seconds }}
362+
num_frames=$(($t*100))
363+
364+
echo "num_frames: $num_frames"
365+
366+
curl -SL -O https://huggingface.co/csukuangfj/sherpa-onnx-zipformer-ctc-zh-2025-07-03-source-models/resolve/main/generate_test_data.py
367+
curl -SL -O https://huggingface.co/csukuangfj/sherpa-onnx-zipformer-ctc-zh-2025-07-03-source-models/resolve/main/test.py
368+
chmod +x generate_test_data.py
369+
370+
curl -SL -O https://huggingface.co/csukuangfj/sherpa-onnx-zipformer-ctc-zh-2025-07-03-source-models/resolve/main/0.wav
371+
curl -SL -O https://huggingface.co/csukuangfj/sherpa-onnx-zipformer-ctc-zh-2025-07-03-source-models/resolve/main/1.wav
372+
curl -SL -O https://huggingface.co/csukuangfj/sherpa-onnx-zipformer-ctc-zh-2025-07-03-source-models/resolve/main/8k.wav
373+
curl -SL -O https://huggingface.co/csukuangfj/sherpa-onnx-zipformer-ctc-zh-2025-07-03-source-models/resolve/main/tokens.txt
374+
375+
./generate_test_data.py --num-frames $num_frames --wav 0.wav
376+
./generate_test_data.py --num-frames $num_frames --wav 1.wav
377+
./generate_test_data.py --num-frames $num_frames --wav 8k.wav
378+
379+
echo -e "0.raw\n1.raw\n8k.raw" > input_list.txt
380+
381+
curl -SL -O https://huggingface.co/csukuangfj/2025-12-22/resolve/main/zipformer-ctc-models/model-$t-seconds.onnx
382+
383+
python3 ../scripts/pyannote/segmentation/show-onnx.py --filename ./model-$t-seconds.onnx
384+
385+
386+
echo "export to qnn"
387+
echo "----------$t----------"
388+
389+
qnn-onnx-converter \
390+
--input_network model-$t-seconds.onnx \
391+
--output_path ./model-$t-seconds-quantized \
392+
--out_node log_probs \
393+
--input_list ./input_list.txt \
394+
--use_native_input_files \
395+
--input_dtype x float32 \
396+
--act_bitwidth 16 \
397+
--bias_bitwidth 32 \
398+
--input_layout x NTF
399+
400+
ls -lh
401+
mv model-$t-seconds-quantized model-$t-seconds-quantized.cpp
402+
echo "----"
403+
ls -lh
404+
405+
python3 "${QNN_SDK_ROOT}/bin/x86_64-linux-clang/qnn-model-lib-generator" \
406+
-c "model-$t-seconds-quantized.cpp" \
407+
-b "model-$t-seconds-quantized.bin" \
408+
-o model_libs > /dev/null 2>&1
409+
410+
ls -lh model_libs/*/
411+
412+
readelf -lW model_libs/*/lib*.so
413+
414+
echo "Generate context binary"
415+
416+
$dir/scripts/qnn/generate_config.py \
417+
--soc ${{ matrix.soc }} \
418+
--graph-name "model_${t}_seconds_quantized" \
419+
--output-dir ./my-config \
420+
--qnn-sdk-root $QNN_SDK_ROOT
421+
422+
ls -lh my-config
423+
424+
head -n 1000 my-config/*.json
425+
426+
$QNN_SDK_ROOT/bin/x86_64-linux-clang/qnn-context-binary-generator \
427+
--backend $QNN_SDK_ROOT/lib/x86_64-linux-clang/libQnnHtp.so \
428+
--model ./model_libs/x86_64-linux-clang/libmodel-$t-seconds-quantized.so \
429+
--output_dir ./binary \
430+
--binary_file model \
431+
--config_file ./my-config/htp_backend_extensions.json
432+
433+
ls -lh binary/
434+
435+
d=sherpa-onnx-qnn-${{ matrix.soc}}-binary-$t-seconds-zipformer-ctc-zh-2025-12-22-int8
436+
mkdir -p $d
437+
mkdir -p $d/test_wavs
438+
cp -v binary/model.bin $d/
439+
cp -v tokens.txt $d
440+
cp -v *.wav $d/test_wavs
441+
echo "num_frames=$num_frames" > $d/info.txt
442+
443+
ls -lh $d
444+
tar cjfv $d.tar.bz2 $d
445+
ls -lh *.tar.bz2
446+
rm -rf $d
447+
mv *.tar.bz2 ../binary/
448+
449+
echo "collect results"
450+
451+
for p in x86_64-linux-clang aarch64-android; do
452+
if [[ $p == x86_64-linux-clang ]]; then
453+
d=sherpa-onnx-qnn-$t-seconds-zipformer-ctc-zh-2025-12-22-int8-linux-x64
454+
elif [[ $p == aarch64-android ]]; then
455+
d=sherpa-onnx-qnn-$t-seconds-zipformer-ctc-zh-2025-12-22-int8-android-aarch64
456+
else
457+
echo "Unknown $p"
458+
exit -1
459+
fi
460+
461+
mkdir -p $d
462+
mkdir -p $d/test_wavs
463+
464+
cp -v model_libs/$p/lib*.so $d/libmodel.so
465+
cp -v tokens.txt $d
466+
cp -v *.wav $d/test_wavs
467+
468+
echo "num_frames=$num_frames" > $d/info.txt
469+
echo "target=$p" >> $d/info.txt
470+
471+
ls -lh $d
472+
tar cjfv $d.tar.bz2 $d
473+
ls -lh *.tar.bz2
474+
rm -rf $d
475+
done
476+
477+
echo "----show---"
478+
ls -lh *.tar.bz2
479+
480+
mv *.tar.bz2 ../so
279481
280482
- uses: actions/upload-artifact@v4
281483
with:
282-
name: ${{ matrix.input_in_seconds }}-seconds
484+
name: ${{ matrix.model_name }}-${{ matrix.soc }}-${{ matrix.input_in_seconds }}-seconds
283485
path: ./tmp/*.json
284486

487+
- name: Release
488+
if: github.repository_owner == 'csukuangfj' && matrix.soc == 'SM8850'
489+
uses: svenstaro/upload-release-action@v2
490+
with:
491+
file_glob: true
492+
file: ./so/*.tar.bz2
493+
overwrite: true
494+
repo_name: k2-fsa/sherpa-onnx
495+
repo_token: ${{ secrets.UPLOAD_GH_SHERPA_ONNX_TOKEN }}
496+
tag: asr-models-qnn
497+
285498
- name: Release
286499
if: github.repository_owner == 'csukuangfj'
287500
uses: svenstaro/upload-release-action@v2
288501
with:
289502
file_glob: true
290-
file: ./*.tar.bz2
503+
file: ./binary/*.tar.bz2
291504
overwrite: true
292505
repo_name: k2-fsa/sherpa-onnx
293506
repo_token: ${{ secrets.UPLOAD_GH_SHERPA_ONNX_TOKEN }}
507+
tag: asr-models-qnn-binary
508+
509+
- name: Release
510+
if: github.repository_owner == 'k2-fsa' && matrix.soc == 'SM8850'
511+
uses: svenstaro/upload-release-action@v2
512+
with:
513+
file_glob: true
514+
file: ./so/*.tar.bz2
515+
overwrite: true
294516
tag: asr-models-qnn
295517

296518
- name: Release
297519
if: github.repository_owner == 'k2-fsa'
298520
uses: svenstaro/upload-release-action@v2
299521
with:
300522
file_glob: true
301-
file: ./*.tar.bz2
523+
file: ./binary/*.tar.bz2
302524
overwrite: true
303-
tag: asr-models-qnn
525+
tag: asr-models-qnn-binary

android/SherpaOnnxSimulateStreamingAsr/app/src/main/java/com/k2fsa/sherpa/onnx/simulate/streaming/asr/SimulateStreamingAsr.kt

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -147,9 +147,19 @@ object SimulateStreamingAsr {
147147
config.modelConfig.senseVoice.qnnConfig.contextBinary,
148148
context
149149
)
150-
} else if (config.modelConfig.zipformerCtc.model.isNotEmpty()) {
151-
config.modelConfig.zipformerCtc.model =
152-
copyAssetToInternalStorage(config.modelConfig.zipformerCtc.model, context)
150+
} else if (config.modelConfig.zipformerCtc.model.isNotEmpty() ||
151+
assetExists(
152+
context.assets,
153+
path = config.modelConfig.zipformerCtc.qnnConfig.contextBinary
154+
)
155+
) {
156+
if (config.modelConfig.zipformerCtc.model.isNotEmpty()) {
157+
config.modelConfig.zipformerCtc.model =
158+
copyAssetToInternalStorage(
159+
config.modelConfig.zipformerCtc.model,
160+
context
161+
)
162+
}
153163

154164
config.modelConfig.zipformerCtc.qnnConfig.contextBinary =
155165
copyAssetToInternalStorage(

0 commit comments

Comments
 (0)