人工智能--自然语言处理简介

news/2024/11/8 12:27:21/文章来源:https://www.cnblogs.com/jellyai/p/18534832

上一篇:《人工智能模型训练中的数据之美——探索TFRecord》

序言:自然语言处理(NLP)是人工智能中的一种技术,专注于理解基于人类语言的内容。它包含了编程技术,用于创建可以理解语言、分类内容,甚至生成和创作人类语言的新作品的模型。在接下来的几章中,我们将会探讨这些技术。此外,现在有许多利用 NLP 的服务来创建应用程序,比如聊天机器人(它们属于应用,属于Agent应用开发),但这些内容不在知识的范围之内——我们将专注于 NLP 的基础知识(实现原理),以及如何进行语言建模,使您可以训练神经网络,教导电脑去理解和分类文本。

我们将从本节开始,先了解如何将语言分解成数字,以及这些数字如何用于神经网络,所谓‘分解’其实就给用一个数字代替语言句子中的字词或者词根,因为计算机只能处理数字;人们把语言转换成数字交由电脑处理后,再重新转回语言文字就可以被人类识别并知道电脑做了什么了。

将语言编码为数字

有多种方法可以将语言编码成数字。最常见的是通过字母进行编码,就像字符串在程序中存储时的自然形式一样。不过,在内存中,您存储的不是字母本身,而是它的编码——可能是 ASCII、Unicode 值,或者其他形式。例如,考虑单词“listen”。用 ASCII 编码的话,这个单词可以被表示为数字 76、73、83、84、69 和 78。这种编码方式的好处是,您现在可以用数字来表示这个单词。但如果考虑“silent”这个词,它是“listen”的一个字母异位词。尽管这两个单词的编码数字相同,但顺序不同,这可能会让建立一个理解文本的模型变得有些困难。

一个“反义词异构词”是指一个单词的字母顺序颠倒后形成的另一个单词,且二者具有相反的含义。例如,“united”和“untied”就是一对反义词异构词,另外还有“restful”和“fluster”,“Santa”和“Satan”,“forty-five”和“over fifty”。我之前的职位名称是“Developer Evangelist”,后来改成了“Developer Advocate”——这是个好事,因为“Evangelist”就是“Evil’s Agent”(邪恶代理人)的反义词异构词!

一种更好的替代方法可能是用数字来编码整个单词,而不是逐个字母编码。在这种情况下,“silent”可以用数字x表示,“listen”可以用数字y表示,它们彼此不会重叠。

使用这种技术,考虑一个句子比如“I love my dog.”您可以将它编码为数字 [1, 2, 3, 4]。如果您想要编码“I love my cat.”,可以是 [1, 2, 3, 5]。您已经可以看出这些句子在数值上相似——[1, 2, 3, 4] 看起来很像 [1, 2, 3, 5],因此可以推测它们的含义相似。

这个过程叫做“分词”,接下来您将探索如何在代码中实现它。

分词入门

TensorFlow Keras 包含一个称为“preprocessing”的库,它提供了许多非常实用的工具来为机器学习准备数据。其中之一是“Tokenizer”,它可以将单词转化为令牌。让我们通过一个简单的示例来看它的实际操作:

import tensorflow as tf

from tensorflow import keras

from tensorflow.keras.preprocessing.text import Tokenizer

sentences = [

'Today is a sunny day',

'Today is a rainy day'

]

tokenizer = Tokenizer(num_words=100)

tokenizer.fit_on_texts(sentences)

word_index = tokenizer.word_index

print(word_index)

在这个例子中,我们创建了一个 Tokenizer 对象,并指定了它可以分词的单词数量。这将是从词库中生成的最大令牌数。我们这里的词库非常小,只包含六个独特的单词,所以远小于所指定的一百个。

一旦我们有了一个分词器,调用 fit_on_texts 就会创建出令牌化的单词索引。打印出来会显示词库中的键/值对集合,类似于这样:

{'today': 1, 'is': 2, 'a': 3, 'day': 4, 'sunny': 5, 'rainy': 6}

这个分词器非常灵活。例如,如果我们将语料库扩展,添加另一个包含单词“today”且带有问号的句子,结果会显示它足够智能,可以将“today?”过滤成“today”:

