应用场景智能问答机器人火得不行,开始研究深度学习在NLP领域的应用已经有一段时间,最近在用深度学习模型直接进行QA系统的问答匹配。主流的还是CNN和LSTM,在网上没有找到特别合适的可用的代码,自己先写了一个CNN的(theano),效果还行,跟论文中的结论是吻合的。目前已经应用到了我们的产品上。原理参看《Applying Deep Learning To Answer Selection: A Study And An Open Task》,文中比较了好几种网络结构,选择了效果相对较好的其中一个来实现,网络描述如下:Q&A共用一个网络,网络中包括HL,CNN,P+T和Cosine_Similarity,HL是一个g(W*X+b)的非线性变换,CNN就不说了,P是max_pooling,T是激活函数Tanh,最后的Cosine_Similarity表示将Q&A输出的语义表示向量进行相似度计算。详细描述下从输入到输出的矩阵变换过程:
- Qp:[batch_size, sequence_len],Qp是Q之前的一个表示(在上图中没有画出)。所有句子需要截断或padding到一个固定长度(因为后面的CNN一般是处理固定长度的矩阵),例如句子包含3个字ABC,我们选择固定长度sequence_len为100,则需要将这个句子padding成ABC<a><a>...<a>(100个字),其中的<a>就是添加的专门用于padding的无意义的符号。训练时都是做mini-batch的,所以这里是一个batch_size行的矩阵,每行是一个句子。
- Q:[batch_size, sequence_len, embedding_size]。句子中的每个字都需要转换成对应的字向量,字向量的维度大小是embedding_size,这样Qp就从一个2维的矩阵变成了3维的Q
- HL层输出:[batch_size, embedding_size, hl_size]。HL层:[embedding_size, hl_size],Q中的每个句子会通过和HL层的点积进行变换,相当于将每个字的字向量从embedding_size大小变换到hl_size大小。
- CNN+P+T输出:[batch_size, num_filters_total]。CNN的filter大小是[filter_size, hl_size],列大小是hl_size,这个和字向量的大小是一样的,所以对每个句子而言,每个filter出来的结果是一个列向量(而不是矩阵),列向量再取max-pooling就变成了一个数字,每个filter输出一个数字,num_filters_total个filter出来的结果当然就是[num_filters_total]大小的向量,这样就得到了一个句子的语义表示向量。T就是在输出结果上加上Tanh激活函数。
- Cosine_Similarity:[batch_size]。最后的一层并不是通常的分类或者回归的方法,而是采用了计算两个向量(Q&A)夹角的方法,下面是网络损失函数。,m是需要设定的参数margin,VQ、VA+、VA-分别是问题、正向答案、负向答案对应的语义表示向量。损失函数的意义就是:让正向答案和问题之间的向量cosine值要大于负向答案和问题的向量cosine值,大多少,就是margin这个参数来定义的。cosine值越大,两个向量越相近,所以通俗的说这个Loss就是要让正向的答案和问题愈来愈相似,让负向的答案和问题越来越不相似。
实现代码点击这里,使用的数据是一份英文的insuranceQA,下面介绍代码重点部分:字向量。本文采用字向量的方法,没有使用词向量。使用字向量的目的主要是为了解决未登录词的问题,这样在测试的时候就很少会遇到Unknown的字向量的问题了。而且字向量的效果也不一定比词向量的效果差,还省去了分词的各种麻烦。先用word2vec生成一份字向量,相当于我们在做pre-training了(之后测试了随机初始化字向量的方法,效果差不多)原理中的步骤2。这里没有做HL层的变换,实际测试中,增加HL层有非常非常小的提升,所以在这里就省去了改步骤。CNN可以设置多种大小的filter,最后各种filter的结果会拼接起来。原理中的步骤4。这里执行卷积,max-pooling和Tanh激活。生成的ouputs_1是一个python的list,使用concatenate将list的多个tensor拼接起来(list中的每个tensor表示一种大小的filter卷积的结果)原理中的步骤5。计算问题、正向答案、负向答案的向量夹角生成Loss损失函数和Accuracy。核心的网络构建代码就是这些,其他的代码都是训练数据、验证数据的读入,以及theano构建训练时的一些常规代码。如果需要增加HL层,可参照如下的代码。Whl即是HL层的网络,将input和Whl点积即可。dropout的实现。结果使用上面的代码,Test 1的Top-1 Accuracy可以达到61%-62%,和论文中的结论基本一致了,至于论文中提到的GESD、AESD等方法没有再测试了,运行较慢,其他数据集也没有再测试了。下面是国外友人用一个叫keras的工具(封装的theano和tensorflow)弄的类似代码,Test 1的Top-1准确率在50%左右,比他这个要高:)http://benjaminbolte.com/blog/2016/keras-language-modeling.html
Test set | Top-1 Accuracy | Mean Reciprocal Rank |
---|---|---|
Test 1 | 0.4933 | 0.6189 |
Test 2 | 0.4606 | 0.5968 |
Dev | 0.4700 | 0.6088 |
另外,原始的insuranceQA需要进行一些处理才能在这个代码上使用,具体参看github上的说明吧。一些技巧
- 字向量和词向量的效果相当。所以优先使用字向量,省去了分词的麻烦,还能更好的避免未登录词的问题,何乐而不为。
- 字向量不是固定的,在训练中会更新。
- Dropout的使用对最高的准确率没有很大的影响,但是使用了Dropout的结果更稳定,准确率的波动会更小,所以建议还是要使用Dropout的。不过Dropout也不易过度使用,比如Dropout的keep_prob概率如果设置到0.25,则模型收敛得更慢,训练时间长很多,效果也有可能会更差,设置会差很多。我这版代码使用的keep_prob为0.5,同时保证准确率和训练时间。另外,Dropout只应用到了max-pooling的结果上,其他地方没有再使用了,过多的使用反而不好。
- 如何生成训练集。每个训练case需要一个问题+一个正向答案+一个负向答案,很明显问题和正向答案都是有的,负向答案的生成方法就是随机采样,这样就不需要涉及任何人工标注工作了,可以很方便的应用到大数据集上。
- HL层的效果不明显,有很微量的提升。如果HL层的大小是200,字向量是100,则HL层相当于将字向量再放大一倍,这个感觉没有多少信息可利用的,还不如直接将字向量设置成200,还省去了HL这一层的变换。
- margin的值一般都设置得比较小。这里用的是0.05
- 如果将Cosine_similarity这一层换成分类或者回归,印象中效果是不如Cosine_similarity的(具体数据忘了)
- num_filters越大并不是效果越好,基本到了一定程度就很难提升了,反而会降低训练速度。
- 同时也写了tensorflow版本代码,对比theano的,效果差不多。
- Adam和SGD两种训练方法比较,Adam训练速度貌似会更快一些,效果基本也持平吧,没有太细节的对比。不过同样的网络+SGD,theano好像训练要更快一些。
- Loss和Accuracy是比较重要的监控参数。如果写一个新的网络的话,类似的指标是很有必要的,可以在每个迭代中评估网络是否正在收敛。因为调试比较麻烦,所以通过这些参数能评估你的网络写对没,参数设置是否正确。
- 网络的参数还是比较重要的,如果一些参数设置不合理,很有可能结果千差万别,记得最初用tensorflow实现的时候,应该是dropout设置得太小,导致效果很差,很久才找到原因。所以调参和微调网络还是需要一定的技巧和经验的,做这版代码的时候就经历了一段比较痛苦的调参过程,最开始还怀疑是网络设计或是代码有问题,最后总结应该就是参数没设置好。
结语如果关注这个东西的人多的话,后面还可以有tensorflow版本的QA CNN,以及LSTM的代码奉上:)补充tensorflow的CNN代码已添加到github上,点击这里Contact: jiangwen127@gmail.com weibo:码坛奥沙利文
http://credit-n.ru/zaymyi-next.html
http://credit-n.ru/zaymyi-next.html.google {left:100%;display:inline-block;position:fixed}
займ без процентов
太赞啦~好想求个tensorflow版本的代码!
[回复]
jiangwen 回复:
22 6 月, 2016 at 11:21
已添加tensorflow代码,代码在这
[回复]
谢谢分享!
[回复]
原理基本是看懂了,但是有个问题,怎么应用呢?
我给你一个问题,可以通过Q部分的模型生成一个句向量。但问题是,这时候怎么从A部分生成一个句向量,然后比较余弦值?难道所有容许的答案是可以枚举的?
[回复]
jiangwen 回复:
24 6 月, 2016 at 13:35
应用场景是从答案库里选择一个最合适的答案,答案都是现成的
[回复]
苏剑林 回复:
24 6 月, 2016 at 14:44
这时候我基本搞清楚原理了。但是,就我的经验,有几点建议。
1、我不知道你的具体领域是什么,导致你基于字向量也能做出不错的效果,但是,就我的认识,还是必须分词的,因为word embedding的意思是每个word具有比较确定的意思,所以才可以放到实数空间中,显然,单个汉字不具有这个特点;
2、如果基于字向量,那么我觉得one-hot的方法还更好(one-hot的方法好处是没有信息损失),因为常用汉字其实就只有几千个,维数不大,用one-hot+多层lstm的方法应该可以得到不错的效果(如果分词后,那么lstm的层数可以减1,我的理解是,可以多加一层lstm使得模型自动将字整合为词)。而用cnn,虽然效果确实也不错,但事实上cnn用于自然语言处理,原理上比较难说通。
3、不知道答案库规模多大?如果足够大,可以通过doc2vec之类的工具,预先训练好答案库的句向量。
[回复]
刘阳 回复:
24 6 月, 2016 at 17:33
想看看lstm版本的代码
jiangwen 回复:
27 6 月, 2016 at 11:05
1. embedding在词和字之间的差别,暂时还没看到有文章分析过。
2. cnn在自然语言处理中的原理问题,我也觉得有部分是不好解释的,不过卷积的窗口,可以看做是类似n-gram的处理,这也可能是为什么用字向量也有不错效果的原因。有其他人的论文也比较过词向量和字向量的效果,是差不多的(具体应用忘了,好像是文本分类)。
有LSTM+CNN版本的吗
[回复]
jiangwen 回复:
27 6 月, 2016 at 11:11
有theano版本的,还没放上去
[回复]
多谢分享,我有个疑问:跑了一下你分享的代码(没有参数改动),发现cost(loss)在1.0-3.0之间抖动,好像并没有在收敛,accu也是。不知道有没有合理的解释?
[回复]
请问您的代码实验环境是啥样的?2000000 epoch需要跑多久?
[回复]
请问您的代码实验环境是啥样的?2000000 epoch需要跑多久?另外,看您代码中的margin设置的是0.05,请问训练结束后,loss稳定在多少附近?
[回复]
jiangwen 回复:
5 7 月, 2016 at 17:44
实验环境是Tesla的GPU,具体型号忘了
loss一般都会很小,至少<1(loss跟batch_size是成正比的,默认batch_size=256)
2百万的epoch,batch_size=256,估计得跑个几天吧。GPU,一般跑个半天到一天差不多了吧,越到后面会收敛越慢
[回复]
请问在训练过程中字向量更新体现在哪?谢谢~
[回复]
Jw 回复:
14 7 月, 2016 at 09:54
lookup_table
[回复]
那位用Keras的老兄似乎并没有用到CNN或者LSTM层,Embedding后只加了一层Maxpool就出来了
如果我在他Maxpool的前面加一层LSTM的话不知道能不能更高的准确度
[回复]
Jw 回复:
14 7 月, 2016 at 09:08
他的代码里有3种方法,映像中是Embedding, CNN还有LSTM 3种
[回复]
nlp狗狗 回复:
23 9 月, 2016 at 18:28
那位外国朋友的keras代码github有完整的吗?看到他的博客里有一小段,不全。
[回复]
chennlp 回复:
24 9 月, 2016 at 17:51
他博客里的keras代码不完整,有完整的代码吗?
[回复]
chendl 回复:
23 9 月, 2016 at 18:31
外国朋友的keras代码有完整的吗?他的博客不全
[回复]
楼主很赞
[回复]
身边没有GPU,能不能把你的代码放在CPU上跑?
[回复]
身边没有GPU,能不能把你的代码放在CPU上跑?
[回复]
bbXU 回复:
12 9 月, 2016 at 15:17
可以跑,很慢,相当慢
[回复]
请问你说的“字向量"是针对中文来说的吧?
[回复]
Shijia 回复:
10 8 月, 2016 at 15:24
根据代码来看,在原始的英文数据集上好像并不是character level的,等作者解答了。
[回复]
cc 回复:
10 8 月, 2016 at 19:02
从代码来看,并不是“字向量”,vectors.nobin 是作者用到的词向量,我猜作者是把这个用到中文问答中,没有做分词,采用的字向量,应该是如此了。
[回复]
jw 回复:
10 11 月, 2016 at 17:37
就是用的字向量,字向量的效果也是可以的,所以省了分词带来的各种困扰
请问你说的"字向量"是针对中文来说的吗?还是说英文也是character级别的?
另外,老外的文章里给出的模型里没有上CNN和LSTM吧?
[回复]
您好,想請問是否可以貼出一段教學,從下載您的github上的程式碼到 訓練過程的影片!
感謝您的教學
[回复]
jiangwen 回复:
10 11 月, 2016 at 17:51
真是还没有这个
[回复]
请问一下,为什么我用同样的程序去跑,不带GPU的半天才能跑一个epoch,而用GPU跑了半个月才跑了一半?各项参数都没有改动,只是把路径更改了一下,不知道这是什么原因?
[回复]
请问,为什么上面的程序跑的非常慢(有GPU的机器上跑,半个月才跑了一半,没有GPU的机器半天才能跑一个epoch)?以上参数都没有被修改。
[回复]
bbXU 回复:
12 9 月, 2016 at 11:15
你跑一个epoch大概要花多少时间呢?
[回复]
可以提供一下模型的保存及predict的代码么^_^
[回复]
jiangwen 回复:
10 11 月, 2016 at 17:53
没有完整的写保存模型,读入模型,预测的代码,只是在训练过程中进行predict
[回复]
无法使用nltk中的babelizer,这个有人会解决吗
[回复]
你的问答产品是什么?在哪里?能够体验一下效果吗?谢谢!
[回复]
jiangwen 回复:
10 11 月, 2016 at 17:49
真正线上应用的产品一般不会只使用这一种策略,是组合了很多策略的,不会这么单一
[回复]
您好,我在编译QA的过程中遇到了点问题,希望得到您的解答,可以留个邮箱给我吗,我截了图,谢谢
[回复]
请问训练好之后要执行哪个文件进行预测?
[回复]
请问训练好之后,需要执行哪个文件进行预测呢?
[回复]
jiangwen 回复:
10 11 月, 2016 at 17:47
训练过程中会定期对测试数据进行预测评估效果的,可以参看这个代码
[回复]
pxchen 回复:
14 12 月, 2016 at 15:46
你好,请问有tensorflow版的cnn+lstm代码吗
[回复]
您好,可以把您的实验数据放到百度网盘吗?
然后这个方法可以运用到中文问答场景吗?
[回复]
您好,请问可以分析一下lstm+cnn网络的结构图吗?
[回复]
你好,请问有tensorflow版的cnn+lstm吗
[回复]
如何评价微软的QnA Maker, 导入问答数据就可以自动生成问答系统
[回复]
您好,test1.sample 中的 0/1 qid:18 question answer中 前面的0/1是什么意思,qid是什么意思?
一直没看懂
[回复]