OpenAI Whisper:生态

OpenAI Whisper:生态

概述

语音转文本ASR工具合集介绍ASR基础概念,汇总几款语音识别模型和项目,其中就包括OpenAI开源的Whisper。OpenAI Whisper则是技术原理和实战。

围绕Whisper有着非常庞大的生态,本文试图介绍几个,不够深入。

whisper.cpp

Whisper模型的开源(GitHub,44.5K Star,4.9K Fork)C++实现版本,核心优势:

  • 几乎无依赖:最大亮点,不同于其他需通过pip安装一大堆Python库的项目,whisper.cpp几乎没有依赖;
  • 性能卓越:C++原生性能优势,转录速度非常快,资源占用也相对较低;
  • 跨平台与硬件支持:支持多种硬件加速,从主流的NVIDIA(CUDA)、AMD(OpenCL)显卡,到苹果的Metal框架,甚至是专用NPU,都能利用起来进一步提速;
  • 部署简单:可以直接下载官方编译好的可执行文件,解压即用,整个程序包非常小巧。

实战

HF模型地址

模型名称文件大小推荐场景
ggml-small.bin~488MB电脑配置较低,对速度要求高,能接受少量错误
ggml-medium.bin~1.5GB平衡之选
ggml-large-v2.bin~3.0GB追求高准确率,电脑配置较好
ggml-large-v3.bin~3.1GB最高准确率,目前效果最好的模型,推荐给追求极致效果的用户
ggml-large-v3-turbo.bin~1.6GBlarge-v3的优化版本,速度比v2/v3更快,准确率相差不大,适合大多数用户

