如何在HuggingFace中获取输出字母对应的音频时间位置
主流基于HuggingFace SpeechRecognitionModel 实现的STT模型(包括CTC类的Wav2Vec2、HuBERT,Seq2Seq类的Whisper等)都支持输出帧级预测结果,只要做简单对齐就能拿到每个字母/字符对应的音频时间,不需要额外微调模型,具体实现步骤如下:
前置配置
推理前不要直接调用封装好的整句转写接口(这类接口默认会做词级合并,丢弃字符级时间信息),需要加载模型对应的原生Processor,推理时保留原始帧级输出。基础初始化代码参考:from transformers import AutoProcessor, AutoModelForCTC, AutoModelForSpeechSeq2Seq import torch import librosa # 替换成你自己使用的模型路径,CTC类模型用AutoModelForCTC加载,Whisper这类Seq2Seq模型换对应类 processor = AutoProcessor.from_pretrained("你的本地/开源模型路径") model = AutoModelForCTC.from_pretrained("你的本地/开源模型路径") model.eval() # 加载音频并重采样到模型要求的采样率(绝大多数STT模型要求16000Hz) audio, sample_rate = librosa.load("待转写音频.wav", sr=16000) model_inputs = processor(audio, sampling_rate=16000, return_tensors="pt")计算帧时间基准
STT模型通过卷积层对原始音频做下采样,输出的每个预测帧对应固定长度的音频片段,单帧时长可以通过模型配置直接计算:单帧时长(秒)= 模型配置hop_length / 音频采样率。比如常用的Wav2Vec2系列hop_length为320,16000Hz采样率下每帧对应20ms音频,这个值就是时间对齐的最小单位。CTC类模型字符对齐逻辑
如果你用的是Wav2Vec2这类CTC架构模型,拿到帧级logits后按照CTC规则做对齐,就能逐个匹配字符和对应时间,参考代码:with torch.no_grad(): frame_logits = model(**model_inputs).logits[0] # 取单条音频的帧输出 frame_duration = model.config.hop_length / processor.feature_extractor.sampling_rate frame_pred_tokens = torch.argmax(frame_logits, dim=-1).numpy() # 每帧预测的token id char_time_map = [] prev_token = processor.tokenizer.pad_token_id # CTC空白符默认对应pad token current_char_start_frame = 0 for frame_idx, token_id in enumerate(frame_pred_tokens): # 碰到空白符,说明上一段连续字符结束 if token_id == processor.tokenizer.pad_token_id: if prev_token != processor.tokenizer.pad_token_id: char = processor.tokenizer.decode([prev_token]).strip() if char: # 过滤特殊空字符 char_time_map.append({ "char": char, "start_time": round(current_char_start_frame * frame_duration, 3), "end_time": round(frame_idx * frame_duration, 3) }) prev_token = token_id continue # 碰到和上一帧不同的非空白token,说明新字符开始 if token_id != prev_token: if prev_token != processor.tokenizer.pad_token_id: char = processor.tokenizer.decode([prev_token]).strip() if char: char_time_map.append({ "char": char, "start_time": round(current_char_start_frame * frame_duration, 3), "end_time": round(frame_idx * frame_duration, 3) }) current_char_start_frame = frame_idx prev_token = token_id # 补全最后一段字符的时间 if prev_token != processor.tokenizer.pad_token_id: char = processor.tokenizer.decode([prev_token]).strip() if char: char_time_map.append({ "char": char, "start_time": round(current_char_start_frame * frame_duration, 3), "end_time": round(len(frame_pred_tokens) * frame_duration, 3) })最终
char_time_map里就按转写顺序存储了每个字符对应的起止时间,单位为秒。Seq2Seq类模型简化用法
如果你用的是Whisper这类Seq2Seq架构的STT模型,不需要手动实现对齐逻辑,直接调用HuggingFace的自动语音识别pipeline时传入return_timestamps="char"参数,返回结果里会直接携带每个字符的起止时间,开箱即用。
注意:不同模型的CTC空白符id不要硬编码,一定要从当前加载的tokenizer配置里读取,避免对齐错误;常规模型的字符时间戳误差在1-2帧(20-40ms)区间,属于正常精度范围。
内容的提问来源于stack exchange,提问作者DuckQueen

