rnn整理

代码其实大前天就写好了,一直在出很奇怪的问题,即不知道什么原因h越更新越小。一直以为问题在数学层面,因此对环节进行了多种监控,且尝试多种方案,最后发现是自己的代码犯了一个低级的错误,即h的更新值应该是wo*do,但是代码中写成了dwo*do,导致h的更新变得极小,事后想想,这就意味着h对应结果来说变得无关紧要,所以h的各个向量在训练的过程中渐渐就是0了。不过这也带来另外一个好玩的现象,即如果把h每一次更新后都归一化处理,那么h的每个维度都将收敛为1/sum_h

另外发现rnn对于简单函数的收敛其实挺快的,而且稳定性极高,因为为了解决上面的bug,我尝试了好几个方案,比如将h由[-1,1]的各维空间限制到球面空间下,或者每一轮对所有w矩阵进行归一化,后来改了bug以后发现这些因素都不影响对最终序列学习结果的影响,但某些改动下会对收敛速度产生影响。

这使我开始思考rnn训练过程中归一化对结果的影响。诚然作为开关作用的W矩阵理论上是否进行归一化都不影响,因为层与层之间有softmax衔接,而softmax天然是一种归一化的函数。但是如果不做w的归一化,理论上w值的精度空间是变化的,那么统一精度对结果有直接的影响吗?我们其实在很多浮点数转整数提高计算速度的场合中感受过这一点。事实上归一化能更好地体现矩阵计算的“开关”特点,但缺点是更新不太好算。

另外实现过程中也感受到,rnn的记忆h挺受干扰值影响的(这或许是attention机制的起源?),而且如lstm的叙述一样,h矩阵的记忆很短,这个可以在附录程序中调整nauty_num的大小来观察。那么理论上最佳的h应该是啥样的?其实理论上最佳的h应该是{x0,x1,x2,...xt-1}这样的格式,但是这样就对应这么多的W,没法训练,所以通过h这种形式进行压缩。h这种压缩方式等同于对X集合的扩充,只不过是通过tanh把x的信息加过去的,这里tanh也可以换成sigmoid或者干脆的0.9*wh*h_t-1+0.1*wx*xt,不会有本质的区别。

这样看,其实rnn和dnn也没有本质区别,只不过对历史信息加了一重更新方式。很好奇的是后来出现的一些借助外部知识来训练rnn的策略中外部知识是如何包装的?很大概率也是如h_t-1一样被压到了x上,这样的话本质上还是一种需要训练的分类器。

【github】https://github.com/wangyaomail/word2vec-demo/blob/master/src/main/java/cn/dendarii/RnnDemo.java