自然语言处理从入门到应用——自然语言处理的基础任务:中文分词和子词切分

分类目录:《自然语言处理从入门到应用》总目录


自然语言处理的一大特点是任务种类纷繁复杂,有多种划分的方式。从处理顺序的角度,可以分为底层的基础任务以及上层的应用任务。其中,基础任务往往是语言学家根据内省的方式定义的,输出的结果往往作为整个系统的一个环节或者下游任务的额外语言学特征,而并非面向普罗大众。本文和后文介绍几种常见的基础任务,包括词法分析(分词、词性标注)、句法分析和语义分析等。

中文分词

词(Word)是最小的能独立使用的音义结合体,是能够独立运用并能够表达语义或语用内容的最基本单元。在以英语为代表的印欧语系(Indo-European lan-guages)中,词之间通常用分隔符(空格等)区分。但是在以汉语为代表的汉藏语系(Sino-Tibetan languages),以及以阿拉伯语为代表的闪-含语系(Semito-Hamitic languages)中,却不包含明显的词之间的分隔符。因此,为了进行后续的自然语言处理,通常需要首先对不含分隔符的语言进行分词(Word Segmentation)操作。本文以中文分词为例,介绍词的切分问题和最简单的分词算法。

中文分词就是将一串连续的字符构成的句子分割成词语序列,如“我喜欢读书”,分词后的结果为“我 喜欢 读书”。最简单的分词算法叫作正向最大匹配(Forward Maximum Matching,FMM)分词算法,即从前向后扫描句子中的字符串,尽量找到词典中较长的单词作为分词的结果。具体代码如下:

def fmm_word_seg(sentence, lexicon, max_len):
    begin = 0
    end = min(begin + max_len, len(sentence))
    words = []
    while begin < end:
        word = sentence[begin:end]
        if word in lexicon or end - begin == 1:
            words.append(word)
            begin = end
            end = min(begin + max_len, len(sentence))
        else:
            end -= 1
    return words

正向最大匹配分词算法存在的明显缺点是倾向于切分出较长的词,这容易导致错误的切分结果,如“研究生命的起源”,由于“研究生”是词典中的词,所以使用正向最大匹配分词算法的分词结果为“研究生命的起源”,显然分词结果不正确。这种情况一般被称为切分歧义问题,即同一个句子可能存在多种分词结果,一旦分词错误,则会影响对句子的语义理解。正向最大匹配分词算法除了存在切分歧义,对中文词的定义也不明确,如“哈尔滨市”可以是一个词,也可以认为“哈尔滨”是一个词,“市”是一个词。因此,目前存在多种中文分词的规范,根据不同规范又标注了不同的数据集。另外,就是未登录词问题,也就是说有一些词并没有收录在词典中,如新词、命名实体、领域相关词和拼写错误词等。由于语言的动态性,新词语的出现可谓是层出不穷,所以无法将全部的词都及时地收录到词典中,因此,一个好的分词系统必须能够较好地处理未登录词问题。相比于切分歧义问题,在真实应用环境中,由未登录词问题引起的分词错误比例更高。因此,分词任务本身也是一项富有挑战的自然语言处理基础任务,可以使用包括后文介绍的多种机器学习方法加以解决,将在后续相关文章中进行详细的介绍。

子词切分

一般认为,以英语为代表的印欧语系的语言,词语之间通常已有分隔符(空格等)进行切分,无须再进行额外的分词处理。然而,由于这些语言往往具有复杂的词形变化,如果仅以天然的分隔符进行切分,不但会造成一定的数据稀疏问题,还会导致由于词表过大而降低处理速度。如“computer”“computers”“computing”等,虽然它们语义相近,但是被认为是截然不同的单词。传统的处理方法是根据语言学规则,引入词形还原(Lemmatization)或者词干提取(Stemming)等任务,提取出单词的词根,从而在一定程度上克服数据稀疏问题。其中,词形还原指的是将变形的词语转换为原形,如将“computing”还原为“compute”;而词干提取则是将前缀、后缀等去掉,保留词干(Stem),如“computing”的词干为“comput”,可见,词干提取的结果可能不是一个完整的单词。

词形还原或词干提取虽然在一定程度上解决了数据稀疏问题,但是需要人工撰写大量的规则,这种基于规则的方法既不容易扩展到新的领域,也不容易扩展到新的语言上。因此,基于统计的无监督子词(Subword)切分任务应运而生,并在现代的预训练模型中使用。所谓子词切分,就是将一个单词切分为若干连续的片段。目前有多种常用的子词切分算法,它们的方法大同小异,基本的原理都是使用尽量长且频次高的子词对单词进行切分。此处重点介绍常用的字节对编码(Byte Pair Encoding,BPE)算法。首先,BPE通过下面的算法构造子词词表:

