基于python BiLSTM-CRF的命名实体识别 附完整代码

简介: 基于python BiLSTM-CRF的命名实体识别 附完整代码

完整代码:https://download.csdn.net/download/qq_38735017/87427497


实验一、中文分词实现


1.1 问题描述


中文分词指的是将一个汉字序列切分成一个一个单独的词。中文分词是文本挖掘的基础,对于输入的一段中文,成功的进行中文分词,可以达到电脑自动识别语句含义的效果。它是信息提取、信息检索、机器翻译、文本分类、自动文摘、语音识别、文本语音转换、自然语言理解等中文信息处理领域的基础。


1.1.1 基础任务

实现基于词典的分词算法


实验一资料包下的“Dictionary_based”文件夹中提供了基础词典和分词算法的大致框架。分词算法的核心部分需要大家完成,实验中提供了若干测试样本用以帮助大家判断算法是否正确实现。


实现基于统计学习的分词算法


实验中给出 Bi-LSTM+CRF 模型的基础实现,相关代码及说明文档位于实验一资料包下的“Bi-LSTM+CRF”文件夹下。请根据给定的实验资料中 README.md 文件配置相应实验环境,说明:(1)提供源码 PyTorch 语言编写(可根据个人掌情况用其他语言编写),默认运行版本是 CPU 版本;(2)如希望运行 NPU 版本,大家可跟任课老师联系,申请华为云资源运行(需提前统计名单:姓名 + 学号 + 个人手机号码 + 邮箱);


1.1.2 选做任务


优化基础任务中实现的分词器,可考虑的优化方案有:


  • 修改网络结构,例如引入 BERT 等预训练语言模型;
  • 与命名实体识别算法相互配合,减少对命名实体的错误分割;
  • 构造合适的词典集(可扩充 + 人工整理);
  • 实现新词发现(登录)功能,识别测试集中的新词(未登录词);
  • 调整、优化模型训练过程中的超参数。


完成优化后对测试文件“Bi-LSTM+CRF/data/test.txt”进行分词,分词结果保存到.txt 文件中 utf-8 编码,词与词之间以空格分隔,每个测试样本占一行。文件“Bi-LSTM+CRF/cws_result.txt”中给出了输出示例。提交分词结果后,依据单词级别的 F1-score 进行评判,决定选做部分的实验分数。


单词级别的 F1-score 的计算方式如下:


  • Gold: 共同 创造 美好 的 新 世纪 —— 二 ○○ 一年 新年 贺词
  • Hypothesis: 共同 创造 美 好 的 新 世纪 —— 二 ○○ 一年 新年 贺词
  • Precision = 9 / 11 = 0.818
  • Recall = 9 / 10 = 0.9
  • F1-score = 2PrecisionRecall/(Precision+Recall)=0.857


1.2 模块设计-基于词典


1.2.1 前向最大匹配


从待分词句子的左边向右边搜索,寻找词的最大匹配。我们需要规定一个词的最大长度,每次扫描的时候寻找当前开始的这个长度的词来和字典中的词匹配,如果没有找到,就缩短长度继续寻找,直到找到字典中的词或者成为单字。

算法流程如下:


  • (1)从待分词子串中从前往后取出 max_len 个字,然后扫描分词字典,测试该 max_len 个字的子串是否在字典中;
  • (2)如果存在,则从待分词子串中删除掉该 max_len 个字的子串,重新按照规则取子串,重复(1);
  • (3)如果不存在于字典中,则减少该子串的最右一个字,之后重复(1)。


1.2.2 后向最大匹配


从待分词句子的右边向左边搜索,寻找词的最大匹配。同样,我们也需要规定一个词的最大长度,每次扫描的时候寻找当前开始的这个长度的词来和字典中的词匹配,如果没有找到,就缩短长度继续寻找,直到找到字典中的词或者成为单字。


算法流程如下:


  • (1)从待分词子串中从后往前取出 max_len 个字,然后扫描分词字典,测试该 max_len 个字的子串是否在字典中;
  • (2)如果存在,则从待分词子串中删除掉该 max_len 个字的子串,重新按照规则取子串,重复(1);
  • (3)如果不存在于字典中,则减少该子串的最左一个字,之后重复(1)。


1.2.3 双向最大匹配


将前向最大匹配算法和后向最大匹配算法进行比较,从而确定正确的分词方法。

算法流程如下:

  • (1)比较前向最大匹配和后向最大匹配结果;
  • (2)如果分词结果相同,返回其中任意一个;
  • (3)如果分词结果不同:
  • 比较两者分词总数量,数量高者罚分;
  • 比较两者分词中单字词数量,单字词多者罚分;
  • 比较两者分词中非字典词数量,非字典词多者罚分;
  • 选择罚分最少的作为最终结果。


1.3 模块设计-基于统计学习


