端到端语音识别案例

news2025/4/2 17:11:34

《DeepSeek大模型高性能核心技术与多模态融合开发(人工智能技术丛书)》(王晓华)【摘要 书评 试读】- 京东图书

语音识别这一技术正如其名,是通过精密地解析说话人的语音来识别并准确转写出其所说的内容。它不仅仅是一个简单的转录过程,更是一项融合了声学、语言学、计算机科学等多个学科领域精华的高科技产物。在现代社会中,随着人工智能技术的飞速发展,语音识别技术正日益显现出其巨大的应用潜力和广阔的市场前景。

无论是在智能手机上的语音助手,还是在家庭中的智能音箱,甚至是在车载系统中,语音识别技术都扮演着举足轻重的角色。它能够将人们的口头语言迅速转化为文字信息,从而极大地提高了交互的便捷性和效率。不仅如此,语音识别还在无障碍沟通、语音搜索、自动化客服等众多领域发挥着不可或缺的作用,为人们的生活和工作带来了前所未有的便利。

11.3.1  全中文音频数据集的准备

我们将使用全中文的音频信号进行转换,这里首选使用aidatatang_200zh数据集作为我们的音频转换目标。aidatatang_200zh是一个用于语音识别的数据集,包含30万条口语化句子,由6408人录制,涵盖不同年龄段和34个省级行政区域。录音环境为安静的室内,采用16kHz 16bit的wav单声道格式,总大小为18G。该数据集适用于语音识别、机器翻译和声纹识别等场景,标注准确率不低于98%。

Aidatatang_200zh是一套开放式中文普通话电话语音库。语料库长达200小时,由Android系统手机(16kHz,16位)和iOS系统手机(16kHz,16位)记录。邀请来自中国不同重点区域的600名演讲者参加录音,录音是在安静的室内环境或环境中进行,其中包含不影响语音识别的背景噪音。参与者的性别和年龄均匀分布。语料库的语言材料是设计为音素均衡的口语句子。每个句子的手动转录准确率大于98%。

读者很容易在互联网上搜索到这个数据集的相关内容,下载解压后的单个文件如图11-8所示。

图11-8  载解压后的单个文件示例

我们说过,对于第一步单文本生成来说,并不需要对语音数据进行批匹配,因此在这一步进行数据读取时仅仅读取txt文本文件中的数据即可。

通过解压后的文件可以看到,Aidatatang_200zh提供了600个文件夹,每个文件夹中存放若干个文本与语音对应的文件,其通过文件名进行一一对应。

首先,第一步是读取所有的文件,代码如下所示。

import os
# 这个是列出所有目录下文件夹的函数
def list_folders(path):
    """
    列出指定路径下的所有文件夹名
    """
    folders = []
    for root, dirs, files in os.walk(path):
        for dir in dirs:
            folders.append(os.path.join(root, dir))
    return folders
from torch.utils.data import DataLoader, Dataset

def list_files(path):
    files = []
    for item in os.listdir(path):
        file = os.path.join(path, item)
        if os.path.isfile(file):
            files.append(file)
    return files

#这里作者使用的是自定义的数据集存放位置,读者可以改成自己所对应的语音数据集位置
dataset_path = "D:/语音识别_数据集/aidatatang_200zh/dataset"

folders = list_folders(dataset_path)	#获取了所有文件夹

for folder in tqdm(folders):
    _files = list_files(folder)		
    for _file in _files:
        if _file.endswith("txt"):
            with open(_file,encoding="utf-8") as f:
                line = f.readline().strip()

其中folders是Aidatatang_200zh目录下所有文件夹,list_folders的作用是对每个文件夹进行重新读取。

接下来,一个非常重要的内容就是建立相应的字库文件,这里我们可以在读取全部文本数据之后使用set结构对每个字符进行存储。

vocab = set()
……
for folder in tqdm(folders):
    _files = list_files(folder)
    for _file in _files:
        if _file.endswith("txt"):
            with open(_file,encoding="utf-8") as f:
                line = f.readline().strip()
                for char in line:
                    vocab.add(char)    
