kaldi中DNN网络结构解析

举报
可爱又积极 发表于 2021/11/30 11:58:22 2021/11/30
【摘要】 网络结构可以被认为是另外需要确定的参数。既然每层可以被认为是前一层的特征抽取器,每层节点的数量应该足够大以获取本质的模式。这在模型低层是特别重要的,因为开始层的特征变化更大,它需要比其他层更多的节点来模拟特征模式。然而,如果每层节点太大,它容易在训练数据上过拟合。一般来说,宽且浅的模型容易过拟合,深且窄的模型谷易欠拟合。事实上,如果有一层很小(通常称为瓶颈),模型性能将有重大的下降,特别是瓶...

网络结构可以被认为是另外需要确定的参数。既然每层可以被认为是前一层的特征抽取器,每层节点的数量应该足够大以获取本质的模式。这在模型低层是特别重要的,因为开始层的特征变化更大,它需要比其他层更多的节点来模拟特征模式。然而,如果每层节点太大,它容易在训练数据上过拟合。
一般来说,宽且浅的模型容易过拟合,深且窄的模型谷易欠拟合。
事实上,如果有一层很小(通常称为瓶颈),模型性能将有重大的下降,特别是瓶颈层接近输人层。如果每层有相同数量的节点,添加更多的层可能把模型从过拟合转为欠拟合。这是因为附加的层对模型参数施加了额外的限制。由这个现象,我们可以先在只有一个隐层的神经网络上优化每层的节点个数,然后再叠加更多的相同节点个数的隐层。
在语音识别任务中,我们发现拥有5~7层,每层拥有1000~3000个节点的 DNN效果很好。相对一个窄且浅的模型,通常在一个宽且深的模型上更容易找到一个好的配置。这是因为在宽且深的模型上有更多好的性能相似的局部最优点。
在kaldi中最终训练好的模型是final.mdl,是二进制的,可以把它转换成文本格式查看
以下这个命令可以转换为文本文件
net3-copy --binary=fasle **/final.mdl **/final.txt
使用以下命令可以看到模型文件内容:
net3-am-copy --binary=fasle **/final.mdl **/final.mdl.info
以下是kaldi中aishell的DNN模型结构;

num-parameters: 12187374
modulus: 1
input-node name=ivector dim=100
input-node name=input dim=43
component-node name=lda component=lda input=Append(Offset(input, -1), input, Offset(input, 1), ReplaceIndex(ivector, t, 0)) input-dim=229 output-dim=229
component-node name=tdnn1.affine component=tdnn1.affine input=lda input-dim=229 output-dim=625
component-node name=tdnn1.relu component=tdnn1.relu input=tdnn1.affine input-dim=625 output-dim=625
component-node name=tdnn1.batchnorm component=tdnn1.batchnorm input=tdnn1.relu input-dim=625 output-dim=625
component-node name=tdnn2.affine component=tdnn2.affine input=Append(Offset(tdnn1.batchnorm, -1), tdnn1.batchnorm, Offset(tdnn1.batchnorm, 1)) input-dim=1875 output-dim=625
component-node name=tdnn2.relu component=tdnn2.relu input=tdnn2.affine input-dim=625 output-dim=625
component-node name=tdnn2.batchnorm component=tdnn2.batchnorm input=tdnn2.relu input-dim=625 output-dim=625
component-node name=tdnn3.affine component=tdnn3.affine input=Append(Offset(tdnn2.batchnorm, -1), tdnn2.batchnorm, Offset(tdnn2.batchnorm, 1)) input-dim=1875 output-dim=625
component-node name=tdnn3.relu component=tdnn3.relu input=tdnn3.affine input-dim=625 output-dim=625
component-node name=tdnn3.batchnorm component=tdnn3.batchnorm input=tdnn3.relu input-dim=625 output-dim=625
component-node name=tdnn4.affine component=tdnn4.affine input=Append(Offset(tdnn3.batchnorm, -3), tdnn3.batchnorm, Offset(tdnn3.batchnorm, 3)) input-dim=1875 output-dim=625
component-node name=tdnn4.relu component=tdnn4.relu input=tdnn4.affine input-dim=625 output-dim=625
component-node name=tdnn4.batchnorm component=tdnn4.batchnorm input=tdnn4.relu input-dim=625 output-dim=625
component-node name=tdnn5.affine component=tdnn5.affine input=Append(Offset(tdnn4.batchnorm, -3), tdnn4.batchnorm, Offset(tdnn4.batchnorm, 3)) input-dim=1875 output-dim=625
component-node name=tdnn5.relu component=tdnn5.relu input=tdnn5.affine input-dim=625 output-dim=625
component-node name=tdnn5.batchnorm component=tdnn5.batchnorm input=tdnn5.relu input-dim=625 output-dim=625
component-node name=tdnn6.affine component=tdnn6.affine input=Append(Offset(tdnn5.batchnorm, -3), tdnn5.batchnorm, Offset(tdnn5.batchnorm, 3)) input-dim=1875 output-dim=625
component-node name=tdnn6.relu component=tdnn6.relu input=tdnn6.affine input-dim=625 output-dim=625
component-node name=tdnn6.batchnorm component=tdnn6.batchnorm input=tdnn6.relu input-dim=625 output-dim=625
component-node name=prefinal-chain.affine component=prefinal-chain.affine input=tdnn6.batchnorm input-dim=625 output-dim=625
component-node name=prefinal-chain.relu component=prefinal-chain.relu input=prefinal-chain.affine input-dim=625 output-dim=625
component-node name=prefinal-chain.batchnorm component=prefinal-chain.batchnorm input=prefinal-chain.relu input-dim=625 output-dim=625
component-node name=output.affine component=output.affine input=prefinal-chain.batchnorm input-dim=625 output-dim=4312
output-node name=output input=output.affine dim=4312 objective=linear
component-node name=prefinal-xent.affine component=prefinal-xent.affine input=tdnn6.batchnorm input-dim=625 output-dim=625
component-node name=prefinal-xent.relu component=prefinal-xent.relu input=prefinal-xent.affine input-dim=625 output-dim=625
component-node name=prefinal-xent.batchnorm component=prefinal-xent.batchnorm input=prefinal-xent.relu input-dim=625 output-dim=625
component-node name=output-xent.affine component=output-xent.affine input=prefinal-xent.batchnorm input-dim=625 output-dim=4312
component-node name=output-xent.log-softmax component=output-xent.log-softmax input=output-xent.affine input-dim=4312 output-dim=4312
output-node name=output-xent input=output-xent.log-softmax dim=4312 objective=linear
......
......
......

