面向对象:想搭建智能问答系统、深度语义匹配的nlp选手。在自己亲手搭建一个之前,学习和走读优秀的框架代码是个不会错的选择。
AnyQ(ANswer Your Questions) :百度QA开源项目,主要包含面向FAQ集合的问答系统框架、文本语义匹配工具SimNet。
框架目录
本文重点走读SimNet框架的代码。开源代码地址,点这里。TensorFlow版SimNet的结构如下:(自动屏蔽名字带 ‘pairwise’ 的文件,稍后解释)
1 | simnet |
读框架代码的工具,相较于jupyter,spyder,推荐Pycharm。
运行环境
- linux,其它系统推荐docker
- python 2.7
- tensorflow 1.7.0
数据类型
解释为何屏蔽名字带 ‘pairwise’ 的文件:
语义匹配网络SimNet可以使用Pointwise与Pairwise两种类型的数据进行训练。
Pointwise训练及测试数据格式
- 训练数据格式:训练数据包含三列,依次为Query1的ID序列(ID间使用空格分割),Query2的ID序列(ID间使用空格分割),Label,每列间使用TAB分割,例如;
1 | 1 1 1 1 1 2 2 2 2 2 0 |
- 测试数据格式:Pointwise测试数据格式与训练数据格式相同。
Pairwise训练及测试数据格式
- 训练数据格式:训练数据包含三列,依次为Query1的ID序列(ID间使用空格分割),Positive Query2的ID序列(ID间使用空格分割),Negative Query3的ID序列(ID间使用空格分割),每列间使用TAB分割,例如;
1 | 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 |
- 测试数据格式:测试数据格式包含三列,依次为Query1的ID序列(ID间使用空格分割),Query2的ID序列(ID间使用空格分割),Label,每列间使用TAB分割,例如;
1 | 1 1 1 1 1 1 1 1 1 1 1 |
由于使用的数据集是Quora数据集,为 [问句1,问句2,label] 的Pointwise格式数据集,因此名字带 ‘pairwise’ 的文件暂时都用不上。若是数据集为pairwise,就能用的上了。
.json 配置文件走读
准备完数据文件之后,观察配置文件,以cnn-pointwise.json为例。 通过配置文件可以灵活的选择网络类型,数据类型,损失函数以及其他超参数。
1 | { |
参数说明:
假设训练集的 data_size = 1000时,运行模型训练train任务时,设置了num_epochs = 5,batch_size = 50,shuffle = 1,print_iter = 10,那么训练集最终的样本量 = data_size num_epochs =5000,重复了5倍原有样本量,shuffle = 1表示打乱样本数据,而每步step跑 batch_size 条样本,一共能跑 data_size num_epochs / batch_size = 5000/50 = 100 步,而每print_iter步报一次loss,那么一共报 100/print_iter = 10次loss。
在代码中还有个参数为 epoch_iter,为保存模型而设置,意为每epoch_iter步时,保存一次模型文件。epoch_iter = data_size / batch_size = 1000/50 = 20,共100步,即跑完train后共保存 100/epoch_iter =100/20 =5个模型文件。若shuffle = 0,epoch_iter 意为跑完一次原data_size 需要的步数,若shuffle = 1,epoch_iter 意为跑完一次与原data_size一样大的数据集需要的步数,这样的话,最终保存的模型数量 = num_epochs 。
假设测试集的 data_size = 200时,运行模型预测predict任务时,经常设置num_epochs = 1,因为在测试时没必要重复测试样本,是否打乱数据影响也不大,当设置batch_size = 50,测试数据总样本量 = data_size *num_epochs =200仍然是原测试数据集,每步跑batch_size = 50条数据,共200/50=4steps跑完,最终会打印一个accuracy数值。
这些参数只对模型训练有效,模型预测的predict任务里的这些参数需要在tf_simnet.py内的predict函数内修改!!!(为自己修改代码把配置参数提到配置文件里埋下伏笔😂)
其它说明:
保存模型文件的路径需要自己手动添加,在目录上新建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
.sh 任务文件走读
数据准备完毕,配置文件修改完成后,可在Linux执行.sh脚本文件来实现 train / predict / freeze / …等任务。
run_train.sh
通过执行脚本run_train.sh可以启动训练任务,打开run_train.sh:
1 | set -e # set -o errexit |
也可以通过如下方式启动自定义训练,效果与上面的.sh文件是一样的:
1 | python tf_simnet.py |
执行完run_train.sh后,在model文件夹内会自动保存各个epoch_iter和final的模型文件。
run_infer.sh
通过执行脚本run_infer.sh可以启动预测任务,可以得到模型预测结果或得分,打开.sh文件:
1 | set -e # set -o errexit |
也可以通过如下方式启动自定义训练:
1 | python tf_simnet.py |
执行完run_infer.sh之后,会自动在路径上生成result文件,可打开查看。
自定义.sh任务文件
据观察,.sh文件主要运行tf_simnet.py文件,有两个参数可以自定义。
参数说明:
- task: 任务类型 ,可选择 train/predict/freeze/convert 。
- task_conf: 使用配置文件地址
接下来尝试生成自定义的.sh任务文件:
可以把转换数据的命令抽取出来,形成 run_convert_data.sh文件:
执行完run_convert_data.sh后,在data文件夹里会自动生成convert前缀的数据文件。
1
2
3
4
5
6
7
8
9
10set -e # set -o errexit
set -u # set -o nounset
set -o pipefail
# 将具体的文件名把下面命令里的中文字替换掉即可
echo "convert train data"
python ./tools/tf_record_writer.py pointwise ./data/待转化的训练数据文件名 ./data/已转化的训练数据文件名 0 32
echo "convert test data"
python ./tools/tf_record_writer.py pointwise ./data/待转化的测试数据文件名 ./data/已转化的测试数据文件名 0 32
echo "convert data finish"定义一个cnn配置文件的执行freeze任务的run_freeze_cnn.sh:
执行完run_freeze_cnn.sh后,根据配置文件里“graph”的参数设置,在路径上会自动生成graph文件夹,里面有model_cnn_pairwise.protxt文件。
1
2
3
4
5
6
7
8
9set -e # set -o errexit
set -u # set -o nounset
set -o pipefail
in_task_type='freeze' # 输入任务类型,可选择 train/predict/freeze/convert
in_task_conf='./examples/cnn-pointwise.json'
python tf_simnet.py \
--task $in_task_type \
--task_conf $in_task_conf定义一个使用lstm网络的训练任务 run_train_lstm.sh,同时一定要记得修改配置文件!!:
1
2
3
4
5
6
7
8
9set -e # set -o errexit
set -u # set -o nounset
set -o pipefail
in_task_type='train'
in_task_conf='./examples/lstm-pointwise.json' # 修改了配置文件路径,配置文件内参数也得修改好
python tf_simnet.py \
--task $in_task_type \
--task_conf $in_task_conf当lstm的train任务跑完后,利用其保存的模型文件进行predict任务,新建run_predict_lstm.sh:
1
2
3
4
5
6
7
8
9set -e # set -o errexit
set -u # set -o nounset
set -o pipefail
in_task_type='predict' # 修改了任务类型
in_task_conf='./examples/lstm-pointwise.json' # 确认lstm配置文件路径
python tf_simnet.py \
--task $in_task_type \
--task_conf $in_task_conf
.py 文件走读
tf_simnet.py
tf_simnet.py是整个深度语义匹配框架的主运行py文件,打开如下,已对主要代码进行一行行的注释:
1 | #coding=utf-8 |
可直接先看最下面的
if __name__ == "__main__":
,两个parser.add_argument分别对应.sh文件的两个task和task_conf参数。选择好任务和配置文件后就运行任务,假如 args.task == ‘train’,那么运行train函数。再跳到
def train(conf_dict):
这行,看train函数如何运行。前面几行还是比较好理解,如有疑问可发私信问我,得到loss,optimizer和配置conf_dict之后,最后一行又运行了一个大函数:1
2# 运行 controler 的 run_trainer 函数
controler.run_trainer(loss, optimizer, conf_dict)那么就再跳到controler这个文件,继续往下看。
需要特殊说明的是,当模型预测执行predict任务运行predict函数时,num_epochs/batch_size/shuffle/…等参数需要在代码里面调整,而配置文件里的参数设置对模型预测不起作用,在tf_simnet.py的predict函数找到如下代码进行设置:
1 | # 更新测试集的 conf_dict配置文件 的配置参数 (这里需要手动调整) |
controler.py
打开controler.py可以看见,里面有与tf_simnet.py里的train/predict/freeze函数一一对应的run_train/run_predict/graph_save函数,对重要代码已做注释:
继续上面的思路,直接看run_trainer(loss, optimizer, conf_dict)函数。同样,如有疑问可留言或私信我。
1 | #coding=utf-8 |
跑完run_trainer(loss, optimizer, config),就自动保存了模型文件,供模型预测使用。
同理按照这个思路,回到tf_simnet.py可以看predict任务或者freeze(graph)任务的代码流程。同样也会再看到controler.py里面的函数是如何执行任务的。
写在最后
- 不同的.sh文件有不同的task和task_conf参数值,对应的是运行tf_simnet.py和controler.py里不同的函数。
- 其它的.py文件点开来应该不太难理解,如有疑问可留言或私信我。
- 每个模型文件比较大,我跑出来的每个有1.5G左右大小。
- 若想观察每一步的数据形状或者变换情况,可自行加入print命令打印出来看看。
- 每个任务完成后有自动生成的文件,可以研究看看,比如model文件,result文件,graph文件,log文件。
- 代码走读只是理解的第一步,若要扎实掌握还需自己改动代码,再调试调试。
- 欢迎打赏😘