SmoothNLP新词发现算法的改进实现

news2025/2/26 6:10:46

SmoothNLP新词发现算法的改进实现

背景介绍

新词发现也叫未登录词提取,依据 《统计自然语言处理》(宗成庆),中文分词有98%的错误来自"未登录词"。即便早就火遍大江南北的Bert也不能解决"未登录词"的Encoding问题,便索性使用‘字’作为最小单元。

未登录词除了来自各行各业的专有名词、缩略语外,还有与时俱进的流行词、新生词,所以通过有监督大量标注的方式性价比过低。

一个行业的词库构建往往都是从0开始的,迁移使用也就是治标不治本。而且没有几个专业人员敢保证坐在那里就能滔滔不绝的把行业内所有词汇撰写下来,所以新词发现也是构建词库的重要辅助工具。

另外最近遇到的热词提取的需求越来越多,热词首先要定位好词,对于新出现的词,即便大量出现也会由于分词器不能正确识别的缘故难以上榜。

和其他AI算法一样,新词发现也有基于BiLSTM+CRF进行序列化标注的有监督算法,也有不需要标注的无监督算法。我个人看来,与其倾注时间在有监督的预训练标注,不如多花花心思在无监督结果确认上。无监督的新词发现,不管是轻量易用的Jieba、丰富工业API的HanLP、还是高校自研的LTP、Thulac、Pkuseg都有实现无监督的新词发现(有的叫关键短语提取)。这些新词发现基本都是基于TFIDF、自由度、凝聚度(部分引入HMM),接下来我就介绍一下业界常用到的NLP工具SmoothNLP中的新词发现——基于自由度和凝聚度的新词发现。

基于自由度和凝聚度的新词发现

一些简单的名词

  • 词元(gram): 定义语料中文字存在最小单位,可以是字可以是分词结果的词。由N个词元组成的新词元叫N词元(NGram),比如规定每个字都是一个词元,则‘华宇信息’就是一个4词元。以下对词元均简称G。
  • 自由度 : 何为自由度,指的就是如果一个或几个词元组合可以成新词nG,它应当出现在丰富的语境中,换句话说就是这个新词比较自由,没有什么特定的和其他词元Gi一起出现的规则,因为如果有,就说明有可能nG应该和Gi组成新词(n+1)G
  • 凝聚度 : 何为凝聚度,就是指组成新词nG的各个词元(G1,…,Gi)应该展现出一定的关联性,或者规律性。

SmoothNLP的定义

  • 以每个作为词元,这样的好处是避免了分词器的干扰,比如语句‘随着芈月传播出 ’分词结果是['随着 ','芈月 ','传播 ',' '],如果基于这个生成新词,那么永远不可出现‘芈月传 ’,所以用字作为词元
  • 在SmoothNLP中将自由度用左右临近信息熵(Left and Right Entropy,LRE)来表示。
  • 在SmoothNLP中将凝聚度用平均互信息(Average Mutual Information,AMI)来表示。

左右临近信息熵

信息熵

信息熵是对信息量多少的度量,信息熵越高,表示信息量越丰富、不确定性越大。相反越小则纯度越高。对于集合D有N个样本di,定义每个样本概率为Pdi,则集合D的信息熵为:
E n t r o p y D = − ∑ ( d i ∈ D ) P ( d i ) log ⁡ 2 P ( d i ) \mathbf{Entropy}_{D}=-\sum_{({d}_{i}\in{D})}{P({d}_{i})\log_2{P({d}_{i})}} EntropyD=(diD)P(di)log2P(di)

临近词元信息熵

