【Python机器学习】NLP分词——利用分词器构建词汇表(一)

news2024/11/14 11:05:03

在NLP中,分词(也称切词)是一种特殊的文档切分过程。而文档切分能够将文本切分成更小的文本块或片段,其中含有更集中的信息内容。文档切分可以是将文本分成段落,将段落分成句子,将句子分成短语,或将短语分成词条(通常是词)和标点符号。将文本分割成词条的过程称为分词

用于编译计算机语言的分词器通常称为扫描器或者语法分词器。某种计算机语言的词汇表(所有有效的记号合)构成所谓的词库。如果分词器合并到计算机语言编译器的分析器中,则该分析器常常称为无扫描器分析器。而记号(token)则是用于分析计算机语言的上下文无关语法(CFG)的最终输出结果,由于它们终结了CFG中从根结点到叶子结点的一条路径,因此它们也称为终结符

对于NLP的基础构建模块,计算机语言编辑器中存在一些与它们等同的模块:

1、分词器:扫描器,或者称为语法分析器;

2、词汇表:词库;

3、分析器:编译器;

4、词条词项n-gram:标识符或终结符。

分词是NLP流水线的第一步,因此它对流水线的后续处理过程具有重要的影响。分词器将自然语言文本这种非结构化数据切分成多个信息块,每个块都可看成可计数的离散元素。这些元素在文档中的出现频率可以直接用于该文档的向量表示。上述过程立即将非结构化字符串(文本文档)转换成适合机器学习的数值型数据结构。元素的出现频率可以直接被计算机用于触发有用的行动或回复。或者,它们也可以以特征方式用于某个机器学习流水线来触发更复杂的决策或行为。通过这种方式构建的词袋向量最常应用于文档检索或搜索任务重。

对句子切分的最简单方法就是利用字符串汇总的空白符来作为词的“边界”。在Python中,可以通过标准库方法split来实现这种操作,split在所有的str对象实例中都可调用,也可以在str内嵌类本身进行调用,示例如下:

sentence="""
Thomas Jefferson Began buliding Monticelli as the age of 26.
"""
print(sentence.split())
print(str.split(sentence))

如上图所示,Python的内置方法已经对这个简单的句子进行了相当不错的分词处理,仅仅出现了“26.”这样带标点符号的问题。通常来说,我们会希望句中有意义的词条和标点符号等分开。“26.”、“26!”、“26,”等意义都不同。一个优秀的分词器应该将额外的字符去掉得到“26”。此外,一个更为精准的分词器应该也将句尾的标点符号作为词条输出,这样句子切分工具或者边界检测工具才能确定句子的结束位置。

现在,我们先利用当前这个并不完美的分词器来进行分词处理,后面再处理标点符号和其他有挑战性的问题。再利用一点Python技术,我们就能构建每个词的数值向量。这些向量称为独热向量。这些独热向量构成的序列能够一向量序列(数字构成的表格)的方式完美捕捉原始文本。上述处理过程解决了NLP的第一个问题,即将词转化成数字:

import numpy as np
token_sequence=str.split(sentence)
#词汇表中列举了所有想要记录的独立词条
vocab=sorted(set(token_sequence))
#词条按照词库顺序进行排序,数字排在字母前面,大写字母排在小写字母的前面
print(','.join(vocab))
num_tokens=len(token_sequence)
vocab_size=len(vocab)
#这张空表的宽度是词汇表中独立词项昂的个数,长度是文档的长度,这里就是10行10列的表
onehot_vectors=np.zeros((num_tokens,vocab_size),int)
#对于集中的每个词,将词汇表中与该词对应的列标记为1
for i,word in enumerate(token_sequence):
    onehot_vectors[i,vocab.index(word)]=1
print(' '.join(vocab))
print(onehot_vectors)

对于上面的数据,如果用Pandas DataFrame可以使其看起来更容易一些,信息量也更多一些。Pandas会利用Series对象中的辅助功能对一维数组打包。此外,Pandas对于表示数值型表格特别方便,如列表组成的列表(list)、二维numpy数组,二维numpy矩阵、数组组成的数组以及字典组成的字典。

DataFrame为每一列记录了其对应的标签,这样就可以将每一列对应的词条或词标在上面。为了加快查找过程,DataFrame也可以利用DataFrame.index为每一行记录其对应的标签。当然,对于大部分应用来说,行的标签只是连续的整数。在刚刚这里例子上,我们目前暂时只使用默认的行标签整数:

import pandas as pd
ddd=pd.DataFrame(onehot_vectors,columns=vocab)
print(ddd)

独热向量看起来十分稀疏,每个行向量中只有一个非零值。因此,我们可以把所有的0替换成空,这样可以使独热行向量表格看上去更美观一些。但是,不要在机器学习流水线中的DataFrame上进行这样的操作,因为这样做的话会在numpy数组中构建大量非数值型对象,从而导致数学计算上的混乱。

当然,如果只是为了显示美观,可以做类似下面的操作:

df=pd.DataFrame(onehot_vectors,columns=vocab)
df[df == 0]=''
print(df)

在上述这个单句子文档的表示中,每行的向量都对应一个单独的词。该句子包含10个相互不同的词,没有任何重复。于是,上述表格包含10列10行。每列中的数字“1”表示词汇表中的词出现在当前文档的当前位置。因此,如果想知道文档中的第3个词是什么,就可以定位表格的第3行(标号为2),从这行中找到数字“1”对应的列,就可以知道是哪个单词。

上述表格的每一行都是一个二值的行向量,这就是该向量成为独热向量的原因:这一行的元素除1个位置之外都是0或空白,而只有该位置上是“热”的(为1)。“1”意味着“打开”或者“热”。而0意味着“关闭”或者“缺失”。

上面的词向量表示及文档的表格化表示有一个优点,就是任何信息都没有丢失。只要记录了哪一列代表哪个词,就可以基于整张表格中的独热向量重构出原始文档。即使分词器在生成我们认为有用的词条时只有90%的精确率,上述重构过程的精确率也是100%。因此,和上面一样的独热向量尝尝用于神经网络、序列到序列语言模型及生成式语言模型中。对任何需要保留原始文本所有含义的模型或NLP流水线来说,独热向量模式提供了一个好的选择。

上述独热向量表格就像是对原始文本进行了完全录制。表格上面的词汇表告诉机器的是,每个行序列到底对应哪个词,就像是钢琴棋谱中应该演奏哪个音符。

我们已经将一个自然语言的句子转换成了数值序列,即向量。现在我们可以利用计算机读入这些向量并进行一系列数字运算,就像对其他向量或者数值列表进行的运算一样。这样就可以将向量输入任何需要这类向量的自然语言处理流水线中。

如果想要为聊天机器人生成文本,可以基于独热编码向量反向还原出文本内容。现在所有需要做的事情就是,规划如何构建一个能够以新方式理解并组合这些词向量的演奏钢琴。最后,我们期望聊天机器人或者NLP流水线能够演奏或者说出某些以前我们没听说过的东西。

上述基于独热向量的句子表示方法保留了原始句子的所有细节,包括语法和语序。至此,我们已经成功的将词转换为计算机能够“理解”的数值,并且这些数值还是计算机非常喜欢的一类数值:二值数字0或1。但是,相对于上述的短句子而言的整个表格却很大。如果考虑到这一点,我们可能已经对文件的大小进行了扩充以便能够存储上述表格。但是,对长文档来说,这种做法不太现实,此时文档的大小会急剧增加。英语中包含至少20000个常用词,如果考虑人名和其他专用词的话,数量可能达到数百万。对于要处理的每篇文档,其独热表示方法都需要一个新的表格(矩阵)。这基本是相当于得到了文档的原始映像。

下面简单地用数学演算一下,以便大概了解了表格会有多大。大部分情况下,NLP流水线中使用的词汇表中的词条数将远远超过10000或20000,有时可能会达到数十甚至上百万。假设我们的NLP流水线的词汇表包含100万个词条,并且我们拥有3000本很薄的书,每本书有3500个句子,每个句子平均15个词,那么,整个表格(矩阵)的大小:

#表格行数:
num_rows=3000*3500*15
print(num_rows)
#如果表格中每个元素只用一个字节表示的话,那么总字节数:
num_bytes=num_rows*1000000
print(num_bytes)
#表格的大小:
num_bytes=num_bytes/1e12
print(num_bytes)

即使将表格中的每个元素用单个位来表示,也需要接近20TB的空间来存储这些书籍。幸运的是,我们从来都不需要用上面的数据结构来存储文档。只有在一个词一个词处理文档时,才会临时在内存中使用上述数据结构。

因此,存储所有0并试图记住所有文档中的词序没有太大意义,也不太现实。我们真正想要做的实际是将文档的语义压缩为其本质内容。我们想将文档压缩成单个向量而不是一张大表。而且我们将放弃完美的“召回”过程,我们想做的是提取文档中的大部分而非全部含义(信息)。

假设我们可以忽略词的顺序和语法,并将它们混合在一个“袋子”中,每个句子或每篇短文对应一个“袋子”,这个假设是合理的。即使对于长达几页的文档,词袋向量也可以用来概括文档的本质内容。对于前面那句关于Jefferson的句子,即使把所有的词都按词库序重新排列,人们也可以猜出那句话的大致意义。机器也可以实现这一点。我们可以使用这种新的词袋向量方法,将每篇文档的信息内容压缩到更易处理的数据结构中。

如果把所有这些独热向量加在一起,而不是一次一个地“回放”它们,我们会得到一个词袋向量。这个向量也被成为词频向量,因为它只计算了词的频率,而不是词的顺序。这个具备合理长度的单一向量可以用来表示整篇文档或整个句子,其长度只相当于词汇表的大小。

另一种做法是,如果正在进行基本的关键词搜索,可以对这些独热词向量进行OR处理,从而得到一个二值的词袋向量。在搜索中可以忽略很多次,这些词并不适合作为搜索词或关键词。这对搜索引擎索引或信息检索系统的第一个过滤器来说都很不错。搜索引擎只需要知道每篇文档中每个词的存在与否,以帮助我们后续找到这些文档。

如果将词条限制在10000个最重要的词以内,就可以将刚才虚构的包含3500个句子的书的数值表示压缩到10kb,也就是说上述虚构的3000本书构成的语料库大约会压缩到30MB左右,独热向量构成的序列仍然需要占用数百GB的空间。

幸运的是,对于任何给定的文本,词汇表中的词只有很少一部分会出现在这个文本中,而对大多数词袋应用来说,往往会保持文档的简洁性,有时候一个句子就够了。即使同一个语句中有很多通常不会一起使用的词而产生不和谐,甚至这种不和谐也包含很多与语句相关的有用信息,机器学习流水线也可以利用这些信息。

这就是如何将词条放入一个二值向量的过程,这个向量可以表示某个具体词在某个特定句子中是否存在。一系列句子的上述向量表示可以“索引”起来,从而记录哪个词出现在哪篇文档中。这个索引除了不记录词出现在哪个页面,这里可以保存句子(或相关向量)的出现位置。

下面就是一篇单文本文档,文档中只有一个关于Thomas Jefferdon的句子,看上去像一个二值的词袋向量:

sentence_bow={}
for token in sentence.split():
    sentence_bow[token]=1
print(sorted(sentence_bow.items()))

可以看到,sorted()将十进制数放在字符之前,同时将大写的词放在小写的词之前。这是ASCII和Unicode字符集中的字符顺序。在ASCII表中,大写字母在小写字母之前。其实,词汇表的顺序并不重要,只要所有需要分词的文档都采用相同的方式,机器学习流水线就可以很好地处理任何词汇表顺序。

还有,使用dict存储二值向量不会浪费太多空间。使用字典来表示向量可以确保只需要存储为数不多的1,因为字典中的数千甚至数百万个词中只要极小一部分会出现在具体某篇文档中,我们可以看到,上述表示会比将一袋子词表示为连续的0和1 的列表要高效的多,后者用一个“密集”向量为词汇表中的每个词都指定了一个位置。即使对于上面这个有关“Thomas Jefferdon”的短句子,采用“密集”的二值向量也需要100kb的存储空间。因为字典会“忽略”不存在的词,所以用字典表示时也只需要对10个词的句子中的每个词用几字节来表示,而如果把每个词都表示乘指向词库内该词所在位置的整数指针,那么这个字典的效率可能会更高。

接下来,我们使用一种更有效的字典形式,即Pandas中的Series,可以把它封装在Pandas的DataFrame中,这样就可以向关于“Thomas Jefferdon”的二值向量文本“语料库”中添加更多的句子。当在DataFrame中添加更多的句子和其对应的词袋向量时,所有这些向量之间以及稀疏与密集词袋之间的差距就会变得清晰起来:

df=pd.DataFrame(pd.Series(dict([(token,1) for token in sentence.split()])),columns=['sent']).T
print(df)

下面向语料库中添加一些文本,观察DataFrame是如何堆叠起来的:

sentence=sentence+"""\n Construction was done mostly by local masons and carpenters.\n"""
sentence=sentence+"""He moved into the South Pavilion in 1770.\n"""
sentence=sentence+"""Turning Monticello into a neoclassical masterpiece was Jefferson's obsession."""
corpus={}
#一般来说,只需要使用.splitlines()即可,但是这里显式地在每个行尾增加了 \n  字符,因此这里要显式地对此字符串进行分割
for i,sent in enumerate(sentence.split('\n')):
    corpus['sent{}'.format(i)]=dict((tok,1) for tok in sent.split())
df=pd.DataFrame.from_records(corpus).fillna(0).astype(int).T
print(df[df.columns[:10]])

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

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

相关文章

C语言学习——文件

目录 十三、文件 13.1C文件概述 13.2文件类型指针 13.3文件的打开与关闭 文件的打开(fopen函数) 文件的关闭(fclose函数) 13.4文件的读写 fputc函数和fgetc函数(putc函数和getc函数) fread函数和fw…

在亚马逊云科技上通过LangChain ReAct Agent开发金融多模态数据AI分析中台

项目简介: 小李哥将继续每天介绍一个基于亚马逊云科技AWS云计算平台的全球前沿AI技术解决方案,帮助大家快速了解国际上最热门的云计算平台亚马逊云科技AWS AI最佳实践,并应用到自己的日常工作里。 本次介绍的是如何在亚马逊云科技机器学习托…

简易版营业厅宽带系统

TOC ssm018简易版营业厅宽带系统jsp 绪论 1.1 研究背景 当前社会各行业领域竞争压力非常大,随着当前时代的信息化,科学化发展,让社会各行业领域都争相使用新的信息技术,对行业内的各种相关数据进行科学化,规范化管…

音频Transformer架构

第3单元:音频Transformer架构 本课程中,我们主要关注Transformer模型以及它们如何应用于音频任务。虽然您不需要了解这些模型的内部细节,但了解使它们工作的主要概念很有用,因此我们在本小节中回顾一下关于Transformer的知识。有关transformer的深入了解,请查看我们的NLP…

互联网的发展是否加剧了数字鸿沟?

有人问:互联网的发展是否加剧了数字鸿沟。 互联网的发展确实在某种程度上加剧了数字鸿沟。虽然互联网的普及为全球范围内的人们提供了前所未有的访问信息、教育资源和经济机会的机会,但其发展也凸显并放大了不同群体之间的差距,比如以下几个…

dokcer 安装 redis(单机版)

准备工作 拉取redis镜像 docker pull redis 通过docker-compose 安装redis 很方便、很简单 先安装docker,参考我这个安装示例进行安装 https://blog.csdn.net/qq_33192671/article/details/13714973 然后安装docker-compose,要是拉取docker-compose无…

【在Linux世界中追寻伟大的One Piece】IO基础

目录 1 -> 回顾 1.1 -> 回顾C文件接口 1.2 -> 总结 2 -> 系统文件I/O 3 -> 接口介绍 3.1 -> open 3.2 -> open函数返回值 3.3 -> 文件描述符fd 4 -> 0 & 1 & 2 5 -> 文件描述符的分配规则 6 -> 重定向 7 -> 使用dup2系…

跨链互通:Web3如何实现多链互操作性

随着区块链技术的发展,各类区块链网络不断涌现,然而,不同链之间的互操作性问题成为了一个重要挑战。跨链互通(Cross-chain Interoperability)技术正是为了解决这一问题,旨在打破各区块链网络间的壁垒&#…

恒创科技:如何管理和减少Windows服务器 CPU 负载?

CPU 负载是衡量网络服务器或计算机中央处理器 (CPU) 在任意给定时间内处理工作量的指标。它通常表示 CPU 正在执行或排队等待处理的进程数。 如何读取和管理CPU负载: 对于 Windows 系统 Windows 本身不支持“top”和“ps”命令,而类 Unix 系统则支持。不…

Xinstall助力App运营,邀请码自动识别,效率翻倍!

在App推广和运营的道路上,邀请码一直是一个让人又爱又恨的存在。它能够帮助我们追踪用户来源,衡量推广效果,但同时,繁琐的填写步骤也让许多潜在用户望而却步。然而,随着Xinstall的出现,这一切都将迎来颠覆性…

Promise学习之同步与异步

目录 前言 一、同步与异步 (一) 同步 (二) 异步 二、总结 (一) 同步 (二) 异步 前言 Java有多线程,前端有同步与异步,异步操作可以优化用户体验、提高性能与响应、处理并发与并行任务等等,异步操作有发送Ajax请求、读文件等&#xff0…

简明的Arthas故障排查实践

写在文章开头 Arthas是一款强大的开源Java诊断程序,它可以非常方便的启动并以界面式的方式和Java程序进行交互,支持监控程序的内存使用情况、线程信息、gc情况、甚至可以反编译并修改现上代码等。所以它成为笔者进行线上问题排查的重要手段,而本文将从实际使用的角度介绍一下…

我带着我的未来回来了!

🔝🔝🔝🔝🔝🔝🔝🔝🔝🔝🔝🔝🔝🔝🔝 🥇博主昵称:小菜元 🍟博客主页…

第九周:机器学习笔记

第九周机器学习周报 摘要Abstract机器学习——Spatial Transformer1.1 How to transform an image/feature map?(怎么做)1.2 Interpolation(插值)1.3 spatial Transformer的应用 Pytorch学习1. 线性层2. 其他层的介绍3. 搭建小实…

Leetcode 237.19.83.82 删除链表重复结点 C++实现

Leetcode 237. 删除链表中的节点 问题:有一个单链表的head,我们想删除它其中的一个节点node。给你一个需要删除的节点 node 。你将 无法访问 第一个节点head。链表的所有值都是唯一的,并且保证给定的节点 node不是链表中的最后一个节点。删除…

buuctf [ACTF新生赛2020]usualCrypt

前言:学习笔记。 常规: 下载 解压 查壳。 32位IDA pro打开 先查找字符串 在进入main() 分析: 关键函数: 第一部分: 大写转小写 小写转大写。 已知: 密文,以及加密过程由三部分组成。 那么逆向…

一款电容型、非接触式感知的智能水浸模组-WS11

水侵模组 - WS11(Water Sensor-MC11S)是一款电容型、非接触式感知的智能水浸模组,集成了高集成度差分式数字电容芯片MC11S。模组内嵌MCU,通过UART输出电容和检测状态信息,进行算法分析,有效滤除振动、凝露等…

Android自定义一个带背景的圆环形进度条(Kotlin)

前言 在Android开发过程中,难免遇到一些复杂的UI组件需要我们自定义 当然使用系统原生组件拼凑也能完成,但是UI复杂度增加了不说,在更新UI状态的时候还不好管理,最重要的是复用的价值不大,上述的操作很容易引增加码冗…

温湿度传感器---DHT11

温湿度传感器---DHT11 一、DHT11介绍二、DHT11原理图三、DHT11工作原理和时序3.1 DHT11工作时序 四、DHT11代码 一、DHT11介绍 DHT11数字温湿度传感器是一款含有已校准数字信号输出的温湿度复合传感器,传感器内部包括一个8位单片机控制一个电阻式感湿元件和一个NTC…

如何用Java SpringBoot+Vue打造“花开富贵”花园管理系统【实战教程】

✍✍计算机编程指导师 ⭐⭐个人介绍:自己非常喜欢研究技术问题!专业做Java、Python、微信小程序、安卓、大数据、爬虫、Golang、大屏等实战项目。 ⛽⛽实战项目:有源码或者技术上的问题欢迎在评论区一起讨论交流! ⚡⚡ Java实战 |…