方言语音识别与转写实践:基于 Qwen3-ASR 的吴语/粤语/川渝方言评测

中文方言语音识别(ASR,Automatic Speech Recognition)与方言转普通话翻译(AST,Automatic Speech Translation)是语音技术落地的重要场景。我国方言体系复杂,吴语、粤语、川渝方言是使用人口多、产业落地需求旺盛的三大方言区。
本文基于 WenetSpeech 系列开源方言数据集,采用 Qwen3-ASR-1.7B 作为语音识别底座,搭配 Qwen3-8B-AWQ 大语言模型构建「ASR 识别 + 文本翻译」的级联 AST 方案,完成了吴语、粤语、川渝方言三个方向的数据处理、模型部署与效果评测,为方言语音技术落地提供可复现的实践参考。

1. 数据集介绍

本次实验采用 ASLP-lab 开源的 WenetSpeech 系列方言数据集,该系列是目前规模较大、标注质量较高的开源中文方言语料,覆盖吴语、粤语、川渝方言等多个方言,包含训练集与专门的评测集,分别在 ModelScope(国内推荐)与 HuggingFace 平台托管。

1.1 训练数据集

以下为大规模训练数据集,适合用于模型微调,国内用户推荐通过 ModelScope 下载,HuggingFace 地址在境内无法正常访问。

注:ModelScope 数据集页面需登录后查看完整数据与下载入口,也可通过 ModelScope Python SDK 一键下载:

pip install modelscope
modelscope download --dataset ASLP-lab/WenetSpeech-Wu --local_dir ./WenetSpeech-Wu

1.2 评测数据集

以下为官方整理的标准测试集,用于模型效果基准测试,每个方言均包含长短音频,覆盖不同难度场景。

吴语基准测试集

粤语 ASR 测试集

川渝方言评测集

2. 数据处理

三个评测集采用了不同的数据组织格式,下文分别说明读取与处理方法,并针对吴语双任务数据集构造 AST 评测专用数据集。

WenetSpeech-Wu-Bench

WenetSpeech-Wu-Bench 采用 Parquet 格式存储,压缩率高、读取效率好,同时兼容 ASR 和 AST 两类任务。

基础读取

import pyarrow.parquet as pq

# 读取完整表,asr或ast任务
table = pq.read_table("WenetSpeech-Wu-Bench/understanding/asr.parquet")
# table = pq.read_table("WenetSpeech-Wu-Bench/understanding/ast.parquet")

print ("表结构:", table.schema)

# 按需转 pandas DataFrame
df = table.to_pandas()

# 基础统计
print(df.shape)
print(df.head())

输出结果
在这里插入图片描述

构造 AST 联合评测集

由于 ASR 文件仅含吴语标注、AST 文件仅含普通话标注,且两个子集存在重叠音频,因此我们可以通过音频 ID 关联两份标注,构造「方言语料 + 吴语转写 + 普通话翻译」的完整三元组,用于后续 COMET 翻译质量评测。
该数据集的价值在于:既可以验证「ASR + 翻译」级联方案的端到端效果,也可以作为 AST 模型的标准评测集。

import pandas as pd

def main():
    # 读取两个parquet文件
    df_asr = pd.read_parquet("WenetSpeech-Wu-Bench/understanding/asr.parquet")
    df_ast = pd.read_parquet("WenetSpeech-Wu-Bench/understanding/ast.parquet")

    # 校验必要字段
    required_cols = {"task", "utt_id", "audio", "label"}
    for name, df in [("asr", df_asr), ("ast", df_ast)]:
        missing = required_cols - set(df.columns)
        if missing:
            raise ValueError(f"{name}.parquet 缺少字段: {missing}")

    df_join = pd.merge(
        df_ast,
        df_asr[["audio", "label", "utt_id"]],
        on="audio",
        how="inner",
        suffixes=("_ast", "_asr")
    )

    df_new = pd.DataFrame({
        "task": df_join["task"],
        "utt_id": df_join["utt_id_ast"],
        "audio": df_join["audio"],
        "label_asr": df_join["label_asr"],   # ASR识别文本作为源文本
        "label_ast": df_join["label_ast"],   # AST翻译参考标签
    })

    print(f"asr数据集总条数: {len(df_asr)}")
    print(f"ast数据集总条数: {len(df_ast)}")
    print(f"完全匹配条数: {len(df_new)}")

    # 输出新parquet
    output_path = "comet_eval_dataset.parquet"
    df_new.to_parquet(output_path, index=False)
    print(f"已保存交集数据集到 {output_path}")