SmoothNLP将候选词nG的临近字(SmoothNLP定义的词元是)两两组合构成集合,并定义每个样本概率为nG与临近字Gi的条件概率,计算该集合的信息熵。由左临近字构成的集合信息熵就是左临近信息熵,由右临近字构成的集合信息熵就是右临近信息熵,具体公式为
L E n G = − ∑ ( G i ∈ G l e f t − n e i g h b o r ) P ( G i ∣ n G ) log ⁡ 2 P ( G i ∣ n G ) \mathbf{LE}_{nG} = -\sum_{({G}_{i}\in{G}_{left-neighbor})}{P({G}_{i}|{nG})\log_2{P({G}_{i}|nG)}} LEnG=(GiGleftneighbor)P(GinG)log2P(GinG)
L E n G = − ∑ ( G j ∈ G r i g h t − n e i g h b o r ) P ( G j ∣ n G ) log ⁡ 2 P ( G j ∣ n G ) \mathbf{LE}_{nG} = -\sum_{({G}_{j}\in{G}_{right-neighbor})}{P({G}_{j}|{nG})\log_2{P({G}_{j}|nG)}} LEnG=(GjGrightneighbor)P(GjnG)log2P(GjnG)

综合左右临近信息熵

左右单侧的信息熵高并不能说明问题,要将左右信息熵综合考虑,这里SmoothNLP的做法是对左右临近信息熵取对数平均值:
L R E = log ⁡ L E ∗ e R E + R E ∗ e L E ∣ L E − R E ∣ \mathbf{LRE} = \log{\frac{{LE}*{e}^{RE}+{RE}*{e}^{LE}}{|LE-RE|}} LRE=logLERELEeRE+REeLE

Tire

为了实现上述逻辑,方便查询N词元的左右临近字,选取字典树Tire存储。

举个栗子

以市场监督管理局数据2W条留言标题为测试样例为例。
在这里插入图片描述

很明显‘混凝’只有跟‘土’,‘凝土’前面只有‘混’,而‘混凝土’前后的就多了去了,所以‘混凝土’大概率是个词。

平均互信息

互信息

互信息是衡量随机变量之间相互依赖程度的度量,假如看见一个背电脑包戴帽子的小伙子,那么‘他职业是程序猿 ’是个随机事件,‘他帽子下是秃头 ’同样是个随机事件,那么这两个随机事件互相依赖的程度是‘已知他职业是程序猿的情况下,他帽子下是秃头的可能性 ’与‘已知帽子下是秃头的情况下,他职业是程序猿的可能性 ’之差,具体公式是:
M I = ∑ y ∈ Y ∑ x ∈ X P ( x , y ) log ⁡ P ( x , y ) P ( x ) P ( y ) \mathbf{MI} = \sum_{{y}\in{Y}}{\sum_{{x}\in{X}}{P(x,y)\log\frac{P(x,y)}{P(x)P(y)}}} MI=yYxXP(x,y)logP(x)P(y)P(x,y)

N词元互信息

SmoothNLP并没有严格按照互信息公式,而是使用点间互信息(point-wise mutual information,PMI),假设新词nG由N个词元Gi构成,则该新词PMI为
P M I = log ⁡ 2 P ( G 1 , . . . , G i ) ∏ G i ∈ n G P ( G i ) \mathbf{PMI} = \log_{2}{\frac{P({G}_1,...,{G}_i)}{\prod_{{G}_i\in{nG}}{P({G}_i)}}} PMI=log2GinGP(Gi)P(G1,...,Gi)

N词元平均互信息

由于点间互信息的值会受到候选词长度的影响——候选词越长,互信息取值偏大,所以使用平均互信息:
A M I = 1 n P M I \mathbf{AMI} = \frac{1}{n}\mathbf{PMI} AMI=n1PMI

在SmoothNLP的实现上做优化

SmoothNLP调皮了

不知什么原因,SmoothNLP很调皮并没有按照上述公式一板一眼的实现,而是加入一些论文中没有提到的‘个人想法’。

# 语料分批读取chunk_size
for corpus_batch in corpus:
	# 删除非中文、非英文、非数字字符
	del_unused_char()
	# 遍历处理后语料
	for line in corpus_batch :
		# 遍历1词元以及min到max+1词元
		for n in [1]+[min,max+1]:
			# 构建N词元词频字典
			n_gram_freq_dict[n] = build_n_gram_freq_dict()
			# 删除频率低于min_freq的N词元
			del_low_freq_n_gram()

for n in [min,max]:
	# 利用N+1词元词频字典构建N词元左右Tire
	left_tire,right_tire = build_tire_from_up(n_gram_freq_dict[n+1])
	# 计算N词元左右信息熵
	gram_n_lre_dict[n] = cal_lre(left_tire,right_tire)

