【云驻共创】华为云AI实战营:语音识别

举报
Linux猿 发表于 2021/12/04 22:56:52 2021/12/04
【摘要】 在日常生活中,语音识别的应用场景越来越常见,例如:语音助手、语音控制智能家居、语音导航、语音查询、语音输入法等。未来随着人工智能的不断发展,语音识别的将变得越来越重要。本文就来带领大家学习一下语音识别,将首先介绍语音和语音识别,然后,使用两个实战,带大家全面了解语音识别。

在日常生活中,语音识别的应用场景越来越常见,例如:语音助手、语音控制智能家居、语音导航、语音查询、语音输入法等。未来随着人工智能的不断发展,语音识别的将变得越来越重要。本文就来带领大家学习一下语音识别,将首先介绍语音和语音识别,然后,使用两个实战,带大家全面了解语音识别。


图1.png

图1 语音识别应用场景

一、语音和语音识别介绍

1.1  语音领域知识介绍

语音是AI领域最重要的分支之一,声音是人与自然接收信息的重要渠道,语音作为人与人交流的重要媒介,对于语音的研究是很重要和必要的AI的三大领域研究对象:计算机视觉(图像)、自然语言处理(文本)、语音(音频)。


语音识别-三大应用领域.png


图1  AI三大应用领域

  • 音频特征

声音从本质上来看是声波,可以作为一种信号来进行处理,成为音频信号。

与图像等相同,需要经过把非结构化数据转化成结构化数据,并提取出有用的特征,以便后续的分析处理。与图像等不同的是,音频信号时以为的序列数据,不能直接采用图像的形式进行处理,需要有领域内特定的处理方法。

梅尔频率倒谱稀疏(MFCC,Mel-frequency cepstral coefficients)是声音处理领域最常见的声音特征,MFCC是基于人耳听觉特性提出来的,与频率成非线性对应关系,是一种非常有效的特征提取方法。MFCC提取步骤主要是:


自制-1.png


图3 提取步骤

依次通过预加重->分帧->加窗->快速傅里叶变换(FFT)->梅尔滤波器组->离散余弦变换(DCT)。其中,快速傅里叶变换(FFT)与梅尔滤波器组是MFCC最重要的部分。然而,DCT变换会损失声音的结构信息,因此MFCC在深度模型中性能不是很好。

随着深度学习的发展,受限的玻尔兹曼机(RBM)、卷积神经网络(CNN)、CNN-LSTM-DNN(CLDNN)等深度神经网络模型作为一个直接学习滤波器代替梅尔滤波器组被用于自动学习的语音特征提取中,并取得良好的效果。

  • 音频数据

语音领域的音频开源数据集,大部分在openslr都可以下载使用,如下所示:

openslr网站首页.png

图4 openslr网站首页

此外,还有一些其它常用音频开源数据集,如:

  • 音乐相关集,比如:Million Song Dataset和MusicNet;
  • 环境声相关的最大数据集AuclioSet,超过200完个音频片段;

其中,常见任务如下所示:

  • 音频分类

音频分类是语音领域的基本任务,从本质上说,它就是从音频中提取特征,然后判断具体属于哪一类。

例如:区分不同动物的叫声、区分男性和女性的声音、音乐曲风分类;

  • 音频分割

音频分割同样是语音领域的基础任务,根据定义的一组特征将音频样本分割成段。

目前音频分割的一个应用是心音分割,即识别心脏的特定信号,帮助诊断心血管疾病。

  • 音频指纹识别

音频指纹识别的目的是从音频中提取一段特定的数字特征,用于快速识别该段音频是否来自音频样本,或从音频库中搜索出带有相同数字特征的音频。听歌识曲的功能就是使用最广泛的音频指纹识别应用。

  • 声源分离

声源分离指的是在多声源混合的信号中提取单一的目标声源。

常见的应用之一就是识别同时翻译音乐中的歌词。

  • 声音增强

通常为语音增强,指的是通过减小噪声来提高语音质量。

通过语音增强可以有效抑制各种干扰信号,增强目标语音信号,如童话或对讲时使语音更清晰。

  • 语音合成

根据数据集中的声音属性特征来生成新的数据。

云合成主要采用波形拼接合成和统计参数合成两种方式。波形拼接语音合成需要有足够的高质量发音人录音才能够合成高质量的语音,在工业界中有广泛的应用。

  • 定位和跟踪

利用多通道信号可以对声源位置进行跟踪和定位。

定位和跟踪相关的应用包括监控、智能家居、智能会议室等。

  • 音乐信息检索

这是音频处理中最困难的任务之一,实质上相当于建立一个基于音频数据的搜索引擎。

音乐相关任务通常包含低级分析、节奏分析、谐波分析、高级分析和高级比较。

  • 语音识别

语音识别指的是将语音信号转化为文字序列,是说是有基于语音交互的基础。

语音识别是语音领域最重要的任务,下面将进行详细的介绍。

1.2  语音识别知识介绍

语音识别技术,也可以称为自动语音识别(Automatic Speech Recognition,ASR),其任务是将人说话的语音信号转换为可被计算机程序所识别的信息,从而识别说话人的语音指令及文字内容的技。语音识别技术是一种综合性的技术,它涉及到多个学科领域,如发生机理和听觉机理、信号处理、概率论和信息论、模式识别以及人工智能等等。语音识别技术已经进入家电、通信、医疗、家庭服务等各个领域。近年来,语音识别在移动终端上的应用最为火热,语音对话机器人、语音助手等层出不穷。

(1)语音识别的技术历程

现代语音识别可以追溯到1952年,Davis等人研制了世界上第一个能识别10个英文数字发音的实验系统,从此正式开启了语音识别的进程,但是进展缓慢。从1993年到2009年,语音识别一直处于GMM-HMM(Gaussian Mixed Model-Hidden Markov Model,基于高斯混合模型的隐马尔科夫模型)时代,语音识别率提升缓慢。

2009年随着深度学习技术,特别是神经网络的兴起,语音识别框架变为DNN-HMM(Deep Neural Networks-Hidden Markov Model,基于深度神经网络的隐马尔科夫模型),语音识别精准率得到了显著提升。

2015年以后,由于端到端技术兴起和硬件设备的不断更新,语音识别进入了百花齐放时代,语音界都在训练更深、更复杂的网络,同时利用端到端技术进一步大幅提升了语音识别的性能,知道2017年语音识别的准确性首次超越了人类,词错误率降低到5.1%。当然,这是在一定限制条件下的实验结果,还不具有普遍代表性。

(2)语音识别流程

语音识别系统的主要流程如下图所示:

语音识别流程.png

图5 语音识别流程

声学模型(acoustic model)用于建模语音信号与音素之间的关系,是自动语音识别系统的模型中最底层的部分,同时也是自动语音识别系统中最关键的组成单元,声学模型建模的好坏会直接从根本上影响语音识别的识别效果和鲁棒性。常用的升学模型有混合高斯-隐马尔科夫模型(GMM-HMM,Gaussian Mixture Model-Hidden Markov Model)、深度神经网络-隐马尔科夫模型(DHH-HMM,Deep Neural Networks-Hidden Markov Model)、深度循环神经网络-隐马尔科夫模型(RNN-HMM,Recurrent Neural Network-Hidden Markov Model)等。

(3)语音模型

语音模型(language model)主要用于建模音素与字词之间的对应关系。由于语音信号的时变性、噪音和其它一些不稳定音素,单纯靠声学模型无法达到较高的语音识别的准确率。在人类语音中,每一句话的单词直接有密切的联系,这些单词层面的信息可以减少声学模型上的搜索范围,有效地提高识别的准确性,要完成这项任务语音模型是必不可少的,它提供了语言中词之间上细纹信息以及语义信息。常用语音模型有n-gram语言模型、长短记忆网络(LSTM,Long Short-Term Memory)等。

(4)语音识别的挑战

1、对自然语言的识别和理解。首先必须将连续的讲话分解为词、音素等单位,其次要建立一个理解语义的规则;

2、语音信息量大。语音模式不仅对不同的说话人不同,对同一说话人也是不同的。例如:一个说话人在随意说话和认真说话时的语音信息是不同的,其说话的流利程度、情绪都会随着时间变化。

3、语音的模糊性。说话者在讲话时,不同的词可能听起来是相似的,或者由于语速快造成词不清晰完整。

4、单个字母或词、字的语音特性受上下文的影响,以致改变了重音、音调、音量或发音速度等。

5、环境噪音、多人语音或背景交谈干扰对语音识别有严重影响,致使识别率低。

6、非标准口音有较大影响,如方言、中文的平翘舌发音及鼻音发音;

7、延迟。如果要解决语音算法的部署,需要极低的延迟,也就是尽可能压缩计算量,并且在芯片等硬件上有更强有力的支撑。

因此,语音识别树在实际应用中还有很长的路要走,需要找到更强大的声学模型和语言模型来适应各种复杂场景。

1.3  实例:音频数据读取与处理

(1)创建ModelArts Notebook

下面,我们在ModelArts中创建一个Notebook开发环境,ModelArts Notebook提供网页版的Python开发环境,可以方便的编写、运行代码,并查看运行结果。

第一步:在ModelArts服务主界面依次点击“开发环境”、“创建”。

图片6.png

图6 开发环境、创建

第二步:填写notebook所需的参数:

表1 notebook所需参数

参数

说明

计费方式

按需计费

名称

自定义名称

工作环境

Python3

资源池

公共资源池

类型

GPU

规格

[限时免费]体验规格GPU版

存储配置

EVS

磁盘规格

5GB

第三步:配置好Notebook参数后,点击下一步,进入Notebook信息预览。确认无误后,点击“立即创建”

第四步:创建完成后,返回开发环境主界面,等待Notebook创建完毕后,打开Notebook,进行下一步操作。


图片7.png

图7 创建完成后

(2)在ModelArts中创建开发环境

接下来,我们创建一个实际的开发环境,用于后续的实验步骤。

第一步:点击下图所示的“打开”按钮,进入刚刚创建的Notebook,如下所示:

图片8.png

图8 Notebook界面

第二步:创建一个Python3环境的的Notebook。点击右上角的"New",然后创建TensorFlow-1.8开发环境。

第三步:点击左上方的文件名"Untitled",并输入一个与本实验相关的名称,如"sound_introduction"。

图片9.png图9 重命名界面

(3)音频数据读取与处理

接下来我们进行初步的音频数据读取与处理,体验音频数据文件的处理过程。

首先通过 ModelArts SDK 下载示例音频文件并解压。

Successfully download file modelarts-labs-bj4/notebook/DL_sound_introduction/test.tar.gz from OBS to local ./test.tar.gz
total 3056
drwxr-s---  4 ma-user ma-group    4096 Aug 14 15:29 .
drwxrwsr-x 10 ma-user ma-group    4096 Aug 14 15:04 ..
drwxr-s---  2 ma-user ma-group    4096 Aug 14 15:25 img
drwxr-s---  2 ma-user ma-group    4096 Aug 14 15:24 .ipynb_checkpoints
-rw-r-----  1 ma-user ma-group   34871 Aug 14 15:24 nlp_introduction.ipynb
-rwxr-x---  1 ma-user ma-group  326044 Dec 30  2015 test1.wav
-rwxr-x---  1 ma-user ma-group  812870 Feb 17 16:57 test2.wav
-rw-r-----  1 ma-user ma-group 1930444 Aug 14 15:28 语音和语音识别介绍.ipynb
from modelarts.session import Sessionsession = Session()
if session.region_name == 'cn-north-1':
    bucket_path = 'modelarts-labs/notebook/DL_sound_introduction/test.tar.gz'
    elif session.region_name == 'cn-north-4':
    bucket_path = 'modelarts-labs-bj4/notebook/DL_sound_introduction/test.tar.gz'else:
    print("请更换地区到北京一或北京四")
    session.download_data(bucket_path=bucket_path, path='./test.tar.gz')
