1. 背景
在众多的开源ASR模型中对于少数民族语言一般是不支持的,比如藏语,维吾尔语,蒙古语,开源的模型均不支持。
这次是基于Qwen3-ASR来训练一个支持藏语的ASR模型,使得可以在本地跑藏语语音识别项目。
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>,如果不想要语种标识,可以不添加这个,可以直接写音频对应的文字内容即可。
这里由于先做的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 训练的格式importjsonimportsysdefconvert_jsonl(input_file,output_file):""" 将原始JSONL转换为目标格式: - 原 'source' -> 新 'audio' - 原 'target' -> 新 'text',并在最前面添加 "language Tibetan<asr text>" """withopen(input_file,'r',encoding='utf-8')asfin,\open(output_file,'w',encoding='utf-8')asfout:forlineinfin:line=line.strip()ifnotline:continuetry:data=json.loads(line)exceptjson.JSONDecodeErrorase:print(f"跳过无效JSON行:{e}",file=sys.stderr)continue# 提取所需字段audio_path=data.get('source')target_text=data.get('target')# 如果缺少必要字段,跳过该行(也可选择报错)ifaudio_pathisNoneortarget_textisNone: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.jsonliflen(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--python3.12这里使用的python版本指定为3.12版本,其它的版本不知道是否可以。
激活环境:
source.venv/bin/activate激活之后,就可以安装对应的python依赖了。
uv pipinstall-e.这里是通过源码方式安装的qwen-asr依赖,此外,训练还需安装datasets依赖。
uv pipinstalldatasets需要主要的是一定要安装上这个cuda版本的torch,你执行上面两条安装命令的时候默认会给你安装上这个torch,可以检查一下,是否安装对了。
importtorch torch.cuda.is_available()如果返回的是True就说明可以使用cuda。
系统安装的CUDA版本是13.2,如果使用的是5090显卡,需要安装的CUDA版本大于等于12.8。这里尽量选择安装最新版本的CUDA和显卡驱动。
4. 开始训练
经过上面的步骤,环境和数据集都做好了,就可以开始训练了,这里为了访问编写一个shell脚本,执行这个脚本就可以开始训练了。
#!/usr/bin/env bashset-eexportCUDA_VISIBLE_DEVICES=0MODEL_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=1qwen3_asr_sft.py\--model_path${MODEL_PATH}\--train_file${TRAIN_FILE}\--eval_file${EVAL_FILE}\--output_dir${OUTPUT_DIR}\--batch_size2\--grad_acc4\--lr2e-5\--epochs10\--log_steps10\--save_strategysteps\--save_steps200\--save_total_limit5\--num_workers32\--pin_memory1\--persistent_workers1\--prefetch_factor2需要根据自己的硬件条件修改上面的数值,比如服务器有一张显卡,那么就指定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值一直在减小,说明训练是有效果的。
可以看到,刚开始这个loss值很高,随着训练的进行,这个loss值会越来越小,如果这个loss值不减小,反而变大,说明这个训练参数设置的不对。
然后可以观察训练中,这个显卡的情况。
从上面可知,如果训练的数据集是10多个小时,那么至少需要一张24GB显存的显卡。如果你的数据集更大,就需要更大显存的显卡。
5. 效果检验
经过上面的训练,现在已经完成了训练,我们可以使用下面的python代码测试一下是否真的有效果。
# !/usr/bin/env python# _*_ coding utf-8 _*_# @Time: 2025/2/28 1:53# @Author: Luke Ewin# @Blog: https://blog.lukeewin.topimporttorchfromqwen_asrimportQwen3ASRModel 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 pipinstallflash-attn --no-build-isolation我这里下载的这个whl文件是:
aria2c-x16-s16-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代码,可以看到有输出藏文,相比训练之前来说好很多,训练之前输出的是中文。
如果要测试训练之前的效果,只需要修改上面python代码中的加载模型权重路径为训练之前的模型权重路径即可。
可以对比发现,原先模型不能识别藏语,输出的直接是中文,并且意思也不对,并不是翻译为中文输出,你可以理解为模型胡乱输出了中文来应付我们。然后经过训练之后的模型,可以看到输出了藏文,由于我看不懂藏文,这里我发送给AI大模型分析,这意思和音频对应藏文意思是差不多的。