Whisper语音大模型:弱监督scaling law的威力

本期介绍OpenAI在ICML 2023发布的语音大模型Whisper: Robust Speech Recognition via Large-Scale Weak Supervision

一、弱监督做语音识别

ASR(Automatic Speech Recognition, 自动语音识别),就是传入声音波形,传出对应的字幕。传统的自监督预训练+有监督微调(如wav2vec 2.0, Neurips 2020)这条路和NLP、CV一样似乎走得很顺。自监督预训练学习通用的声学表征,有监督微调得找些人工标注的数据,最后可能再加一个纠错处理(比如挂一个外部语言模型,或者整点后处理规则。

语音比NLP走得慢,涌现能力出现得晚,一个很大的原因是数据少很多。NLP里,GPT2到3的时候,呼啦一下把整个互联网的数据都盘下来了,一下子聪明了好几档。反观那时候语音还在吭哧吭哧给数据打标签。那么,为何不直接用从互联网去找海量音频来训练?Whisper就做出来了,它收集了网上68万小时弱标注音频-文本对,比如YouTube视频配上自动字幕,播客和文字转录等等,这些数据的质量自然是比不上精心标注的,但胜在量大覆盖广管饱。接下来看看量大的弱监督如何匹敌精选的全监督。

二、架构老两样:Encoder-Decoder Transformer

Whisper的模型架构几乎没有什么新花样,就是一个标准的Encoder-Decoder Transformer,结构上和翻译模型一脉相承。具体的结构流程我们稍后从头到尾细讲。

2.1 音频预处理

原始音频首先被重采样到16kHz单声道(这样人能听到的最高频是8kHz,反正比玛利亚·凯丽的3kHz海豚音要高多了),然后通过短时傅里叶变换(STFT, n_fft=400, hop_length=160)提取频谱,再经过Mel滤波器组投影并取对数,得到log-Mel频谱图。例如,Whisper接受的输入都是裁成30秒音频,这30秒在16kHz采样率下有480000个采样点,经STFT后得到约3000帧。每一帧通过Mel滤波器用一个向量表示,Whisper-v1/v2中这个向量的维度是80,v3升级到了128。接着这个形状是[n_mels, 3000]的频谱图就作为输入送入Encoder了。(音频的惯用预处理,可以参见Grad-TTS的科普)

2.2 Audio Encoder

Encoder首先用两层一维卷积对频谱做下采样。第一层卷积将n_mels维投影到d_model维,第二层卷积通过stride=2实现2倍下采样,将3000帧压缩为1500帧。然后加上正弦位置编码(不学习的、固定的sinusoidal编码,好处是30秒固定长度也玩不出什么花儿来,又少了一些要优化的东西),再过N层标准Transformer Encoder Block(Self-Attention + FFN,Pre-LayerNorm结构),最后一个LayerNorm收尾:

class AudioEncoder(nn.Module):
    def __init__(self, n_mels, n_ctx, n_state, n_head, n_layer):
        self.conv1 = Conv1d(n_mels, n_state, kernel_size=3, padding=1)
        self.conv2 = Conv1d(n_state, n_state, kernel_size=3, stride=2, padding=1)
        self.register_buffer("positional_embedding", sinusoids(n_ctx, n_state))
        self.blocks = nn.ModuleList(
            [ResidualAttentionBlock(n_state, n_head) for _ in range(n_layer)]
        )
        self.ln_post = LayerNorm(n_state)

ViT的patch embedding也是这么干的,连续信号离散化为token序列,再交给Transformer处理。

2.3 Text Decoder

Decoder则是一个标准的自回归Transformer Decoder。
与Encoder不同的是,位置编码是可学习的,因为这时候语种token、任务token、时间戳、文本……什么乱七八糟的符号都进来了,它们互相交织在一起,其实也未必是一个顺序的关系,还是学一学更能领悟任务的实质。由于支持最大长度448个token,这部分的计算开销也不容小觑。
第二个不同是每个Block多了一个Cross-Attention层,这是自然的,下一个生成什么字,除了看字本身,还得看原来的语音长什么样。Q来自Decoder的隐状态,K和V来自Encoder的输出,NLP的惯常操作。
另一个惯常操作是因果掩码(causal mask),防止看到未来token,不多讲了。
输出层与token embedding共享权重,这也很自然,上一个文字token和自回归的这一个文字token都在一个embedding空间里,谁和谁又有差别呢,文字乘到embedding的,再用转置乘回来回到原来的空间。即输出logits = x @ token_embedding.weight.T

class TextDecoder(nn.Module):
    def __init__(self, n_vocab, n_ctx, n_state, n_head, n_layer):
        self.token_embedding = nn.Embedding(n_vocab, n_state)
        self.positional_embedding = nn.Parameter(torch.empty(n_ctx, n_state))
        self.blocks = nn.ModuleList(
            [ResidualAttentionBlock(n_state, n_head, cross_attention=True)
             for _ in range(n_layer)]
        )
        self.ln = LayerNorm(n_state)
        ...
    def forward(self, x: Tensor, xa: Tensor, kv_cache: Optional[dict] = None):
        ...
        offset = next(iter(kv_cache.values())).shape[1] if kv_cache else 0
        x = (
            self.token_embedding(x)
            + self.positional_embedding[offset : offset + x.shape[-1]]
        )
        ...
        logits = (
            x @ torch.transpose(self.token_embedding.weight.to(x.dtype), 0, 1)
        ).float()
        return logits

2.4 训好的现成果实,后面有谁来摘?

Whisper的Encoder和Decoder在后续研究中被分别复用于不同场景:

Encoder被用作通用音频特征提取器。Qwen-Audio系列直接冻结Whisper large-v2的Encoder(32层Transformer),接一个投影层映射到LLM的嵌入空间,再用Qwen LLM做Decoder。这种做法保留了Whisper的声学理解能力,同时获得了LLM的语言生成能力,可以做语音问答、音频理解等更复杂任务。类似地,许多多模态大模型也把Whisper Encoder当作现成的audio backbone。

Decoder被蒸馏或替换。Whisper-large-v3-turbo直接把Decoder从32层剪枝到4层,通过知识蒸馏保留了大部分能力。Distil-Whisper也是类似思路,压缩Decoder以提高推理速度。

三、模型家族&任务

Whisper提供了一系列不同规模的模型,结构差异主要体现在层数和隐藏维度上:

模型 参数量 Enc层数 Dec层数 d_model 注意力头 FFN维度 Mel维度
tiny 39M 4 4 384 6 1536 80
base 74M 6 6 512 8 2048 80
small 244M 12 12 768 12 3072 80
medium 769M 24 24 1024 16 4096 80
large (v1/v2) 1550M 32 32 1280 20 5120 80
large-v3 1550M 32 32 1280 20 5120 128
large-v3-turbo 809M 32 4 1280 20 5120 128

几个值得注意的设计:

  • 每个注意力头的维度始终为64(d_model / n_head = 64)
  • FFN维度始终为4×d_model
  • 从tiny到large,宽度增长3.3倍(从384到1280),深度增长8倍(从4到32)
  • turbo特殊一点,保留了32层Encoder但把Decoder砍到只剩4层,参数量减半但推理速度提升8倍

适合什么硬件?

模型 推理显存 微调显存(Full) 微调显存(LoRA) 推荐推理卡 推荐训练卡
tiny ~1GB ~2GB ~1.5GB 任意GPU/CPU 任意GPU
base ~1GB ~3GB ~2GB 任意GPU/CPU 任意GPU
small ~2GB ~6GB ~3GB RTX 3060+ RTX 3080+
medium ~5GB ~12GB ~6GB RTX 3080+ RTX 3090/A100
large ~10GB ~24GB+ ~12GB A100/RTX 4090 A100 80GB
turbo ~6GB ~16GB ~8GB RTX 3090+ RTX 4090/A100

medium需要3090/A100级别的显卡,但在多数任务上和large差距不大。large系列通常需要多卡或者至少A100 80GB才能做全参数微调,但LoRA微调的话单卡4090也勉强可以。

训练了多久?

OpenAI没公开各模型的确切训练时长和GPU-hours。Whisper-large-v2说v2在v1的基础上又训了2.5倍的时间。根据社区估计和Epoch AI的数据,large模型的训练量级大约在数千到数万GPU-hours。对于小模型,社区微调的经验是:在单卡A100上,small模型对几百小时的领域数据做LoRA微调,通常几天就可以收敛。

不同模型能做的任务

所有多语言模型(tiny到large)理论上都支持这些任务:转录、翻译、99种语言识别、时间戳对齐。不过在实践中:

  • tiny/base:英语转录尚可,多语言和翻译能力较弱,时间戳精度低,适合对延迟和资源敏感的场景
  • small/medium:多语言能力提升,翻译质量好用,时间戳也可靠,是大多数应用的主力
  • large :多语言和翻译能力最强,低资源语言表现最好
  • turbo:推理速度最快,可惜不支持做翻译任务,只能做转录

此外,tiny和base有专门的English-only版本(tiny.en、base.en等),在纯英语场景下性能略优于同级别的多语言版本,因为它们的词表中没有99种语言的token,所有容量都专注于英语。

四、一些细节魔鬼

4.1 SOT token

Whisper用一套特殊Start-of-Transcript (SOT) token序列统一多个任务。当然我们熟知的GPT-4, T5, Flamingo都这么干,只不过Whisper的token更结构化一点。解码器的输入格式是:

<|startoftranscript|> <|语言|> <|任务|> [时间戳/文本] <|endoftext|>

具体来说,

  • 中文转录:<|startoftranscript|><|zh|><|transcribe|><|0.00|>今天天气很好<|1.20|><|endoftext|>
  • 中译英:<|startoftranscript|><|zh|><|translate|>The weather is nice today<|endoftext|>
  • 语言识别:输入<|startoftranscript|>,看下一个token预测为哪个语言token

whisper/tokenizer.py代码中可以看到,词表包含:

  • 51865个基础token(GPT-2的BPE分词器扩展到多语言)
  • 99个语言token(<|en|><|zh|><|ja|>等)
  • 任务token(<|transcribe|><|translate|>
  • 1501个时间戳token(<|0.00|><|30.00|>,精度20ms)
  • 一些些辅助token(<|nospeech|><|notimestamps|>等)

4.2 这些任务从机理上是如何实现的?

对于ASR,Decoder看到<|transcribe|>这个条件token后,生成的分布会偏向源语言的token。
对于翻译,因为训练数据里面有大量翻译对(非英语视频配有英语字幕),模型靠在这些数据上训练学会了在<|translate|>条件下把源语言语音映射到英语文本。
对于自动语言识别,Decoder输入<|startoftranscript|>这一个token,对下一个位置的logits在所有99个语言token上做softmax,argmax就可以得到最可能的语言。在转录中,如果你不指定语言,transcribe()函数也用前30秒做语言检测,然后用检测到的语言做后续转录。(这样的话对中英混合有一定的处理能力)
对于时间戳对齐,Whisper通过Cross-Attention权重和DTW(Dynamic Time Warping,动态时间规整)算法实现。先提取Decoder中特定Cross-Attention头的注意力权重,这些权重反映了文本token和音频帧之间的对应关系,再通过DTW找到最优对齐路径,从而得到每个词的起止时间。

Whisper也有不能做的。它不支持说话人区分。因为它训练的时候就没有speaker embedding或者speaker标签。的输出,也没有说话人切换的标注。

能实现说话人区分的主流方案有:

  • pyannote-audio:先用pyannote做说话人分割,得到"谁在什么时间段说话"的信息,再用Whisper对每个片段做转录,最后合并。
  • WhisperX:在Whisper基础上集成了forced alignment和pyannote diarization,所以提供word-level的时间戳和说话人标签。
  • NeMo:NVIDIA的框架也提供了端到端的ASR+Diarization pipeline。

效果方面,pyannote在DER(Diarization Error Rate)上通常可以达到5-10%的水平,配合Whisper做转录的整体效果,那就要取决于音频质量和说话人重叠程度了。要是两人清晰对话场景下,那还表现不错,要是多人重叠交谈场景可就是业界难题了。

4.3 30秒窗口

Whisper把音频切分为30秒的片段,生成好后拼接结果。这个设计坏处是跨片段的上下文被截断。为了缓解这个问题,transcribe()函数实现了一个condition_on_previous_text机制:它把上一个片段的输出文本作为prompt喂给下一个片段的Decoder,帮助保持上下文的连贯性。

4.4 解码策略

Whisper支持Greedy Decoding和Beam Search两种解码策略,greedy直接取argmax,Beam Search选beam_size=5,但可以超过5,也就是支持patience机制,允许在max_candidates = beam_size × patience个候选收集了再挑后面最好的,拿时间换质量。

greedy温度是0,beam温度是5,Whisper还可以采用温度回退的策略,先用temperature=0做greedy解码,如果检测到输出质量不好就逐步升高温度重试,直到得到一个可接受的结果。

五、性能表现

Whisper large-v2的zero-shot表现如下:

  • 英语(LibriSpeech test-clean):WER约4.2%,接近人类水平(约5.2%)。

  • 中文(Common Voice / Fleurs):CER约10-12%,属于可用但不顶尖的水平。中文在Whisper的训练数据中占比不高(远少于英语的65%),所以原生表现中规中矩。相比之下,同时期专门在中文数据上训练的模型表现更好:

    • 讯飞、阿里FunASR(Paraformer)、百度DeepSpeech等,CER通常在3-5%
    • Whisper large-v3微调后、腾讯ASR等,CER在5-8%
    • Whisper原生(不微调)、Google Speech-to-Text等,CER在10-15%
  • 低资源语言:表现参差不齐,数据量大的语言(西班牙语、法语等)WER在5-8%,数据量小的语言WER可能高达30%+。

large-v3相比v2在中文上有明显进步(从CER12.5%降到10.8%),主要得益于Mel滤波器从80维升级到128维——更细的频率分辨率对中文声调语言更敏锐,能捕捉更多特质。

七、评价

对学术界的影响

Whisper对后续研究的影响是深远的:

  1. 验证了弱监督scaling law,证明了语音领域同样适用"大数据+大模型"的范式,无需精心标注的小数据集;
  2. 成为事实上的baseline,几乎所有新的ASR论文都会和Whisper做对比;
  3. 催生了一系列衍生工作,WhisperX(精细对齐+说话人分离)、Distil-Whisper(蒸馏加速)、faster-whisper(推理优化)、whisper.cpp(轻量化的C++移植,可在手机上运行);
  4. 推动了多模态研究,Whisper Encoder成为音频多模态模型的标准组件(Qwen-Audio、SALMONN等);
  5. 降低了ASR的门槛,开源、免费、多语言,而且有标准化的微调流程,让语音技术有种飞入寻常百姓家的感觉。

优点

刚才已经说了,零样本就能开做、多语言多任务、开源免费、微调友好,好处太多。

缺点

不支持说话人区分、流式推理(有30秒窗口限制),非英语性能有限、混合语种处理欠佳。

展望

在Whisper之后,OpenAI在其API中持续迭代语音能力,GPT-4o已经支持原生语音输入输出。而开源社区的Whisper生态也在不断壮大(见上文的衍生工作),各类多模态大模型借助Whisper Encoder实现了各显神通的音频理解能力。可以说,Whisper的真正贡献不仅是一个高质量的ASR模型,更像是为整个社区提供了一个基础设施。就像GPT-3之于NLP、CLIP之于视觉语言,Whisper之于语音识别,也是一个划时代的锚点。

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

  • """1.个性化消息: 将用户的姓名存到一个变量中,并向该用户显示一条消息。显示的消息应非常简单,如“Hello ...
    她即我命阅读 13,388评论 0 6
  • 1、expected an indented block 冒号后面是要写上一定的内容的(新手容易遗忘这一点); 缩...
    庵下桃花仙阅读 4,533评论 1 2
  • 一、工具箱(多种工具共用一个快捷键的可同时按【Shift】加此快捷键选取)矩形、椭圆选框工具 【M】移动工具 【V...
    墨雅丫阅读 4,810评论 0 0
  • 跟随樊老师和伙伴们一起学习心理知识提升自已,已经有三个月有余了,这一段时间因为天气的原因休课,顺便整理一下之前学习...
    学习思考行动阅读 4,300评论 0 2
  • 一脸愤怒的她躺在了床上,好几次甩开了他抱过来的双手,到最后还坚决的翻了个身,只留给他一个冷漠的背影。 多次尝试抱她...
    海边的蓝兔子阅读 3,077评论 1 4

友情链接更多精彩内容