知乎: nghuyong

链接: https://zhuanlan.zhihu.com/p/523164712

前言

最近在重刷李航老师的《统计机器学习方法》尝试将其与NLP结合,通过具体的NLP应用场景,强化对书中公式的理解,最终形成「统计机器学习方法 for NLP」的系列。这篇将介绍条件随机场CRF(绝对给你一次讲明白)并基于CRF完成一个词性标注的任务。

CRF是什么

条件随机场(Conditional random field, CRF)是一个NLP领域广泛使用的模型,即使在深度学习时代也是如此。尤其在序列标注任务上,DNN+CRF依然是目前最主流的范式。 CRF是一个判别式模型,通过训练数据直接学习输入序列和对应的标签序列的条件概率。相比于逻辑回归(Logistic Regression)的分类,CRF能学习到标签之间的序列关系来辅助分类。例如在词性标注任务中,如果之前上一个词的词性是「动词」,那么当前词的词性就很小的概率依然是「动词」,因为「动词」后面继续跟「动词」是小概率事件。

马尔可夫随机场

条件随机场CRF中的随机场指的是马尔可夫随机场。马尔可夫随机场满足马尔可夫随机性,即节点上的随机变量只与跟节点直接相连的节点有关。所以我们就可以把一个无向图上随机变量进行因式分解,写成「最大团」乘积的形式。这里「最大团」指的是一个子图中的任何两个节点都有边相连(这就是「团」),并且在这个子图上不能再添加一个新的节点使其构成一个更大的团。例如下图中的{A,B,C}就是一个最大团。
所以随机变量 可以写成下面的形式, 为图中的一个最大团:
这里 是全部可能的 的概率加和, 用来进行概率的归一化 (类似 softmax) ; 这里的 是一个严格意义上正函数, 一般取指数函数。

条件随机场

当马尔可夫随机场中有一部分节点是确定节点 , 这时候就是求解在 的条件下, 的概率, 即 。我们可以继续按照「最大团」进行概率展开:

线性链条件随机场

NLP任务中一般为上图所示的线性链随机场, 与 均为线性表示的序列, 即 。这里我们称已知变量 (确定变量) 为观测变量, 末知变量 (随机变量) 为隐变量。如上图所示, 在图上的边只有 (观测变量和对应的隐变量的边)以及 (相邻的两个隐变量的边) 。
所以图中的最大团只有两部分构成:

(1) 观测变量和对应的隐变量之间

(2) 相邻的隐变量之间

根据上面的公式进行展开:
进一步将 写成指数函数的形式, 概率相乘就会转写成指数相加的形式:
这里 为特征函数, 函数值为 1 或者 0, 满足特征为 1, 不满足特征为 0; 其中 为定义在节点的特征函数, 称之为状态特征, 依赖当前位置; 为定义在边的特征函数, 称之为转移特征, 依赖当前和前一个位置。 为特征函数对应的系数, 依然是归一化因子。
再进一步,我们可以将这两类最大团合并进行形式上的简化, 所以可以写成下面的形式:
这就是线性CRF最终推出的公式了!!!

额外补充

「特征函数」
:可以看到特征函数与4个变量有关:

(1) 整体的观测变量

(2) 当前位置

(3) 当前的隐变量

(4) 前一个隐变量
所以在词性标注的例子中, 整体的观测变量就是输入的句子 , 当前位置就是当前的单词 , 当前的隐变量就是当前的位置的词性标签 , 前一个隐变量就是前一个单词的词性标签 。这时候我们就可以定义特征函数,例如:如果句子的结尾是问号, 当前的单词为第一个单词, 且当前单词的词性为动词, 那么函数输出为 1 , 否则为 0 。如果这个特征函数的权重越大, 说明这种范式越正确, 即问句的第一个词的词性是动词的概率较大, 比如: "吃饭了吗?"
「与HMM的关系」: HMM在之前的文章中专门介绍过, 具体参见: 统计机器学习方法 for NLP: 基于HMM的词性标注。先说结论, HMM是一种特殊的CRF。下面进行推倒, 首先对于 HMM:
写成log相加的形式:
接着我们通过定义两类特征函数就能转变成CRF的形式:
首先定义一类特征函数, 对于HMM中的两个tag 到 的转移概率 , 定义特征函数 , 如果 那么函数 为 1 , 否则函数 为 0 , 同时这个函数对应的特征权重就是 到 的转移概率 。 首先定义一类特征函数, 对于HMM中的tag 到单词 的发射概率 , 定义特征函数 , 如果 那么函数 为 1 , 否则函数 为 0 , 同时这个函数对应的特征权重就是 到 的发射概率 。
所以我们可以看到HMM是一种特殊的CRF,同时具有两方面的局限性:

(1) HMM是局部特征而非全局特征,也就是没有利用
整体的特征,而CRF可以利用整个句子的全局特征;

(2) HMM中的写成CRF特征函数的形式后,权重就是概率值,所以有加和为1的限制,而CRF没有系数和的限制。

模型训练与预测

在定义了特征函数之后,CRF模型本质是一个线性的模型,模型的参数就是特征函数的权重,这里可以采用「梯度下降」的方法学习模型参数。模型训练之后,预测阶段可通过viterbi算法进行解码,来获得最优的隐变量序列。

