自然语言处理(NLP):文本向量化从文字到数字的原理

news/2025/2/26 6:20:11

人工智能领域,尤其是自然语言处理NLP)中,将文本信息转化为机器可以理解的形式是一个至关重要的步骤。本文探讨如何将文本转换为向量表示的过程,包括分词、ID映射、One-hot编码以及最终的词嵌入(Embedding),并通过具体的案例代码来辅助解释这些概念。

处理字符还是数字

人工智能算法只能处理数字形式的数据,特别是浮点数。这意味着任何非数字的信息,如汉字、字母等,都需要被转换成数值形式才能用于模型训练或预测。

由于AI算法不能直接处理汉字或其他字符,因此必须通过特定的方法将这些字符转换为数字表示。这一过程通常涉及到两个主要步骤:文本向量化词向量生成

如何把词变为向量?

假设有一个简单的句子:“我爱北京天安门!”,将其转化为向量的具体步骤分为分词、ID映射、One-hot编码、降维矩阵

第一步分词

第一步是对句子进行分词,即将一句话按照最小的语义单元拆解成一个个特征词/token。例如:

  • 原始句子:“我爱北京天安门!”
  • 分词结果:“我”|“爱”|“北京”|“天安门”|“!”

第二步ID映射

第二步是为每个token分配一个唯一的ID号。这一步通常需要借助一个词汇表(dictionary),其中每个单词都有一个对应的ID。例如:

  • “我” -> 3
  • “爱” -> 54
  • “北京” -> 65
  • “天安门” -> 78
  • “!” -> 89

第三步One-hot编码

第三步是进行One-hot编码,这是一种将分类数据转换为可提供给机器学习算法处理的形式的方法。然而,这种方法的一个显著缺点是它会产生高度稀疏的向量,即大部分元素都是0。

import numpy as np
# token_id:表示单词在词汇表中的唯一标识符(ID)。
# vocab_size:词汇表的大小,即词汇表中不同单词的数量。
def one_hot_encode(token_id, vocab_size):
    return np.eye(vocab_size)[token_id]

vocab_size = 10  # 假设词汇表大小为10
encoded_vector = one_hot_encode(3, vocab_size)
print(encoded_vector)

将token爱对应的 ID 3,输入到one_hot_encode函数,输出:

[0. 0. 0. 1. 0. 0. 0. 0. 0. 0.]

这是一个长度为10的数组,只有第4个位置上的值是1,其余均为0。这代表了ID为3的单词的One-hot编码。