if __name__ == "__main__":
    main()

运行结果
在这里插入图片描述

WSYue-ASR-eval

粤语评测集采用经典的 Praat TextGrid 标注格式,音频为 16kHz 单声道 WAV 格式,分为 Long(长音频,370 条,10–30s)和 Short(短音频,7060 条,0–10s)两个目录。长音频场景更贴近真实对话场景,因此本次以长音频子集作为测试对象。

标注解析与音频读取

TextGrid 文件包含分段的时间戳与文本标注,我们通过正则提取「文本」层的所有有效标注,拼接为完整的句子级标注。

import re
import csv
import wave
from pathlib import Path

def parse_textgrid(file_path: Path):
    """
    解析TextGrid文件,提取【文本】tier下所有非空text
    返回: list[dict], 每个元素 {"xmin":float, "xmax":float, "text":str}
    """
    with open(file_path, "r", encoding="utf-8") as f:
        content = f.read()

    # 定位到 name = "文本" 这个tier块
    pattern = re.compile(
        r'item \[\d+\]:\s*?class = "IntervalTier"\s*?name = "文本"\s*?'
        r'xmin = ([0-9.]+)\s*?xmax = ([0-9.]+)\s*?intervals: size = \d+\s*?(.*?)(?=item \[\d+\]:|$)',
        re.DOTALL
    )
    match = pattern.search(content)
    if not match:
        return []

    intervals_block = match.group(3)
    # 解析每一个intervals [n] 块
    interval_pat = re.compile(
        r'intervals \[\d+\]:\s*?'
        r'xmin = ([0-9.]+)\s*?'
        r'xmax = ([0-9.]+)\s*?'
        r'text = "(.*?)"',
        re.DOTALL
    )
    results = []
    for m in interval_pat.finditer(intervals_block):
        xmin = float(m.group(1))
        xmax = float(m.group(2))
        text = m.group(3).strip()
        if text:  # 只保留非空文本
            results.append({
                "xmin": xmin,
                "xmax": xmax,
                "text": text
            })
    return results


def get_audio_from_file(file_path):
    with wave.open(str(file_path), "rb") as wav_file:
        frames = wav_file.readframes(wav_file.getnframes())
        data = bytes(frames)
    return data


def main(input_dir):
    input_dir = Path(input_dir)
    tg_dir = input_dir / "TextGrid"
    wav_dir = input_dir / "wav"
    
    csv_fields = ["utt_id", "asr_gt_text"]
    with open("yue_long_gt.csv", "w", encoding="utf-8", newline="") as csv_f:
        csv_writer = csv.DictWriter(csv_f, fieldnames=csv_fields)
        csv_writer.writeheader()

        wav_files = sorted(wav_dir.glob("*.wav"))
        for idx, wav_file in enumerate(wav_files):
            base_name = wav_file.stem  # 不带后缀文件名
            tg_file = tg_dir / f"{base_name}.TextGrid"
            if not tg_file.exists():
                print(f"警告:找不到对应TextGrid文件 {tg_file}, 跳过")
                continue

            text_items = parse_textgrid(tg_file)
            asr_gt_text = "".join([item["text"] for item in text_items])
            
            csv_writer.writerow({
                "utt_id": base_name,
                "asr_gt_text": asr_gt_text
            })

if __name__ == "__main__":
    main("WSYue-ASR-eval/Long")

WSChuan-Eval

川渝方言评测集采用 WenetSpeech 原生的 Kaldi 风格格式:text文件存储「音频 ID + 标注文本」,wav目录存放对应音频文件,同样分为长音频(322 条)和短音频(8051 条)两个子集。本次同样选取长音频进行测试。

标注解析与音频读取

import os
import wave
from pathlib import Path

