使用CRF进行中文分词训练。本文主要从实际应用出发,讨论如何使用CRF进行训练和推理。如果是对其中数学原理有兴趣的同学,可以参考《统计自然语言处理》的相关内容,以及台大李宏毅老师的视频课等等。

CRF 条件随机场

要使用CRF,就要先了解它最基本的一些概念。

打个广告,自己总结的一点点CRF知识。

其中对于应用,个人认为最重要的概念是:两个特征函数${s_l()}$和${t_k()}$。

${s_l()}$:状态特征函数,只与当前节点的观测值与隐藏标签有关。个人认为可以按照HMM中的emission发射概率来理解。

${t_k()}$:转移特征函数,与当前节点及其相邻节点有关。个人认为可以按照HMM中的transition转移概率来理解。

值得注意的是,特征函数是可以自己进行设计的。CRF++与crfsuite中都支持相应的自行设计特征函数,可以将CRF的“感受野”扩大。

针对上面两种特征函数,CRF有相对应的两个可学习参数λμ,通过学习获得。

问题定义

Input:观测序列O(observation),条件随机场模型CRF(${s_l()}$,${t_k()}$)

Output:隐藏序列H(Hidden)

训练

CRF的训练主要是针对上面特征函数相关的两个参数的学习,在学习过程中,一般按照CRF简化的形式来表示,即**f()**表示特征函数,ω表示参数。主流的学习策略有:梯度下降、拟牛顿法、L-BFGS等等。以梯度下降法为例:

定义出损失函数(or 优化函数)${L(\omega)}$,再通过对ω求导,得到${\frac{\partial f(\omega)}{\partial \omega}}$,就可以通过一般的梯度下降方式求解参数了。

训练代码

请将global-nlp/knlp代码克隆到本地,

1
2
3
git clone https://github.com/global-nlp/knlp.git
pip install -r requirements.txt
python knlp/seq_labeling/crf/train.py {train_data_path}

上面的操作会将训练好的模型以pkl形式存储于knlp/model/crf下。

预测

预测的过程,实际上就是通过前面训练好的参数与模型对观测序列进行相应的计算与解码。

sklearn-crfsuite这个库的解码依然调用的是维特比算法,关于Viterbi算法,可以参考学长的博文:小李:Viterbi解码-可能是最易懂且全面的隐马尔可夫介绍(二)

完整步骤

1
2
3
git clone https://github.com/global-nlp/knlp.git
pip install -r requirements.txt
python knlp/seq_labeling/crf/train.py {train_data_path}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
# !/usr/bin/python
# -*- coding:UTF-8 -*-
from knlp.common.constant import KNLP_PATH
from knlp.seq_labeling.crf.inference import Inference
from knlp.seq_labeling.crf.train import Train

# init trainer and inferencer
crf_inferencer = Inference()
crf_trainer = Train()


def crf_train(training_data_path, model_save_file):
"""
This function call crf trainer and inference. You could just prepare training data and test data to build your own
model from scratch.

Args:
training_data_path:

Returns:

"""
crf_trainer.init_variable(training_data_path=training_data_path)
crf_trainer.load_and_train()
crf_trainer.save_model(file_name=model_save_file)
print(
"Congratulations! You have completed the training of crf model for yourself. "
f"Your training info: training_data_path: {training_data_path}. "
f"model_save_path: {model_save_file}"
)


def load_and_test_inference(model_save_file, sentence):
"""
测试推理
Args:
model_save_file: string
sentence: string

Returns:

"""
crf_inferencer.spilt_predict(file_path=model_save_file, in_put=sentence)
print("POS结果:" + str(crf_inferencer.label_prediction))
print("模型预测结果:" + str(crf_inferencer.out_sentence))


if __name__ == '__main__':

training_data_path = KNLP_PATH + "/knlp/data/hanzi_segment.txt"
model_save_file = KNLP_PATH + "/knlp/model/crf/crf.pkl"
crf_train(training_data_path=training_data_path, model_save_file=model_save_file)

sentence = "从明天起,做一个幸福的人,关心粮食与蔬菜。"
load_and_test_inference(model_save_file=model_save_file, sentence=sentence)