小爱同学,小度小度,天猫精灵,叮咚叮咚......我们身边好像突然就出现了一些可以和我们"聊天"的音箱,图所示为百度智能音箱。
智能音箱与传统音箱最大的区别就是能够听懂我们的语音,人们通过说话就能与电子设备沟通,实现信息的检索与查找,比如查询天气,设定闹钟,播放音乐等。这其中最关键的就是语音识别技术。
1.1 语音识别技术
语音识别通俗的讲就是将人类语音中的词汇内容转换为计算机可读的输入,让机器知道我们在说什么,这是一门交叉学科,涉及的领域包括信号处理,模式识别,概率论和信息论,发声机理和听觉机理,人工智能等等。最近几年随着人工智能技术的快速发展,语音识别技术取得显著进步,开始从实验室走向市场。
1.2 语音识别技术的发展
自从机器出现以后,通过语音与机器进行交流一直是科研人员的梦想。应用语音是被技术的第一个装置应该是1952年贝尔研究所Davis等人研发的能识别10个英文数字发音的实验系统。
1960年,英国Denes等人成功研制出第一个计算机语音识别系统。而大规模的语音识别研究是在进入了20世纪70年代以后,在小词汇量,孤立词的识别方面取得了实质性的发展。
20世纪80年代,语音识别技术研究的重点逐渐转向大词汇量,非特定人连续语音识别,包括噪声环境下的语音识别和会话识别系统。在研究思路上也发生了重大变化,既由传统的基于标准模板匹配的技术思路转向基于统计模型的技术思路。当时出现了两项非常重要的技术:隐马尔可夫模型和N-gram语言模型。此外还提出了将神经网络技术引入语音识别领域的技术思路。20世纪90年代以后,在语音识别的系统框架方面并没有什么重大突破,不过在语音识别技术的应用及产品化方面进展很大。
进入21世纪,随着深度学习的不断发展,神经网络之父Hinton提出深度置信网络,2009年,Hinton和学生Mohamed将深度神经网络应用于语音识别,在小词汇量连续语音是被任务TIMIT上获得成功。
中国的语音识别技术研究始于1958年,中国科学院声学研究所利用电子管电路实现了识别10个元音的实验系统。1973年,中国科学院声学研究所开始研究计算机语音识别。由于当时条件的限制,中国的语音识别技术研究工作一直处于缓慢发展的阶段。
20世纪80年代以后,随着计算机应用技术在中国逐渐普及和广泛应用,以及数字信号技术的进一步发展,国内许多单位具备了研究语音识别技术的基本条件。与此同时,国际上语音识别技术在多年沉寂之后重新成为研究的热点,迅速发展。在这种形式下,国内许多单位纷纷投入这项研究工作中。
1986年3月,中国高技术研究发展计划(863计划)启动,语音识别技术作为智能计算机系统研究的一个重要组成部分被专门列为研究课题。在863计划的支持下,中国开始了有组织的语音识别技术的研究,中国的语音识别技术进入了一个前所未有的发展阶段。
1.3 语音识别技术原理
语音识别系统可以分为:特定人与非特定人的识别,独立词与连续词的识别,小词汇量与大词汇量以及无限词汇量的识别。但无论哪种语音识别系统,其基本原理和处理方法都大体类似。
1.3.1 WAV文件
大家都知道语音(或声音)实际上是一种波,声波经过拾音器(话筒或麦克风)采集后被转换成连续变化的电信号。电信号再经过放大和滤波,然后被电子设备以一个固定的频率进行采样,每个采样值就是当时检测到的电信号幅值。接着电子设备会将采样值由模拟信号量化为由二进制数表示的数字信号。最后是对数字信号编码,并将编码后的内容存储为音频流数据。在计算机应用中,能够达到高保真水平的是PCM(脉冲编码调制)编码。编码之后有些应用为了节省存储空间,存储前还会对音频流数据进行压缩,常见的MP3文件就是一种压缩后的音频流数据。
说明:
虽然PCM是数字音频中最佳的保真水平,但并不意味着PCM就和原音频一样毫无失真。PCM只是能做到最大限度的无线接近,但依然是有失真的。
处理音频流数据时,必须时非压缩的纯波形文件。WAV就是最常见的无压缩声音文件格式之一(也是采用PCM编码),时微软公司转么为Window操作系统开发的一种标准数字音频文件,最早于1991年8月出现在Windows3.1操作系统上。WAV文件里存储的内容除了一个文件头以外,就是声音波形的采样点。WAV文件能记录各种单声道或立体声的声音信息,并且能保证声音不失真。不过相比于MP3文件,WAV文件非常大。一般来说,由WAV文件还原的声音的音质取决于声音采样样本的多少(既采样频率的高低),采样频率越高,音质就越好,但WAV文件也就越大。
一个WAV文件的参数包括采样频率,采样精度和声道数,这几个参数介绍如下:
(1)采样频率:每秒钟采集音频数据的次数。采样频率越高,音频保真度越高。常用的采样频率包括11025Hz,22050Hz,44100Hz和48000Hz四种,其中,11025Hz的采样频率相当于电话声音的效果,22050Hz的采样频率相当于FM调频广播的效果;44100Hz的采样频率相当于CD声音的效果。
(2)采样精度:这是用来衡量声音波动变化的参数,也是声卡的分辨率。它的数值越大,声卡的分辨率就越高。目前计算机配置的16位声卡的采样精度包括8位和16位两种。一般讲话以8位11.025KHz采样就能较好的还原。
(3)声道数:有单声道和立体声之分,单声道的声音只能使一个喇叭发声(有的声卡会将单声道信息处理成两个喇叭同时输出),立体声的声音可以使两个喇叭都发声(一般左右声道各有分工),这样更能感受到音频信息的空间效果。显然,立体声数据还原特性更接近人们的听力习惯,但采集的数据量会增加1倍。
1.3.2 声学特征提取
有了数字化的音频文件之后,语音识别的第二步时进行声学特征提取。这时语音识别最重要的一环。提取的特诊参数必须满足以下要求:
(1)提取的特征参数能有效代表语音特征,具有很好的区分性。
(2)各阶参数之间有良好的独立性。
(3)特征参数要计算方便,最好有高效的算法,以保证语音识别的实时实现。
简单来说,声学特征提取的过程如下:
首先在语音识别之前,通常先把首尾端的静音切除,降低对后续步骤造成的干扰。
其次对声音分帧,也就是把声音切成一小段一小段,每小段称为一帧。分帧操作不是简单的切开,而是通过移动窗函数来实现。帧与帧之间一般是有交叠的。对于典型的语音识别任务,推荐一帧的时间位20-30ms。在这段时间内,人类最多只能说一个音素(根据语音的自然属性划分出来的最小语音单位)。帧与帧之间的重叠率可以根据需要在25%-75%之间选择,通常来说,设置为50%。
分帧后,语音就变成了很多小段。不过波形在时域上几乎没有描述能力,因此,必须对波形进行变换。常见的一种变换方法是提取MFCC(梅尔频率倒谱系数)特征。梅尔频率是基于人耳听觉特性提出来的,它与赫兹频率成非线性对应关系。梅尔频率倒谱系数则是利用它们之间的这种关系,计算得到的赫兹频谱特征。经过变换,每一帧波形会变成一个多维向量,可以简单的理解为这个向量包含了这帧语音的内容信息。实际应用中,变换又分为预加重,分帧,加窗,快速傅里叶变换,梅尔滤波器组,离散余弦变换等几个步骤,而声学特征也不是只有MFCC这一种。
1.3.3 匹配识别
提取音频的声学特征之后,语音识别的最后一步就是通过训练号的模型将这些特征进行分类,进而依据判定准则找出最佳匹配结果。
声学模型是语音识别系统中非常重要的一个组件,对不同基本单元的区分能力直接关系到识别结果。语音是被本质上是一个模式识别的过程,而模式识别的核心是分类器和分类决策的问题。
通常,在孤立词,中小词汇量识别中使用动态时间规整分类器会又良好的识别效果,并且识别速度快,系统开销小,是语音识别中很成功的匹配算法。但是,在大词汇量,非特定人语音识别的时候,DTW识别效果就会急剧下降,这时候使用隐马尔可夫模型(HMM)进行训练识别效果会有明显提升,由于在传统语音识别中一般采用连续的高斯混合模型对状态输出密度函数进行刻画,因此又称为GMM-HMM构架。不过随着人工只能(尤其是深度学习)的发展,通过深度神经网络(DNN)来完成声学建模,形成所谓的DNN-HMM构架来取代传统的GMM-HMM架构,在语音识别上也取得了很好的效果。
1.4 录制及播放音频
编写Python代码实现录制和播放音频,需要用到wave模块和PyAudio模块。
1.4.1 wave模块
wave模块是Python标准库中的模块,它提供了一个处理WAV声音格式文件的接口,让用户可以读写,分析及创建WAV文件。它不支持压缩/解压,但是支持单声道/立体声。
如果希望打开一个WAV文件,可以使用wave模块中的函数
python
wave.open(file,mode=None)
1.第一个参数file是一个字符串,表示要打开的WAV文件的路径以及文件名
2.第二个参数mode表示文件的读写模式,如果是rb表示只读模式,返回一个wave_read对象,如果是wb表示只写模式,返回一个wave_write对象。
说明:
打开WAV文件时不支持同时读写的。
对于Wave_read对象来说,其包含以下方法:
(1)Wave_read.colse(),关闭打开的数据流并使对象不可用。当对象销毁时会自动调用。
(2)Wave_read.getsampwidth(),返回采样字节长度
(3)Wave_read.getframerate(),返回采样频率。
(4)Wave_read.getnframes(),返回音频总帧数。
(5)Wave_read.getcomptype(),返回压缩类型(只支持N哦呢类型,表示未压缩)。
(7)Wave_read.getprams(),返回音频的参数,返回值是一个由nchannels,sampwidth,framerate,nframes,comtype。compname组成的元组。
(8)Wave_read.readframes(n),读取并返回n各帧的语音数据。
(9)Wave_read.rewind(),回到语音数据流的开头。
对于Wave_write对象来水,包含以下的方法:
(1)Wave_write.close(),关闭打开的数据流并使对象不可用。当对象销毁时会自动调用。
(2)Wave_write.setnchannels(n),设置声道数,1为单声道,2为立体声。
(3)Wave_write.setframerate(n),设置采样频率。
(4)Wave_write.setsampwidth(n),设置采用字节长度。
(5)Wave_write.writeframesraw(data),写入语音数据帧,但是没有文件表头。
(6)Wave_write.writeframes(data),写入语音数据帧及文件表头。
1.4.2 PyAudio模块
相比wave模块而言,PyAudio模块是由第三方提供的模块,使用之前需要先安装。PyAudio模块是一个跨平台的音频I/O模块,可以在Linux,Window,Android和Mac OS操作系统上运行。这里我们通过Python的pip工具来安装。
pip是Python包管理工具,该工具提供了对Python模块的查找,下载,安装及卸载功能。Python3.4以上版本都自带pip工具。在Windows操作系统中使用pip工具安装第三方模块的方法是打开cmd命令行工具,在其中输入pip install并加上对应的模块名称。如果安装PyAudio模块则输入以下命令:
python
pip install PyAudio
PyAudio模块安装界面如图:
界面中有一个安装进度条,等待进度条完成即可。要测试是否安装正确,可以打开Python的IDIE编辑器,在其中输入Import pyaudio,如果回车之后没有报错就说明一切正常。
PyAudio模块包含两个类:PyAudio和Stream类,如果要使用PyAudio模块,首先要使用pyaudio.PyAudio()方法生成一个实例化对象。
如果要录制或播放音频,则先要使用PyAudio.PyAudio.open()方法打开一个数据流,这里需要设定一系列的参数,包括采样频率,采用精度和声道数等。该方法返回的是一个Stream类的对象。
如果要播放音频,则使用PyAudio.Stream.write()方法将音频数据写入Stream类的对象;如果是要录制音频,则使用PyAudio.Stream.read()方法将Stream类的对象中的数据读出来。
当不需要录制和播放音频时,可使用PyAudio.Stream.stop_stream()方法停止数据流,并使用PyAudio.Stream.close()方法关闭数据流。
最后使用PyAudio.PyAudio.terminate()方法终止对象的操作。
1.4.3 录制音频
了解了PyAudio模块使用的流程之后,下面来实现一个录制音频的例子,在Python的开发工具Pycharm中输入以下代码:
python
import pyaudio
import wave
# 一次读取数据流的数据量,避免一次性的数据量太大
chunk=1024
#采样精度
FORMAT = pyaudio.paInt16
#声道数
CHANNELS = 1
#采样频率
RATE = 11025
#设置录音时长,单位秒
RECORD_SECONDS =5
# 创建PyAudio对象
p = pyaudio.PyAudio()
stream=p.open(format=FORMAT,channels=CHANNELS,
rate=RATE,input=True,frames_per_buffer=chunk)
#开始录音
print("请开始你的说话......")
frames=[]
for i in range(0, int(RATE/chunk*RECORD_SECONDS)):
data = stream.read(chunk)
frames.append(data)
#录音结束
print("录音完成")
stream.start_stream()
stream.close()
p.terminate()
wf=wave.open("output.wav","wb")
wf.setnchannels(CHANNELS)
wf.setsampwidth(p.get_sample_size(FORMAT))
wf.setframerate(RATE)
wf.writeframes(b''.join(frames))
wf.close()
这段程序中,首先导入PyAudio和wave两个模块,接着而定义一些变量用来保存之后要用到的参数,包括采样频率,采样精度和声道数等。
然后,准备开始录音。先使用pyaudio.PyAudio()方法生成一个实例化对象p。接着使用对象的open()方法打开一个数据流并将方法的返回值赋值给变量stream,这里注意input参数要设置为true。
开始录音后通过一个for循环不断将数据流中的数据添加到数组变量frames中。
当不需要录制音频时,使用对象stream的stop_stream()方法停止数据流,使用close()方法关闭数据流,并使用对象p的terminate()方法终止对象的操作,这样录音就完成了。
最后将变量frames中的内容保存在WAV文件中,这就需要用到wave模块中的对象方法了。这里时将音频数据保存在文件optput.wav中。
将以上代码保存为.py文件。之后运行程序时就会在Python的控制台中显示"请开始你的说话",表示开始录音了。此时我们可以对着计算机说一些简短的语句。由于程序中设定的录音时间为5秒,所以5秒之后,录音会停止,同时在控制台中会显示信息"录音完成"。
程序运行完成后,会在.py文件相同的文件夹下出现一个output.wav文件,如果想听一下这个音频文件的内容则可以通过计算机系统自带的播放器播放。
1.4.4 播放音频
相对于录音,播放音频的过程要相对简单一些。下面就是一个播放音频的例子,在PyCharm开发工具中,编写以下代码:
python
import pyaudio
import wave
chunk=1024
wf=wave.open("output.wav","rb")
p=pyaudio.PyAudio()
stream=p.open(format=p.get_format_from_width(wf.getsampwidth()),
channels=wf.getnchannels(),
rate=wf.getframerate(),
output=True)
while True:
data=wf.readframes(chunk)
if data == b"":break
stream.write(data)
stream.close()
p.terminate()
这段程序中,依然首先导入PyAudio和wave两个模块。
然后打开要播放的音频文件,这里时output.wav。
播放音频还是先使用pyaudio.PyAudio()方法生成一个实例化对象p,接着使用对象的open()方法打开一个数据流,并将方法的返回值赋值给变量stream,这里注意open()方法的参数都是通过wave模块的对象和方法获取的,同时注意output参数要设置为True。
播放音频是在一个while循环中,不断通过wave模块中wave_read对象的readframes()方法和PyAudio模块中Stream类的write()方法将音频文件的数据写入Stream类的对象。当读取的音频数据为空时表示音频已经结束,此时通过break跳出循环。
最后使用对象stream的close()方法关闭数据流,并使用对象p的terminate()方法终止对象的操作,这样播放音频的操作就完成了。
将以上代码保存为.py文件,之后运行程序时就会听到刚才录制的音频。