# 使用tar命令解压资源包!tar xf ./test.tar.gz# 使用rm命令删除压缩包!rm ./test.tar.gz
#查看目录!ls -la   

播放音频文件,代码如下所示:

import IPython
test1 = './test1.wav'IPython.display.Audio(test1)
test2 = './test2.wav'IPython.display.Audio(test2)

Librosa 是最常用的音频、音乐分析、处理的 python 工具包,常见的时频处理、特征提取、绘制声音图形等功能应有尽有,功能十分强大。

今天我们学习使用 librosa 来读取音频文件,提取 MFCC 特征,绘制波形图和频谱图。

首先安装 librosa,代码如下所示:

!pip install librosa
Requirement already satisfied: librosa in /home/ma-user/anaconda3/envs/TensorFlow-1.8/lib/python3.6/site-packages
Requirement already satisfied: audioread>=2.0.0 in /home/ma-user/anaconda3/envs/TensorFlow-1.8/lib/python3.6/site-packages (from librosa)
Requirement already satisfied: numpy>=1.8.0 in /home/ma-user/anaconda3/envs/TensorFlow-1.8/lib/python3.6/site-packages (from librosa)
Requirement already satisfied: scipy>=0.14.0 in /home/ma-user/anaconda3/envs/TensorFlow-1.8/lib/python3.6/site-packages (from librosa)
Requirement already satisfied: scikit-learn!=0.19.0,>=0.14.0 in /home/ma-user/anaconda3/envs/TensorFlow-1.8/lib/python3.6/site-packages (from librosa)
Requirement already satisfied: joblib>=0.12 in /home/ma-user/anaconda3/envs/TensorFlow-1.8/lib/python3.6/site-packages (from librosa)
Requirement already satisfied: decorator>=3.0.0 in /home/ma-user/anaconda3/envs/TensorFlow-1.8/lib/python3.6/site-packages (from librosa)
Requirement already satisfied: six>=1.3 in /home/ma-user/anaconda3/envs/TensorFlow-1.8/lib/python3.6/site-packages (from librosa)
Requirement already satisfied: resampy>=0.2.0 in /home/ma-user/anaconda3/envs/TensorFlow-1.8/lib/python3.6/site-packages (from librosa)
Requirement already satisfied: numba>=0.38.0 in /home/ma-user/anaconda3/envs/TensorFlow-1.8/lib/python3.6/site-packages (from librosa)
Requirement already satisfied: llvmlite>=0.25.0dev0 in /home/ma-user/anaconda3/envs/TensorFlow-1.8/lib/python3.6/site-packages (from numba>=0.38.0->librosa)You are using pip version 9.0.1, however version 20.2.2 is available.
You should consider upgrading via the 'pip install --upgrade pip' command.

加载音频文件,返回的两个值分别是音频时间序列(numpy数组),默认采样率(sr)

import librosaimport librosa.displayimport matplotlib.pyplot as plt
y1, sr1 = librosa.load(test1, sr=None)print("test1 音频时间序列 y1:", y1)print("test1 默认采样率 sr1:", sr1)
y2, sr2 = librosa.load(test2, sr=None)print("test2 音频时间序列 y2:", y2)print("test2 默认采样率 sr2:", sr2)
test1 音频时间序列 y1: [-0.00695801 -0.01132202 -0.0128479  ... -0.01156616 -0.01165771 -0.01211548]
test1 默认采样率 sr1: 16000
test2 音频时间序列 y2: [ 0.0000000e+00 -1.5258789e-05  1.5258789e-05 ... -3.2043457e-04 1.8310547e-04  7.3242188e-04]
test2 默认采样率 sr2: 16000

显示音频波形图,如下所示:

plt.figure()librosa.display.waveplot(y1, sr1)plt.title('test1')plt.show()

图片10.png

图10 音频波形图

显示音频频谱图,如下所示:

melspec1 = librosa.feature.melspectrogram(y1, sr1, n_fft=1024, hop_length=512, n_mels=128)logmelspec1 = librosa.power_to_db(melspec1)plt.figure()librosa.display.specshow(logmelspec1, sr=sr1, x_axis='time', y_axis='mel')plt.title('test1')plt.show()

图片11.png

图11 音频频谱图

melspec2 = librosa.feature.melspectrogram(y2, sr2, n_fft=1024, hop_length=512, n_mels=128)logmelspec2 = librosa.power_to_db(melspec2)plt.figure()librosa.display.specshow(logmelspec2, sr=sr2, x_axis='time', y_axis='mel')plt.title('test2')plt.show()

图片12.png

图12 音频频谱图

提取MFCC特征,如下所示:

mfcc1 = librosa.feature.mfcc(y=y1, sr=sr1)mfcc2 = librosa.feature.mfcc(y=y2, sr=sr2)
print("test1 的 MFCC:", mfcc1)print(mfcc1.shape)print("test2 的 MFCC:", mfcc2)print(mfcc2.shape)

test1 的 MFCC: [[-4.95988437e+02 -5.05147607e+02 -5.14900406e+02 ... -5.05147780e+02
  -5.15383727e+02 -5.26900504e+02]
 [ 4.69612383e+01  4.76198066e+01  4.36448418e+01 ...  2.42590096e+01
   2.34368069e+01  3.32601179e+01]
 [ 6.63757311e+00  5.37317474e+00  3.87170056e+00 ...  7.80046583e+00
   3.37550540e+00  6.52361552e+00]
 ...
 [ 1.12448024e+01  1.00868238e+01  5.21030986e+00 ...  6.51137989e+00
   6.42023382e+00  5.64207509e+00]
 [ 8.35918538e+00  8.70087201e+00  4.91722851e+00 ... -1.80603031e+00
   2.98696875e-01  3.47118457e+00]
 [ 2.46081892e+00  2.92839197e+00  7.10055989e+00 ...  2.25417952e+00
   1.24352005e+00  2.55906367e+00]]
(20, 319)
test2 的 MFCC: [[-7.54980265e+02 -7.08014776e+02 -6.62894931e+02 ... -5.34064862e+02
  -5.36352710e+02 -5.33487734e+02]
 [-2.30455875e+00  1.11016923e+01  2.10026881e+01 ...  5.14833889e+00
   5.83748665e+00  1.99595035e+00]
 [-4.75530206e+00 -1.60624388e+01 -2.61427714e+01 ... -9.42498286e+00
  -8.99282379e+00 -6.96443646e+00]
 ...
 [ 7.42018049e+00  4.34331842e+00  1.89516364e+00 ...  8.84575052e+00
   1.14450473e+01  1.85537264e+01]
 [-1.44511412e+00 -1.06407673e+01 -1.15823193e+01 ...  4.50537217e-01
  -4.67942825e+00 -8.98093603e-01]
 [-3.28809429e+00 -8.46655632e+00 -2.88116935e+00 ...  7.03202032e-01
  -7.35357079e-01 -4.05514158e+00]]
(20, 397)

绘制 MFCC 特征图(进行缩放后),如下所示:

import sklearnmfcc1 = sklearn.preprocessing.scale(mfcc1, axis=1)plt.figure()librosa.display.specshow(mfcc1, sr=sr1, x_axis='time')plt.title('test1')plt.show()

图片13.png

图13 特征图

mfcc2 = sklearn.preprocessing.scale(mfcc2, axis=1)plt.figure()librosa.display.specshow(mfcc2, sr=sr2, x_axis='time')plt.title('test2')plt.show()

图片14.png

图14 特征图

二、实战:零代码动物叫声分类模型开发

下面通过实战零代码动物叫声分类模型开发进一步学习语音识别的流程。将详细介绍使用ModelArts自动学习声音分类识别动物叫声。

2.1 准备工作

首先,需要完成ModelArts准备工作。包括注册华为云账号、ModelArts全局配置和OBS相关操作。

2.2 下载数据集

点击此处下载数据集压缩包至本地,然后解压。可以得到文件夹sound_classification。训练集位于sound_classification\train目录下,共4类动物叫声,分别是鸟(bird)、猫(cat)、狗(dog)和虎(tiger),每种动物25个叫声音频数据,共一百条音频数据。测试集位于sound_classification\test目录下,每种动物提供5个叫声音频数据。音频数据格式均为wav格式,均可在各音频播放器直接播放试听。

2.3 创建声音分类项目

点击进入ModelArts自动学习界面,然后点击“声音分类”创建项目按钮:

图片15.png

图15 ModelArts界面

按照如下示例填写参数:名称、数据集名称、数据集输入位置、数据集输出位置。最后点击“创建项目”完成声音分类项目创建。

图片16.png

图16 配置参数界面

2.4 动物叫声音频数据上传

进入声音分类项目后,首先进入数据标注界面。点击下图中的“添加音频”按钮,如下所示:

图片17.png

图 17 数据标注界面

添加任一个动物文件夹下数据,注意要求“仅支持WAV格式音频文件,单个音频文件不能超过4MB,且单次上传的音频文件总大小不能超过8MB。点击确定,如下所示:

图片18.png

图18 添加音频界面

可以看到上一步选取的音频文件已全部上传并显示未标注,此处可以点击播放音频文件:

图片19.png

图19 数据标注界面

2.5 动物叫声音频数据标注

在“未标注”页面,批量选中相同类别的音频文件,在右侧添加标签名称,最后点击“确定”按钮。

图片20.png

图20 未标注界面

随后可以看到刚才选中的音频文件已标注完成。


图片21.png

图21 未标注界面

按照同样的方法,将所有训练数据上传并进行标注,直到100个音频数据文件全部标注完,确认每类动物的数量为25,可以点击右侧的标签来查看每个标签对应的文件。


图片22.png


图22 已标注界面

如果发现标签不正确,可以选中音频数据,重新选择标签。

2.6 模型训练

点击右上角“开始训练”按钮,设置数据集版本名称,可以选择设置训练时长(不同任务类型和数据量需要的训练时长不一样),点击“确定”即可开始训练。

图片23.png

图23 训练设置

训练任务提交成功,自动跳转模型训练的界面。

图片24.png

图24 训练任务提交

大概花1分30秒左右,模型训练完成。可以看到训练时长和训练结果,包括准确率、召回率、精确率、F1值等常用评价指标,还可以看到具体每个类别的评价指标。

图片25.png

图25 完成界面

2.7 模型部署

点击左侧“部署”按钮,将模型部署为一个在线服务:

图片28.png

图28 部署界面

选择节点计算规格,是否自动停止,点击确定开始部署模型。


图片29.png

图29 部署界面

部署任务提交成功,自动跳转部署上线的界面。

图片30.png

图30提交成功界面

2.8 服务测试

部署成功后,左侧显示服务状态为“运行中”,此时可以进行在线预测。

图片31.png

图31 在线测试界面

点击“上传”按钮,上传一张本地的测试文件(从测试集test目录中挑选)。上传的音频文件可以实时播放。

点击“预测”按钮进行在线预测,右侧可以看到预测结果和URL接口。

图片32.png


图32 预测结果

同时,自动学习部署的服务可以在ModelArts控制台的“部署上线” -> “在线服务”中调用。


