条件随机场 CRF

条件随机场算法是一种概率算法,通过底层状态图或者链式结构来推测输出序列,输出元素之间存在依赖关系。典型应用是 NLP 的 NER,NER 是判断词性的一种算法,比如人名、地名等等。输出元素之间是有关系的,例如 北京和北京大学,是不同的实体,一个是地名、一个是组织。

  • CRF 公式

P ( Y ∣ X ) = exp ⁡ ( ∑ i = 1 n ψ ( y i , y i − 1 , X ) ) Z ( X ) P(Y|X) = \frac{\exp\left( \sum_{i=1}^n \psi(y_i, y_{i-1}, X) \right)}{Z(X)} P(YX)=Z(X)exp(i=1nψ(yi,yi1,X))

Z ( X ) = ∑ Y exp ⁡ ( ∑ i = 1 n ψ ( y i , y i − 1 , X ) ) Z(X) = \sum_{Y} \exp\left( \sum_{i=1}^n \psi(y_i, y_{i-1}, X) \right) Z(X)=Yexp(i=1nψ(yi,yi1,X))

分子:给定 X 到 y i y_i yi 的概率,标签 y i − 1 y_{i-1} yi1 y i y_i yi 概率
分母:归一化计算所有序列的可能性

  • CRF 损失函数

L = − log ⁡ P ( Y ∣ X ) \mathcal{L} = -\log P(Y|X) L=logP(YX)

计算损失函数复杂的在于 Z ( X ) Z(X) Z(X) 的计算,使用 Forward-Backward 算法进行优化。Forward-Backward 是一种动态规划算法,计算从 t-1 到 t 所有可能性。
在这里插入图片描述

  • CRF 推理
    找到每一步的最高概率,最终输出完整序列,使用 Viterbi 算法进行计算。

在这里插入图片描述

Sklearn 实现 CRF 算法

实现来自sklearn_crfsuite 官网,https://sklearn-crfsuite.readthedocs.io/en/latest/tutorial.html

# 安装依赖
pip install sklearn_crfsuite
pip install nltk
# 导入相关库
import nltk
import sklearn
import scipy.stats
from sklearn.metrics import make_scorer
from sklearn.model_selection import cross_val_score
from sklearn.model_selection import RandomizedSearchCV

import sklearn_crfsuite
from sklearn_crfsuite import scorers
from sklearn_crfsuite import metrics

# 基于NLTK下载示例数据集
nltk.download('conll2002')

# 设置训练和测试样本
train_sents = list(nltk.corpus.conll2002.iob_sents('esp.train'))
test_sents = list(nltk.corpus.conll2002.iob_sents('esp.testb'))

train_sents[0]

# 单词转化为数值特征
def word2features(sent, i):
    word = sent[i][0]
    postag = sent[i][1]

    features = {
        'bias': 1.0,
        'word.lower()': word.lower(),
        'word[-3:]': word[-3:],
        'word[-2:]': word[-2:],
        'word.isupper()': word.isupper(),
        'word.istitle()': word.istitle(),
        'word.isdigit()': word.isdigit(),
        'postag': postag,
        'postag[:2]': postag[:2],
    }
    if i > 0:
        word1 = sent[i-1][0]
        postag1 = sent[i-1][1]
        features.update({
            '-1:word.lower()': word1.lower(),
            '-1:word.istitle()': word1.istitle(),
            '-1:word.isupper()': word1.isupper(),
            '-1:postag': postag1,
            '-1:postag[:2]': postag1[:2],
        })
    else:
        features['BOS'] = True

    if i < len(sent)-1:
        word1 = sent[i+1][0]
        postag1 = sent[i+1][1]
        features.update({
            '+1:word.lower()': word1.lower(),
            '+1:word.istitle()': word1.istitle(),
            '+1:word.isupper()': word1.isupper(),
            '+1:postag': postag1,
            '+1:postag[:2]': postag1[:2],
        })
    else:
        features['EOS'] = True

    return features


def sent2features(sent):
    return [word2features(sent, i) for i in range(len(sent))]

def sent2labels(sent):
    return [label for token, postag, label in sent]

def sent2tokens(sent):
    return [token for token, postag, label in sent]

sent2features(train_sents[0])[0]

# 构造训练集和测试集
X_train = [sent2features(s) for s in train_sents]
y_train = [sent2labels(s) for s in train_sents]

X_test = [sent2features(s) for s in test_sents]
y_test = [sent2labels(s) for s in test_sents]

print(len(X_train), len(X_test))

# 创建CRF模型实例
crf = sklearn_crfsuite.CRF(
    algorithm='lbfgs',
    c1=0.1,
    c2=0.1,
    max_iterations=100,
    all_possible_transitions=True
)
# 模型训练
crf.fit(X_train, y_train)
# 类别标签
labels = list(crf.classes_)
labels.remove('O')
# 模型预测
y_pred = crf.predict(X_test)
# 计算F1得分
metrics.flat_f1_score(y_test, y_pred,
                      average='weighted', labels=labels)

# 打印B和I组的模型结果
sorted_labels = sorted(
    labels,
    key=lambda name: (name[1:], name[0])
)
print(metrics.flat_classification_report(
    y_test, y_pred, labels=sorted_labels, digits=3
))

总结

CRF 这种随机概率算法,从一种序列推算另一组序列,虽然现在大语言模型可以更好的解决 NER 问题,无需大量训练,对硬件资源有限的项目来讲 CRF 也可以采用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值