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

在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.mzph.cn/pingmian/52474.shtml

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

相关文章

GUI编程03:3种布局管理器

本节内容视频链接:https://www.bilibili.com/video/BV1DJ411B75F?p5&vd_sourceb5775c3a4ea16a5306db9c7c1c1486b5https://www.bilibili.com/video/BV1DJ411B75F?p5&vd_sourceb5775c3a4ea16a5306db9c7c1c1486b5 1.FlowLayout 流式布局 代码:…

c++类的继承

1.直接继承父类的方法 #include <iostream> #include <string>using namespace std; class Person{ public:void eat(){cout<<"在吃饭"<<endl;} }; class Student : public Person{ private:int age; public:string name;Student() {cout &…

echarts倾斜横向堆叠柱状图

代码如下 option: {backgroundColor: "transparent",tooltip: {trigger: "axis",axisPointer: {lineStyle: {color: {type: "linear",x: 0,y: 0,x2: 0,y2: 1,colorStops: [{offset: 0,color: "rgba(126,199,255,0)", // 0% 处的颜色}…

J.U.C Review - 基本概念:进程、线程、线程组、优先级

文章目录 进程与线程的故事1.1 进程的诞生对操作系统的要求进一步提高为什么我们要使用多线程&#xff1f; 1.2 上下文切换的故事 Java多线程入门1. 继承Thread类代码示例 2. 实现Runnable接口代码示例 3. Thread类的构造方法和常用方法构造方法常用方法 4. Thread类与Runnable…

数据结构(邓俊辉)学习笔记】串 07——KMP算法:分摊分析

文章目录 1.失之粗糙2.精准估计 1.失之粗糙 以下&#xff0c;就来对 KMP 算法的性能做一分析。我们知道 KMP 算法的计算过程可以根据对齐位置相应的分为若干个阶段&#xff0c;然而每一个阶段所对应的计算量是有很大区别的。很快就会看到&#xff0c;如果只是简单地从最坏的角…

CocosCreator3.8 IOS 构建插屏无法去除的解决方案

CocosCreator3.8 IOS 构建插屏无法去除的解决方案 在实际项目开发过程中&#xff0c;我们通常无需CocosCreator 自带的插屏&#xff0c;一般采用自定义加载页面。 然后在构建IOS 项目时&#xff0c;启用&#xff08;禁用&#xff09;插屏无法操作&#xff0c;如下图所示&#…

运放阻抗和噪声(同相放大器的输入/输出阻抗 + 电压跟随器阻抗 + 噪声 +信噪比)

2024-8-27&#xff0c;星期一&#xff0c;21:03&#xff0c;天气&#xff1a;阴雨&#xff0c;心情&#xff1a;晴。培训终于结束啦&#xff0c;开始轮岗了&#xff0c;看了两天PPT&#xff0c;加油加油&#xff0c;继续学习。 今天继续学习第六章运算放大器&#xff0c;主要学…

第4章 汇编语言和汇编软件

第4章 汇编语言和汇编软件 该章主要介绍了汇编语言和汇编语言编译器的安装和使用。 汇编语言程序 该小节主要介绍了为什么要有汇编语言和汇编语言程序的一些基础写法。 书中有提到CPU有不同的架构&#xff0c;汇编语言有不同的风格&#xff0c;那么不同的CPU架构和不同的汇…

正则表达式——详解

正则表达式是什么&#xff1f; 正则表达式&#xff08;Regular Expression&#xff0c;通常简写为 regex、regexp 或 RE&#xff09;是一种强大的文本处理工具&#xff0c;用于描述一组字符串的模式。它可以用来匹配、查找、替换等操作&#xff0c;几乎所有现代编程语言都支持…

cola_os学习笔记(上)

cola_os的学习笔记 声明 该项目系本人学习项目所做的笔记。该项目的项目地址为cola_os: 300行代码实现多任务管理的OS&#xff0c;在很多MCU开发中&#xff0c;功能很简单&#xff0c;实时性要求不强&#xff0c;如果使用RTOS显得太浪费&#xff0c;任务多了管理不当又很乱&a…

Anaconda3简介与安装步骤

目录 Anaconda3简介与功能 1.Anaconda3简介 2.主要功能和特点 3.使用场景 4.总结 Anaconda3安装 1.Anaconda3下载 1.1我的百度网盘 1.2官网下载 1.2.1访问官网 1.2.2输入邮箱 1.2.3登录你的邮箱下载&#xff08;你的噶&#xff09; 2.安装 2.1双击安装 2.2选择安…

计算机视觉编程 3(图片处理)

目录 图像差分 高斯差分 形态学-物体计数 ​编辑 图片降噪 图像差分 # -*- coding: utf-8 -*- from PIL import Image from pylab import * from scipy.ndimage import filters import numpy# 添加中文字体支持 from matplotlib.font_manager import FontProperties font…

VMWare中添加Ubuntu20.04.06镜像

一、下载Ubuntu镜像 Ubuntu20.04&#xff1a; 官方下载地址https://releases.ubuntu.com/20.04.6/ 进入官网 点击下图红框位置&#xff0c;下载镜像镜像名为ubuntu-20.04.6-desktop-amd64.iso 也可点击下面链接直接下载&#xff1a;https://releases.ubuntu.com/20.04.6/ubu…

车间多台分散PLC如何在不同协议的情况下实现无线通讯?

项目背景 为推动企业智能化数字化升级&#xff0c;积极响应节能减排与能源可持续发展的号召&#xff0c;进一步增强企业竞争力&#xff0c;同时为避免大幅度电缆铺设及维护工作&#xff0c;厂区需要针对目前的燃煤发电作业进行技术及流程的无线改造。通过这些无线技改措施的实施…

2023年最新自适应主题懒人网址导航v3.9php源码

源码简介 这个懒人网址导航源码是一个基于PHPMySQL开发的网址导航系统。该版本是在原有3.8版本的基础上进行了修复和功能增强。我们建议新用户直接使用这个最新版本&#xff0c;放弃旧版本。如果你有二次开发的能力&#xff0c;可以根据更新日志自行进行升级。我们将在后期继续…

记录一次target引发的事故:一直提示数据库连接超时

你们好&#xff0c;我是金金金。 场景 启动项目&#xff0c;一直报数据库连接超时&#xff1a; The last packet sent successfully to the server was 0 milliseconds ago. The driver has not received any packets from the server. The error may exist in com/xuecheng/sy…

稚晖君智元机器人远程机器人系列发布:引领具身智能新高度

在最近的发布会上&#xff0c;前华为“天才少年”稚晖君及其团队亮相了他们的最新作品——智元机器人的第二代远程机器人系列。这次发布会不仅展示了丰富的产品线&#xff0c;还揭示了其未来的发展路线以及开源计划。本文将详细解析本次发布会的亮点和技术背后的创新。 一、发…

Kafka事件(消息、数据、日志)的存储

1、查看有关kafka日志配置文件的信息 2、查看kafka全部主题的日志文件 3、查看每个主题的日志文件 4、__consumer_offsets-xx文件夹的作用 package com.power;public class Test {public static void main(String[] args) {int partition Math.abs("myTopic".hashCo…

秋招TCP篇(较全的TCP网络知识,通俗理解底层逻辑)

TCP详细知识 计算机网络八股-局域网和广域网详解八股-OSI七层模型和TCP/IP五层模型八股-数据在网络之间传递的过程八股-UDP详解根据协议格式详解TCPSocket详解八股-TCP可靠性机制确认应答超时重传超时重传等待时间数据去重 八股-三次握手和四次挥手三次握手四次挥手为什么要三次…

中科服务器磁盘未断电状态被人拔插导致raid故障,安装系统找不到系统盘 修复raid再次安装系统成功

1&#xff0c;根据提示按del进入bios 直接回车 改成good状态保存&#xff08;多块盘的话重复此操作即可&#xff0c;直到让盘的状态显示good或者online&#xff09; 然后回到上级导入raid信息 raid信息导入 设置成yes&#xff0c;然后保存退出 然后他会自己同步数据&…