sentences = [

'Today is a sunny day',

'Today is a rainy day',

'Is it sunny today?'

]

输出结果为:{'today': 1, 'is': 2, 'a': 3, 'sunny': 4, 'day': 5, 'rainy': 6, 'it': 7}

这种行为是由分词器的filters参数控制的,默认情况下会移除除撇号外的所有标点符号。因此,例如,“Today is a sunny day”将根据之前的编码变成一个包含 [1, 2, 3, 4, 5] 的序列,而“Is it sunny today?”将变成 [2, 7, 4, 1]。当您已将句子中的单词分词后,下一步就是将句子转换为数字列表,其中数字是单词在词典中的键值对所对应的值。

将句子转换为序列

现在您已经了解了如何将单词分词并转化为数字,接下来的一步是将句子编码为数字序列。分词器有一个名为text_to_sequences的方法,您只需传递句子的列表,它就会返回序列的列表。例如,如果您修改之前的代码如下:

sentences = [

'Today is a sunny day',

'Today is a rainy day',

'Is it sunny today?'

]

tokenizer = Tokenizer(num_words=100)

tokenizer.fit_on_texts(sentences)

word_index = tokenizer.word_index

sequences = tokenizer.texts_to_sequences(sentences)

print(sequences)

您将得到表示这三句话的序列。回想一下词汇索引是这样的:

{'today': 1, 'is': 2, 'a': 3, 'sunny': 4, 'day': 5, 'rainy': 6, 'it': 7}

输出结果将如下所示:

[[1, 2, 3, 4, 5], [1, 2, 3, 6, 5], [2, 7, 4, 1]]

然后,您可以将数字替换成单词,这样句子就会变得有意义了。

现在考虑一下,当您用一组数据训练神经网络时会发生什么。通常的模式是,您有一组用于训练的数据,但您知道它无法涵盖所有的需求,只能尽量覆盖多一些。在 NLP 的情况下,您的训练数据中可能包含成千上万个单词,出现在不同的上下文中,但您不可能在所有的上下文中涵盖所有可能的单词。所以,当您向神经网络展示一些新的、之前未见过的文本,包含未见过的单词时,会发生什么呢?您猜对了——它会感到困惑,因为它完全没有那些单词的上下文,结果它的预测就会出错。

使用“词汇表外”令牌

处理这些情况的一个工具是“词汇表外”(OOV)令牌。它可以帮助您的神经网络理解包含未见过的文本的数据上下文。例如,假设您有以下的小型语料库,希望处理这样的句子:

test_data = [

'Today is a snowy day',

'Will it be rainy tomorrow?'

]

请记住,您并没有将这些输入添加到已有的文本语料库中(可以视作您的训练数据),而是考虑预训练网络如何处理这些文本。如果您使用已有的词汇和分词器来分词这些句子,如下所示:

test_sequences = tokenizer.texts_to_sequences(test_data)

print(word_index)

print(test_sequences)

输出结果如下:

{'today': 1, 'is': 2, 'a': 3, 'sunny': 4, 'day': 5, 'rainy': 6, 'it': 7}

[[1, 2, 3, 5], [7, 6]]

那么新的句子,在将令牌换回单词后,变成了“today is a day”和“it rainy”。

正如您所见,几乎完全失去了上下文和意义。这里可以用“词汇表外”令牌来帮助,您可以在分词器中指定它。只需添加一个名为 oov_token 的参数,您可以将其设置为任意字符串,但确保它不会出现在您的语料库中:

tokenizer = Tokenizer(num_words=100, oov_token="")

tokenizer.fit_on_texts(sentences)

word_index = tokenizer.word_index

sequences = tokenizer.texts_to_sequences(sentences)

test_sequences = tokenizer.texts_to_sequences(test_data)

print(word_index)

print(test_sequences)

您会看到输出有了一些改进:

{'': 1, 'today': 2, 'is': 3, 'a': 4, 'sunny': 5, 'day': 6, 'rainy': 7, 'it': 8}

[[2, 3, 4, 1, 6], [1, 8, 1, 7, 1]]