# 计算1词元频率总和
gram_1_total = cal_gram_1_total(n_gram_freq_dict[1])

for n in [min,max]:
	# 计算N词元总和
	gram_n_total = cal_gram_n_total(n_gram_freq_dict[n])
	# 计算N词元平均互信息
	gram_n_pmi_dict[n] = cal_pmi(n_gram_freq_dict[1],gram_1_total,n_gram_freq_dict[n],gram_n_total)
	# 计算N词元评分
	gram_n_score_dict[n] = cal_score(gram_n_pmi_dict[n],gram_n_lre_dict[n])

# 统计所有词元首词元,统计所有词元尾词元
start_gram_freq,end_gram_freq = build_start_end_gram_freq(gram_n_score_dict)

# 移除包含频率高于阈值 首尾词元的词元
result = drop_some_gram(gram_n_score_dict,start_gram_freq,end_gram_freq)

# 排序
result.sort()	

单从算法逻辑上可以发现以下几个问题:

  • 语料是分批读取的,而且删除频率低于min_freq的N词元是在一个批内执行的,这样会误删一些‘单批内词频低,但总体词频高于min_freq’的候选词
  • 他是根据N+1词元频率构建的N词元左右Tire,这样考虑是因为他是以字为词元的,那么N+1词元就可以拆分成‘首字词元+N词元’或‘N词元+尾字词元’。但其实大可不必浪费时间额外统计一次max+1词元频率。
  • 另外好多统计操作其实是可以在一个预语料循环里完成的。
  • 还有好多中间变量是应该回收的

总体优化

针对以上问题我做了以下调整【Smoothnlp为左图,我实现的右图】
在这里插入图片描述

删除频率低于min_freq的N词元我拿到了计算评分时做。

可以发现在我的算法没有提及[min,max]的遍历,是因为我写的只是一个n词元,没错我也调皮了,不过我调皮一下使算法多线程成为可能。

这里我们再看一下SmoothNLP在多线程方面的尝试:
在这里插入图片描述


看来这个todo要我们来写了,其实他不写也是有原因的,对比看一下简化的逻辑图便知,先看SmoothNLP。

在这里插入图片描述


每元计算都是在词频上关联的,多线程就要一个等一个

再看看我们的
在这里插入图片描述


移除不必要的max+1词元计算,我们在第一次遍历语料时就顺带创建左右临近树的元素列表了。省了一次计算并使得每元计算解耦,这样就使得多线程成为可能。

细节优化

再具体公式实现上,Smoothnlp也并没有严格实现以及一些牺牲一部分精度:

  • 公式中常量值或对数底数采取了取近似值
  • 定义高频首末词元的阈值很小,比例在小数据集上效果不会太好
  • 保留数字和英文,这方面的新词还是比较少的,但出现的自由度越普遍很高。
  • 由于分批读取的缘故在频率字典融合时使用Update反而会覆盖原本有的,而不是与原本的相加。

既然我们能多线程了,就不近似了也不牺牲精度了,也解决巴拉巴拉的问题。

另外这里详细说一下我们是怎么实现‘第一次遍历语料时就顺带创建左右临近树的元素列表’的。

Smoothnlp遍历语料生成词元其实就是返回N长的字符串子串,而我们定义了一种数据结构,一种元组。

Tuple[Tuple[],Tuple[],Tuple[]]

这元组里有三个元素:

  • 第一个是左临近词元元组,只有一个元素,左临近词元
  • 第二个是N词元元组,是组成N词元的每个词元
  • 第一个是右临近词元元组,只有一个元素,右临近词元

之后使用Python的Counter就轻松实现Tire树的数据字典的构建。

这种实现又方便了基于分词结果的新词发现。

另外SmoothNLP是直接将‘无效字符’替换成掉,这样会带来一个问题,比如例句。

“根据《劳动法》规定能保护…”

我们希望看到的肯定是【‘根据’ ‘劳动法’ ‘规定’ ‘能’ ‘保护’】,但是由于直接替换掉,变成了 “根据劳动法规定能保护…”,那么在计算2次元结果就变成了【‘根据’ ‘劳动’ ‘法规’ ‘定能’ ‘保护’】,这是对提出‘劳动法’这个词的干扰。

