[天池ASR入门赛]语音识别初尝试
- 背景
- ASR及语音相关的初认识
- 一些在我司常听到的关键词的介绍
- 1)波形图
- 2)采样点
- 3)频谱图
- 整体解决思路
- 声学模型
- 语言模型
- 解码器
- 端到端的方法
- 赛题及base-line的介绍
- 赛题介绍
- 音频数据分析
- 声学特征介绍及提取
- 其他声学特征
- MFCC特征抽取过程一个简介
- MFCC抽取过程涉及到的内容
- 短时傅立叶变换
- 梅尔谱
- 关于py中MFCC的抽取使用
- 关于LSTM的baseline
- 简介
- 关于LSTM
- 实现
- 改进模型方面的思路
- 过拟合与欠拟合
- 拓展内容
- 语音识别的应用
- 语音识别的开发工具
- 4.1 开源数据集
- 4.2 开源语音识别项目
背景
这是第一次接触到语音识别的相关任务,本篇的笔记是对语音识别相应资料的个人直观理解,及datawhale的baseline解法的相关介绍 (免责:真的是很主观的个人理解,可能涵盖错误)
ASR及语音相关的初认识
一些在我司常听到的关键词的介绍
语音不像文本,可以看得见,仅有对应的音频,需要对语音有一个“可以看见”的过程,于是有了下列的几种音频文件的表示方法:
1)波形图
波形图:语音的保存形式可用波形图展现,可以看作是上下摆动的数字序列,每一秒的音频用16000个电压数值表示,采样率即为16kHz
2)采样点
采样点:对波形图的放大,可以看到的更细的单位
3)频谱图
频谱图:可以变为 频谱图,颜色代表频带能量大小,语音的傅立叶变换是按帧进行,短的窗口有着高时域和低频域,长时窗口有低时域和高频域
基本单位 对于语音而言,基本单位是帧(对应文本的token),一帧即是一个向量,整条语音可以表示为以帧为单位的向量组。 帧是由ASR的前端声学特征提取模块产生,提取的技术设计“离散傅立叶变换”和”梅尔滤波器组“
整体解决思路
在我的理解认知中,对于ASR的解决方法可以分为两种,一种是声学模型加语言模型的组合,另外一种是端到端的解决方式。
第一种方式: 路线的个人理解大约是,有一个音频,先有声学模型,将对应的音频信号处理为对应的声学特征,再有语言模型,将声学特征的结果得到概率最大的输出字符串。 在上图中,X代表的是声学特征向量,W代表输出的文本序列,在(2.1)中,*P(X|W)*代表的是声学模型,*P(W)*代表的是语言模型
第二种方式: 端到端的解决手段,个人印象中在吴恩达的课程里提到,ASR在CTC提出后有一个较大的提升。个人理解是在CTC之前,seq2seq的建模方式比较难处理输出序列远短于输入序列的情况,以及在不同帧出现的相同音素的输出
声学模型
常用的话,包括了HMM,GMM,DNN-HM的声学模型
语言模型
常用的语言模型这里包括了n-gram语言模型以及RNN的语言模型
解码器
最终目的是取得最大概率的字符输出,解码本质上是一个搜索问题,并可借助加权有限状态转换器(Weighted Finite State Transducer,WFST) 统一进行最优路径搜索.
端到端的方法
seq2seq+CTC 损失函数, RNN Transducer, Transforme,这里需要补充的话 应该要去看李宏毅2020年的人类语言处理的课程
赛题及base-line的介绍
比赛链接及介绍 (对榜首的99.95准确率表达amazing,怎么做到的…)
赛题介绍
介绍: 有20种不同食物的咀嚼声音,给出对应的音频,对声音的数据进行建模,判断是哪种食物的咀嚼声音
baseline思路:将对应的音频文件,使用librosa转化为梅尔谱作为输入的特征,用CNN对梅尔谱的特征进行建模分类预测。
较为重要的内容 1)关于librosa库 一个关于提取语音特征的库
import librosa
#根据采样率读取音频文件
y, sr = librosa.load('./beat.wav', sr=16000)
#提取梅尔谱特征
melspec = librosa.feature.melspectrogram(y, sr,
n_fft=1024, hop_length=512, n_mels=128)
#提取MFCC特征
mfccs = librosa.feature.mfcc(y=y, sr=sr, n_mfcc=40)
# 关于音频的可视化
import librosa.display
y, sr = librosa.load('./beat.wav', sr=None)
plt.figure()
librosa.display.waveplot(y, sr)
plt.title('Beat wavform')
plt.show()
相应的图片如下:
而基于base-line的实现中,就是将梅尔谱作为对应的cnn特征,
features = []
X, sample_rate = librosa.load(fn,res_type='kaiser_fast')
mels =np.mean(librosa.feature.melspectrogram(y=X,sr=sample_rate).T,axis=0) # 计算梅尔频谱(mel spectrogram),并把它作为特征
feature.extend([mels])
推测这里的mean是做了一个正则化的操作
2)关于glob库 比较神奇的一个库,先前找文件夹下的文件可能会通过 os.path 结合是实现,通过该库是可以有一种比较优雅的方式获得文件夹中想要的文件
#获得文件夹中的py文件
f = glob.iglob(r'../*.py')
for py in f:
print py
而在其base-line中的显示使用
#获取目录下的所有wav文件
glob.glob(os.path.join(parent_dir, sub_dir, '.wav'))
3)关于tf.keras的建模方式 keras的建模方式还是比较便捷的 快速建模的核心的建模部分为通过Sequential,首先确定好输入的特征的维度,然后model.add()为模型添加上需要的模块(其实是比较好气说对于多输入特征的话这种方式work不work,要是有大佬看到的话,还是希望留言告知的) 通过model.compile模块将模型的结构固定,且在此时添加对应的损失函数及优化器等
model = Sequential()
# 输入的大小
input_dim = (16, 8, 1)
model.add(Conv2D(64, (3, 3), padding = "same", activation = "tanh", input_shape = input_dim))# 卷积层
model.add(MaxPool2D(pool_size=(2, 2)))# 最大池化
model.add(Conv2D(128, (3, 3), padding = "same", activation = "tanh")) #卷积层
model.add(MaxPool2D(pool_size=(2, 2))) # 最大池化层
model.add(Dropout(0.1))
model.add(Flatten()) # 展开
model.add(Dense(1024, activation = "tanh"))
model.add(Dense(20, activation = "softmax")) # 输出层:20个units输出20个类的概率
# 编译模型,设置损失函数,优化方法以及评价标准
model.compile(optimizer = 'adam', loss = 'categorical_crossentropy', metrics = ['accuracy'])
对于训练和预测过程,则分别通过model.fit()和model.predict()完成
model.fit(X_train, Y_train, epochs = 20, batch_size = 15, validation_data = (X_test, Y_test))
predictions = model.predict(X_test.reshape(-1, 16, 8, 1))
音频数据分析
两个较为重要的库 对于音频文件,可用librosa 和 PyAudio 进行一个语音特征的探索
第一个库除了上述的可以看波形图外,还可以看语音里的另一个特征声谱图,声谱图指的是声音随时间变化的频谱的一种直观表示(其实还是不太明白是什么意思)
频谱图的展示
plt.figure(figsize=(20, 10))
D = librosa.amplitude_to_db(np.abs(librosa.stft(data1)), ref=np.max)
plt.subplot(4, 2, 1)
librosa.display.specshow(D, y_axis='linear')
plt.colorbar(format='%+2.0f dB')
plt.title('Linear-frequency power spectrogram of aloe')
其中其实表示了三个维度的信息,横轴代表时间,纵轴代表频率,然后颜色的深浅代表着音强的大小
而至于PyAudio是为了让用户可以在交互的环境下,听对应的音频(但对于我这种.py用户来说 可能比较缺少实用性)
import IPython.display as ipd
ipd.Audio('./train_sample/aloe/24EJ22XBZ5.wav')
其他语音特征的名词简介:pitch: 称之为音调,以Hz为单位基频: 称之为音高能量:称之为音强
关于语音识别了解到的大致过程: 1)静音切除,使用的手段包括了VAD 2) 声音分帧,通过滑动窗口实现,帧与帧之间会有交叉 3)声学特征提取:通过线性预测倒谱系数(LPCC)和Mel 倒谱系数(MFCC),把每一帧的波形给转换为声学特征向量(类似于nlp中的embedding)? 4)声学模型:根据对应的声学特征,找到对应的音素信息 5)词典:由发音找到字词对应的token集合 6)语言模型:找到概率最大的输出字符序列 7)解码:过声学模型,字典,语言模型对提取特征后的音频数据进行文字输出。
声学特征介绍及提取
其他声学特征
过零率:是一个信号符号变化的比率,即语音信号由负变为正,由正变为负的次数,一般,过零率越高,频率越高
# 利用libroas计算过零率的代码
zero_crossings = librosa.zero_crossings(x[n0:n1], pad=False)
频谱质心: 用以描述音色属性,是频率成分的重心,是一定范围内通过能量加权平均的频率。
spectral_centroids = librosa.feature.spectral_centroid(x, sr=sr)[0]
print(spectral_centroids.shape)
# (2647,)
# 计算时间变量
frames = range(len(spectral_centroids))
t = librosa.frames_to_time(frames)
# 归一化频谱质心
def normalize(x, axis=0):
return sklearn.preprocessing.minmax_scale(x, axis=axis)
#沿波形绘制频谱质心
librosa.display.waveplot(x, sr=sr, alpha=0.4)
声谱衰减:是对声音信号形状(波形图)的一种衡量,表示低于总频谱能量的指定百分比的频率。
spectral_rolloff = librosa.feature.spectral_rolloff(x+0.01, sr=sr)[0]
librosa.display.waveplot(x, sr=sr, alpha=0.4)
MFCC特征抽取过程一个简介
MFCC: 是语音识别中常用的一种语音特征,提取的步骤如下
- 分帧
- 用周期图(periodogram) 法进行gong功率谱(power spercturm)的估计
- 对功率谱用Mel滤波器组进行滤波,计算每个滤波器里的能量
- 对每个滤波器的能量取log
- 进行离散余弦变换(DCT)变换
- 保留DCT的第2-13个系数,去掉其它 前面两步是短时傅立叶变换,后面几步主要涉及梅尔频谱
关于短时傅立叶变换在干嘛: 首先涉及到 时域 与 频域 的概念,参照这里 时域是时间与振幅的函数,频域是频率与振幅的函数。(感觉这里音乐的例子比较让人容易懂) 个人理解是,单个时域,我们缺乏了振幅(?)的信息,单看频域,我们失去了时间的信息,所以这时候需要有个工具,将二者结合到一起,然后这里用了傅立叶变换。 这是DataWhale小姐姐的解释,所谓的短时傅里叶变换,即把一段长信号分帧、加窗,再对每一帧做快速傅里叶变换(FFT),最后把每一帧的结果沿另一个维度堆叠起来,得到类似于一幅图的二维信号形式。
MFCC抽取过程涉及到的内容
短时傅立叶变换
1)分帧: 将长语音切分为一个较为小的单位,个人认为是可以对接为nlp中的一个token,但为了连续性(?),不能帧跟帧之间完全的等分割裂,需要有一定的重合。我们通常以25ms为1帧,帧移为10ms,因此1秒的信号会有10帧。 2)对每帧信号做离散傅立叶变换(DFT) 这里…巴拉巴拉的放了一个公式… ,看不太懂到底干了啥… 累觉不爱(贴上一个 关于傅立叶变换的讲解,课后阅读) 可能是 将每一帧的信号 转换为 时域(?) 由离散的傅立叶变换可以得到声谱图,它的横坐标是帧下标,纵坐标是不同频率或者叫能量,图中颜色越深(比如红色),对应频率的能量越大。
在 librosa库,有对短时傅立叶变换的实现
# STFT
y, sr = librosa.load('./train_sample/aloe/24EJ22XBZ5.wav')
S =librosa.stft(y, n_fft=2048, hop_length=None, win_length=None, window='hann', center=True, pad_mode='reflect')
#复数的实部:np.abs(D(f,t))为频率的振幅
#复数的虚部:np.angle(D(f,t))为频率的相位
S = np.abs(S)
梅尔谱
声谱图往往是很大的一张图,且依旧包含了大量无用的信息,所以我们需要通过梅尔标度滤波器组(mel-scale filter banks)将其变为梅尔频谱。
1) 梅尔尺度
更好的匹配人类的听觉感知效果。从频率到梅尔频率的转换公式如下:
2)梅尔滤波器
为了模拟人耳对声音的感知,人们发明的梅尔滤波器组。一组大约20-40(通常26)个三角滤波器组,它会对上一步得到的周期图的功率谱估计进行滤波。而且区间的频率越高,滤波器就越宽(但是如果把它变换到美尔尺度则是一样宽的)。
(不知道这里的滤波器,其实是不是可以理解为类似于CNN的东西)
3)梅尔到谱 为了模拟人耳对声音的感知,人们发明的梅尔滤波器组。一组大约20-40(通常26)个三角滤波器组,它会对上一步得到的周期图的功率谱估计进行滤波。而且区间的频率越高,滤波器就越宽(但是如果把它变换到美尔尺度则是一样宽的)。
关于py中MFCC的抽取使用
在py中,可以通过librosa直接使用抽取MFCC的特征
#梅尔频率倒谱系数 MFCC
mfccs = librosa.feature.mfcc(x, sr)
print (mfccs.shape)
# (20, 151)
#Displaying the MFCCs:
librosa.display.specshow(mfccs, sr=sr, x_axis='time')
# mfcc 特征缩放
mfccs = sklearn.preprocessing.scale(mfccs, axis=1)
关于LSTM的baseline
简介
看到task4中其实是CNN中的模型base-line介绍,此时想看关于DataWhale的关于LSTM的代码介绍。对于ASR领域,应该是使用LSTM的情况会比较多一些?
关于LSTM
LSTM其实是RNN的一种变体,RNN的提出感觉整体上是为了解决时序问题数据的建模,即后续相对于RNN来讲,缓解了梯度爆炸和梯度消失的问题。 在给定的baseline中,是将音频文件中抽取队MFCC特征,继而将对应的特征,将每帧的MFCC特征传入到了LSTM中,由LSTM得到的hidden-state进入到两层的全连接层中。预测对应的标签概率。
实现
本文用的train_data.sample为例子
1)通过sox的指令可以将wav文件整理成16k的wav文件
sox file -b 16 -e signed-integer $new_dir"/"$file
2) 抽取MFCC特征的代码 由抽取出的MFCC的特征,存到文件中,后续写为json,由train.py的脚本读取进行训练。
def extract_mfcc(y, sr, size=1):
"""
extract MFCC feature
:param y: np.ndarray [shape=(n,)], real-valued the input signal (audio time series)
:param sr: sample rate of 'y'
:param size: the length (seconds) of random crop from original audio, default as 3 seconds
:return: MFCC feature
"""
# normalization
y = y.astype(np.float32)
normalization_factor = 1 / np.max(np.abs(y))
y = y * normalization_factor
# random crop
# start = random.randint(0, len(y) - size * sr)
# y = y[start: start + size * sr]
# extract log mel spectrogram #####
melspectrogram = librosa.feature.melspectrogram(y=y, sr=sr, n_fft=2048, hop_length=1024)
mfcc = librosa.feature.mfcc(S=librosa.power_to_db(melspectrogram), n_mfcc=20)
mfcc_delta = librosa.feature.delta(mfcc)
mfcc_delta_delta = librosa.feature.delta(mfcc_delta)
mfcc_comb = np.concatenate([mfcc, mfcc_delta, mfcc_delta_delta], axis=0)
return mfcc_comb
(在此将数据集的wav的文件及对应的特征写成json的写法感觉很值得借鉴)
4)关于训练的代码
核心的训练代码在此处
Solver/solver.py
看到train的代码是写在模型中的,逻辑上是造一个新的类,将数据,模型以及超参数,都作为类的一个属性,参与后续的训练代码的调用。
在其类中,查看到了一种写法,既是若是每个训练轮次比较复杂以及需要有对应的返回时,可以为每个轮次单独写一个类中的方法,反复调用
for epoch in range(self.start_epoch, self.epochs):
self.model.train()
start = time.time()
tr_avg_loss, _ = self._run_one_epoch(epoch)
self.model.eval()
val_loss, val_acc = self._run_one_epoch(epoch, cross_valid=True)
改进模型方面的思路
过拟合与欠拟合
粗糙定义:过拟合:训练集上表现得好,测试集上表现的差欠拟合:训练集和测试集都差 过拟合的应对思路: 1)加数据 即如同名字所说,加更多的数据,让模型不仅适应某种分布 2)砍模型 模型很大,但数据规模不大时,需要考虑从这个角度出发 3)正则化这篇文章 是从损失函数的角度入手,例如我们拟合一条曲线时,如果各个次项的参数都学的比较好,完好的拟合了给定的曲线,会出现过拟合,此时我们希望说,低阶次项的影响小一些。所以损失函数会加上低阶的参数的值乘以一个较大的权重,这样loss-function在计算的时候,就会把对应的低阶项的参数值给减小。 当在神经网络时,我们不知道是需要给哪个参数的值乘以较大的权重,就对所有参数的值取范数,希望通过损失函数,由模型自己取挑选较为重要的特征。这篇文章 讲了BN,LN之类的核心思想,大致的意思是,其实是做一个数据归一化的事情,既是为了尽量让中间的特征层呈现独立同分布的情况,BN为纵向的标准化,而LN为横向的标准化。 需要注意的是,就目前的实现来看,BN应该是可用在CNN类的网络,而LN适用在RNN类的网络 4)Dropout NN的网络结构图中,某个计算节点,随机失活,减少输出层对某个节点的依赖 5) 集成模型 分为booosting和bagging,前者是基于分布和迭代。后者比较粗暴,基于多个分类器一起算,然后最后做投票,进行决策。 (对于上了深度学习的系统上应该比较少用,打比赛还行) (当时在学的时候就有点不太明白说是不是这两个是做不了回归任务的,只能做分类任务) 6) 损失函数加上floadding
#pytorch 写法
loss = (loss - args.floadding_num).abs() + args.floadding_num
欠拟合的应对思路 欠拟合,一般是数据不够,网络设计不够好,或者是现有的特征不够,所以自身是以以下三个角度进行改进 1)加特征:例如NLP加分词,加词性。“在深度学习潮流中,有很多模型可以帮助完成特征工程,如因子分解机、梯度提升决策树、Deep-crossing等都可以成为丰富特征的方法” 2)扩大模型 3)考虑网络的设计是否合理
拓展内容
语音识别的应用
关键词检出(KWS,Keyword Spotting), 唤醒词检测(Wake-up Word Detection,WUW) 声纹检测(VR,Voiceprint Recognition),说话人识别 (SR,Speaker Recognition) 语音识别(SR,Speech Recognition),语种识别(Spoken Language Recognition)
语音识别的开发工具
kaldi-asr/kaldi : 支持的模型结构:GMM/CNN/LSTM/TDNN-HMM/LF-MMI/RNNLMespnet Chainer/PyTorch:支持的模型结构:CTC/Attention/DNN-HMM/RNN-T
4.1 开源数据集
清华中文语音数据集THCHS-30希尔贝壳中文普通话语音数据库AISHELL-2中国明星声纹数据集 CN-Celeb2kaldi中免费的数据集(中文跟英文)Google发布的语音分离数据集好未来开源近600小时中英文混合语音数据集字节跳动发布全球最大钢琴MIDI数据集中英文NLP数据集搜索库:CLUEDatasetSearch
4.2 开源语音识别项目
https://github.com/kaldi-asr/kaldi https://github.com/SeanNaren/deepspeech.pytorch https://github.com/srvk/eesen https://github.com/Uberi/speech_recognition https://github.com/xxbb1234021/speech_recognition
参考资料李宏毅深度学习语音识别(2020最新版)清语赋. 语音识别基本法【概率图模型】 那些的你不知道的事语音信号处理传统语音识别(GMM+HMM)GMM-HMM语音识别模型原理篇马尔可夫链和隐马尔可夫模型语音识别中的CTC算法的基本原理解释Attention在语音识别中的应用深度学习中的attention机制语音交互评价指标语音基础语音识别的大致流程DataWhale的github代码