概述
在語音轉文本ASR工具合集彙總介紹過幾款語音識別模型和項目,其中就包括OpenAI開源的Whisper。
論文,OpenAI開源的支持多語言的通用ASR。在68萬小時的標註數據上進行訓練,有很強的泛化能力;作為一個多任務模型,可執行多語言語音識別、語音翻譯和口語識別。通過使用分塊算法,也可用於轉錄任意長度的音頻樣本。分塊是通過在實例化管道時設置chunk_length_s=30啓用。
Whisper共提供5種不同參數大小的模型,其中4種有專一的英文模型,在速度與準確性做平衡。
|
Size
|
Parameters
|
English-only model
|
Multilingual model
|
Required VRAM
|
Relative speed
|
|
tiny
|
72M
|
tiny.en
|
tiny
|
~1GB
|
~32x
|
|
base
|
138M
|
base.en
|
base
|
~1GB
|
~16x
|
|
small
|
-M
|
small.en
|
small
|
~2GB
|
~6x
|
|
medium
|
-M
|
medium.en
|
medium
|
~5GB
|
~2x
|
|
large
|
-M
|
N/A
|
large
|
~10GB
|
1x
|
注:large有v1,v2,v3三個版本,v3還有個openai/whisper-large-v3-turbo版本。
對於本地部署的模型,WAV和TS(Transport Stream,一種流媒體容器格式,用於在數字電視廣播中傳輸音頻和視頻數據)文件都可直接使用,但TS文件在OpenAI付費接口上不行,需要提前通過FFMPEG將TS文件轉為mp3文件。
技術
核心工作流
五步完成語音轉文本的蜕變,Whisper的成功關鍵,在於精準的預處理與高效Transformer二者之間的協同作用。許多人僅僅關注Transformer架構本身,卻忽略其前置處理環節中那些精巧的設計——正是80通道的梅爾頻譜圖,經過兩次卷積操作,並結合GELU激活函數,才為Transformer的高效運行,打下堅實的基礎。整個處理流程,可劃分為五個核心步驟:
- 音頻標準化:將任意採樣率的原始音頻統一重採樣至16kHz(模型標準輸入);
- 梅爾頻譜轉換:將標準化音頻,轉為80通道對數幅度梅爾頻譜圖(v3版本升級為128通道),把波形信號,變成符合人耳特性的特徵圖像;
- 特徵精煉:通過兩層卷積提取局部語音特徵,GELU(Gaussian Error Linear Unit)注入非線性,完成降維提效;
- 編碼階段:加入旋轉位置編碼(RoPE),經Transformer編碼器捕捉全局依賴;
- 解碼與後處理:結合CTC(Connectionist Temporal Classification)與自迴歸解碼生成文本,自動完成語言識別、標點與時間戳標註。
架構基石
Transformer雖擅長於捕捉長序列的全局關係,但卻無法直接對原始音頻進行處理——它既不善於提取局部特徵,也難以應付海量冗餘的數據。Whisper的80通道梅爾頻譜圖、經過兩次的卷積以及GELU,恰恰是為了解決這一矛盾而誕生的三重前置處理模塊,構成模型架構的底層重要基礎。
第一重魔法:80通道梅爾頻譜圖
原始音頻,它是連續的波形(在16kHz的採樣率下,10秒之中包含16萬個振幅的數據點),倘若直接進行輸入,這樣就會致使數據爆炸和特徵冗餘。梅爾頻譜圖的核心作用在於模擬人耳的聽覺特性,以此來達成信號提純的目的。
- 設計邏輯:人耳對於低頻(100-400Hz,此為説話基音)較為敏感,而對高頻(>8000Hz,乃是環境噪聲)不太敏感。80個通道恰好能夠覆蓋人類語音的核心頻率範圍,這樣既不會丟失關鍵信息,與此同時又能夠壓縮數據量(10秒音頻數據量從16萬降低至8萬)。
- 技術細節:經短時傅里葉變換(STFT轉頻譜→梅爾濾波器組提能量→取對數放大弱信號,最終得到時間-頻率二維特徵圖
- 架構意義:將波形信號轉化為模型易理解的頻率特徵,為後續處理奠定基礎
核心代碼:梅爾頻譜圖生成(關鍵參數標註)
import librosa
import numpy as np
def generate_whisper_mel(audio_path: str):
# 1. 加載音頻並統一採樣率為16kHz
waveform, _ = librosa.load(audio_path, sr=16000)
# 2. 按Whisper標準參數生成80通道梅爾譜
mel_spec = librosa.feature.melspectrogram(
y=waveform, sr=16000, n_fft=400, hop_length=160, n_mels=80, fmax=8000
)
# 3. 對數轉換+歸一化(Whisper必填步驟)
log_mel = np.log(np.clip(mel_spec, a_min=1e-10, a_max=None))
return (log_mel - log_mel.mean()) / (log_mel.std() + 1e-6)
mel_feature = generate_whisper_mel("sample_audio.wav")
print(f"梅爾譜形狀: {mel_feature.shape}") # 輸出(80, 時間步長)
第二重魔法:兩次卷積
梅爾頻譜圖,仍含有局部冗餘,兩次卷積所構成的特徵提取詞幹,核心作用在於提取局部語音特徵,與此同時降低時間維度,以便為Transformer輸送精煉素材。
- 層級分工
- 第一次卷積:16個3×3核,提取聲帶振動濁音、摩擦音清音等基礎特徵,80通道→16通道;
- 第二次卷積:32個3×3核步長2,將其整合為音素級特徵;在此期間,把時間維度壓縮至12,減少Transformer計算量;
- 架構意義:幫Transformer提前完成局部特徵提取,避免其在原始頻譜中大海撈針
第三重魔法:GELU激活函數——給模型注入非線性理解能力
卷積輸出,需經激活函數,這樣能產生非線性變換,從而才能夠理解複雜語音規律,(例如升調、輕音)。Whisper選擇GELU而非ReLU,其核心之處在於精準地把捉語音裏的細微細節。
- 關鍵區別
- ReLU是硬開關:輸入若小於等於0,則便直接將其置為0,如此這般容易丟失像那輕聲音節這般較弱的特徵;
- GELU是漸變開關,基於高斯誤差函數來動態地保留特徵,能夠以一種温和的方式保留那些較弱的信號。
- 架構意義:讓模型不遺漏細節、不誇大噪聲,是複雜場景識別準確率領先的關鍵
核心代碼:預處理模塊整合(含卷積+GELU)
import torch
import torch.nn as nn
class WhisperPreprocessStem(nn.Module):
"""Whisper Transformer前的核心預處理模塊"""
def __init__(self, out_dim: int = 512):
super().__init__()
# 兩次卷積+GELU特徵提取
self.conv_block = nn.Sequential(
nn.Conv2d(1, 16, 3, padding=1), # 1→16通道(輸入為單通道梅爾譜)
nn.GELU(),
nn.Conv2d(16, 32, 3, padding=1, stride=(2,1)), # 16→32通道,時間降維1/2
nn.GELU()
)
# 映射到Transformer輸入維度
self.fc = nn.Linear(32 * 80 * 500, out_dim) # 假設輸入時間步1000,降維後500
def forward(self, mel_spec: torch.Tensor):
# 梅爾譜形狀:(batch, 時間步, 80) → (batch, 1, 時間步, 80)(適配卷積)
mel_spec = mel_spec.unsqueeze(1)
conv_out = self.conv_block(mel_spec).flatten(1) # 展平卷積結果
return self.fc(conv_out).permute(1, 0, 2) # 轉成Transformer需要的(seq_len, batch, d_model)
fake_mel = torch.randn(1, 1000, 80) # 1個batch、1000時間步、80通道
preprocessor = WhisperPreprocessStem()
print(f"預處理後輸出形狀: {preprocessor(fake_mel).shape}")
架構精髓
多任務聯合訓練的底層邏輯,完成“三重預處理”後精煉的特徵,會輸入Transformer編碼器-解碼器架構。Whisper以單一模型來承載語音識別、翻譯以及語言檢測這三大任務,其核心在於多任務的聯合訓練以及統一的文本生成接口。
核心代碼:Transformer位置編碼(時序建模關鍵)
import torch
import math
class PositionalEncoding(nn.Module):
def __init__(self, d_model: int, max_len: int = 5000):
super().__init__()
# 預計算位置編碼(不參與梯度更新)
pe = torch.zeros(max_len, d_model)
position = torch.arange(0, max_len, dtype=torch.float).unsqueeze(1)
div_term = torch.exp(torch.arange(0, d_model, 2) * (-math.log(10000.0) / d_model))
pe[:, 0::2] = torch.sin(position * div_term)
pe[:, 1::2] = torch.cos(position * div_term)
self.register_buffer('pe', pe)
def forward(self, x: torch.Tensor):
return x + self.pe[:x.size(0)] # 按輸入序列長度截取編碼
未來演進
Whisper未來可能的演進方向:
- 低資源語言突破:從英語優先到全球覆蓋
- 接入Mozilla Common Voice等多語言數據集,豐富方言樣本
- 用高資源語言預訓練知識,通過語言嵌入適配低資源語言
- 多模態融合:不止聽聲辨字,更能理解語境
- 結合視頻唇動信號優化嘈雜環境識別,
- 聯動GPT-4o實現語音理解生成閉環,支持複雜指令
- 邊緣部署深化:從雲端依賴到設備原生
- 微型模型(如
ggml-base-tdrz內存<100MB,支持手機、嵌入式設備 - 實時流處理優化,端到端延遲降至200ms以內
實戰
有多種安裝方式,最簡單的就是:pip install -U openai-whisper
源碼安裝:
git clone https://github.com/openai/whisper.git
python -m venv whisper-env
source whisper-env/bin/activate
可選:
pip install torch torchaudio librosa
簡單使用:
import whisper
# 加載模型,以Windows為例,下載路徑C:\Users\johnny\.cache\whisper
model = whisper.load_model("base")
# load audio and pad/trim it to fit 30 seconds
audio = whisper.load_audio("ok.wav")
audio = whisper.pad_or_trim(audio)
# make log-Mel spectrogram and move to the same device as the model
mel = whisper.log_mel_spectrogram(audio).to(model.device)
# detect the spoken language
_, probs = model.detect_language(mel)
print(f"Detected language: {max(probs, key=probs.get)}")
本地模型:
運行效果:
使用CPU:
model = whisper.load_model("tiny")
result = model.transcribe("ok.wav")
print(result["text"])
使用GPU加速:
import torch
torch.cuda.is_available()
DEVICE = "cuda" if torch.cuda.is_available() else "cpu"
model = whisper.load_model("tiny", device=DEVICE)
result = model.transcribe("ok.wav")
print(result["text"])
轉錄實例:
import whisper
import time
# 轉錄
def basic_transcribe(audio_path: str, model_size: str = "base"):
model = whisper.load_model(model_size)
start = time.time()
# 核心轉錄(內部自動執行梅爾譜+卷積+GELU預處理)
result = model.transcribe(
audio_path, language="zh", task="transcribe", word_timestamps=True
)
return {
"text": result["text"],
"word_level": [{"word": w["word"], "time": (w["start"], w["end"])} for w in result["segments"][0]["words"]],
"耗時": f"{time.time()-start:.2f}秒"
}
print(basic_transcribe("meeting.wav", "large-v3-turbo"))
結合PyAnnote實現會議轉錄:
from pyannote.audio import Pipeline
import whisper
def meeting_transcribe(audio_path: str, hf_token: str):
# 1. 説話人分離
diarize = Pipeline.from_pretrained("pyannote/speaker-diarization-3.1", use_auth_token=hf_token)
speaker_segments = diarize(audio_path)
# 2. Whisper轉錄全量音頻
model = whisper.load_model("medium")
result = model.transcribe(audio_path, word_timestamps=True)
# 3. 文本與説話人對齊
meeting_notes = []
for text_seg in result["segments"]:
# 匹配該時間段的説話人
speaker = next((spk for turn, _, spk in speaker_segments.itertracks()
if turn.start <= text_seg["start"] <= turn.end), "未知")
meeting_notes.append({"speaker": speaker, "text": text_seg["text"]})
return meeting_notes
print(meeting_transcribe("team_meeting.wav", "xxx"))
量化模型低延遲推理
import torch
from transformers import AutoModelForSpeechSeq2Seq, AutoProcessor
def industrial_whisper_infer(audio_tensor: torch.Tensor):
# 加載8-bit量化模型,顯存佔用降低45%
processor = AutoProcessor.from_pretrained("openai/whisper-large-v3-turbo")
model = AutoModelForSpeechSeq2Seq.from_pretrained(
"openai/whisper-large-v3-turbo", load_in_8bit=True, device_map="auto", torch_dtype=torch.float16
)
# 低延遲推理參數
inputs = processor(audio_tensor, sampling_rate=16000, return_tensors="pt").to("cuda")
with torch.no_grad():
gen_ids = model.generate(**inputs, max_new_tokens=256, do_sample=False, temperature=0.0)
return processor.batch_decode(gen_ids, skip_special_tokens=True)[0]
實踐建議
- 選型策略:邊緣場景選
tiny或small量化,桌面端選medium,工業級選largev3-turbo+GPU - 性能優化:優先8-bit量化+混合精度計算,複雜場景可裁剪高頻噪聲進一步降維
- 場景拓展:基礎轉錄用官方API,會議場景加PyAnnote,工業場景固化預處理流程
集成HuggingFace:
import torch
from transformers import AutoModelForSpeechSeq2Seq, AutoProcessor, pipeline
device = "cuda:0" if torch.cuda.is_available() else "cpu"
torch_dtype = torch.float16 if torch.cuda.is_available() else torch.float32
model_id = "openai/whisper-large-v2"
model = AutoModelForSpeechSeq2Seq.from_pretrained(
model_id, torch_dtype=torch_dtype, low_cpu_mem_usage=True, use_safetensors=True
)
model.to(device)
processor = AutoProcessor.from_pretrained(model_id)
pipe = pipeline(
"automatic-speech-recognition",
model=model,
tokenizer=processor.tokenizer,
feature_extractor=processor.feature_extractor,
torch_dtype=torch_dtype,
device=device,
)
generate_kwargs = {
"max_new_tokens": 448
}
命令行使用:
# 使用medium模型識別音頻,生成json、srt、tsv、txt、vtt文本文件
whisper --model medium dataset/9s.wav
解讀:
- srt:用於存儲音視頻文件的字幕信息
- tsv:存儲表格數據,類似於CSV
- vtt:用於存儲視頻文件的字幕信息
使用付費API:
import openai
CHATGPT_KEY = "OpenAI API Key"
openai.api_key = CHATGPT_KEY
audio_file = open("9s.wav", "rb")
transcription = openai.audio.transcriptions.create(
model="whisper-1",
file=audio_file,
response_format="text"
)
print(transcription)