def get_audio_from_file(file_path):
    with wave.open(str(file_path), "rb") as wav_file:
        frames = wav_file.readframes(wav_file.getnframes())
        data = bytes(frames)
    return data

def main(input_dir):
    input_dir = Path(input_dir)

    all_data = []
    with open(os.path.join(input_dir, "text"), "r", encoding="utf8") as fin:
        for line in fin.readlines():
            data = line.strip().split()
            all_data.append(
                {
                    "utt_id": data[0],
                    "gt_text": " ".join(data[1:]),
                }
            )
       
    for data in all_data:
        utt_id = data["utt_id"]
        asr_gt_text = data["gt_text"]
        audio_bytes = get_audio_from_file(os.path.join(input_dir, "wav", f"{utt_id}.wav"))
        # 后续可批量写入评测文件

if __name__ == "__main__":
    main("WSChuan-Eval/WS-Chuan-ASR-Eval/open-source/long")

3. ASR 和 AST 任务测试

本次实验采用「语音识别 + 文本翻译」的两级级联方案实现方言 AST:

  1. 第一级:使用 Qwen3-ASR-1.7B 将方言语音识别为对应方言的书面文本
  2. 第二级:使用 Qwen3-8B-AWQ 大模型将方言文本翻译为标准普通话

该方案的优势是无需训练专属 AST 模型,复用成熟的 ASR 与 LLM 能力,落地成本低、迭代灵活。

3.1 ASR(Qwen3-ASR)

模型选型说明

本次选用 Qwen3-ASR-1.7B 作为识别底座,核心原因如下:

  • 方言覆盖广:官方支持 22 种中文方言,包括吴语、粤语、四川话等本次测试的所有方言,无需额外微调即可开箱即用
  • 精度领先:根据官方基准测试,该模型在 WenetSpeech 系列方言数据集上的识别精度显著优于 Whisper-large-v3 等主流开源模型
  • 部署友好:支持 transformers 与 vLLM 双后端,原生支持长音频、流式推理,搭配 FlashAttention 可大幅降低显存占用

模型地址:https://www.modelscope.cn/models/Qwen/Qwen3-ASR-1.7B

推理实现

import torch
from qwen_asr import Qwen3ASRModel
import numpy as np
import wave

class Qwen3ASR:
    def __init__(self, model_name, device_id=0):
        self.model = Qwen3ASRModel.from_pretrained(
            model_name,
            dtype=torch.bfloat16,
            device_map=f"cuda:{device_id}",
            attn_implementation="flash_attention_2",
            max_inference_batch_size=1,
            max_new_tokens=1024,  # 长音频需设置足够大的生成token数
        )
    
    def single_transcribe(self, audio_bytes):
        # 将WAV字节流转为模型要求的numpy数组 + 采样率格式
        np_wav = np.frombuffer(audio_bytes, np.int16).flatten()
        
        results = self.model.transcribe(
            audio=(np_wav, 16000),
            language=None,  # 不指定语言,测试模型自动方言识别能力
        )
        return results[0]
        

if __name__ == "__main__":
    model = Qwen3ASR("Qwen/Qwen3-ASR-1.7B", device_id=0)
    with wave.open("WSYue-ASR-eval/Long/wav/0000000831.wav", "rb") as wav_file:
            frames = wav_file.readframes(wav_file.getnframes())
            data = bytes(frames)
    result = model.single_transcribe(data)
    print("识别语言:", result.language)
    print("识别文本:", result.text)

运行结果
在这里插入图片描述

配置说明:

  • 使用 bf16 精度推理,在保证精度的同时降低显存占用
  • 启用 FlashAttention-2 加速,长音频场景显存收益更明显
  • language=None测试模型零样本方言识别能力,实际生产中可指定语言进一步提升精度

3.2 AST(Qwen3-ASR + Qwen3)

在 ASR 识别出方言文本的基础上,通过 Qwen3-8B-AWQ 大模型完成方言到普通话的文本翻译。

模型选型说明