图片33.png

图33 自动学习部署服务

进入“预测”,可以进行测试文件上传,上传的音频文件可以播放,进行在线预测,右侧可以看到预测结果。

图片34.png

图34 预测成功界面

下面是另外两类动物的在线预测结果,可以看到模型的预测效果比较好。

图片35.png

图35 预测成功界面

2.9 关闭在线服务

在使用完部署的服务后,需关闭服务,否则会持续收费。可以通过以下两种方式关闭服务:

(1)在“自动学习” -> “部署上线”中停止在线服务;

(2)在“部署上线” -> “在线服务”中停止在线服务。

(3)当需要使用该在线服务的时候,可以重新启动该在线服务。

至此,完成零代码动物叫声分类,赶紧动手实践下吧!

三、实战:DFCNN + Transformer 模型完成中文语音识别

下面通过实战DFCNN+Transformer模型进一步加深对语音识别的理解。

3.1 实验数据集

本次实验使用的数据集是THCHS-30,该数据集具有如下特点:

  • 经典的中文语音数据集,包含了1万余条语音文件,约40小时;
  • 在安静的办公室环境下,通过单个碳粒麦克风录取的,采样频率16kHz,采样大小16bits;
  • 内容以新闻、文章、诗句为主,录制对象为普通话流利的女性大学生;
  • 语音数据格式为 .wav,对应的拼音和汉字的文本文件格式为 .wav.trn;
  • 可从 http://www.openslr.org/18/下载;

其中,训练、验证、测试的数据集分布如下表所示:

表2 训练、验证以及测试数据集的分布

数据集

音频时长(h)

句子数

词数

train(训练)

25

10000

198252

dev(验证)

2:14

893

17743

test(测试)

6:15

2495

49085

 本实验的语音识别流程如下所示:

图片36.png

图36 语音识别流程

本次实验的实验环境为:

  • 数据集

THCHS-30 数据集

  • 模型

DFCNN 模型 Transformer 模型

  • 实验环境
  • 环境创建默认Notebook多引擎,Python3, GPU
  • notebook创建创建notebook时请选择:TensorFlow-1.13
  • 实战内容

DFCNN + Transformer 模型完成中文语音识别

本实践任务为搭建一个基于深度学习的中文语音识别系统,主要包括声学模型和语言模型,能够将输入的音频信号识别为汉字。实践使用的模型均为近年来在语音识别深度学习领域中表现较为突出的模型,声学模型为 DFCNN,语言模型为 Transformer,下面开始进行实践。

3.2 进入ModelArts

点击如下链接:https://www.huaweicloud.com/product/modelarts.html ,进入ModelArts主页。点击“立即使用”按钮,输入用户名和密码登录,进入ModelArts使用页面。

3.3 创建ModelArts notebook

下面,我们在ModelArts中创建一个notebook开发环境,ModelArts notebook提供网页版的Python开发环境,可以方便的编写、运行代码,并查看运行结果。

第一步:在ModelArts服务主界面依次点击“开发环境”、“创建”。

图片37.png

图37 服务主界面

第二步:填写notebook所需的参数:

表3 参数

参数

说明

计费方式

按需计费

名称

自定义名称

工作环境

Python3

资源池

公共资源池

类型

GPU

规格

[限时免费]体验规格GPU版

存储配置

EVS

磁盘规格

5GB

第三步:配置好notebook参数后,点击下一步,进入notebook信息预览。确认无误后,点击“立即创建”。

第四步:创建完成后,返回开发环境主界面,等待Notebook创建完毕后,打开Notebook,进行下一步操作。

图片38.png

图38 开发环境主界面

3.4 在ModelArts中创建开发环境

接下来,我们创建一个实际的开发环境,用于后续的实验步骤。

第一步:点击下图所示的“打开”按钮,进入刚刚创建的Notebook。

图片39.png

图39 Notebook界面

第二步:点击右上角的"New",然后创建TensorFlow 1.13.1开发环境。

第三步:点击左上方的文件名"Untitled",并输入一个与本实验相关的名称,如“speech_recognition”

图片40.png

图40 重命名界面

3.5 在Notebook中编写并执行代码

在Notebook中,我们输入一个简单的打印语句,然后点击上方的运行按钮,可以查看语句执行的结果:

图片41.png

图41 执行代码

开发环境准备好啦,接下来可以愉快地写代码啦!

准备源代码和数据,准备案例所需的源代码和数据,相关资源已经保存在 OBS 中,我们通过 ModelArts SDK 将资源下载到本地。

import os
import subprocess
from modelarts.session
import Sessionsession = Session()
if session.region_name == 'cn-north-1':
    bucket_path = 'modelarts-labs/notebook/DL_speech_recognition/speech_recognition.tar.gz'elif session.region_name == 'cn-north-4':
    bucket_path = 'modelarts-labs-bj4/notebook/DL_speech_recognition/speech_recognition.tar.gz'else:
    print("请更换地区到北京一或北京四")
if not os.path.exists('speech_recognition'):
    session.download_data(bucket_path=bucket_path, path='./speech_recognition.tar.gz')
    subprocess.run(['tar xf ./speech_recognition.tar.gz;rm ./speech_recognition.tar.gz'], stdout=subprocess.PIPE, shell=True, check=True)
Successfully download file modelarts-labs-bj4/notebook/DL_speech_recognition/speech_recognition.tar.gz from OBS to local ./speech_recognition.tar.gz

上一步下载了speech_recognition.tar.gz,解压后文件夹结构如下:

图片42.png

THCHS30是一个经典的中文语音数据集,包含了1万余条语音文件,大约40小时的中文语音数据,内容以新闻文章诗句为主,全部为女声。THCHS-30是在安静的办公室环境下,通过单个碳粒麦克风录取的,采样频率16kHz,采样大小16bits,录制对象为普通话流利的女性大学生。

thchs30数据库大小为6.4G。其中,这些录音根据其文本内容分成了四部分,A(句子的ID是1-250),B(句子的ID是251-500),C(501-750),D(751-1000)。ABC三组包括30个人的10893句发音,用来做训练,D包括10个人的2496句发音,用来做测试。其具体的划分如下表所示:

表5 数据集

数据集

音频时长(h)

句子数

词数

train(训练)

25

10000

198252

dev(验证)

2:14

893

17743

test(测试)

6:15

2495

49085

 THCHS-30数据集可从 http://www.openslr.org/18/ 下载。其他常用的开源中文语音数据集还有 Aishell、Free ST Chinese Mandarin Corpus、Primewords Chinese Corpus Set、aidatatang_200zh 等,均可以从 http://www.openslr.org/resources.php 下载。

THCHS-30语音数据格式为.wav,对应的拼音和汉字的文本文件格式为.wav.trn。

在本实践中,选取A部分语音均放在data文件夹下进行训练和测试。

同时将所有数据的拼音和汉字文本整理在data.txt文件中,以方便使用。下面为读取data.txt文件十条内容。

with open('./speech_recognition/data.txt',"r", encoding='UTF-8') as f:    #设置文件对象
    f_ = f.readlines()
    for i in range(10):
        for j in range(3):
            print(f_[i].split('\t')[j])
    print('语音总数量:',len(f_),'\n')
A11_0.wav
lv4 shi4 yang2 chun1 yan1 jing3 da4 kuai4 wen2 zhang1 de di3 se4 si4 yue4 de lin2 luan2 geng4 shi4 lv4 de2 xian1 huo2 xiu4 mei4 shi1 yi4 ang4 ran2
绿是阳春烟景大块文章的底色四月的林峦更是绿得鲜活秀媚诗意盎然
 
A11_1.wav
ta1 jin3 ping2 yao1 bu4 de li4 liang4 zai4 yong3 dao4 shang4 xia4 fan1 teng2 yong3 dong4 she2 xing2 zhuang4 ru2 hai3 tun2 yi4 zhi2 yi3 yi1 tou2 de you1 shi4 ling3 xian1
他仅凭腰部的力量在泳道上下翻腾蛹动蛇行状如海豚一直以一头的优势领先

A11_10.wav
pao4 yan3 da3 hao3 le zha4 yao4 zen3 me zhuang1 yue4 zheng4 cai2 yao3 le yao3 ya2 shu1 di4 tuo1 qu4 yi1 fu2 guang1 bang3 zi chong1 jin4 le shui3 cuan4 dong4
炮眼打好了炸药怎么装岳正才咬了咬牙倏地脱去衣服光膀子冲进了水窜洞

 A11_100.wav
ke3 shei2 zhi1 wen2 wan2 hou4 ta1 yi1 zhao4 jing4 zi zhi1 jian4 zuo3 xia4 yan3 jian3 de xian4 you4 cu1 you4 hei1 yu3 you4 ce4 ming2 xian3 bu2 dui4 cheng1
可谁知纹完后她一照镜子只见左下眼睑的线又粗又黑与右侧明显不对称

 A11_102.wav
yi1 jin4 men2 wo3 bei4 jing1 dai1 le zhe4 hu4 ming2 jiao4 pang2 ji2 de lao3 nong2 shi4 kang4 mei3 yuan2 chao2 fu4 shang1 hui2 xiang1 de lao3 bing1 qi1 zi3 chang2 nian2 you3 bing4 jia1 tu2 si4 bi4 yi1 pin2 ru2 xi3
一进门我被惊呆了这户名叫庞吉的老农是抗美援朝负伤回乡的老兵妻子长年有病家徒四壁一贫如洗

 A11_103.wav
zou3 chu1 cun1 zi lao3 yuan3 lao3 yuan3 wo3 hai2 hui2 tou2 zhang1 wang4 na4 ge4 an1 ning2 tian2 jing4 de xiao3 yuan4 na4 ge4 shi3 wo3 zhong1 shen1 nan2 wang4 de xiao3 yuan4
走出村子老远老远我还回头张望那个安宁恬静的小院那个使我终身难忘的小院

 A11_104.wav
er4 yue4 si4 ri4 zhu4 jin4 xin1 xi1 men2 wai4 luo2 jia1 nian3 wang2 jia1 gang1 zhu1 zi4 qing1 wen2 xun4 te4 di4 cong2 dong1 men2 wai4 gan3 lai2 qing4 he4
二月四日住进新西门外罗家碾王家冈朱自清闻讯特地从东门外赶来庆贺

 A11_105.wav
dan1 wei4 bu2 shi4 wo3 lao3 die1 kai1 de ping2 shen2 me yao4 yi1 ci4 er4 ci4 zhao4 gu4 wo3 wo3 bu4 neng2 ba3 zi4 ji3 de bao1 fu2 wang3 xue2 xiao4 shuai3
单位不是我老爹开的凭什么要一次二次照顾我我不能把自己的包袱往学校甩

 A11_106.wav
dou1 yong4 cao3 mao4 huo4 ge1 bo zhou3 hu4 zhe wan3 lie4 lie4 qie ju1 chuan1 guo4 lan4 ni2 tang2 ban1 de yuan4 ba4 pao3 hui2 zi4 ji3 de su4 she4 qu4 le
都用草帽或胳膊肘护着碗趔趔趄趄穿过烂泥塘般的院坝跑回自己的宿舍去了

 A11_107.wav
