ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

AISHELL-1实战:微调Wav2Vec2训练中文语音识别模型

AISHELL-1实战:微调Wav2Vec2训练中文语音识别模型 第一次跑中文语音识别项目很多人不是死在模型上而是死在数据上。英文社区有 LibriSpeech中文开源数据集里你绕不开 AISHELL-1。这个数据集来自北京希尔贝壳科技提供了 178 小时的高质量普通话录音、400 位说话人规模不大不小做研究和入门训练都刚刚好。这篇实战文章我会带你从拿到 OpenSLR 上的 AISHELL-1 压缩包开始完成解压、解析标注、编写数据加载器再基于 Hugging Face Transformers 微调一个 Wav2Vec2 中文 ASR 模型最后给出切实可用的评估和推理代码。整个流程即使只用 CPU 都能跑通小样本实验如果你想认真练手一张普通显卡也能跑起来。1. 项目概述为什么我推荐 AISHELL-1 作为第一个中文 ASR 数据集1.1 认识 AISHELL-1 的基本盘AISHELL-1 是当前中文语音识别社区里最常见、最规范的开源数据集之一。它由 AISHELL 开源数据内容覆盖智能家居、智能车载、工业生产、新闻播报等真实场景而不是实验室环境下那种“播音腔”录音。数据集的主要参数如下项目数值语言中文普通话总时长约 178 小时说话人数400 位采样率16kHz单声道16bit PCM训练集340 人开发集40 人测试集20 人转录单位中文字符级这个参数意味着什么178 小时对于深度学习模型来说不算大但对于“跑通一个完整流程”来说一点也不小。你可以用它训练出真正能识别中文的模型而不是像很多教程那样只做一个不痛不痒的 demo。训练集中 340 位说话人覆盖了不同性别、年龄、口音这能让模型具备一定的泛化能力。录音格式是常见的 WAV不用转码Python 直接就能读非常省心。1.2 对比 THCHS-30、Primewords它赢在哪儿中文开源语音数据集其实不止 AISHELL-1我还接触过 THCHS-30、Primewords 等但如果你是一个刚入门 ASR 的开发者AISHELL-1 是最合适的第一站。数据集时长说话人数适合场景AISHELL-1约 178 小时400系统学习、论文实验、原型验证THCHS-30约 30 小时50快速测试、CPU 小规模训练Primewords约 100 小时296口音更多变但标注细节略粗糙THCHS-30 虽然小、跑起来快但数据量太少模型很容易陷入欠拟合做完之后会让人误以为“中文 ASR 很难”。Primewords 更大但它的口音来源比较杂标注规范也不如 AISHELL-1 清晰做入门项目反而会带来额外的数据清洗成本。AISHELL-1 最大的优势是“干净”转录文本格式统一、说话人划分明确、论文和开源项目里到处都能看到它的基准结果。你遇到任何问题搜一下 AISHELL-1 报错信息基本都能找到答案。除了技术上的优势AISHELL-1 对初学者还有一个很直接的价值它提供的音频文本配对非常整齐几乎没有“脏数据”。这让你可以把精力集中在模型代码上而不是整天清洗数据。当然这不代表 AISHELL-1 没有坑最典型的坑是转录文本里带空格我后面会专门讲。2. 环境准备与技术选型新手别掉进 Kaldi 的深坑2.1 硬件和软件环境清单先说环境因为很多人的项目进度卡在“依赖装不上”而不是“模型不好写”。我这边的推荐组合是 Linux 系统 Python 3.9 以上 PyTorch 2.x这套组合在 ASR 生态里最省心。如果你用的是 Windows也不是不行但建议把 WSL2 装上否则音频编解码、路径处理都可能遇到奇奇怪怪的问题。硬件方面训练 Wav2Vec2 这种预训练模型的微调任务显存 8GB 以上就能开始小批量实验想处理完整数据集并得到比较好的效果建议至少 24GB 显存。没有独显也没关系CPU 可以跑通流程只是速度慢适合先用一两百条样本验证代码。磁盘空间至少预留 30GB因为压缩包 15GB 左右解压后还要 20GB 左右。很多同学下载完了才发现磁盘满了很尴尬。2.2 为什么选 Transformers Wav2Vec2中文 ASR 的技术栈大概有三条路Kaldi、ESPnet、Hugging Face Transformers。三条路我都走过但如果你让我给新手推荐我强烈建议从 Transformers Wav2Vec2 入手。Kaldi 是传统语音识别的王者但它需要理解音素、词典、语言模型、WFST 解码图这一整套复杂概念而且配置文件和脚本风格非常老派新人很容易被劝退。ESPnet 是学术界常用的端到端语音处理工具包功能很全但它的 recipe 封装得比较厚改一个数据路径、换一个模型都要小心谨慎对于想快速理解“训练循环里发生了什么”的人来说黑盒程度太高。Wav2Vec2 则不同它本身在大量无标注语音上做过自监督预训练已经学到了非常好的声学特征我们只需要在 AISHELL-1 上做 CTC 微调训练目标明确、代码直观。方案上手难度可定制性适合人群Kaldi很高极高深入研究传统 ASR 的工程师ESPnet中高高做实验对比的科研人员Transformers Wav2Vec2中低中想快速跑通和落地的开发者Wav2Vec2 的另一个好处是生态成熟。Hugging Face 的 Trainer API 帮你处理了梯度累积、混合精度、断点恢复、评估调度等大量工程细节我们只需要关注数据格式和损失函数。对新手来说先跑通流程建立“原来 ASR 是这样工作”的体感比一步到位追求 SOTA 重要得多。2.3 一行命令装好训练依赖在终端执行下面的命令pip install torch torchaudio pip install transformers datasets accelerate soundfile librosatransformers提供模型和训练器datasets用来做数据集管理和采样accelerate是 Trainer 的后端加速库librosa负责读取音频并重采样soundfile是 librosa 读取 WAV 文件的底层依赖之一。如果你的网络环境下载慢可以给 pip 临时指定国内镜像源pip install -i https://pypi.tuna.tsinghua.edu.cn/simple torch torchaudio pip install -i https://pypi.tuna.tsinghua.edu.cn/simple transformers datasets accelerate soundfile librosa装完之后可以快速验证一下版本import transformers, torch print(transformers.__version__, torch.__version__)版本号打印出来不报错环境就基本没问题了。3. AISHELL-1 数据准备下载、解压、解析标注3.1 下载与解压AISHELL-1 在 OpenSLR 上的编号是 33直接下载地址是wget https://www.openslr.org/resources/33/data_aishell.tgz文件大小在 15GB 左右下载时间取决于网速。下完了用 tar 解压tar -xvzf data_aishell.tgz如果不想用命令行直接在浏览器里打开上面的链接下载也可以。解压过程会比较久因为里面有大量小的 WAV 文件。解压完成后你会得到一个data_aishell目录。3.2 搞懂数据集的目录结构打开data_aishell结构是这样的data_aishell/ ├── transcript/ │ └── aishell_transcript_v0.8.txt └── wav/ ├── dev/ ├── test/ └── train/wav目录下面按dev、test、train三个集合划分每个集合里再按说话人编号分目录比如train/S0002目录里放的是说话人 S0002 的所有录音。每个 WAV 文件的命名很有规律例如BAC009S0002W0122.wav这串编号里包含了语料库、说话人和句子编号。另一个关键文件是transcript/aishell_transcript_v0.8.txt它保存了所有语音文件的转录文本。文本内容长这样BAC009S0002W0122 而 对 于 这 样 的 需 求注意这里每个汉字之间都可能有空格但空格不是语音内容的一部分。我们需要在解析时把这些空格去掉否则后面训练时模型会学出一堆无意义的空白标签。3.3 解析 transcript 并生成样本列表训练之前要把转录文本和 WAV 文件路径对应起来。官方 transcript 文件并不直接告诉你某个 WAV 在哪个子目录所以更稳定的做法是遍历wav目录用文件名的 stem 去查 transcript 字典。下面这段代码是数据准备的起点from pathlib import Path BASE Path(data_aishell) TRANSCRIPT_FILE BASE / transcript / aishell_transcript_v0.8.txt transcripts {} with TRANSCRIPT_FILE.open(r, encodingutf-8) as f: for line in f: parts line.strip().split() if len(parts) 2: continue uid parts[0] text .join(parts[1:]) # 去掉所有空格只保留中文 transcripts[uid] text print(ftranscripts: {len(transcripts)})然后根据数据集划分构建样本列表def build_items(split, limitNone): wav_dir BASE / wav / split items [] for wav_path in sorted(wav_dir.rglob(*.wav)): uid wav_path.stem if uid in transcripts: items.append({ audio: str(wav_path), text: transcripts[uid], }) if limit is not None and len(items) limit: break return items train_items build_items(train) dev_items build_items(dev) test_items build_items(test) print(len(train_items), len(dev_items), len(test_items))这样我们得到了三个 Python 列表每个元素包含音频绝对路径和对应的中文文本。为什么不用官方直接提供的文件列表因为官方 transcript 没有写清路径树里的层级关系自己遍历 wav 目录是最稳的永远不会因为某个文件夹层级变化而失效。3.4 先跑小样本再上全量效率翻倍新手最常见的错误是一上来就用全部 178 小时数据训练结果训练了两天才发现数据预处理有个 bugCER 一直降不下来白白浪费大量时间。正确做法是先跑一个“最小闭环”训练集只取 200 条开发集只取 50 条训练只跑 3 个 epoch确认 loss 下降、能够解码出中文再放开全量数据这就是我在完整代码里设置LIMIT_TRAIN和LIMIT_DEV的原因。小样本训练可能只要几分钟但能把数据读取、标签对齐、模型前向、梯度回传、评估解码这些环节全部验证一遍。4. 核心训练代码微调 Wav2Vec2 中文 ASR 模型4.1 加载预训练模型和 Processor我选择的模型是jonatasgrosman/wav2vec2-large-xlsr-53-chinese-zh。它基于 XLSR-53 多语言预训练权重又用大量中文音频做了二次预训练并且自带一个面向中文的 CTC 分类头。我们直接用它在 AISHELL-1 上继续微调会比从wav2vec2-base重新学中文快得多。直接看加载代码from transformers import Wav2Vec2ForCTC, Wav2Vec2Processor MODEL_NAME jonatasgrosman/wav2vec2-large-xlsr-53-chinese-zh processor Wav2Vec2Processor.from_pretrained(MODEL_NAME) model Wav2Vec2ForCTC.from_pretrained(MODEL_NAME, ctc_loss_reductionmean)这里的processor包含两部分feature_extractor负责把音频波形变成模型输入的input_valuestokenizer负责把中文文本变成标签 ID。加载模型时设置ctc_loss_reductionmean表示每个 batch 内所有样本的 CTC loss 取平均这样在样本长度不一时梯度更稳定。4.2 自定义 Dataset 与填充策略Hugging Face 的 Trainer 支持自定义torch.utils.data.Dataset所以我们的数据接口非常简单。核心思路是在__getitem__里读取 WAV用processor把音频转成input_values再把中文文本转成标签 ID。import torch import librosa class AISHELLDataset(torch.utils.data.Dataset): def __init__(self, items, processor): self.items items self.processor processor def __len__(self): return len(self.items) def __getitem__(self, idx): item self.items[idx] audio, sr librosa.load(item[audio], sr16000) input_values self.processor(audio, sampling_ratesr).input_values[0] with self.processor.as_target_processor(): labels self.processor.tokenizer(item[text]).input_ids return {input_values: input_values, labels: labels}这段代码里有两处容易出错。一是sr16000这是强制重采样到 16kHz避免某些 WAV 文件采样率不统一。二是标签需要放在as_target_processor()的上下文里处理这样tokenizer会知道当前是在处理标签而不是音频。接下来是 DataCollator它的作用是把一个 batch 里的样本填充到相同长度。音频填充的部分用 0 填充对应attention_mask需要能知道哪些位置是真实音频标签填充的部分用-100填充这样 CTC loss 会自动忽略这些位置。from dataclasses import dataclass from typing import Optional, Union dataclass class DataCollatorCTCWithPadding: processor: Wav2Vec2Processor padding: Union[bool, str] True max_length: Optional[int] None max_length_labels: Optional[int] None pad_to_multiple_of: Optional[int] None pad_to_multiple_of_labels: Optional[int] None def __call__(self, features): input_features [{input_values: f[input_values]} for f in features] label_features [{input_ids: f[labels]} for f in features] batch self.processor.pad( input_features, paddingself.padding, max_lengthself.max_length, pad_to_multiple_ofself.pad_to_multiple_of, return_tensorspt, ) with self.processor.as_target_processor(): labels_batch self.processor.pad( label_features, paddingself.padding, max_lengthself.max_length_labels, pad_to_multiple_ofself.pad_to_multiple_of_labels, return_tensorspt, ) labels labels_batch[input_ids].masked_fill(labels_batch.attention_mask.ne(1), -100) batch[labels] labels return batch这个 Collator 是我从 Hugging Face 官方 Wav2Vec2 微调示例里一直沿用的非常稳定。processor.pad会同时处理音频的input_values和文本的input_ids并且自动生成音频的attention_mask。标签部分用masked_fill把 padding 位置替换成-100这是 CTC 训练的标准操作。4.3 CER 评估函数中文 ASR 最常用的指标是 CER字错误率也就是识别结果和目标文本之间的最小编辑距离再除以目标文本的字数。我们不想引入太多额外库直接用 Python 实现一个编辑距离计算函数就够了。import numpy as np def edit_distance(s1, s2): m, n len(s1), len(s2) dp [[0] * (n 1) for _ in range(m 1)] for i in range(m 1): dp[i][0] i for j in range(n 1): dp[0][j] j for i in range(1, m 1): for j in range(1, n 1): if s1[i - 1] s2[j - 1]: dp[i][j] dp[i - 1][j - 1] else: dp[i][j] min(dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1]) 1 return dp[m][n] def compute_cer(labels, preds): total_ed 0 total_chars 0 for ref, hyp in zip(labels, preds): ref ref.replace( , ) hyp hyp.replace( , ) if not ref: continue total_ed edit_distance(ref, hyp) total_chars len(ref) return total_ed / total_chars if total_chars 0 else 1.0 def compute_metrics(pred): pred_logits pred.predictions pred_ids np.argmax(pred_logits, axis-1) pred_str processor.batch_decode(pred_ids) label_ids pred.label_ids label_ids[label_ids -100] processor.tokenizer.pad_token_id label_str processor.batch_decode(label_ids) cer compute_cer(label_str, pred_str) return {cer: cer}CTC 解码时预测序列里会有重复帧processor.batch_decode已经默认做了去重和去空白处理所以这里不需要再手动处理。CER 越小越好通常 AISHELL-1 上的强模型 CER 可以做到 5% 到 10%但我们小样本快速验证不会这么低重点是看下降趋势。4.4 一个可直接运行的 train_asr.py把前面所有模块拼起来就是一个最小可运行的完整训练脚本。将它保存为train_asr.py然后执行python train_asr.py就能开始训练。# train_asr.py import torch import librosa import numpy as np from dataclasses import dataclass from pathlib import Path from typing import Optional, Union from transformers import ( Wav2Vec2ForCTC, Wav2Vec2Processor, Trainer, TrainingArguments, ) BASE Path(data_aishell) TRANSCRIPT_FILE BASE / transcript / aishell_transcript_v0.8.txt MODEL_NAME jonatasgrosman/wav2vec2-large-xlsr-53-chinese-zh # 快速验证用正式训练可以改成 None LIMIT_TRAIN 200 LIMIT_DEV 50 # 1. 解析转录文本 transcripts {} with TRANSCRIPT_FILE.open(r, encodingutf-8) as f: for line in f: parts line.strip().split() if len(parts) 2: continue transcripts[parts[0]] .join(parts[1:]) def build_items(split, limitNone): wav_dir BASE / wav / split items [] for wav_path in sorted(wav_dir.rglob(*.wav)): uid wav_path.stem if uid in transcripts: items.append({audio: str(wav_path), text: transcripts[uid]}) if limit is not None and len(items) limit: break return items # 2. 数据集 class AISHELLDataset(torch.utils.data.Dataset): def __init__(self, items, processor): self.items items self.processor processor def __len__(self): return len(self.items) def __getitem__(self, idx): item self.items[idx] audio, sr librosa.load(item[audio], sr16000) input_values self.processor(audio, sampling_ratesr).input_values[0] with self.processor.as_target_processor(): labels self.processor.tokenizer(item[text]).input_ids return {input_values: input_values, labels: labels} # 3. Collator dataclass class DataCollatorCTCWithPadding: processor: Wav2Vec2Processor padding: Union[bool, str] True max_length: Optional[int] None max_length_labels: Optional[int] None pad_to_multiple_of: Optional[int] None pad_to_multiple_of_labels: Optional[int] None def __call__(self, features): input_features [{input_values: f[input_values]} for f in features] label_features [{input_ids: f[labels]} for f in features] batch self.processor.pad( input_features, paddingself.padding, max_lengthself.max_length, pad_to_multiple_ofself.pad_to_multiple_of, return_tensorspt, ) with self.processor.as_target_processor(): labels_batch self.processor.pad( label_features, paddingself.padding, max_lengthself.max_length_labels, pad_to_multiple_ofself.pad_to_multiple_of_labels, return_tensorspt, ) labels labels_batch[input_ids].masked_fill(labels_batch.attention_mask.ne(1), -100) batch[labels] labels return batch # 4. CER 评估 def edit_distance(s1, s2): m, n len(s1), len(s2) dp [[0] * (n 1) for _ in range(m 1)] for i in range(m 1): dp[i][0] i for j in range(n 1): dp[0][j] j for i in range(1, m 1): for j in range(1, n 1): if s1[i - 1] s2[j - 1]: dp[i][j] dp[i - 1][j - 1] else: dp[i][j] min(dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1]) 1 return dp[m][n] def compute_cer(labels, preds): total_ed 0 total_chars 0 for ref, hyp in zip(labels, preds): ref ref.replace( , ) hyp hyp.replace( , ) if not ref: continue total_ed edit_distance(ref, hyp) total_chars len(ref) return total_ed / total_chars if total_chars 0 else 1.0 def compute_metrics(pred): pred_logits pred.predictions pred_ids np.argmax(pred_logits, axis-1) pred_str processor.batch_decode(pred_ids) label_ids pred.label_ids label_ids[label_ids -100] processor.tokenizer.pad_token_id label_str processor.batch_decode(label_ids) cer compute_cer(label_str, pred_str) return {cer: cer} # 5. 主流程 processor Wav2Vec2Processor.from_pretrained(MODEL_NAME) model Wav2Vec2ForCTC.from_pretrained(MODEL_NAME, ctc_loss_reductionmean) train_items build_items(train, LIMIT_TRAIN) dev_items build_items(dev, LIMIT_DEV) train_dataset AISHELLDataset(train_items, processor) dev_dataset AISHELLDataset(dev_items, processor) data_collator DataCollatorCTCWithPadding(processorprocessor, paddingTrue) training_args TrainingArguments( output_dir./aishell-wav2vec2, per_device_train_batch_size4, per_device_eval_batch_size4, gradient_accumulation_steps4, evaluation_strategysteps, eval_steps100, save_steps200, logging_steps20, learning_rate1e-4, warmup_steps100, num_train_epochs3, fp16True, save_total_limit2, load_best_model_at_endTrue, metric_for_best_modelcer, greater_is_betterFalse, report_tonone, ) trainer Trainer( modelmodel, argstraining_args, data_collatordata_collator, train_datasettrain_dataset, eval_datasetdev_dataset, compute_metricscompute_metrics, ) trainer.train() trainer.save_model(./aishell-wav2vec2-final) processor.save_pretrained(./aishell-wav2vec2-final)几个地方我说一下。LIMIT_TRAIN 200表示只取前 200 条训练数据这是为了让你第一次运行不至于等几个小时正式训练时把它改成None即可。per_device_train_batch_size4加上gradient_accumulation_steps4等效 batch size 是 16。如果显存不够把 batch size 改成 1、梯度累积改成 8效果类似但显存占用会降很多。如果用的是 Transformers 4.46 以上版本evaluation_strategy可能提示改成eval_strategy两者都是合法的新版也能跑只是日志里会有 deprecation warning。不想看到 warning 的话直接改成eval_strategy。5. 评估与推理模型训练完怎么用起来5.1 在测试集上计算 CER训练结束后我们最关心的是模型在没见过的测试集上表现如何。通过 Trainer 的evaluate方法可以直接用训练好的模型跑评估。test_items build_items(test) test_dataset AISHELLDataset(test_items, processor) test_result trainer.evaluate(eval_datasettest_dataset, metric_key_prefixtest) print(test_result)这里需要注意build_items(test)会构建完整测试集如果测试集比较大并且你的显存不够可以先给build_items(test, limit200)或者使用per_device_eval_batch_size1。输出里的test_cer就是测试集上的字错误率。5.2 单条 WAV 快速转录训练完模型后大多数时候我们不是要跑完整评估集而是想对一条新录音做识别。下面这个脚本可以直接加载保存好的模型识别一个 WAV 文件并打印中文文本。import torch import librosa from transformers import Wav2Vec2Processor, Wav2Vec2ForCTC model_path ./aishell-wav2vec2-final processor Wav2Vec2Processor.from_pretrained(model_path) model Wav2Vec2ForCTC.from_pretrained(model_path) model.eval() def transcribe(wav_path): audio, sr librosa.load(wav_path, sr16000) inputs processor(audio, sampling_ratesr, return_tensorspt) with torch.no_grad(): logits model(input_valuesinputs.input_values).logits pred_ids torch.argmax(logits, dim-1)[0] return processor.decode(pred_ids) if __name__ __main__: text transcribe(data_aishell/wav/test/S0002/BAC009S0002W0122.wav) print(text)如果你的模型在 GPU 上训练之后想在 CPU 上做推理也没问题。from_pretrained加载权重时会自动适配当前设备。想用 GPU 加速推理时在transcribe函数里加一句model.to(cuda)并把input_values也移动到 GPU 上。5.3 模型保存、加载与后续部署思路训练脚本里已经保存了最终模型和 processor保存路径是./aishell-wav2vec2-final。这个目录里会包含model.safetensors或pytorch_model.bin、config.json、preprocessor_config.json以及 tokenizer 相关的文件。后续不管是想继续训练还是想部署成 HTTP 服务这一个目录就够了。部署方面如果只是做个内部工具直接 Flask 或 FastAPI 把这个推理函数包成一个接口就行。如果是生产环境需要低延迟可以考虑用 ONNX Runtime 导出但 Wav2Vec2 的输入长度是动态的导出的关键点是让input_values的序列维度保持动态轴。实际落地时我发现直接用 PyTorch 服务加 GPU 推理往往比转 ONNX 更省事尤其是中文识别本来就需要较大的模型先跑通再优化延迟。6. 训练过程中的常见问题与避坑记录6.1 Loss 一直不降CER 接近 1这基本上是最常见的问题。我刚接触 AISHELL-1 时也踩过训练了好几个 epochLoss 在 10 附近纹丝不动解码结果全是空的。排查到最后发现transcript 文件里的中文之间有空格我一开始没有去掉直接把空格字符也当成标签了。CTC 的空标签和真正的空格字符混在一起模型完全学不出有效规律。解决办法就是解析文本时用.join(parts[1:])把所有空格去掉。另外如果 CER 一直等于 1还要检查音频读取后的波形是否全是 0。某个 WAV 文件损坏、目录路径不对librosa.load会返回一个静音信号模型自然什么都预测不出来。建议在 Dataset 里加一个简单的断言比如if np.abs(audio).max() 0: continue或者确认len(input_values) 0。6.2 CUDA OOM 和 CPU 训练建议很多人会卡在显存不足上。Wav2Vec2 Large 模型本来就有 3 亿多参数再加上音频序列特别长batch size 稍微开大一点就会爆显存。我的建议是先把per_device_train_batch_size降到 1gradient_accumulation_steps提高到 8这样等效 batch size 仍是 8但显存占用会小很多。开启gradient_checkpointing它的代价是增加一些计算时间但能大幅减少中间激活值显存。如果还是爆显存就把LIMIT_TRAIN调小先跑通 50 条数据再说。完全没有 GPU 的话把fp16True改为fp16Falsebatch size 保持 1训练 3 个 epoch200 条数据大约需要一二十分钟CPU 也能接受。全量数据就不建议 CPU 硬跑了。6.3 音频读取异常与预处理细节librosa.load默认会把音频转成单声道 float 数组范围在 [-1, 1] 之间。AISHELL-1 本身就是单声道 16kHz所以这里不会出问题。但如果你的自定义数据包含双声道或者采样率不是 16kHz一定要重新指定sr16000否则模型会因为输入特征长度变化而报错或表现异常。还有一个小坑librosa.load依赖于soundfile或audioread后端如果你的系统里soundfile没装好读取 WAV 会报“Error opening file”。通常执行pip install soundfile就能解决。如果在 Windows 上遇到音频库底层 DLL 缺失优先跑 WSL2别浪费时间折腾本机环境。6.4 关于 AISHELL-1 的授权与使用边界AISHELL-1 可以免费用于研究和个人学习这一点对新手很友好。但如果未来你要把它用在商业产品上建议先联系版权方确认授权范围。开源不等于完全无限制商用特别是语音数据涉及发音人个人权益商业使用前需要格外谨慎。另一个需要注意的点是AISHELL-1 转录文本不包含标点符号所以模型默认不会输出标点。如果你需要的是“带标点的会议纪要”后面还得再接一个中文标点恢复模型这不是 ASR 模型本身要解决的问题。我把整个流程前前后后跑了很多遍之后最大的体会是中文 ASR 的障碍从来不在“模型有多难”而在“数据和工程链路上有多少小坑”。AISHELL-1 数据干净、划分明确Wav2Vec2 预训练模型也足够成熟只要先把小样本链路跑通后续扩展到全量数据只是时间问题。最后再分享一个小技巧每次调整数据预处理后不要急着全量重训先用 100 条数据跑一个 epoch看一眼 loss 和 CER 是否符合预期。这种“先小后大”的习惯能帮你省下大量的 GPU 经费和咖啡。
返回列表