选用 Qwen3-8B-AWQ 的原因:

  • 量化效率高:AWQ 4bit 量化后精度损失极小,显存占用仅为原版的 1/4 左右,单张消费级显卡即可部署
  • 翻译能力强:Qwen3 原生支持上百种语言与方言,具备优秀的中文内翻译能力
  • 部署生态完善:完美兼容 vLLM 推理框架,支持高并发批量调用,适合批量评测

模型地址:https://www.modelscope.cn/models/Qwen/Qwen3-8B-AWQ

3.2.1 vLLM 启动服务

通过 vLLM 部署为 OpenAI 兼容接口,便于批量调用与后续工程化落地。

vllm serve Qwen3-8B-AWQ --port 1234 --gpu-memory-utilization 0.4 --max-model-len 4096 --quantization awq

3.2.2 调用服务

翻译任务属于确定性文本生成,因此关闭思考模式以提升推理速度与输出稳定性。根据官方最佳实践,非思考模式使用 Temperature=0.7TopP=0.8TopK=20MinP=0 的采样参数。

from openai import OpenAI

client = OpenAI(base_url="http://127.0.0.1:1234/v1", api_key="default")

prompt_template = """你是方言转普通话翻译专家,支持方言文本翻译。
规则:
1.输入是方言口语书面转写文本,输出标准现代汉语普通话;
2.保留原句语义、语气,不要随意增删内容;
3.方言代词、助词替换成普通话对应词汇;
4.只输出翻译后的句子,不要额外解释,不要输出思考过程。

方言文本:{text}
普通话输出:"""

def dialect_to_mandarin(text: str):
    resp = client.chat.completions.create(
        model="Qwen3-8B-AWQ",
        messages=[{"role":"user","content":prompt_template.format(text=text)}],
        temperature=0.7,
        top_p=0.8,
        max_tokens=256,
        # vLLM扩展参数放到extra_body
        extra_body={
            "chat_template_kwargs": {
                "enable_thinking": False
            },
            "top_k": 20,
            "min_p": 0.0
        }
    )
    return resp.choices[0].message.content.strip()

if __name__ == "__main__":
    print(dialect_to_mandarin("阿拉现在可以请理工大的老教授带了伊拉的孙子孙女"))

Prompt 设计思路:

  • 明确定义角色与任务边界,避免模型输出无关内容
  • 约束输出格式,仅返回翻译结果,便于批量自动化评测
  • 明确翻译规则,保证方言口语特征正确转换为普通话书面表达

3.3 评价指标

3.3.1 ASR 任务 ——CER(字错误率)

中文语音识别通用**字错误率(Character Error Rate, CER)**作为核心指标,计算识别结果与标注文本之间的字符级编辑距离(替换、插入、删除操作数之和)与参考文本总字符数的比值,数值越低代表识别精度越高。

使用jiwer库进行计算:

import jiwer
# reference: 参考标注文本,hypothesis: 模型识别结果
score = jiwer.cer(reference, hypothesis)
print(f"CER: {score:.4f}")

说明:中文场景优先使用 CER 而非 WER(词错误率),因为中文分词标准不统一,CER 不受分词影响,结果更稳定可比。

3.3.2 AST 任务

语音翻译任务采用两类主流指标进行评价:统计指标 BLEU 与语义指标 COMET。

BLEU

BLEU 是基于 n-gram 重合度的经典翻译评价指标,计算速度快、计算逻辑简单,适合快速横向对比。中文评测需指定tokenize="zh"以启用中文分词。

import sacrebleu
# hypotheses: 模型翻译结果列表,references: 参考译文列表
bleu_result = sacrebleu.corpus_bleu(hypotheses, [references], tokenize="zh")
print(f"BLEU: {bleu_result.score:.2f}")
COMET

COMET 是基于预训练语言模型的语义化翻译评价指标,更贴合人类主观判断,分为两种范式,环境安装可参考我的另一篇博客【机器翻译评价——COMET环境安装(GPU版本)】

  • 有参考(wmt22-comet-da):需要源文本、机器译文、人工参考译文三者,评价精度最高
  • 无参考(wmt22-cometkiwi-da):仅需源文本与机器译文,适合缺少人工标注的场景
wmt22-comet-da(有参考)
from comet import load_from_checkpoint
import torch

