AI 音视频转录背后的技术链路,从信号处理到结构化笔记的完整实现
我上周整理了一个三小时的 AI 技术直播,用的工具自动生成了图文笔记和思维导图。我盯着那份笔记看了半天,好奇这背后的技术链路是怎么跑通的。
不是简单地把语音变成文字,而是从声波到结构化知识,中间隔着信号处理、语音识别、自然语言理解、知识图谱好几座山。
音频先变成数字,这一步就有讲究
声音是空气振动,麦克风把振动变成模拟电信号,ADC 芯片再采样成数字。采样率是关键参数,CD 音质 44.1kHz,语音电话 8kHz。采样率越高,高频细节越多,但数据量也越大。
拿到数字音频后,预处理直接影响后续准确率。降噪、去混响、音量归一化,这些操作在安静环境下差别不大,但在咖啡厅录的播客里,预处理能把识别率从 70% 拉到 90% 以上。
我用 FFmpeg 做过一个实验,同一段带噪音频,先做高通滤波去噪再识别,WER(词错误率)降了差不多 8 个百分点。
python
import subprocess
def preprocess_audio(input_path, output_path):
# 高通滤波 + 降噪 + 音量归一化
cmd = [
'ffmpeg', '-i', input_path,
'-af', 'highpass=f=200,afftdn=nf=-25,volume=1.5',
'-ar', '16000', # 统一采样率到 16kHz
'-ac', '1', # 单声道
output_path
]
subprocess.run(cmd, check=True)
# 使用
preprocess_audio('noisy_podcast.mp3', 'cleaned.wav')
从声波到特征向量
预处理完的信号还是时域波形,直接扔给神经网络效果不好。需要特征提取,把时域转成频域表示。
最经典的做法是梅尔频率倒谱系数(MFCC)。模拟人耳听觉特性,对低频敏感、对高频迟钝。一段音频切成 20-40 毫秒的帧,每帧提取 13-40 维特征,变成特征向量。
python
import librosa
import numpy as np
def extract_mfcc(audio_path, n_mfcc=40):
y, sr = librosa.load(audio_path, sr=16000)
# 预加重,增强高频
y = librosa.effects.preemphasis(y)
# 提取 MFCC
mfccs = librosa.feature.mfcc(
y=y, sr=sr, n_mfcc=n_mfcc,
n_fft=512, hop_length=160
)
# 添加一阶和二阶差分,捕捉动态特征
delta = librosa.feature.delta(mfccs)
delta2 = librosa.feature.delta(mfccs, order=2)
return np.vstack([mfccs, delta, delta2])
# 使用
features = extract_mfcc('cleaned.wav')
# 输出 shape: (120, 时间帧数),40维MFCC + 40维一阶差分 + 40维二阶差分
但现在更流行的做法是让神经网络自己学特征。wav2vec 2.0、HuBERT 这些模型直接拿原始波形或梅尔频谱图当输入,效果比手工设计的 MFCC 好一截。
声学模型,从 HMM 到 Transformer
特征向量怎么变成文字?靠声学模型。
早期用隐马尔可夫模型(HMM),把语音看作状态序列,每个状态对应音素。HMM 需要大量人工设计,建模能力有限,噪声场景下基本崩盘。
2012 年后 DNN-HMM 混合系统成了主流,深度神经网络替代高斯混合模型,负责把特征映射到音素概率。
再往后,端到端模型彻底改变游戏规则。Listen-Attend-Spell、Transformer、Conformer 直接从音频学到文字,不需要中间音素表示。
python
import torch
import torch.nn as nn
class ConformerBlock(nn.Module):
def __init__(self, dim, num_heads, d_ff, kernel_size=31):
super().__init__()
# 前馈网络
self.ffn1 = nn.Sequential(
nn.Linear(dim, d_ff),
nn.SiLU(),
nn.Dropout(0.1),
nn.Linear(d_ff, dim)
)
# 多头自注意力
self.attention = nn.MultiheadAttention(dim, num_heads, batch_first=True)
self.norm1 = nn.LayerNorm(dim)
# 卷积模块
self.conv = nn.Sequential(
nn.LayerNorm(dim),
nn.Conv1d(dim, dim, kernel_size, padding=kernel_size//2, groups=dim),
nn.BatchNorm1d(dim),
nn.SiLU(),
nn.Conv1d(dim, dim, 1),
nn.BatchNorm1d(dim),
nn.SiLU()
)
# 第二个前馈网络
self.ffn2 = nn.Sequential(
nn.Linear(dim, d_ff),
nn.SiLU(),
nn.Dropout(0.1),
nn.Linear(d_ff, dim)
)
self.norm2 = nn.LayerNorm(dim)
def forward(self, x):
# 前馈 + 残差
x = x + 0.5 * self.ffn1(x)
# 注意力 + 残差
attn_out, _ = self.attention(x, x, x)
x = self.norm1(x + attn_out)
# 卷积 + 残差
conv_out = self.conv(x.transpose(1, 2)).transpose(1, 2)
x = x + conv_out
# 第二个前馈 + 残差
x = x + 0.5 * self.ffn2(x)
x = self.norm2(x)
return x
Conformer 把 CNN 的局部特征提取和 Transformer 的全局注意力结合,在 LibriSpeech 上 WER 干到了 2% 以下。
语言模型,让句子通顺
声学模型输出单个字的概率,但语音识别是猜句子,不是猜字。语言模型给通顺句子打高分,不通顺的打低分。
传统 n-gram 统计模型看前面几个字预测下一个。现在大语言模型接棒,GPT、BERT 对上下文理解更深,能纠正声学模型的错误。
python
from transformers import BertTokenizer, BertForMaskedLM
import torch
class RescoringLM:
def __init__(self):
self.tokenizer = BertTokenizer.from_pretrained('bert-base-chinese')
self.model = BertForMaskedLM.from_pretrained('bert-base-chinese')
self.model.eval()
def score(self, text):
# 计算句子的困惑度
inputs = self.tokenizer(text, return_tensors='pt')
with torch.no_grad():
outputs = self.model(**inputs)
logits = outputs.logits
# 计算交叉熵损失作为分数
labels = inputs['input_ids']
loss = torch.nn.functional.cross_entropy(
logits.view(-1, logits.size(-1)),
labels.view(-1),
reduction='mean'
)
return -loss.item() # 分数越高越好
def rescore(self, candidates):
# 对多个候选句子重新打分
scores = [(c, self.score(c)) for c in candidates]
return sorted(scores, key=lambda x: x[1], reverse=True)
# 使用
lm = RescoringLM()
candidates = ["深度学习是机器学习的分支", "深度学习是机器学习的分子"]
best = lm.rescore(candidates)
# 输出: [('深度学习是机器学习的分支', -2.3), ('深度学习是机器学习的分子', -5.1)]
说话人识别,多人场景的噩梦
会议录音、播客访谈、课堂讨论,多个说话人。单纯转文字不够,得知道哪句话是谁说的。
说话人识别分两个层面。一是分割,把音频切成不同说话人的片段。二是聚类,把同一个人的片段归到一起。
python
from speechbrain.pretrained import EncoderClassifier
import torch
import numpy as np
from sklearn.cluster import AgglomerativeClustering
class SpeakerDiarizer:
def __init__(self):
# 加载预训练的说话人嵌入模型
self.encoder = EncoderClassifier.from_hparams(
source="speechbrain/ecapa-tdnn"
)
def extract_embeddings(self, audio_path, segments):
# 提取每个语音片段的嵌入向量
embeddings = []
for start, end in segments:
# 切分音频
waveform, sr = torchaudio.load(audio_path)
start_sample = int(start * sr)
end_sample = int(end * sr)
segment = waveform[:, start_sample:end_sample]
# 提取嵌入
emb = self.encoder.encode_batch(segment)
embeddings.append(emb.squeeze().numpy())
return np.array(embeddings)
def cluster(self, embeddings, num_speakers=None):
# 层次聚类
if num_speakers:
clusterer = AgglomerativeClustering(n_clusters=num_speakers)
else:
# 自动决定聚类数量
clusterer = AgglomerativeClustering(
n_clusters=None,
distance_threshold=0.5
)
labels = clusterer.fit_predict(embeddings)
return labels
# 使用
diarizer = SpeakerDiarizer()
segments = [(0, 10), (10, 25), (25, 40)] # 语音片段时间戳
embeddings = diarizer.extract_embeddings('meeting.wav', segments)
speaker_labels = diarizer.cluster(embeddings)
# 输出: [0, 1, 0] 表示第1、3段是说话人A,第2段是说话人B
实际工程中这个环节最容易出问题。两个人声音相似、有人说话断断续续、背景噪音干扰,都会导致聚类错误。我试过一个三小时会议录音,最后发现两个说话人被误判成一个人,手动修正花了半小时。
从语音到笔记,最后一公里
语音识别把音频变成文字,但用户要的是笔记,不是文字稿。
这需要 NLP 技术介入。文本摘要提取核心观点,命名实体识别找关键人名地名,关系抽取梳理逻辑结构。思维导图生成需要理解文本层级关系,AI 播客需要把文字改写成对话体。
python
from transformers import pipeline
class NoteGenerator:
def __init__(self):
self.summarizer = pipeline("summarization", model="facebook/bart-large-cnn")
self.ner = pipeline("ner", model="dslim/bert-base-NER")
self.classifier = pipeline("zero-shot-classification")
def generate_summary(self, text, max_length=150):
# 生成摘要
summary = self.summarizer(
text,
max_length=max_length,
min_length=30,
do_sample=False
)
return summary[0]['summary_text']
def extract_entities(self, text):
# 提取命名实体
entities = self.ner(text)
return {
'persons': [e['word'] for e in entities if e['entity'] == 'B-PER'],
'organizations': [e['word'] for e in entities if e['entity'] == 'B-ORG'],
'locations': [e['word'] for e in entities if e['entity'] == 'B-LOC']
}
def classify_structure(self, text):
# 零样本分类,判断文本结构
candidate_labels = ['背景介绍', '核心观点', '案例分析', '总结结论']
result = self.classifier(text, candidate_labels)
return result['labels'][0] # 返回最可能的类别
# 使用
generator = NoteGenerator()
text = "深度学习是机器学习的一个分支,它基于神经网络架构..."
summary = generator.generate_summary(text)
entities = generator.extract_entities(text)
structure = generator.classify_structure(text)
现在的做法是先让大语言模型做理解和总结,再按需生成不同格式。比如把技术演讲转成笔记,模型先提取「问题-方案-实验-结论」的结构,再填充每个部分的关键细节。
实际落地的坑
长音频的内存和计算是挑战。两小时视频按 10 毫秒一帧处理,特征序列长度超过 72 万。直接扔给 Transformer,显存扛不住。工程上一般用滑动窗口或层级处理,先切成段落再分别识别。
专业术语识别也是老大难。医学、法律、金融领域有大量专有名词,通用模型没见过,识别出来全是谐音字。解决办法是用领域语料做微调,或者运行时动态注入热词。
多语言混合更麻烦。中文演讲里突然冒出英文术语,模型经常切换不过来。现在的做法是用语言识别模块先判断语种,再路由到对应识别模型,但切换点判断本身不太准。
这类工具的工程实践
市面上做音视频转笔记的产品,底层架构大同小异。前端负责音视频解析和上传,后端做语音识别、NLP 处理、结果生成。
比如我平时用的 Ai 好记 ,就是基于这套思路。
它把 B 站、抖音、小宇宙这些平台的链接直接解析,后端做语音提取和识别,然后生成图文笔记、思维导图、精华速览。
多人对话场景下能区分说话人,带时间戳标记。
专业领域比如医学、金融,用专门语料库训练过,无字幕视频也能识别得比较准。
跟纯语音识别工具比,这类产品多了 NLP 层和生成层。不是只给文字稿,而是直接给结构化笔记。对学习效率的提升,我觉得还是挺明显的。
FAQ
Q,语音识别准确率能做到多少?
A,安静环境下普通话可以达到 95% 以上,带口音或噪声场景会下降。专业术语密集的领域需要专门优化。
Q,长视频怎么处理?
A,一般分段处理再合并。技术上用滑动窗口或层级注意力,工程上限制单次处理时长。
Q,多语言视频能识别吗?
A,可以,但需要语言识别模块先判断语种。中英混合场景切换准确率还在提升。
Q,说话人识别准确率怎么样?
A,两人差异明显时不错,声音相似或噪音大时容易出错。目前还是辅助功能,不能完全替代人工核对。