Skip to content

Export zipformer ctc models to QNN - #2815

Merged
csukuangfj merged 1 commit into
k2-fsa:masterfrom
csukuangfj:qnn-zipformer-ctc-models
Nov 24, 2025
Merged

csukuangfj merged 1 commit into
k2-fsa:masterfrom
csukuangfj:qnn-zipformer-ctc-models

Conversation

@csukuangfj

@csukuangfj csukuangfj commented Nov 24, 2025 •

Copy link
Copy Markdown
Collaborator

You can find the exported models at
https://github.com/k2-fsa/sherpa-onnx/releases/tag/asr-models-qnn

The original onnx model is from
https://k2-fsa.github.io/sherpa/onnx/pretrained_models/offline-ctc/icefall/zipformer.html#sherpa-onnx-zipformer-ctc-zh-int8-2025-07-03-chinese

Summary by CodeRabbit

  • Chores
    • Added CI/CD workflow automation for model export and release pipeline, enabling automated quantized model conversion, building, and artifact distribution to GitHub Releases.

✏️ Tip: You can customize this high-level summary in your review settings.

@gemini-code-assist

Copy link
Copy Markdown

Note

Gemini is unable to generate a summary for this pull request due to the file types involved not being currently supported.

@dosubot dosubot Bot added the size:L This PR changes 100-499 lines, ignoring generated files. label Nov 24, 2025
@coderabbitai

coderabbitai Bot commented Nov 24, 2025 •

Copy link
Copy Markdown

Walkthrough

A GitHub Actions workflow is added that triggers on pushes to the qnn-zipformer-ctc-models branch and manual dispatch. The workflow converts Zipformer CTC models to QNN format using a multi-step pipeline involving environment setup, dependency installation, model conversion, quantization, artifact collection, and conditional release to GitHub Releases.

Changes

Cohort / File(s) Summary
GitHub Actions Workflow
\.github/workflows/export-zipformer-ctc-to-qnn-20250703.yaml
New workflow that exports Zipformer CTC models to QNN format. Defines matrix strategy across OS (ubuntu-22.04) and Python versions (3.10) with various input duration configurations. Performs environment setup, dependency installation, model conversion via qnn-onnx-converter, library building for multiple targets, artifact collection per target, and conditional release to GitHub Releases based on repository owner.

Sequence Diagram

sequenceDiagram
    participant Trigger as Trigger (push/dispatch)
    participant Setup as Environment Setup
    participant Build as Model Conversion
    participant Artifact as Artifact Collection
    participant Release as Conditional Release

    Trigger->>Setup: Workflow triggered
    Setup->>Setup: Checkout code
    Setup->>Setup: Setup Python & NDK
    Setup->>Setup: Create virtualenv
    Setup->>Setup: Install dependencies
    Setup->>Build: Dependencies ready
    Build->>Build: Download models & test data
    Build->>Build: Run qnn-onnx-converter<br/>(quantization pipeline)
    Build->>Build: Build per-target libraries
    Build->>Artifact: Conversion complete
    Artifact->>Artifact: Collect per-target artifacts
    Artifact->>Artifact: Generate tarballs<br/>(per-target dirs)
    Artifact->>Release: Artifacts ready
    alt Repository owner is csukuangfj or k2-fsa
        Release->>Release: Release tar.bz2 packages<br/>to GitHub Releases
    else Other owner
        Release->>Release: Upload artifacts only
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Areas requiring attention:

  • Matrix strategy configuration and concurrency settings with non-fail-fast behavior
  • Model conversion pipeline and qnn-onnx-converter invocation parameters
  • Artifact collection logic across multiple per-target directories and naming conventions
  • Conditional release logic dependent on repository owner verification
  • Environment variable setup, path adjustments (envsetup.sh), and NDK/SDK/toolkit integration
  • Download and caching strategy for models and test data

Suggested labels

size:XL

Poem

🐰 Hoppy Automation!

A workflow hops in, so grand and bright,
Zipformer models convert with might,
QNN format now in sight,
Artifacts bundle, release takes flight,
Automation makes the CI clean and tight! ✨

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately and clearly describes the main change: adding a workflow to export zipformer CTC models to QNN format.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Tip

📝 Customizable high-level summaries are now available in beta!

You can now customize how CodeRabbit generates the high-level summary in your pull requests — including its content, structure, tone, and formatting.

  • Provide your own instructions using the high_level_summary_instructions setting.
  • Format the summary however you like (bullet lists, tables, multi-section layouts, contributor stats, etc.).
  • Use high_level_summary_in_walkthrough to move the summary from the description to the walkthrough section.

Example instruction:

