完整代码: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)基于词典的中文分词,得到输出结果如下:
(2)基于统计学习的中文分词,将输出的分词文件进行在线测评,结果如下:
1.6 实验小结
实验中总体的代码框架已给出,需要实现的部分为前向最大匹配、后向最大匹配和双向最大匹配三个核心函数。通过相关资料的参考以及对中文分词的个人理解,能够顺利实现各个匹配模式的实现。
基于统计学习的中文分词代码,由于已经给出了代码框架,所以只是在其基础上做了部分优化,并增加了一部分的训练数据。