news 2026/5/1 4:03:02

NLP 图解,第一部分:文本编码

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
NLP 图解,第一部分:文本编码

原文:towardsdatascience.com/nlp-illustrated-part-1-text-encoding-41ba06c0f512

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/5de16d507d802b50a228a1ebc20307a2.png

今天,我们开始一个新的系列,关于自然语言处理(NLP)。这很令人兴奋,因为 NLP 是我们随处可见的所有花哨的大型语言模型(LLMs)的支柱——想想 Claude、GPT 和 Llama。

简而言之,NLP 帮助机器理解人类语言——无论是理解它、分析它,甚至生成它。


如果你一直在跟随我们的**深度学习之旅**,我们已经了解到,在本质上,神经网络遵循一个简单的原则:它们接收一个输入,施展数学魔法,然后输出一个结果。

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/510668ce6eb1a8adf3f3f798c0dc218b.png

然而,为了使神经网络能够做到这一点,输入和输出都必须是它们理解的格式:数字。

这个规则适用于我们处理任何简单模型的情况…

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/a55a71d67515e6402538c9e152084bb8.png

…或者一个高度复杂的模型如 GPT。

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/60b61b9687b79d97c4ac761ce3a4381d.png

现在事情变得有趣了。我们通过文本与模型如 GPT 进行交互。例如,我们可能会问它:“印度的首都是哪里?”,模型能够理解这段文本并提供一个回答。

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/e55ec0da3f61713b75f69348ab537c31.png

我们不能将文本输入到神经网络中

但等等——我们不是刚刚说过神经网络不能直接处理文本,而需要数字吗?

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/c451c705039dd6da13c53d465ed4df39.png

欢迎回到互联网的角落,我们在这里处理听起来复杂的机器学习概念,并通过图解的方式展示它们——最终发现它们其实并不那么复杂!

向 ChatGPT 提问

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/5de16d507d802b50a228a1ebc20307a2.png

我们需要在将文本输入神经网络之前将其转换为数字

这就是文本编码的作用,在这篇文章中,我们将探讨一些处理这种文本到数字转换的简单方法。

独热编码

编码文本最简单的方法之一是通过独热编码

让我们分解一下:想象我们有一个包含 10,000 个词的词典。这个词典中的每个词都有一个独特的位置。

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/ae3b8bade0e4e3f1fa9bcd66b1a5b37a.png

我们词典中的第一个词“about”位于位置 1,而最后一个词“zoo”则位于位置 10,000。同样,每个其他词都有其独特的位置在两者之间。

现在,让我们说我们想要编码单词“dogs”。首先,我们在字典中查找它的位置…

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/949bb082b3b829b7887f10eff0c5c7d7.png

…并且发现“dogs”在第 850 个位置。为了表示它,我们创建一个包含 10,000 个零的向量,然后将第 850 个位置设置为 1,如下所示:

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/5a79e0a20d3187ecc0b0e939c2728b79.png

这就像一个开关:如果单词的位置匹配,开关是开着的(1)如果不匹配,开关是关着的(0)。

现在,假设我们想要编码这个句子:

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/ef4f2517ece76389c06816a5100d6e30.png

除了“dogs”的词向量外,我们还找到了“barks”的词向量…

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/0d7324ba83b4730015548b634986cefc.png

…以及“loudly”

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/88e90b2eb96a74ecd68ea31a46c7f2a5.png

然后为了表示整个句子,我们将这些单独的词向量堆叠成一个矩阵,其中每一行对应一个单词的向量:

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/2cfc1535d883d15ad5aefd55f39b520d.png

这形成了一个句子矩阵,行对应于单词。虽然这很简单且直观,但独热编码有一个很大的缺点:低效。

每个词向量都非常大,大部分都是零。例如,对于一个包含 10,000 个单词的字典,每个向量包含 10,000 个元素,其中 99.99%是零。如果我们扩展到更大的字典——比如拥有大约 170,000 个单词的剑桥英语词典——这种低效性变得更加明显。

现在想象一下通过将这些 170,000 大小的词向量堆叠成句子矩阵来编码一个句子——它很快就会变得巨大且难以管理。为了解决这些问题,我们转向一个更有效的方法:词袋模型。

词袋模型

词袋模型(BoW)通过为整个句子创建一个单一向量,而不是为每个单词创建单独的向量来简化文本表示

