1. 背景

在众多的开源模型中基本上没有可以准确识别藏语的语音识别模型,无论是国外开源的Whisper,还是国内阿里开源的Qwen3-ASR,均无法识别藏语,这个你们可以自行测试。如果我们需要本地化私有化部署一个可以识别藏语的语音识别项目,那么就需要使用大量的藏语数据集来训练模型。这里记录一位客户找到我训练藏语语音识别模型的过程,供你们参考学习。

首先我使用基于FunASR这个依赖使用Paraformer和SenseVoiceSmall都训练过,但是效果不理想,后面客户要求使用Qwen3-ASR来训练藏语语音识别模型,下面主要记录基于阿里开源的Qwen3-ASR-1.7B这个大模型来训练藏语语音识别模型的过程。

2. 数据集的准备

首先需要准备数据集,这里客户提供了10多个小时的藏语数据集,这些数据均是客户自己安排人员采集制作的。数据集要符合一定的格式要求,需要整理为jsonl格式文件,分为两个jsonl文件,一个是train.jsonl文件,这是训练集,还有一个是验证集val.jsonl。格式可以参考下面这样的:

{"audio": "/data/corpus/2026-08-11/converted/1000.wav", "text": "language Tibetan<asr text>རང་ཉིད་ངལ་རྩོལ་གྲལ་རིམ་གྱི་བསམ་བློ་བཡིན་རློམ་བྱེད་བཞིན་དུ།"}

其中这个audio是指音频所在的绝对路径,然后这个text是音频对应的文字内容,当然前面还添加了语种标识language Tibetan<asr text>,如果不想要语种标识,可以不添加这个,可以直接写音频对应的文字内容即可。

image-20260825000103737

这里由于先做的paraformer训练,我这里编写了一个python脚本直接把paraformer_train.jsonl这样的数据转为这个Qwen3-ASR训练用的数据,下面是这个脚本代码。

# !/usr/bin/env python
# _*_ coding utf-8 _*_
# @Time: 2026/8/24 14:51
# @Author: Luke Ewin
# @Blog: https://blog.lukeewin.top
# 转换 paraformer 的训练的 jsonl 格式为符合 Qwen3-ASR 训练的格式
import json
import sys

def convert_jsonl(input_file, output_file):
    """
    将原始JSONL转换为目标格式:
    - 原 'source' -> 新 'audio'
    - 原 'target' -> 新 'text',并在最前面添加 "language Tibetan<asr text>"
    """
    with open(input_file, 'r', encoding='utf-8') as fin, \
         open(output_file, 'w', encoding='utf-8') as fout:
        for line in fin:
            line = line.strip()
            if not line:
                continue
            try:
                data = json.loads(line)
            except json.JSONDecodeError as e:
                print(f"跳过无效JSON行: {e}", file=sys.stderr)
                continue

            # 提取所需字段
            audio_path = data.get('source')
            target_text = data.get('target')

            # 如果缺少必要字段,跳过该行(也可选择报错)
            if audio_path is None or target_text is None:
                print("跳过缺少 'source' 或 'target' 的行", file=sys.stderr)
                continue

            # 构建新记录
            new_record = {
                "audio": audio_path,
                "text": f"language Tibetan<asr text>{target_text}"
            }

            # 写入输出文件
            fout.write(json.dumps(new_record, ensure_ascii=False) + '\n')

if __name__ == "__main__":
    # 使用方法:python script.py input.jsonl output.jsonl
    if len(sys.argv) != 3:
        print("用法: python convert.py <输入文件.jsonl> <输出文件.jsonl>", file=sys.stderr)
        sys.exit(1)

    input_file = sys.argv[1]
    output_file = sys.argv[2]
    convert_jsonl(input_file, output_file)
    print(f"转换完成,结果已保存至 {output_file}")

3. 搭建训练环境

上面准备好了数据集,就可以开始搭建训练的Python环境了,这里使用uv,当然你也可以使用其它的,比如conda

如何安装uv这里就不说了,比较简单,网上也可以很容易找到安装的文档。

安装好uv之后,就可以使用下面的命令创建一个python运行环境了。

uv venv --python 3.12

这里使用的python版本指定为3.12版本,其它的版本不知道是否可以。

激活环境:

source .venv/bin/activate

激活之后,就可以安装对应的python依赖了。

uv pip install -e .

这里是通过源码方式安装的qwen-asr依赖,此外,训练还需安装datasets依赖。

uv pip install datasets

需要主要的是一定要安装上这个cuda版本的torch,你执行上面两条安装命令的时候默认会给你安装上这个torch,可以检查一下,是否安装对了。

import torch
torch.cuda.is_available()

如果返回的是True就说明可以使用cuda

系统安装的CUDA版本是13.2,如果使用的是5090显卡,需要安装的CUDA版本大于等于12.8。这里尽量选择安装最新版本的CUDA和显卡驱动。

image-20260825001243760

4. 开始训练

