ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

HuBERT 自监督语音预训练标签生成实战:fairseq simple_kmeans 分片特征提取与 K-means 聚类全流程

HuBERT 自监督语音预训练标签生成实战:fairseq simple_kmeans 分片特征提取与 K-means 聚类全流程 HuBERT 自监督语音预训练标签生成实战fairseq simple_kmeans 分片特征提取与 K-means 聚类全流程【免费下载链接】unilmLarge-scale Self-supervised Pre-training Across Tasks, Languages, and Modalities项目地址: https://gitcode.com/GitHub_Trending/un/unilm本文基于本仓库 kosmos-2/fairseq/examples/hubert/simple_kmeans 目录完整讲解如何从*.tsv音频清单出发经过「特征提取 → K-means 聚类 → K-means 应用」三步产出 HuBERT 预训练所需的帧级伪标签.km文件与哑词典dict.km.txt。读完本文你将掌握分片shard并行的特征抽取方法、MFCC 与 HuBERT 深层特征的提取差异、MiniBatchKMeans 聚类的参数调优以及如何把标签流水线无缝衔接到 HuBERT 预训练 流程中。一、流水线总览与脚本清单HuBERTHidden-unit BERT通过迭代式的聚类伪标签进行自监督预训练先用传统声学特征MFCC做第一轮聚类生成伪标签训练一个 HUBERT 模型后再用模型中间层表示做第二轮聚类生成更高质量的标签如此迭代。simple_kmeans目录正是为这套标签制备流程提供的最小化工具集。整个流水线包含三个步骤特征提取feature extraction把每条音频转成定长的帧级特征向量K-means 聚类k-means clustering在特征上训练 K-means 模型把特征空间离散化为固定数量的簇K-means 应用k-means application用训练好的聚类模型为每条音频的每一帧打上簇标签即伪标签。目录内共 8 个文件其中 6 个是可执行脚本、1 个公共工具模块、1 个说明文档文件职责dump_mfcc_feature.py提取 39 维 MFCC delta ddelta 特征第一轮迭代标签dump_hubert_feature.py从已训练 HuBERT 模型指定 Transformer 层提取特征后续迭代标签dump_w2v2_feature.py从 wav2vec 2.0 模型提取特征可选变体dump_hubert_feature_s2t.py面向 speech-to-text 场景、从压缩 zip 包读音频的特征提取变体learn_kmeans.py在特征分片上训练 MiniBatchKMeans 聚类模型dump_km_label.py用聚类模型为特征打帧级簇标签feature_utils.py分片范围划分、TSV 路径迭代、特征落盘的公共工具分片shard并行设计所有特征提取和标签生成脚本都支持nshardrank两个参数其核心逻辑在 feature_utils.py 的get_shard_range中给定总样本数tot按start round(tot / nshard * rank) end round(tot / nshard * (rank 1))把 TSV 中的音频列表切成nshard份rank进程只处理[start, end)区间内的那一段。这意味着你可以同时启动nshard个进程例如在多卡或多机上每个进程指定不同的rank即可把大规模语料的特征提取和打标签任务线性加速而每份输出独立成文件最后用cat合并即可。二、数据准备TSV 音频清单格式simple_kmeans的所有脚本都以*.tsv文件作为输入索引。TSV 的格式非常简洁第一行音频根目录root dir之后每一行一条音频相对于根目录的子路径。root-dir audio-path-1 audio-path-2 ...实际读取时get_path_iterator 会执行line.split(\t)把每一行按制表符切分取第一列作为子路径并与第一行的根目录拼接成完整路径{root}/{subpath}如果该行还有第二列则解析为整数nsample作为该音频的期望采样帧数ref_len用于在读取时校验实际波形长度是否与清单一致允许 160 帧以内的偏差超出会打印 warning 而不会中断。实践要点保持 TSV 中音频的采样率与特征提取器的sample_rate一致默认 16000 Hz否则sf.read后的assert sr sample_rate会直接抛错典型用法是每个数据集分片如train、valid各生成一个${tsv_dir}/${split}.tsv后续所有命令都通过split参数引用。三、特征提取特征提取是把波形转换为帧级特征向量的第一步也是标签质量的源头。3.1 MFCC 特征第 1 轮迭代假设 TSV 位于${tsv_dir}/${split}.tsv为 HuBERT 第 1 轮迭代训练提取 39 维 mfccdeltaddelta 特征运行python dump_mfcc_feature.py ${tsv_dir} ${split} ${nshard} ${rank} ${feat_dir}该命令会把 TSV 切成${nshard}份并为第${rank}份rank是[0, nshard-1]内的整数提取特征输出保存到${feat_dir}/${split}_${rank}_${nshard}.npy ${feat_dir}/${split}_${rank}_${nshard}.len其中.npy存放拼接后的特征矩阵按帧堆叠.len每行记录一条音频对应的帧数两者配合即可还原出每条音频的特征片段。命令还支持可选参数--sample_rate默认 16000用于指定目标采样率。从源码看MfccFeatureReader.get_feats 的特征计算路径为用soundfile读取波形双声道取均值转单声道wav.mean(-1)在torch.no_grad()下调用torchaudio.compliance.kaldi.mfcc计算 Kaldi 风格 MFCCuse_energyFalse得到(time, freq)的 13 维静态系数转置后分别用torchaudio.functional.compute_deltas计算一阶差分delta和二阶差分ddelta将[mfcc, deltas, ddeltas]在特征维拼接得到 13 × 3 39 维特征。MFCC 特征的帧率默认为 100Hz即每 10ms 一帧这与后面 HuBERT 预训练配置中的label_rate直接对应。3.2 HUBERT 特征后续迭代当第 1 轮训练出 HuBERT 模型后后续迭代需要从模型中间层提取更语义化的特征。从保存在${ckpt_path}的训练好的 HuBERT 模型的第${layer}个 Transformer 层提取特征运行python dump_hubert_feature.py ${tsv_dir} ${split} ${ckpt_path} ${layer} ${nshard} ${rank} ${feat_dir}输出同样保存在${feat_dir}/${split}_${rank}_${nshard}.{npy,len}。如果显存溢出out-of-memory通过--max_chunk减小分块大小。HubertFeatureReader 的实现要点如下通过fairseq.checkpoint_utils.load_model_ensemble_and_task([ckpt_path])加载模型与任务配置模型置为eval()并搬运到 CUDA音频的采样率校验直接使用self.task.cfg.sample_rate与任务配置保持一致若任务配置task.cfg.normalize为真则先对波形做 LayerNorm 归一化HuBERT 预训练配置 中normalize: false注释明确要求must be consistent with extractor即特征提取与预训练时该开关必须一致为避免长音频显存爆炸波形按max_chunk默认 1600000 个采样点约 100 秒 16kHz切块逐块调用model.extract_features(source..., padding_maskNone, maskFalse, output_layerself.layer)提取第layer层表示最后torch.cat拼回完整序列。HuBERT 特征的默认帧率为 50Hz即每 20ms 一帧对应extract_features在 1/320 采样后的输出因此后续预训练配置的label_rate应相应设为 50。3.3 补充变体wav2vec 2.0 与 S2T 场景目录还提供了两个可选的提取器dump_w2v2_feature.py从 wav2vec 2.0 模型提取特征命令格式与dump_hubert_feature.py完全一致。其layer参数同样按 1 基HuBERT 惯例解释内部调用self.model.extract_features(..., layerself.layer - 1)取res[x]。dump_hubert_feature_s2t.py面向 speech-to-text 数据集TSV 由csv.DictReader解析按audio字段取路径且支持从未压缩的 zip 包中按audio:index:length的格式切片读取波形适合音频以 zip 归档存放的场景。四、K-means 聚类特征提取完成后需要在特征上训练一个 K-means 模型。以下命令在${split}数据的 10% 子集上拟合一个含${n_clusters}个簇的 K-means 模型python learn_kmeans.py ${feat_dir} ${split} ${nshard} ${km_path} ${n_cluster} --percent 0.1训练好的模型会通过joblib.dump保存到${km_path}。设置--percent -1使用全部数据更多 K-means 选项可通过-h标志查看。4.1 采样逻辑--percentload_feature_shard 展示了子采样如何工作脚本先读取.len文件得到每条音频的帧数并计算累积偏移然后若percent 0直接以mmap_moder内存映射加载整个.npy文件不占额外内存否则nsample ceil(总音频数 × percent)用np.random.choice无放回地随机选nsample条音频再按偏移量把对应帧段concatenate出来参与训练。最后load_feature会把nshard个分片各自采样的结果沿帧轴拼接成一个大矩阵一次性喂给聚类模型。日志会打印sampled N utterances, M frames以及加载后的特征维度。4.2 MiniBatchKMeans 参数详解聚类模型在 get_km_model 中构造使用的是 scikit-learn 的MiniBatchKMeans小批量 K-means相比经典 K-means 更适合大规模帧级数据全部可配置参数如下表均来自learn_kmeans.py的 argparse 默认值参数默认值含义--seed0随机种子控制采样与初始化可复现--percent-1参与训练的数据比例-1表示全部数据--initk-means初始簇中心选择策略--max_iter100单次小批量迭代的最大轮数--batch_size10000每个小批次的样本数--tol0.0相对容差用于提前停止--max_no_improvement100连续多少轮无改进即提前收敛--n_init20不同随机初始化尝试次数--reassignment_ratio0.0允许被重新分配的最大中心点比例模型中还固定了verbose1打印训练进度和compute_labelsFalse聚类阶段不计算标签加快训练。训练完成后脚本用km_model.score(feat)计算并打印平均 inertia-score / len(feat)作为聚类质量的参考指标。4.3 簇数量选择n_cluster是决定标签词典大小的关键超参数需由训练计划指定本仓库配置中以 ???” 占位等待传入。实践中建议根据语料规模和后续任务的词典需求在数百量级内尝试不同取值并通过训练损失与下游任务效果来权衡。五、K-means 应用与标签生成聚类模型训练好后用它为${split}的每一帧打上簇标签python dump_km_label.py ${feat_dir} ${split} ${km_path} ${nshard} ${rank} ${lab_dir}该命令为${nshard}个分片中的第${rank}个分片提取标签输出到${lab_dir}/${split}_${rank}_${nshard}.km。5.1 打标签的原理ApplyKmeans 把 joblib 加载出的cluster_centers_转置为(特征维, 簇数)的矩阵并预计算簇中心的平方范数Cnorm。对每个特征向量x通过展开的欧氏距离dist x^2 - 2 * x·C Cnorm取argmin得到最近的簇编号。矩阵可搬运到 CUDA 上用torch.matmul加速若输入是 numpy 数组则走np.matmul的 CPU 路径。整个过程不需要重新加载模型只依赖聚类中心因此非常轻量。5.2 输出格式dump_label 逐条音频读取特征片段利用.len偏移量切分为每条音频输出一行由空格分隔的簇编号序列用tqdm显示进度即帧级伪标签3 17 17 42 8 ...六、合并分片与创建哑词典6.1 合并所有分片特征和标签都按 shard 分散存储合并${split}的所有分片标签for rank in $(seq 0 $((nshard - 1))); do cat $lab_dir/${split}_${rank}_${nshard}.km done $lab_dir/${split}.km合并后得到{train,valid}.km帧对齐伪标签文件。6.2 创建哑词典HuBERT 预训练要求一个与标签对应的词典文件dict.km.txt。由于伪标签是通过聚类得到的离散编号无需真实词表直接生成一个哑词典即可每个簇编号占一行for x in $(seq 0 $((n_clusters - 1))); do echo $x 1 done $lab_dir/dict.km.txt这会产生n_clusters行形如0 1、1 1的条目即{簇编号} {频次}格式。七、与 HuBERT 预训练流程的衔接按上述步骤产出的三样东西正是 HuBERT 预训练所需的数据输入详见 HuBERT 主 README{train,valid}.tsv波形清单{train,valid}.km帧对齐伪标签dict.km.txt哑词典。其中label_rate标签帧率必须与聚类所用特征的帧率一致MFCC 特征默认 100HzHuBERT 特征默认 50Hz。以 base 模型预训练为例参考 hubert_base_librispeech.yaml假设 TSV 保存在/path/to/data、.km标签保存在/path/to/labels、标签帧率为 100Hzpython fairseq_cli/hydra_train.py \ --config-dir /path/to/fairseq-py/examples/hubert/config/pretrain \ --config-name hubert_base_librispeech \ task.data/path/to/data task.label_dir/path/to/labels task.labels[km] model.label_rate100该配置中与标签流水线直接相关的关键项包括task.label_rate: ${model.label_rate}标签帧率与聚类特征帧率绑定task.labels: ???标签类型预训练时传[km]task.sample_rate: 16000音频采样率须与特征提取时的sample_rate一致task.normalize: false波形归一化开关须与特征提取器保持一致。八、实践建议与注意事项并行提速nshard建议取可用的 GPU/CPU 核数同时启动nshard个rank0..nshard-1的进程可把特征提取与打标签阶段整体缩短约nshard倍learn_kmeans.py因为是全量加载特征后统一训练不需要分片运行。内存与显存长音频导致显存溢出时优先调小dump_hubert_feature.py的--max_chunk聚类阶段learn_kmeans.py默认以mmap_mode映射特征文件采样时只把选中的帧段读入内存控制内存占用。可复现性learn_kmeans.py通过--seed控制np.random.choice的采样与 K-means 初始化dump_km_label.py的打标签过程是确定性的不依赖随机种子。一致性约束采样率、normalize开关、label_rate三者必须在「特征提取 → 聚类 → 预训练」三个阶段保持一致这是本流水线最容易踩坑、也最容易被忽视的地方。字典与标签配套dict.km.txt的条目数必须等于n_cluster否则预训练数据加载时词典与标签编号会不匹配。【免费下载链接】unilmLarge-scale Self-supervised Pre-training Across Tasks, Languages, and Modalities项目地址: https://gitcode.com/GitHub_Trending/un/unilm创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表