Whisper语音识别

概述

Whisper是OpenAI于2022年发布的开源自动语音识别(ASR)系统,采用了基于Transformer的编解码器架构,能够进行多语言识别并具有较强的泛化能力。Whisper的核心创新在于使用弱监督训练方法,通过大规模互联网音频数据学习语音识别能力。

1. Whisper架构详解

1.1 编解码器架构

Whisper采用标准的Transformer编码器-解码器架构:

class Whisper(nn.Module):
    def __init__(self, config):
        # 编码器
        self.encoder = TransformerEncoder(
            num_layers=config.num_layers,
            d_model=config.d_model,  # 1280
            num_heads=config.num_heads,  # 16
            d_ff=config.d_ff,  # 5120
            dropout=0.1
        )
        
        # 解码器
        self.decoder = TransformerDecoder(
            num_layers=config.num_layers,
            d_model=config.d_model,
            num_heads=config.num_heads,
            d_ff=config.d_ff,
            dropout=0.1,
            vocab_size=config.vocab_size  # 51865
        )
        
        # 音频嵌入层
        self.audio_embed = nn.Conv2d(
            in_channels=80,  # Mel频谱通道数
            out_channels=config.d_model,
            kernel_size=3,
            stride=2
        )
        
        # 文本嵌入层
        self.text_embed = nn.Embedding(
            num_embeddings=config.vocab_size,
            embedding_dim=config.d_model
        )

1.2 音频特征处理

def process_audio(audio, sample_rate=16000):
    """
    音频预处理:转换为Mel频谱
    """
    # 1. 重采样到16kHz
    audio = resample(audio, target_sr=16000)
    
    # 2. 短时傅里叶变换 (STFT)
    stft = librosa.stft(audio, n_fft=400, hop_length=160)
    
    # 3. 计算幅度谱
    magnitude = np.abs(stft)
    
    # 4. Mel滤波器组变换
    mel_spec = librosa.feature.melspectrogram(
        S=magnitude,
        n_mels=80,
        n_fft=400,
        hop_length=160,
        fmin=0,
        fmax=8000
    )
    
    # 5. 对数压缩
    log_mel = np.log(mel_spec + 1e-9)
    
    # 6. 归一化
    log_mel = (log_mel - log_mel.mean()) / log_mel.std()
    
    return log_mel  # [80, T] 其中T=audio_duration/0.01

1.3 编码器结构

class WhisperEncoder(nn.Module):
    def __init__(self, d_model=1280, num_layers=32):
        # 输入卷积层
        self.conv1 = nn.Conv1d(80, d_model, kernel_size=3, padding=1)
        self.conv2 = nn.Conv1d(d_model, d_model, kernel_size=3, stride=2, padding=1)
        
        # Transformer块
        self.blocks = nn.ModuleList([
            ResidualAttentionBlock(d_model, num_heads=16)
            for _ in range(num_layers)
        ])
        
        # 层归一化
        self.ln = LayerNorm(d_model)
    
    def forward(self, x):
        # x: [B, 80, T]
        
        # 初始卷积下采样
        x = self.conv1(x)
        x = nn.functional.gelu(x)
        x = self.conv2(x)
        x = nn.functional.gelu(x)
        
        # Transformer处理
        for block in self.blocks:
            x = block(x, self_attn_mask=None)
        
        # 最终归一化
        x = self.ln(x)
        
        return x  # [B, D, T']

1.4 解码器结构

class WhisperDecoder(nn.Module):
    def __init__(self, d_model=1280, num_layers=32, vocab_size=51865):
        # 文本嵌入
        self.token_embedding = nn.Embedding(vocab_size, d_model)
        
        # 位置编码
        self.positional_embedding = nn.Embedding(5000, d_model)
        
        # Transformer块
        self.blocks = nn.ModuleList([
            ResidualAttentionBlock(d_model, num_heads=16, use_cross_attention=True)
            for _ in range(num_layers)
        ])
        
        # 输出层
        self.ln = LayerNorm(d_model)
        self.proj = nn.Linear(d_model, vocab_size)
    
    def forward(self, x, encoder_output):
        # x: [B, seq_len] token IDs
        # encoder_output: [B, D, T'] 编码器输出
        
        # 文本嵌入 + 位置编码
        x = self.token_embedding(x) + self.positional_embedding(
            torch.arange(x.shape[1], device=x.device)
        )
        
        # 自注意力掩码(causal)
        seq_len = x.shape[1]
        mask = torch.triu(
            torch.ones(seq_len, seq_len, device=x.device),
            diagonal=1
        ).bool()
        
        # Transformer块
        for block in self.blocks:
            x = block(x, encoder_output, self_attn_mask=mask)
        
        # 输出投影
        x = self.ln(x)
        logits = self.proj(x)
        
        return logits  # [B, seq_len, vocab_size]