您的令牌列表中多了一个新的项“”,并且您的测试句子保持了它们的长度。现在反向编码后得到的是“today is a day”和“ it rainy ”。

前者更加接近原始含义,而后者由于大部分单词不在语料库中,仍然缺乏上下文,但这算是朝正确方向迈出了一步。

理解填充(padding)

在训练神经网络时,通常需要所有数据的形状一致。回忆一下之前章节中提到的,训练图像时需要将图像格式化为相同的宽度和高度。在文本处理中也面临相似的问题——一旦您将单词分词并将句子转换为序列后,它们的长度可能会各不相同。为了使它们的大小和形状一致,可以使用填充(padding)。

为了探索填充,让我们在语料库中再添加一个更长的句子:

sentences = [

'Today is a sunny day',

'Today is a rainy day',

'Is it sunny today?',

'I really enjoyed walking in the snow today'

]

当您将它们转换为序列时,您会看到数字列表的长度不同:

[

[2, 3, 4, 5, 6],

[2, 3, 4, 7, 6],

[3, 8, 5, 2],

[9, 10, 11, 12, 13, 14, 15, 2]

]

(当您打印这些序列时,它们会显示在一行上,为了清晰起见,我在这里分成了多行。)

如果您想让这些序列的长度一致,可以使用 pad_sequences API。首先,您需要导入它:

from tensorflow.keras.preprocessing.sequence import pad_sequences

使用这个 API 非常简单。要将您的(未填充的)序列转换为填充后的集合,只需调用 pad_sequences,如下所示:

padded = pad_sequences(sequences)

print(padded)

您会得到一个格式整齐的序列集合。它们会在单独的行上,像这样:

[[ 0 0 0 2 3 4 5 6]

[ 0 0 0 2 3 4 7 6]

[ 0 0 0 0 3 8 5 2]

[ 9 10 11 12 13 14 15 2]]

这些序列被填充了 0,而 0 并不是我们单词列表中的令牌。如果您曾疑惑为什么令牌列表从 1 开始而不是 0,现在您知道原因了!

现在,您得到了一个形状一致的数组,可以用于训练。不过在此之前,让我们进一步探索这个 API,因为它提供了许多可以优化数据的选项。

首先,您可能注意到在较短的句子中,为了使它们与最长的句子形状一致,必要数量的 0 被添加到了开头。这被称为“前填充”,它是默认行为。您可以通过 padding 参数来更改它。例如,如果您希望序列在末尾填充 0,可以使用:

padded = pad_sequences(sequences, padding='post')

其输出如下:

[[ 2 3 4 5 6 0 0 0]

[ 2 3 4 7 6 0 0 0]

[ 3 8 5 2 0 0 0 0]

[ 9 10 11 12 13 14 15 2]]

现在您可以看到单词在填充序列的开头,而 0 位于末尾。

另一个默认行为是,所有句子都被填充到与最长句子相同的长度。这是一个合理的默认设置,因为这样您不会丢失任何数据。权衡之处在于您会得到大量填充。如果不想这样做,比如因为某个句子太长导致填充过多,您可以使用 maxlen 参数来指定所需的最大长度,如下所示:

padded = pad_sequences(sequences, padding='post', maxlen=6)

其输出如下:

[[ 2 3 4 5 6 0]

[ 2 3 4 7 6 0]

[ 3 8 5 2 0 0]

[11 12 13 14 15 2]]

现在您的填充序列长度一致,且填充量不多。不过,您会发现最长句子的一些单词被截断了,它们是从开头截断的。如果您不想丢失开头的单词,而是希望从句子末尾截断,可以通过 truncating 参数来覆盖默认行为,如下所示:

padded = pad_sequences(sequences, padding='post', maxlen=6, truncating='post')

结果显示最长的句子现在从末尾截断,而不是开头:

[[ 2 3 4 5 6 0]

[ 2 3 4 7 6 0]

[ 3 8 5 2 0 0]

[ 9 10 11 12 13 14]]

TensorFlow 支持使用“稀疏”(形状不同的)张量进行训练,这非常适合 NLP 的需求。使用它们比本书的内容稍微进阶一些,但在您完成接下来几章提供的 NLP 入门后,可以进一步查阅文档了解更多。

移除停用词和清理文本

