【自然语言处理】基于sklearn-crfsuite进行命名实体识别

news2024/11/23 16:44:37

基于sklearn-crfsuite进行命名实体识别

  • 0. 条件随机场
  • 1. 训练数据
  • 2. 特征提取
  • 3. 训练一个CRF模型
  • 4. 评估
  • 5. 超参数优化
  • 6. 检查参数空间
  • 7. 检查在测试数据上的最优估计器
  • 8.检查分类器学到了什么东西
  • 9.检查模型权重
  • 10. 定制化
  • 11.在控制台中进行格式化
  • 参考资料

本文中,针对 CoNLL2002数据训练了一个用于 命名实体识别的基本 CRF模型,并检查其权重以查看该模型学到了什么。需要 NLTK>3.xsklearn-crfsuite Python包。本文使用 Python 3

0. 条件随机场

**条件随机场:**条件随机场这个模型属于概率图模型中的无向图模型,这里我们不做展开,只直观解释下该模型背后考量的思想。一个经典的链式 CRF 如下图所示:
条件随机厂
CRF 本质是一个无向图,其中绿色点表示输入,红色点表示输出。点与点之间的边可以分成两类:

  • 一类是 x 与 y 之间的连线,表示其相关性;
  • 另一类是相邻时刻的 y之间的相关性。

也就是说,在预测某时刻 y时,同时要考虑相邻的标签解决。当 CRF 模型收敛时,就会学到类似 P-B 和 T-I 作为相邻标签的概率非常低。

对于 CRF,我们给出准确的数学语言描述:设 X 与 Y 是随机变量,P(Y|X) 是给定 X 时 Y 的条件概率分布,若随机变量 Y 构成的是一个马尔科夫随机场,则称条件概率分布 P(Y|X) 是条件随机场

1. 训练数据

首先导入依赖库

import nltk
import eli5
import sklearn
import sklearn_crfsuite
import scipy.stats
from itertools import chain

from sklearn.metrics import make_scorer
from sklearn.model_selection import cross_val_score
from sklearn.model_selection import RandomizedSearchCV

from sklearn_crfsuite import scorers
from sklearn_crfsuite import metrics

CoNLL2002数据集包含西班牙语句子列表,并带有注释的命名实体。 它使用IOB2编码。 CoNLL 2002 数据还提供词性标记。

import nltk
nltk.download('conll2002')
'''
[nltk_data] Downloading package conll2002 to /root/nltk_data...
[nltk_data]   Package conll2002 is already up-to-date!
True
'''
nltk.corpus.conll2002.fileids()
'''
['esp.testa', 'esp.testb', 'esp.train', 'ned.testa', 'ned.testb', 'ned.train']
'''
train_sents = list(nltk.corpus.conll2002.iob_sents('esp.train'))
test_sents = list(nltk.corpus.conll2002.iob_sents('esp.testb'))
train_sents[0]
'''
[('Melbourne', 'NP', 'B-LOC'),
 ('(', 'Fpa', 'O'),
 ('Australia', 'NP', 'B-LOC'),
 (')', 'Fpt', 'O'),
 (',', 'Fc', 'O'),
 ('25', 'Z', 'O'),
 ('may', 'NC', 'O'),
 ('(', 'Fpa', 'O'),
 ('EFE', 'NC', 'B-ORG'),
 (')', 'Fpt', 'O'),
 ('.', 'Fp', 'O')]
'''

2. 特征提取

接下来,定义一些特征。 POS标签可以看作是预先提取的特征。 这里提取更多特征(word parts、简化的POS标签、lower/title/upper标记、临近单词的特征)并将它们转换为 sklear-crfsuite 格式——每个句子都应转换为字典列表。 这是一个非常简单的基线任务; 当然也可以做得更好。

sklearn-crfsuite(和python-crfsuite)支持多种特征格式; 这里我们使用feature dicts

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.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]

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]

从单个标记中提取的特性如下:

X_train[0][1]
{'bias': 1.0,
 'word.lower()': '(',
 'word[-3:]': '(',
 'word.isupper()': False,
 'word.istitle()': False,
 'word.isdigit()': False,
 'postag': 'Fpa',
 'postag[:2]': 'Fp',
 '-1:word.lower()': 'melbourne',
 '-1:word.istitle()': True,
 '-1:word.isupper()': False,
 '-1:postag': 'NP',
 '-1:postag[:2]': 'NP',
 '+1:word.lower()': 'australia',
 '+1:word.istitle()': True,
 '+1:word.isupper()': False,
 '+1:postag': 'NP',
 '+1:postag[:2]': 'NP'}