2. 多语言识别能力

2.1 支持的语言

Whisper能够识别99种语言:

LANGUAGES = {
    'en': 'english',
    'zh': 'chinese',
    'es': 'spanish',
    'fr': 'french',
    'de': 'german',
    'ja': 'japanese',
    'ko': 'korean',
    'ar': 'arabic',
    'ru': 'russian',
    # ... 共99种语言
}

2.2 语言检测与自动切换

def detect_language_and_transcribe(audio, model):
    """
    自动检测语言并转录
    """
    # 1. 音频特征提取
    mel = process_audio(audio)
    
    # 2. 语言检测(使用一小段音频)
    lang_mel = mel[:, :3000]  # 前30秒
    lang_logits = model.detect_language(lang_mel)
    detected_lang = LANGUAGES[lang_logits.argmax().item()]
    
    # 3. 完整转录(指定语言)
    if detected_lang != 'english':
        # 使用语言特定的解码器
        transcription = model.transcribe(
            audio, 
            language=detected_lang,
            task='transcribe'
        )
    else:
        transcription = model.transcribe(audio)
    
    return transcription

2.3 多语言训练数据

# Whisper训练数据集分布
dataset_distribution = {
    'english': 0.35,      # 最大比例
    'spanish': 0.10,
    'chinese': 0.08,
    'french': 0.07,
    'german': 0.06,
    'russian': 0.05,
    'other': 0.29        # 其他语言混合
}

3. 弱监督训练方法

3.1 弱监督定义

弱监督(Weak Supervision): 使用互联网上有噪声但数量庞大的音频-文本配对数据进行训练,相比使用人工标注的高质量数据,规模更大但标注质量较低。

# 传统监督 vs 弱监督
traditional_supervision = {
    'data_source': '人工标注',
    'quality': '高',
    'quantity': '低(数千到数万小时)',
    'cost': '昂贵',
    'annotation': '精确时间对齐'
}
 
weak_supervision = {
    'data_source': '互联网音频(YouTube等)',
    'quality': '中等(有噪声)',
    'quantity': '大(数百万小时)',
    'cost': '低',
    'annotation': '不保证时间对齐'
}

3.2 训练数据收集

def collect_weak_supervision_data():
    """
    收集弱监督训练数据
    """
    data_sources = [
        'YouTube videos',
        'Podcasts',
        'Audiobooks',
        'Public speaking recordings',
        'Movie trailers',
        'News broadcasts',
        'Educational content'
    ]
    
    collection_pipeline = {
        'step1': '从公开源收集音频',
        'step2': '原始转录文本获取(CC subtitles, transcripts)',
        'step3': '质量过滤(长度、语言、清晰度)',
        'step4': '音频-文本配对构建',
        'step5': '去重(去除重复内容)'
    }
    
    return collected_data  # Whisper使用约68万小时

3.3 数据预处理

def preprocess_audio_text_pair(audio_path, text):
    """
    预处理音频-文本对
    """
    # 1. 音频清理
    audio = load_audio(audio_path)
    audio = remove_silence(audio)
    audio = normalize_volume(audio)
    
    # 2. 文本清理
    text = clean_transcript(text)
    text = remove_filler_words(text)  # 可选
    text = standardize_punctuation(text)
    
    # 3. 时长过滤
    if len(audio) < 0.5 or len(audio) > 30:
        return None
    
    # 4. 语言验证
    detected_lang = detect_language(text)
    if not is_supported_language(detected_lang):
        return None
    
    return {'audio': audio, 'text': text, 'language': detected_lang}

3.4 损失函数与训练