comet_inputs = []
comet_inputs.append({
    "src": src,
    "mt": translation,
    "ref": tgt
})

comet_model = load_from_checkpoint("wmt22-comet-da/checkpoints/model.ckpt")

if torch.cuda.is_available():
    comet_model = comet_model.cuda()

outputs = comet_model.predict(comet_inputs, batch_size=8)
scores = outputs.scores
avg_comet = sum(scores)/len(scores)
wmt22-cometkiwi-da(无参考)
from comet import load_from_checkpoint
import torch

comet_inputs = []
comet_inputs.append({
    "src": src,
    "mt": translation,
})

comet_model = load_from_checkpoint("wmt22-cometkiwi-da/checkpoints/model.ckpt")

if torch.cuda.is_available():
    comet_model = comet_model.cuda()

outputs = comet_model.predict(comet_inputs, batch_size=8)
scores = outputs.scores
avg_comet = sum(scores)/len(scores)

3.4 评测结果

3.4.1 ASR 识别效果

本次测试均采用长音频子集,不指定语言、让模型自动识别方言,最终字错误率如下:

方言CER
吴语0.2331
粤语0.2199
川渝方言0.1062

结果分析:

  1. 川渝方言识别精度最优,CER 仅 10.62%,原因是川渝方言属于西南官话,与普通话语音体系差异较小,模型零样本识别难度低。
  2. 吴语与粤语 CER 相对较高,符合预期 —— 吴语、粤语与普通话发音、词汇差异大,属于汉语方言中差异度最高的层级。
  3. 整体来看,Qwen3-ASR 在三款方言上均达到可用水平,无需微调即可支撑基础方言转写场景。

3.4.2 AST 翻译效果

BLEU 指标

方言BLEU
吴语47.50

有参考(wmt22-comet-da)

方言COMET
吴语0.7345

无参考(wmt22-cometkiwi-da)

方言COMET
粤语0.5467

结果分析:

  1. 吴语有参考 COMET 得分 0.7345,说明级联方案的翻译整体语义准确性较好,能够完成基本的方言转普通话需求。
  2. 粤语无参考 COMET 得分偏低,主要受两方面影响:一是粤语 ASR 识别误差会传递到翻译阶段,形成误差累积;二是无参考 COMET 模型本身对中文方言的适配度有限。
  3. 级联方案的核心瓶颈在于 ASR 识别精度 —— 方言识别错误越多,后续翻译的偏差越大,优化 ASR 方言识别能力是提升 AST 效果的关键。

总结

核心结论

  1. Qwen3-ASR 方言能力突出:在吴语、粤语、川渝方言三款主流方言上,零样本即可达到可用的识别精度,其中川渝方言 CER 低至 10.62%,是目前开源 ASR 中方言能力第一梯队的模型。
  2. 级联 AST 方案落地性强:「ASR 识别 + 大模型翻译」的方案无需专门训练端到端 AST 模型,依托通用大模型即可快速实现方言转普通话能力,适合快速落地与小成本试点。
  3. 方言差异是核心影响因素:方言与普通话的语音、词汇差异越大,ASR 识别难度越高,进而影响最终 AST 效果;西南官话类方言落地门槛最低,吴语、粤语等差异大的方言建议针对性微调优化。

踩坑与注意事项

  1. 数据集下载:HuggingFace 数据集地址在国内无法正常访问,国内用户请优先使用 ModelScope 平台或 SDK 下载。
  2. 环境依赖:qwen-asr包建议在 Python 3.12 环境中安装,FlashAttention 需匹配 CUDA 版本,低内存机器编译时可设置MAX_JOBS限制并发数。
  3. 显存配置:Qwen3-ASR-1.7B(bf16)约占 5-6GB 显存,Qwen3-8B-AWQ 约占 9-10GB 显存。
  4. 长音频参数:处理长音频时务必调大max_new_tokens,否则会出现识别结果截断的问题。
  5. vLLM 部署:AWQ 量化模型必须添加–quantization awq参数,否则会出现加载错误;建议使用 vLLM 0.8.5 及以上版本以兼容 Qwen3 系列模型。
Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