np.eye(vocab_size)会创建一个vocab_size x vocab_size的单位矩阵(对角线上是1,其余位置都是0,例如:np.eye(10),生成一个 10 x 10 的单位矩阵:

array([[1., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 1., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 1., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 1., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 1., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 1., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 1., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 1., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 1., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 1.]])

当你用token_id作为索引访问这个矩阵的一行时,实际上就是获取了一个只在token_id位置上有1,其他地方全是0的向量,这就是所谓的One-hot编码。

我爱北京天安门!做 one hot 编码之后的高度稀疏矩阵就形如以下结构
3:[0, 0, 0, 1, … 0, 0, 0, …N-1]
54:[0, 0, … 0, 1, 0, 0, 0, …N-1]
65:[0, 0, 0, … 0, 0, 0, 1, …N-1]

第四步降维
通常One-hot编码使用的词汇表都很很大,上万的词汇表,不会是上面所列举的词汇表大小为10,

使用上万的词汇表进行One-hot编码之后,接下来,使用一个可学习的降维矩阵,把高维向量变成低维向量,比如:512维,这种低维向量不仅减少了计算复杂度,还能捕捉到单词之间的语义关系。

数据结构变化

整个过程中的数据结构变化如下:

  • 原始句子:[batch_size, seq_len],batch_size指的是有几个句子,seq_len指的是每一句被拆分成多少个词,也可以说有多少个token。
  • 向量化后:[batch_size, seq_len, embedding_dim],每句话中的每个词都被替换为其对应的embedding_dim维向量。

上面这 2 个步骤,一是One-hot 编码,二是降维,可以合并一起做,通常使用 Embedding 层 一步到位解决!

import torch
import torch.nn as nn

# 定义词汇表大小和嵌入维度
vocab_size = 10000  # 假设词汇表中有10,000个单词
embedding_dim = 128  # 每个单词的嵌入向量维度为128

# 创建 Embedding 层,
embedding = nn.Embedding(vocab_size, embedding_dim)

# 假设我们有一些单词索引
word_indices = torch.LongTensor([1, 2, 3, 4, 5])

# 获取嵌入向量,该层将词汇表中的每个单词索引映射到一个128维的向量。
# embedded_words 的形状为 (5, 128),表示5个单词,每个单词是128维嵌入向量。
embedded_words = embedding(word_indices)

print(embedded_words)
print(embedded_words.shape)  # 输出应为 torch.Size([5, 128])

嵌入向量输出:

tensor([[-4.6017e-01,  1.4473e+00,  1.3787e+00, -5.0634e-01, -1.4204e+00,
         -1.4081e+00,  9.2571e-01, -6.7002e-02,  1.1946e+00,  2.6210e-01,
......
         -2.5038e-01,  1.7327e+00,  6.1820e-01, -2.5919e+00, -7.2244e-01,
         -1.2460e+00,  1.8335e-01, -6.0779e-01],
        [ 8.9795e-01,  1.4445e-01,  3.1429e-01,  9.1202e-01,  4.9661e-01,
         -8.2164e-01, -5.8186e-02, -5.8424e-01,  1.6252e+00, -8.2808e-01,
         -6.9109e-01, -6.8143e-01,  8.2476e-01, -6.3271e-04, -2.0201e+00,
......
          5.3912e-02,  6.3298e-01,  1.4325e-01,  1.5289e+00, -1.7104e+00,
         -1.4025e+00, -1.2311e+00, -2.2923e-01],
        [-6.1438e-01,  2.2085e-01, -3.0483e-01,  1.5419e+00, -1.5819e+00,
         -3.1500e-01,  2.9932e-01, -6.7334e-01,  5.3232e-01,  1.2593e+00,
......
          1.1688e+00,  3.6866e-02,  1.1927e+00,  6.4568e-01,  2.6445e+00,
          4.6353e-01,  1.1167e+00,  1.1976e+00],
        [-9.9785e-01, -2.2409e-01,  2.2492e-01,  1.1209e+00,  5.7221e-01,
         -1.7736e+00,  3.1228e-01,  1.4183e-01, -5.0892e-01, -7.8893e-01,
......
          1.7181e-02,  6.5555e-01, -3.8586e-01, -6.2199e-01,  1.9411e+00,
         -1.6575e+00,  5.7638e-01, -9.1050e-02],
        [-1.8926e-01, -5.7981e-01, -9.2958e-01, -5.5733e-01,  2.2229e-01,
          8.1851e-01,  7.6115e-01, -6.0253e-01,  4.3705e-01,  6.8111e-01,
         -1.3935e+00,  1.0176e+00,  3.8146e-01,  3.0739e-01,  2.0513e+00,
          1.2356e+00, -2.3017e-02,  5.3749e-01, -7.2238e-01,  4.8466e-01,
......
          1.1089e+00,  1.7572e+00, -2.1472e-01, -8.5052e-01, -3.2292e-01,
          1.0617e+00,  8.3969e-02,  9.5239e-01]], grad_fn=<EmbeddingBackward0>)
torch.Size([5, 128])

nn.Embedding(...)是 PyTorch 中的一个模块,用于将离散的输入(如词索引)转换为连续的向量表示(嵌入向量),这些向量通常称为嵌入(embeddings)

如何理解将离散的输入数据转换为连续的向量表示?

自然语言处理NLP)中,最常见的离散数据是单词。每个单词可以用一个唯一的索引(整数)来表示。例如,假设我们有一个词汇表,其中每个单词都有一个唯一的索引:

0 -> "apple"
1 -> "banana"
2 -> "cherry"
3 -> "date"
...

在这种情况下,单词 “apple” 的索引是0,“banana” 的索引是1,依此类推。这种单词索引,经过nn.Embedding层处理之后,索引会被映射为下面这样的[4,N]向量

