From 0347d8572dee2a698b208450d2f89b423d65dd2f Mon Sep 17 00:00:00 2001 From: zhifu gao Date: Wed, 9 Sep 2026 16:47:24 +0000 Subject: [PATCH] docs: make native Transformers the Nano starting point Signed-off-by: zhifu gao --- .../native-transformers-examples.yml | 28 +++++ README.md | 44 ++++++- README_ja.md | 44 ++++++- README_ko.md | 44 ++++++- README_zh.md | 42 ++++++- examples/README.md | 13 +- .../colab/fun_asr_nano_transformers.ipynb | 115 ++++++++++++++++++ examples/transformers/README.md | 84 +++++++++++++ examples/transformers/requirements.txt | 8 ++ examples/transformers/transcribe.py | 102 ++++++++++++++++ tests/test_funasr_requirement.py | 4 +- tests/test_transformers_quickstart.py | 71 +++++++++++ 12 files changed, 588 insertions(+), 11 deletions(-) create mode 100644 .github/workflows/native-transformers-examples.yml create mode 100644 examples/colab/fun_asr_nano_transformers.ipynb create mode 100644 examples/transformers/README.md create mode 100644 examples/transformers/requirements.txt create mode 100644 examples/transformers/transcribe.py create mode 100644 tests/test_transformers_quickstart.py diff --git a/.github/workflows/native-transformers-examples.yml b/.github/workflows/native-transformers-examples.yml new file mode 100644 index 00000000..7ec11660 --- /dev/null +++ b/.github/workflows/native-transformers-examples.yml @@ -0,0 +1,28 @@ +name: Native Transformers examples + +on: + pull_request: + paths: ['README*.md', 'examples/**', 'tests/**', '.github/workflows/native-transformers-examples.yml'] + push: + branches: [main, 'codex/transformers-first-*'] + paths: ['README*.md', 'examples/**', 'tests/**', '.github/workflows/native-transformers-examples.yml'] + +permissions: + contents: read + +jobs: + offline-contracts: + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: '3.12' + - name: Install model-free test dependencies + run: python -m pip install 'numpy==1.26.4' 'librosa==0.11.0' 'soundfile==0.13.1' 'pytest==8.4.2' + - name: Validate examples and documentation without downloading weights + run: >- + python -m pytest -q tests/test_transformers_quickstart.py + tests/test_examples_smoke.py tests/test_funasr_requirement.py + tests/test_timestamp_documentation.py tests/test_moss_ecosystem_docs.py diff --git a/README.md b/README.md index cc186f66..042d595d 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ Model repositories: **Fun-ASR-Nano** ([ModelScope](https://www.modelscope.cn/mod Online Experience: [ModelScope Community Space](https://modelscope.cn/studios/FunAudioLLM/Fun-ASR-Nano), [huggingface space](https://huggingface.co/spaces/FunAudioLLM/Fun-ASR-Nano) -[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/QwenAudio/Fun-ASR/blob/main/examples/colab/fun_asr_nano_quickstart.ipynb) +[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/QwenAudio/Fun-ASR/blob/main/examples/colab/fun_asr_nano_transformers.ipynb) [Runnable examples](examples/README.md) cover quickstart inference, direct inference, speaker diarization, vLLM batch inference, and the streaming SDK. @@ -38,10 +38,50 @@ Online Experience: # What's New 🔥 -- **FunASR 1.4.14** is the current Python release for source installs, MOSS discovery, and realtime or industrial deployment. Install with `python -m pip install -U "funasr==1.4.14"`. [Release ->](https://github.com/modelscope/FunASR/releases/tag/v1.4.14) +- **FunASR 1.4.15** is the current Python release for source installs, MOSS discovery, and realtime or industrial deployment. Install with `python -m pip install -U "funasr==1.4.15"`. [Release ->](https://github.com/modelscope/FunASR/releases/tag/v1.4.15) - **MOSS-Transcribe-Diarize** is a third-party OpenMOSS model for offline long-form transcription, timestamps, and anonymous speaker labels, with FunASR service, Docker, Kubernetes, vLLM, SGLang, LocalAI, and FunClip deployment paths. [Deploy MOSS ->](https://www.funasr.com/deploy/moss-transcribe-diarize.html) - **Production deployment** covers realtime WebSocket serving, native vLLM batch/streaming paths, and verified llama.cpp / GGUF packages for Linux, macOS, and Windows. [Runtime v0.2.6 ->](https://github.com/modelscope/FunASR/releases/tag/runtime-llamacpp-v0.2.6) · [vLLM guide ->](docs/vllm_guide.md) +# Native Transformers quickstart + +Transcribe with the released Transformers 5.17.0 package. No toolkit installation or remote Python code is needed. Base Nano supports Chinese, English and Japanese; the 31-language MLT checkpoint is separate. + +```bash +python -m pip install 'transformers==5.17.0' 'torch==2.10.0' 'torchaudio==2.10.0' 'librosa==0.11.0' 'soundfile==0.13.1' +``` + +[Full Python recipe](https://www.funasr.com/en/docs/native-transformers.html) · [Local audio, batches and keywords](examples/transformers/) · [Notebook](examples/colab/fun_asr_nano_transformers.ipynb) · [Space](https://huggingface.co/spaces/FunAudioLLM/Fun-ASR-Nano) + +```python +import torch +from transformers import AutoModelForSpeechSeq2Seq, AutoProcessor + +torch.set_num_threads(4) +model_id = "FunAudioLLM/Fun-ASR-Nano-2512-hf" +revision = "d93b302ee7fd505e1b3576120fc142fc6f7820e1" +audio = "https://huggingface.co/FunAudioLLM/Fun-ASR-Nano-2512/resolve/272c57b82523ada6fd87095e955f8e29100979ab/example/en.mp3" + +processor = AutoProcessor.from_pretrained( + model_id, revision=revision, trust_remote_code=False, token=False +) +model = AutoModelForSpeechSeq2Seq.from_pretrained( + model_id, revision=revision, trust_remote_code=False, token=False, + dtype=torch.float32, +).to("cpu").eval() +inputs = processor.apply_transcription_request( + audio=audio, language="en", + processor_kwargs={ + "return_tensors": "pt", + "audio_kwargs": {"sampling_rate": 16000}, + "text_kwargs": {"padding": True}, + }, +) +with torch.inference_mode(): + generated = model.generate(**inputs, max_new_tokens=128, do_sample=False) +new_tokens = generated[:, inputs.input_ids.shape[1]:] +print(processor.batch_decode(new_tokens, skip_special_tokens=True)[0]) +``` + # Core Features 🎯 **Fun-ASR** focuses on high-precision speech recognition, checkpoint-specific multilingual support, and industry customization capabilities. diff --git a/README_ja.md b/README_ja.md index 441b70a3..aea68065 100644 --- a/README_ja.md +++ b/README_ja.md @@ -2,7 +2,7 @@ 「[简体中文](README_zh.md)」|「[English](README.md)」|「日本語」 -> **FunASR 1.4.14:** 現在の Python リリースで、source install、MOSS の導線、realtime / industrial deployment を提供します。`python -m pip install -U "funasr==1.4.14"`。[Release ->](https://github.com/modelscope/FunASR/releases/tag/v1.4.14) · [Runtime v0.2.6 ->](https://github.com/modelscope/FunASR/releases/tag/runtime-llamacpp-v0.2.6) +> **FunASR 1.4.15:** 現在の Python リリースで、source install、MOSS の導線、realtime / industrial deployment を提供します。`python -m pip install -U "funasr==1.4.15"`。[Release ->](https://github.com/modelscope/FunASR/releases/tag/v1.4.15) · [Runtime v0.2.6 ->](https://github.com/modelscope/FunASR/releases/tag/runtime-llamacpp-v0.2.6) > **MOSS-Transcribe-Diarize:** OpenMOSS の第三者モデルで、オフライン長時間転写、timestamp、匿名 speaker label を一度に処理します。FunASR service、Docker、Kubernetes、vLLM、SGLang、LocalAI、FunClip のデプロイパスを利用できます。[MOSS をデプロイ ->](https://www.funasr.com/deploy/moss-transcribe-diarize.html) @@ -27,7 +27,7 @@ Fun-ASRは通義実験室が開発したエンドツーエンド音声認識モ オンラインデモ: [ModelScope Space](https://modelscope.cn/studios/FunAudioLLM/Fun-ASR-Nano)、[HuggingFace Space](https://huggingface.co/spaces/FunAudioLLM/Fun-ASR-Nano) -[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/QwenAudio/Fun-ASR/blob/main/examples/colab/fun_asr_nano_quickstart.ipynb) +[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/QwenAudio/Fun-ASR/blob/main/examples/colab/fun_asr_nano_transformers.ipynb) [実行可能なサンプル](examples/README.md) では、クイックスタート推論、直接推論、話者分離、vLLM バッチ推論、Streaming SDK を確認できます。 @@ -40,6 +40,46 @@ Fun-ASRは通義実験室が開発したエンドツーエンド音声認識モ CPU/エッジ端末では、Fun-ASR-Nano を llama.cpp / GGUF ランタイムで単一バイナリとして実行できます(Python/GPU 不要、内蔵 FSMN-VAD)。[funasr.com/llama-cpp](https://www.funasr.com/llama-cpp.html) · [Nano GGUF](https://huggingface.co/FunAudioLLM/Fun-ASR-Nano-GGUF) · [FSMN-VAD GGUF](https://huggingface.co/FunAudioLLM/fsmn-vad-GGUF) +# Transformers ネイティブクイックスタート + +リリース済み Transformers 5.17.0 で音声を文字起こしできます。FunASR toolkit やリモート Python コードは不要です。Nano は中国語・英語・日本語に対応し、31 言語の MLT は別 checkpoint です。 + +```bash +python -m pip install 'transformers==5.17.0' 'torch==2.10.0' 'torchaudio==2.10.0' 'librosa==0.11.0' 'soundfile==0.13.1' +``` + +[Python ガイド](https://www.funasr.com/en/docs/native-transformers.html) · [ローカル音声・バッチ・キーワード](examples/transformers/) · [Notebook](examples/colab/fun_asr_nano_transformers.ipynb) · [Space](https://huggingface.co/spaces/FunAudioLLM/Fun-ASR-Nano) + +```python +import torch +from transformers import AutoModelForSpeechSeq2Seq, AutoProcessor + +torch.set_num_threads(4) +model_id = "FunAudioLLM/Fun-ASR-Nano-2512-hf" +revision = "d93b302ee7fd505e1b3576120fc142fc6f7820e1" +audio = "https://huggingface.co/FunAudioLLM/Fun-ASR-Nano-2512/resolve/272c57b82523ada6fd87095e955f8e29100979ab/example/en.mp3" + +processor = AutoProcessor.from_pretrained( + model_id, revision=revision, trust_remote_code=False, token=False +) +model = AutoModelForSpeechSeq2Seq.from_pretrained( + model_id, revision=revision, trust_remote_code=False, token=False, + dtype=torch.float32, +).to("cpu").eval() +inputs = processor.apply_transcription_request( + audio=audio, language="en", + processor_kwargs={ + "return_tensors": "pt", + "audio_kwargs": {"sampling_rate": 16000}, + "text_kwargs": {"padding": True}, + }, +) +with torch.inference_mode(): + generated = model.generate(**inputs, max_new_tokens=128, do_sample=False) +new_tokens = generated[:, inputs.input_ids.shape[1]:] +print(processor.batch_decode(new_tokens, skip_special_tokens=True)[0]) +``` + # 主要機能 🎯 diff --git a/README_ko.md b/README_ko.md index 0a781f90..8ef517c0 100644 --- a/README_ko.md +++ b/README_ko.md @@ -2,7 +2,7 @@ 「[简体中文](README_zh.md)」|「[English](README.md)」|「[日本語](README_ja.md)」|「한국어」 -> **FunASR 1.4.14:** 현재 Python 릴리스로 source install, MOSS 탐색 경로, realtime / industrial deployment를 제공합니다. `python -m pip install -U "funasr==1.4.14"`. [Release ->](https://github.com/modelscope/FunASR/releases/tag/v1.4.14) · [Runtime v0.2.6 ->](https://github.com/modelscope/FunASR/releases/tag/runtime-llamacpp-v0.2.6) +> **FunASR 1.4.15:** 현재 Python 릴리스로 source install, MOSS 탐색 경로, realtime / industrial deployment를 제공합니다. `python -m pip install -U "funasr==1.4.15"`. [Release ->](https://github.com/modelscope/FunASR/releases/tag/v1.4.15) · [Runtime v0.2.6 ->](https://github.com/modelscope/FunASR/releases/tag/runtime-llamacpp-v0.2.6) > **MOSS-Transcribe-Diarize:** OpenMOSS의 서드파티 모델로 오프라인 장시간 전사, timestamp, 익명 speaker label을 한 번에 처리합니다. FunASR service, Docker, Kubernetes, vLLM, SGLang, LocalAI, FunClip 배포 경로를 사용할 수 있습니다. [MOSS 배포 ->](https://www.funasr.com/deploy/moss-transcribe-diarize.html) @@ -27,7 +27,7 @@ Fun-ASR는 통의(Tongyi) 실험실에서 개발한 엔드투엔드 음성 인 온라인 체험: [ModelScope Space](https://modelscope.cn/studios/FunAudioLLM/Fun-ASR-Nano), [HuggingFace Space](https://huggingface.co/spaces/FunAudioLLM/Fun-ASR-Nano) -[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/QwenAudio/Fun-ASR/blob/main/examples/colab/fun_asr_nano_quickstart.ipynb) +[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/QwenAudio/Fun-ASR/blob/main/examples/colab/fun_asr_nano_transformers.ipynb) [실행 가능한 예제](examples/README.md)는 quickstart 추론, 직접 추론, 화자 분리, vLLM 배치 추론, Streaming SDK를 다룹니다. @@ -40,6 +40,46 @@ Fun-ASR는 통의(Tongyi) 실험실에서 개발한 엔드투엔드 음성 인 CPU/엣지 환경에서는 Fun-ASR-Nano를 llama.cpp / GGUF 런타임으로 단일 바이너리 실행할 수 있습니다(Python/GPU 불필요, FSMN-VAD 내장). 이 GGUF 경로는 Nano의 중국어·영어·일본어 및 중국어 방언 범위에 해당하며, 한국어 인식은 위의 MLT-Nano/FunASR GPU 경로를 사용하세요. [funasr.com/llama-cpp](https://www.funasr.com/llama-cpp.html) · [Nano GGUF](https://huggingface.co/FunAudioLLM/Fun-ASR-Nano-GGUF) · [FSMN-VAD GGUF](https://huggingface.co/FunAudioLLM/fsmn-vad-GGUF) +# Transformers 네이티브 빠른 시작 + +정식 Transformers 5.17.0으로 음성을 전사합니다. FunASR toolkit 설치나 원격 Python 코드가 필요 없습니다. 기본 Nano는 중국어·영어·일본어를 지원하며 31개 언어의 MLT는 별도 checkpoint입니다. + +```bash +python -m pip install 'transformers==5.17.0' 'torch==2.10.0' 'torchaudio==2.10.0' 'librosa==0.11.0' 'soundfile==0.13.1' +``` + +[Python 가이드](https://www.funasr.com/en/docs/native-transformers.html) · [로컬 오디오·배치·키워드](examples/transformers/) · [Notebook](examples/colab/fun_asr_nano_transformers.ipynb) · [Space](https://huggingface.co/spaces/FunAudioLLM/Fun-ASR-Nano) + +```python +import torch +from transformers import AutoModelForSpeechSeq2Seq, AutoProcessor + +torch.set_num_threads(4) +model_id = "FunAudioLLM/Fun-ASR-Nano-2512-hf" +revision = "d93b302ee7fd505e1b3576120fc142fc6f7820e1" +audio = "https://huggingface.co/FunAudioLLM/Fun-ASR-Nano-2512/resolve/272c57b82523ada6fd87095e955f8e29100979ab/example/en.mp3" + +processor = AutoProcessor.from_pretrained( + model_id, revision=revision, trust_remote_code=False, token=False +) +model = AutoModelForSpeechSeq2Seq.from_pretrained( + model_id, revision=revision, trust_remote_code=False, token=False, + dtype=torch.float32, +).to("cpu").eval() +inputs = processor.apply_transcription_request( + audio=audio, language="en", + processor_kwargs={ + "return_tensors": "pt", + "audio_kwargs": {"sampling_rate": 16000}, + "text_kwargs": {"padding": True}, + }, +) +with torch.inference_mode(): + generated = model.generate(**inputs, max_new_tokens=128, do_sample=False) +new_tokens = generated[:, inputs.input_ids.shape[1]:] +print(processor.batch_decode(new_tokens, skip_special_tokens=True)[0]) +``` + # 주요 기능 🎯 diff --git a/README_zh.md b/README_zh.md index 90c57d4d..ab5cb5e3 100644 --- a/README_zh.md +++ b/README_zh.md @@ -36,10 +36,50 @@ Fun-ASR 是通义实验室推出的端到端语音识别模型家族,不同 ch # 最新动态 🔥 -- **FunASR 1.4.14** 是当前 Python 发布版,覆盖源码安装、MOSS 发现与实时/工业部署。安装命令:`python -m pip install -U "funasr==1.4.14"`。[发布说明 ->](https://github.com/modelscope/FunASR/releases/tag/v1.4.14) +- **FunASR 1.4.15** 是当前 Python 发布版,覆盖源码安装、MOSS 发现与实时/工业部署。安装命令:`python -m pip install -U "funasr==1.4.15"`。[发布说明 ->](https://github.com/modelscope/FunASR/releases/tag/v1.4.15) - **MOSS-Transcribe-Diarize** 是 OpenMOSS 的第三方模型,可离线完成长音频转写、时间戳和匿名说话人标签;FunASR 已提供服务、Docker、Kubernetes、vLLM、SGLang、LocalAI 与 FunClip 部署路径。[部署 MOSS ->](https://www.funasr.com/deploy/moss-transcribe-diarize.html) - **工业部署** 覆盖实时 WebSocket 服务、原生 vLLM 批量/流式路径,以及已校验的 Linux、macOS、Windows llama.cpp / GGUF 包。[Runtime v0.2.6 ->](https://github.com/modelscope/FunASR/releases/tag/runtime-llamacpp-v0.2.6) · [vLLM 指南 ->](docs/vllm_guide_zh.md) +# Transformers 原生快速开始 + +直接使用已发布的 Transformers 5.17.0,不需要安装 FunASR 工具库或执行模型仓库的远程 Python 代码。基础 Nano 支持中、英、日;31 语言 MLT 是另一个 checkpoint。 + +```bash +python -m pip install 'transformers==5.17.0' 'torch==2.10.0' 'torchaudio==2.10.0' 'librosa==0.11.0' 'soundfile==0.13.1' +``` + +[完整 Python 示例](https://www.funasr.com/docs/native-transformers.html) · [本地音频、批处理与热词](examples/transformers/) · [Notebook](examples/colab/fun_asr_nano_transformers.ipynb) · [Space](https://huggingface.co/spaces/FunAudioLLM/Fun-ASR-Nano) + +```python +import torch +from transformers import AutoModelForSpeechSeq2Seq, AutoProcessor + +torch.set_num_threads(4) +model_id = "FunAudioLLM/Fun-ASR-Nano-2512-hf" +revision = "d93b302ee7fd505e1b3576120fc142fc6f7820e1" +audio = "https://huggingface.co/FunAudioLLM/Fun-ASR-Nano-2512/resolve/272c57b82523ada6fd87095e955f8e29100979ab/example/en.mp3" + +processor = AutoProcessor.from_pretrained( + model_id, revision=revision, trust_remote_code=False, token=False +) +model = AutoModelForSpeechSeq2Seq.from_pretrained( + model_id, revision=revision, trust_remote_code=False, token=False, + dtype=torch.float32, +).to("cpu").eval() +inputs = processor.apply_transcription_request( + audio=audio, language="en", + processor_kwargs={ + "return_tensors": "pt", + "audio_kwargs": {"sampling_rate": 16000}, + "text_kwargs": {"padding": True}, + }, +) +with torch.inference_mode(): + generated = model.generate(**inputs, max_new_tokens=128, do_sample=False) +new_tokens = generated[:, inputs.input_ids.shape[1]:] +print(processor.batch_decode(new_tokens, skip_special_tokens=True)[0]) +``` + # 核心特性 🎯 **Fun-ASR** 专注于高精度语音识别、checkpoint 级多语言支持和行业定制化能力。 diff --git a/examples/README.md b/examples/README.md index ee9ecf4b..36c8dd39 100644 --- a/examples/README.md +++ b/examples/README.md @@ -1,7 +1,16 @@ # Runnable examples -These scripts mirror the main README snippets and are intended to run from a -fresh clone. Install the base requirements first: +## Native Transformers (start here for the Hugging Face API) + +Use the [native quickstart](transformers/) for the released `transformers==5.17.0` +package and the official `Fun-ASR-Nano-2512-hf` checkpoint. It includes a tested +CPU script, local audio, batching, keywords and an [upload notebook](colab/fun_asr_nano_transformers.ipynb). +This path does not require the FunASR toolkit or repository-local remote code. + +## FunASR toolkit examples + +The remaining scripts use the original toolkit checkpoint and a separate +environment. Install the base requirements for these scripts only: ```bash pip install -r requirements.txt diff --git a/examples/colab/fun_asr_nano_transformers.ipynb b/examples/colab/fun_asr_nano_transformers.ipynb new file mode 100644 index 00000000..cdf7f4b8 --- /dev/null +++ b/examples/colab/fun_asr_nano_transformers.ipynb @@ -0,0 +1,115 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Fun-ASR-Nano with native Transformers\n", + "\n", + "Chinese, English and Japanese transcription, without the FunASR toolkit or remote Python code. This notebook uses CPU float32, pinned Transformers 5.17.0 and the official native checkpoint. The first run downloads about 1.66 GB of weights. Colab runtimes change: verify dependency output before loading a model.\n", + "\n", + "[Model card](https://huggingface.co/FunAudioLLM/Fun-ASR-Nano-2512-hf) | [Guide](https://www.funasr.com/en/docs/native-transformers.html) | [Fun-ASR](https://github.com/QwenAudio/Fun-ASR)\n" + ], + "id": "native-01" + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%pip install --index-url https://download.pytorch.org/whl/cpu 'torch==2.10.0+cpu' 'torchaudio==2.10.0+cpu'\n", + "%pip install 'transformers==5.17.0' 'librosa==0.11.0' 'soundfile==0.13.1'\n", + "%pip check\n" + ], + "id": "native-02" + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Transcribe the official English sample\n", + "Restart the notebook session after installation if it had already imported Torch or Transformers. Then run the following cell. This uses the original model repository only for its public audio sample; inference weights come from the separate native `-hf` checkpoint.\n" + ], + "id": "native-03" + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import torch\n", + "from transformers import AutoModelForSpeechSeq2Seq, AutoProcessor\n", + "\n", + "torch.set_num_threads(4)\n", + "model_id = \"FunAudioLLM/Fun-ASR-Nano-2512-hf\"\n", + "revision = \"d93b302ee7fd505e1b3576120fc142fc6f7820e1\"\n", + "audio = \"https://huggingface.co/FunAudioLLM/Fun-ASR-Nano-2512/resolve/272c57b82523ada6fd87095e955f8e29100979ab/example/en.mp3\"\n", + "\n", + "processor = AutoProcessor.from_pretrained(\n", + " model_id, revision=revision, trust_remote_code=False, token=False\n", + ")\n", + "model = AutoModelForSpeechSeq2Seq.from_pretrained(\n", + " model_id, revision=revision, trust_remote_code=False, token=False,\n", + " dtype=torch.float32,\n", + ").to(\"cpu\").eval()\n", + "inputs = processor.apply_transcription_request(\n", + " audio=audio, language=\"en\",\n", + " processor_kwargs={\n", + " \"return_tensors\": \"pt\",\n", + " \"audio_kwargs\": {\"sampling_rate\": 16000},\n", + " \"text_kwargs\": {\"padding\": True},\n", + " },\n", + ")\n", + "with torch.inference_mode():\n", + " generated = model.generate(**inputs, max_new_tokens=128, do_sample=False)\n", + "new_tokens = generated[:, inputs.input_ids.shape[1]:]\n", + "print(processor.batch_decode(new_tokens, skip_special_tokens=True)[0])\n" + ], + "id": "native-04" + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Transcribe an uploaded recording\n", + "Upload a short recording you are authorized to process to the notebook's Files panel. Set its path below. Audio is averaged to mono and explicitly resampled to 16 kHz; it is not renamed or trimmed. This short-file example rejects audio longer than 60 seconds. Use `zh`, `en` or `ja` to match the recording.\n" + ], + "id": "native-05" + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import librosa\nimport numpy as np\nimport soundfile as sf\n\npath = \"/content/audio.wav\"\ninfo = sf.info(path)\nif not 0 < info.duration <= 60:\n raise ValueError(\"Use a non-empty recording of at most 60 seconds\")\nwaveform, sample_rate = sf.read(path, dtype=\"float32\")\nif not np.isfinite(waveform).all():\n raise ValueError(\"Non-finite audio samples\")\nif waveform.ndim == 2:\n waveform = waveform.mean(axis=1)\nif sample_rate != 16000:\n waveform = librosa.resample(waveform, orig_sr=sample_rate, target_sr=16000)\ninputs = processor.apply_transcription_request(\n audio=waveform, language=\"zh\",\n processor_kwargs={\"return_tensors\": \"pt\", \"audio_kwargs\": {\"sampling_rate\": 16000}},\n)\nwith torch.inference_mode():\n generated = model.generate(**inputs, max_new_tokens=256, do_sample=False)\nnew_tokens = generated[:, inputs.input_ids.shape[1]:]\nprint(processor.batch_decode(new_tokens, skip_special_tokens=True)[0])\neos = model.generation_config.eos_token_id\neos_ids = eos if isinstance(eos, list) else [eos]\nif not any(token in eos_ids for token in new_tokens[0].tolist()):\n print(\"Warning: no EOS; generation may be truncated. Do not treat this as complete.\")\n" + ], + "id": "native-06" + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Next steps\n", + "For batches, keywords and a command-line script with input validation, see [the runnable example](https://github.com/QwenAudio/Fun-ASR/tree/main/examples/transformers). The native export returns text, not word timestamps, speaker identities, or a realtime protocol. The 31-language MLT checkpoint is separate. CPU success is not a GPU or throughput benchmark; model output still needs human evaluation.\n", + "\n", + "Choose [FunASR services, vLLM or llama.cpp](https://www.funasr.com/en/deploy/) when you need deployment rather than a notebook. Preserve the checkpoint/runtime versions and raw results when evaluating your application.\n" + ], + "id": "native-07" + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "name": "python" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/examples/transformers/README.md b/examples/transformers/README.md new file mode 100644 index 00000000..d1c75be2 --- /dev/null +++ b/examples/transformers/README.md @@ -0,0 +1,84 @@ +# Fun-ASR-Nano with Transformers + +Transcribe Chinese, English or Japanese with the native Hugging Face processor +and generation API. No FunASR toolkit, repository-local `model.py`, or remote +Python code is needed. The first model load downloads about 1.66 GB of weights. + +[Online demo](https://huggingface.co/spaces/FunAudioLLM/Fun-ASR-Nano) · +[Notebook](../colab/fun_asr_nano_transformers.ipynb) · +[中文指南](https://www.funasr.com/docs/native-transformers.html) + +## First transcript + +Clone the **QwenAudio/Fun-ASR code repository**, not a Hugging Face weight +repository. Run all commands below from this repository's root: + +```bash +git clone https://github.com/QwenAudio/Fun-ASR.git +cd Fun-ASR +``` + +Use an isolated environment. This reproducible recipe was exercised on Linux +x86-64, Python 3.12 and CPU; it does not upgrade your existing serving environment. + +```bash +python3.12 -m venv .venv-native +. .venv-native/bin/activate +python -m pip install --index-url https://download.pytorch.org/whl/cpu 'torch==2.10.0+cpu' 'torchaudio==2.10.0+cpu' +python -m pip install -r examples/transformers/requirements.txt +python -m pip check +python examples/transformers/transcribe.py +``` + +The default input is the pinned official English sample. A successful result +contains `text` and `reached_eos: true`. No access token is required for this +public checkpoint. The model itself also runs through the short, no-clone +[Python recipe](https://www.funasr.com/en/docs/native-transformers.html). + +## Your recordings and batches + +```bash +python examples/transformers/transcribe.py recording.wav --language zh --keywords 开放时间 +python examples/transformers/transcribe.py chinese.wav english.wav --language zh en +python examples/transformers/transcribe.py english.wav --language en --prompt 'A conversation about opening hours.' +``` + +Files are read as float32, stereo/multichannel audio is averaged to mono, and +non-16 kHz audio is explicitly resampled with `soxr_hq`. Original files are not +modified. This example accepts 1-4 files and at most 60 seconds total. Longer +recordings are rejected, not silently trimmed. Segment long audio deliberately +or choose a [serving recipe](https://www.funasr.com/en/deploy/). + +Output order matches input order. One language applies to all files, or give one +per file. `keywords` and `prompt` are hints, not enforced vocabulary. Missing EOS +or empty text produces a nonzero exit after printing the diagnostic result. +EOS is a generation boundary, not proof that every word was recognized. + +## Select a backend, not just a suffix + +| Need | Artifact and entry point | +| --- | --- | +| Native Transformers | `FunAudioLLM/Fun-ASR-Nano-2512-hf`, `AutoProcessor` + `AutoModelForSpeechSeq2Seq` | +| FunASR pipelines and existing services | `FunAudioLLM/Fun-ASR-Nano-2512`, `funasr.AutoModel` | +| Native vLLM serving | `FunAudioLLM/Fun-ASR-Nano-2512-vllm`, [native vLLM guide](https://www.funasr.com/en/docs/official-native-vllm.html) | +| C++ / edge | Converted GGUF, [llama.cpp guide](https://www.funasr.com/en/llama-cpp.html) | + +The native `-hf` export produces transcription text. It does not add word +timestamps, speaker identities, a streaming protocol or an HTTP server. The +31-language MLT checkpoint is a separate model, not an alternate name for this +zh/en/ja export. GPU dtype, attention kernels and throughput require separate +hardware validation; the CPU sample is not a GPU benchmark. + +## Verification + +Transformers **5.17.0** is a released package containing `fun_asr_nano`. +The examples pin native checkpoint revision +`d93b302ee7fd505e1b3576120fc142fc6f7820e1` and set +`trust_remote_code=False`. Chinese/English public-sample inference, keywords +and padded batching were functionally exercised on CPU on 2026-09-09; these +checks are not CER/WER or capacity evaluation. + +[Upstream model documentation](https://huggingface.co/docs/transformers/v5.17.0/en/model_doc/fun_asr_nano) +and [model card](https://huggingface.co/FunAudioLLM/Fun-ASR-Nano-2512-hf) +describe the native interface. Keep the runtime, model revision and raw output +with your own evaluation. Never publish private recordings in a bug report. diff --git a/examples/transformers/requirements.txt b/examples/transformers/requirements.txt new file mode 100644 index 00000000..0840570a --- /dev/null +++ b/examples/transformers/requirements.txt @@ -0,0 +1,8 @@ +transformers==5.17.0 +torch==2.10.0 +torchaudio==2.10.0 +numpy==1.26.4 +librosa==0.11.0 +soundfile==0.13.1 +huggingface-hub==1.30.0 +tokenizers==0.23.2 diff --git a/examples/transformers/transcribe.py b/examples/transformers/transcribe.py new file mode 100644 index 00000000..d61389ec --- /dev/null +++ b/examples/transformers/transcribe.py @@ -0,0 +1,102 @@ +"""Native Transformers CPU example for short, authorized recordings.""" +import argparse +import json +from pathlib import Path + +import numpy as np + +MODEL_ID = "FunAudioLLM/Fun-ASR-Nano-2512-hf" +REVISION = "d93b302ee7fd505e1b3576120fc142fc6f7820e1" +SAMPLE_MODEL = "FunAudioLLM/Fun-ASR-Nano-2512" +SAMPLE_REVISION = "272c57b82523ada6fd87095e955f8e29100979ab" +SAMPLE_RATE = 16000 +MAX_SECONDS = 60 + + +def prepare_audio(audio, sample_rate): + import librosa + + audio = np.asarray(audio, dtype=np.float32) + if sample_rate <= 0 or audio.ndim not in (1, 2) or not audio.size: + raise ValueError("Audio must be non-empty mono or frames-by-channels audio") + if not np.isfinite(audio).all(): + raise ValueError("Audio contains non-finite samples") + if len(audio) / sample_rate > MAX_SECONDS: + raise ValueError("This short-file example accepts at most 60 seconds; no silent trimming") + if audio.ndim == 2: + audio = audio.mean(axis=1) + if sample_rate != SAMPLE_RATE: + audio = librosa.resample(audio, orig_sr=sample_rate, target_sr=SAMPLE_RATE, res_type="soxr_hq") + return np.ascontiguousarray(audio, dtype=np.float32) + + +def languages_for_batch(languages, count): + if count < 1 or count > 4 or len(languages) not in (1, count): + raise ValueError("Use 1-4 files and one language or one language per file") + if any(language not in ("zh", "en", "ja") for language in languages): + raise ValueError("Base Nano supports zh, en and ja; MLT is a separate checkpoint") + return languages * count if len(languages) == 1 else languages + + +def load_audio(path): + import soundfile as sf + + info = sf.info(path) + if not info.frames or not 0 < info.duration <= MAX_SECONDS: + raise ValueError("Use a non-empty recording of at most 60 seconds") + audio, sample_rate = sf.read(path, dtype="float32") + return prepare_audio(audio, sample_rate) + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("audio", nargs="*", type=Path, help="Local audio files; default: pinned official English sample") + parser.add_argument("--language", nargs="+", default=["en"], choices=["zh", "en", "ja"]) + parser.add_argument("--keywords", nargs="*", default=None) + parser.add_argument("--prompt", default=None) + parser.add_argument("--max-new-tokens", type=int, default=256) + args = parser.parse_args() + if not 1 <= args.max_new_tokens <= 1024: + parser.error("--max-new-tokens must be between 1 and 1024") + languages = languages_for_batch(args.language, len(args.audio) or 1) + if not args.audio: + from huggingface_hub import hf_hub_download + + args.audio = [Path(hf_hub_download(SAMPLE_MODEL, "example/en.mp3", revision=SAMPLE_REVISION, token=False))] + audio = [load_audio(path) for path in args.audio] + if sum(len(item) for item in audio) > MAX_SECONDS * SAMPLE_RATE: + raise ValueError("Keep total batch audio within 60 seconds for this example") + + import torch + import transformers + from transformers import AutoModelForSpeechSeq2Seq, AutoProcessor + + torch.set_num_threads(4) + processor = AutoProcessor.from_pretrained(MODEL_ID, revision=REVISION, trust_remote_code=False, token=False) + model = AutoModelForSpeechSeq2Seq.from_pretrained( + MODEL_ID, revision=REVISION, trust_remote_code=False, token=False, + dtype=torch.float32, + ).to("cpu").eval() + inputs = processor.apply_transcription_request( + audio=audio, language=languages, keywords=args.keywords, prompt=args.prompt, + processor_kwargs={"return_tensors": "pt", "audio_kwargs": {"sampling_rate": SAMPLE_RATE}, + "text_kwargs": {"padding": True}}, + ) + with torch.inference_mode(): + generated = model.generate(**inputs, max_new_tokens=args.max_new_tokens, do_sample=False) + new_tokens = generated[:, inputs.input_ids.shape[1]:] + texts = processor.batch_decode(new_tokens, skip_special_tokens=True) + eos = model.generation_config.eos_token_id + eos_ids = eos if isinstance(eos, list) else [eos] + results = [] + for path, language, text, tokens in zip(args.audio, languages, texts, new_tokens.tolist()): + results.append({"file": str(path), "language": language, "text": text, + "reached_eos": any(token in eos_ids for token in tokens)}) + print(json.dumps({"model": MODEL_ID, "revision": REVISION, "transformers": transformers.__version__, + "device": "cpu", "results": results}, ensure_ascii=False, indent=2)) + if not all(row["reached_eos"] and row["text"].strip() for row in results): + raise SystemExit("Incomplete generation: inspect empty text or missing EOS; do not treat this as a complete transcript") + + +if __name__ == "__main__": + main() diff --git a/tests/test_funasr_requirement.py b/tests/test_funasr_requirement.py index e6bcaaec..01cfd82d 100644 --- a/tests/test_funasr_requirement.py +++ b/tests/test_funasr_requirement.py @@ -40,8 +40,8 @@ def test_docs_use_quoted_current_funasr_install_commands(): def test_readmes_surface_current_release_and_deployment_paths(): required = [ - "funasr==1.4.14", - "https://github.com/modelscope/FunASR/releases/tag/v1.4.14", + "funasr==1.4.15", + "https://github.com/modelscope/FunASR/releases/tag/v1.4.15", "MOSS-Transcribe-Diarize", "https://www.funasr.com/deploy/moss-transcribe-diarize.html", "runtime-llamacpp-v0.2.6", diff --git a/tests/test_transformers_quickstart.py b/tests/test_transformers_quickstart.py new file mode 100644 index 00000000..ddb4900e --- /dev/null +++ b/tests/test_transformers_quickstart.py @@ -0,0 +1,71 @@ +"""Offline contract tests; speech quality is verified separately on real audio.""" +import importlib.util +import ast +import json +from pathlib import Path +import unittest + +import numpy as np + +ROOT = Path(__file__).resolve().parents[1] +SPEC = importlib.util.spec_from_file_location("native_example", ROOT / "examples/transformers/transcribe.py") +native = importlib.util.module_from_spec(SPEC) +SPEC.loader.exec_module(native) + + +class NativeExampleTests(unittest.TestCase): + def test_fixed_native_artifact(self): + self.assertEqual(native.MODEL_ID, "FunAudioLLM/Fun-ASR-Nano-2512-hf") + self.assertEqual(native.REVISION, "d93b302ee7fd505e1b3576120fc142fc6f7820e1") + + def test_audio_is_resampled_and_downmixed_explicitly(self): + out = native.prepare_audio(np.ones((48000, 2), dtype=np.float32), 48000) + self.assertEqual(out.shape, (16000,)) + self.assertEqual(out.dtype, np.float32) + + def test_invalid_audio_is_rejected(self): + for audio in [np.array([]), np.array([np.nan]), np.array([np.inf]), np.zeros((2, 2, 2))]: + with self.subTest(shape=audio.shape), self.assertRaises(ValueError): + native.prepare_audio(audio, 16000) + with self.assertRaises(ValueError): + native.prepare_audio(np.zeros(61 * 16000), 16000) + with self.assertRaises(ValueError): + native.prepare_audio(np.ones(10), 0) + + def test_language_contract(self): + self.assertEqual(native.languages_for_batch(["en"], 2), ["en", "en"]) + self.assertEqual(native.languages_for_batch(["zh", "en"], 2), ["zh", "en"]) + for languages, count in [([], 1), (["en"], 0), (["zh", "en"], 3), (["ko"], 1)]: + with self.subTest(languages=languages), self.assertRaises(ValueError): + native.languages_for_batch(languages, count) + + def test_readmes_offer_native_before_toolkit_install(self): + for suffix in ["", "_zh", "_ja", "_ko"]: + text = (ROOT / f"README{suffix}.md").read_text() + self.assertIn("examples/transformers/", text) + self.assertLess(text.index("transformers==5.17.0"), text.index("pip install -r requirements.txt")) + + def test_notebook_is_unexecuted_and_python_cells_parse(self): + notebook = json.loads((ROOT / "examples/colab/fun_asr_nano_transformers.ipynb").read_text()) + ids = [cell["id"] for cell in notebook["cells"]] + self.assertEqual(len(ids), len(set(ids))) + for cell in notebook["cells"]: + if cell["cell_type"] != "code": + continue + self.assertIsNone(cell["execution_count"]) + self.assertEqual(cell["outputs"], []) + source = "".join(cell["source"]) + if not source.startswith("%pip"): + ast.parse(source) + + def test_clone_directory_and_legacy_anchors_remain_clear(self): + guide = (ROOT / "examples/transformers/README.md").read_text() + self.assertIn("git clone https://github.com/QwenAudio/Fun-ASR.git", guide) + self.assertIn("cd Fun-ASR", guide) + for suffix, anchor, heading in [("_ja", "主要機能", "主要機能"), ("_ko", "주요-기능", "주요 기능")]: + text = (ROOT / f"README{suffix}.md").read_text() + self.assertIn(f'\n\n# {heading}', text) + + +if __name__ == "__main__": + unittest.main()