想象我们有这四个句子想要编码:

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/cd72aed94c9039f3a0b77d62f1845b9f.png

如果你知道这个引用的来源,那你就赚到了。如果你不知道,我们就假装这是人们常说的话。

第一步是创建一个包含这四个句子中所有独特单词的字典。

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/be7b2529540b1a14cd52eb60bf96c744.png

BoW 字典

每个句子都表示为一个向量,其长度等于我们字典中独特单词的数量。并且向量中的每个元素代表字典中的一个单词,并设置为该单词在句子中出现的次数。

例如,如果我们取第一句话“onions have layers,”它的向量看起来是这样的:

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/a273e357db509422d8adb78293c19549.png

句子 1 的 BoW 编码

“onions”出现了一次,“have”出现了一次,“layers”出现了一次。所以,这个句子的向量会在这些位置上有一个1

同样,我们可以编码剩余的句子:

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/bc11486bb94c57546990721a1c19f6cb.png

句子的 BoW 编码

让我们再编码一个例子:

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/9c3f7ad7335fd562e76a36c4bfbd8098.png

对于这个句子,单词“layers”“have”重复了两次,因此它们在向量中的对应位置将具有值2

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/e168448ec21499e93eb08fef70248e37.png

句子的 BoW 编码

下面是如何在 Python 中实现 BoW 的示例:

fromsklearn.feature_extraction.textimportCountVectorizer sentences=["Onions have layers","Ogres have layers","You get it?","We both have layers"]bag_of_words=CountVectorizer()X=bag_of_words.fit_transform(sentences)print("BoW dictionary:",bag_of_words.get_feature_names_out())print("BoW encoding:n",X.toarray())
BoW dictionary:['both''get''have''it''layers''ogres''onions''we''you']BoW encoding:[[001010100][001011000][010100001][101010010]]

虽然 BoW 在计数单词方面简单有效,但它无法捕捉单词的顺序或上下文。例如,考虑以下两个句子中的单词“bark”

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/3530657f70805fc4e6e8a28228d306db.png

“dogs bark loudly”中的单词“bark”与在“the tree’s bark”中的单词“bark”完全不同的意义。但 BoW 在这两种情况下都会将“bark”视为相同,忽略了周围单词提供的意义差异。

二元组

这就是二元组发挥作用的地方。通过查看相邻的单词,它们有助于捕捉更多的上下文。让我们用这两个句子来说明这一点:

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/3530657f70805fc4e6e8a28228d306db.png

就像在 BoW 方法中一样,我们首先创建一个字典:

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/31ac8b23036d62771724b73dc5662bb5.png

然而,这次,除了单个单词外,我们还包括单词对(二元组)。这些二元组是通过查看每个句子中直接相邻的单词形成的。

例如,在句子“dogs bark loudly”中,二元组将是:

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/988ae32cf20068b950858d6ab35a14f0.png

“the tree’s bark”中,这些是二元组:

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/12914e7decf4b262c92a6365150023f4.png

我们将其添加到我们的字典中,以获得我们的二元组词典:

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/8f2d87f84332ec859ed2e1bfacf3b9b1.png

二元组词典

接下来,我们将每个句子表示为一个向量。与 BoW 类似,这个向量中的每个元素都对应于字典中的一个单词或二元组,其值表示该单词或二元组在句子中出现的次数。

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/d618f5ba77ac44d591f5c0ab13a89d17.png

二元组编码

使用二元组允许我们通过捕捉相邻单词之间的关系来保留上下文。因此,如果一个句子包含“tree’s bark”而另一个句子包含“dogs bark”,这些二元组将以不同的方式表示,从而保留它们的意义。

下面是如何在 Python 中实现二元组的示例:

fromsklearn.feature_extraction.textimportCountVectorizer sentences=["dogs bark loudly","the tree's bark"]bigram=CountVectorizer(ngram_range=(1,2))#(1, 2) specifies that we want single words and bigramsX=bigram.fit_transform(sentences)print("Bigram dictionary:",bigram.get_feature_names_out())print("Bigram encoding:n",X.toarray())
Bigram dictionary:['bark''bark loudly''dogs''dogs bark''loudly''the''the tree''tree''tree bark']Bigram encoding:[[111110000][100001111]]

N-gram