tensor([[ 0.0347,  0.0254, -0.0123,  ..., -0.0056,  0.0123,  0.0045],
        [ 0.0123, -0.0045,  0.0034,  ...,  0.0234, -0.0123,  0.0056],
        [-0.0045,  0.0123,  0.0034,  ...,  0.0056,  0.0123, -0.0045],
        [ 0.0034,  0.0123, -0.0056,  ...,  0.0045,  0.0123,  0.0034]])

这样的值就是连续的向量表示,128维的嵌入向量是一个包含128个浮点数的向量,这些浮点数可以在某个区间内取任意值,通常初始化为某个分布(如标准正态分布或均匀分布)。

nn.Embedding 的作用

  1. 将离散数据转换为连续向量:nn.Embedding 将离散的输入(如单词索引)映射到固定维度的向量空间中。这些向量通常被称为嵌入向量。
  2. 捕捉语义信息:嵌入向量可以捕捉输入数据的语义信息。例如,在 NLP 中,相似的单词通常会有相似的嵌入向量。相似的单词在向量空间中距离较近。
  3. 减少维度:嵌入向量通常比原始的独热编码(one-hot encoding)维度要小得多,这有助于减少模型的复杂性和计算成本。

nn.Embedding 主要有两个参数:

  • num_embeddings:表示嵌入矩阵的大小,例如,如果你的词汇表中有10,000个单词,num_embeddings 应该设置为10,000。
  • embedding_dim:表示每个嵌入向量的维度。例如,如果你希望每个单词的嵌入向量是128维,embedding_dim 应该设置为128。

embedding_dim为什么是128维?根据什么来确定embedding_dim的大小呢?

选择嵌入向量的维度(如128维)是一个重要的超参数选择,它会影响模型的性能和训练效率。没有严格的规则规定必须选择某个特定的维度,选择嵌入向量的维度(如128维)是一个平衡模型容量、任务复杂度、数据规模和计算资源的决策。

没有固定的规则,但可以通过实验和经验来确定最适合你任务的维度。

  1. 模型容量
    高维度:更高的维度(如256、512)通常意味着模型有更多的参数,可以捕捉更复杂的语义信息。这在处理大型词汇表和复杂任务时可能更有优势。
    低维度:较低的维度(如64、128)通常意味着模型参数较少,训练速度更快,内存占用更少。这在资源受限的环境中或处理较小词汇表和简单任务时可能更有优势。
  2. 任务复杂度
    简单任务:对于简单的任务(如情感分析、文本分类),较低的维度(如64、128)通常足够。
    复杂任务:对于复杂的任务(如机器翻译、问答系统),可能需要更高的维度(如256、512)来捕捉更多的语义信息。
  3. 数据规模
    大数据集:对于大规模数据集,可以选择较高的维度,因为有足够的数据来训练更多的参数。
    小数据集:对于小规模数据集,选择较低的维度可以减少过拟合的风险。
  4. 计算资源
    资源充足:如果你有充足的计算资源(如GPU、TPU),可以选择较高的维度,因为训练时间和内存占用不是主要瓶颈。
    资源有限:如果你的计算资源有限,选择较低的维度可以加快训练速度并减少内存占用。

上面示例中输出的这128维的嵌入向量,各个数据之间有什么关系吗?

128维的嵌入向量中的各个数据之间并没有固定的数学关系,但它们通过训练过程逐渐学习到一些有意义的结构。这些嵌入向量的设计目的是捕捉输入数据(如单词)的语义信息

  • 语义相似性
    相似单词的嵌入向量相近:在训练过程中,相似的单词(如 “猫” 和 “狗”)的嵌入向量会在向量空间中靠近彼此。这意味着它们之间的欧几里得距离较小。
    上下文相关性:嵌入向量还会捕捉单词在不同上下文中的使用情况。例如,“银行” 在金融上下文中和河流岸边的上下文中会有不同的嵌入向量。
  • 方向和角度
    方向:嵌入向量的方向可以表示某些语义属性。例如,在某些嵌入空间中,向量 “king” - “man” + “woman” 可能接近 “queen”。
    角度:嵌入向量之间的夹角可以反映它们之间的语义关系。例如,余弦相似度可以用来衡量两个向量的相似性。
  • 维度的意义
    无明确意义:通常情况下,嵌入向量的各个维度本身并没有明确的语义意义。每个维度的值是通过训练过程自动学习的,目的是使模型在特定任务上的表现更好。
    潜在特征:虽然单个维度没有明确意义,但整个向量可以看作是单词在多维特征空间中的表示。每个维度可以被视为一个潜在特征,这些特征共同决定了单词的语义。
  • 训练过程
    反向传播:在训练过程中,嵌入向量的值会通过反向传播算法不断调整,以最小化损失函数。这使得嵌入向量能够更好地捕捉输入数据的语义信息。
    优化目标:优化目标通常是为了使模型在特定任务(如语言模型、分类任务等)上的性能最大化。在这个过程中,嵌入向量逐渐学习到有意义的结构。