"Divide the high-level summary into five sections:

  1. 📝 Description — Summarize the main change in 50–60 words, explaining what was done.
  2. 📓 References — List relevant issues, discussions, documentation, or related PRs.
  3. 📦 Dependencies & Requirements — Mention any new/updated dependencies, environment variable changes, or configuration updates.
  4. 📊 Contributor Summary — Include a Markdown table showing contributions:
    | Contributor | Lines Added | Lines Removed | Files Changed |
  5. ✔️ Additional Notes — Add any extra reviewer context.
    Keep each section concise (under 200 words) and use bullet or numbered lists for clarity."

Note: This feature is currently in beta for Pro-tier users, and pricing will be announced later.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds a new GitHub Actions workflow to export Zipformer CTC models to QNN (Qualcomm Neural Network) format for deployment on Qualcomm platforms. The workflow processes multiple duration configurations (5-30 seconds) of a Chinese Zipformer CTC ASR model and generates quantized libraries for both Linux x64 and Android aarch64 targets.

Key changes:

  • Automated export pipeline for Zipformer CTC models to QNN format with INT8 quantization
  • Matrix-based execution across 11 different input duration configurations (5-30 seconds)
  • Dual-platform artifact generation (Linux x64 and Android aarch64) for each configuration

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

- uses: actions/upload-artifact@v4
with:
name: ${{ matrix.input_in_seconds }}-seconds
path: ./tmp/*.json

Copilot AI Nov 24, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The artifact upload path ./tmp/*.json doesn't match the expected output. Based on the workflow steps, .json files are not generated. The tar.bz2 files are moved to the parent directory (../) at line 278, but no JSON files are created. Either:

  1. Remove this artifact upload if JSON files are not needed
  2. Update the path to match actual artifacts if JSON files should be generated
Suggested change
path: ./tmp/*.json
path: ./*.tar.bz2

Copilot uses AI. Check for mistakes.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (1)
.github/workflows/export-zipformer-ctc-to-qnn-20250703.yaml (1)

216-216: Validate script path before execution.

Line 216 runs python3 ../scripts/pyannote/segmentation/show-onnx.py with a hardcoded relative path. If the script does not exist, the step will fail without clear diagnostics. Additionally, the current directory is tmp/ (set on line 191), making the path fragile to directory structure changes.

Consider adding a check or using a more explicit path:

+          if [[ ! -f ../scripts/pyannote/segmentation/show-onnx.py ]]; then
+            echo "Error: show-onnx.py script not found at ../scripts/pyannote/segmentation/show-onnx.py"
+            exit 1
+          fi
           python3 ../scripts/pyannote/segmentation/show-onnx.py --filename ./model-$t-seconds.onnx

Alternatively, define the script path as a variable at the beginning of the "Run" step for maintainability.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 16d62b6 and 32918a2.

📒 Files selected for processing (1)
  • .github/workflows/export-zipformer-ctc-to-qnn-20250703.yaml (1 hunks)

Comment on lines +75 to +87
- name: Install linux dependencies
shell: bash
run: |
ls -lh

echo "---"

ls -lh qairt

cd qairt/2.33.0.250327/bin
source envsetup.sh

yes | sudo ${QNN_SDK_ROOT}/bin/check-linux-dependency.sh || true

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Reconsider error suppression in dependency check.

Line 87 uses yes | sudo ... || true, which suppresses all errors (both from the script and from sudo). If the dependency check legitimately fails, this masks the issue and allows the workflow to proceed with potentially missing dependencies.

Consider running the check without automatic suppression first, and only use || true if the script is expected to fail gracefully in certain conditions:

-          yes | sudo ${QNN_SDK_ROOT}/bin/check-linux-dependency.sh || true
+          yes | sudo ${QNN_SDK_ROOT}/bin/check-linux-dependency.sh

If the script is known to return non-zero even on success, add a comment explaining why suppression is necessary and consider checking the actual exit behavior.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- name: Install linux dependencies
shell: bash
run: |
ls -lh
echo "---"
ls -lh qairt
cd qairt/2.33.0.250327/bin
source envsetup.sh
yes | sudo ${QNN_SDK_ROOT}/bin/check-linux-dependency.sh || true
- name: Install linux dependencies
shell: bash
run: |
ls -lh
echo "---"
ls -lh qairt
cd qairt/2.33.0.250327/bin
source envsetup.sh
yes | sudo ${QNN_SDK_ROOT}/bin/check-linux-dependency.sh

Comment on lines +249 to +257
for p in x86_64-linux-clang aarch64-android; do
if [[ $p == x86_64-linux-clang ]]; then
d=sherpa-onnx-qnn-$t-seconds-zipformer-ctc-zh-2025-07-03-int8-linux-x64
elif [[ $p == aarch64-android ]]; then
d=sherpa-onnx-qnn-$t-seconds-zipformer-ctc-zh-2025-07-03-int8-android-aarch64
else
echo "Unknown $p"
exit -1
fi

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Correct the exit code convention.

Line 256 uses exit -1, which is non-standard. The exit code -1 is reinterpreted as 255 by the shell. Use exit 1 for a standard error exit code.

             else
               echo "Unknown $p"
-              exit -1
+              exit 1
             fi
🤖 Prompt for AI Agents
.github/workflows/export-zipformer-ctc-to-qnn-20250703.yaml around lines 249 to
257: the script uses `exit -1` which is non-standard (shell interprets negative
codes as 255); change the exit to a positive standard error code (for example
`exit 1`) so failures are signaled correctly and consistently.

Comment on lines +280 to +283
- uses: actions/upload-artifact@v4
with:
name: ${{ matrix.input_in_seconds }}-seconds
path: ./tmp/*.json

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Fix artifact upload path—workflow produces .tar.bz2 files, not .json files.

The artifact upload attempts to capture ./tmp/*.json, but the workflow generates *.tar.bz2 files (created in tmp/ and moved to the parent directory on line 278). No JSON files are produced, so this upload will find nothing.

Update the artifact path to match what the workflow actually produces:

       - uses: actions/upload-artifact@v4
         with:
           name: ${{ matrix.input_in_seconds }}-seconds
-          path: ./tmp/*.json
+          path: ./*.tar.bz2

Alternatively, if .json files should be captured, clarify the intent and update the main build step accordingly.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- uses: actions/upload-artifact@v4
with:
name: ${{ matrix.input_in_seconds }}-seconds
path: ./tmp/*.json
- uses: actions/upload-artifact@v4
with:
name: ${{ matrix.input_in_seconds }}-seconds
path: ./*.tar.bz2
🤖 Prompt for AI Agents
In .github/workflows/export-zipformer-ctc-to-qnn-20250703.yaml around lines 280
to 283 the artifact upload step is pointing at ./tmp/*.json while the workflow
actually produces .tar.bz2 archives (created in tmp/ and then moved to parent
dir on line 278); update the upload-artifact path to match the produced files
(for example point to the .tar.bz2 location or pattern such as ./*.tar.bz2 or
tmp/*.tar.bz2 depending on where files end up) or, if JSON artifacts were
intended, change the earlier build step to produce JSONs and keep the path
as-is.

Comment on lines +285 to +303
- name: Release
if: github.repository_owner == 'csukuangfj'
uses: svenstaro/upload-release-action@v2
with:
file_glob: true
file: ./*.tar.bz2
overwrite: true
repo_name: k2-fsa/sherpa-onnx
repo_token: ${{ secrets.UPLOAD_GH_SHERPA_ONNX_TOKEN }}
tag: asr-models-qnn

- name: Release
if: github.repository_owner == 'k2-fsa'
uses: svenstaro/upload-release-action@v2
with:
file_glob: true
file: ./*.tar.bz2
overwrite: true
tag: asr-models-qnn

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Deduplicate and clarify the two identical "Release" step names.

Both Release steps (lines 285–295 and 296–303) have the same name, which reduces clarity in the workflow UI and logs. Rename them to distinguish their purposes. Additionally, verify that the file path ./*.tar.bz2 correctly resolves after the "Run" step, since the tarball files are generated inside the tmp/ subdirectory and moved to the parent on line 278.

Apply this diff to rename the steps for clarity:

       - name: Release
+        id: release-csukuangfj
         if: github.repository_owner == 'csukuangfj'
         uses: svenstaro/upload-release-action@v2
         with:
           file_glob: true
           file: ./*.tar.bz2
           overwrite: true
           repo_name: k2-fsa/sherpa-onnx
           repo_token: ${{ secrets.UPLOAD_GH_SHERPA_ONNX_TOKEN }}
           tag: asr-models-qnn

       - name: Release
+        id: release-k2fsa
         if: github.repository_owner == 'k2-fsa'
         uses: svenstaro/upload-release-action@v2
         with:
           file_glob: true
           file: ./*.tar.bz2
           overwrite: true
           tag: asr-models-qnn

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
.github/workflows/export-zipformer-ctc-to-qnn-20250703.yaml lines 285-303: both
Release steps use the identical name and possibly the wrong file glob; rename
each step to be distinct (e.g., "Release (csukuangfj)" and "Release (k2-fsa)")
so the workflow UI/logs clearly show which branch/publisher ran, and update the
uploaded file path to match where the tarballs actually exist after the Run step
(use tmp/*.tar.bz2 or ensure files are moved to the workspace root before using
./*.tar.bz2); keep the existing if conditions and repo/token settings.

@csukuangfj
csukuangfj merged commit 28c27bd into k2-fsa:master Nov 24, 2025
7 checks passed
@csukuangfj
csukuangfj deleted the qnn-zipformer-ctc-models branch November 24, 2025 07:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:L This PR changes 100-499 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants