自然语言处理基础任务(FMMBPE原理以及代码)
创始人
2025-05-31 01:59:36
0

中文分词

背景

词语的概念:词语(word)是最小独立使用的音义结合体(即为任务中的原子单词),能够独立表达语言和内容的最基本单元。

对于英文等体系的印-欧体系,一般会有空格作为间隔,但是对于其他体系(汉-藏体系,闪-含体系)等没有明显的词语分隔符,为了更好完成分词任务,方便后续任务展开,我们一般采用分词算法。平时自然语言处理使用比较多的中文分词是调用第三方库jieba。但是在下文中用正向最大匹配分词算(Forward Maximum Matching FMM)。

FMM代码

def fmm_word_seg(sentence, lexicon, max_len):'''sentence: 待分词的句子lexicon:词典(所有单词的集合),一般为txt文件,每一行存储一个词典max_len:词典中最长单词的长度'''begin = 0end = min(begin + max_len, len(sentence))words = []while begin < end: # 这里的话使用了贪心算法word = sentence[begin:end]if word in lexicon or end - begin == 1:# 考虑两组情况,一组为end==begin,即为只有字母的差距不需要分词(即为到临界条件),另外为在词典中直接开始分词words.append(word)begin = end # 更新begin为end,即为算后面的句子,前面在词典的已经被划分了,使用更新beginend = min(begin + max_len, len(sentence)) # 更新新的end,即为最后句子的结尾要么是sentence的长度,要么在sentence之前else:end -= 1return words
def load_dict():f = open('lexicon.txt') # 词典文件,每一行存储一个单词lexicon = set()max_len = 0for line in f:word = line.strip() # 一般为跳过空格lexicon.add(word)if len(word) > max_len:max_len = len(word)f.close()return lexicon, max_len
lexicon,max_len = load_dict()
words = fmm_word_seg(input("输入的句子:",lexicon, max_len))
for word in words:print(word,)

FMM缺点

  • 更倾向于切分较长的词语(通过begin和end的遍历变化可知)

  • 容易造成切分歧义的问题

切分歧义:同一个句子有不一样的切分结果,例子:研究生命的起源可以且分为['研究生',‘命’,‘的’,‘起源’]或者是[‘研究’,‘生命’,‘的’,‘起源’]
  • 对中文的定义不明确,即为可能出现多对一的情况。(因为中文情况比较难以理解,不同词语可能代表不一样的意思)

例子:‘哈尔滨市’=‘哈尔滨’,但是具体切分有不一样的结果,这个问题可以在后续的中文分词的规范进行调整
  • 有一些词语未出现在lexicon(词典)中,就有可能造成未登录词的问题。

子词切分

一般情况下,有分隔符的词语就不需要在进行额外的分词,但是这些语言里面的单词可能有不一样的词义变化,容易造成数据矩阵稀疏,词表过大等问题,这种情况下运用简单的分词方法无法处理,所以我们一般会采取词形还原,词干提取,提取出单词的词根等特征,减少数据稀疏。

词形还原:将单词变成一个原型的单词,比如computer变成compute
词干处理有可能就是将词语的前缀,后缀等去除比如computing 变成compute

因为子词切分任务需要人为制定规则进行处理,robust(稳健性和适应性)较差,所以我们更倾向于将子词切分任务变成基于统计的无监督子词切分方法(Subword)。而且在预训练语言模型中被广泛使用。

子词切分定义:将一个单词切分为若干连续的片段

子词切分原理:使用尽量长,频次高的子词对单词进行切分

下面介绍比较多的是字节对编码(Byte Pair Encoding BPE)算法。

BPE算法(提取词干方法)

构造长度为L的子词表

主要是通过以下方式构建子词表,流程框架图如图所示。

具体步骤为将每个字符作为一个单词,划分之后计算相对应的频次,然后进行合并前面系数最多的单词(类似于哈夫曼树)成为一个新的单词。(每个单词后面的\w代表一个单词的分解符号)

原先的句子

初始化的词表(做一个去重的操作,长度为11)

之后进行合并出现次数最高的e和s,去除s,增加一个es(因为s在语料库已经不存在了,s被合并为st,所以删除的是不存在的子词s),然后重新统计子词出现的长度(此时词表的长度为11)

不断循环,直至词表长度为所想要的L为止。

将单词切分为子词序列

我们可以先将单词按照子词长度从大到小排序,然后前项遍历子词此表,如果是的话就切分,如果子词全部遍历结束,单词中仍有子串未被切分,我们认为这个单词为低频串,用进行标记。

例子如下,在子词表中的部分是我们不需要的部分,即为舍弃的部分,这里运用的是提取词干的方法。

句子:['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代码

参考:https://zhuanlan.zhihu.com/p/448147465

BPE函数代码

import re, collectionsdef get_vocab(filename): # 这里的话是针对于文件中的单词进行划分加上统计,最终整理出来的是一个defaultdict类型的字典形式vocab = collections.defaultdict(int)'''vocal的形式为['l o w e r ']:int'''with open(filename, 'r', encoding='utf-8') as fhand:for line in fhand: # 每一行为n个单词,对每个行的单词出现频次进行统计,比如low出现了几次words = line.strip().split() # 将每个单词进行切分for word in words: # 这里的words为一个列表,大概为['l,o,w,e,r],每一个wordvocab[' '.join(list(word)) + ' '] += 1 return vocabdef get_stats(vocab):pairs = collections.defaultdict(int)for word, freq in vocab.items(): # 对'l o w e r ':5的vocab进行处理,word为'l o w e r',symbols = word.split() # symbol为[l, o, w, e, r]# print("word:",word,'\n',"symbol:",symbols)for i in range(len(symbols)-1):# (symbols)-1)的原因是因为需要统计相邻出现的次数# print(pairs[symbols[i],symbols[i+1]])# print(pairs)'''pairs在这里为为一下的形式,基本上回算相邻的字母之间的最高频数defaultdict(, {})defaultdict(, {('l', 'o'): 5})defaultdict(, {('l', 'o'): 5, ('o', 'w'): 5})defaultdict(, {('l', 'o'): 5, ('o', 'w'): 5, ('w', ''): 5})'''pairs[symbols[i],symbols[i+1]] += freq # 统计相邻子词对的出现频次return pairs def merge_vocab(pair, v_in):v_out = {}# 这里的话会将最高频次进行合并bigram = re.escape(' '.join(pair))# 匹配到最高的那个字符,大量去除pair里面的字符,这里将他封装成一string类型的变量,这里为合并的子词p = re.compile(r'(?

BPE代码调用

vocab = {'l o w ': 5, 'l o w e r ': 2, 'n e w e s t ': 6, 'w i d e s t ': 3}
print('==========')
print('Tokens Before BPE')
tokens = get_tokens(vocab)
print('Tokens: {}'.format(tokens))
print('Number of tokens: {}'.format(len(tokens)))
print('==========')num_merges = 5
for i in range(num_merges):pairs = get_stats(vocab)if not pairs:breakbest = max(pairs, key=pairs.get)vocab = merge_vocab(best, vocab)print('Iter: {}'.format(i))print('Best pair: {}'.format(best))print('vocab:',vocab) # vocab返回的是一个词典的工作# 此时的vocab的大概格式为{'l o w ': 5, 'l o w e r ': 2, 'n e w es t ': 6, 'w i d es t ': 3}# vocab: {'low ': 5, 'low e r ': 2, 'n e w est': 6, 'w i d est': 3}tokens = get_tokens(vocab)# print('token:',tokens)# tokens返回的是是一个defaultdict词典,而且value只能是int类型两者不一致# tokens的格式为token: defaultdict(, {'l': 7, 'o': 7, 'w': 16, '': 16, 'e': 8, 'r': 2, 'n': 6, 'es': 9, 't': 9, 'i': 3, 'd': 3})# token: defaultdict(, {'low': 7, '': 7, 'e': 8, 'r': 2, 'n': 6, 'w': 9, 'est': 9, 'i': 3, 'd': 3})print('Tokens: {}'.format(tokens))print('Number of tokens: {}'.format(len(tokens)))

其他分词方法

WordPiece选择能够选定提升语言模型中概率最大的相邻子词进行合并,提升语言模型概率最大的相邻子词的互信息值。是的两个子词在模型中具有较强的关联性。(运用了似然估计等方法)

如果需要查看wordPiece的原理,可以查看博客https://zhuanlan.zhihu.com/p/191648421

ULM算法(Unigram Language Model)是减量法,最开始初始化一个打次表,然后根据一定的评估准则不断丢弃词表中的单词,直到满足词表的大小。

而且将句子看成是Unicode编码序列,从而能够处理更多的语言的词语,而且在Bert模型里面运用到的就是WordPiece算法,即为在合并词语的时候优先经过一定的估计从而进行选择点互信息最丰富的进行合并。

相关内容

热门资讯

蓝桥杯Web前端练习题----... 一、水果拼盘 介绍 目前 CSS3 中新增的 Flex 弹性布局已经成为前端页面布局的首选方案&#x...
小王子读后感 小王子读后感大全  当仔细品读一部作品后,你心中有什么感想呢?是时候写一篇读后感好好记录一下了。那么...
《三个火枪手》读后感 《三个火枪手》读后感(精选28篇)  当看完一本著作后,你心中有什么感想呢?为此需要认真地写一写读后...
《了不起的狐狸爸爸》读后感 《了不起的狐狸爸爸》读后感(精选22篇)  认真读完一本名著后,大家一定对生活有了新的感悟和看法,不...
城南旧事读后感400字 城南旧事读后感400字(精选20篇)  细细品味一本名著后,大家对人生或者事物一定产生了许多感想,记...
数学书籍读后感 数学书籍读后感(精选9篇)  读完一本名著以后,相信你心中会有不少感想,这时就有必须要写一篇读后感了...
机器学习实战-习题1.7 1. 如何定义机器学习? MyAnswer: 使用大量的数据训练一个算法...
《草房子》读后感600字 《草房子》读后感600字《草房子》读后感(小学生读书有感心得作文)《草房子》这本长篇小说的主人公是油...
狼世界读后感 狼世界读后感四篇  狼,一向给人的印象就是凶猛残暴的,然而在沈石溪的《狼世界》中,这种凶残的动物却令...
《曾参杀猪》读后感200字 《曾参杀猪》读后感200字《曾参杀猪》读后感200字今天没什么事我就看了看课外书。书的名字叫《108...
「VScode」通过VScod... 前言 之前在git的版本管理上,我使用的是sourcetree,说实话...
【SQL开发实战技巧】系列(二... 系列文章目录 【SQL开发实战技巧】系列(一):关于SQL不得不说的那些...
《GreenPlum系列-部署... GreenPlum单机节点快速安装教程 一、创建用户及用户组 [root@zxy_maste...
《海蒂》读后感 《海蒂》优秀读后感(精选13篇)  当品读完一部作品后,想必你有不少可以分享的东西,这时最关键的读后...
读骆驼祥子有感 读骆驼祥子有感(精选51篇)  细细品味一本名著后,相信大家都积累了属于自己的读书感悟,不妨坐下来好...
开学有感作文 开学有感作文450字(通用80篇)  在日常学习、工作和生活中,大家都不可避免地要接触到作文吧,作文...
《无常》读后感 《无常》读后感  当细细品完一本名著后,想必你有不少可以分享的东西,此时需要认真地做好记录,写写读后...
Django 之Logging 1. logging 1.1 什么是 logging logging 模块是 Python 内置的日...
Jetpack:DataBin... 文章目录DataBinding 的使用DataBinding 的布局绑定过程DataBinding ...
ThingsBoard开源物联... ThingsBoard部署教程文档 文章目录ThingsBoard部署教程文档1. JDK环境安装2...