在接下来的章节中,我们会看一些真实的文本数据集,并发现数据中经常有不想要的文本内容。你可能需要过滤掉一些所谓的“停用词”,这些词过于常见,不带任何实际意义,比如“the”,“and”和“but”。你也可能会遇到很多HTML标签,去除它们可以使文本更加干净。此外,其他需要过滤的内容还包括粗话、标点符号或人名。稍后我们会探索一个推文的数据集,其中经常包含用户的ID,我们也会想要去除这些内容。

虽然每个任务会因文本内容的不同而有所差异,但通常有三种主要的方法可以编程地清理文本。第一步是去除HTML标签。幸运的是,有一个名叫BeautifulSoup的库可以让这项任务变得简单。例如,如果你的句子包含HTML标签(比如
),以下代码可以将它们移除:

from bs4 import BeautifulSoup

soup = BeautifulSoup(sentence)

sentence = soup.get_text()

一种常见的去除停用词方法是创建一个停用词列表,然后预处理句子,移除其中的停用词。以下是一个简化的例子:

stopwords = ["a", "about", "above", ... "yours", "yourself", "yourselves"]

一个完整的停用词列表可以在本章的一些在线示例中找到。然后,当你遍历句子时,可以使用如下代码来移除句子中的停用词:

words = sentence.split()

filtered_sentence = ""

for word in words:

if word not in stopwords:

filtered_sentence = filtered_sentence + word + " "

sentences.append(filtered_sentence)

另一件可以考虑的事情是去除标点符号,它可能会干扰停用词的移除。上面展示的代码是寻找被空格包围的词语,因此如果停用词后紧跟一个句号或逗号,它将不会被识别出来。

Python的string库提供的翻译功能可以轻松解决这个问题。它还带有一个常量string.punctuation,其中包含了常见的标点符号列表,因此可以使用如下代码将其从单词中移除:

import string

table = str.maketrans('', '', string.punctuation)

words = sentence.split()

filtered_sentence = ""

for word in words:

word = word.translate(table)

if word not in stopwords:

filtered_sentence = filtered_sentence + word + " "

sentences.append(filtered_sentence)

在这里,每个句子在过滤停用词之前,单词中的标点符号已经被移除。因此,如果将句子拆分后得到“it;”,它会被转换为“it”,然后作为停用词被过滤掉。不过,注意当这样处理时,你可能需要更新停用词列表。通常,这些列表中会包含一些缩略词和缩写形式,比如“you’ll”。翻译器会将“you’ll”转换为“youll”,如果想要将它过滤掉,就需要在停用词列表中添加它。

遵循这三个步骤后,你将获得一组更加干净的文本数据。但当然,每个数据集都有其独特之处,你需要根据具体情况进行调整

本节总结,本节介绍了自然语言处理(NLP)的基础概念,包括文本编码、分词、去停用词和清理文本等技术。首先,探讨了如何将语言转为数字以便于计算机处理,并通过编码方法将单词分解为数值。接着,介绍了分词工具(如Tokenizer)在文本预处理中分配和管理单词索引。还讨论了处理未见过的词汇(OOV)以减少模型误差的策略。在清理文本方面,使用BeautifulSoup库去除HTML标签,并利用停用词列表和标点符号过滤功能对数据集进一步清理。此外,为确保数据一致性,介绍了填充(padding)技术以使数据形状一致,适用于模型训练。这些步骤为文本清理和建模提供了坚实的基础,但在实际应用中应灵活调整以应对不同数据集的需求。

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

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

相关文章

精选 Top10 开源调度工具,解锁高效工作负裁自动化

在大数据和现代 IT 环境中,任务调度与工作负载自动化(WLA)工具是优化资源利用、提升生产效率的核心驱动力。随着企业对数据分析、实时处理和多地域任务调度需求的增加,这些工具成为关键技术。本文将介绍当前技术发展背景下的Top 10开源任务调度工具,并探讨它们在大数据和工…

五分钟入门双拼!

从零开始学双拼的第一篇:概述‍这是从零开始学双拼的第一篇:概述 双拼的原理 如果你使用全拼,想要完整敲出一个字的读音,需要敲出这个字拼音的每个字母。 虽然简拼能简化一点步骤,但除非是很常见的成语、俗语,否则重码率很高,选词很困难。 有没办法提高效率呢?有的,那…