优化效果

看下效果,都是基于【某政府机关网站2W条留言标题数据】的新词发现,最小2词元,最多5词元,最小词元频率5 。smoothnlp效果如下,绿色是正常有效词,黄色是正常无效词,红色是不正常的词:

在这里插入图片描述


我的优化效果如下:

在这里插入图片描述

基于分词结果的新词发现

虽然Smoothnlp依靠‘芈月传’的例子摒弃了,基于分词结果的新词发现,但是不可否认的是,基于字的新词发现往往都是分词词典里已经有了的短词,换句话说基于分词结果的新词发现并不是没有意义,相反他的效率也许更高。

所以我实现了基于字和基于分词两种新词发现,后者是使用jieba默认词典分词的。

基于【某政府机关网站2W条留言标题数据】,最小2词元,最多3词元,最小词元频率5效果如下:

在这里插入图片描述

一点点想法

我计划基于这个制作一个根据语料构建行业词典的辅助服务。

本文多是个人的一些理解欢迎交流、欢迎批评指正。

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

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

相关文章

棋牌类游戏测试用例怎么写?我敢打赌你绝对不知道

目录 一.登陆 二.大厅 三.小游戏 四.银行功能 五.其他按钮 总结感谢每一个认真阅读我文章的人!!! 重点:配套学习资料和视频教学 一.登陆 1&#xff0e…

Redis高可用集群方案

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 @[TOC](文章目录)主从复制哨兵模式(sentinel)Cluster集群在生产过程中,Redis不一定会单独部署。因为一旦redis服务因为某些原因导致无法提供数,那么redis就不可用了。那么实现redis高可用的方式就…

Orin装机

安装目录orin刷机谷歌输入法ROS遇到的问题:1、sudo rosdep init2、rosdep updatelibrealsenserealsense_ros安装librealsense安装realsense_ros总的来说就是,注意librealsense和realsense-ros的版本,对于librealsense,采用源码安装…

企业级分布式应用服务 EDAS

什么是企业级分布式应用服务EDAS企业级分布式应用服务EDAS(Enterprise Distributed Application Service)是一个应用托管和微服务管理的云原生PaaS平台,提供应用开发、部署、监控、运维等全栈式解决方案,同时支持Spring Cloud和Ap…

gcc/g++、动静态库、make/makefile

目录 gcc/g gcc和g的对比 "一段代码的使命" ●预处理 ●编译 ●汇编 ●链接 ●动/静态链接 make/makefile gcc/g gcc和g的对比 对于c文件而言,使用gcc或者g并没有什么区别。而对于cpp文件,在预处理、编译、汇编这三部分,…

《精通Spring4.x 企业应用开发实战》第1章 Spring概述

目录标题前言一、Spring带给我们什么二、Spring体系结构三、Spring4.0新特性核心容器的增强泛型依赖注入Map依赖注入Lazy延迟依赖注入List注入Conditional 注解CGLIB 代理类增强其他四、Spring 子项目总结前言 汇总:《精通Spring4.x 企业应用开发实战》 一、Spring带…

L2-010 排座位

布置宴席最微妙的事情,就是给前来参宴的各位宾客安排座位。无论如何,总不能把两个死对头排到同一张宴会桌旁!这个艰巨任务现在就交给你,对任何一对客人,请编写程序告诉主人他们是否能被安排同席。 输入格式&#xff1…

【LeetCode】剑指 Offer 10- II. 青蛙跳台阶问题 p77 -- Java Version

题目链接:https://leetcode.cn/problems/qing-wa-tiao-tai-jie-wen-ti-lcof/ 1. 题目介绍(10- II. 青蛙跳台阶问题) 一只青蛙一次可以跳上1级台阶,也可以跳上2级台阶。求该青蛙跳上一个 n 级的台阶总共有多少种跳法。 答案需要取…

采购评标管理过程是怎样的?有哪些评标标准?

采购活动的评标是检查和比较投标的有组织的过程,以选择最佳报价,努力获得实现企业目标所需的货物、工程和服务。 评标是由一个被称为评标小组的机构负责。这个小组如何称呼,取决于企业的情况。同义词有报价审查小组、投标审查委员会或投标审…