xiang1 gang3 yan3 yi4 quan1 huan1 ying2 mao2 a1 min3 jia1 meng2 wu2 xian4 tai2 yu3 hua2 xing1 yi1 xie1 zhong4 da4 de yan3 chang4 huo2 dong4 dou1 yao1 qing3 ta1 chu1 chang3 you3 ji3 ci4 hai2 te4 yi4 an1 pai2 ya1 zhou4 yan3 chu1
香港演艺圈欢迎毛阿敏加盟无线台与华星一些重大的演唱活动都邀请她出场有几次还特意安排压轴演出

语音总数量: 13388

首先加载需要的python库

import osimport numpy as np
import scipy.io.wavfile as wav
import matplotlib.pyplot as plt
import tensorflow as tf

3.6 声学模型

在本实践中,选择使用深度全序列卷积神经网络(DFCNN,Deep Fully Convolutional NeuralNetwork)进行声学模型的建模。CNN早在2012年就被用于语音识别系统,但始终没有大的突破。主要的原因是其使用固定长度的帧拼接作为输入,无法看到足够长的语音上下文信息;另外一个缺陷将CNN视作一种特征提取器,因此所用的卷积层数很少,表达能力有限。

DFCNN直接将一句语音转化成一张图像作为输入,即先对每帧语音进行傅里叶变换,再将时间和频率作为图像的两个维度,然后通过非常多的卷积层和池化层的组合,对整句语音进行建模,输出单元直接与最终的识别结果(比如音节或者汉字)相对应。DFCNN 的原理是把语谱图看作带有特定模式的图像,其结构如下图所示。


图片42-1.png


图42 声学模型

下面从输入端、模型结构和输出端三个方面来阐述 DFCNN 的优势:

首先,从输入端来看,传统语音特征在傅里叶变换之后使用各种人工设计的滤波器组来提取特征,造成了频域上的信息损失,在高频区域的信息损失尤为明显,而且传统语音特征为了计算量的考虑必须采用非常大的帧移,无疑造成了时域上的信息损失,在说话人语速较快的时候表现得更为突出。因此 DFCNN 直接将语谱图作为输入,避免了频域和时域两个维度的信息损失,相比其他以传统语音特征作为输入的语音识别框架相比具有天然的优势。

其次,从模型结构来看,DFCNN 借鉴了图像识别中效果最好的网络配置,每个卷积层使用 3x3 的小卷积核,并在多个卷积层之后再加上池化层,这样大大增强了 CNN 的表达能力,与此同时,通过累积非常多的这种卷积池化层对,DFCNN 可以看到非常长的历史和未来信息,这就保证了 DFCNN 可以出色地表达语音的长时相关性,相比 RNN 或者 LSTM 网络结构在鲁棒性上更加出色。

最后,从输出端来看,DFCNN 比较灵活,可以方便地和其他建模方式融合。比如,本实践采用的 DFCNN 与连接时序分类模型(CTC,connectionist temporal classification)方案结合,以实现整个模型的端到端声学模型训练,且其包含的池化层等特殊结构可以使得以上端到端训练变得更加稳定。与传统的声学模型训练相比,采用CTC作为损失函数的声学模型训练,是一种完全端到端的声学模型训练,不需要预先对数据做对齐,只需要一个输入序列和一个输出序列即可以训练。这样就不需要对数据对齐和一一标注,并且CTC直接输出序列预测的概率,不需要外部的后处理。

下面来建立DFCNN声学模型,如下所示:

import kerasfrom keras.layers import Input, Conv2D, BatchNormalization, MaxPooling2Dfrom keras.layers import Reshape, Dense, Dropout, Lambdafrom keras.optimizers import Adamfrom keras import backend as Kfrom keras.models import Modelfrom tensorflow.contrib.training import HParams
#定义卷积层def conv2d(size):
    return Conv2D(size, (3,3), use_bias=True, activation='relu',
        padding='same', kernel_initializer='he_normal')
#定义BN层def norm(x):
    return BatchNormalization(axis=-1)(x)
#定义最大池化层def maxpool(x):
    return MaxPooling2D(pool_size=(2,2), strides=None, padding="valid")(x)
#定义dense层def dense(units, activation="relu"):
    return Dense(units, activation=activation, use_bias=True,
        kernel_initializer='he_normal')
#两个卷积层加一个最大池化层的组合def cnn_cell(size, x, pool=True):
    x = norm(conv2d(size)(x))
    x = norm(conv2d(size)(x))
    if pool:
        x = maxpool(x)
    return x
#CTC损失函数def ctc_lambda(args):
    labels, y_pred, input_length, label_length = args
    y_pred = y_pred[:, :, :]
    return K.ctc_batch_cost(labels, y_pred, input_length, label_length)
#组合声学模型class acoustic_model():
    def __init__(self, args):
        self.vocab_size = args.vocab_size
        self.learning_rate = args.learning_rate
        self.is_training = args.is_training
        self._model_init()
        if self.is_training:
            self._ctc_init()
            self.opt_init()
    def _model_init(self):
        self.inputs = Input(name='the_inputs', shape=(None, 200, 1))
        self.h1 = cnn_cell(32, self.inputs)
        self.h2 = cnn_cell(64, self.h1)
        self.h3 = cnn_cell(128, self.h2)
        self.h4 = cnn_cell(128, self.h3, pool=False)
        self.h5 = cnn_cell(128, self.h4, pool=False)
        # 200 / 8 * 128 = 3200
        self.h6 = Reshape((-1, 3200))(self.h5)
        self.h6 = Dropout(0.2)(self.h6)
        self.h7 = dense(256)(self.h6)
        self.h7 = Dropout(0.2)(self.h7)
        self.outputs = dense(self.vocab_size, activation='softmax')(self.h7)
        self.model = Model(inputs=self.inputs, outputs=self.outputs)

    def _ctc_init(self):
        self.labels = Input(name='the_labels', shape=[None], dtype='float32')
        self.input_length = Input(name='input_length', shape=[1], dtype='int64')
        self.label_length = Input(name='label_length', shape=[1], dtype='int64')
        self.loss_out = Lambda(ctc_lambda, output_shape=(1,), name='ctc')\
            ([self.labels, self.outputs, self.input_length, self.label_length])
        self.ctc_model = Model(inputs=[self.labels, self.inputs,
            self.input_length, self.label_length], outputs=self.loss_out)

    def opt_init(self):
        opt = Adam(lr = self.learning_rate, beta_1 = 0.9, beta_2 = 0.999, decay = 0.01, epsilon = 10e-8)
        self.ctc_model.compile(loss={'ctc': lambda y_true, output: output}, optimizer=opt)
def acoustic_model_hparams():
    params = HParams(
        vocab_size = 50,
        learning_rate = 0.0008,
        is_training = True)
    return params
print("打印声学模型结构")acoustic_model_args = acoustic_model_hparams()    acoustic = acoustic_model(acoustic_model_args)acoustic.ctc_model.summary()
Using TensorFlow backend.
WARNING:tensorflow:From /home/ma-user/anaconda3/envs/TensorFlow-1.13.1/lib/python3.6/site-packages/tensorflow/python/framework/op_def_library.py:263: colocate_with (from tensorflow.python.framework.ops) is deprecated and will be removed in a future version.
Instructions for updating:
Colocations handled automatically by placer.

打印声学模型结构,如下所示:

