CRF算法学习——自己动手实现Bi-LSTM+CRF分词(python)

意图识别 <= Bi-LSTM+CRF <= 先懂CRF <= 先懂HMM <= 先懂EM
终于到这一步了,这一次要用python写了,使用tensorflow去写。关于tensorflow的使用不是我的重点。
相关代码还是在我的GitHub上。
题外话,idea可以安装一个python插件,然后设置一下python编译器,这样就可以在项目里既能运行java又能运行python了,美滋滋。

Bi-LSTM+CRF分词实现路线图

由于不是很会写python(就会用tensorflow....),所以开始实现的代码比较ugly,然后又仿造着github比较好的项目重写了一版,所以有两个文件夹:uglybilstmcrf和bilstmcrf

思路

1.所有字构建字典,同时所有字变成字嵌入(可以利用gensim预训练字向量,也可以到时候随机生成)
2.得到字嵌入后,用字嵌入特征喂给双向LSTM
3.然后加一个CRF层,最小化CRF极大似然得到的对数极大似然值
4.训练结束以后,得到转移矩阵和Bi-LSTM的参数,直接利用viterbi解码

对着代码讲如何实现

这一部分用的bilstmcrf里面的代码,ugly的看看就好

首先定义一些占位符

word_ids是就是每个字对应的字典的id形成的list
比如“我爱北京天安门”这7个字,可能就会变成[305,249,70,291,58,131,254]这样的id序列。
因为这个句子的长度不一定是一样的,一般要先算一个这个batch的所有句子的最长的句子的长度作为这个值,所以shape的第二个是None;
第一个是None的原因是训练的时候可能是batchsize=64,但是预测的时候就是1,所以也是None;
labels同理,sequence_lengths是每个句子的真实长度,比如刚才就是7,这个为了进行CRF计算以及解码。
dropout_pl和lr_pl这个就是dropout的保留值和学习率,放不放这都行。

然后去找字向量


这个self.embeddings是类初始化的时候传进来的,这个地方我写了两种(在embedding.py文件里),一种随机的,一种我先用gensim预训练好的

然后构建双向BiLSTM层

前向后向,这个网上都一大堆了

最后用个连接层把一个词在四种标签的预测值拿到以后去进行损失函数的计算。

再定义损失函数

有两种,一种是CRF的得分计算,一种就是看成softmax的输出去计算

最后选择梯度下降算法进行训练

最后,解码问题

tf.contrib自带

嫌复杂,可以看看ugly版,100行解决

上面说的很轻巧,其实实现起来还是比较繁琐的
ugly版做的事情就没那么灵活,就是什么都定好了,根本没有解耦这一说,完全是按照思路一步一步做的面向流程编码,所以代码就牵一发动全身,写完都没法修改。

一些总结

关于结果

分词效果还行

预训练VS随机生成

预训练的字向量竟然不如随机生成然后一直trainable的字向量,这个也是我没想到的。

关于python

python是面向对象编程的硬类型语言,一直误解了python。。。
self就像是java的this关键字,深刻的体验到了
tensorflow其实还好,原来但是用python进行数据预处理很难,这次逼着自己用面向对象编程的思想去写了一遍,感觉越来越会写python
yield关键字真是好用,不知道设计这个语言的人怎么想的,厉害
还有就是python挺好的,一年前写python的时候总觉得不好用,比如想打印个带数字的字符串都弄不好,现在好多了,而且很方便
还想吐槽的一点就是python定义方法,方法带有参数的时候,有时候参数是个dict,你希望每次“.”之后能弹出来他自带的方法,但是没有,尴尬。

发表评论

电子邮件地址不会被公开。