正如二元组将两个连续的单词分组一样,我们可以将这个概念扩展到n-gram,其中n代表一起分组的单词数量。例如,当n=3(三元组)时,我们会将三个连续的单词分组,例如“dogs bark loudly”。同样,当n=5时,我们会将五个连续的单词分组,从而从文本中捕捉到更多的上下文。

这种方法使我们能够捕捉到文本数据中更丰富的关系和上下文,但它也增加了字典的大小和计算复杂度。

TF-IDF

虽然“词袋模型”和“二元组”在计数单词和捕捉基本上下文方面很有效,但它们没有考虑单词在句子或多个句子中的重要性或独特性。这就是**TF-IDF(词频-逆文档频率)**发挥作用的地方。它根据以下因素对单词进行加权:

  • 词频 (TF):一个单词在句子中出现的频率

  • 逆文档频率 (IDF):一个单词在所有句子中的稀有程度或独特性

这种加权系统使得 TF-IDF 在突出句子中的重要单词同时,可以降低常见单词的影响。

为了看到这个在实际中的应用,让我们将 TF-IDF 应用于我们熟悉的四句话集合。

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/cd72aed94c9039f3a0b77d62f1845b9f.png

和之前一样,我们创建了一个包含我们句子中所有唯一单词的字典。

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/be7b2529540b1a14cd52eb60bf96c744.png

词频 (TF)

要计算一个单词的 TF,我们使用以下公式:

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/eb8058bbbaf1419d22ad4c081617e8a1.png

例如,对于第一句话中的单词“onions”

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/a932816ddeca7dbcf44dc931968111f3.png

…TF 是:

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/cb43fd529e8ba209ffc4dfab3ba06fa7.png

同样,让我们计算第一句话中“both”的 TF:

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/8d666af75971c7be3bb7ac5ad9a70a29.png

使用相同的逻辑,我们可以得到所有四个句子中所有单词的 TF,如下所示:

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/ea34605d30f2d2fa8aba69771b30e493.png

每个句子中所有单词的 TF

注意,一个单词的 TF 可能在不同句子中有所不同。例如,单词 “both” 在前三句中没有出现,所以这些句子的 TF 为 0。然而,在最后一句中,它出现在四个单词中的其中一个,其 TF 为 1/4。

逆文档频率 (IDF)

接下来,我们计算每个单词的 IDF。IDF 会给出现在句子中较少的单词更高的值,从而强调那些出现在较少句子中的单词。

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/81c6cd34d90cd5c675e23597b8ce790c.png

例如,我们看到单词“both”只出现在四句话中的一句:

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/fa88445df7a1a16603c5e48b04e32809.png

因此,其 IDF 为:

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/6075c0557da4b4daff635ba5f403313a.png

同样,我们可以得到字典中其他单词的 IDF:

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/4c0efa3505eb82a716b966892f5e907f.png

字典中所有单词的 IDF

在这里,单词“both”只出现在第 4 句话中,这使得它的 IDF 得分比像“have”这样出现在多句中的常见单词要高。

与 TF 不同,一个单词的 IDF 在所有句子中保持一致。

TF-IDF

一个单词最终的 TF-IDF 分数是其 TF 和 IDF 的乘积:

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/36d61736f9de29f36bfbe16639b2f61e.png

这导致句子向量,其中每个单词的得分反映了它在句子中的重要性(TF)以及它在所有句子中的独特性(IDF)。

将 TF 和 IDF 项代入我们的公式,我们得到最终的 TF-IDF 句子向量:

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/31eb951acd9c335e074bbb6e1d384692.png

所有句子的 TD-IDF 编码

这是我们在 Python 中计算 TF-IDF 的方法:

fromsklearn.feature_extraction.textimportTfidfVectorizer sentences=["Onions have layers","Ogres have layers","You get it?","We both have layers"]tfidf=TfidfVectorizer()X=tfidf.fit_transform(sentences)print("TF-IDF dictionary:",tfidf.get_feature_names_out())print("TF-IDF encoding:n",X.toarray())

注意:Python 的结果可能与手动计算略有不同,因为:

1. L2 Normalization:Scikit-learn 的TfidfVectorizer默认将向量归一化到单位长度。2. 调整后的 IDF 公式:IDF 计算包括一个平滑项,以防止对于出现在所有句子中的单词进行除零操作。