def compute_loss(logits, targets, pad_token_id=50257):
    """
    计算交叉熵损失
    忽略padding token
    """
    # logits: [B, seq_len, vocab_size]
    # targets: [B, seq_len]
    
    # 移位:解码器预测下一个token
    shift_logits = logits[:, :-1, :].contiguous()
    shift_targets = targets[:, 1:].contiguous()
    
    # 计算交叉熵
    loss = nn.functional.cross_entropy(
        shift_logits.view(-1, shift_logits.size(-1)),
        shift_targets.view(-1),
        ignore_index=pad_token_id,
        reduction='mean'
    )
    
    return loss

3.5 多任务训练

Whisper使用多任务学习框架:

class MultiTaskWhisper(nn.Module):
    def forward(self, audio, targets=None, task='transcribe', language='en'):
        # 编码音频
        encoder_output = self.encoder(audio)
        
        # 构建任务提示
        if task == 'transcribe':
            task_tokens = [TO_TRANSCRIBE, LANG_TOKEN[language]]
        elif task == 'translate':
            task_tokens = [TO_TRANSLATE, LANG_TOKEN[language]]
        elif task == 'detect_language':
            task_tokens = [TO_DETECT_LANGUAGE]
        
        # 解码
        decoder_input = task_tokens + (targets or [])
        decoder_output = self.decoder(decoder_input, encoder_output)
        
        return decoder_output

4. 模型变体

4.1 Whisper模型系列

模型参数量English WER多语言 WER
Tiny39M~8%~15%
Base74M~6%~12%
Small244M~4%~8%
Medium769M~3%~6%
Large-v21550M~2.5%~5%
Large-v31550M~2%~4%

4.2 性能对比

# Whisper vs 其他ASR系统
performance_comparison = {
    'Whisper-Large-v3': {'WER': '2-3%', 'languages': 99, 'robustness': 'high'},
    'Wav2Vec2': {'WER': '4-5%', 'languages': 50+', 'robustness': 'medium'},
    'Google Speech-to-Text': {'WER': '3-4%', 'languages': 125+, 'robustness': 'high'},
    'DeepSpeech': {'WER': '8-10%', 'languages': 5', 'robustness': 'low'}
}

5. 使用方法

5.1 基本推理

import whisper
 
# 加载模型
model = whisper.load_model("base")
 
# 转录
result = model.transcribe("audio.mp3", language='zh')
 
# 打印结果
print(result["text"])
print(result["segments"])

5.2 多语言识别

# 加载模型
model = whisper.load_model("medium")
 
# 方法1:自动检测语言
result = model.transcribe("unknown_language_audio.mp3")
 
# 方法2:指定语言
result = model.transcribe(
    "japanese_audio.mp3",
    language="ja",
    task="transcribe"  # 或 'translate'
)
 
# 方法3:翻译
result = model.transcribe(
    "chinese_audio.mp3",
    language="zh",
    task="translate"  # 翻译为英语
)

5.3 批量处理

def batch_transcribe(audio_files, model, language=None):
    """
    批量转录多个音频文件
    """
    results = []
    
    for audio_path in audio_files:
        print(f"Processing: {audio_path}")
        
        result = model.transcribe(
            audio_path,
            language=language,
            verbose=False
        )
        
        results.append({
            'file': audio_path,
            'text': result['text'],
            'language': result['language']
        })
    
    return results

6. 核心优势

优势说明
多语言支持99种语言,无需微调
弱监督训练68万小时,规模大
鲁棒性抗噪声能力强
开源可本地部署
零样本能力未经训练的语言也能识别

7. 局限性与改进

7.1 当前局限

限制描述
实时性推理延迟较高
专有名词人名、地名识别准确率低
方言口音某些方言识别困难
标点符号自动标点不够准确

7.2 改进方向

# Whisper的潜在改进
improvements = {
    'speed': '优化推理速度,支持实时',
    'accuracy': '提升专有名词识别',
    'punctuation': '改进标点生成',
    'dialect': '增加方言支持',
    'speaker': '加入说话人分离'
}

8. 总结

Whisper的核心技术贡献:

技术贡献影响
Transformer编解码器统一的多语言ASR简化模型架构
弱监督训练大规模互联网数据降低数据成本
多语言泛化99种语言零样本广泛适用性
开源模型可本地部署推动社区发展

Whisper证明了弱监督训练在语音识别领域的有效性,为后续多语言语音模型的发展提供了重要参考。