基于CRF的词性标注

词性标注任务是指给定一句话,给这种话中的每个词都标记上词性,例如动词/形容词等。例如给定句子:“I love China”, 需要输出: (I: 代词, love: 动词, China: 名词),具体可以参见HMM章节中对词性标注任务的介绍:统计机器学习方法 for NLP:基于HMM的词性标注。
下面将分为:数据处理,模型训练,模型预测 三个部分来介绍如何利用CRF实现词性标注,具体参考的是这篇工作:「NLP Guide: Identifying Part of Speech Tags using Conditional Random Fields」

数据处理

这里采用的数据集是:NLTK Treebank 。获取数据并切分数据集为训练集和测试集。
tagged_sentence = nltk.corpus.treebank.tagged_sents(tagset=
'universal'
)

print
(
"Number of Tagged Sentences "
,len(tagged_sentence))

tagged_words=[tup 
for
 sent 
in
 tagged_sentence 
for
 tup 
in
 sent]

print
(
"Total Number of Tagged words"
, len(tagged_words))

vocab=
set
([word 
for
 word,tag 
in
 tagged_words])

print
(
"Vocabulary of the Corpus"
,len(vocab))

tags=
set
([tag 
for
 word,tag 
in
 tagged_words])

print
(
"Number of Tags in the Corpus "
,len(tags))

“”“

Number of Tagged Sentences  3914

Total Number of Tagged words 100676

Vocabulary of the Corpus 12408

Number of Tags 
in
 the Corpus  12

”“”

train_set, test_set = train_test_split(tagged_sentence,test_size=0.2,random_state=1234)

print
(
"Number of Sentences in Training Data "
,len(train_set))

print
(
"Number of Sentences in Testing Data "
,len(test_set))

“”“

Number of Sentences 
in
 Training Data  3131

Number of Sentences 
in
 Testing Data  783

”“”

模型训练

首先我们手工定义一组特征函数(状态特征函数),例如「第一个字母是不是大写」,「是不是第一个单词」,「是不是最后一个单词」,「前一个单词」,「后一个单词」等,并从数据集中进行特征的抽取:
# 特征定义
def features(sentence,index):

### sentence is of the form [w1,w2,w3,..], index is the position of the word in the sentence
return
 {

'is_first_capital'
:int(sentence[index][0].isupper()),

'is_first_word'
: int(index==0),

'is_last_word'
:int(index==len(sentence)-1),

'is_complete_capital'
: int(sentence[index].upper()==sentence[index]),

'prev_word'
:
''if
 index==0 
else
 sentence[index-1],

'next_word'
:
''if
 index==len(sentence)-1 
else
 sentence[index+1],

'is_numeric'
:int(sentence[index].isdigit()),

'is_alphanumeric'
: int(bool((re.match(
'^(?=.*[0-9]$)(?=.*[a-zA-Z])'
,sentence[index])))),

'prefix_1'
:sentence[index][0],

'prefix_2'
: sentence[index][:2],

'prefix_3'
:sentence[index][:3],

'prefix_4'
:sentence[index][:4],

'suffix_1'
:sentence[index][-1],

'suffix_2'
:sentence[index][-2:],

'suffix_3'
:sentence[index][-3:],

'suffix_4'
:sentence[index][-4:],

'word_has_hyphen'
: 1 
if'-'in
 sentence[index] 
else
 0

    }


def untag(sentence):

return
 [word 
for
 word,tag 
in
 sentence]



def prepareData(tagged_sentences):

    X,y=[],[]

for
 sentences 
in
 tagged_sentences:

        X.append([features(untag(sentences), index) 
for
 index 
in
 range(len(sentences))])

        y.append([tag 
for
 word,tag 
in
 sentences])

return
 X,y


# 特征抽取
X_train,y_train=prepareData(train_set)

X_test,y_test=prepareData(test_set)

下面简单看一下抽取出来的特征:
[{
'is_alphanumeric'
: 0,

'is_complete_capital'
: 0,

'is_first_capital'
: 1,

'is_first_word'
: 1,

'is_last_word'
: 0,

'is_numeric'
: 0,

'next_word'
'Wall'
,

'prefix_1'
'O'
,

'prefix_2'
'On'
,

'prefix_3'
'On'
,

'prefix_4'
'On'
,

'prev_word'
''
,

'suffix_1'
'n'
,

'suffix_2'
'On'
,

'suffix_3'
'On'
,

'suffix_4'
'On'
,

'word_has_hyphen'
: 0},

 {
'is_alphanumeric'
: 0,

'is_complete_capital'
: 0,

'is_first_capital'
: 1,

'is_first_word'
: 0,

'is_last_word'
: 0,

'is_numeric'
: 0,

'next_word'
'Street'
,

'prefix_1'
'W'
,

'prefix_2'
'Wa'
,

'prefix_3'
'Wal'
,

'prefix_4'
'Wall'
,

'prev_word'
'On'
,

'suffix_1'
'l'
,

'suffix_2'
'll'
,

'suffix_3'
'all'
,

'suffix_4'
'Wall'
,

'word_has_hyphen'
: 0},

...

]

