Quora Question Pairs是kaggle里的问句语义匹配比赛。这场比赛对于nlp选手应该不陌生了,数据集也是大家入门nlp必备。本文在深度语义匹配使用的是百度开源的语义匹配框架AnyQ里的SimNet。
环境说明
- Linux
- python 2.7
- TensorFlow 1.7.0
- CPU
- Jupyter Notebook
下载AnyQ
首先需要有git,如果没有可以点这里下载。在Linux环境选定路径下敲入git clone https://github.com/baidu/AnyQ
进行AnyQ的下载。
下载完,查看SimNet的路径是AnyQ/tools/simnet/train/tf/
1 | simnet |
❤另外已经专门写了一篇关于SimNet的代码走读,强烈推荐打开那篇文章放在旁边与这篇一起看,点这里查看。
另外,在Linux查看/修改代码,推荐jupyter notebook
。
其它说明:
保存模型文件的路径需要自己手动添加,在目录上新建model和pointwise文件夹:
1
2
3
4
5
6
7
8
9
10
11
12
13simnet
|-tf
|- date
|- examples
|- layers
|- losses
|- nets
|- tools
|- util
# 新建下面的文件夹
|- model
|- pointwise
下载数据集
请点击 Quora 进行数据集下载。本文只用到训练数据集,所以下载train.csv
即可。train.csv还不能直接作为SimNet的输入数据,需要做词嵌入等数据预处理。
词嵌入处理
SimNet的训练数据是有格式要求的。具体请查看SimNet的README.md。这一步数据处理也可以在win环境下操作。由于Quora训练集是两个问句列加一个label列,适合SimNet的pointwise数据格式。
- pointwise数据格式:数据包含三列,依次为Query1的ID序列(ID间使用空格分割),Query2的ID序列(ID间使用空格分割),Label,每列间使用TAB分割,例如;
1 | 1 1 1 1 1 2 2 2 2 2 0 |
pointwise需要问句都以id的形式,所以word embedding选择词袋法(BOW)。
新建一个quora.py
文件来做词嵌入处理,先处理了空(null)问题,再预览10个问题对(question pairs)。
输入:
1 | # -*- coding: utf-8 -*- |
输出:
1 | What is the step by step guide to invest in share market in india? |
继续做同义词替换、停用词处理、删除介词等数据清洗,输入:
1 | stop_words = ['the','a','an','and','but','if','or','because','as','what','which','this','that','these','those','then', |
输出处理之后的10个问题对:
1 | step by step guide invest in share market in India |
继续删除自定义停用词,删除只出现过一次的词,将问题1和问题2合并成一个大语料库,输入:
1 | import itertools |
形成字典,输入:
1 | from gensim import corpora |
输出字典:
1 | Dictionary(52069 unique tokens: ['vicodin', 'mermaid', 'kgb', 'dusk', 'glonass']...) |
输入一个新文本,试一下这个字典,输入:
1 | new_doc = "would happen Indian government stole Kohinoor Koh i Noor diamond back" |
输出:
1 | [(8, 1), |
感觉还可以,那么将quora的所有问题对的语料都用字典里的id代替,输入:
1 | bow_corpus = [dictionary.doc2idx(text) for text in precessed_corpus] |
由于语料库是问题1和问题2按顺序组成,那么用id代替后的语料库前一半的是词袋处理后问题1,后一半是词袋处理后问题2,最终恢复到pointwise格式的数据,输入:
1 | # 生成文件 |
得到tsv格式的train_0829.tsv
,test_0829.tsv
,可以随便命名。
数据准备
切换到Linux,将嵌入完的 tsv 数据集放入AnyQ/tools/simnet/train/tf/data
路径下:
1 | simnet |
按照下图路径AnyQ/tools/simnet/train/tf/
,新建 run_convert_data.sh 脚本文件,
其实就是把原来的run_train.sh里转换数据的命令拿出来。因为之后需要多模型跑一样的数据,数据转换做一次就够了,内容如下:
1 | set -e # set -o errexit |
在Linux黑命令框里敲入命令./run_convert_data.sh
,如果有permission denied情况,先使用chmod 777 文件名
,在这里是chmod 777 run_convert_data.sh
。如果成功将打印出:
1 | convert train data |
在data文件夹目录下,新增两个转换后的数据文件,convert_train_0829 和 convert_test_0829。
修改代码
修改配置文件
用 jupyter notebook 打开 examples 文件夹下的所有形如 xxx-pointwise.json的配置文件,修改以下几个参数数值:
- data_size = 323273 , 因为train_0829.tsv有 323273 条样本
- vocabulary_size = 1000000
- batch_size = 800
- num_epochs = 1
- print_iter = 10
- train_file = data/convert_train_0829
- test_file = data/convert_test_0829
以上,只是修改模型训练的配置参数,还得另外修改模型检验的配置参数,打开AnyQ/tools/simnet/train/tf/
目录下的 tf_simnet.py,找到def predict(conf_dict)
,找到如下代码(应该在第90行):
1 | conf_dict.update({"num_epochs": "1", "batch_size": "1", |
将其修改成:
1 | conf_dict.update({"num_epochs": "1", "batch_size": "400", |
保存,关闭文件。
修改保存模型文件规则
default的代码将在每个epoch迭代时保存一个模型,且最终跑完还会保存一个模型。由于模型文件过大,所以将把代码修改成只保存最后跑完的模型,如果不需要可不做此修改。打开utils文件夹下的controler.py,将100-104行隐去:
1 | # if step % epoch_iter == 0: |
修改打印命令
default的代码在模型训练过程中,每一个print_iter会打印出一个loss值,在模型检验过程中,最后会打印出一个accuracy值。但是为了观察跑迭代的速度和精度,还需要在每次报loss的时候,打印出每个print_iter花了几秒钟(如不需要此功能可不做修改)。打开utils文件夹下的controler.py,将90-99行代码修改成如下:
1 | epoch_num = 1 |
保存,关闭文件。
比对模型效果
在AnyQ/tools/simnet/train/tf/
路径增加 .sh 文件。因为SimNet目前有7个可选择的网络,分别是bow, cnn, knrm, lstm, mmdnn, mvlstm, pyramid,分别与nets文件夹里的文件一一对应,所以每种任务都有7个 .sh 脚本。任务类型分别是 train/predict/freeze,对应模型训练,模型检验,模型结果示意。
增加模型训练任务的 .sh文件
以cnn为例,新建run_train_cnn.sh,内容如下:
1 | set -e # set -o errexit |
增加模型验证任务的 .sh文件
以cnn为例,新建run_predict_cnn.sh,内容如下:
1 | set -e # set -o errexit |
增加模型结果示意任务的 .sh文件
以cnn为例,新建run_freeze_cnn.sh,内容如下:
1 | set -e # set -o errexit |
最终,生成7个 run_train_xxx.sh 文件,7个 run_predict_xxx.sh文件,7个 run_freeze_xxx.sh文件,或者选择性生成几个,示意如下图:
切换回Linux命令界面,开始运行各种命令,结果如配图。
bow
./run_train_bow.sh
./run_predict_bow.sh
cnn
./run_train_cnn.sh
./run_predict_cnn.sh
knrm
./run_train_knrm.sh
./run_predict_knrm.sh
lstm
./run_train_lstm.sh
./run_predict_lstm.sh
mmdnn
./run_train_mmdnn.sh
./run_predict_mmdnn.sh
mvlstm
./run_train_mvlstm.sh
./run_predict_mvlstm.sh
pyramid
- ./run_train_pyramid.sh
- ./run_predict_pyramid.sh
可自行尝试./run_freeze_xxx.sh命令系列。当跑完上面所有命令时,原文件夹中就自动形成了预测文件,如下:
随便打开其中一个看一下:
如下是自动保存的模型文件:
也会自动生成log文件夹,运行freeze任务后会自动生成graph文件夹,把任务结果保存在文件夹里。
优缺点
缺点:
- 由于SimNet的数据格式有要求,将文本都以ID格式代替,因此词嵌入使用的是词袋bow处理。
- 在前期文本清洗的规则可以再完善些。
- 呃。。。。百度(摊手🤷♀️
优点:
- SimNet是一个集成体,有很多深度模型可以选择。
写在最后
- 跑了个SimNet流程,仅作效果比对,没有追求精度的提升。后来我有尝试把batch_size调成30,那么将有10000多步,精度有几个百分点的提升,但还远远不够。所以任重道远呀,还有很多需要学习的。
- 在本文没有用到测试集,如果要参加比赛,在词嵌入做字典时,是否应该把test测试集的单词也加入到字典里来?这个还没有真的尝试一下,毕竟测试集test.cvs大的吓人!
- 如有疑问,欢迎留言或者点这里找到我。