Whisper语音识别 -- 自回归解码分析

news2025/1/13 17:29:45

前言

Whisper 是由 OpenAI 开发的一种先进语音识别系统。它采用深度学习技术,能够高效、准确地将语音转换为文本。Whisper 支持多种语言和口音,并且在处理背景噪音和语音变异方面表现出色。其广泛应用于语音助手、翻译服务、字幕生成等领域,为用户提供了更流畅的语音交互体验。作为一个开源项目,Whisper 鼓励开发者和研究人员进一步优化和创新。
在这里插入图片描述
作者将解码过程整理成 简单的python代码进行讲解

核心思想

whisper解码核心是 基于自回归解码的token游戏 ,换句话说他的参数读取是通过传入token id的形式,即采用大语言模型的prompt范式(whisper的解码器一定程度上也是个大语言模型,虽然语音训练样本token数远不及纯文本token数)
h
图中除了识别结果的框框大多数都是prompt工程, 常用的token id 如图:
在这里插入图片描述

自回归解码

在这里插入图片描述

详细解释放在代码中啦

def main():
    
    """
        解码器须构建Deocder的prompt,序列为【SOT,语种,任务】, 本文中是 model.sot_sequence
        其中SOT:50258
        语种:50332,50309,50333,50335,50273,...
        任务:transcribe 转写 50359, translate 翻译 50358
    """


    """
                加载whisper模型
    """
    encoder_onnx_file = './small-encoder.int8.onnx'
    decoder_onnx_file = './small-decoder.int8.onnx'
    tokenizer_file = './small-tokens.txt'
    model = OnnxModel(encoder_onnx_file, decoder_onnx_file)
    token_table = load_tokenizer(tokenizer_file) # token id to char 


    """
                提取MEL特征
    """
    wav_file = "output.wav"
    mel = compute_features(wav_file)


    """
                计算encoder的K/V编码 
    """
    # 交叉注意力 encoder:K/V, with decoder:Q
    n_layer_cross_k, n_layer_cross_v = model.run_encoder(mel)
    # 自注意力 decoder:K/V, with decoder:Q
    n_layer_self_k_cache, n_layer_self_v_cache = model.get_self_cache()


    """
                检测语种
    """
    lang = model.detect_language(n_layer_cross_k, n_layer_cross_v)
    model.sot_sequence[1] = lang


    """
                任务选择
    """
    # task = model.translate
    task = model.transcribe
    model.sot_sequence[2] = task
    
    
    """
                根据prompt进行首次解码
    """
    tokens = torch.tensor([model.sot_sequence], dtype=torch.int64)
    offset = torch.zeros(1, dtype=torch.int64)
    logits, n_layer_self_k_cache, n_layer_self_v_cache = model.run_decoder(
        tokens=tokens,
        n_layer_self_k_cache=n_layer_self_k_cache,
        n_layer_self_v_cache=n_layer_self_v_cache,
        n_layer_cross_k=n_layer_cross_k,
        n_layer_cross_v=n_layer_cross_v,
        offset=offset,
    )
    offset += len(model.sot_sequence)
    logits = logits[0, -1] # token 声学后验
    model.suppress_tokens(logits, is_initial=True) # 无效token后验抑制



    """
                自回归解码
    """
    max_token_id = logits.argmax(dim=-1) # 选择后验中最大输出的token【贪心解码】
    results = []
    sentence = {'start':0,'end':0,'text':b""} 
    sentences = []
    for i in range(model.n_text_ctx):

        # 打印token属性
        if max_token_id.item() == model.sot:
            print("iter:%8s docode token id:%8s [sot]"%(i,max_token_id.item()))
        elif max_token_id.item() == model.eot:
            print("iter:%8s docode token id:%8s [eot]"%(i,max_token_id.item()))
        elif max_token_id.item() >= model.timestamp_begin:
            print("iter:%8s docode token id:%8s [boundary]"%(i,max_token_id.item()))
        else:
            print("iter:%8s docode token id:%8s [char]"%(i,max_token_id.item()))
        
        # eot 结束
        if max_token_id.item() == model.eot:
            print("Finish !!")
            break

        # 检测到时间戳
        if max_token_id.item()>=model.timestamp_begin:
            timestamp = ((max_token_id.item()-model.timestamp_begin)*model.time_precision)
            # 遇到结束符
            if sentence['text']:
                sentence['end'] = timestamp
                sentence['text'] = sentence['text'].decode().strip()
                print(sentence)
                sentences.append(sentence)
                sentence = {'start':0,'end':0,'text':b""}
            # 遇到开始符
            else:
                sentence['start'] = timestamp
        else:
            decode_token = base64.b64decode(token_table[max_token_id.item()])
            sentence['text'] += decode_token


        results.append(max_token_id.item())
        tokens = torch.tensor([[results[-1]]])
        # deocder 单步解码
        logits, n_layer_self_k_cache, n_layer_self_v_cache = model.run_decoder(
            tokens=tokens,
            n_layer_self_k_cache=n_layer_self_k_cache,
            n_layer_self_v_cache=n_layer_self_v_cache,
            n_layer_cross_k=n_layer_cross_k,
            n_layer_cross_v=n_layer_cross_v,
            offset=offset,
        )
        offset += 1
        logits = logits[0, -1]
        model.suppress_tokens(logits, is_initial=False)
        max_token_id = logits.argmax(dim=-1) # 贪心搜索

