技术原理与算法设计
基于 wav2vec2 预训练模型的构音障碍语音识别系统
系统流程图
用户上传患者语音/视频
↓
FFmpeg 提取音频 → 16kHz 单声道 WAV
↓
wav2vec2-large-xlsr-53 提取 1024 维语音嵌入
↓
余弦相似度匹配基线向量库
↓
≥ 0.75 → 输出匹配词
< 0.75 → 提示未识别
↓
输出标准中文 + 置信度 + Top-3 备选
软件基础设置信息
| 功能名称 | 构音障碍语音识别系统 |
| 适用场景 | 脑卒中构音障碍患者的个性化语音识别,将含糊不清的发音翻译为标准中文 |
| 开发语言 | Python 3.10+ |
| 核心依赖 | Flask, PyTorch, Transformers (HuggingFace), soundfile, NumPy, FFmpeg |
| AI 模型 | wav2vec2-large-xlsr-53-chinese-zh-cn,Transformer 架构中文语音预训练模型 |
| 匹配算法 | 余弦相似度(Cosine Similarity),L2 归一化嵌入向量 |
| 部署方式 | Flask + systemd + nginx,Ubuntu 22.04,支持 HTTPS |
软件系统设计
- 音频提取引擎:基于 FFmpeg,支持 MP4/WAV/M4A 等多格式输入,自动转换为 16kHz 单声道 PCM WAV,适配模型输入要求。
- 语音嵌入模型:使用 HuggingFace wav2vec2-large-xlsr-53 中文预训练模型,取 Transformer 最后一层 hidden state 的平均池化作为 1024 维语音嵌入向量,L2 归一化后存储。
- 基线向量库:JSON 格式存储每个词汇的多条嵌入向量及均值。支持同词多次录入,自动更新平均嵌入。患者录音独立存档。
- 相似度匹配器:查询嵌入与基线库中所有词的平均嵌入计算余弦相似度。相似度 ≥ 0.75 判定匹配成功,返回 Top-3 备选;低于阈值提示重新录入。
- 单人定制机制:每位患者拥有独立基线向量库,通过多次录入同词发音,逐步建立个性化语音特征模型。同一患者的含糊发音具有稳定的嵌入模式。
- Web 服务层:Flask 提供 RESTful API,支持文件上传识别(/api/recognize)和基线录入(/api/baseline/add),模型常驻内存避免重复加载。
- 前端交互:响应式 Web 设计,深色主题适配移动端。页面内嵌上传识别区,结果实时展示大字结果 + 置信度进度条 + 备选列表 + 一键朗读。
参数配置
| 字段 | 说明 |
| 嵌入维度 | 1024 维 float32 向量,L2 归一化后点积即余弦相似度 |
| 采样率 | 16,000 Hz,单声道,FFmpeg 自动重采样转换 |
| 相似度阈值 | THRESHOLD = 0.75,低于此值判定为"未识别",高于则输出最佳匹配 |
| 模型名称 | jonatasgrosman/wav2vec2-large-xlsr-53-chinese-zh-cn |
| 模型大小 | 约 1.2 GB(首次自动下载至 ~/.cache/huggingface/) |
| 推理速度 | 单次嵌入提取 < 200ms(CPU),模型常驻内存 |
| 上传限制 | MAX_CONTENT_LENGTH = 128MB,支持 MP4/WAV/M4A 格式 |
| 音频格式 | PCM S16LE, 16kHz, Mono, FFmpeg 自动转换 |
语音嵌入提取模块
import torch, numpy as np, soundfile as sf
from transformers import Wav2Vec2Processor, Wav2Vec2Model
MODEL = "jonatasgrosman/wav2vec2-large-xlsr-53-chinese-zh-cn"
processor = Wav2Vec2Processor.from_pretrained(MODEL)
model = Wav2Vec2Model.from_pretrained(MODEL); model.eval()
def extract_embedding(audio_path):
audio, _ = sf.read(audio_path, dtype="float32")
inputs = processor(audio, sampling_rate=16000, return_tensors="pt")
with torch.no_grad():
outputs = model(**inputs)
# 最后一层 hidden state 平均池化 -> 1024D embedding
emb = outputs.last_hidden_state.mean(dim=1).squeeze().numpy()
return emb / np.linalg.norm(emb) # L2 归一化
余弦相似度匹配模块
def match_embedding(query_emb, threshold=0.75):
baselines = load_baselines() # 从 JSON 加载基线向量库
scores = []
for word, entry in baselines.items():
mean_emb = np.array(entry["mean_embedding"])
sim = np.dot(query_emb, mean_emb) # L2 归一化后点积=余弦
scores.append((word, float(sim)))
scores.sort(key=lambda x: x[1], reverse=True)
best_word, best_score = scores[0]
if best_score < threshold:
return {"result": None, "confidence": best_score}
return {"result": best_word, "confidence": best_score,
"candidates": scores[:3]}