3. 训练一个CRF模型

一旦拥有正确格式的特征,就可以使用sklearn_crfsuite.CRF训练一个linear-chain CRF(条件随机场)模型:

crf = sklearn_crfsuite.CRF(
    algorithm='lbfgs',
    c1=0.1,
    c2=0.1,
    max_iterations=20,
    all_possible_transitions=False,
)
crf.fit(X_train, y_train);

4. 评估

数据集中有更多的 O 实体,但我们对其他实体更感兴趣。 为了解决这个问题,将为除O之外的所有标签计算的平均F1分数。sklearn-crfsuite.metrics 包为序列分类任务提供了一些有用的指标,包括这个。

labels = list(crf.classes_)
labels.remove('O')
labels
'''
['B-LOC', 'B-ORG', 'B-PER', 'I-PER', 'B-MISC', 'I-ORG', 'I-LOC', 'I-MISC']
'''
y_pred = crf.predict(X_test)
metrics.flat_f1_score(y_test, y_pred, average='weighted', labels=labels)
'''
0.76980231377134023
'''

更详细地检查每个类的结果:

# group B and I results
sorted_labels = sorted(labels, key=lambda name: (name[1:], name[0]))
sorted_labels
print(metrics.flat_classification_report(y_test, y_pred, labels=sorted_labels, digits=3))

报错:
报错信息

5. 超参数优化

为了提高质量,尝试使用随机搜索和 3 折交叉验证来选择正则化参数。

# define fixed parameters and parameters to search
crf = sklearn_crfsuite.CRF(
    algorithm='lbfgs', 
    max_iterations=100, 
    all_possible_transitions=True
)
params_space = {
    'c1': scipy.stats.expon(scale=0.5),
    'c2': scipy.stats.expon(scale=0.05),
}

# use the same metric for evaluation
f1_scorer = make_scorer(metrics.flat_f1_score, 
                        average='weighted', labels=labels)

# search
rs = RandomizedSearchCV(crf, params_space, 
                        cv=3, 
                        verbose=1, 
                        n_jobs=-1, 
                        n_iter=50, 
                        scoring=f1_scorer)
rs.fit(X_train, y_train)

报错:AttributeError: ‘CRF’ object has no attribute ‘keep_tempfiles’
报错信息

pip install -U 'scikit-learn<0.24'

打印最优结果:

# crf = rs.best_estimator_
print('best params:', rs.best_params_)
print('best CV score:', rs.best_score_)
print('model size: {:0.2f}M'.format(rs.best_estimator_.size_ / 1000000))

6. 检查参数空间

显示哪些 c1 和 c2 值已检查 RandomizedSearchCV 的图表。 红色意味着更好的结果,蓝色意味着更差。

_x = [s.parameters['c1'] for s in rs.grid_scores_]
_y = [s.parameters['c2'] for s in rs.grid_scores_]
_c = [s.mean_validation_score for s in rs.grid_scores_]

fig = plt.figure()
fig.set_size_inches(12, 12)
ax = plt.gca()
ax.set_yscale('log')
ax.set_xscale('log')
ax.set_xlabel('C1')
ax.set_ylabel('C2')
ax.set_title("Randomized Hyperparameter Search CV Results (min={:0.3}, max={:0.3})".format(
    min(_c), max(_c)
))

ax.scatter(_x, _y, c=_c, s=60, alpha=0.9, edgecolors=[0,0,0])

print("Dark blue => {:0.4}, dark red => {:0.4}".format(min(_c), max(_c)))

7. 检查在测试数据上的最优估计器

crf = rs.best_estimator_
y_pred = crf.predict(X_test)
print(metrics.flat_classification_report(
    y_test, y_pred, labels=sorted_labels, digits=3
))

8.检查分类器学到了什么东西

from collections import Counter

def print_transitions(trans_features):
    for (label_from, label_to), weight in trans_features:
        print("%-6s -> %-7s %0.6f" % (label_from, label_to, weight))

print("Top likely transitions:")
print_transitions(Counter(crf.transition_features_).most_common(20))

print("\nTop unlikely transitions:")
print_transitions(Counter(crf.transition_features_).most_common()[-20:])