构造子词词表
输入:规模生文本语料库;期望的子词词表大小 L L L
输出:子词词表
算法:
( 1 ) 将语料库中每个单词切分成字符作为子词
( 2 ) 用切分的子词构成初始子词词表
( 3 ) while 子词词表小于或等于 L L L do
( 4 ) \quad 在语料库中统计单词内相邻子词对的频次
( 5 ) \quad 选取频次最高的子词对,合并成新的子词
( 6 ) \quad 将新的子词加入子词词表,并将语料库中不再存在的子词从子词词表中删除
( 7 )end while

下面,通过一个例子说明如何构造子词词表。首先,假设语料库中存在下列Python词典中的3个单词以及每个单词对应的频次。其中,每个单词结尾增加了一个</w>字符,并将每个单词切分成独立的字符构成子词。

{'l o v e r </w>':2, 'n e w e s t </w>':6, 'w i d e s t </w>':3}

初始化的子词词表为3个单词包含的全部字符:

{'l', 'o', 'w', 'e', 'r', 'n', 's', 't', 'i', 'd', '</w>'}

然后,统计单词内相邻的两个子词的频次,并选取频次最高的子词对'e''s',合并成新的子词'es'(共出现9次),然后加入子词词表中,并将语料库中不再存在的子词's'从子词词表中删除。此时,语料库以及子词词表变为:

{'l o v e r </w>':2, 'n e w es t </w>':6, 'w i d es t </w>':3}
{'l', 'o', 'w', 'e', 'r', 'n', 'es', 't', 'i', 'd', '</w>'}

然后,合并下一个子词对’es’和’t’,新的语料库和子词词表为:

{'l o v e r </w>':2, 'n e w est </w>':6, 'w i d est </w>':3}
{'l', 'o', 'w', 'e', 'r', 'n', 'est', 'i', 'd', '</w>'}

重复以上过程,直到子词词表大小达到一个期望的词表大小为止。构造好子词词表后,可以采用贪心的方法将一个单词切分成子词序列,即首先将子词词表按照子词的长度由大到小进行排序。然后,从前向后遍历子词词表,依次判断一个子词是否为单词的子串,如果是的话,则将该单词切分,然后继续向后遍历子词词表。如果子词词表全部遍历结束,单词中仍然有子串没有被切分,那么这些子串一定为低频串,则使用统一的标记,如<UNK>进行替换。例如,对一个含有三个单词的句子['the</w>','highest</w>','mountain</w>']进行切分,假设排好序的词表为['errrr</w>','tain</w>','moun','est</w>','high','the</w>','a</w>'],则子词切分的结果为['the</w>','high','est</w>','moun','tain</w>']。此过程也叫作对句子(单词序列)进行编码。

那么,如何对一个编码后的句子进行解码,也就是还原成原始的句子呢?此时,单词结尾字符</w>便发挥作用了。只要将全部子词进行拼接,然后将结尾字符替换为空格,就恰好为原始的句子了。通过以上过程可以发现,BPE算法中的编码步骤需要遍历整个词表,是一个非常耗时的过程。可以通过缓存技术加快编码的速度,即将常见单词对应的编码结果事先存储下来,然后编码时通过查表的方式快速获得编码的结果。对于查不到的单词再实际执行编码算法。由于高频词能够覆盖语言中的大部分单词,因此该方法实际执行编码算法的次数并不多,因此可以极大地提高编码过程的速度。

除了BPE,还有很多其他类似的子词切分方法,如WordPiece、Unigram Language Model(ULM)算法等。其中,WordPiece与BPE算法类似,也是每次从子词词表中选出两个子词进行合并。与BPE的最大区别在于,选择两个子词进行合并的策略不同:BPE选择频次最高的相邻子词合并,而WordPiece选择能够提升语言模型概率最大的相邻子词进行合并。经过公式推导,提升语言模型概率最大的相邻子词具有最大的互信息值,也就是两子词在语言模型上具有较强的关联性,它们经常在语料中以相邻方式同时出现。与WordPiece一样,ULM同样使用语言模型挑选子词。不同之处在于,BPE和WordPiece算法的词表大小都是从小到大变化,属于增量法。而ULM则是减量法,即先初始化一个大词表,根据评估准则不断丢弃词表中的子词,直到满足限定条件。ULM算法考虑了句子的不同分词可能,因而能够输出带概率的多个子词分段。为了更方便地使用上述子词切分算法,Google推出了SentencePiece开源工具包,其中集成了BPE、ULM等子词切分算法,并支持Python、C++编程语言的调用,具有快速、轻量的优点。此外,通过将句子看作Unicode编码序列,从而使其能够处理多种语言。