从网络上找了一个图,很形象的描述了向量化的过程
在这里插入图片描述


http://www.niftyadmin.cn/n/5868183.html

相关文章

Anaconda 与 Jupyter Notebook 的结合使用

1. 引言 Jupyter Notebook 是一种非常流行的交互式计算环境&#xff0c;广泛应用于数据科学、机器学习和学术研究领域。它允许用户将代码、文本、可视化和数学公式结合在一个文档中&#xff0c;极大地提高了分析过程的可视性和可重复性。Anaconda 作为 Python 生态中最为流行的…

排序算法适合的场景

排序算法的选择取决于数据规模、特性、稳定性需求、内存限制等因素。以下为常见排序算法及其适用场景&#xff1a; 1. 简单排序算法&#xff08;O(n)&#xff09; 冒泡排序 场景&#xff1a;数据量极小&#xff08;如 n ≤ 100&#xff09;或几乎有序&#xff1b;教学演示。缺点…

在k8s中,如何在argocd中添加proxy

在 Kubernetes 的 Argo CD 中添加代理&#xff08;Proxy&#xff09;设置&#xff0c;你可以从多个层面进行操作&#xff0c;下面分别介绍不同组件设置代理的方法。 1. Argo CD Server 代理设置 Argo CD Server 负责提供 Web UI 和 API 服务&#xff0c;要为其设置代理&#…

【论文学习】基于规模化Transformer模型的低比特率高质量语音编码

以下文章基于所提供的文档内容撰写&#xff0c;旨在对该论文“Scaling Transformers for Low-Bitrate High-Quality Speech Coding”进行较为系统和深入的分析与总结。 论文地址&#xff1a;https://arxiv.org/pdf/2411.19842 一、研究背景与动机 自20世纪70年代以来&#xff…

FMEA软件系统在制造业应用的必要性解析

FMEA软件系统在制造业应用的必要性 在当今制造业竞争日益激烈的环境下&#xff0c;企业面临着来自产品质量、生产效率和成本控制等多方面的挑战。FMEA&#xff08;失效模式与影响分析&#xff09;作为一种重要的质量管理工具&#xff0c;已被广泛应用于制造业的各个环节。然而…

Java进阶-在Ubuntu上部署SpringBoot应用

随着云计算和容器化技术的普及&#xff0c;Linux 服务器已成为部署 Web 应用程序的主流平台之一。Java 作为一种跨平台的编程语言&#xff0c;具有广泛的应用场景。本文将详细介绍如何在 Ubuntu 服务器上部署 Java 应用&#xff0c;包括环境准备、应用发布、配置反向代理&#…

HarmonyOS 5.0应用开发——鸿蒙接入高德地图实现POI搜索

【高心星出品】 文章目录 鸿蒙接入高德地图实现POI搜索运行结果&#xff1a;准备地图编写ArkUI布局来加载HTML地图 鸿蒙接入高德地图实现POI搜索 在当今数字化时代&#xff0c;地图应用已成为移动设备中不可或缺的一部分。随着鸿蒙系统的日益普及&#xff0c;如何在鸿蒙应用中…

springboot实现文件上传到华为云的obs

一、前言 有时在项目中需要使用一些存储系统来存储文件&#xff0c;那么当项目要接入obs作为存储系统时&#xff0c;就会利用obs来进行文件的上传下载&#xff0c;具体实现如下。 二、如何通过obs实现文件的上传下载&#xff1f; 1.添加相关的obs的maven依赖。 <dependency…