经过上面的步骤,环境和数据集都做好了,就可以开始训练了,这里为了访问编写一个shell脚本,执行这个脚本就可以开始训练了。

#!/usr/bin/env bash
set -e

export CUDA_VISIBLE_DEVICES=0

MODEL_PATH="/opt/asr/Qwen3/models/Qwen3-ASR-1.7B"
TRAIN_FILE="/opt/asr/Qwen3/finetuning/data/train.jsonl"
EVAL_FILE="/opt/asr/Qwen3/finetuning/data/val.jsonl"
OUTPUT_DIR="/opt/asr/Qwen3/output"

torchrun --nproc_per_node=1 qwen3_asr_sft.py \
  --model_path ${MODEL_PATH} \
  --train_file ${TRAIN_FILE} \
  --eval_file ${EVAL_FILE} \
  --output_dir ${OUTPUT_DIR} \
  --batch_size 2 \
  --grad_acc 4 \
  --lr 2e-5 \
  --epochs 10 \
  --log_steps 10 \
  --save_strategy steps \
  --save_steps 200 \
  --save_total_limit 5 \
  --num_workers 32 \
  --pin_memory 1 \
  --persistent_workers 1 \
  --prefetch_factor 2

需要根据自己的硬件条件修改上面的数值,比如服务器有一张显卡,那么就指定CUDA_VISIBLE_DEVICES=0,如果有多张,就可以使用CUDA_VISIBLE_DEVICES=0,1,这种方式添加多张显卡。还有这个batch_size的值一定要是2的n次方,这个n可以是非负整数。然后这个num_workers的值最大填写你服务器CPU核心数,比如这里有32个核心,就最大可以填写32,这个值也尽量填写2的n次方,同样这个n可以是非负整数。

授予可执行权限。

chmod +x finetun.sh

然后执行这个脚本,开始训练,如果你的显存不够,就可以减小batch_size值。

./finetun.sh

可以看到下面日志中输出的loss值一直在减小,说明训练是有效果的。

image-20260825002610968

可以看到,刚开始这个loss值很高,随着训练的进行,这个loss值会越来越小,如果这个loss值不减小,反而变大,说明这个训练参数设置的不对。

然后可以观察训练中,这个显卡的情况。

image-20260825002829526

从上面可知,如果训练的数据集是10多个小时,那么至少需要一张24GB显存的显卡。如果你的数据集更大,就需要更大显存的显卡。

5. 效果检验

经过上面的训练,现在已经完成了训练,我们可以使用下面的python代码测试一下是否真的有效果。

# !/usr/bin/env python
# _*_ coding utf-8 _*_
# @Time: 2025/2/28 1:53
# @Author: Luke Ewin
# @Blog: https://blog.lukeewin.top
import torch
from qwen_asr import Qwen3ASRModel

model = Qwen3ASRModel.from_pretrained(
    "/opt/asr/Qwen3/output/checkpoint-12830",
    dtype=torch.bfloat16,
    device_map="cuda:0",
    attn_implementation="flash_attention_2",
    max_inference_batch_size=32, # Batch size limit for inference. -1 means unlimited. Smaller values can help avoid OOM.
    max_new_tokens=256, # Maximum number of tokens to generate. Set a larger value for long audio input.
)

results = model.transcribe(
    audio=input("请输入音频路径:"),
    language=None, # set "English" to force the language
)

print(results[0].language)
print(results[0].text)

这个/opt/asr/Qwen3/output/checkpoint-12830是我上面训练输出的模型权重路径,你需要修改你自己保存训练后的模型权重路径,这里最后填写绝对路径。

然后这里还需要说明一下,我这里开启了flash_attention_2,你需要提前安装好这个依赖flash-attn。我这里使用的是whl方式安装的,如果执行下面方式安装,速度会很慢,会编译很长时间,所以这里找了一个编译好的whl文件直接安装。

uv pip install flash-attn --no-build-isolation

我这里下载的这个whl文件是:

aria2c -x 16 -s 16 -c "https://github.com/mjun0812/flash-attention-prebuild-wheels/releases/download/v0.9.47/flash_attn-2.6.3+cu130torch2.13-cp312-cp312-linux_x86_64.whl"

执行上面的python代码,可以看到有输出藏文,相比训练之前来说好很多,训练之前输出的是中文。

image-20260825003911851

如果要测试训练之前的效果,只需要修改上面python代码中的加载模型权重路径为训练之前的模型权重路径即可。

image-20260825004212728

可以对比发现,原先模型不能识别藏语,输出的直接是中文,并且意思也不对,并不是翻译为中文输出,你可以理解为模型胡乱输出了中文来应付我们。然后经过训练之后的模型,可以看到输出了藏文,由于我看不懂藏文,这里我发送给AI大模型分析,这意思和音频对应藏文意思是差不多的。

6. 其它

如果需要训练其它少数民族语言或者方言的语音识别模型,欢迎私信咨询。

更多内容可以关注我的博客

Q.E.D.


热爱生活,热爱程序