在ONLYOFFICE中借助ChatGPT一键创建招聘启事的内容

大家好,相信和多人都在生活中或工作中看到过招聘启示,或多或少都会有些了解。今天教大家在ONLYOFFICE中怎样通过chetGPT创建一份满意的招聘启示,下面是我用chatgpt制作的一份招聘信息,请大家看一下。 ONLYOFFICE ONLYOFFICE文档是…

从0到1实现单机记账APP原理与细节uniApp内含源码 (一)

单机记账APP演示及源码 具体演示如下面视频所示。免费下载地址:点击进入 预览APP(内含开屏广告)下载地址:http://8.142.10.182:8888/down/aWHWeGaEQE2W.apk 服务器买的便宜,带宽很小所以下载速度慢,主要还…

mathtype7.0最新版安装下载及使用教程

MathType是一款专业的数学公式编辑器,理科生专用的必备工具,可应用于教育教学、科研机构、工程学、论文写作、期刊排版、编辑理科试卷等领域。2014年11月,Design Science将MathType升级到MathType 6.9版本。在苏州苏杰思网络有限公司与Design…

APP任务模块功能借助php-resque实现业务解耦

先上设计图 说明:任务模块分一次性任务和每日任务,可能还包括男女用户任务区分 处理步骤: 一、同步任务数据库 1.1、任务列表数据库 1.2、完成任务数据库 二、搭建即时消息队列 一、composer require resque/php-resque二、因为服务器red…

数据结构:栈和队列(Leetcode20. 有效的括号+225. 用队列实现栈+232. 用栈实现队列)

目录 一.数据结构--栈 1.栈的基本介绍 2.栈的实现 二.数据结构--队列 1.队列的基本介绍 2.队列的实现 三.栈的运用(Leetcode20. 有效的括号225) 1.问题描述 2.问题分析 题解代码: 四.用两个队列实现栈(225. 用队列实现栈 - 力扣(Leetcode&a…

Talk | 清华大学交叉信息研究院助理教授杜韬:利用计算方法探究流固耦合

本期为TechBeat人工智能社区第474期线上Talk! 北京时间2月15日(周三)20:00,清华大学交叉信息研究院助理教授——杜韬的Talk将准时在TechBeat人工智能社区开播! 他与大家分享的主题是: “利用计算方法探究流固耦合”,届时将介绍流固…

Windows10使用-处理IE自动跳转至Edge

文章目录 前言一、调整Edge二、调整Internet选项三、搜索栏的恢复总结前言 微软官方宣布,自2023年2月14日永久停止支持Internet Explorer 11浏览器。后期点击IE 图标将会自动跳转到Edge界面。对于一些网站,可能需要使用IE模式才能正常使用,这时候就需要做相应的调整,才能够…

做外贸怎么找客户

现在国内贸易内卷非常严重,很多商家都转向海外市场了,总结而言,目前所有做外贸的人,核心的点就是要找到重点意向客户,今天就和大家分享一下目前市面上外贸找客户的几种方法。主动出击式开发外贸客户1、参加展会找外贸客…

使用拦截器实现登录状态检测(以及在注册拦截器类时要使用ioc中的拦截器类)

拦截器 preHandler(HttpServletRequest request, HttpServletResponse response, Object handler) 方法在请求处理之前被调用。该方法在 Interceptor 类中最先执行,用来进行一些前置初始化操作或是对当前请求做预处理,也可以进行一些判断来决定请求是否…

C++类和对象-继承多态

继承 继承是面向对象三大特性之一 定义类时,下级别的成员除了拥有上一级的共性,还有自己的特性,就可以考虑使用继承的技术,减少代码的重复 继承的基本语法 语法:class 子类 : 继承方式 父类 子类也被成为派生类父类…

FreeRTOS队列

队列简介队列是一种任务到任务,任务到中断,中断到任务数据交流得一种机制。在队列中可以存储数量有限,大小固定得多个数据,队列中的每一个数据叫做队列项目,队列能够存储队列项目的最大数量称为队列的长度,…