DNN的基本结构
神经网络是基于感知机的扩展,而DNN可以理解为有很多隐藏层的神经网络。多层神经网络和深度神经网络DNN其实也是指的一个东西,DNN有时也叫做多层感知机(Multi-Layer perceptron,MLP)。
从DNN按不同层的位置划分,DNN内部的神经网络层可以分为三类,输入层,隐藏层和输出层,如下图示例,一般来说第一层是输入层,最后一层是输出层,而中间的层数都是隐藏层。
image.png
层与层之间是全连接的,也就是说,第i层的任意一个神经元一定与第i+1层的任意一个神经元相连。虽然DNN看起来很复杂,但是从小的局部模型来说,还是和感知机一样,即一个线性关系image.png加上一个激活函数image.png
由于DNN层数多,则我们的线性关系系数w和偏倚b的数量也就是很多了。具体的参数在DNN是如何定义的呢?
首先看线性关系系数w的定义。以下图一个三层的DNN为例,第二层的第4个神经元到第三层的第2个神经元的线性关系定义为 image.png上标3代表线性系数w所在的层数,而下标对应的是输出的第三层索引2和输入的第二层索引4。你也许会问,为什么不是image.png呢?这主要是为了便于模型用于矩阵表示运算,如果是image.png而每次进行矩阵运算是image.png,需要进行转置。将输出的索引放在前面的话,则线性运算不用转置,即直接为image.png。第l−1层的第k个神经元到第l层的第j个神经元的线性系数定义为image.png。注意,输入层是没有w参数的。
image.png
再看偏倚b的定义。还是以这个三层的DNN为例,第二层的第三个神经元对应的偏倚定义为image.png其中,上标2代表所在的层数,下标3代表偏倚所在的神经元的索引。同样的道理,第三层的第一个神经元的偏倚应该表示为image.png
.输出层是没有偏倚参数的。
image.png
DNN前向传播算法数学原理
假设选择的激活函数是 image.png,隐藏层和输出层的输出值为 image.png,则对于下图的三层DNN,利用和感知机一样的思路,我们可以利用上一层的输出计算下一层的输出,也就是所谓的DNN前向传播算法。
image.png
image.png
DNN前向传播算法
所谓的DNN前向传播算法就是利用若干个权重系数矩阵W,偏倚向量b来和输入值向量x进行一系列线性运算和激活运算,从输入层开始,一层层的向后计算,一直到运算到输出层,得到输出结果为值。
输入: 总层数L,所有隐藏层和输出层对应的矩阵W,偏倚向量b,输入值向量x
输出:输出层的输出。
image.png
DNN反向传播算法的基本思路
在进行DNN反向传播算法前,我们需要选择一个损失函数,来度量训练样本计算出的输出和真实的训练样本输出之间的损失。

DNN可选择的损失函数有不少,为了专注算法,这里使用最常见的均方差来度量损失。即对于每个样本,我们期望最小化下式:
image.png
损失函数有了,用梯度下降法迭代求解每一层的w,b。
首先是输出层第L层。注意到输出层的W,b满足下式:
image.png
对于输出层的参数,损失函数变为:
image.png
求解W,b的梯度:
image.png
image.png
image.png
DNN反向传播算法过程
由于梯度下降法有批量(Batch),小批量(mini-Batch),随机三个变种,为了简化描述,这里我们以最基本的批量梯度下降法为例来描述反向传播算法。实际上在业界使用最多的是mini-Batch的梯度下降法。区别仅仅在于迭代时训练样本的选择。

输入:总层数L ,以及各隐藏层与输出层的神经元个数,激活函数,损失函数,迭代步长 a ,最大迭代次数 max与停止迭代阈值image.png,输入的m个训练样本

输出:各隐藏层与输出层的线性关系系数矩阵W和偏倚向量。

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

评论(0

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

全部回复

上滑加载中

设置昵称

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

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

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