【AI】PaddlePaddle实现自动语音识别
文章目录
- 文档背景
- 安装环境
- Python版本
- pip环境
- 安装模型需要的环境
- 项目目录结构
- 数据准备
- 生成数据字典
- 数据预处理
- 训练模型
- 创建模型
- 构建模型的目的
- 模型黑盒在模型中充当什么角色
- 解码方法
- 总结
文档背景
学习AI的过程中,难免会出现各种各样的问题。比如,什么样的模型需要什么样的环境。依赖与Python版本不兼容时怎么办。数据集如何自定义。构建模型的目的是什么。原本模型黑盒是如何训练并得以优化的,等等等等。基于这些问题,利用入门级PP ASR简单的语音模型,做一个详细的记录。
安装环境
Python版本
pip环境
pip install --upgrade pip
安装模型需要的环境
!pip install -i https://pypi.mirrors.ustc.edu.cn/simple/ numpy scipy tqdm pytest-runner librosa python-Levenshtein==0.12.2 visualdl --upgrade --user
!pip install -i https://pypi.mirrors.ustc.edu.cn/simple/ SoundFile --upgrade --user
# 执行create_mainfest.py依赖下载
!pip uninstall -y numba
!pip install -i https://pypi.mirrors.ustc.edu.cn/simple/ --upgrade numba
!pip uninstall -y resampy
!pip install -i https://pypi.mirrors.ustc.edu.cn/simple/ --upgrade resampy
!pip uninstall -y librosa
!pip install -i https://pypi.mirrors.ustc.edu.cn/simple/ --upgrade librosa
项目目录结构
数据准备
- 可以直接使用官方提供的训练数据集。
- 也可以直接使用自定义数据集
生成数据字典
当数据集构建完成后。将所有数据进行分词。因为模型不是以音频区分的。而是将音频处理成序列的形式进行排列,最后输出时,根据生成的字典进行查找,来返回最终的结果。所以生成数据字典的目的,既是固定构建音频生成序列的规则,也是语音识别的依赖。
数据预处理
音频数据预处理通常包括以下步骤:
读入音频文件:使用库如 librosa、pydub 等加载音频文件,得到音频数据和采样率。
数据截取:按照需要的长度截取音频数据。例如,将音频数据切割成固定长度的语音片段。
数据增强:数据增强指对原始数据进行变换、扩充、合成等处理,以提高模型的鲁棒性和泛化能力。例如,随机速度变化、加入噪音、改变音高等方式对数据进行增强。
特征提取:通过对音频数据进行短时傅里叶变换(STFT)、梅尔频率倒谱系数(MFCC)等方式提取音频特征。特征提取是将高维的音频数据转换为低维度的特征表示。
数据标准化:标准化是为了使不同样本的特征具有可比性,减少由于特征维度大小造成的模型不稳定或低效问题。例如,将特征值归一化到 [0,1] 范围内、均值为 0,标准差为 1 等方式进行标准化。
数据划分:将数据划分为训练集、验证集和测试集,可使用库如 sklearn 等将样本划分为不同集合。
数据存储:将处理好的数据按照一定格式保存,以便在模型训练和测试时使用。
下面是数据预处理工具类 data.py
注意! 需要前面的环境才可以使用
import json
import waveimport librosa
import numpy as np
import soundfile
from paddle.io import Dataset# 加载二进制音频文件,转成短时傅里叶变换
def load_audio_stft(wav_path, mean=None, std=None):with wave.open(wav_path) as wav:wav = np.frombuffer(wav.readframes(wav.getnframes()), dtype="int16").astype("float32")stft = librosa.stft(wav, n_fft=255, hop_length=160, win_length=200, window="hamming")spec, phase = librosa.magphase(stft)spec = np.log1p(spec)if mean is not None and std is not None:spec = (spec - mean) / stdreturn spec# 读取音频文件转成梅尔频率倒谱系数(MFCCs)
def load_audio_mfcc(wav_path, mean=None, std=None):wav, sr = librosa.load(wav_path, sr=16000)mfccs = librosa.feature.mfcc(y=wav, sr=sr, n_mfcc=128, n_fft=512, hop_length=128).astype("float32")if mean is not None and std is not None:mfccs = (mfccs - mean) / stdreturn mfccs# 改变音频采样率为16000Hz
def change_rate(audio_path):data, sr = soundfile.read(audio_path)if sr != 16000:# librosa最新版本在调用参数时必须明确指定参数名,避免出现参数传递错误data = librosa.resample(y=data, orig_sr=sr, target_sr=16000)soundfile.write(audio_path, data, samplerate=16000)# 音频数据加载器
class PPASRDataset(Dataset):def __init__(self, data_list, dict_path, mean=None, std=None, min_duration=0, max_duration=-1):super(PPASRDataset, self).__init__()self.mean = meanself.std = std# 获取数据列表with open(data_list, 'r', encoding='utf-8') as f:lines = f.readlines()self.data_list = []for line in lines:line = json.loads(line)# 跳过超出长度限制的音频if line["duration"] < min_duration:continueif max_duration != -1 and line["duration"] > max_duration:continueself.data_list.append([line["audio_path"], line["text"]])# 加载数据字典with open(dict_path, 'r', encoding='utf-8') as f:labels = eval(f.read())self.vocabulary = dict([(labels[i], i) for i in range(len(labels))])def __getitem__(self, idx):# 分割音频路径和标签wav_path, transcript = self.data_list[idx]# 读取音频并转换为梅尔频率倒谱系数(MFCCs)mfccs = load_audio_mfcc(wav_path, self.mean, self.std)# 将字符标签转换为int数据transcript = list(filter(None, [self.vocabulary.get(x) for x in transcript]))transcript = np.array(transcript, dtype='int32')return mfccs, transcriptdef __len__(self):return len(self.data_list)# 对一个batch的数据处理
def collate_fn(batch):# 找出音频长度最长的batch = sorted(batch, key=lambda sample: sample[0].shape[1], reverse=True)freq_size = batch[0][0].shape[0]max_audio_length = batch[0][0].shape[1]batch_size = len(batch)# 找出标签最长的batch_temp = sorted(batch, key=lambda sample: len(sample[1]), reverse=True)max_label_length = len(batch_temp[0][1])# 以最大的长度创建0张量inputs = np.zeros((batch_size, freq_size, max_audio_length), dtype='float32')labels = np.zeros((batch_size, max_label_length), dtype='int32')input_lens = []label_lens = []for x in range(batch_size):sample = batch[x]tensor = sample[0]target = sample[1]seq_length = tensor.shape[1]label_length = target.shape[0]# 将数据插入都0张量中,实现了paddinginputs[x, :, :seq_length] = tensor[:, :]labels[x, :label_length] = target[:]input_lens.append(seq_length)label_lens.append(len(target))input_lens = np.array(input_lens, dtype='int64')label_lens = np.array(label_lens, dtype='int64')return inputs, labels, input_lens, label_lens
训练模型
创建模型
构建模型的目的
PPASR模型是一个只使用卷积层的模型,并没有使用更加复杂的RNN模型,以下就是使用PaddlePaddle实现的一个语音识别模型。使用动态图自定义网络模型非常简单。
在语音识别的过程中,构建模型的目的是为了将语音信号转化为文字(文本)信息。这个过程可以分为两个主要步骤:语音特征提取和声学模型训练。
在语音特征提取过程中,将来自音频数据的语音信号转化为机器学习算法能够处理的形式,例如用梅尔频率倒谱系数(Mel Frequency Cepstral Coefficients,MFCCs)等特征来描述语音信号。
在声学模型训练过程中,使用大量的带有标注的语音数据来训练语音识别模型。常用的模型包括隐马尔可夫模型(Hidden Markov Model,HMM)、深度学习模型等。机器学习算法通过学习这些带有标注的语音样本的特征,来建立模型,以将音频信号转化为文字(文本)信息。
因此,构建模型的目的是为了使机器能够对人类语音进行自动识别,实现语音识别应用,例如语音助手、语音输入等。
模型黑盒在模型中充当什么角色
原本的黑盒指的是模型的内部结构和工作流程难以被理解和解释的情况。在语音识别的过程中,原本的黑盒主要存在于模型内部,由于模型参数众多且相互作用复杂,难以解释和理解模型内部的具体工作流程。
在构建的模型中,原本的黑盒逐渐被打破,因为随着机器学习技术的发展,一些新的方法和技术被引入来提高模型的解释性和可理解性。例如,利用深度神经网络模型中的可视化方法来可视化模型的神经网络结构,可以更好地理解模型的工作原理。同时,通过对模型进行逐层解析,可以逐步了解模型对不同特征以及语音信号的处理方式。
总之,在构建的模型中,原本的黑盒逐渐被打破,通过不断的优化和改进,模型的解释性和可理解性得到了提高,使得我们更加深入地理解语音识别的工作原理。
所以,原本的模型黑盒,我们就可以将它看做成一个已经存在的智能AI对象。
import paddle
import paddle.nn as nn
from paddle.nn.initializer import KaimingNormal# 门控线性单元 Gated Linear Units (GLU)
class GLU(nn.Layer):def __init__(self, axis):super(GLU, self).__init__()self.sigmoid = nn.Sigmoid()self.axis = axisdef forward(self, x):a, b = paddle.split(x, num_or_sections=2, axis=self.axis)act_b = self.sigmoid(b)out = paddle.multiply(x=a, y=act_b)return out# 基本卷积块
class ConvBlock(nn.Layer):def __init__(self, in_channels, out_channels, kernel_size, stride, padding=0, p=0.5):super(ConvBlock, self).__init__()self.conv = nn.Conv1D(in_channels, out_channels, kernel_size, stride, padding, weight_attr=KaimingNormal())self.conv = nn.utils.weight_norm(self.conv)self.act = GLU(axis=1)self.dropout = nn.Dropout(p)def forward(self, x):x = self.conv(x)x = self.act(x)x = self.dropout(x)return x# PPASR模型
class PPASR(nn.Layer):def __init__(self, vocabulary, data_mean=None, data_std=None, name="PPASR"):super(PPASR, self).__init__(name_scope=name)# 数据均值和标准值到模型中,方便以后推理使用if data_mean is None:data_mean = paddle.to_tensor(1.0)if data_std is None:data_std = paddle.to_tensor(1.0)self.register_buffer("data_mean", data_mean, persistable=True)self.register_buffer("data_std", data_std, persistable=True)# 模型的输出大小,字典大小+1self.output_units = len(vocabulary) + 1self.conv1 = ConvBlock(128, 500, 48, 2, padding=97, p=0.2)self.conv2 = ConvBlock(250, 500, 7, 1, p=0.3)self.conv3 = ConvBlock(250, 2000, 32, 1, p=0.3)self.conv4 = ConvBlock(1000, 2000, 1, 1, p=0.3)self.out = nn.utils.weight_norm(nn.Conv1D(1000, self.output_units, 1, 1))def forward(self, x, input_lens=None):x = self.conv1(x)for i in range(7):x = self.conv2(x)x = self.conv3(x)x = self.conv4(x)x = self.out(x)if input_lens is not None:return x, paddle.to_tensor(input_lens / 2 + 1, dtype='int64')return x
解码方法
import Levenshtein as Lev
import paddleclass GreedyDecoder(object):def __init__(self, vocabulary, blank_index=0):self.int_to_char = dict([(i, c) for (i, c) in enumerate(vocabulary)])self.blank_index = blank_index# 给定一个数字序列列表,返回相应的字符串def convert_to_strings(self, sequences, sizes=None, remove_repetitions=False, return_offsets=False):strings = []offsets = [] if return_offsets else Nonefor x in range(len(sequences)):seq_len = sizes[x] if sizes is not None else len(sequences[x])string, string_offsets = self.process_string(sequences[x], seq_len, remove_repetitions)strings.append([string])if return_offsets:offsets.append([string_offsets])if return_offsets:return strings, offsetselse:return strings# 获取字符,并删除重复的字符def process_string(self, sequence, size, remove_repetitions=False):string = ""offsets = []sequence = sequence.numpy()for i in range(size):char = self.int_to_char[sequence[i].item()]if char != self.int_to_char[self.blank_index]:# 是否删除重复的字符if remove_repetitions and i != 0 and char == self.int_to_char[sequence[i - 1].item()]:passelse:string = string + charoffsets.append(i)return string, paddle.to_tensor(offsets, dtype='int64')def cer(self, s1, s2):"""通过计算两个字符串的距离,得出字错率"""s1, s2, = s1.replace(" ", ""), s2.replace(" ", "")return Lev.distance(s1, s2)def decode(self, probs, sizes=None):"""解码,传入结果的概率解码得到字符串,删除序列中的重复元素和空格。"""max_probs = paddle.argmax(probs, 2)strings, offsets = self.convert_to_strings(max_probs,sizes,remove_repetitions=True,return_offsets=True)return strings, offsets
总结
- 训练的过程和评估详情可以查看此链接: https://aistudio.baidu.com/aistudio/projectdetail/5902290?forkThirdPart=1
【AI】PaddlePaddle实现自动语音识别相关推荐
- 【语音之家】AI产业沙龙—自动语音识别利器 - NVIDIA NeMo
由CCF语音对话与听觉专委会 .中国人工智能产业发展联盟(AIIA)评估组.NVIDIA.语音之家.希尔贝壳共同主办的[语音之家]AI产业沙龙-自动语音识别利器 - NVIDIA NeMo,将于202 ...
- AI周报丨快手团队长文解读:基于FPGA加速的自动语音识别在大规模直播和短视频场景的应用
AI周报 - 热门论文 - 题目:On Training Implicit Models 本文主要研究无限层隐式模型的训练问题.具体地说,以前的工作采用隐式微分,并为反向传播计算精确的梯度.然而,是否 ...
- 快手团队长文解读:基于FPGA加速的自动语音识别在大规模直播和短视频场景的应用...
来源:机器之心 本文约6000字,建议阅读10分钟 本文介绍了基于FPGA加速的自动语音识别在大规模直播和短视频场景的应用. 典型的实时流式自动语音识别业务如语音搜索.语音输入等和用户操作相关,直接影 ...
- 谷歌提出新型自动语音识别数据增强大法,直接对频谱图“动刀”,提升模型表现...
铜灵 发自 编译整理 量子位 出品 | 公众号 QbitAI 每次用语音输入完成"打字"过程,你的手机就经历了一次自动语音识别(ASR). 这种已经无处不在的音频转录成文本的技术, ...
- 什么是自动语音识别?
全文共2019字,预计学习时长6分钟 来源:dy.163 Siri .Alexa 等虚拟助手的出现,让自动语音识别系统得到了更广泛的运用与发展. 自动语音识别(ASR)是一种将口语转换为文本的过程.该 ...
- ASR自动语音识别基础理论
基础理论 对话式AI的理解与其子任务的介绍 ASR自动语音识别 简史和发展历程 工作流程及原理 ASR自动语音识别简介 ASR自动语音识别应用场景 ASR自动语音识别理论 自动语音识别工具包的简介及设 ...
- NVIDIA Riva中文手册 (四) —— Riva ASR自动语音识别API的使用
如何通过Riva ASR API 与开箱即用的语音识别模型一起使用? 本节将引导您了解 Riva Speech Skills ASR 服务的基础知识,特别介绍如何将 Riva ASR API 与开箱即 ...
- 2019年,这8款自动语音识别方案你应该了解!
2019年,这8款自动语音识别方案你应该了解! 原创: AI前线小组 译 AI前线 1周前 作者 | Derrick Mwiti翻译 | 核子可乐编辑 | LindaAI 前线导读: 基于计算机的人类 ...
- 【语音识别】自动语音识别(ASR)研究综述
自动语音识别(ASR)研究综述 Note: 正文内容绝大部分取自 语音识别研究综述 WeNet的部署参考该Blog WeNet平台搭建 文章目录 自动语音识别(ASR)研究综述 零.参考资料 1.参考 ...
最新文章
- 安卓真机测试安装时报错
- 控制科学对计算机能力的要求,0811控制科学与工程基本要求.pdf
- java通过maven构建项目实现日志生成模拟(三)通过logback 打印日志
- 关于win7的64位和32位有什么不同性价比怎么比
- matlab图像边缘检测分析
- Android线程池的简单使用
- 河北民间组织管理系统之社会团体许可业务的项目目标文档
- linux内核丢包分析工具,Linux模拟网络丢包与延迟的方法
- rsync通过服务同步、linux日志、screen工具
- 【谷粒学院】项目总结
- 调试错误解决方案之VC++
- fanuc机器人与示教器配对_阳江Fanuc机器人示教器维修中心
- CISA,CISM,CISSP证书区别及cisp,oscp,osce,che简介
- uniapp H5页面 点击图片放大预览
- probability是什么意思_概率(Probability)的本质是什么?
- 小明左右手分别拿两张纸牌:黑桃10和红心8,现在交换手中的牌。编写并输出互换后的结果
- pb函数库之字符串操作函数
- 测鬼记(中)之奋斗——变数
- 中学物理教学参考杂志社中学物理教学参考编辑部2022第9期目录
- IE中不能自动选择UTF-8编码的解决办法
热门文章
- 50种黑白人像摄影调色效果lr预设
- 万字长文综述:文本增强技术的研究进展及应用实践
- 什么是HTML DOM对象
- 数论-快速幂、矩阵快速幂、慢速乘
- Ubuntu / Linux 手动安装Paraview
- AR2开源桌面机器人
- RadioButton前面的小圆圈颜色设置
- python可以用保留字作为函数的名字吗_Python不允许使用关键字作为变量名,允许使用内置函数名作为变量名,但这会改变函数名的含义...
- 老站长揭秘Google Adsense盈利的真相(2021版)
- WebGL+Three.js 入门与实战、搞定前端前沿技术