1.3.1 data_u.py


  • getist:单个分词转换成 tag 序列。按行读入数据,并分析各个字对应的标签,然后返回分析结果。
  • handle_data:处理数据,并保存至 save_path。按行读取对应文件中的数据,并做相应的处理,然后把处理的结果保存到 data_save.pkl 中。


1.3.2 dataloader.py

读取通过 data_u.py 处理完后的文件 data_save.pkl,并将其向量化。

1.3.3 infer.py

通过已经训练好的模型,完成对测试文件的分析,并将分词结果保存到 cws_result.txt 文件中。

1.3.4 model.py


  • init_hidden:通过 torch.randn 函数进行初始化操作。
  • _get_lstm_features:获取 LSTM 框架。
  • forward:预测每个标签的 loss 值,以减少无效预测。
  • infer:采用 Bi-LSTM+CRF 的基础结构的分析结果。


1.3.5 run.py

采用小批量梯度下降法,对模型进行训练,使得 loss 值降低。

小批量梯度下降,是对批量梯度下降以及随机梯度下降的一个折中办法。其思想是:每次迭代 使用 batch_size 个样本来对参数进行更新,每次使用一个 batch 可以大大减小收敛所需要的迭代次数,同时可以使收敛到的结果更加接近梯度下降的效果。


1.4 代码实现-基于词典

1.4.1 前向最大匹配


defforward_mm_split(self,fmm_text):"""
正向最大匹配分词算法
:param fmm_text:待分词字符串
:return:分词结果,以list形式存放,每个元素为分出的词
"""# 字词列表,存放分词结果word_list=[]# 用于记录分词的起始位置count=0# 字或词当前的长度word_len=self.max_lenwhileword_len>0andcount<len(fmm_text):word=fmm_text[count:count+word_len]word_len=len(word)if(wordinself.words)or(wordinself.delimiter):word_list.append(word)count=count+word_lenword_len=self.max_lenelse:word_len=word_len-1returnword_list

1.4.2 后向最大匹配

defreverse_mm_split(self,rmm_text):"""
逆向最大匹配分词算法
:param rmm_text:待分词字符串
:return:分词结果,以list形式存放,每个元素为分出的词
"""# 字词列表,存放分词结果word_list=[]# 用于记录分词的末尾位置count=len(rmm_text)# 字或词当前的长度word_len=self.max_lenwhileword_len>0andcount>0:ifcount<=word_len:word=rmm_text[:count]else:word=rmm_text[(count-word_len):count]word_len=len(word)if(wordinself.words)or(wordinself.delimiter):word_list.insert(0,word)count=count-word_lenword_len=self.max_lenelse:word_len=word_len-1returnword_list

1.4.3 双向最大匹配

defbidirectional_mm_split(self,bi_text):"""
双向最大匹配分词算法
:param bi_text:待分词字符串
:return:分词结果,以list形式存放,每个元素为分出的词
"""# 前向最大匹配得到的分词结果forward=self.forward_mm_split(bi_text)# 后向最大匹配得到的分词结果reverse=self.reverse_mm_split(bi_text)# 总词数forward_total_words=len(forward)reverse_total_words=len(reverse)# 单字词个数forward_single_words=0reverse_single_words=0# 非字典词数forward_illegal_words=0reverse_illegal_words=0# 罚分,分值越低,表明结果越好forward_score=0reverse_score=0ifforward==reverse:returnforwardelse:# 统计前向匹配的各个词情况
  forwordinforward:iflen(word)==1:forward_single_words+=1ifwordnotinself.words:forward_illegal_words+=1# 统计后向匹配的各个词情况
  forwordinreverse:iflen(word)==1:reverse_single_words+=1ifwordnotinself.words:reverse_illegal_words+=1# 计算罚分
  ifforward_total_words<reverse_total_words:reverse_score+=1
  else:forward_score+=1
  ifforward_illegal_words<reverse_illegal_words:reverse_score+=1
  else:forward_score+=1
  ifforward_single_words<reverse_single_words:reverse_score+=1
  else:forward_score+=1# 比较罚分情况,罚分最小的选做最终结果
  ifforward_score<reverse_score:
    returnforward
  else:
    returnreverse

1.5 运行结果


利用实验包已给出的代码框架,实现完对应的匹配函数后,运行测试样例。

(1)基于词典的中文分词,得到输出结果如下:


f44eb08f68a7f969468d0f0a1246673f.png

(2)基于统计学习的中文分词,将输出的分词文件进行在线测评,结果如下:59cd443afae87ef6c3015fbe5661eae9.png



1.6 实验小结


实验中总体的代码框架已给出,需要实现的部分为前向最大匹配、后向最大匹配和双向最大匹配三个核心函数。通过相关资料的参考以及对中文分词的个人理解,能够顺利实现各个匹配模式的实现。


基于统计学习的中文分词代码,由于已经给出了代码框架,所以只是在其基础上做了部分优化,并增加了一部分的训练数据。