参考文献:
[1] 车万翔, 崔一鸣, 郭江. 自然语言处理:基于预训练模型的方法[M]. 电子工业出版社, 2021.
[2] 邵浩, 刘一烽. 预训练语言模型[M]. 电子工业出版社, 2021.
[3] 何晗. 自然语言处理入门[M]. 人民邮电出版社, 2019
[4] Sudharsan Ravichandiran. BERT基础教程:Transformer大模型实战[M]. 人民邮电出版社, 2023
[5] 吴茂贵, 王红星. 深入浅出Embedding:原理解析与应用实战[M]. 机械工业出版社, 2021.

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

C语言---初始C语言

1、初始C语言 1、编译器主要有&#xff1a;Clang、GCC、WIN-TC、MSVC、Turbo C等 什么是编译&#xff1f; test.c----------------------------->test.exe 这个过程需要经过编译、链接等过程&#xff0c;而众多编译器实现的功能就是把我们写的test.c进行编译。 2、VS20…

利用栈和队列共同解决迷宫问题

文章目录 什么是迷宫问题&#xff1f;如何解决迷宫问题&#xff1f;DFS&#xff08;深度优先搜索&#xff09;BFS&#xff08;广度优先搜索&#xff09; 总结 什么是迷宫问题&#xff1f; 迷宫问题是一道经典的算法问题&#xff0c;旨在寻找一条从起点到终点的最短路径。通常迷…

桶排序 — 计数排序和基数排序

计数排序 int类型数组&#xff0c;其中存的是员工的年龄。比如说16 - 150。对于这样的数据来讲&#xff0c;数据状况是受限的。此时如果将数组从小到大进行排序&#xff0c;该如果实现&#xff1f; 这个实现很简单&#xff0c;实现一个统计数组范围从 0 ~ 150&#xff0c;新数组…

Flume的安装和使用

安装Flume 1.1访问Flume的官网&#xff08;http://flume.apache.org/download.html&#xff09;&#xff0c;下载Flume安装apache-flume-1.9.0-bin.tar.gz。或者下载我的百度网盘资源。把安装文件解压缩到windows操作“D:\”目录下&#xff0c;然后执行如下命令测试是否安装成…

【JavaSE】Java基础语法(十六):抽象类

文章目录 1. 抽象类的概述2. 抽象类的特点3. 抽象类的实用价值4. 抽象类的案例 1. 抽象类的概述 当我们在做子类共性功能抽取时&#xff0c;有些方法在父类中并没有具体的体现&#xff0c;这个时候就需要抽象类了&#xff01; 在Java中&#xff0c;一个没有方法体的方法应该定义…

在Linux设备上让程序在任意目录都能执行

目录 0. 前言1. 编写代码2. 创建软链接3. 其他Linux文章 0. 前言 在Ubuntu上使用espidf中往往需要先设置环境变量&#xff0c;再执行export.sh&#xff0c;对环境装的乱七八糟的我造成了很大的不便我希望无论我在哪个目录&#xff0c;都能快速执行某个命令 我先是使用了编写b…

微信小程序开发实战 ⑨(TabBar)

作者 : SYFStrive 博客首页 : HomePage &#x1f4dc;&#xff1a; 微信小程序 &#x1f4cc;&#xff1a;个人社区&#xff08;欢迎大佬们加入&#xff09; &#x1f449;&#xff1a;社区链接&#x1f517; &#x1f4cc;&#xff1a;觉得文章不错可以点点关注 &#x1f4…

【Unittest】自动化测试框架核心要素

1、什么是Unittest框架&#xff1f; python自带一种单元测试框架 2、为什么使用UnitTest框架&#xff1f; >批量执行用例 >提供丰富的断言知识 >可以生成报告 3、核心要素&#xff1a; 1). TestCase&#xff08;测试用例&#xff09; 2). TestSuite(测试套件)…

WalkRE--刷图流程(超具体)

1、打开WalkRE软件&#xff0c;界面如下&#xff1a; 2、选择“根据模板新建工程”。操作如下&#xff1a; 3、导入数据。需要准备好dxf格式的CAD地形数据。操作如下&#xff1a; 在空白处右键&#xff0c;先关闭所有层&#xff08;大部分层在刷图时用不上&#xff0c;仅打开刷…

