文章目录
一、语音识别为什么要用CNN
通常情况下,语音识别都是基于时频分析后的语音谱完成的,而其中语音时频谱是具有结构特点的。
要想提高语音识别率,就是需要克服语音信号所面临各种各样的多样性,包括说话人的多样性(说话人自身、以及说话人间),环境的多样性等。
一个卷积神经网络提供在时间和空间上的平移不变性卷积,将卷积神经网络的思想应用到语音识别的声学建模中,则可以利用卷积的不变性来克服语音信号本身的多样性。
从这个角度来看,则可以认为是将整个语音信号分析得到的时频谱当作一张图像一样来处理,采用图像中广泛应用的深层卷积网络对其进行识别。
二、rnn层使用的坑
1. 我自己使用:keras实现的GRU
- 我调用keras的双向GRU,选GRU的目的是其容易收敛,使用双向的目的是提高准确率。
- 百度官方开源代码调用keras的LSTM
训练和部署阶段调用一样的层
GPU上训练加速可使用:
- 我使用keras的CuDNNGRU调用:tensorflow.contrib.cudnn_rnn.CudnnGRU
- 百度官方使用keras的CuDNNLSTM调用:tensorflow.contrib.cudnn_rnn.CudnnLSTM
训练和部署阶段调用一样的层
问题:内存使用量很大,推理时间长,在CPU上单条推理时间1s左右,GPU上0.1s左右。
2. mozilla:deepspeech使用
训练阶段
CPU:tf.contrib.rnn.LSTMBlockFusedCell
GPU:tf.contrib.cudnn_rnn.CudnnLSTM
部署阶段
tf lite部署(tfv1自己写的r1.14分支):tfv1.nn.rnn_cell.LSTMCell
正常部署:tf.contrib.rnn.LSTMBlockFusedCell
3. tensorflow官方推荐:tf.contrib.cudnn_rnn
参考rnn性能:https://www.tensorflow.org/guide/performance/overview
三、Batch Normalization批标准化的坑
BatchNorm就是在深度神经网络训练过程中使得每一层神经网络的输入保持相同分布的。
使用好处:
①不仅仅极大提升了训练速度,收敛过程大大加快;
②还能增加分类效果,一种解释是这是类似于Dropout的一种防止过拟合的正则化表达方式,所以不用Dropout也能达到相当的效果;
③另外调参过程也简单多了,对于初始化要求没那么高,而且可以使用大的学习率等。
参考:https://www.cnblogs.com/guoyaohua/p/8724433.html
BN使用的坑
- 使用BN层在推理阶段单条推理的准确率会下降很多。
- fine-tuning的时候出现BN层不能正确冻结的问题。
keras的BN解决方案:https://zhuanlan.zhihu.com/p/56225304
tensorflow的BN解决方案:https://www.jianshu.com/p/437fb1a5823e
四、优化器选择
之前的模型用的是momentum,baidu的代码里用的是SGD
参考文章:https://zhuanlan.zhihu.com/p/22252270
预训练选SGD,先保证其收敛。
Adam,训练过程比较智能,新手容易训练。
五、权重初始化的方式
- Batch Normalization:使用Batch Normalization Layer可以有效降低深度网络对weight初始化的依赖。
当前的模型每一层都进行了BN处理 - fine-tuning:不想从零开始训练神经网络时,我们往往选择一个已经训练好的在任务A上的模型(称为pre-trained model),将其放在任务B上做模型调整。
当前的策略是用开源数据和爬取的数据做预训练,然后在此基础上用业务领域数据训练调整。
六、ctc损失函数的调用
1. baidu的ctc-warp
keras后端选择:theano
接口说明
ctc.cpu_ctc_th(network_output, output_lens, label, label_lens)
参数:
- acts: 3-d numpy float array, same as c++ bindings
- act_lens: 1-d int array of input length of each example
- labels: list of 1-d int array for each example in minibatch
- label_lens: 1-d int array of label length of each example
ctc入参
调用:
ctc_cost = ctc.cpu_ctc_th(network_output, output_lens, label, label_lens).mean()
维度:
DimShuffle{1,0,2}.0
----------------------------------------
<TensorType(int32, vector)>
----------------------------------------
<TensorType(int32, vector)>
----------------------------------------
<TensorType(int32, vector)>
类型:
<type 'numpy.ndarray'>
----------------------------------------
<type 'list'>
----------------------------------------
<type 'list'>
----------------------------------------
<type 'list'>
模型入参
_, ctc_cost = train_fn([inputs, output_lengths, labels, label_lengths, True])
inputs.shape: (16, 169, 101) # 16是batch size,169是时间步,101是特征长度
----------------------------------------
output_lengths: [29, 32, 33, 36, 38, 39, 53, 62, 62, 66, 66, 68, 68, 71, 75, 80]
----------------------------------------
len(labels): 81
----------------------------------------
label_lengths: [2, 3, 3, 4, 3, 5, 7, 5, 4, 7, 7, 8, 6, 3, 5, 9]
2. google的tensorflow里面的ctc接口
keras后端选择:tensorflow
接口说明
ctc_batch_cost(y_true, y_pred, input_length, label_length)
在batch上运行CTC损失算法
参数:
- y_true:形如(samples,max_tring_length)的张量,包含标签的真值
- y_pred:形如(samples,time_steps,num_categories)的张量,包含预测值或输出的softmax值
- input_length:形如(samples,1)的张量,包含y_pred中每个batch的序列长
- label_length:形如(samples,1)的张量,包含y_true中每个batch的序列长
- 返回值:形如(samoles,1)的tensor,包含了每个元素的CTC损失
ctc入参
ctc_cost = K.mean(K.ctc_batch_cost(labels, network_output, ctc_input_lens, label_lens))
labels:Tensor("Placeholder_1:0", shape=(?, ?), dtype=int32)
----------------------------------------
network_output:Tensor("TimeDistributed/Reshape_1:0", shape=(?, ?, 5134), dtype=float32)
----------------------------------------
ctc_input_lens:Tensor("Placeholder:0", shape=(?, ?), dtype=int32)
----------------------------------------
label_lens:Tensor("Placeholder_2:0", shape=(?, ?), dtype=int32)
模型入参
调用:
pred_texts, ctc_text = self.train_fn([inputs, ctc_input_lens, batch['y'], batch['label_lengths'], True])
ctc_cost = K.mean(K.ctc_batch_cost(label, network_output, ctc_input_lengths, label_lens))
维度:
inputs.shape: (16, 425, 161)
----------------------------------------
ctc_input_lens: [[94], [100], [70], [89], [157], [179], [103], [94], [170], [81], [108], [166], [208], [121], [139], [101]]
----------------------------------------
labels.shape: (16, 31)
----------------------------------------
label_lengths: [[4], [3], [8], [18], [31], [24], [20], [19], [26], [21], [2], [5], [6], [18], [4], [8]]
类型:
<class 'numpy.ndarray'>
----------------------------------------
<class 'list'>
----------------------------------------
<class 'numpy.ndarray'>
----------------------------------------
<class 'list'>
X: (16, 1600, 200, 1) <class 'numpy.ndarray'>
y: (16, 64) <class 'numpy.ndarray'>
input_length: (16, 1) <class 'numpy.ndarray'>
label_length: (16, 1) <class 'numpy.matrixlib.defmatrix.matrix'>
labels: (16, 1) <class 'numpy.ndarray'>
七、训练过程中loss:inf的原因
出现这个warning/Error message 的原因就是 Y 的长度大于X 的长度, 导致CTC 无法计算。
解决方案就是检查训练集 中的label 和 实际内容是否相符。
比如,音频中是 hello, how are you doing。 而label 是 hello. 这样的训练集肯定会出问题的。
八、训练过程中loss:NaN的解决
训练过程中loss出现NaN:
- 换优化器
- 改学习率
- 改训练集验证集比例
- 改batch size
一开始的loss是NaN:
- 重新洗数,洗掉脏数据
- 去掉差异较大的样本
九、单向GRU和双向GRU的抉择
单向GRU/LSTM的算法只能捕获当前词之前词的特征,而双向的GRU/LSTM算法则能够同时捕获前后词的特征,实验证明双向的GRU/LSTM比单向的GRU/LSTM算法效果更佳。LSTM算法性能稍优于GRU算法,但是GRU算法训练速度要比LSTM算法快。