SVN提交日志模板设置

前言:每次提交时都要手动输入很多固定日志信息,或者在最近中选择信息记录会比较麻烦,通过这个设置可以在每次提交时,自动填充日志信息 设置步骤 1:先进入你想要提交svn自动设置模板时的目录(例如策划同学进入到Table表格提交记录,程序同学进入到代码提交目录,美术同学进…

ubuntu:旧版本配置apt源(ubuntu 21.10)

一,旧版本ubuntu上的apt源不能用了 # apt-get update 忽略:1 http://mirrors.aliyun.com/ubuntu hirsute InRelease 忽略:2 http://mirrors.aliyun.com/ubuntu hirsute-security InRelease 忽略:3 http://mirrors.aliyun.com/ubuntu hirsute-updates InRelease 忽略:4 http://…

医药企业数据治理,从何入手?一文讲清楚!

在医药行业,随着企业信息化进程的加速推进,ERP、CRM等系统纷纷引入业务流程。这些系统的不断增加,虽提升了业务管理的精细度,但也带来了数据的分散与冗余问题,数据治理因此成为企业面临的关键挑战。那么,医药企业的数据治理该如何入手?本文将为您逐一解析。 1. 数据标准…

【SpringBoot开发】 文件上传 (秒传、断点续传、分片上传)

原创 Java技术前沿引言 文件上传在软件开发项目中极为常见,涵盖了图片、音频、视频及各类文档的上传需求。对于小型文件,简单的Form表单上传机制通常足以应对。然而,当面对体积庞大的文件,如超过1GB的文件,或用户处于网络条件不佳的环境下时,传统的上传方式便显得力不从心…

数据采集与融合第三次作业

码云仓库地址 https://gitee.com/sun-jiahui22/crawl_project作业1仓库地址 https://gitee.com/sun-jiahui22/crawl_project/tree/master/作业3/实验3.1作业2的仓库地址 https://gitee.com/sun-jiahui22/crawl_project/tree/master/作业3/实验3.2作业3的仓库地址 https://gitee…

【java编程】深入浅出JVM(四):类文件结构

原创 菜菜的后端私房菜Java文件编译成字节码文件后,通过类加载机制到Java虚拟机中,Java虚拟机能够执行所有符合要求的字节码,因此无论什么语言,只要能够编译成符合要求的字节码文件就能够被Java虚拟机执行. Java虚拟机和字节码是语言、平台无关性的基石. 本篇文章将深入浅出…

HyperWorks实体网格划分

实体网格剖分 在 HyperMesh 中,使用 Solid Map 功能进行实体网格剖分。该面板如下图所示:图 4-4 Solid Map 面板 通过 Solid Map Panel 进行实体网格剖分: • 通过主菜单栏选择 3D 页面 > solid map 。 • 通过下拉式菜单选择 Mesh > create > Solid Map。 Solid Ma…

VS 2022 不支持 .NET Framework 4.5 项目解决办法(Visual Studio 2022)

VS 2022 不支持 .NET Framework 4.5 项目解决办法(Visual Studio 2022) 概述 最近 C# 开发工具 Visual Studio 升级到了 2022,打开速度快了很多,开发体验也舒服很多。只是使用过程中遇到了一个比较尴尬的问题:默认Visual Studio 2022 不再支持安装 .NET Framework 4.5 组件…

新建流程隐藏指定流程(建模+api+ecode)

ecode代码 `// 功能总开关 let enable = true; let list=[]; $.ajax({ type:GET, url:/api/xiangxin/Multiple/HideProcess, success:function(res){ res.data.map((i)=>{ // console.log(lc :, i.lc); list.push(i.lc); }) } }) let pd; //判断是否是新建流程页面 ecodeSDK…

分布式事物传递 NetMQ测试

using NetMQ; using NetMQ.Sockets; using System; using System.Threading; namespace 消息传递库_NetMQ服务端 {internal class Program{public static void Main(){using (var publisher = new PublisherSocket()){// 绑定到一个端口,等待订阅者连接publisher.Bind("t…