ARTICLE DETAIL

资讯详情

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

wav2vec 2.0深度拆解:从量化器到对比学习的语音自监督预训练

wav2vec 2.0深度拆解:从量化器到对比学习的语音自监督预训练 “wav2vec 2.0”是我在语音自监督学习这个方向上唯一会跟人反复推荐三遍以上的模型。2020年Meta AI把它放到NeurIPS上的时候整个语音圈的震撼程度不亚于当年BERT在NLP领域的落地只用无标注语音做预训练在LibriSpeech上10分钟标注数据就能打到之前需要100小时标注才能勉强追上的水平。这篇文章作为系列第三篇前面已经铺垫了自监督预训练的基本范式这次我们直接把它拆开看从整体设计到损失函数从模块取舍到复现踩坑尽量还原作者当时做选择时的思考过程而不是背一遍结构图。适合的读者也先说一下你不需要是语音专业出身但最好了解Transformer和对比学习的基本概念。如果你是做NLP、CV的想了解“语音版的BERT”到底是怎么把连续信号变成离散Token的这篇也能帮你把思路捋顺。这次我们不聊调包而是把它当成一个完整的工程系统来解剖。1. 从整体看wav2vec 2.0到底在做什么1.1 自监督预训练的三种路线为什么wav2vec 2.0选了这一条在wav2vec 2.0之前语音自监督学习大体上分成两个流派。一个是预测式代表是CPC和wav2vec 1.0。思路很简单用过去一段时间的上下文预测未来若干帧的特征。这类方法学到的表示对时序关系比较敏感但毛病也明显——它只建模了“前面能预测后面”这个单向关系对全局双向上下文的利用非常弱而且预测目标用的是连续特征模型很容学到声学层面的冗余细节。另一个是生成式/重建式代表是DeCoAR、TERA这类。思路更像传统自编码器把整段语音编码再还原。问题是语音信号的局部信息太冗余直接重建会让模型把大量能力花在“还原基频、还原音色”这种表层任务上学到的表示反而不够语义化。wav2vec 2.0没有继续在这两条路上走而是把NLP里BERT的掩码预测思路搬到了语音上。但它不是简单替换任务——因为语音不是离散token序列没有天然切分好的“词”或“子词”直接把BERT拿过来根本没法用。所以它做了一个非常重要的前置处理用一个可学习的量化器把连续语音特征变成动态的离散码本。这样一来就有了清晰的预测目标掩码后补全。注意这里“动态”两个字是关键。语音没有固定词表不同说话人、不同口音、不同环境下的同一个音素声学特征差距可以非常大。所以wav2vec 2.0的码本不是事先定义好的音素表而是在训练中跟特征编码器联合更新的。1.2 三段式结构连续编码、离散量化、上下文建模wav2vec 2.0的整体结构可以看成三段流水线各司其职特征编码器Feature Encoder多层CNN把原始波形下采样成帧级连续向量。可以理解为先把每秒16000个采样点压缩成50个“声音片段”的向量表示。量化器Quantizer把每个时间步的连续向量映射成离散码本中的码字。这是整个模型的“分词器”负责决定“这段声音属于哪个token”。上下文网络Context Network堆叠的Transformer接收被mask的连续特征序列输出融合了全局上下文信息的表示。这个三段式设计有很清晰的工程逻辑。可以把原始音频想象成一本没有空格、没有标点的连续字母流特征编码器相当于先做粗分词把连续语音切成一帧一帧量化器相当于给每帧指定一个候选“词”Transformer则负责根据上下文理解“这个词在当前句子里到底是什么含义”。对比学习发生在Transformer输出和量化器输出之间被mask位置的Transformer输出视为“预测结果”量化器得到的离散码字视为“标准答案”模型要能从一堆干扰项中把正确答案认出来。1.3 为什么非要用对比损失而不是直接交叉熵重建这是理解wav2vec 2.0时最值得想明白的问题。BERT在NLP里用的是交叉熵分类词表是固定的输入一个被mask的句子输出在每个词上的概率分布。但wav2vec 2.0的词表——也就是量化码本——是模型自己学出来的不是先验定义的“音素表”。如果直接用交叉熵会有两个问题第一码本本身还在变。训练早期量化器学得并不好很多码字对应的声学类别模糊拿一个不稳定目标做交叉熵模型会陷入“追着一个移动靶子打”的困境。第二交叉熵要求每个位置必须扛分给一个类别对预测置信度的要求极高。而语音的连续特征里mask掉的位置可能对应多个合理的声学单元硬性分类会让模型过度自信反而学不到稳健的上下文表示。对比损失的好处是它只要求“预测结果与正确答案的相似度高于干扰项”。它不需要维护一个概率分布只需要拉开距离这对连续信号天然友好。2. 核心模块逐个拆每个设计都是一个回答2.1 特征编码器用320倍下采样把音频变成50Hz的“帧词”特征编码器在wav2vec 2.0里是一个7层CNN。第一层卷积核大小为10、步长为5后续几层卷积核为3或2、步长全部为2。整体下采样倍数是5乘以2的6次方也就是320倍。对于16kHz采样率的语音1秒钟16000个采样点经过编码器之后变成了50个特征帧也就是每帧对应约20毫秒的音频。这个下采样倍数的选择不是随便定的。20毫秒一帧刚好跟传统语音信号处理里常用的帧长25毫秒窗、10毫秒移接近保留了音素级别的基本粒度。太粗会丢掉发音细节太细则Transformer的序列长度过长计算成本不可控。50Hz这个频率意味着1小时音频会变成18万帧这个数量级对Transformer来说已经需要多卡并行才能吃得下了。每个输出的特征维度是512。可以这样理解特征编码器干的事是把一段连续波形“正交化”到一个高维空间让后续的量化器和Transformer有足够的信息量去提取模式。这一步不是端到端学习的旁观者它的梯度同时来自两条路径——一条通过Transformer和对比损失传回来一条通过量化器的多样性损失传回来。这让特征编码器学到的特征既保留声学细节又逐渐向“可分类、可预测”的方向调整。实际使用中我很建议你把它当成一个可学习的“前端特征提取器”来看而不是固定的滤波器组这样才能理解为什么wav2vec 2.0在带噪语音上的表现能超过传统的Fbank特征。2.2 量化器Gumbel Softmax是怎么把连续向量变成离散码字的量化器是wav2vec 2.0里最容易让人绕晕的部分也是整个模型的灵魂。它的任务是把特征编码器输出的连续向量z映射成一组离散的码字组合。论文里选择了乘积量化的方案不直接在一整个大码本里做选择而是拆成两个子码本每个子码本里有320个码字每个码字的维度是256维。训练时每个时间步从两个子码本里各选出一个码字拼接得到512维的向量再经过一个线性层投影到Transformer的维度作为对比学习的目标表示。这里的关键是如何“选择”。直接选择不能用梯度下降训练因为argmax不可导。wav2vec 2.0的解法是Gumbel Softmax 直通估计器Straight-Through Estimator。具体操作是编码器输出的512维向量过一个线性层得到两个子码本各自的logits每个子码本320个logits然后在logits上加上Gumbel噪声再做softmax得到软概率分布。前向阶段用argmax取出码字反向阶段梯度假装argmax那个位置的权重就是softmax的权重相当于把梯度“直通”过去。为什么要加Gumbel噪声因为单纯取softmax的话模型很容易在训练早期就锁定某一个码字导致码本利用率极低。Gumbel噪声给选择过程注入了随机性迫使模型在探索阶段更均匀地尝试不同码字就像一个学生在不确定答案时先广泛涉猎而不是一开始就死磕一道题。训练中还有一个温度系数在起作用。早期的温度值高Gumbel Softmax分布接近均匀选择充分随机随着训练推进温度逐渐降低分布越来越尖锐最终逼近离散选择。论文里把温度从2退火到0.5左右这个设置让模型先探索后收敛可以减少码本崩溃。2.3 量化器的“码本崩溃”问题为什么必须靠多样性损失兜底无论用VQ还是Gumbel Softmax离散表示学习里都有一个经典问题码本崩溃。表现是大量码字在训练结束后几乎从没被选中过模型只依赖少数几个码字完成表示导致离散token的信息量急剧下降。wav2vec 2.0为这个问题设置了一个专门的多样性损失Diversity Loss目标是让每个子码本里的码字被选中的概率尽量均匀。具体做法是把每个batch内Gumbel Softmax的概率分布做平均再让这个平均分布尽量接近均匀分布。损失值越小说明码字使用越分散。这里有个细节多样性损失需要的是概率平均值而不是实际选择的argmax结果。因为argmax结果过于稀疏统计意义不强。用Gumbel Softmax的软概率可以保留“倾向但还没选”的信息让梯度更平滑地作用到整个码本上。论文里给多样性损失加了个权重系数0.1。不要小看这个值如果设成0码本会在训练中后期大面积死掉如果设得太大模型会过度关注“均匀使用码字”而忽略对比学习本身的语义目标。0.1是一个经过大量消融实验得到的安全值。2.4 Transformer输入与mask机制连续特征负责输入量化特征负责目标这里有一个非常容易搞错的点我必须重点强调输入给Transformer的不是量化后的离散表示而是特征编码器直接输出的连续特征。量化表示只作为对比学习的监督目标存在。为什么这么设计如果Transformer输入直接用量化特征等于先把连续音频硬编码成离散token再进行序列建模整个过程就像先做了一步绝难的最优分词再做语言模型两个环节的错误会不断累加。让Transformer吃连续特征保留了声学层面的细粒度信息Transformer自己决定怎么融合上下文而量化器只负责提供一个稳定的“目标锚点”。这是wav2vec 2.0比VQ-VAE流畅很多的原因。mask机制也很讲究。预训练时模型在Transformer输入序列上做掩码不是像BERT那样随机掩盖单个token而是先随机采样起始点再连续掩盖后续10帧。论文里设置的起始点采样比例是0.065每次被掩盖的十帧大约对应200毫秒的音频最终会有约一半的时间步被盖上。被mask的位置输入向量会被替换成一个共享的、可学习的mask向量。模型要做的是根据前后文把被盖住的帧对应的量化特征“认出来”。每次训练迭代的mask位置都会重新随机生成也就是说同一个样本每次看到的“题目”都不同这相当于给模型做了无数种数据增强。我觉得这是wav2vec 2.0泛化性能特别强的一个重要隐性原因。2.5 表格对比wav2vec 2.0与BERT的异同维度BERTwav2vec 2.0输入单位确定的离散子词下采样后的连续语音帧词表固定词表动态学习的量化码本掩码方式随机掩盖token连续掩盖时间步训练目标交叉熵分类对比损失 多样性损失位置编码绝对位置编码卷积式相对位置编码输出用法接任务头微调接线性层CTC微调NLP里的BERT处理的是语言学家已经定义好的词元而语音没有这种天然边界所以wav2vec 2.0的最大贡献其实是解决了“语音如何定义词元”的问题。量化器加对比学习就是在动态地学出适合当前任务的词元切分方式。关于位置编码语音序列和文本不太一样。文本中“第5个词”有明确的绝对位置含义而语音帧的绝对序号并没有太大语义重要的是帧与帧之间的相对距离。论文里用的是在Transformer之前叠加一个卷积层来生成相对位置信息这也算是对语音信号特点的合理适配。3. 训练目标拆解对比损失在学什么3.1 对比损失从干扰项里认出正确答案wav2vec 2.0的核心训练目标是一个对比损失形式跟InfoNCE基本一致。对每个被mask的时间步t模型要最大化Transformer输出c_t和同位置量化表示q_t的相似度同时压低c_t跟其他负样本的相似度。相似度用余弦相似度除以温度系数0.1。温度系数越小相似度分布越尖锐模型越激进地拉大正负样本差距。负样本的采样方式非常关键。论文里是从同一条音频的其他时间步中随机抽取100个量化表示作为干扰项注意不是从整个数据集里随机抽。这个设计值得多想一步。如果负样本来自别的句子模型很容易找到捷径不同句子的说话人、录音环境、信道都不同对比学习会退化成“这个人跟那个人说话不一样”模型学到的是说话人区分能力而不是语音内容理解。负样本从同一句里抽就强制模型必须抓住真正的语义差异因为在同一句里说话人和环境都是恒定的唯一的变量是内容本身。3.2 为什么要同时优化两个损失总损失长这样对比损失 0.1乘以多样性损失。这两个损失的目标其实有微妙的对立对比损失希望量化表示尽量准确而精细多样性损失希望码字使用尽量均匀而分散。它们的合力让模型在一个可控的张力中学习——既不能把所有东西都塞进少数几个码字也不能为了均匀而丢掉信息。在实际训练中我建议你观察两者的比例变化。如果对比损失降得很快但多样性损失始终在低位说明量化器很可能死码字了如果多样性损失很健康但对比损失迟迟降不下去可能mask太难或者batch里的负样本分布有问题。完全端到端训练让特征编码器能同时接收到两个损失的梯度这也是wav2vec 2.0能在LibriSpeech上拿到惊人结果的结构基础。3.3 从预训练到微调为什么微调时量化器不参与预训练完成后量化器就退场了。微调阶段直接丢弃量化器把Transformer的输出接一个线性分类层用CTC损失训练。为什么能丢因为量化器的使命是在预训练阶段提供一个稳定的离散目标帮Transformer学会“这个地方该是什么内容”。当Transformer真正掌握了下文预测能力之后量化表示的重要性和准确性就不那么关键了此时直接把连续表示用于下游任务反而更有利因为连续表示保留了更多细粒度声学信息。微调时特征是端到端一起更新的特征编码器的参数也会继续调整。如果不更新特征编码器只调Transformer和输出层在跨数据集或者带噪场景下性能会明显下降。这一点我实测过微调学习率不要设太大一般从5e-5左右起步配合warmup效果会比较稳。3.4 对比损失公式与直觉解释如果不看公式对比损失的直觉其实特别像“在一堆烟雾弹里找真人”。对每个时间步t模型手里有一个预测结果c_t需要从100多个候选表示里找出真正的q_t。公式的分母是所有候选相似度的总和分子是正样本相似度两者相除再取负对数。完美的情况下正样本相似度远高于其他负样本损失趋近于0。模型唯一的学习动力就是把“上下文预测结果”朝着“量化目标”的方向不断拉近同时把不相关的候选推远。温度系数在这个公式里的作用很微妙。温度越小logits的分布越尖模型只要稍微把正样本靠近一丁点损失就会有明显变化温度越大分布越平负样本的相对关系也会被放大。0.1这个值在对比学习里属于比较“激进”的设定它迫使模型对正样本有更强的敏感度。4. Wav2Vec 2.0为什么效果好以及实操中需要注意什么4.1 标注效率的本质提升10分钟数据到底意味着什么wav2vec 2.0论文最震撼的结果不是绝对WER最低而是标注效率的跨越式提升。在LibriSpeech的100小时标注训练集上一个强监督模型大概能把test-clean的WER做到8以上。wav2vec 2.0预训练后只用10分钟标注数据微调就能达到差不多的水平。用1小时标注数据可以达到传统方法用100小时数据才能达到的效果。这个对比背后是整个范式变化预训练阶段用了几千小时无标注语音学习通用表示下游任务只需要很少的标注数据就能“唤醒”已经学到的知识。我自己的理解是wav2vec 2.0在预训练阶段其实已经掌握了大量语音的“语言学先验”——哪些声学模式对应相似的语义、不同说话人口音里的共同结构是什么、上下文如何约束发音。这些先验一旦学好下游只需要少量标注来校准到具体任务。4.2 后续模型的演进脉络wav2vec 2.0是一切的起点wav2vec 2.0之后语音自监督领域迎来了一波快速迭代HuBERT用离线K-means聚类生成的伪标签替代在线量化让训练目标更稳定WavLM在wav2vec 2.0结构上增加了去噪目标学到的表示在说话人任务上更强data2vec把wav2vec 2.0的思路扩展到跨模态让视觉、语音、文本共享一个预训练框架。这些模型都绕不开wav2vec 2.0建立的“连续特征编码 离散目标 上下文建模”三段式范式。即使放到今天不少语音大模型的核心模块里依然能看到它的影子。4.3 复现wav2vec 2.0的几个实操建议如果只是做下游任务强烈建议直接加载预训练权重不要从零开始训练。Fairseq和HuggingFace Transformers都提供了完整的wav2vec 2.0实现和权重。从零预训练需要极其庞大的算力和数据官方Base模型也是用了960小时LibriSpeech无标注语音加上多卡训练数天。如果你只有单卡或者少量数据很容易在预训练阶段就翻车。这种情况下更务实的路线是用预训练权重在目标领域再做一次Masked Unsupervised预训练也就是常说的领域自适应几小时就能跑完效果提升非常明显。微调流程比较成熟把预训练模型接一个输出层用CTC损失在标注数据上训练。需要注意学习率不要太高配合线性warmuup和半精度训练能省大量显存。5. 常见问题与排查技巧问题1预训练loss不降或者降到一定程度就不动了。大概率是学习率或者mask强度设置有问题。对比学习对batch size很敏感batch太小会让负样本不够丰富mask比例太大则任务太难。建议先确认p0.065和l10这两个参数没有改再确认学习率warmup是否到位。问题2微调效果远不如论文。最常见原因是特征编码器没有跟着一起微调或者微调学习率太大造成灾难性遗忘。另一个可能是没有用语言模型解码论文里的WER是带了外部语言模型的单纯用CTC贪心解码会差很多。对比结果前先对齐这两个条件。问题3量化码本利用率特别低大量码字从来没被选中过。检查多样性损失的权重是否被误调成了0或极低也可以观察Gumbel Softmax概率分布的平均熵正常训练后期应该落在“差不多均匀”的水平。如果发现某几个码字占了绝大多数概率多半是温度退火太快可以调整退火步数。问题4显存不够用。分两条路减小mask比例或者减小batch size并用梯度累积补齐。mask比例在训练过程中也可以稍微浮动从0.065降到0.05能明显省显存任务难度变化不大。问题5对比学习对随机种子敏感同配置跑两次差很多。这不是bug而是对比学习中负样本采样带来的自然波动。实验对比时务必固定种子多次重复取平均。6. 一些个人体会回头看wav2vec 2.0我最深的感受是它最大的创造性不是某个单独的技术点而是把量化、对比学习、掩码预测这三个看似独立的东西拧成了一个互相成就的整体。特征编码器提供连续表示量化器提供离散目标Transformer提供上下文三者缺一不可。单独哪个拿出来都不是原创但组合起来就是一次范式级别的突破。如果你想把wav2vec 2.0的原理真正吃透我建议你动手做一个简化版的小实验哪怕是在几十小时的语音上跑一个缩小版模型。亲自观察一下码字的使用情况看一下训练中对比损失和多样性损失的此消彼长你的理解会大不一样。经典之所以是经典就在于它经得起拆解也经得起复现。
返回列表