如何监控电动车充电桩能耗?

一 背景 随着新能源汽车的快速发展&#xff0c;像特斯拉、BYD、蔚来、小鹏和理想等品牌的电动汽车在我们的日常生活中越来越多了&#xff0c;可见电动汽车如今已逐渐被我们所认可了。同汽油车需要加油一样&#xff0c;电动汽车需要充电&#xff0c;如此一来&#xff0c;电动汽…

C++进阶 —— multimap

目录 一&#xff0c;multimap介绍 类pair 函数模板make_pair 二&#xff0c;multimap使用 一&#xff0c;multimap介绍 multimap是关联式容器&#xff0c;按照特定顺序存储键值对<key、value>&#xff0c;其中多个键值对之间的key可以重复&#xff1b;键key通常用于排…

供应链 | 在线平台的研究与思考(一):销售渠道与模式选择

封面图来源&#xff1a; https://www.pexels.com/zh-cn/photo/4968391/ 编者按 当前&#xff0c;电商平台主要采用两种销售模式&#xff1a;代理和分销。商家根据自身情况选择线上或线下渠道&#xff0c;而电商平台会根据不同的线上商家选择适当的分销模式。本期编者精选的两…

C++环形缓冲区设计与实现:从原理到应用的全方位解析

C环形缓冲区设计与实现&#xff1a;从原理到应用的全方位解析 一、环形缓冲区基础理论解析&#xff08;Basic Theory of Circular Buffer&#xff09;1.1 环形缓冲区的定义与作用&#xff08;Definition and Function of Circular Buffer&#xff09;1.2 环形缓冲区的基本原理&…

Smartbi“三步走”构建智慧经营分析平台,实现国有企业监管报送和数智化转型

01. 现状与痛点 — 一直以来&#xff0c;国资国企都是促进我国经济高速发展的领头羊&#xff0c;但近年来受疫情冲击和国际经济下行影响&#xff0c;国资企业经营面临较大压力&#xff0c;同时为实现国有企业高质量发展&#xff0c;国务院国资委下发一系列政策要求&#xff…

linuxOPS基础_vmware虚拟机安装及介绍

虚拟机概念 什么是虚拟机&#xff1f; 虚拟机&#xff0c;有些时候想模拟出一个真实的电脑环境&#xff0c;碍于使用真机安装代价太大&#xff0c;因此而诞生的一款可以模拟操作系统运行的软件。 虚拟机目前有2 个比较有名的产品&#xff1a;vmware 出品的vmware workstatio…

chatgpt赋能python:Python如何创建一个DataFrame

Python如何创建一个DataFrame 在数据科学和分析领域中&#xff0c;DataFrame是一种非常常见的数据结构。它类似于电子表格&#xff0c;可以存储和处理包含多个列和行的数据。在Python中&#xff0c;pandas库提供了DataFrame数据结构的支持。 什么是DataFrame&#xff1f; Da…

C++ list类成员函数介绍

目录 &#x1f914;list模板介绍&#xff1a; &#x1f914;特点&#xff1a; &#x1f914;list内存结构图解&#xff1a; &#x1f914; list的成员函数&#xff1a; &#x1f60a;list构造函数&#xff1a; &#x1f50d;代码示例&#xff1a; &#x1f50d;运行结果&…

RobotFramework接口测试方案

1. Robot FrameWork介绍 1.1 介绍 Robot Framework是用于验收测试和回归测试的通用测试自动化框架。它使用易于理解的表格数据语法&#xff0c;非常友好的实现了关键字驱动和数据驱动模式。它的测试功能可以通过使用Python或Java实现的测试库进行扩展&#xff0c;用户可以使用…

【JavaSE】Java基础语法(十七)

文章目录 1. final2. 代码块2.1 代码块概述2.2 代码块分类 1. final fianl关键字的作用 final代表最终的意思&#xff0c;可以修饰成员方法&#xff0c;成员变量&#xff0c;类 final修饰类、方法、变量的效果 fianl修饰类&#xff1a;该类不能被继承&#xff08;不能有子类&a…

软件测试基础概念

目录 软件测试的生命周期如何描述一个bug如何定义bug的级别bug的生命周期产生争执怎么办&#xff08;处理人际关系&#xff09;如何开始第一次测试测试的执行和bug管理如何发现更多的bug 软件测试的生命周期 需求分析 – 测试计划 – 测试设计、测试开发 – 测试执行 – 测试评…
最新文章