整理、收集自网上资源
sudo ldconfig /usr/local/cuda-9.0/lib64
sudo ln -sf /usr/local/cuda-9.0/lib64/libcudnn.so.7.0.5 /usr/local/cuda-9.0/lib64/libcudnn.so.7
在代码入口处加入一下代码:
import sys
sys.path.append('/project_path/module')
LSTM 的核心概念在于细胞状态以及“门”结构。细胞状态相当于信息传输的路径,让信息能在序列连中传递下去。可以将其看作网络的“记忆”。理论上讲,细胞状态能够将序列处理过程中的相关信息一直传递下去。
LSTM 有三种类型的门结构:遗忘门、输入门和输出门。
遗忘门
遗忘门用于决定应丢弃或保留哪些信息。来自前一个隐藏状态的信息和当前输入的信息同时传递到 sigmoid 函数中去,输出值介于 0 和 1 之间,越接近 0 意味着越应该丢弃,越接近 1 意味着越应该保留。
输入门
输入门用于更新细胞状态。首先将前一层隐藏状态的信息和当前输入的信息传递到 sigmoid 函数中去。将值调整到 0~1 之间来决定要更新哪些信息。0 表示不重要,1 表示重要。
其次还要将前一层隐藏状态的信息和当前输入的信息传递到 tanh 函数中去,创造一个新的侯选值向量。最后将 sigmoid 的输出值与 tanh 的输出值相乘,sigmoid 的输出值将决定 tanh 的输出值中哪些信息是重要且需要保留下来的。
细胞状态
下一步,就是计算细胞状态。首先前一层的细胞状态与遗忘向量逐点相乘。如果它乘以接近 0 的值,意味着在新的细胞状态中,这些信息是需要丢弃掉的。然后再将该值与输入门的输出值逐点相加,将神经网络发现的新信息更新到细胞状态中去。至此,就得到了更新后的细胞状态。
输出门
输出门用来确定下一个隐藏状态的值。首先,我们将前一个隐藏状态和当前输入传递到 sigmoid 函数中,然后将新得到的细胞状态传递给 tanh 函数。
最后将 tanh 的输出与 sigmoid 的输出相乘,以确定隐藏状态应携带的信息。再将隐藏状态作为当前细胞的输出,把新的细胞状态和新的隐藏状态传递到下一个时间步长中去。
1. 产生新数据型:过采样小样本(SMOTE),欠采样大样本
2. 对原数据的权值进行改变
3. 通过组合集成方法解决
4. 通过特征选择
过拟合可以通过阻止某些特征的协同作用来缓解。在每次训练的时候,每个神经元有百分之50的几率被移除,可以让一个神经元的出现尽量不依赖于另外一个神经元。另外,可以把dropout理解为模型平均。
如果采用dropout,训练时间延长。dropout在数据量比较小的时候,不建议使用,效果并没有特别好,dropout的值一般来说取值为0.5。
梯度消失的根源:深度神经网络和反向传播。
梯度消失经常出现,一是在深层网络中,二是采用了不合适的损失函数,比如sigmoid(sigmoid梯度不大于0.25,所以很容易发生梯度消失,tanh类似。)。
梯度爆炸一般出现在深层网络和权值初始化值太大的情况下。
解决办法:
- 预训练加微调:每次训练一层隐节点,在预训练完成后,再对整个网络进行“微调”。
- 梯度剪切、正则:设置一个梯度剪切阈值,更新梯度的时候,如果梯度超过这个阈值,就将其强制限制在这个范围之内。通过正则化项,可以部分限制梯度爆炸的发生。
- relu、leakrelu、elu等激活函数:如果激活函数的导数为1,那么就不存在梯度消失爆炸的问题了。
- batchnorm:批规范化,通过规范化操作将输出信号x规范化保证网络的稳定性。通过对每一层的输出规范为均值和方差一致的方法,消除了w带来的放大缩小的影响,进而解决梯度消失和爆炸的问题。
- 残差结构:短路机制可以无损地传播梯度,而另外一项残差梯度则需要经过带有weights的层,梯度不是直接传递过来的。残差梯度不会那么巧全为-1,而且就算其比较小,有1的存在也不会导致梯度消失。
- LSTM:LSTM通过它内部的“门”可以接下来更新的时候“记住”前几次训练的”残留记忆“。
这篇博文,
梯度消失和梯度爆炸
讲的比较透彻。
批量梯度下降的时候,每次更新w的迭代,要对所有的样本进行计算,样本非常大的情况下,计算量太大。因此实用的办法是随机梯度下降SGD, 由于样本的噪音和随机性,每次更新w并不一定总是按照减少E的方向,但总体上还是沿着减少E的方向前进的。
这里的随机是w的更新有一定的方向随机性,并不是随机抽取样本。对于非凸函数来说,存在许多局部最小值。随机性有助于我们逃离某些很糟糕的局部最小值,从而获得一个更好的模型。
Dropout暂时从网络中移除神经网络中的单元;
DropConnect通过随机丢弃权重而不是激活来扩展Dropout;
变分Dropout通过重复“输入,输出和循环层的每个时间步长相同的dropout掩码(在每个时间步骤丢弃相同的网络单元);
循环Dropout使用网络的隐藏状态作为计算门值和小区更新以及使用dropout的子网络的输入来正则化子网络;
Zoneout不是将某些单位的激活设置为0,而是随机替换某些单位的激活与他们从前一个时间步的激活。
Batch Normalization允许模型对参数的初始化不那么敏感,并且简化了学习速率的调整。
- 1、训练的更快
在每一轮训练中的前向传播和反向传播的额外计算会造成更慢的训练,Batch Normalization可以让收敛速度更快,总的训练时间更短。
- 2、容忍更高的学习率(learning rate)
为了网络能收敛,梯度下降通常需要更小的学习率。但是神经网络的层次越深,则反向传播时梯度越来越小,因此需要更多的训练迭代次数。Batch Normalization可以容忍更高的学习率,则梯度下降的幅度更大,也就加快了训练的速度。
- 3、让权重更容易初始化
权重的初始化通常来说比较麻烦,尤其是深层的神经网络。Batch Normalization可以降低权重初始化的值的分布的影响。
- 4、可支持更多的激活函数
有些激活函数在某些场景下表现很差,比如Sigmoid随层次的增加梯度衰减的很快,也就无法用在深层的神经网络。ReLU的问题是可能导致神经元的死亡(即ReLU掉入0的区域),所以要小心输入值的范围。Batch Normalization可以规范化输入,这些激活函数也可以使用了。
- 5、简化创建深层的神经网络
以上四点降低了创建神经网络的要求,可以创建更深层的神经网络,而更深层的网络也更有机会表现的更好。
- 6、提供了一点正则化的功能
虽然Batch Normalization在某些情况下增加了一些网络的噪声。在Inception模型中,Batch Normalization起到了和Dropout一样的正则化效果。考虑用Batch Normalization 替代一些Dropout的情况。
- 7、可能得到更好的结果
一些实验结果表明Batch Normalization 可以提高训练的表现。不过主要还是优化训练,让训练更快。网络训练的更快,就能迭代更多次或者收敛的更快,也能构建更深的网络。
目前为止并没有很好的评价embedding好坏的方法,只能采用一些比较合理的方式,去抽样查看。
- Relatedness:看看空间距离近的词,跟人的直觉是否一致
- Analogy:著名 A - B = C - D 词汇类比任务
- Categorization:看词在每个分类中的概率
- 聚类算法(可视化): kmeans 聚类,查看聚类分布效果 。若向量维度偏高,则对向量进行降维,并可视化。
输入序列非常长时,模型难以学到合理的向量表示;
序列输入时,随着序列的不断增长,原始根据时间步的方式的表现越来越差,这是由于原始的时间步模型设计的结构有缺陷,即所有的上下文输入信息都被限制到固定长度,整个模型的能力都同样受到限制,暂且把这种原始的模型称为简单的编解码器模型;
编解码器的结构无法解释,也就导致了其无法设计。
- 增加训练样本的量。
- 降低网络复杂度。
- 规范化:权重衰减(weight decay)或者 L2规范化。λ 越小,就偏向于最小化原始代价函数,反之,倾向于小的权重。
- L1 规范化:L1 规范化倾向于聚集网络的权重在 相对少量的高重要度连接上,而其他权重就会被驱使向 0 接近。L1通常用来鼓励稀疏解;换句话说,大多数模型参数值都接近于零。
- Dropout:弃权过程就如同大量不同网络的效果的平均那样。
虽然使用huffman树代替传统的神经网络,可以提高模型训练的效率,但是如果训练样本中的中心词w是一个很生僻的词,那么就需要沿着huffman树往下走很多,因为越是生僻的词,越是远离根节点。
Teacher
对softmax(T=20)的输出与原始label求loss。
Student
- 对softmax(T=20)的输出与Teacher的softmax(T=20)的输出求loss1。
- 对softmax(T=1)的输出与原始label求loss2。
- loss = loss1+loss2
可以这么理解:logits与 softmax都属于在输出层的内容, logits = tf.matmul(X, W) + bias, 再对logits做归一化处理,就用到了softmax:Y_pred = tf.nn.softmax(logits,name='Y_pred')