以及对应的标签:
[
'ADP'
,

'NOUN'
,

'NOUN'
,

...

]

接着使用sklearn-crfsuite - sklearn-crfsuite 0.3 documentation 进行模型训练:
from sklearn_crfsuite import CRF

crf = CRF(

    algorithm=
'lbfgs'
,

    c1=0.01,

    c2=0.1,

    max_iterations=100,

    all_possible_transitions=True

)

crf.fit(X_train, y_train)

模预预测

对测试集进行预测,并计算F1指标
y_pred=crf.predict(X_test)

metrics.flat_f1_score(y_test, y_pred,average=
'weighted'
,labels=crf.classes_)

“”“

0.9738471726864286

”“”

可以看到最终评估指标高达97.3% 表明抽取的特征很有效,CRF也是一个很强的模型。
下面看一下头部的转移特征和状态特征:
# 头部的转移特征
[((
'ADJ'
'NOUN'
), 4.114996),

 ((
'NOUN'
'NOUN'
), 2.935448),

 ((
'NOUN'
'VERB'
), 2.891987),

 ((
'VERB'
'PRT'
), 2.519179),

 ((
'X'
'VERB'
), 2.271558),

 ((
'ADP'
'NOUN'
), 2.265833),

 ((
'NOUN'
'PRT'
), 2.172849),

 ((
'PRON'
'VERB'
), 2.117186),

 ((
'NUM'
'NOUN'
), 2.059221),

 ((
'DET'
'NOUN'
), 2.053832),

 ((
'ADV'
'VERB'
), 1.994419),

 ((
'ADV'
'ADJ'
), 1.957063),

 ((
'NOUN'
'ADP'
), 1.838684),

 ((
'VERB'
'NOUN'
), 1.763319),

 ((
'ADJ'
'ADJ'
), 1.660578),

 ((
'NOUN'
'CONJ'
), 1.591359),

 ((
'PRT'
'NOUN'
), 1.398473),

 ((
'NOUN'
'.'
), 1.381863),

 ((
'NOUN'
'ADV'
), 1.380086),

 ((
'ADV'
'ADV'
), 1.301282)]

# 头部的状态特征
[((
'prev_word:will'
'VERB'
), 6.751359),

 ((
'prev_word:would'
'VERB'
), 5.940819),

 ((
'prefix_1:*'
'X'
), 5.830558),

 ((
'suffix_4:rest'
'NOUN'
), 5.644523),

 ((
'suffix_2:ly'
'ADV'
), 5.260228),

 ((
'is_first_capital'
'NOUN'
), 5.043121),

 ((
'prev_word:could'
'VERB'
), 5.018842),

 ((
'suffix_3:ous'
'ADJ'
), 4.870949),

 ((
'prev_word:to'
'VERB'
), 4.849822),

 ((
'suffix_4:will'
'VERB'
), 4.677684),

 ((
'next_word:appeal'
'ADJ'
), 4.386434),

 ((
'prev_word:how'
'PRT'
), 4.35094),

 ((
'suffix_4:pany'
'NOUN'
), 4.329975),

 ((
'prefix_4:many'
'ADJ'
), 4.205028),

 ((
'prev_word:lock'
'PRT'
), 4.153643),

 ((
'word_has_hyphen'
'ADJ'
), 4.151036),

 ((
'prev_word:tune'
'PRT'
), 4.147576),

 ((
'next_word:Express'
'NOUN'
), 4.137127),

 ((
'suffix_4:food'
'NOUN'
), 4.116688),

 ((
'suffix_2:ed'
'VERB'
), 4.070659)]

根据转移特征('ADJ', 'NOUN')可以看到「形容词」后面接「名词」的概率非常高;根据状态特征('prev_word:will', 'VERB')可以看到如果上一个单词是will,那么当前词的词性为「动词」的概率非常高。
本篇的CRF还处于统计学习阶段,所以构建的特征也都是手工构建的特征,还是存在一定的局限性。所以后来在深度学习时代,一般会先用LSTM或者BERT这种深度神经网络先进行特征的抽取,再送到CRF进行标签的预测,能有更加显著的效果提升。

参考

  • Introduction to Conditional Random Fields: https://blog.echen.me/2012/01/03/introduction-to-conditional-random-fields/
  • NLP Guide: Identifying Part of Speech Tags using Conditional Random Fields: https://medium.com/analytics-vidhya/pos-tagging-using-conditional-random-fields-92077e5eaa31
  • https://github.com/AiswaryaSrinivas/DataScienceWithPython/blob/master/CRF%20POS%20Tagging.ipynb

📝论文解读投稿,让你的文章被更多不同背景、不同方向的人看到,不被石沉大海,或许还能增加不少引用的呦~ 投稿加下面微信备注“投稿”即可。
最近文章

投稿或交流学习,备注:昵称-学校(公司)-方向,进入DL&NLP交流群。
方向有很多:机器学习、深度学习,python,情感分析、意见挖掘、句法分析、机器翻译、人机对话、知识图谱、语音识别等
记得备注~
继续阅读
阅读原文