vocab = list(sorted(vocab))

11.3.2  音频特征的提取与融合

梅尔频谱作为音频提取的主要方法,其作用在于对提取的音频信号进行高效的转换与分析。通过模拟人类听觉系统的特性,梅尔频谱能够将复杂的音频数据转化为易于处理和解读的频域表示,从而揭示出音频信号中的关键特征和潜在结构。这种转换不仅有助于简化音频处理流程,还能提高特征提取的准确性和效率,为后续的音频识别、分类和合成等任务奠定坚实基础。因此,梅尔频谱在音频处理领域具有广泛的应用价值,是研究人员和工程师们不可或缺的工具之一。

梅尔频谱的独特之处在于其基于梅尔刻度的频率划分方式。与传统的线性频率刻度相比,梅尔刻度更符合人类听觉系统对频率的感知特性。在梅尔频谱中,低频段的分辨率较高,能够捕捉到更多的细节信息,而高频段的分辨率则相对较低,以适应人类对高频声音的不敏感性。这种特性使得梅尔频谱在处理具有丰富低频成分的音频信号时表现出色,如语音和音乐等。

此外,梅尔频谱还具有良好的抗噪性能和稳定性。在音频信号受到噪声干扰或质量下降时,梅尔频谱仍能有效地提取出有用的特征信息,保持较高的识别准确率。这使得梅尔频谱在实际应用中具有更强的鲁棒性和可靠性,能够满足各种复杂场景下的音频处理需求。

基于librosa库完成的特征信号提取,其代码如下所示。

# 计算梅尔频率图
def compute_melspec(y, sr, n_mels, fmin, fmax):
    """
    :param y:传入的音频序列,每帧的采样
    :param sr: 采样率
    :param n_mels: 梅尔滤波器的频率倒谱系数
    :param fmin: 短时傅里叶变换(STFT)的分析范围 min
    :param fmax: 短时傅里叶变换(STFT)的分析范围 max
    :return:
    """
    # 计算Mel频谱图的函数
    melspec = lb.feature.melspectrogram(y=y, sr=sr, n_mels=n_mels, fmin=fmin, fmax=fmax)  # (128, 1024) 这个是输出一个声音的频谱矩阵
    # 是Python中用于将音频信号的功率值转换为分贝(dB)值的函数
    melspec = lb.power_to_db(melspec).astype(np.float32)

    # 计算MFCC
    mfccs = lb.feature.mfcc(S=melspec)

    return melspec,mfccs

从上面代码可以看到,我们通过梅尔频谱获取到了梅尔特征以及梅尔频率倒谱系数,这是从不同的角度对语音特征进行提取。

接下来就是我们希望将提取到的特征进行融合,具体融合的方式可以在数据特征输入到模型之前完成,即在特征提取后,经过一个正则化处理使用在特定维度拼接的方式完成,代码如下所示。

# 对输入的频谱矩阵进行正则化处理
def mono_to_color(X, eps=1e-6, mean=None, std=None):
    mean = mean or X.mean()
    std = std or X.std()
    X = (X - mean) / (std + eps)
    _min, _max = X.min(), X.max()
    if (_max - _min) > eps:
        V = np.clip(X, _min, _max)
        V = 255. * (V - _min) / (_max - _min)
        V = V.astype(np.uint8)
    else:
        V = np.zeros_like(X, dtype=np.uint8)
    return V
……
def audio_to_image(audio, sr, n_mels, fmin, fmax):
    melspec,mfccs = compute_melspec(audio, sr, n_mels, fmin, fmax)    #(128, 688)
    melspec = mono_to_color(melspec)
    mfccs = mono_to_color(mfccs)
    spec = np.concatenate((melspec, mfccs), axis=0)
    return spec

这里需要注意,我们获取到的音频特征,由于其采样的方式不同,其数值大小也千差万别。因此,在进行concatenate拼接之前,需要进行正则化处理。

获取数据的完整代码如下:

