使用Doc2Vec模型参加Kaggle的NLP比赛,最终score达到0.97,前2%。
本文所有的代码都可以在我的github上找到。
在上一篇博文中,我们使用了TF-IDF
,准确率达到了0.95
,已经进入前100,但还不够,我们试试使用更加高大上的Doc2Vec
结合神经网络模型,其准确率能否再次提升。
数据介绍
- 本数据来源于IMDB电影评论集,是Kaggle上一个入门的项目。
- 在Kaggle上详细使用了
word2vec
进行向量化,本文主要介绍Doc2Vec
模型的使用,并使用神经网络模型提高准确率。 - 数据包括
- **测试数据(testData):**25000条
- 未标注数据(unlabelData):5000条
- 训练数据(trainData):25000条(正负情感各一半)
- 每个电影的评论都不超过30条(测试集电影与训练集电影不相同)
- 我们使用所有数据来作为
doc2vect
模型的语料
- 本文主要使用python中的
pandas
、nltk
、gensim
、TensorFlow
库进行数据清理和分析。 - 注意,在kaggle上,针对此题的评分标准是按照
ROC
曲线(AUC) ,并不是硬分类,参考Wikipedia。
数据预处理
此部分的JupyterNotebook
可参考这里。
对于拿到的电影评论数据,我们需要进行数据清理以后才能使用doc2vec
进行向量化。
本文采用pandas
库进行数据预处理,好处是可以使用apply
函数对其进行并发操作,提高效率。
NLP的数据预处理一般包括以下几个部分:
- 如果是网页内容,首先需要去掉
Html tag
- 将文档分割成句子。
- 将句子分割成词语。
- 纠正拼写错误(可选)、替换缩写词(可选)。
Lemmatizer
、stemming
- 词型还原或词干提取,两者的差别可参考这里
- 去除停顿词,去除标点、转化为小写字母。
- 去掉长度过短的词语。
本项目没有纠正拼写错误(口语化词语太多)、没有去处数字(实测会提高精度)最后将训练集分开(pos
和neg
)。
Doc2vec
此部分的JupyterNotebook
可参考这里。
Doc2Vec
模型比较复杂,相对于word2vec
模型,它可以直接得到每个文档的向量,省略了将词向量转换为段向量的过程。
由于其直接是段向量,因此考虑了词之间的顺序,具有较好的语义信息。
而传统的word2vec
模型,使用平均词向量或聚类的方式得到的段向量没有考虑词的顺序。
输入数据
test.txt
:25000 条评论用于测试train-neg.txt
: 12500 条negative 评论用于训练train-pos.txt
: 12500 条positive 评论用于训练train-unsup.txt
: 50000 条未标注的评论用于训练doc2vec
模型
格式化数据
我们需要将每句话以LabeledSentence
类的形式传入gensim
的Doc2Vec
模型中:
[['word1', 'word2', 'word3',..., 'lastword'], ['label1']]
其中,label
标签是指这一段话的唯一标签,在官方文档中这个标签可以有多个(本文只使用一个),可以用标签提取当前模型训练出来的段向量。
传统的LabeledSentence
类只能接受一个文件,而我们需要将多个文件一起进行训练,因此我们对其进行简单的封装,生成我们的LabeledLineSentence
类,再使用gensim
的TaggedDocument
函数转化为合适的格式。
其中,LabeledSentence.sentences_perm()
是为了我们之后训练时能够随机打乱段落顺序,这能有效提高训练准确率。
现在,我们只需要传入一个字典(数据路径+数据前缀)即可,请记住,数据的前缀必须要唯一,例如:
sources = {'CleanData\\test.txt': 'TEST', 'CleanData\\train-neg.txt': 'TRAIN_NEG','CleanData\\train-pos.txt': 'TRAIN_POS', 'CleanData\\train-unsup.txt': 'TRAIN_UNS'}
sentences = LabeledLineSentence(sources)
训练模型
训练模型的过程比较简单,分为模型构建
、构建词汇
、模型训练
、模型序列化
,主要代码为:
self.model = Doc2Vec(min_count=40, window=15, size=400, sample=1e-4, negative=5, workers=7)
self.model.build_vocab(self.sentences.to_array())
self.model.train(self.sentences.shuffle(),total_examples=self.model.corpus_count, epochs=self.model.iter)
self.model.save(filename)
- 在设置参数时,将
mini cout
设置为40,window
为10-20,size
为400或者更大,训练出来的模型会比较准确(具体训练参数参考代码Doc2Vec.py
)。
Inspecting Model
查找给定词语中最相似的词语:
python
model.wv.most_similar('good')
[('decent', 0.7674194574356079),
('great', 0.7440007328987122),
('fine', 0.7338722944259644),
('bad', 0.7250358462333679),
('solid', 0.6936144232749939),
('nice', 0.6750378012657166),
('well', 0.6653556823730469),
('fantastic', 0.6441545486450195),
('terrible', 0.6301878690719604),
('excellent', 0.6274154186248779)]
对给定词语中最不匹配的词:
python
model.wv.doesnt_match("man woman child kitchen".split())
'kitchen'
我们也可以使用
label
标签查看训练的段落向量:python
model["TEST_24810"]
TensorBoard
我们可以利用google提供的TensorBoard很方便的对模型进行可视化,来帮助查看我们训练的好坏。
其可视化代码可参考我的Github上Visualize.py
。
- 展示界面如下:
我们想观察与
suck
关联度最高的词
得到段向量
至此,我们已经成功将文本向量化,只需要从模型中把需要的向量取出,即可进行常规的数据分析:
train_arrays = np.zeros((25000, 100))
train_labels = np.zeros(25000)
for i in range(12500):
prefix_train_pos = 'TRAIN_POS_' + str(i)
prefix_train_neg = 'TRAIN_NEG_' + str(i)
train_arrays[i] = model[prefix_train_pos]
train_arrays[12500 + i] = model[prefix_train_neg]
train_labels[i] = 1
train_labels[12500 + i] = 0
这里只显示了训练数据的向量,实际上,我们需要将所有数据取出,为了方便,可使用pickle
保存到磁盘。
机器学习
本节代码见这里。
首先我们用传统的机器学习算法进行训练,使用GridSearchCV
调参,查看在训练集上的ROC
,与使用TF-IDF中的训练方式类似,不再赘述。
最后,在逻辑回归上的训练集准确率有0.92
,在SVM上准确率有0.94
,还不错。
神经网络
此部分的JupyterNotebook可参考这里。
对于NLP
领域来说,最常用的神经网络模型是LSTM,因为它能很好的考虑到词向量之间的关联。
这里需要对输入向量进行修改,我们要将每个文档提取feature
,每个feature
用一个向量表示,我使用了文档中前500个词作为feature
,每个词拿到之前训练好的Doc2Vec
中提取向量,因此每个文档是(500,400)
的二维向量(400是训练的模型向量长度)。
由于矩阵过于庞大,我们需要先保存到磁盘,而传统的使用pickle
库效率很低,我们使用了numpy
自带的np.save
保存,避免内存不够用。
LSTM
具体原理可见附录,这里为了方便起见,使用Keras
提供的high level API进行训练。其中,有4层卷积层,一层LSTM,最后是全连接层。
最后训练出来,在测试集上有0.97
左右的准确率,挺不错的。
收获
- 注意题目要求,题目说了
Submissions are judged on area under the ROC curve.
,所以这个题并不是要求我们硬分类,只是需要我们提供概率即可(衡量分类器的好坏)。关于ROC
的介绍和讨论参考附录。 kears
没有提供ROC
的性能评价,需要我们自己实现,可以参考这里,Keras上也有简短的介绍(使用Tensorflow
的评价指标)。- 序列化大数据时,使用
np.save()
性能好于pickle
。 - 当对数据进行向量化后,先进行线性回归等简单的分类器,来衡量向量化好坏。
- 熟悉了
keras
框架和LSTM的使用,锻炼了调参过程。
Reference
主要参考资料
Doc2Vec
部分主要参考:Sentiment Analysis Using Doc2Vecword2vec
可以参考kaggle上的toturial:kaggle tutorialLSTM
可参考The Unreasonable Effectiveness of Recurrent Neural NetworksHow to compute Receiving Operating Characteristic (ROC) and AUC in keras?
文本预处理
Doc2Vec
- 利用Doc2Vec的改进
- Kaggle针对Word2vector
- Gensim发明者写的
- Github上的一篇,但没太看懂
- Kaggle上的讨论
- gensim官方参数文档
- 关于参数
negative sampling
- 关于window的调参