在这里了解更多关于这个信息。


虽然我们讨论的方法是 NLP 中的基本构建块,但它们存在显著的局限性。

1 – 这些方法缺乏语义理解。它们无法理解单词的意义,也无法识别像“fast”(快)和“quick”(迅速)这样的同义词之间的关系。虽然双词组可以提供一些局部上下文,但它们仍然错过了更深层次的联系和细微的意义差异。

2 – 这些方法依赖于刚性表示,将单词视为孤立实体。例如,我们直观地理解“king”(国王)和“queen”(王后)是相关的,但这些方法将“king”和“queen”表示为与“king”和“apple”(苹果)一样无关,完全忽略了它们的相似性。

3 – 他们面临可扩展性挑战。它们依赖于稀疏、高维向量,随着词典大小的增加,这些向量变得更加难以处理和效率低下。

如果我们能够以捕捉单词的意义、相似性和关系的方式来表示单词,这正是词嵌入所追求的。词嵌入通过创建密集、有意义的向量,同时保留上下文和语义关系,从而革新了文本编码。

在下一篇文章中,**NLP 图解,第二部分:词嵌入**,我们将探讨这些嵌入如何超越基本的词频统计,捕捉单词之间复杂、细微的关系!

NLP 图解,第二部分:词嵌入

如果您有任何问题/评论,请通过LinkedIn与我联系或给我发送电子邮件至[email protected]。

备注:除非另有说明,所有插图均由作者绘制。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/1 2:47:07

Docker+AnythingtoRealCharacters2511:一键部署生产环境

DockerAnythingtoRealCharacters2511:一键部署生产环境 1. 这个工具到底能帮你做什么 你可能已经见过那些让人眼前一亮的效果——一张二次元角色立绘,几秒钟后变成皮肤有质感、光影自然、神态生动的真实人像。AnythingtoRealCharacters2511 就是专门做…

作者头像 李华
网站建设 2026/5/1 2:48:26

Flowise模型热替换:不重启服务切换LLM后端实测

Flowise模型热替换:不重启服务切换LLM后端实测 1. Flowise 是什么?一个让AI工作流“看得见、摸得着”的平台 Flowise 不是又一个需要写几十行代码才能跑起来的 LangChain 项目,它是一个真正把复杂变简单、把抽象变具体的可视化工具。2023 年…

作者头像 李华
网站建设 2026/4/30 12:19:53

立知lychee-rerank-mm入门教程:5分钟搭建图文匹配系统

立知lychee-rerank-mm入门教程:5分钟搭建图文匹配系统 1. 这个模型到底能帮你做什么 你有没有遇到过这样的情况:在做图片搜索时,系统返回的前几条结果明明和你的描述不太搭,但真正想要的那张图却藏在第十几页?或者在…

作者头像 李华
网站建设 2026/5/1 3:49:24

Z-Image模型Keil5开发:嵌入式AI图像生成实践

Z-Image模型Keil5开发:嵌入式AI图像生成实践 1. 嵌入式AI的新可能:当轻量级图像生成遇上Keil5 最近在调试一款智能安防设备时,我遇到了一个典型问题:设备需要在本地实时生成告警场景的示意图,但传统方案要么依赖云端…

作者头像 李华
网站建设 2026/4/28 6:52:43

EasyAnimateV5-7b-zh-InP入门指南:从零开始搭建视频生成环境

EasyAnimateV5-7b-zh-InP入门指南:从零开始搭建视频生成环境 1. 为什么选择EasyAnimateV5-7b-zh-InP作为入门起点 刚开始接触AI视频生成时,面对动辄几十GB的模型和复杂的硬件要求,很多人会感到无从下手。EasyAnimateV5-7b-zh-InP这个模型就…

作者头像 李华
网站建设 2026/4/30 14:34:40

Qwen2.5-7B-Instruct开源镜像详解:vLLM异步IO与高并发请求压测

Qwen2.5-7B-Instruct开源镜像详解:vLLM异步IO与高并发请求压测 1. Qwen2.5-7B-Instruct模型核心能力解析 Qwen2.5是通义千问系列最新发布的语言模型迭代版本,代表了当前开源大模型在知识广度、任务泛化和工程实用性上的重要进步。相比前代Qwen2&#x…

作者头像 李华