WARNING:tensorflow:From /home/ma-user/anaconda3/envs/TensorFlow-1.13.1/lib/python3.6/site-packages/keras/backend/tensorflow_backend.py:3445: calling dropout (from tensorflow.python.ops.nn_ops) with keep_prob is deprecated and will be removed in a future version.
Instructions for updating:
Please use `rate` instead of `keep_prob`. Rate should be set to `rate = 1 - keep_prob`.
WARNING:tensorflow:From /home/ma-user/anaconda3/envs/TensorFlow-1.13.1/lib/python3.6/site-packages/keras/backend/tensorflow_backend.py:4249: to_int32 (from tensorflow.python.ops.math_ops) is deprecated and will be removed in a future version.
Instructions for updating:
Use tf.cast instead.
WARNING:tensorflow:From /home/ma-user/anaconda3/envs/TensorFlow-1.13.1/lib/python3.6/site-packages/keras/backend/tensorflow_backend.py:4229: to_int64 (from tensorflow.python.ops.math_ops) is deprecated and will be removed in a future version.
Instructions for updating:
Use tf.cast instead.
__________________________________________________________________________________________________
Layer (type)                    Output Shape         Param #     Connected to                     
==================================================================================================
the_inputs (InputLayer)         (None, None, 200, 1) 0                                            
__________________________________________________________________________________________________
conv2d_1 (Conv2D)               (None, None, 200, 32 320         the_inputs[0][0]                 
__________________________________________________________________________________________________
batch_normalization_1 (BatchNor (None, None, 200, 32 128         conv2d_1[0][0]                   
__________________________________________________________________________________________________
conv2d_2 (Conv2D)               (None, None, 200, 32 9248        batch_normalization_1[0][0]      
__________________________________________________________________________________________________
batch_normalization_2 (BatchNor (None, None, 200, 32 128         conv2d_2[0][0]                   
__________________________________________________________________________________________________
max_pooling2d_1 (MaxPooling2D)  (None, None, 100, 32 0           batch_normalization_2[0][0]      
__________________________________________________________________________________________________
conv2d_3 (Conv2D)               (None, None, 100, 64 18496       max_pooling2d_1[0][0]            
__________________________________________________________________________________________________
batch_normalization_3 (BatchNor (None, None, 100, 64 256         conv2d_3[0][0]                   
__________________________________________________________________________________________________
conv2d_4 (Conv2D)               (None, None, 100, 64 36928       batch_normalization_3[0][0]      
__________________________________________________________________________________________________
batch_normalization_4 (BatchNor (None, None, 100, 64 256         conv2d_4[0][0]                   
__________________________________________________________________________________________________
max_pooling2d_2 (MaxPooling2D)  (None, None, 50, 64) 0           batch_normalization_4[0][0]      
__________________________________________________________________________________________________
conv2d_5 (Conv2D)               (None, None, 50, 128 73856       max_pooling2d_2[0][0]            
__________________________________________________________________________________________________
batch_normalization_5 (BatchNor (None, None, 50, 128 512         conv2d_5[0][0]                   
__________________________________________________________________________________________________
conv2d_6 (Conv2D)               (None, None, 50, 128 147584      batch_normalization_5[0][0]      
__________________________________________________________________________________________________
batch_normalization_6 (BatchNor (None, None, 50, 128 512         conv2d_6[0][0]                   
__________________________________________________________________________________________________
max_pooling2d_3 (MaxPooling2D)  (None, None, 25, 128 0           batch_normalization_6[0][0]      
__________________________________________________________________________________________________
conv2d_7 (Conv2D)               (None, None, 25, 128 147584      max_pooling2d_3[0][0]            
__________________________________________________________________________________________________
batch_normalization_7 (BatchNor (None, None, 25, 128 512         conv2d_7[0][0]                   
__________________________________________________________________________________________________
conv2d_8 (Conv2D)               (None, None, 25, 128 147584      batch_normalization_7[0][0]      
__________________________________________________________________________________________________
batch_normalization_8 (BatchNor (None, None, 25, 128 512         conv2d_8[0][0]                   
__________________________________________________________________________________________________
conv2d_9 (Conv2D)               (None, None, 25, 128 147584      batch_normalization_8[0][0]      
__________________________________________________________________________________________________
batch_normalization_9 (BatchNor (None, None, 25, 128 512         conv2d_9[0][0]                   
__________________________________________________________________________________________________
conv2d_10 (Conv2D)              (None, None, 25, 128 147584      batch_normalization_9[0][0]      
__________________________________________________________________________________________________
batch_normalization_10 (BatchNo (None, None, 25, 128 512         conv2d_10[0][0]                  
__________________________________________________________________________________________________
reshape_1 (Reshape)             (None, None, 3200)   0           batch_normalization_10[0][0]     
__________________________________________________________________________________________________
dropout_1 (Dropout)             (None, None, 3200)   0           reshape_1[0][0]                  
__________________________________________________________________________________________________
dense_1 (Dense)                 (None, None, 256)    819456      dropout_1[0][0]                  
__________________________________________________________________________________________________
dropout_2 (Dropout)             (None, None, 256)    0           dense_1[0][0]                    
__________________________________________________________________________________________________
the_labels (InputLayer)         (None, None)         0                                            
__________________________________________________________________________________________________
dense_2 (Dense)                 (None, None, 50)     12850       dropout_2[0][0]                  
__________________________________________________________________________________________________
input_length (InputLayer)       (None, 1)            0                                            
__________________________________________________________________________________________________
label_length (InputLayer)       (None, 1)            0                                            
__________________________________________________________________________________________________
ctc (Lambda)                    (None, 1)            0           the_labels[0][0]                 
                                                                 dense_2[0][0]                    
                                                                 input_length[0][0]               
                                                                 label_length[0][0]               
==================================================================================================
Total params: 1,712,914
Trainable params: 1,710,994
Non-trainable params: 1,920
__________________________________________________________________________________________________

获取数据类,如下所示:

from scipy.fftpack import fft
# 获取信号的时频图def compute_fbank(file):
    x=np.linspace(0, 400 - 1, 400, dtype = np.int64)
    w = 0.54 - 0.46 * np.cos(2 * np.pi * (x) / (400 - 1) )
    fs, wavsignal = wav.read(file)
    time_window = 25
    window_length = fs / 1000 * time_window
    wav_arr = np.array(wavsignal)
    wav_length = len(wavsignal)
    range0_end = int(len(wavsignal)/fs*1000 - time_window) // 10
    data_input = np.zeros((range0_end, 200), dtype = np.float)
    data_line = np.zeros((1, 400), dtype = np.float)
    for i in range(0, range0_end):
        p_start = i * 160
        p_end = p_start + 400
        data_line = wav_arr[p_start:p_end]    
        data_line = data_line * w
        data_line = np.abs(fft(data_line))
        data_input[i]=data_line[0:200]
    data_input = np.log(data_input + 1)
    return data_input
 
class get_data():
    def __init__(self, args):
        self.data_path = args.data_path        
        self.data_length = args.data_length
        self.batch_size = args.batch_size
        self.source_init()

    def source_init(self):
        self.wav_lst = []
        self.pin_lst = []
        self.han_lst = []
        with open('speech_recognition/data.txt', 'r', encoding='utf8') as f:
            data = f.readlines()
        for line in data:
            wav_file, pin, han = line.split('\t')
            self.wav_lst.append(wav_file)
            self.pin_lst.append(pin.split(' '))
            self.han_lst.append(han.strip('\n'))
        if self.data_length:
            self.wav_lst = self.wav_lst[:self.data_length]
            self.pin_lst = self.pin_lst[:self.data_length]
            self.han_lst = self.han_lst[:self.data_length]
        self.acoustic_vocab = self.acoustic_model_vocab(self.pin_lst)
        self.pin_vocab = self.language_model_pin_vocab(self.pin_lst)
        self.han_vocab = self.language_model_han_vocab(self.han_lst)

    def get_acoustic_model_batch(self):
        _list = [i for i in range(len(self.wav_lst))]
        while 1:
            for i in range(len(self.wav_lst) // self.batch_size):
                wav_data_lst = []
                label_data_lst = []
                begin = i * self.batch_size
                end = begin + self.batch_size
                sub_list = _list[begin:end]
                for index in sub_list:
                    fbank = compute_fbank(self.data_path + self.wav_lst[index])
                    pad_fbank = np.zeros((fbank.shape[0] // 8 * 8 + 8, fbank.shape[1]))
                    pad_fbank[:fbank.shape[0], :] = fbank
                    label = self.pin2id(self.pin_lst[index], self.acoustic_vocab)
                    label_ctc_len = self.ctc_len(label)
                    if pad_fbank.shape[0] // 8 >= label_ctc_len:
                        wav_data_lst.append(pad_fbank)
                        label_data_lst.append(label)
                pad_wav_data, input_length = self.wav_padding(wav_data_lst)
                pad_label_data, label_length = self.label_padding(label_data_lst)
                inputs = {'the_inputs': pad_wav_data,
                          'the_labels': pad_label_data,
                          'input_length': input_length,
                          'label_length': label_length,
                          }
                outputs = {'ctc': np.zeros(pad_wav_data.shape[0], )}
                yield inputs, outputs

    def get_language_model_batch(self):
        batch_num = len(self.pin_lst) // self.batch_size
        for k in range(batch_num):
            begin = k * self.batch_size
            end = begin + self.batch_size
            input_batch = self.pin_lst[begin:end]
            label_batch = self.han_lst[begin:end]
            max_len = max([len(line) for line in input_batch])
            input_batch = np.array(
                [self.pin2id(line, self.pin_vocab) + [0] * (max_len - len(line)) for line in input_batch])
            label_batch = np.array(
                [self.han2id(line, self.han_vocab) + [0] * (max_len - len(line)) for line in label_batch])
            yield input_batch, label_batch

    def pin2id(self, line, vocab):
        return [vocab.index(pin) for pin in line]

    def han2id(self, line, vocab):
        return [vocab.index(han) for han in line]
    def wav_padding(self, wav_data_lst):
        wav_lens = [len(data) for data in wav_data_lst]
        wav_max_len = max(wav_lens)
        wav_lens = np.array([leng // 8 for leng in wav_lens])
        new_wav_data_lst = np.zeros((len(wav_data_lst), wav_max_len, 200, 1))
        for i in range(len(wav_data_lst)):
            new_wav_data_lst[i, :wav_data_lst[i].shape[0], :, 0] = wav_data_lst[i]
        return new_wav_data_lst, wav_lens

    def label_padding(self, label_data_lst):
        label_lens = np.array([len(label) for label in label_data_lst])
        max_label_len = max(label_lens)
        new_label_data_lst = np.zeros((len(label_data_lst), max_label_len))
        for i in range(len(label_data_lst)):
            new_label_data_lst[i][:len(label_data_lst[i])] = label_data_lst[i]
        return new_label_data_lst, label_lens

    def acoustic_model_vocab(self, data):
        vocab = []
        for line in data:
            line = line
            for pin in line:
                if pin not in vocab:
                    vocab.append(pin)
        vocab.append('_')
        return vocab

    def language_model_pin_vocab(self, data):
        vocab = ['<PAD>']
        for line in data:
            for pin in line:
                if pin not in vocab:
                    vocab.append(pin)
        return vocab

    def language_model_han_vocab(self, data):
        vocab = ['<PAD>']
        for line in data:
            line = ''.join(line.split(' '))
            for han in line:
                if han not in vocab:
                    vocab.append(han)
        return vocab

    def ctc_len(self, label):
        add_len = 0
        label_len = len(label)
        for i in range(label_len - 1):
            if label[i] == label[i + 1]:
                add_len += 1
        return label_len + add_len

3.7 声学模型训练

首先,准备训练参数及数据,为了本示例演示效果,将参数batch_size在此仅设置为1,参数data_length在此仅设置为20。若进行完整训练,则应注释data_args.data_length = 20,并调高batch_size。如下所示:

def data_hparams():
    params = HParams(
        data_path = './speech_recognition/data/', #d数据路径
        batch_size = 1,      #批尺寸
        data_length = None,   #长度
    )
    return params
data_args = data_hparams()data_args.data_length = 20 # 重新训练需要注释该行train_data = get_data(data_args)
acoustic_model_args = acoustic_model_hparams()acoustic_model_args.vocab_size = len(train_data.acoustic_vocab)acoustic = acoustic_model(acoustic_model_args)
print('声学模型参数:')print(acoustic_model_args)
if os.path.exists('/speech_recognition/acoustic_model/model.h5'):
    print('加载声学模型')
    acoustic.ctc_model.load_weights('./speech_recognition/acoustic_model/model.h5')

声学模型参数:

[('is_training', True), ('learning_rate', 0.0008), ('vocab_size', 353)]

训练声学模型

epochs = 20batch_num = len(train_data.wav_lst) // train_data.batch_sizeprint("训练轮数epochs:",epochs)print("批数量batch_num:",batch_num)
print("开始训练!")for k in range(epochs):
    print('第', k+1, '个epoch')
    batch = train_data.get_acoustic_model_batch()
    acoustic.ctc_model.fit_generator(batch, steps_per_epoch=batch_num, epochs=1)
print("\n训练完成,保存模型")acoustic.ctc_model.save_weights('./speech_recognition/acoustic_model/model.h5')

训练轮数epochs: 20

批数量batch_num: 20

开始训练!

第 1 个epoch

WARNING:tensorflow:From /home/ma-user/anaconda3/envs/TensorFlow-1.13.1/lib/python3.6/site-packages/tensorflow/python/ops/math_grad.py:102: div (from tensorflow.python.ops.math_ops) is deprecated and will be removed in a future version.
Instructions for updating:
Deprecated in favor of operator or tf.math.divide.
Epoch 1/1
20/20 [==============================] - 15s 741ms/step - loss: 292.0404
第 2 个epoch
Epoch 1/1
20/20 [==============================] - 1s 26ms/step - loss: 195.0730
第 3 个epoch
Epoch 1/1
20/20 [==============================] - 1s 27ms/step - loss: 179.2059
第 4 个epoch
Epoch 1/1
20/20 [==============================] - 1s 28ms/step - loss: 156.6438
第 5 个epoch
Epoch 1/1
20/20 [==============================] - 1s 26ms/step - loss: 125.4767
第 6 个epoch
Epoch 1/1
20/20 [==============================] - 1s 28ms/step - loss: 94.7194
第 7 个epoch
Epoch 1/1
20/20 [==============================] - 1s 27ms/step - loss: 65.2171
第 8 个epoch
Epoch 1/1
20/20 [==============================] - 1s 27ms/step - loss: 44.6281
第 9 个epoch
Epoch 1/1
20/20 [==============================] - 1s 26ms/step - loss: 25.8198
第 10 个epoch
Epoch 1/1
20/20 [==============================] - 1s 27ms/step - loss: 16.2043
第 11 个epoch
Epoch 1/1
20/20 [==============================] - 1s 27ms/step - loss: 8.9479
第 12 个epoch
Epoch 1/1
20/20 [==============================] - 1s 26ms/step - loss: 2.3711
第 13 个epoch
Epoch 1/1
20/20 [==============================] - 1s 26ms/step - loss: 1.5653
第 14 个epoch
Epoch 1/1
20/20 [==============================] - 0s 25ms/step - loss: 1.0379
第 15 个epoch
Epoch 1/1
20/20 [==============================] - 1s 27ms/step - loss: 0.6209
第 16 个epoch
Epoch 1/1
20/20 [==============================] - 1s 29ms/step - loss: 0.5799
第 17 个epoch
Epoch 1/1
20/20 [==============================] - 1s 26ms/step - loss: 0.5872
第 18 个epoch
Epoch 1/1
20/20 [==============================] - 1s 26ms/step - loss: 0.6505
第 19 个epoch
Epoch 1/1
20/20 [==============================] - 1s 25ms/step - loss: 0.5772
第 20 个epoch
Epoch 1/1
20/20 [==============================] - 1s 25ms/step - loss: 0.4246

 训练完成,保存模型

3.8 语言模型

在本实践中,选择使用 Transformer 结构进行语言模型的建模。Transformer 是完全基于注意力机制(attention mechanism)的网络框架,attention 来自于论文《attention is all you need》。 一个序列每个字符对其上下文字符的影响作用都不同,每个字对序列的语义信息贡献也不同,可以通过一种机制将原输入序列中字符向量通过加权融合序列中所有字符的语义向量信息来产生新的向量,即增强了原语义信息。其结构如下图所示。


图片43.png


图43 结构图

其中左半部分是编码器 encoder 右半部分是解码器 decoder。在本实践中,仅需要搭建左侧 encoder 结构即可。encoder的详细结构为: 由N=6个相同的 layers 组成, 每一层包含两个 sub-layers. 第一个 sub-layer 就是多头注意力层(multi-head attention layer)然后是一个简单的全连接层。 其中每个 sub-layer 都加了residual connection(残差连接)和 normalisation(归一化)。

下面来具体构建一个基于 Transformer 的语言模型。定义归一化 normalize层,如下所示:

def normalize(inputs,
              epsilon = 1e-8,
              scope="ln",
              reuse=None):
    with tf.variable_scope(scope, reuse=reuse):
        inputs_shape = inputs.get_shape()
        params_shape = inputs_shape[-1:]
        mean, variance = tf.nn.moments(inputs, [-1], keep_dims=True)
        beta= tf.Variable(tf.zeros(params_shape))
        gamma = tf.Variable(tf.ones(params_shape))
        normalized = (inputs - mean) / ( (variance + epsilon) ** (.5) )
        outputs = gamma * normalized + beta
    return outputs

定义嵌入层 embedding。即位置向量,将每个位置编号,然后每个编号对应一个向量,通过结合位置向量和词向量,就给每个词都引入了一定的位置信息,以便Attention分辨出不同位置的词。

def embedding(inputs,
              vocab_size,
              num_units,
              zero_pad=True,
              scale=True,
              scope="embedding",
              reuse=None):
    with tf.variable_scope(scope, reuse=reuse):
        lookup_table = tf.get_variable('lookup_table',
                                       dtype=tf.float32,
                                       shape=[vocab_size, num_units],
                                       initializer=tf.contrib.layers.xavier_initializer())
        if zero_pad:
            lookup_table = tf.concat((tf.zeros(shape=[1, num_units]),
                                      lookup_table[1:, :]), 0)
        outputs = tf.nn.embedding_lookup(lookup_table, inputs)
        if scale:
            outputs = outputs * (num_units ** 0.5)
    return outputs

多头注意力层(multi-head attention layer)。要了解多头注意力层,首先要知道点乘注意力(Scaled Dot-Product Attention)。Attention 有三个输入(querys,keys,values),有一个输出。选择三个输入是考虑到模型的通用性,输出是所有 value 的加权求和。value 的权重来自于 query 和 keys 的乘积,经过一个 softmax 之后得到,Scaled Dot-Product Attention 的公式及结构如下图所示。

 


图片44.png


图44 结构图

Multi-Head Attention 就是对输入 K,V,Q 分别进行 H 次线性变换,然后把Scaled Dot-Product Attention的过程做 H 次,把输出的结果做 concat 结合,即为输出。Multi-Head Attention 的公式及结构如下图所示。

 图片45.png

图45 结构图

 

定义 multi-head attention层,如下所示:

def multihead_attention(emb,
                        queries,
                        keys,
                        num_units=None,
                        num_heads=8,
                        dropout_rate=0,
                        is_training=True,
                        causality=False,
                        scope="multihead_attention",
                        reuse=None):
    with tf.variable_scope(scope, reuse=reuse):
        if num_units is None:
            num_units = queries.get_shape().as_list[-1]
        Q = tf.layers.dense(queries, num_units, activation=tf.nn.relu) # (N, T_q, C)
        K = tf.layers.dense(keys, num_units, activation=tf.nn.relu) # (N, T_k, C)
        V = tf.layers.dense(keys, num_units, activation=tf.nn.relu) # (N, T_k, C)
        Q_ = tf.concat(tf.split(Q, num_heads, axis=2), axis=0) # (h*N, T_q, C/h)
        K_ = tf.concat(tf.split(K, num_heads, axis=2), axis=0) # (h*N, T_k, C/h)
        V_ = tf.concat(tf.split(V, num_heads, axis=2), axis=0) # (h*N, T_k, C/h)
        outputs = tf.matmul(Q_, tf.transpose(K_, [0, 2, 1])) # (h*N, T_q, T_k)
        outputs = outputs / (K_.get_shape().as_list()[-1] ** 0.5)
        key_masks = tf.sign(tf.abs(tf.reduce_sum(emb, axis=-1))) # (N, T_k)
        key_masks = tf.tile(key_masks, [num_heads, 1]) # (h*N, T_k)
        key_masks = tf.tile(tf.expand_dims(key_masks, 1), [1, tf.shape(queries)[1], 1]) # (h*N, T_q, T_k)
        paddings = tf.ones_like(outputs)*(-2**32+1)
        outputs = tf.where(tf.equal(key_masks, 0), paddings, outputs) # (h*N, T_q, T_k)
        if causality:
            diag_vals = tf.ones_like(outputs[0, :, :]) # (T_q, T_k)
            tril = tf.contrib.linalg.LinearOperatorTriL(diag_vals).to_dense() # (T_q, T_k)
            masks = tf.tile(tf.expand_dims(tril, 0), [tf.shape(outputs)[0], 1, 1]) # (h*N, T_q, T_k)
            paddings = tf.ones_like(masks)*(-2**32+1)
            outputs = tf.where(tf.equal(masks, 0), paddings, outputs) # (h*N, T_q, T_k)
        outputs = tf.nn.softmax(outputs) # (h*N, T_q, T_k)
        query_masks = tf.sign(tf.abs(tf.reduce_sum(emb, axis=-1))) # (N, T_q)
        query_masks = tf.tile(query_masks, [num_heads, 1]) # (h*N, T_q)
        query_masks = tf.tile(tf.expand_dims(query_masks, -1), [1, 1, tf.shape(keys)[1]]) # (h*N, T_q, T_k)
        outputs *= query_masks # broadcasting. (N, T_q, C)
        outputs = tf.layers.dropout(outputs, rate=dropout_rate, training=tf.convert_to_tensor(is_training))
        outputs = tf.matmul(outputs, V_) # ( h*N, T_q, C/h)
        outputs = tf.concat(tf.split(outputs, num_heads, axis=0), axis=2 ) # (N, T_q, C)
        outputs += queries
        outputs = normalize(outputs) # (N, T_q, C)
    return outputs

定义 feedforward层,两层全连接层,用卷积模拟加速运算,并添加残差结构,如下所示:

def feedforward(inputs,
                num_units=[2048, 512],
                scope="multihead_attention",
                reuse=None):
    with tf.variable_scope(scope, reuse=reuse):
        params = {"inputs": inputs, "filters": num_units[0], "kernel_size": 1,
                  "activation": tf.nn.relu, "use_bias": True}
        outputs = tf.layers.conv1d(**params)
        params = {"inputs": outputs, "filters": num_units[1], "kernel_size": 1,
                  "activation": None, "use_bias": True}
        outputs = tf.layers.conv1d(**params)
        outputs += inputs
        outputs = normalize(outputs)
    return outputs

定义 label_smoothing层,如下所示:

def label_smoothing(inputs, epsilon=0.1):
    K = inputs.get_shape().as_list()[-1] # number of channels
    return ((1-epsilon) * inputs) + (epsilon / K)

下面可以将上述层组合,建立完整的语言模型:

#组合语言模型class language_model():
    def __init__(self, arg):
        self.graph = tf.Graph()
        with self.graph.as_default():
            self.is_training = arg.is_training
            self.hidden_units = arg.hidden_units
            self.input_vocab_size = arg.input_vocab_size
            self.label_vocab_size = arg.label_vocab_size
            self.num_heads = arg.num_heads
            self.num_blocks = arg.num_blocks
            self.max_length = arg.max_length
            self.learning_rate = arg.learning_rate
            self.dropout_rate = arg.dropout_rate
            self.x = tf.placeholder(tf.int32, shape=(None, None))
            self.y = tf.placeholder(tf.int32, shape=(None, None))
            self.emb = embedding(self.x, vocab_size=self.input_vocab_size, num_units=self.hidden_units, scale=True, scope="enc_embed")
            self.enc = self.emb + embedding(tf.tile(tf.expand_dims(tf.range(tf.shape(self.x)[1]), 0), [tf.shape(self.x)[0], 1]),
                                        vocab_size=self.max_length,num_units=self.hidden_units, zero_pad=False, scale=False,scope="enc_pe")
            self.enc = tf.layers.dropout(self.enc,
                                        rate=self.dropout_rate,
                                        training=tf.convert_to_tensor(self.is_training))
            for i in range(self.num_blocks):
                with tf.variable_scope("num_blocks_{}".format(i)):
                    self.enc = multihead_attention(emb = self.emb,
                                                    queries=self.enc,
                                                    keys=self.enc,
                                                    num_units=self.hidden_units,
                                                    num_heads=self.num_heads,
                                                    dropout_rate=self.dropout_rate,
                                                    is_training=self.is_training,
                                                    causality=False)
            self.outputs = feedforward(self.enc, num_units=[4*self.hidden_units, self.hidden_units])
            self.logits = tf.layers.dense(self.outputs, self.label_vocab_size)
            self.preds = tf.to_int32(tf.argmax(self.logits, axis=-1))
            self.istarget = tf.to_float(tf.not_equal(self.y, 0))
            self.acc = tf.reduce_sum(tf.to_float(tf.equal(self.preds, self.y))*self.istarget)/ (tf.reduce_sum(self.istarget))
            tf.summary.scalar('acc', self.acc)
            if self.is_training:  
                self.y_smoothed = label_smoothing(tf.one_hot(self.y, depth=self.label_vocab_size))
                self.loss = tf.nn.softmax_cross_entropy_with_logits_v2(logits=self.logits, labels=self.y_smoothed)
                self.mean_loss = tf.reduce_sum(self.loss*self.istarget) / (tf.reduce_sum(self.istarget))
                self.global_step = tf.Variable(0, name='global_step', trainable=False)
                self.optimizer = tf.train.AdamOptimizer(learning_rate=self.learning_rate, beta1=0.9, beta2=0.98, epsilon=1e-8)
                self.train_op = self.optimizer.minimize(self.mean_loss, global_step=self.global_step)
                tf.summary.scalar('mean_loss', self.mean_loss)
                self.merged = tf.summary.merge_all()
print('语音模型建立完成!')

语音模型建立完成!

语言模型训练

准备训练参数及数据

def language_model_hparams():
    params = HParams(
        num_heads = 8,
        num_blocks = 6,
        input_vocab_size = 50,
        label_vocab_size = 50,
        max_length = 100,
        hidden_units = 512,
        dropout_rate = 0.2,
        learning_rate = 0.0003,
        is_training = True)
    return params
language_model_args = language_model_hparams()language_model_args.input_vocab_size = len(train_data.pin_vocab)language_model_args.label_vocab_size = len(train_data.han_vocab)language = language_model(language_model_args)
print('语言模型参数:')print(language_model_args)
WARNING:tensorflow:From <ipython-input-13-89328ea9c47f>:23: dropout (from tensorflow.python.layers.core) is deprecated and will be removed in a future version.
Instructions for updating:
Use keras.layers.dropout instead.
WARNING:tensorflow:From <ipython-input-10-c0da1b61dd9f>:15: dense (from tensorflow.python.layers.core) is deprecated and will be removed in a future version.
Instructions for updating:
Use keras.layers.dense instead.
WARNING:tensorflow:From <ipython-input-11-0657c4b451a0>:8: conv1d (from tensorflow.python.layers.convolutional) is deprecated and will be removed in a future version.
Instructions for updating:
Use keras.layers.conv1d instead.
WARNING:tensorflow:From <ipython-input-13-89328ea9c47f>:40: to_float (from tensorflow.python.ops.math_ops) is deprecated and will be removed in a future version.
Instructions for updating:
Use tf.cast instead.

语言模型参数:

[('dropout_rate', 0.2), ('hidden_units', 512), ('input_vocab_size', 353), ('is_training', True), ('label_vocab_size', 415), ('learning_rate', 0.0003), ('max_length', 100), ('num_blocks', 6), ('num_heads', 8)]

训练语言模型,如下所示:

epochs = 20print("训练轮数epochs:",epochs)
print("\n开始训练!")with language.graph.as_default():
    saver =tf.train.Saver()with tf.Session(graph=language.graph) as sess:
    merged = tf.summary.merge_all()
    sess.run(tf.global_variables_initializer())
    if os.path.exists('/speech_recognition/language_model/model.meta'):
        print('加载语言模型')
        saver.restore(sess, './speech_recognition/language_model/model')
    writer = tf.summary.FileWriter('./speech_recognition/language_model/tensorboard', tf.get_default_graph())
    for k in range(epochs):
        total_loss = 0
        batch = train_data.get_language_model_batch()
        for i in range(batch_num):
            input_batch, label_batch = next(batch)
            feed = {language.x: input_batch, language.y: label_batch}
            cost,_ = sess.run([language.mean_loss,language.train_op], feed_dict=feed)
            total_loss += cost
            if (k * batch_num + i) % 10 == 0:
                rs=sess.run(merged, feed_dict=feed)
                writer.add_summary(rs, k * batch_num + i)
        print('第', k+1, '个 epoch', ': average loss = ', total_loss/batch_num)
    print("\n训练完成,保存模型")
    saver.save(sess, './speech_recognition/language_model/model')
    writer.close()

训练轮数epochs: 20

开始训练!

第 1 个 epoch : average loss =  6.218134140968322
第 2 个 epoch : average loss =  3.2289454698562623
第 3 个 epoch : average loss =  1.8876620471477508
第 4 个 epoch : average loss =  1.291277027130127
第 5 个 epoch : average loss =  1.1049616515636445
第 6 个 epoch : average loss =  1.0946208953857421
第 7 个 epoch : average loss =  1.082367479801178
第 8 个 epoch : average loss =  1.0920936942100525
第 9 个 epoch : average loss =  1.0675474047660827
第 10 个 epoch : average loss =  1.0946592748165132
第 11 个 epoch : average loss =  1.1080005645751954
第 12 个 epoch : average loss =  1.0991867125034331
第 13 个 epoch : average loss =  1.088491427898407
第 14 个 epoch : average loss =  1.10106263756752
第 15 个 epoch : average loss =  1.0777182012796402
第 16 个 epoch : average loss =  1.0825719654560089
第 17 个 epoch : average loss =  1.0695657193660737
第 18 个 epoch : average loss =  1.0671538591384888
第 19 个 epoch : average loss =  1.04828782081604
第 20 个 epoch : average loss =  1.0553287982940673

训练完成,保存模型。

3.9 模型测试

准备解码所需字典,需和训练一致,也可以将字典保存到本地,直接进行读取。

data_args = data_hparams()data_args.data_length = 20 # 重新训练需要注释该行train_data = get_data(data_args)

准备测试所需数据,不必和训练数据一致。

在本实践中,由于教学原因演示数据集及模型参数均较小,故不区分训练集和测试集。

test_data = get_data(data_args)acoustic_model_batch = test_data.get_acoustic_model_batch()language_model_batch = test_data.get_language_model_batch()

加载训练好的声学模型

acoustic_model_args = acoustic_model_hparams()
acoustic_model_args.vocab_size = len(train_data.acoustic_vocab)
acoustic = acoustic_model(acoustic_model_args)
acoustic.ctc_model.summary()
acoustic.ctc_model.load_weights('./speech_recognition/acoustic_model/model.h5')
print('声学模型参数:')
print(acoustic_model_args)
print('\n加载声学模型完成!')
__________________________________________________________________________________________________
Layer (type)                    Output Shape         Param #     Connected to                     
==================================================================================================
the_inputs (InputLayer)         (None, None, 200, 1) 0                                            
__________________________________________________________________________________________________
conv2d_21 (Conv2D)              (None, None, 200, 32 320         the_inputs[0][0]                 
__________________________________________________________________________________________________
batch_normalization_21 (BatchNo (None, None, 200, 32 128         conv2d_21[0][0]                  
__________________________________________________________________________________________________
conv2d_22 (Conv2D)              (None, None, 200, 32 9248        batch_normalization_21[0][0]     
__________________________________________________________________________________________________
batch_normalization_22 (BatchNo (None, None, 200, 32 128         conv2d_22[0][0]                  
__________________________________________________________________________________________________
max_pooling2d_7 (MaxPooling2D)  (None, None, 100, 32 0           batch_normalization_22[0][0]     
__________________________________________________________________________________________________
conv2d_23 (Conv2D)              (None, None, 100, 64 18496       max_pooling2d_7[0][0]            
__________________________________________________________________________________________________
batch_normalization_23 (BatchNo (None, None, 100, 64 256         conv2d_23[0][0]                  
__________________________________________________________________________________________________
conv2d_24 (Conv2D)              (None, None, 100, 64 36928       batch_normalization_23[0][0]     
__________________________________________________________________________________________________
batch_normalization_24 (BatchNo (None, None, 100, 64 256         conv2d_24[0][0]                  
__________________________________________________________________________________________________
max_pooling2d_8 (MaxPooling2D)  (None, None, 50, 64) 0           batch_normalization_24[0][0]     
__________________________________________________________________________________________________
conv2d_25 (Conv2D)              (None, None, 50, 128 73856       max_pooling2d_8[0][0]            
__________________________________________________________________________________________________
batch_normalization_25 (BatchNo (None, None, 50, 128 512         conv2d_25[0][0]                  
__________________________________________________________________________________________________
conv2d_26 (Conv2D)              (None, None, 50, 128 147584      batch_normalization_25[0][0]     
__________________________________________________________________________________________________
batch_normalization_26 (BatchNo (None, None, 50, 128 512         conv2d_26[0][0]                  
__________________________________________________________________________________________________
max_pooling2d_9 (MaxPooling2D)  (None, None, 25, 128 0           batch_normalization_26[0][0]     
__________________________________________________________________________________________________
conv2d_27 (Conv2D)              (None, None, 25, 128 147584      max_pooling2d_9[0][0]            
__________________________________________________________________________________________________
batch_normalization_27 (BatchNo (None, None, 25, 128 512         conv2d_27[0][0]                  
__________________________________________________________________________________________________
conv2d_28 (Conv2D)              (None, None, 25, 128 147584      batch_normalization_27[0][0]     
__________________________________________________________________________________________________
batch_normalization_28 (BatchNo (None, None, 25, 128 512         conv2d_28[0][0]                  
__________________________________________________________________________________________________
conv2d_29 (Conv2D)              (None, None, 25, 128 147584      batch_normalization_28[0][0]     
__________________________________________________________________________________________________
batch_normalization_29 (BatchNo (None, None, 25, 128 512         conv2d_29[0][0]                  
__________________________________________________________________________________________________
conv2d_30 (Conv2D)              (None, None, 25, 128 147584      batch_normalization_29[0][0]     
__________________________________________________________________________________________________
batch_normalization_30 (BatchNo (None, None, 25, 128 512         conv2d_30[0][0]                  
__________________________________________________________________________________________________
reshape_3 (Reshape)             (None, None, 3200)   0           batch_normalization_30[0][0]     
__________________________________________________________________________________________________
dropout_5 (Dropout)             (None, None, 3200)   0           reshape_3[0][0]                  
__________________________________________________________________________________________________
dense_5 (Dense)                 (None, None, 256)    819456      dropout_5[0][0]                  
__________________________________________________________________________________________________
dropout_6 (Dropout)             (None, None, 256)    0           dense_5[0][0]                    
__________________________________________________________________________________________________
the_labels (InputLayer)         (None, None)         0                                            
__________________________________________________________________________________________________
dense_6 (Dense)                 (None, None, 353)    90721       dropout_6[0][0]                  
__________________________________________________________________________________________________
input_length (InputLayer)       (None, 1)            0                                            
__________________________________________________________________________________________________
label_length (InputLayer)       (None, 1)            0                                            
__________________________________________________________________________________________________
ctc (Lambda)                    (None, 1)            0           the_labels[0][0]                 
                                                                 dense_6[0][0]                    
                                                                 input_length[0][0]               
                                                                 label_length[0][0]               
==================================================================================================
Total params: 1,790,785
Trainable params: 1,788,865
Non-trainable params: 1,920
__________________________________________________________________________________________________

Total params: 1,790,785

Trainable params: 1,788,865

Non-trainable params: 1,920

声学模型参数:

[('is_training', True), ('learning_rate', 0.0008), ('vocab_size', 353)]

加载声学模型完成!

加载训练好的语言模型。

language_model_args = language_model_hparams()language_model_args.input_vocab_size = len(train_data.pin_vocab)language_model_args.label_vocab_size = len(train_data.han_vocab)language = language_model(language_model_args)sess = tf.Session(graph=language.graph)with language.graph.as_default():
    saver =tf.train.Saver()with sess.as_default():
    saver.restore(sess, './speech_recognition/language_model/model')
print('语言模型参数:')print(language_model_args)print('\n加载语言模型完成!')
WARNING:tensorflow:From /home/ma-user/anaconda3/envs/TensorFlow-1.13.1/lib/python3.6/site-packages/tensorflow/python/training/saver.py:1266: checkpoint_exists (from tensorflow.python.training.checkpoint_management) is deprecated and will be removed in a future version.
Instructions for updating:
Use standard file APIs to check for files with this prefix.

语言模型参数:

[('dropout_rate', 0.2), ('hidden_units', 512), ('input_vocab_size', 353), ('is_training', True), ('label_vocab_size', 415), ('learning_rate', 0.0003), ('max_length', 100), ('num_blocks', 6), ('num_heads', 8)]

加载语言模型完成!

3.10 定义解码器

接下来定义解码器,如下所示:

def decode_ctc(num_result, num2word):
    result = num_result[:, :, :]
    in_len = np.zeros((1), dtype = np.int32)
    in_len[0] = result.shape[1]
    t = K.ctc_decode(result, in_len, greedy = True, beam_width=10, top_paths=1)
    v = K.get_value(t[0][0])
    v = v[0]
    text = []
    for i in v:
        text.append(num2word[i])
    return v, text

使用搭建好的语音识别系统进行测试,在这里显示出10条语音示例的原文拼音及识别结果、原文汉字及识别结果。如下所示:

for i in range(10):
    print('\n示例', i+1)
    # 载入训练好的模型,并进行识别
    inputs, outputs = next(acoustic_model_batch)
    x = inputs['the_inputs']
    y = inputs['the_labels'][0]
    result = acoustic.model.predict(x, steps=1)
    # 将数字结果转化为文本结果
    _, text = decode_ctc(result, train_data.acoustic_vocab)
    text = ' '.join(text)
    print('原文拼音:', ' '.join([train_data.acoustic_vocab[int(i)] for i in y]))
    print('识别结果:', text)
    with sess.as_default():
        try:
            _, y = next(language_model_batch)
            text = text.strip('\n').split(' ')
            x = np.array([train_data.pin_vocab.index(pin) for pin in text])
            x = x.reshape(1, -1)
            preds = sess.run(language.preds, {language.x: x})
            got = ''.join(train_data.han_vocab[idx] for idx in preds[0])
            print('原文汉字:', ''.join(train_data.han_vocab[idx] for idx in y[0]))
            print('识别结果:', got)
        except StopIteration:
            breaksess.close()

示例 1

WARNING:tensorflow:From /home/ma-user/anaconda3/envs/TensorFlow-1.13.1/lib/python3.6/site-packages/keras/backend/tensorflow_backend.py:4303: sparse_to_dense (from tensorflow.python.ops.sparse_ops) is deprecated and will be removed in a future version.
Instructions for updating:

Create a `tf.sparse.SparseTensor` and use `tf.sparse.to_dense` instead.
原文拼音: lv4 shi4 yang2 chun1 yan1 jing3 da4 kuai4 wen2 zhang1 de di3 se4 si4 yue4 de lin2 luan2 geng4 shi4 lv4 de2 xian1 huo2 xiu4 mei4 shi1 yi4 ang4 ran2
识别结果: lv4 shi4 yang2 chun1 yan1 jing3 da4 kuai4 wen2 zhang1 de di3 se4 si4 yue4 de lin2 luan2 geng4 shi4 lv4 de2 xian1 huo2 xiu4 mei4 shi1 yi4 ang4 ran2
原文汉字: 绿是阳春烟景大块文章的底色四月的林峦更是绿得鲜活秀媚诗意盎然
识别结果: 绿是阳春烟景大块文章的底色四月的林峦更是绿得鲜活秀媚诗意盎然

 示例 2

原文拼音: ta1 jin3 ping2 yao1 bu4 de li4 liang4 zai4 yong3 dao4 shang4 xia4 fan1 teng2 yong3 dong4 she2 xing2 zhuang4 ru2 hai3 tun2 yi4 zhi2 yi3 yi1 tou2 de you1 shi4 ling3 xian1
识别结果: ta1 jin3 ping2 yao1 bu4 de li4 liang4 zai4 yong3 dao4 shang4 xia4 fan1 teng2 yong3 dong4 she2 xing2 zhuang4 ru2 hai3 tun2 yi4 zhi2 yi3 yi1 tou2 de you1 shi4 ling3 xian1
原文汉字: 他仅凭腰部的力量在泳道上下翻腾蛹动蛇行状如海豚一直以一头的优势领先
识别结果: 他仅凭腰部的力量在泳道上下翻腾泳动蛇行状如海豚一直以一头的优势领先

 示例 3

原文拼音: pao4 yan3 da3 hao3 le zha4 yao4 zen3 me zhuang1 yue4 zheng4 cai2 yao3 le yao3 ya2 shu1 di4 tuo1 qu4 yi1 fu2 guang1 bang3 zi chong1 jin4 le shui3 cuan4 dong4
识别结果: pao4 yan3 da3 hao3 le zha4 yao4 zen3 me zhuang1 yue4 zheng4 cai2 yao3 le yao3 ya2 shu1 di4 tuo1 qu4 yi1 fu2 guang1 bang3 zi chong1 jin4 le shui3 cuan4 dong4
原文汉字: 炮眼打好了炸药怎么装岳正才咬了咬牙倏地脱去衣服光膀子冲进了水窜洞
识别结果: 炮眼打好了炸药怎么装岳正才咬了咬牙倏地脱去衣服光膀子冲进了水窜洞

 示例 4

原文拼音: ke3 shei2 zhi1 wen2 wan2 hou4 ta1 yi1 zhao4 jing4 zi zhi1 jian4 zuo3 xia4 yan3 jian3 de xian4 you4 cu1 you4 hei1 yu3 you4 ce4 ming2 xian3 bu2 dui4 cheng1
识别结果: ke3 shei2 zhi1 wen2 wan2 hou4 ta1 yi1 zhao4 jing4 zi zhi1 jian4 zuo3 xia4 yan3 jian3 de xian4 you4 cu1 you4 hei1 yu3 you4 ce4 ming2 xian3 bu2 dui4 cheng1
原文汉字: 可谁知纹完后她一照镜子只见左下眼睑的线又粗又黑与右侧明显不对称
识别结果: 可谁知纹完后她一照镜子知见左下眼睑的线又粗又黑与又侧明显不对称

 示例 5

原文拼音: yi1 jin4 men2 wo3 bei4 jing1 dai1 le zhe4 hu4 ming2 jiao4 pang2 ji2 de lao3 nong2 shi4 kang4 mei3 yuan2 chao2 fu4 shang1 hui2 xiang1 de lao3 bing1 qi1 zi3 chang2 nian2 you3 bing4 jia1 tu2 si4 bi4 yi1 pin2 ru2 xi3
识别结果: yi1 jin4 men2 wo3 bei4 jing1 dai1 le zhe4 hu4 ming2 jiao4 pang2 ji2 de lao3 nong2 shi4 kang4 mei3 yuan2 chao2 fu4 shang1 hui2 xiang1 de lao3 bing1 qi1 zi3 chang2 nian2 you3 bing4 jia1 tu2 si4 bi4 yi1 pin2 ru2 xi3
原文汉字: 一进门我被惊呆了这户名叫庞吉的老农是抗美援朝负伤回乡的老兵妻子长年有病家徒四壁一贫如洗
识别结果: 一进门我被惊呆了这户名叫庞吉的老农是抗美援朝负伤回乡的老兵妻子长年有病家徒四壁一贫如洗

 示例 6

原文拼音: zou3 chu1 cun1 zi lao3 yuan3 lao3 yuan3 wo3 hai2 hui2 tou2 zhang1 wang4 na4 ge4 an1 ning2 tian2 jing4 de xiao3 yuan4 na4 ge4 shi3 wo3 zhong1 shen1 nan2 wang4 de xiao3 yuan4
识别结果: zou3 chu1 cun1 zi lao3 yuan3 lao3 yuan3 wo3 hai2 hui2 tou2 zhang1 wang4 na4 ge4 an1 ning2 tian2 jing4 de xiao3 yuan4 na4 ge4 shi3 wo3 zhong1 shen1 nan2 wang4 de xiao3 yuan4
原文汉字: 走出村子老远老远我还回头张望那个安宁恬静的小院那个使我终身难忘的小院
识别结果: 走出村子老远老远我还回头张望那个安宁恬静的小院那个使我终身难望的小院

 示例 7

原文拼音: er4 yue4 si4 ri4 zhu4 jin4 xin1 xi1 men2 wai4 luo2 jia1 nian3 wang2 jia1 gang1 zhu1 zi4 qing1 wen2 xun4 te4 di4 cong2 dong1 men2 wai4 gan3 lai2 qing4 he4
识别结果: er4 yue4 si4 ri4 zhu4 jin4 xin1 xi1 men2 wai4 luo2 jia1 nian3 wang2 jia1 gang1 zhu1 zi4 qing1 wen2 xun4 te4 di4 cong2 dong1 men2 wai4 gan3 lai2 qing4 he4
原文汉字: 二月四日住进新西门外罗家碾王家冈朱自清闻讯特地从东门外赶来庆贺
识别结果: 二月四日住进新西门外罗家碾王家冈朱自清闻讯特地从东门外赶来庆贺

 示例 8

原文拼音: dan1 wei4 bu2 shi4 wo3 lao3 die1 kai1 de ping2 shen2 me yao4 yi1 ci4 er4 ci4 zhao4 gu4 wo3 wo3 bu4 neng2 ba3 zi4 ji3 de bao1 fu2 wang3 xue2 xiao4 shuai3
识别结果: dan1 wei4 bu2 shi4 wo3 lao3 die1 kai1 de ping2 shen2 me yao4 yi1 ci4 er4 ci4 zhao4 gu4 wo3 wo3 bu4 neng2 ba3 zi4 ji3 de bao1 fu2 wang3 xue2 xiao4 shuai3
原文汉字: 单位不是我老爹开的凭什么要一次二次照顾我我不能把自己的包袱往学校甩
识别结果: 单位不是我老爹开的凭什么要一次二次照顾我我不能把自己的包袱往学校甩

 示例 9

原文拼音: dou1 yong4 cao3 mao4 huo4 ge1 bo zhou3 hu4 zhe wan3 lie4 lie4 qie ju1 chuan1 guo4 lan4 ni2 tang2 ban1 de yuan4 ba4 pao3 hui2 zi4 ji3 de su4 she4 qu4 le
识别结果: dou1 yong4 cao3 mao4 huo4 ge1 bo zhou3 hu4 zhe wan3 lie4 lie4 qie ju1 chuan1 guo4 lan4 ni2 tang2 ban1 de yuan4 ba4 pao3 hui2 zi4 ji3 de su4 she4 qu4 le
原文汉字: 都用草帽或胳膊肘护着碗趔趔趄趄穿过烂泥塘般的院坝跑回自己的宿舍去了
识别结果: 都用草帽或胳膊肘护着碗趔趔趄趄穿过烂泥塘般的院坝跑回自己的宿舍去了

 示例 10

原文拼音: xiang1 gang3 yan3 yi4 quan1 huan1 ying2 mao2 a1 min3 jia1 meng2 wu2 xian4 tai2 yu3 hua2 xing1 yi1 xie1 zhong4 da4 de yan3 chang4 huo2 dong4 dou1 yao1 qing3 ta1 chu1 chang3 you3 ji3 ci4 hai2 te4 yi4 an1 pai2 ya1 zhou4 yan3 chu1
识别结果: xiang1 gang3 yan3 yi4 quan1 huan1 ying2 mao2 a1 min3 jia1 meng2 wu2 xian4 tai2 yu3 hua2 xing1 yi1 xie1 zhong4 da4 de yan3 chang4 huo2 dong4 dou1 yao1 qing3 ta1 chu1 chang3 you3 ji3 ci4 hai2 te4 yi4 an1 pai2 ya1 zhou4 yan3 chu1
原文汉字: 香港演艺圈欢迎毛阿敏加盟无线台与华星一些重大的演唱活动都邀请她出场有几次还特意安排压轴演出
识别结果: 香港演意圈欢迎毛阿敏加盟无线台与华星一些重大的演唱活动都邀请她出场有几次还特意安排压轴演出

至此,一个简易的语音识别系统就搭建完成。

四、总结

通过本文的学习,相信你能够对语音识别以及语音模型的训练有了一定的了解,对于音频数据的处理过程也有了一定的了解,并且,分别通过实战零代码动物叫声分类模型开发和DFCNN+Transformer模型完成中文语音识别介绍了语音识别的整个流程,小伙伴们可以根据本文的实战案例进行学习。

本文整理自华为云社区内容共创活动:任务41华为云AI实战营:语音识别
查看活动详情:https://bbs.huaweicloud.com/blogs/308924

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。