可以看到,例如,组织名称 (B-ORG) 的开头很可能后面跟着组织名称内部的标记 (I-ORG),但是从带有其他标签的令牌到 I-ORG 的转换会受到惩罚。

检查状态特征:

def print_state_features(state_features):
    for (attr, label), weight in state_features:
        print("%0.6f %-8s %s" % (weight, label, attr))    

print("Top positive:")
print_state_features(Counter(crf.state_features_).most_common(30))

print("\nTop negative:")
print_state_features(Counter(crf.state_features_).most_common()[-30:])

一些观察结果:

  • 9.385823 B-ORG word.lower():psoe-progresistas——模型记住了一些实体的名字——可能是过度拟合,或者我们的特征不充分,或者记忆确实有帮助;
  • 4.636151 I-LOC -1:word.lower():calle:“calle”是西班牙语中的一条街道; 模型了解到,如果前一个单词是“calle”,那么该标记可能是位置的一部分;
  • -5.632036 O word.isupper(), -8.215073 O word.istitle() :UPPERCASED 或 TitleCased 单词可能是某种实体;
  • -2.097561 O postag:NP ——专有名词(NP 是西班牙语标记集中的专有名词)通常是实体。

9.检查模型权重

CRFsuite CRF 模型使用两种特征:state featurestransition features。 使用eli5.explain_weights 检查它们的权重:
权重
输出结果
Transition features是有道理的:至少模型知道 I-ENITITY 必须遵循 B-ENTITY。 它还了解到某些转换不太可能发生,例如 在这个数据集中,在组织名称之后有一个位置并不常见(I-ORG -> B-LOC 具有很大的负权重)。

特征不使用地名词典,因此模型必须记住训练数据中的一些地理名称,例如 España 是一个位置。

如果对 CRF 进行更多的正则化,可以预期只有通用的特征会保留下来,而记忆化的标记将会消失。 使用 L1 正则化(c1 参数),大多数特征的系数应该被驱动为零。 检查一下正则化对 CRF 权重有什么影响:

crf = sklearn_crfsuite.CRF(
    algorithm='lbfgs',
    c1=200,
    c2=0.1,
    max_iterations=20,
    all_possible_transitions=False,
)
crf.fit(X_train, y_train)
eli5.show_weights(crf, top=30)

输出结果
输出结果
正如所看到的,记忆标记大部分都消失了,模型现在依赖于字形和 POS 标签。 只剩下几个非零特征。 在示例中,更改可能会使质量变差,但这是一个单独的问题。

现在,关注transition weights。 可以预期 O -> I-ENTIRY 转换具有较大的负权重,因为它们是不可能的。 但是这些转换在高度正则化模型和初始模型中都具有零权重,而不是负权重。 这里发生了一些事情。

它们为零的原因是 crfsuite 没有在训练数据中看到这些转换,并假设没有必要为它们学习权重,以节省一些计算时间。 这是默认行为,但可以使用 sklearn_crfsuite.CRF all_possible_transitions 选项将其关闭。 检查一下它如何影响结果:

crf = sklearn_crfsuite.CRF(
    algorithm='lbfgs',
    c1=0.1,
    c2=0.1,
    max_iterations=20,
    all_possible_transitions=True,
)
crf.fit(X_train, y_train);
eli5.show_weights(crf, top=5, show=['transition_features'])

输出结果
对于all_possible_transitions=True,CRF 为不可能的转换学习了大的负权重,比如 O -> I-ORG。

10. 定制化

上面的表格很大,有点难以检查; eli5提供了几个选项来只查看一部分功能。 只能检查一部分标签:

eli5.show_weights(crf, top=10, targets=['O', 'B-ORG', 'I-ORG'])

定制化
另一种选择是仅检查部分功能——它有助于检查功能是否按预期工作。 例如,使用feature_re参数和隐藏转换表来检查模型如何使用词形特征:

eli5.show_weights(crf, top=5, feature_re='^word\.is', horizontal_layout=False, show=['targets'])

输出结果
看起来不错——大写和标题大写的单词很可能是某种实体。

11.在控制台中进行格式化

也可以将结果格式化为文本(在控制台中可能有用):

expl = eli5.explain_weights(crf, top=5, targets=['O', 'B-LOC', 'I-LOC'])
print(eli5.format_as_text(expl))