whisper.cpp本身只处理标准格式.wav`文件,对于mp3、m4a、mp4、mkv等格式,直接处理可能会报错或得到空白结果。可使用FFmpeg预先转换

Faster-Whisper

开源(GitHub,19.3K Star,1.6K Fork),使用CTranslate2技术。

没有提供图形用户界面GUI,也没提供命令行接口,只能作为一个Python库被调用。

模型:

实战

安装:pip install faster-whisper

实例:

from faster_whisper import WhisperModel # GPU + FP16 model = WhisperModel("medium", device="cuda", compute_type="float16")# GPU + INT8 model = WhisperModel("large-v2", device="cuda", compute_type="int8_float16") model = WhisperModel("large-v3", device="cpu", compute_type="int8") segments, info = model.transcribe("audio.mp3", beam_size=5, language="en", condition_on_previous_text=False)print("Detected language '%s' with probability %f"%(info.language, info.language_probability))for segment in segments:print("[%.2fs -> %.2fs] %s"%(segment.start, segment.end, segment.text))

以及:

from faster_whisper import WhisperModel, BatchedInferencePipeline model = WhisperModel("turbo", device="cuda", compute_type="float16") batched_model = BatchedInferencePipeline(model=model) segments, info = batched_model.transcribe("audio.mp3", batch_size=16)for segment in segments:print("[%.2fs -> %.2fs] %s"%(segment.start, segment.end, segment.text))

WhisperX

论文,开源(GitHub,19K Star,2K Fork)支持词级时间戳(及分词)的说话人分离的ASR工具。

后端基于:Faster-Whisper和CTranslate2。

只有命令行工具,没提供API。

实战

安装:pip install whisperx或基于nv安装:uvx whisperx

Whisper

开源(GitHub,9.9K Star,895 Fork)whisper.cpp的Windows实现版,基于C++/C后端,C#界面。

WhisperLiveKit

开源(GitHub,8.9K Star,866 Fork)。

架构

在这里插入图片描述

安装:pip install whisperlivekit

Python SDK集成:

import asyncio from contextlib import asynccontextmanager from fastapi import FastAPI, WebSocket, WebSocketDisconnect from fastapi.responses import HTMLResponse from whisperlivekit import AudioProcessor, TranscriptionEngine, parse_args transcription_engine =None@asynccontextmanagerasyncdeflifespan(app: FastAPI):global transcription_engine transcription_engine = TranscriptionEngine(model="medium", diarization=True, lan="en")yield app = FastAPI(lifespan=lifespan)asyncdefhandle_websocket_results(websocket: WebSocket, results_generator):asyncfor response in results_generator:await websocket.send_json(response)await websocket.send_json({"type":"ready_to_stop"})@app.websocket("/asr")asyncdefwebsocket_endpoint(websocket: WebSocket):global transcription_engine # Create a new AudioProcessor for each connection, passing the shared engine audio_processor = AudioProcessor(transcription_engine=transcription_engine) results_generator =await audio_processor.create_tasks() results_task = asyncio.create_task(handle_websocket_results(websocket, results_generator))await websocket.accept()whileTrue: message =await websocket.receive_bytes()await audio_processor.process_audio(message)

Whisper-Diarization

开源(GitHub,3.6K Star,496 Fork)

WhisperLive

开源(GitHub,3.6K Star,496 Fork)

Faster-Whisper-GUI

开源(GitHub,2.8K Star,164 Fork)。

适用于Faster-Whisper、WhisperX的GUI程序。

参数:

  • 转写参数
    • audio:输入文件的路径,或类似文件的对象,或音频波形;
    • language:音频语言,如en。如果未设置,则在音频前30秒内检测语言;
    • task:要执行的任务,转录或翻译;
    • beam_size:用于解码的beam大小;
    • best_of:采样时使用非零温度的候选数;
    • patience:Beam搜索耐心因子;
    • length_penalty:指数长度惩罚常数;
    • temperature:采样温度,可以是温度元组,如果根据compression_ratio_thresholdlog_prob_threshold失败,则会依次使用;
    • compression_ratio_threshold:如果gzip压缩比高于此值,则视为失败;
    • log_prob_threshold:如果对采样标记的平均对数概率低于此值,则视为失败;
    • no_speech_threshold:如果无话音概率高于此值,并且对采样标记的平均对数概率低于log_prob_threshold,则将该段视为静音;
    • condition_on_previous_text:如果为True,则将模型的前一个输出作为下一个窗口的提示提供;禁用可能会导致文本在窗口之间不一致,但模型不太容易陷入失败循环,比如重复循环或时间戳失去同步;
    • initial_prompt:为第一个窗口提供的可选文本字符串或词元id可迭代项;
    • prefix:为第一个窗口提供的可选文本前缀;
    • suppress_blank:在采样开始时抑制空白输出;
    • suppress_tokens:要抑制的标记ID列表。-1将抑制配置文件config.json中定义的默认符号集;
    • without_timestamps:仅对文本标记进行采样;
    • max_initial_timestamp:初始时间戳不能晚于此时间;
    • word_timestamps:使用交叉注意力模式和动态时间规整提取单词级时间戳,并在每个段的每个单词中包含时间戳;
    • prepend_punctuations:如果word_timestampsTrue,则将这些标点符号与下一个单词合并;
    • append_punctuations:如果word_timestampsTrue,则将这些标点符号与前一个单词合并;
    • vad_filter:启用语音活动检测(VAD)以过滤掉没有语音的音频部分,使用Silero VAD模型;
    • vad_parametersSilero VAD参数字典或VadOptions类;
    • max_new_tokens:每个区块生成的新令牌的最大数量。未设置,最大值将通过默认max_size设置;
    • chunk_length:音频段的长度。如果不是None,将覆盖FeatureExtractor的默认chunk_size
    • clip_timestamps:逗号分隔的要处理的剪辑的时间戳列表(以秒为单位)开始,结束,开始,结束......。最后一个结束时间戳默认为文件的结束。如果使用clip_timestamps,将忽略VAD设置;
    • hallucination_silence_threshold:当word_timestampsTrue时,当检测到可能的幻觉时,跳过长于此阈值(以秒为单位)的静默期;
    • hotwords:为模型提供的热词/提示短语。如果prefix不是None,则无效;
    • language_detection_threshold:如果语言标记的最大概率高于此值,则会检测为该语言;
    • language_detection_segments:语言检测需要考虑的分段数量。
  • VAD参数
    • threshold:语音阈值。Silero VAD为每个音频块输出语音概率,概率高于此值的认为是语音。最好对每个数据集单独调整此参数,0.5对大多数数据集来说都非常好;
    • min_speech_duration_ms:短于min_speech_duration_ms的最终语音块会被抛弃;
    • max_speech_duration_s:语音块的最大持续时间(秒)。比max_speech_duration_s更长的块将在最后一个持续时间超过100ms的静音时间戳拆分(如果有的话),以防止过度切割。否则,它们将在max_speech_duration_s之前强制拆分;
    • min_silence_duration_ms:在每个语音块结束时等待min_silence_duration_ms再拆分它;
    • window_size_sampleswindow_size_samples大小的音频块被馈送到Silero VAD模型。Silero VAD模型使用16000采样率训练得到512,1024、1536样本,其他值可能会影响模型性能;
    • speech_pad_ms:最终的语音块每边都由speech_pad_ms填充。
  • 模型参数
  • model_size_or_path:使用的模型大小(tiny,tiny.en,base,base.en,small,small.en,medium, medium.en,large-v1或large-v2),转换后的模型目录路径,或来自HuggingFace的CTranslate2转换的Whisper模型ID。当配置了大小或模型ID时,转换后的模型将从HuggingFace下载。
  • device:转写设备("cpu""cuda""auto")。
  • device_index:要使用的设备ID。也可以通过传递ID列表(如[0,1,2])在多GPU上加载模型。在这种情况下,当从多个Python线程调用transcribe()时,可以并行运行多个转录;
  • compute_type:计算类型。请参阅https://opennmt.net/CTranslate2/quantization.html。
  • cpu_threads:在CPU上运行时使用的线程数(默认为4)。非零值会覆盖OMP_NUM_THREADS环境变量。
  • num_workers:当从多个Python线程调用transcribe()时,具有多个工作线程可以在运行模型时实现真正的并行性(对self.model.generate()的并发调用将并行运行)。可以以增加内存使用为代价提高整体吞吐量。
  • download_root:模型应该保存的目录。如果未设置,则模型将保存在标准HuggingFace缓存目录中。
  • local_files_only:如果为True,避免下载文件,并在本地缓存的文件存在时返回其路径。

whisper-timestamped

开源(GitHub,2.7K Star,201 Fork)

whisper-ctranslate2

开源(GitHub,1.2K Star,116 Fork)基于CTranslate2兼容OpenAI的命令行客户端。

stable-ts

开源(GitHub,2.1K Star,223 Fork)基于Whisper进行转录、强制对齐和音频索引。

echogarden

开源(GitHub,420 Star,43 Fork)

CarelessWhisper

论文GitHub

哈哈哈:George Michael有同名歌曲。

这些年,ASR模型依托于Transformer架构发展特别快,主要分三类:只用编码器的、只用解码器的、编码器-解码器全用,Whisper属于第三种。

问题:Whisper编码器是非因果的,得输入全部语音才能输出表示,没法实时转录。试图实现流式Whisper的实践举例:

  • Simul-Whisper:不微调,靠对齐头判断啥时候输出Token,但每次都要把输入补到30秒,计算效率差;
  • UfalWhisper:不微调,靠音频缓冲区和局部一致算法,但同样要补全输入,效率低;
  • U2-Whisper:微调编码器加个CTC头,推理要走两趟(先CTC预测,再解码器排序),麻烦;
  • WhisperFlow:让Whisper检测每个分块末尾的静音词,但要改架构。

流式ASR的几种方向:

  • RNN-T:循环神经网络转换器,最经典的流式架构,用LSTM或双向RNN,天生因果(只能看过去的语音),适合实时。比如有人用知识蒸馏把离线模型的知识传给RNN-T,还有人优化它适配移动端,但RNN的并行性差,大模型效率不高。
  • Transformer Transducer:把RNN-T里的循环组件换成Transformer编码器,兼顾并行和流式。比如混合编码器(一个因果掩码,一个没有),或基于分块注意力,但这些方法要么架构复杂,要么适配预训练模型麻烦。
  • 直接用Transformer做流式:如Emformer(增强版内存Transformer)、SpeechLLM(把音频和文本嵌入喂给LLM解码器),还有微调自监督语音模型(S3M)改流式,但这些大多不是针对Whisper设计,适配起来要大改。
  • 改Whisper做流式的前人方法:就是引言里提到的那几个,要么不微调但效率低(补全输入),要么要加额外参数、改架构(比如加CTC头),总之都有短板。

Whisper的基本工作方式:

  • 输入是log-mel谱,先过几层CNN压缩时间维度,得到序列 X T = ( x 1 , … , x T ) XT=(x_1,…,x_T) XT=(x1​,…,xT​),其中 T T T是帧数;
  • 编码器把 X T X_T XT​转换成表示序列 Z T = ( z 1 , … , z T ) Z_T=(z_1,…,z_T) ZT​=(z1​,…,zT​),其中 z t z_t zt​是 d d d维向量;
  • 解码器自回归预测:给定之前的 t o k e n ( y ^ ) token(ŷ) token(y^​)和 Z T ZT ZT,输出下一个 t o k e n ( y ^ i ) token(ŷi) token(y^​i)的概率 P ( y ^ i ∣ y ^ , Z T ) P(ŷ_i|ŷ,Z_T) P(y^​i​∣y^​,ZT​),贪心解码就是选概率最大Token。

流式场景的关键区别是分块处理:不是等全部语音来,而是每次处理一个分块chunk。比如第 k k k个分块是 X ( ( k − 1 ) τ + 1 ) k τ X((k-1)τ+1)^{kτ} X((k−1)τ+1)kτ,其中 τ τ τ是分块大小,单位是帧数,对应几十毫秒的语音)。流式编码器处理这个分块,输出的表示是 Z ^ ( ( k − 1 ) τ + 1 ) k τ Ẑ((k-1)τ+1)^{kτ} Z^((k−1)τ+1)kτ,这个 Z ^ Ẑ Z^和离线编码器处理全序列得到的 Z Z Z的对应部分不一样。

核心目标:微调后的流式编码器+解码器,使词错误率(WER)尽可能接近离线Whisper的WER,同时延迟低(分块小)、计算快。

核心创新集中在:流式编码器、流式解码器、推理机制、词级时间戳

流式编码器:加个因果掩码,让模型只看过去

普通Whisper编码器是非因果的,每个帧都能看到所有帧(包括未来的),这在流式里肯定不行。解决办法是给编码器的自注意力加一个因果掩码矩阵M,让每个帧只能看自己和之前的帧。

  • 元素是0:不屏蔽,能看;
  • 元素是 − ∞ -∞ −∞:屏蔽,看不到;

分初始分块 τ 0 τ_0 τ0​和后续分块 τ τ τ:初始分块(比如600毫秒)内所有帧可以互看(要先攒点语音保证准确性),后续分块(比如300毫秒)里,第 i i i帧只能看和它同属一个分块或更早分块的帧。

在这里插入图片描述


举个例子(图1):τ=15帧(对应300毫秒,因为每帧20毫秒),τ₀=30帧(600毫秒),处理第10个分块时,帧35只能看帧23(同属第3个分块),但看不到帧50(第4个分块,未来的)。

证明关键定理:加掩码后,分块处理到第 k k k个分块得到的前 k τ kτ kτ帧表示,和一次性处理全序列得到的前 k τ kτ kτ帧表示完全一样。保证流式处理的准确性不会因为分块而下降,而且不用每次都重算所有历史帧,只算新分块就行,效率大大提高。

流式解码器:带缓存的注意力,处理动态输入

在这里插入图片描述


解码器要做两件事:一是记住之前的语音表示(缓存),二是自回归预测Token。

关键是注意力机制的缓存策略:

  • 交叉注意力(看编码器输出):新分块的K和V算出来后缓存,下次用的时候直接和新的Q算,不用重算历史K/V;
  • 自注意力(看之前Token):没法直接缓存,因为新语音会改变解码器的上下文,导致Token的嵌入变化,之前的自注意力结果就不准。作者的分析:很多Whisper的交叉注意力头不是时间对齐的,新语音会让交叉注意力输出变,进而影响自注意力,所以自注意力缓存暂时不可行。

推理机制:判断稳定Token,避免反复改结果

流式转录的痛点是分块可能切在词中间,比如appleap在这个分块,ple在下个分块,直接输出ap肯定错。解决办法是定义稳定Token,只输出稳定Token,不稳定的就等下一个分块再确认。
贪心解码的稳定Token
满足以下任一条件,Token在第 k k k个分块可以被认为是稳定的:

  • 在第 k k k个分块的概率≥第 k − 1 k-1 k−1个分块的概率(概率上升,说明更确定);
  • 还是第 k k k个分块里概率最大的Token(即使概率降了,但还是最可能的)。

解码时,如果发现最后 n n n个Token里有不稳定的,就回到第一个不稳定的位置重新算,把后面Token删掉。可以证明这个贪心解码是局部最优,比普通贪心解码的概率路径更高。

BeamSearch解码的稳定Token
BeamSearch会同时保留top-b个候选序列( b b b是束大小),稳定Token:这个Token在当前分块的top-b候选里。
停止准则:如果某个候选序列出现结束符(EOT),不马上输出,等下一个分块再确认——避免因为分块切在词中间导致提前结束,减少幻觉(比如重复词)。

词级时间戳:微调的意外收获

因为微调时使用弱对齐数据集(靠强制对齐把语音和文本对应起来),模型的Token和声学特征对齐得更好,顺带解决实时词级时间戳的问题。

在这里插入图片描述


具体方法:只要模型预测出一个新的非EOT Token,就把当前分块的时间戳记给这个词;下一个词的开始时间,就是上一个词的结束时间。这个方法只在小分块(40毫秒或100毫秒)下有效,大分块可能包含多个词,边界就不准确。

KV缓存和因果掩码带来的复杂度降低:

  • 编码器KV缓存:普通非因果编码器每次处理新分块,都要重算所有历史帧的自注意力,复杂度 O ( T 2 d ) O(T²d) O(T2d);加因果掩码后,历史帧的K和V可以缓存,新分块只算自己的K/V,再和历史缓存结合,复杂度从二次降到线性 O ( T d ) O(Td) O(Td);
  • 复杂度定理:作者的方法处理全长为T的序列,计算量是 O ( T 2 d + T d 2 ) O(T^2d + Td^2) O(T2d+Td2);之前方法(如Simul-Whisper)因为要补全输入,计算量是 O ( T 3 d / τ + T 2 d 2 / τ ) O(T^3d/τ + T^2d^2/τ) O(T3d/τ+T2d2/τ),分块越小 τ τ τ越小,差距越大,比如 τ τ τ=40毫秒时,比Simul-Whisper快好几个量级。
  • 解码器效率:交叉注意力用缓存,不用重算历史;自注意力虽然不能缓存,但因为分块小,计算量也不大。

微调目标:

  • 让编码器学会因果表示;
  • 让解码器知道什么时候输出Token。

具体步骤如下:

  • LoRA层插入:只在关键地方加LoRA(不训整个模型,轻量)——编码器的自注意力层、解码器的自注意力层、解码器的交叉注意力层。LoRA的秩(rank)根据模型大小调:base/small模型用32,large-v2用4(避免过拟合)。
  • 数据集处理:用弱对齐数据集,先通过强制对齐把语音和文本对应起来(知道每个Token的开始/结束时间),然后采样一部分时间点来训(不用每个时间点都训,效率高)。比如每个分块大小τ对应一组时间点,采样其中的f%(f是采样率,比如0.02~0.25)来计算交叉熵损失。
  • 训练参数:优化器用AdamW,学习率1e-5,权重衰减0.01,学习率调度用ReduceLROnPlateau(效果不好就降半,耐心2个epoch)。batch size根据模型大小调:base用32,small用16,large-v2用4(显存限制)。

贡献

  • 首次用LoRA微调把Whisper改成纯流式模型,不用加额外头(如CTC),不用多阶段解码,轻量高效;
  • 提出因果掩码和稳定Token推理机制,保证低延迟(分块≥40毫秒),WER接近离线Whisper;
  • 顺带解决词级时间戳问题,小分块下精度比Canary高;
  • 复杂度分析和实验证明,效率比现有流式Whisper方法高3~4倍。

局限

  • 每个分块大小要单独训一个模型,不能动态调整分块;
  • 解码器自注意力还没法用KV缓存,大模型推理时还有优化空间;
  • 多语言泛化性不如Ufal-Whisper,因为微调数据不够。

未来方向

  • 随机掩码,让一个模型支持多种分块大小;
  • 优化解码器自注意力缓存,比如让交叉注意力头更时间对齐,减少动态嵌入的影响;
  • 用更多多语言数据微调,提升多语言泛化性。

Read more

Flutter 三方库 shelf_web_socket 的鸿蒙化适配指南 - 实现具备高性能全双工长连接与协议协商能力的端侧服务端架构、支持分布式实时信令与多端协同实战

Flutter 三方库 shelf_web_socket 的鸿蒙化适配指南 - 实现具备高性能全双工长连接与协议协商能力的端侧服务端架构、支持分布式实时信令与多端协同实战

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.ZEEKLOG.net Flutter 三方库 shelf_web_socket 的鸿蒙化适配指南 - 实现具备高性能全双工长连接与协议协商能力的端侧服务端架构、支持分布式实时信令与多端协同实战 前言 在进行 Flutter for OpenHarmony 开发时,当我们的鸿蒙应用需要充当“控制中心”角色(如控制智能家居、开启本地调试服务或实现 P2P 实时对抗脚本时),如何在端侧直接拉起一个支持 WebSocket 协议的高性能微服务端?shelf_web_socket 是针对 shelf 后端框架封装的一款官方级 WebSocket 处理器。本文将探讨如何在鸿蒙端构建极致、透明的长连接交互引擎。 一、原直观解析 / 概念介绍 1.1 基础原理 该库本质上是一个 shelf 处理函数(Handler)

不用AList也能挂载115网盘?飞牛NAS原生WebDAV配置全攻略

飞牛NAS原生WebDAV直连115网盘全流程解析 在私有云存储领域,飞牛NAS凭借其简洁易用的特性赢得了不少用户的青睐。对于拥有115网盘资源的用户来说,如何在不依赖第三方工具的情况下实现高效挂载,成为提升使用体验的关键。本文将深入探讨飞牛NAS原生支持WebDAV协议挂载115网盘的全套方案,从原理分析到实操细节,帮助用户构建更稳定的私有云存储架构。 1. WebDAV协议与飞牛NAS的兼容性解析 WebDAV(Web Distributed Authoring and Versioning)作为一种基于HTTP/HTTPS的扩展协议,早已成为跨平台文件管理的通用标准。飞牛NAS在系统层面原生集成WebDAV服务,这为直接挂载各类云存储提供了技术基础。相比需要通过AList等第三方工具中转的方案,原生WebDAV连接具有明显的优势: * 性能提升:省去中间层处理,传输效率提高30%以上 * 稳定性增强:减少因第三方服务更新导致的兼容性问题 * 资源占用降低:无需额外安装维护应用,节省系统资源 在实际测试中,原生WebDAV挂载的响应速度比AList方案快1.5-2

WebSocket:告别轮询,实现Web实时通信 WebRTC:无需插件,实现浏览器端实时音视频通信

WebSocket:告别轮询,实现Web实时通信 WebRTC:无需插件,实现浏览器端实时音视频通信

目录 一、HTTP 协议的缺点和解决方案 二、如何实现服务器主动发数据 ①:HTTP定时轮询 ②:HTTP长轮询机制 三、WeSocket的由来 四、如何建立websocket链接 五、websocket的实现方式 六、关于WebRTC(Web Real-Time Communication) 七、流程设计 核心原理: 八、WebRTC项目搭建与依赖配置 步骤1: 服务端开发(一)—— 项目搭建与依赖配置 步骤2: 服务端开发(二)—— 核心实体类与消息处理器 步骤3: Netty 服务启动类与 SpringBoot 启动类 步骤 4: Vue前端开发 1.编辑模版template 2.编写核心脚本(script setup),实现交互逻辑 步骤5: 补充 “挂断”

OpenClaw 中 web_search + web_fetch 最佳实践速查表

OpenClaw 中 web_search + web_fetch 最佳实践速查表

OpenClaw 中 web_search + web_fetch 最佳实践速查表 摘要:本文帮助读者明确 OpenClaw 网络搜索工具和不同搜索技能的的职责边界,理解“先搜索、再抓取、后总结”的最佳实践,并能更稳定地在 OpenClaw 中使用 tavily-search 与 web_fetch 完成网络信息搜索任务。主要内容包括:解决 OpenClaw 中 web_search、tavily-search、web_fetch、原生 provider 与扩展 skill 容易混淆的问题、网络搜索能力分层说明、OpenClaw 原生搜索 provider 与 Tavily/Firecrawl 扩展 skill 的区别、标准工作流、提示词模板、