在港科大rnet(https://github.com/HKUST-KnowComp/R-Net) 实现的基础上作了复现 python
采用melt框架训练,缘由是港科大实如今工程上不是很完美,包括固定了batch size,固定了context 长度为400,过滤了超过长度的context,每一个batch长度都固定到400。 git
melt框架支持dynamic batch size, dynamic batch length,多gpu,修复了bucket的问题,目前采用长度<=400 batch size 64 长度> 400 batch size 32 github
Dynamic batch能够比较准确的eval 由于样本数目不必定是64的倍数, dynamic batch length 能够必定程度提速 而且结合buket和不一样的batch size来跑较长的文本,港科大去掉了部分长度>400的训练数据 api
港科大版README展现的效果(word+char) 安全
下载港科大代码修改hiden size 75->76 实际跑没有达到上面这么高的dev效果 实际效果以下 app
Word + char 框架
Exact Match: 70.39735099337749, F1: 79.38397223161256 dom
Word only ide
Exact Match: 69.59318826868495, F1: 78.64400408874889 函数
港科大word only
港科大 word + char hidden 76
Word + char hidden 75 (原始代码不作任何改动, python3 tensorflow_version: 1.8.0-dev20180330) 彷佛仍是没达到readme 给出的那么高效果 em 差不到0.6个点
训练速度以下图 采用单gpu gtx1080ti训练 硬盘ssd,参考港科大我提的issue 改动港科大默认hidden size 75 -> 76 (4的倍数)不然速度比较慢, 不使用bucket
buckets按照 40,361 加速一点点
两个gpu加速效果甚小
四个gpu
单gpu word + char 训练 char embedding和港科大同样 设置为8
对应大概0.35s/it
在这个数据集 其它一些实验结果
adadelta 0.5 learning rate效果最好 > adam 0.001 > adagrad 0.01
按照dev loss动态减少learning rate有效 和港科大同样采用decay patience 3, decay factor 0.5 后面也能够考虑对比采用监控dev em或者f1来作lr decay
Glove word embedding 若是finetune 效果提高一点
context和question采用不一样rnn encoder 效果变差。
4个gpu 64*4 batch size训练 因为batch size的增大 收敛加快,word only模型f1值 能达到0.783因为 单gpu batch size 64,可是比较奇怪是dev loss在30 epoch以后就呈现增加过拟合趋势 尽管dev em 和 f1指标没有过拟合倾向
后来我发现 始终和HKUST版本效果有差距 港科大代码实测 虽然em 70+ f1 78+ 若是只使用word em69.6 f1 78+, 差在哪里呢 只能说理解不深。。。 通过屡次实验定位。。。
这里发现开始阶段表现更好的模型 通常来讲最后效果也会好一些。
缘由在于dropout。。
我改为了这个样子
而 港科大代码是dropout放在了类的初始化函数中 每一个layer一个dropout
A Theoretically Grounded Application of Dropout in
Recurrent Neural Networks
The variant Gal and Ghahramani [6] proposed is to use the same dropout mask at each time step for both inputs, outputs, and recurrent layers.
来自 <https://becominghuman.ai/learning-note-dropout-in-recurrent-networks-part-1-57a9c19a2307>
来自 <https://www.tensorflow.org/api_docs/python/tf/contrib/rnn/DropoutWrapper>
这里港科大采用的是variational_recurrent 也就是个人修改也没有改动这个事实 港科大为啥要放在init里面?
来自 <https://www.tensorflow.org/api_docs/python/tf/layers/dropout>
我理解主要目的是为了context 和question在一次训练batch中 同一个context和question共享 相同的droput
不过港科大 处理char的时候 context question 的embedding用了不一样droput。。 彷佛用cudnn gru 也不太好share。。。由于是3纬度的 展开以后 batch size 不同 对应不上去了 固然也能够实现share(title + reshape)
为啥港科大对单层 char 没有用cudnn gru 仍是用普通gru 我理解同样的 我这里仍是使用cudnn gru对char建模
Ok 最后是运行效果
对应squad 这个 HKUST只选了glove有预训练的词 而且不finetune,实测是否finetune word embedding针对这个应用效果差很少
特别须要注意是context 和 question须要共享相同的dropout不然效果变差
无一例外 不共享的.nsd效果差
实验了 模仿HKUST的gloveonly,选取训练集合中min_count>=10的做为vocab增长少许不在glove中的词(91590->91651不到100),选取glove+train全量
彷佛min count10 效果好一点点
Word only采用了context和question共享droput以后 效果追上HKUST效果
比较明显的 貌似 gloveonly 仍是 finetune效果比较好 说明基本都是训练中出现充分的词汇finetune效果会好一些 ?
可是rnet.fixebm效果好于rnet说明 可能 部分词汇finetune了 部分没训练到 用的初始随机变量 效果会稍微差一些?
Word only版本 min10.fixemb效果好 说明若是词汇出现足够 训练过程当中参与rnn调整 对效果有必定提高 尽管随机的词向量不变化
看dev指标提高一点 训练数据指标提高明显 包括loss 可是看dev loss已经有明显过拟合倾向 虽然dev指标仍是稍好 安全起见squad能够不finetune word embedding, char embedding能够补充不常见词的知识。
TODO 能够尝试 ngram embedding 而且尝试不一样组合方式 以及添加highway network 和 layer normalization.
下图是我对char 也采用了 droput share机制 相似word对应.chsd 不过看起来相比word的droput share 对结果影响不大。。 因为模型自己存在随机性 几个对比效果以下
不过感受效果仍是比HKUST的实现差一丢丢,尽快已经不多了,可是特别eval loss 仍是明显比HKUST的高 word only 2.9+ word+char 2.88 大概 而 HKUST word only 2.88 word+char 2.85大概
又仔细看了下HKUST的预处理发现一个diff 我对于 unk的处理和HKUST不同 HKUST使用的是全0向量做为unk向量 而且训练过程当中再也不变化。。
尽管直觉感受这样很差。。 不过我仍是实验了一下 设置 specail_as_zero == True(对应模型.hkust)
可是这组实验仍是dev loss 不如HKUST。。这不是关键性的影响
最后考虑是不是bucket 400 影响了效果 尽管>400的文本很少 是个人的tf bucket代码使用 或是 tf bucket自己影响了随机性?
HKUST注意是去掉了全部>400长度的文章,这里我仍是使用全部文章 可是不使用bucket(nobucket) bucket貌似有一点点影响 不大
另外以前我使用了手工设定的按照特定epoch修改learning rate的方式,这里也改成使用HKUST的patience 3,decay 0.5的方式。
实验结果wordonly基本打平了HKUST的原版效果,尽管彷佛dev loss后期不是如HKUST原版稳定可是数值基本一致,
这说明decay是很是重要的。。。 可能由于训练数据集合比较小,针对dev监控的动态学习率调整比较关键。 而bucket若是只是400这个影响不大。
另外语料不是太大 随机性也起到很大效果 可能陷入局部最优 不能找到全局最优
HKUST word only 最佳
Em 69.69 F1 78.69 dev loss 2.87
可是word_char效果仍是明显比HKUST的差 , 定位问题应该出在char的处理上,注意HKUST的char的encoding是context和qustion 先走的recurrent droput 也就是说
noise_shape=[shape[0], 1, shape[-1]]状况下的droputout,因而改原来word和char都用cudnn gru一样encoding方式为HKUST原版的处理方式。
不过这里实际用cudnn效果差的缘由是last state不对,由于cudnn不支持动态长度 因此padding 0也参与计算了 最后last state极可能是padding 0对应的 因此不许确。
实现效果 dev loss效果确实更贴近了2.85 可是f1值仍是有差距79.1
HKUST word+char 最佳
Em 70.37 F1 79.34 dev loss 2.85 效果彷佛还有一点点上升趋势
不过按照HKUST的issue
2.82 in dev, 1.32 in train
来自 <https://github.com/HKUST-KnowComp/R-Net/issues/6>
另一个遗留的问题 cudnn gru HKUST的实现 我搬动到其它地方使用save模型 只有 cudnn_gru/opaque_kernel (DT_FLOAT) [87096]
可是原版代码 save以后都是带有
不知道是哪里影响的 我猜想这里不影响模型效果 opaque_kernel 应该存储了全部信息,下面的只是不知道什么开关影响的 存储了转换后的权重数据。
Finally 总结
测试集合长度偏短 显然位置的softmax loss会低一点了 不过彷佛去掉长文本以后效果还稳定了一些 并且提高了f1值。。 那么说明loss须要考虑下文本长度吗? TODO
Train 87599
Dev 10570
Filter context > 400 or qeustion > 100
Train 87391 99.7%
Dev 10483 99.1%
HKUST 60k step
Exact Match: 70.39735099337749, F1: 79.3839722316126
gezi@gezi:~/mine/hasky/deepiu/squad$ c0 sh ./train/evaulate.sh ./mount/temp/squad/model/rnet.hkust/epoch/model.ckpt-37.00-50505
Mine:
[('metric/valid/loss', 2.892608779740621), ('metric/valid/em', 0.7044465468306528), ('metric/valid/f1', 0.7919028182076531)]
epoch:37.3626/60 valid_step:51000 valid_metrics:['metric/valid/loss:2.87955', 'metric/valid/em:0.70466', 'metric/valid/f1:0.79236']
[('metric/valid/loss', 2.9003277952412523), ('metric/valid/em', 0.7048249763481551), ('metric/valid/f1', 0.7928490855150692)]
最高
./mount/temp/squad/model/rnet.hkust/epoch/model.ckpt-39.00-53235
[('metric/valid/loss', 2.890633753983371), ('metric/valid/em', 0.7052980132450332), ('metric/valid/f1', 0.7933954605863096)]
60k
[('metric/valid/loss', 2.887699271541044), ('metric/valid/em', 0.7054872280037843), ('metric/valid/f1', 0.7925004189917955)]
仍是f1差一点点 那么 发现HKUST的init state是训练出来的 而不是使用它zero默认初始化的方式 修改按照HKUST的方式
Model增长变量。。
rnet/main/encoding/cudnn_gru/bw_0/init_state (DT_FLOAT) [1,1,76]
效果。。 就差在这个地方了 至少对应squad的应用 init state是很是影响效果的。。。 貌似以前各类应用场景都是用的zero做为init state 能够试一下对init state训练。
Rnet.hkust 表示char使用random uniform
rnet.hkust2 表示char使用random normal stddev 0.01
Rnet.hkust3 表示char使用相似google transofer的random normal , stddev dim ** -0.5
Rnet.hkust.initstate同rnet.hkust可是 使用了 训练 init state
貌似hkust3的初始化方式好一点 dev loss
因为测试数据少了一小部分 最终测试结果
[('metric/valid/loss', 2.871750499110624), ('metric/valid/em', 0.7094607379375591), ('metric/valid/f1', 0.7962988473545626)]
gezi@gezi:~/mine/hasky/deepiu/squad$ c3 sh ./train/evaulate.sh ~/temp/squad/model/rnet.hkust.initstate/epoch/model.ckpt-40.00-54600
HKUST 60k step
Exact Match: 70.39735099337749, F1: 79.3839722316126
基本完整复现了 HKUST RNET固然是在几乎彻底copy的基础上 不过作了不少工程改进,有了这个baseline, 更加方便后续实验对比,有了这个baseline,后面能够尝试快速的conv seq2seq, transformer, qanet中的encoder用在文本分类和图文关系的效果。
最终定位了 影响最终效果的几个核心地方 感谢HKUST无私的代码开源! cudnn的使用 droput的使用 init state参与训练,太赞了
TODO:
Train init state的效果和 <S> </S> 相比呢 是否添加 <S>能够起到一样效果?
Random normal google transform效果不错的 特别是dev loss相对较低 可是随机性起到了更大的做用 也就是说相同配置 不一样运行 结果可能会diff很大 特别是因为动态learning rate调整的不肯定性形成 最终结果不肯定性很大
最终目前跑出来的最佳结果 对应
使用random uniform embedding, char dim 8, glove only vocab, not finetune word embedding, char use share dropuout using cudnn gru with outpout max pooling, using all trainging data for train,dev data for evaluate, buckets [400] batchs size[64,32]
75k step EM 70.96 F1 79.86