注意:本文中代码可能由于相关依赖库版本无法完全实现!!!

参考资料

[1] Named Entity Recognition using sklearn-crfsuite
[2] CoNLL2002.ipynb
[3] 一文理解条件随机场CRF
[4] 利用CRF模型进行文本分类完整教程(Python语言)
[5] CRF进行中文命名实体识别(使用sklearn_crfsuite进行实现)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/189244.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

一种前端无源码定制化开发能力专利解读

背景 目前市面上一些web前端工程在打包发布之前都会进行代码混淆加密。代码混淆(Obfuscated code)是将计算机程序的代码&#xff0c;转换成一种功能上等价&#xff0c;但是难于阅读和理解的形式的行为。代码混淆可以用于程序源代码&#xff0c;也可以用于程序编译而成的中间代…

Leetcode刷题Day38-------------------动态规划

Leetcode刷题Day38-------------------动态规划 1. 理论基础 文章链接&#xff1a;https://programmercarl.com/%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92%E7%90%86%E8%AE%BA%E5%9F%BA%E7%A1%80.html视频链接&#xff1a;https://www.bilibili.com/video/BV13Q4y197Wg题目链接&a…

GBASE荣获“2022证券基金行业信息技术应用创新联盟年度优秀成员奖

日前&#xff0c;证券基金行业信息技术应用创新联盟2022年度峰会于上海成功举办&#xff0c;在会上&#xff0c;GBASE南大通用作为联盟成员单位&#xff0c;积极相应联盟号召&#xff0c;有力支撑证券信创建设&#xff0c;荣获“2022证券基金行业信息技术应用创新联盟年度优秀成…

第一章 Arm 架构概述(2023新)

第一章 启发式 Arm 架构解读 第二章 CPU微架构 第三章 系统微架构 第四章 总线微架构 第五章 监控微架构 第六章 安全微架构 第七章 虚拟化微架构 第八章 Armv9-A 架构 第九章 Armv8-M 架构 第十章 Armv8-R 架构 第十一章 Cortex-A715 解读 第十二章 Cortex-X3 解读…

图片怎么转成PDF格式?介绍三种转换思路

PDF文件作为一类办公常见格式&#xff0c;很多场合都会使用到。有时我们需要将图片转成PDF格式以方便归纳整理。图片资料怎么转成PDF呢&#xff1f;给大家介绍几个手机和电脑都可以用的方式。希望对你有帮助。方法一、用文件自带的转换功能将图片转成PDF随意打开一个PDF文件后&…

Github每日精选(第94期):免费网页在线情况监控

Upptime Upptime 是开源的正常运行时间监控和状态页面&#xff0c;完全由 GitHub Actions、Issues 和 Pages 提供支持。 Upptime 是 [GitHub Actions] 的一个非常巧妙的用法。您基本上可以根据需要获得免费的可配置正常运行时间监视器。 github 地址在这里。 特点 利用 G…

CTPN的Python实现笔记一

文章目录一、疑难代码讲解1. 文本框左上角标注置信度(1) s str(round(i[-1] * 100, 2)) %(2) cv2.putText() 函数(3) cv2.line()函数2. 文本框进行扩展操作3. 文本框进行NMS操作(1) 非极大值抑制函数def nms(dets, thresh):a. order scores.argsort()[::-1]b. xx1 np.maxim…

[oeasy]python0068_控制序列_清屏_控制输出位置_2J