相关文章
|
4天前
|
缓存 监控 测试技术
Python中的装饰器:功能扩展与代码复用的利器###
本文深入探讨了Python中装饰器的概念、实现机制及其在实际开发中的应用价值。通过生动的实例和详尽的解释,文章展示了装饰器如何增强函数功能、提升代码可读性和维护性,并鼓励读者在项目中灵活运用这一强大的语言特性。 ###
|
7天前
|
缓存 开发者 Python
探索Python中的装饰器:简化代码,增强功能
【10月更文挑战第35天】装饰器在Python中是一种强大的工具,它允许开发者在不修改原有函数代码的情况下增加额外的功能。本文旨在通过简明的语言和实际的编码示例,带领读者理解装饰器的概念、用法及其在实际编程场景中的应用,从而提升代码的可读性和复用性。
|
3天前
|
Python
探索Python中的装饰器:简化代码,提升效率
【10月更文挑战第39天】在编程的世界中,我们总是在寻找使代码更简洁、更高效的方法。Python的装饰器提供了一种强大的工具,能够让我们做到这一点。本文将深入探讨装饰器的基本概念,展示如何通过它们来增强函数的功能,同时保持代码的整洁性。我们将从基础开始,逐步深入到装饰器的高级用法,让你了解如何利用这一特性来优化你的Python代码。准备好让你的代码变得更加优雅和强大了吗?让我们开始吧!
12 1
|
8天前
|
设计模式 缓存 监控
Python中的装饰器:代码的魔法增强剂
在Python编程中,装饰器是一种强大而灵活的工具,它允许程序员在不修改函数或方法源代码的情况下增加额外的功能。本文将探讨装饰器的定义、工作原理以及如何通过自定义和标准库中的装饰器来优化代码结构和提高开发效率。通过实例演示,我们将深入了解装饰器的应用,包括日志记录、性能测量、事务处理等常见场景。此外,我们还将讨论装饰器的高级用法,如带参数的装饰器和类装饰器,为读者提供全面的装饰器使用指南。
|
4天前
|
存储 缓存 监控
掌握Python装饰器:提升代码复用性与可读性的利器
在本文中,我们将深入探讨Python装饰器的概念、工作原理以及如何有效地应用它们来增强代码的可读性和复用性。不同于传统的函数调用,装饰器提供了一种优雅的方式来修改或扩展函数的行为,而无需直接修改原始函数代码。通过实际示例和应用场景分析,本文旨在帮助读者理解装饰器的实用性,并鼓励在日常编程实践中灵活运用这一强大特性。
|
8天前
|
存储 算法 搜索推荐
Python高手必备!揭秘图(Graph)的N种风骚表示法,让你的代码瞬间高大上
在Python中,图作为重要的数据结构,广泛应用于社交网络分析、路径查找等领域。本文介绍四种图的表示方法:邻接矩阵、邻接表、边列表和邻接集。每种方法都有其特点和适用场景,掌握它们能提升代码效率和可读性,让你在项目中脱颖而出。
21 5
|
6天前
|
机器学习/深度学习 数据采集 人工智能
探索机器学习:从理论到Python代码实践
【10月更文挑战第36天】本文将深入浅出地介绍机器学习的基本概念、主要算法及其在Python中的实现。我们将通过实际案例,展示如何使用scikit-learn库进行数据预处理、模型选择和参数调优。无论你是初学者还是有一定基础的开发者,都能从中获得启发和实践指导。
17 2
|
8天前
|
数据库 Python
异步编程不再难!Python asyncio库实战,让你的代码流畅如丝!
在编程中,随着应用复杂度的提升,对并发和异步处理的需求日益增长。Python的asyncio库通过async和await关键字,简化了异步编程,使其变得流畅高效。本文将通过实战示例,介绍异步编程的基本概念、如何使用asyncio编写异步代码以及处理多个异步任务的方法,帮助你掌握异步编程技巧,提高代码性能。
26 4
|
10天前
|
缓存 开发者 Python
探索Python中的装饰器:简化和增强你的代码
【10月更文挑战第32天】 在编程的世界中,简洁和效率是永恒的追求。Python提供了一种强大工具——装饰器,它允许我们以声明式的方式修改函数的行为。本文将深入探讨装饰器的概念、用法及其在实际应用中的优势。通过实际代码示例,我们不仅理解装饰器的工作方式,还能学会如何自定义装饰器来满足特定需求。无论你是初学者还是有经验的开发者,这篇文章都将为你揭示装饰器的神秘面纱,并展示如何利用它们简化和增强你的代码库。
|
8天前
|
API 数据处理 Python
探秘Python并发新世界:asyncio库,让你的代码并发更优雅!
在Python编程中,随着网络应用和数据处理需求的增长,并发编程变得愈发重要。asyncio库作为Python 3.4及以上版本的标准库,以其简洁的API和强大的异步编程能力,成为提升性能和优化资源利用的关键工具。本文介绍了asyncio的基本概念、异步函数的定义与使用、并发控制和资源管理等核心功能,通过具体示例展示了如何高效地编写并发代码。
19 2