from tqdm import tqdm
import os

# 这个是列出所有目录下文件夹的函数
def list_folders(path):
    """
    列出指定路径下的所有文件夹名
    """
    folders = []
    for root, dirs, files in os.walk(path):
        for dir in dirs:
            folders.append(os.path.join(root, dir))
    return folders
from torch.utils.data import DataLoader, Dataset

def list_files(path):
    files = []
    for item in os.listdir(path):
        file = os.path.join(path, item)
        if os.path.isfile(file):
            files.append(file)
    return files

dataset_path = "D:/语音数据库/aidatatang_200zh"
#dataset_path = "../dataset/aidatatang_200zh/"
folders = list_folders(dataset_path)
folders = folders[:5]

max_length = 18
sampling_rate = 16000
wav_max_length = 22#这里的计数单位是秒
context_list = []
token_list = []
wav_image_list = []

for folder in tqdm(folders):
    _files = list_files(folder)
    for _file in _files:
        if _file.endswith("txt"):#_file = "D:/aidatatang_200zh/G0084/T0055G0084S0496.txt"
            with open(_file,encoding="utf-8") as f:
                line = f.readline().strip()
                if len(line) <= max_length:

                    wav_name = _file.replace("txt", "wav")
                    audio, orig_sr = sf.read(wav_name, dtype="float32")  # 这里均值是 1308338,   0.8中位数是1730351,所以我采用了中位数的部分
                    audio = sound_untils.crop_or_pad(audio, length=sampling_rate * wav_max_length)  # 我的想法是把audio做一个整体输入,在这里就所有的都做了输入
                    wav_image = sound_untils.audio_to_image(audio, sampling_rate, 128, 0, sampling_rate//2) #输出的是(128, 688)

                    wav_image_list.append(wav_image)
                    #token_list.append(token)

np.save("./saver/wav_image_list.npy",wav_image_list)

这里为了加速模型的训练,我们首先读取了音频,并创建了融合后的音频特征,将其进行存储。为了将数据输入到模型中,还需要实现torch.utils.data.Dataset数据类。代码如下:

class TextSamplerDataset(torch.utils.data.Dataset):
    def __init__(self, token_list = token_list,wav_image_list = wav_image_list):
        super().__init__()
        self.token_list = token_list
        self.wav_image_list = wav_image_list

    def __getitem__(self, index):
        token = self.token_list[index]
        token = torch.tensor(token).long()
        token_inp, token_tgt = token[:-1], token[1:]

        wav_image = self.wav_image_list[index]#sound_untils.audio_to_image(audio, sampling_rate, 128, 0, sampling_rate//2) #输出的是(128, 688)
        wav_image = torch.tensor(wav_image,dtype=torch.float).float()

        return token_inp,wav_image,token_tgt

    def __len__(self):
        return len(self.token_list)

11.3.3  基于生成模型的端到端语音识别任务

我们需要完成的是基于端到端的语音识别任务,特别是使用生成模型将输入的语音特征转化为文本内容,遇到的第一个问题将会是如何将可变的生成文本与语音特征信号进行融合。

首先,我们采用将语音特征压缩特性的方式进行融合,即将多维的语音特征压缩成一维后与输入的可变长度的文本信息相加后进行处理,代码如下:

class ReshapeImageLayer(torch.nn.Module):
    def __init__(self):
        super().__init__()
        self.reshape_layer = torch.nn.Linear(688,model_cfg.dim * 2)

        self.norm = layers.LayerNorm(model_cfg.dim * 2)
        self.act = layers.SwiGLU()

    def forward(self,image):
        image = self.reshape_layer(image)
        image = self.norm(image)
        image = self.act(image)

        image = torch.permute(image,[0,2,1])
        image = torch.nn.AdaptiveAvgPool1d(1)(image)
        image = torch.permute(image,[0,2,1])

        return image

上面代码创建了一个简单的卷积层对信号进行提取,之后通过了AvgPool对特征进行压缩,在调整维度后进行返回。

对于生成模型来说,其核心就是采用注意力机制建立跨区域关注。因此,我们可以在创建因果掩码后完成生成模型的设计。代码如下:

class GLMSimple(torch.nn.Module):
    def __init__(self,dim = model_cfg.dim,num_tokens = model_cfg.num_tokens,device = all_config.device):
        super().__init__()
        self.num_tokens = num_tokens
        self.causal = model_cfg.causal
        self.device = device

        self.token_emb = torch.nn.Embedding(num_tokens,dim)
        self.layers = torch.nn.ModuleList([])

        for _ in range(model_cfg.depth):
            block = GLMBlock()
            self.layers.append(block)

        self.to_logits = torch.nn.Linear(dim, num_tokens, bias=False)
        self.reshape_layer = ReshapeImageLayer()
        self.merge_norm = layers.LayerNorm(dim)

    def forward(self,x,image = None):
        if not self.causal:
            mask = x > 0
            x = x.masked_fill(~mask, 0)
        else:
            mask = None
        x = self.token_emb(x)

        image = self.reshape_layer(image)

        for layer in self.layers:
            x += image
            x = self.merge_norm(x)
            x = x + layer(x, mask = mask)

        x = torch.nn.Dropout(0.1)(x)
        logits = self.to_logits(x)

        return logits

在上面代码中,GLMBlock是我们实现的经典的因果注意力模型,目的是将向量化处理后的可变文本特征与一维的语音特征相加后,输入到因果注意力模型进行计算。

为了配合因果注意力机制的输入,对于文本的最终输入,我们也可以采用比较巧妙的设计,代码如下:

@torch.no_grad()
def generate(
    self, seq_len, image=None, temperature=1., filter_logits_fn=top_k,
    filter_thres=0.99, pad_value=0., eos_token=2,                       return_seq_without_prompt=True, #这个的作用是在下面随机输出的时候,把全部的字符输出
    ):

    # 这里是我后加上去的,输入进来可以是list
    image = torch.tensor(image,dtype=torch.float).float()
    image = torch.unsqueeze(image,dim=0)
    image = image.to(self.device)

    prompt = torch.tensor([1])
    prompt = prompt.to(self.device)

    prompt, leading_dims = pack([prompt], '* n')

    n, out = prompt.shape[-1], prompt.clone()

    #wrapper_fn = identity if not use_tqdm else tqdm
    sample_num_times = max(1, seq_len - prompt.shape[-1])

    for _ in (range(sample_num_times)):
        logits = self.forward(out,image)
        logits = logits[:, -1]

        sample = gumbel_sample_once(logits, temperature=temperature, dim=-1)

        out, _ = pack([out, sample], 'b *')
        if exists(eos_token):
            is_eos_tokens = (out == eos_token)

            if is_eos_tokens.any(dim=-1).all():
                break

    out, = unpack(out, leading_dims, '* n')
    if not return_seq_without_prompt:
        return out

    return out[..., n:]

上面代码中,我们采用generate 函数来产生输入的文本内容,随后通过逐个添加字符的方式逐步扩充所给信息,进而利用下一个字符的预测来完成最终结果的构建。

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

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

相关文章

【软件系统架构】微服务架构

一、引言 随着互联网技术的快速发展&#xff0c;传统的单体应用架构在面对复杂业务需求时逐渐暴露出诸多问题&#xff0c;如开发效率低、部署困难、扩展性差等。为了解决这些问题&#xff0c;微服务架构应运而生。本文将详细介绍微服务架构的定义、发展历史、特点、细分类型、优…

Linux内核设计——(一)进程管理

目录 一、进程及线程简介 二、进程描述符 2.1 进程描述符简介 2.2 分配进程描述符 2.3 进程标识值 2.4 进程状态 2.5 进程上下文 三、进程创建 3.1 写时拷贝 3.2 fork()和vfork() 四、线程 4.1 Linux线程实现 4.2 内核线程 五、进程终结 5.1 删除进程描述符 5.…

22 安装第三方包

一、什么是第三方包 在 Python 的世界里&#xff0c;包就像是一个个功能强大的工具箱&#xff0c;它将多个 Python 模块收纳其中&#xff0c;而每个模块又蕴含着丰富多样的具体功能。可以说&#xff0c;一个包就是一系列同类功能的集合体&#xff0c;它们就像紧密协作的团队&a…

oracle 常用函数的应用

在使用开发中会经常遇到数据类型转换、显示系统时间等情况&#xff0c;需要使用函数来实现。通过函数来实现业务需求会非常的省事便捷&#xff0c;函数可以用在适当的dml语句和查询语句中。 Oracle 数据库中主要使用两种类型的函数&#xff1a; (1)单行函数&#xff1a;对每一个…

“上云入端” 浪潮云剑指组织智能化落地“最后一公里”

进入2025年&#xff0c;行业智能体正在成为数实融合的核心路径。2025年初DeepSeek开源大模型的横空出世&#xff0c;通过算法优化与架构创新&#xff0c;显著降低算力需求与部署成本&#xff0c;推动大模型向端侧和边缘侧延伸。其开源策略打破技术垄断&#xff0c;结合边缘计算…

CentOS 7 如何挂载ntfs的移动硬盘

CentOS 7 如何挂载ntfs的移动硬盘 前言一、查看硬盘并尝试挂载(提示无法挂载)二、yum安装epel-release提示yum被锁定三、强行终止yum的进程四、yum安装epel-release完成五、yum安装ntfs-3g六、此时可正常挂载NTFS硬盘 前言 CentOS 7默认情况下是不支持NTFS的文件系统&#xff…

pytorch+maskRcnn框架训练自己的模型以及模型导出ONXX格式供C++部署推理

背景 maskrcnn用作实例分割时&#xff0c;可以较为精准的定位目标物体&#xff0c;相较于yolo只能定位物体的矩形框而言&#xff0c;优势更大。虽然yolo的计算速度更快。 直接开始从0到1使用maskrCNN训练自己的模型并并导出给C部署&#xff08;亲测可用&#xff09; 数据标注…

①EtherCAT/Ethernet/IP/Profinet/ModbusTCP协议互转工业串口网关

型号 协议转换通信网关 EtherCAT 转 Modbus TCP MS-GW15 概述 MS-GW15 是 EtherCAT 和 Modbus TCP 协议转换网关&#xff0c;为用户提供一种 PLC 扩展的集成解决方案&#xff0c;可以轻松容易将 Modbus TCP 网络接入 EtherCAT 网络 中&#xff0c;方便扩展&#xff0c;不受限…

《Oracle服务进程精准管控指南:23c/11g双版本内存优化实战》 ——附自动化脚本开发全攻略

正在学习或者是使用 Oracle 数据库的小伙伴&#xff0c;是不是对于那个一直启动且及其占用内存的后台进程感到烦躁呢&#xff1f;而且即使是手动去开关也显得即为麻烦&#xff0c;所以基于我之前所学习到的方法&#xff0c;我在此重新整理&#xff0c;让大家动动手指就能完成开…

Java单列集合[Collection]

目录 1.Collection单列集合 1.1单列集合各集合特点 1.2、Collection集合 1.2.1、Collection方法 1.2.2、Collection遍历方式 1.2.2.1、迭代器遍历集合 1.2.2.2、增强for遍历集合 1.2.2.3、forEach遍历集合&#xff08;JDK8之后&#xff09; 1.2.2.4、遍历案例 1.3、Li…

如何在ONLYOFFICE插件中添加自定义AI提供商:以通义千问和Kimi为例

随着 ONLYOFFICE AI 插件的发布&#xff0c;我们极大地提升了编辑器的默认功能。在ONLYOFFICE&#xff0c;我们致力于提供强大且灵活的解决方案&#xff0c;以满足您的特定需求。其中一项便是能够在 AI 插件中添加自定义提供商。在这篇文章中&#xff0c;我们将展示如何将通义千…

Spark,配置hadoop集群1

配置运行任务的历史服务器 1.配置mapred-site.xml 在hadoop的安装目录下&#xff0c;打开mapred-site.xml&#xff0c;并在该文件里面增加如下两条配置。 eg我的是在hadoop199上 <!-- 历史服务器端地址 --> <property><name>mapreduce.jobhistory.address…

FPGA实现4K MIPI视频解码H265压缩网络推流输出,基于IMX317+VCU架构,支持4K60帧,提供工程源码和技术支持

目录 1、前言工程概述免责声明 2、相关方案推荐我已有的所有工程源码总目录----方便你快速找到自己喜欢的项目我这里已有的 MIPI 编解码方案我这里已有的视频图像编解码方案 3、详细设计方案设计框图FPGA开发板IMX317摄像头MIPI D-PHYMIPI CSI-2 RX Subsystem图像预处理Sensor …

【Linux】网络概念

目录 网络模型 OSI七层模型 TCP/IP五层(或四层)模型 网络传输 网络传输基本流程 封装与分用 以太网通信&#xff08;局域网传输&#xff09; 跨网络传输 网络模型 OSI七层模型 TCP/IP五层(或四层)模型 网络层和传输层就是操作系统的一部分 网络传输 网络传输基本流程…

【模拟CMOS集成电路设计】电荷泵(Charge bump)设计与仿真(示例:栅极开关CP+轨到轨输入运放+基于运放CP)

【模拟CMOS集成电路设计】电荷泵&#xff08;Charge bump&#xff09;设计与仿真 0前言1电荷泵1.1 PFD/CP/电容器级联1.2 PFD/CP/电容传递函数 2基本电荷泵(CP)结构2.1“漏极开关”结构2.2“源极开关”结构2.3“栅极开关”结构 3 CP的设计与仿真13.1 P/N电流源失配仿真3.2 电荷…

Kafka消息丢失全解析!原因、预防与解决方案

作为一名高并发系统开发工程师&#xff0c;在使用消息中间件的过程中&#xff0c;无法避免遇到系统中消息丢失的问题&#xff0c;而Kafka作为主流的消息队列系统&#xff0c;消息丢失问题尤为常见。 在这篇文章中&#xff0c;将深入浅出地分析Kafka消息丢失的各种情况&#xf…

VS Code 云服务器远程开发完整指南

VS Code Ubuntu 云服务器远程开发完整指南 远程开发是现代开发者的标配之一&#xff0c;特别是在使用云服务器&#xff08;如 Ubuntu&#xff09;进行部署、测试或大项目开发时&#xff0c;利用 VS Code 的 Remote-SSH 插件&#xff0c;可以像本地一样顺滑操作远程服务器。本…

【Rtklib入门指南】4. 使用RTKLIB进行载波相位差分定位(RTK)

RTK RTK&#xff08;Real-Time Kinematic&#xff0c;实时动态&#xff09;定位技术是一种高精度的卫星导航技术。相比传统的GPS定位技术&#xff0c;RTK能够在厘米级别的精度范围内提供定位结果。这使得RTK技术在无人机、自动驾驶、工程测绘、农业机械自动化等领域具有广泛应用…

【SECS】初识SECS协议

【SECS】初识SECS协议 基本知识流和功能函数数量官方文件中缩写标注正常是不是都是主机向设备端?对数据信息中第1字节第1-2位官网介绍 S1F1双向指令说明测试H发起端E发起端 参考资料 基本知识 SECS&#xff08;SEMI Equipment Communications Standard&#xff09;即半导体设…

【C++项目】从零实现RPC框架「三」:项⽬抽象层实现

🌈 个人主页:Zfox_ 🔥 系列专栏:C++从入门到精通 目录 一:🔥 常⽤的零碎功能接⼝类实现🦋 简单⽇志宏实现🦋 Json 序列化/反序列化🦋 UUID ⽣成二:🔥 项⽬消息类型字段信息定义 🦋 请求字段宏定义🦋 消息类型定义🦋 响应码类型定义🦋 RPC 请求类型定…