没错连时间戳也是token形式~,下面是运行结果感受一下。我们在边界处对句子进行保存
在这里插入图片描述

以上就是whisper解码的基本原理,感兴趣的同学关注走一波

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1819722.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【深度学习基础】激活函数:Tanh、Sigmoid 和 Softmax

激活函数是深度学习模型中不可或缺的一部分,它们赋予神经网络强大的非线性变换能力,使其能够拟合复杂的函数关系。在这篇博文中,我们将探讨三种常见的激活函数:Tanh、Sigmoid 和 Softmax,并提供一些记忆它们的技巧。 1…

4.0 Python 数字类型常用操作

文章目录 1. Numbers数字型1.1 int整型1.2 float浮点型1. 浮点型2. 尾数问题3. 溢出问题4. 高精度计算模块5. 无穷大 1.3 complex复数1.4 bool布尔值 2. 运算符2.1 算术运算符2.2 增值运算符 3. 类型转换3.1 转换为整型3.2 转为浮点型 4. 进制转换4.1 十进制转二进制4.2 十进制…

腾讯云EdgeOne对比普通CDN的分别

EdgeOne架构图 普通CDN架构图 ​​​​​​​ 腾讯云EdgeOne对比普通CDN的不同点 服务范围和集成度 腾讯云EdgeOne是一体化的综合平台,不仅提供内容分发功能,还包括安全防护、性能优化和边缘计算等服务。EdgeOne提供了DDoS防护、WAF(Web应…

洛谷B3642 二叉树的遍历(前序、中序、后序)

题目描述 有一个 𝑛(𝑛≤10^6) 个结点的二叉树。给出每个结点的两个子结点编号(均不超过 𝑛),建立一棵二叉树(根节点的编号为 1),如果是叶子结点,则输入 0。…

Golang的channel

目录 基本使用 channel 数据结构 阻塞的协程队列 协程节点 构建 channel 写流程 读流程 非阻塞与阻塞 closechan(关闭) 基本使用 创建无缓存 channel c : make(chan int) //创建无缓冲的通道 cc : make(chan int,0) //创建无缓冲的通道 c 创建有缓存 channel c : m…

(三十九)Vue之集中式的状态管理机制Vuex

目录 概念vuex的核心概念State(状态)Getters(获取器)Mutations(突变)Actions(动作) 搭建vuex环境基本使用getters的使用 上一篇:(三十八)Vue之插槽…

JDK8新特性【接口新特征、lambda语法、Supplier、Consumer、Function、Predicate】

目录 一、关于接口的新特性1.1 jdk1.8之前的接口重要特性1.2 JDK8以后代码演示 1.3 总结通过代码演示发现作用 二、Lambda表达式[重点]2.1 将匿名内部类写法改写为lambda写法2.2 语法特点能够写成lambda形式的的前提语法特征代码演示深入理解lambda 2.3 总结 三、函数式接口3.1…

社区疫情管理系统

社区疫情管理系统 java web源码 增删改查 适合新手有导入视频 java项目 ssm疫情防控登记管理社区疫情管理源码jsp项目 技术 ssm 源码➕数据库文件➕eclipse导入视频

基于C#开发web网页管理系统模板流程-打包发布

点击返回目录-> 基于C#开发web网页管理系统模板流程-总集篇-CSDN博客 前言 本系列中,作为开发者我们通过ASP.net Web模板设计网页,网页的任何设计、源代码都是直接可见的,在实际应用开发中,显然这些都是商业、公司机密 通过打包…

Vue基础知识:异步DOM更新是什么?$nextTick是什么?到底应该如何使用。什么是同步?什么是异步?

要先了解异步dom更新是什么就必须先了解,什么是同步?什么是异步? 1.什么是同步?什么是异步? 同步(Synchronous): 同步操作是按照代码的顺序执行的,每个操作都必须等待上…

SAP OB52 财务账期月结月底月初开关

公告:周一至周五每日一更,周六日存稿,请您点“关注”和“在看”,后续推送的时候不至于看不到每日更新内容,感谢。 这是一条刮刮乐,按住全部选中:点关注的人最帅最美,欢迎&#xff1…

vue2+echarts:echarts在dialog弹框中不显示的解决方案

重点是open方法里使用$nextTick拿到最新的dom&#xff0c;在里面加载echarts //html <el-button click.stop"getIfStorage"></el-button><el-dialog title"图表数据" :visible.sync"ifStorageShowOpen" open"open()" …

嵌入式系统中判断大小端的方法与实现

第一&#xff1a;大小端基本分析 程序判断计算机是大端的还是小端的&#xff0c;判断的思路是确定一个多字节的值(下面使用的是4字节的整数)&#xff0c;将其写入内存(即赋值给一个变量)&#xff0c;然后用指针取其首地址所对应的字节(即低地址的一个字节)&#xff0c;判断该字…

Photoshop中颜色与色调的调整

Photoshop中颜色与色调的调整 Photoshop中的颜色模式RGB模式灰度模式位图模式索引模式CMYK模式Lab模式 Photoshop中的颜色/色调调整命令颜色/色调调整命令的分类亮度/对比度调整命令色阶命令曲线命令曝光度命令自然饱和度命令色相/饱和度命令色彩平衡命令照片滤镜调整命令通道混…

[leetcode]swap-nodes-in-pairs

. - 力扣&#xff08;LeetCode&#xff09; class Solution { public:ListNode* swapPairs(ListNode* head) {ListNode* dummyHead new ListNode(0);dummyHead->next head;ListNode* temp dummyHead;while (temp->next ! nullptr && temp->next->next !…

单链表实现:从理论到代码

✨✨欢迎&#x1f44d;&#x1f44d;点赞☕️☕️收藏✍✍评论 个人主页&#xff1a;秋邱博客 所属栏目&#xff1a;C语言&#xff0c;数据结构 ​ 前言 前面学习了顺序表&#xff0c;顺序表优点&#xff1a; 可以随机访问元素&#xff0c;通过索引能快速定位到元素。存储密…

Python界面编辑器Tkinter布局助手 使用体验

一、发现 我今天在网上搜关于Python Tkinter方面的信息时&#xff0c;发现了Python界面编辑器 Tkinter布局助手 的使用说明。 https://blog.csdn.net/weixin_52777652/article/details/135291731?spm1001.2014.3001.5506 这个编辑器是个开源的项目&#xff0c;个人用户可以…

Python学习笔记7:入门知识(七)

前言 之前说过我更换了新的学习路线&#xff0c;现在是根据官方文档和书籍Python crash course来进行学习的&#xff0c;在目前的学习中&#xff0c;对于之前的知识有一些遗漏&#xff0c;这里进行补充。 学习资料有两个&#xff0c;书籍中文版PDF&#xff0c;关注我私信发送…

Lua实现自定义函数面向对象编程

本文目录 1、引言2、原理3、实例4、层析验证 文章对应视频教程&#xff1a; 暂无&#xff0c;可以关注我的B站账号等待更新。 点击图片或链接访问我的B站主页~~~ 1、引言 在现代软件开发中&#xff0c;面向对象编程&#xff08;OOP&#xff09;已经成为一种广泛使用的编程范式…

nodejs 某音douyin网页端搜索接口及x_bogus、a_bogus(包含完整源码)(2024-06-13)

前言 x_bogus或a_bogus算法大概是对数据、ua、时间戳、浏览器的几个指纹进行计算&#xff0c;拿到一个110位大数组&#xff0c;然后转字符&#xff0c;在头部再添加十二位随机字符&#xff0c;再进行魔改的base64加密。 问&#xff1a;抖音的x_bogus、a_bogus值有什么用&#x…