光标位置 回忆上次内容 上次了解了键盘演化的过程 ESC 从 组合键到 独立按键 ESC 的目的 是进入控制序列配置控制信息 控制信息 \033[y;xH 设置光标位置\033[2J 清屏 这到底怎么控制来着&#xff1f;&#xff1f;&#xff1f;&#x1f914;现在 系统里 这些行为 是谁来实现的…

【机器学习 - 6】:梯度下降法(第一篇)

文章目录梯度下降法的理解图解极值点和最值点梯度下降法的求导运算公式推导梯度下降法的实现梯度下降法的理解 梯度下降法不是一个机器学习算法&#xff0c;既不是在做监督学习&#xff0c;也不是在做非监督学习&#xff0c;是一种基于搜索的最优化方法。 作用&#xff1a;最小…

【2319. 判断矩阵是否是一个 X 矩阵】

来源&#xff1a;力扣&#xff08;LeetCode&#xff09; 描述&#xff1a; 如果一个正方形矩阵满足下述 全部 条件&#xff0c;则称之为一个 X 矩阵 &#xff1a; 矩阵对角线上的所有元素都 不是 0 矩阵中所有其他元素都是 0 给你一个大小为 n x n 的二维整数数组 grid &a…

JVM虚拟机知识总结

什么是虚拟机&#xff1f;从字面意思上来看&#xff0c;顾名思义即使一台虚拟的计算机&#xff0c;用来执行虚拟的计算机指令&#xff0c;从大体上来看&#xff0c;虚拟机一般分为两种。一种是系统虚拟机&#xff0c;另外一种是程序虚拟机。系统虚拟机&#xff1a;代表为VMware…

微信小程序 java中医知识库百科科普

中管理员的主要功能有&#xff1a; 1.管理员输入账户登陆后台 2.个人中心&#xff1a;管理员修改密码和账户信息 3.用户管理&#xff1a;对注册的用户信息进行删除&#xff0c;查询&#xff0c;添加&#xff0c;修改 4.中医知识管理&#xff1a;对中医的知识信息进行添加&#…

什么是地址解析协议 (ARP)?

最近不想更文章了&#xff0c;药吃完了&#xff0c; 本文目录地址解析协议 &#xff08;ARP&#xff09; 含义ARP 是做什么的&#xff0c;它是如何工作的&#xff1f;地址解析协议与 DHCP 和 DNS 的关系是什么&#xff1f;它们有何不同&#xff1f;ARP 有哪些类型&#xff1f;1…

应急救护培训报名-因疫情原因,暂停开班?

应急救护培训报名背景和急救证书区别2023年一定要被培训项目报名渠道状态上海市红十字应急救护培训上海市医疗急救中心公众课程专业课程背景和急救证书区别 关于背景和急救证书区别&#xff0c;请参考&#xff0c;程序员的生命与急救 文章 2023年一定要被培训项目 由于疫情原…

Springboot+vue冷冻仓储进销存管理系统 java

端技术&#xff1a;nodejsvueelementui 前端&#xff1a;HTML5,CSS3、JavaScript、VUE 系统分为不同的层次&#xff1a;视图层&#xff08;vue页面&#xff09;&#xff0c;表现层&#xff08;控制器类&#xff09;&#xff0c;业务层&#xff08;接口类&#xff09;和持久层&a…

php宝塔搭建部署实战易优游戏竞技公司网站源码

大家好啊&#xff0c;我是测评君&#xff0c;欢迎来到web测评。 本期给大家带来一套php开发的易优游戏竞技公司网站源码&#xff0c;感兴趣的朋友可以自行下载学习。 技术架构 PHP7.2 nginx mysql5.7 JS CSS HTMLcnetos7以上 宝塔面板 文字搭建教程 下载源码&#xff…

SpringBoot或SpringCloud集成Nacos

一、创建一个项目首先创建一个空项目&#xff0c;然后引入Nacos的依赖&#xff0c;并选择正确的SpringBoot和Nacos版本&#xff0c;如果版本选择不对的话可能会启动失败&#xff0c;带来很大的问题<!--SpringBoot的版本--><parent><groupId>org.springframew…

详解axios(快速入门)

axios这一篇就够啦~axios1、axios的理解和使用1.1 axios概述1.2 axios特点1.3 axios常用语法1.4 难点语法的理解和使用2、axios源码分析2.1 源码目录结构2.2 源码分析axios 1、axios的理解和使用 1.1 axios概述 前端最流行的ajax请求库 react/vue官方都推荐使用axios 发ajax…

推荐系统与深度学习关联

6.1 推荐系统与深度学习关联 学习目标 目标 无应用 无 6.1.1 深度学习到推荐系统 深度学习发展成功与局限 最近几年深度学习的流行&#xff0c;大家一般认为是从2012年 AlexNet 在图像识别领域的成功作为一个里程碑。AlexNet 提升了整个业界对机器学习的接受程度&#xff1…

如果把小程序业务和研发管理都放到一个平台

伴随着互联网在中国进程的发展&#xff0c;线上研发效能及业务应用软件也不落后于时代进步的脚步&#xff0c;中国软件行业从未停止过持续的创新。 2022年&#xff0c;业务应用开发正在简化&#xff0c;研发效能也在提升&#xff0c;其中不得不提软件在协同促进、研发一体化管…