- 🍨 本文为🔗365天深度学习训练营 中的学习记录博客
- 🍖 原作者:K同学啊
一、Embedding( 词嵌入技术)详解
Embedding
和 EmbeddingBag
是在自然语言处理(NLP)和其他机器学习任务中常用的两种嵌入层,它们用于将离散的类别数据(如单词、字符、物品等)映射为连续的向量表示。这些向量表示可以捕捉到数据中的语义或结构信息,并且可以在后续的神经网络层中使用。
Embedding
Embedding
层是神经网络中的一个基础组件,它将整数索引(代表离散的类别)映射到固定大小的密集向量。每个整数索引对应一个唯一的向量,这些向量通常在训练过程中学习得到。在 NLP 中,Embedding
层通常用于将单词(通过其索引表示)映射到向量,这些向量可以捕捉到单词的语义信息。
例如,假设我们有一个包含 10,000 个单词的词汇表,我们想要将每个单词映射到一个 128 维的向量。那么,Embedding
层将会有 10,000 个输入(每个单词一个)和 128 个输出(每个单词的向量表示)。
在 PyTorch 中,Embedding
层的基本用法如下:
import torch.nn as nn
# 假设词汇表大小为 10,000,嵌入向量的维度为 128
embedding_layer = nn.Embedding(num_embeddings=10000, embedding_dim=128)
在训练过程中,Embedding
层的权重会根据下游任务的损失函数通过反向传播进行更新,从而学习到能够代表单词语义的向量。
embedding
的输入时是整数张量,每个整数代表一个词汇的索引,输出是一个浮点数张量,每个浮点数都代表着对应词汇的词嵌入向量。
在机器学习和深度学习中,嵌入层(embedding layer)是一种特殊的神经网络层,它的作用是将离散的输入数据(如单词、物品、用户ID等)映射到连续的向量表示。这种映射是通过一个可训练的权重矩阵实现的,该矩阵的行数等于输入数据的类别数(例如,词汇表中的单词数),列数等于嵌入向量的维度。
嵌入层的作用
- 降维:嵌入层可以将高维的离散数据(one-hot编码形式的单词、物品等)映射到低维的连续向量,从而减少数据的维度。
- 泛化:嵌入层可以学习到输入数据的分布式表示,这种表示能够捕捉到数据中的语义或结构信息,有助于模型泛化到未见过的数据。
- 相似性:在嵌入空间中,语义上相似或相关的输入数据(如单词的同义词、物品的类似类别等)会被映射到相近的向量,这使得模型能够利用这些相似性。
- 效率:相比于使用one-hot编码,嵌入向量提供了更加紧凑的数据表示,可以减少模型的参数数量,提高训练和推理的效率。
嵌入层使用随机权重初始化,将数据集中的词嵌入:可以作为深度学习模型的一部分一起被学习,也可以用于加载训练好的词嵌入模型。嵌入层被定义为网络的第一个隐藏层
EmbeddingBag
EmbeddingBag
是 Embedding
的一个变种,它用于处理bags of embeddings,即当输入数据是由多个嵌入向量组成的集合时。EmbeddingBag
的主要优点是它能够有效地计算这些嵌入向量的平均值,而不需要显式地计算每个向量的和。
在 NLP 中,这通常用于处理句子或段落,其中每个单词都有一个对应的嵌入向量。使用 EmbeddingBag
,我们可以直接计算句子中所有单词嵌入向量的平均值,而不是先分别计算每个单词的嵌入向量,然后再计算它们的平均值。
EmbeddingBag
的另一个优点是它能够减少内存和计算资源的使用,因为它可以避免存储和计算每个单独的嵌入向量。
在 PyTorch 中,EmbeddingBag
层的基本用法如下:
import torch.nn as nn
# 假设词汇表大小为 10,000,嵌入向量的维度为 128
embedding_bag_layer = nn.EmbeddingBag(num_embeddings=10000, embedding_dim=128, mode='mean')
在使用 EmbeddingBag
时,你需要提供一个额外的参数 offsets
,它指定了每个嵌入向量集合的起始索引。例如,如果有一个句子 “I love machine learning”,那么 offsets
可能是 [0, 1, 2, 3]
,表示第一个单词 “I” 的嵌入向量从索引 0 开始,第二个单词 “love” 的嵌入向量从索引 1 开始,依此类推。
总之,Embedding
和 EmbeddingBag
都是用于将离散数据映射为连续向量表示的层,他们将离散词汇映射到一个低维的连续向量空间中,使得词汇之间的语义关系能够在向量空间中得到体现,但 EmbeddingBag
提供了一种更高效的方式来处理嵌入向量的集合。
如何理解低维向量空间以及它在机器学习数据处理中的作用
低维向量空间是指维度相对较低的空间,其中的向量由几个数值(即维度)来表示。在数学和机器学习中,低维向量空间的概念通常用于降维、数据压缩、可视化以及特征提取等任务。为了解释这个概念,我们需要理解一些基础的数学和几何概念。
首先,让我们从最简单的情形开始,即二维向量空间。在二维空间中,任何向量都可以通过平面上的一个点来表示,向量的大小(长度或范数)和方向由该点的位置决定。二维向量有两个数值,通常表示为 (x, y),其中 x 和 y 是向量在水平和垂直方向上的分量。
类似地,三维向量空间是我们在日常生活中最能直观理解的空间。一个三维向量由三个数值 (x, y, z) 表示,可以对应于三维空间中的一个点,其中 x、y 和 z 分别表示向量在三个垂直方向上的分量。
当我们谈论低维向量空间时,我们通常指的是二维、三维或四维(不包括时间维度的四维空间)这样的空间,因为这些空间可以通过图形在纸上或计算机屏幕上直观地展示出来。在这些低维空间中,向量的性质和行为可以通过几何图形和直观的视觉方式进行理解和分析。
然而,在实际应用中,我们经常处理的数据和模型存在于更高维度的空间中。例如,一个包含数千个特征的数据库可能存在于数千维的空间中。在这样的高维空间中,直接进行数据的可视化变得非常困难,甚至不可能,因为我们的直觉和视觉感知是在三维空间中形成的。
低维向量空间的重要性在于它们提供了一种简化的视角来近似和理解高维数据。通过降维技术(如主成分分析PCA、t-SNE、自编码器等),我们可以将高维数据映射到低维空间,同时尽量保留数据中的关键结构和信息。这样做的目的是为了能够在低维空间中更有效地分析和处理数据,例如通过可视化来发现模式、通过简化模型来减少计算成本、或通过压缩数据来减少存储需求。
在数学上,向量空间是一个由向量组成的集合,这些向量遵循加法和标量乘法的规则。低维向量空间是这些向量空间的一个子集,其中向量的维度相对较小。在低维向量空间中,我们可以更容易地研究向量的性质,如线性相关性、基的概念、以及子空间的结构等。
总结来说,低维向量空间是一个数学概念,它帮助我们通过减少数据的维度来简化问题,使得我们能够使用几何直觉和图形工具来分析和理解数据。在机器学习中,低维表示通常用于提取和利用数据中的关键信息,同时减少计算和存储的复杂性。
二、使用方法
1.Embedding
在PyTorch中,torch.nn.Embedding
是用于创建嵌入层的类。它的函数原型和参数如下:
class torch.nn.Embedding(num_embeddings, embedding_dim, padding_idx=None, max_norm=None, norm_type=2.0, scale_grad_by_freq=False, sparse=False, _weight=None)
下面是每个参数的解释和使用方法:
- num_embeddings (int) - 必需的参数,表示嵌入层中的类别总数,或者说是词汇表的大小。例如,如果处理一个包含10,000个唯一单词的语料库,那么
num_embeddings
将是10,000。 - embedding_dim (int) - 必需的参数,表示每个嵌入向量的维度。这个参数决定了嵌入空间的大小。例如,如果希望每个单词都由一个128维的向量表示,那么
embedding_dim
将是128。 - padding_idx (int, optional) - 可选参数,表示填充索引。在处理序列数据时,我们经常需要将序列填充到相同长度,这时可以使用一个特殊的索引来表示填充。如果设置了
padding_idx
,那么在嵌入层中,这个索引对应的嵌入向量会被初始化为全0,并且在训练过程中不会更新。默认值为None
。 - max_norm (float, optional) - 可选参数,用于指定嵌入向量的最大范数。如果设置了
max_norm
,那么每个嵌入向量的范数(L2范数)将被限制在这个值以内。默认值为None
。 - norm_type (float, optional) - 当
max_norm
被指定时,这个参数决定了范数的类型(只有L2范数是支持的)。默认值为2.0。 - scale_grad_by_freq (bool, optional) - 可选参数,指示是否根据单词在数据中的频率来缩放梯度。如果设置为
True
,那么在反向传播过程中,罕见单词的梯度会相对于常见单词的梯度被放大。默认值为False
。 - sparse (bool, optional) - 可选参数,指示是否使用稀疏梯度。如果设置为
True
,那么在计算梯度时,只有非零的嵌入向量才会被考虑,这可以节省内存和计算资源。默认值为False
。 - _weight (Tensor, optional) - 内部参数,用于指定嵌入层的权重矩阵。通常,这个参数不需要手动设置,因为权重矩阵会在初始化时自动创建。
使用torch.nn.Embedding
创建嵌入层的基本步骤如下: - 确定嵌入层的大小和维度。
- 创建
Embedding
实例。 - 将输入数据(通常是单词的索引)传递给嵌入层,以获取嵌入向量。
下面是一个简单的例子:
import torch
import torch.nn as nn
# 假设词汇表大小为10,000,嵌入向量的维度为128
vocab_size = 10000
embedding_dim = 128
# 创建嵌入层
embedding_layer = nn.Embedding(num_embeddings=vocab_size, embedding_dim=embedding_dim)
# 假设我们有一些输入数据,例如一个批量大小为4的单词索引
input_data = torch.tensor([1, 2, 3, 4], dtype=torch.long)
# 获取嵌入向量
embedded_data = embedding_layer(input_data)
print(embedded_data) # 输出嵌入向量
在这个例子中,我们首先定义了词汇表的大小和嵌入向量的维度,然后创建了一个Embedding
实例。接着,我们创建了一些模拟的输入数据(这里是单词的索引),并通过嵌入层得到了这些单词的嵌入向量表示。
2.Embeddingbag
nn.EmbeddingBag
是 PyTorch 中的一个模块,它用于计算一个“bag”中所有嵌入向量的平均值,这个“bag”是指一系列索引的集合。这在处理变长输入序列时非常有用,例如文本数据中的句子,其中每个单词由一个索引表示。
使用方法:
- 初始化 EmbeddingBag:
你需要指定词汇量(num_embeddings
),嵌入维度(embedding_dim
),以及可选的参数,比如是否使用权重共享(sparse
)等。import torch.nn as nn embedding_bag = nn.EmbeddingBag(num_embeddings=1000, embedding_dim=32, sparse=False)
- 输入数据:
nn.EmbeddingBag
接受两个张量作为输入:一个索引张量和一个偏置张量。索引张量包含所有嵌入索引,偏置张量指定每个嵌入向量的偏置(即位置)。
例如,如果有一个包含两个句子的数据集,其中第一个句子有3个单词,第二个句子有2个单词,那么索引张量可能看起来像这样:
在这个例子中,indices = torch.tensor([1, 2, 4, 5, 6], dtype=torch.long) offsets = torch.tensor([0, 3], dtype=torch.long)
offsets
指明了句子之间的边界:前3个索引属于第一个句子,接下来的2个索引属于第二个句子。 - 前向传播:
将索引和偏置传递给nn.EmbeddingBag
实例进行前向传播:embedded = embedding_bag(indices, offsets)
embedded
张量将包含每个句子的平均嵌入向量。 - 使用嵌入向量:
你可以使用这些嵌入向量作为输入,传递给其他神经网络层,比如全连接层或者循环神经网络(RNN)。# 假设我们有一个全连接层 fc = nn.Linear(32, 10) # 假设我们想要10个类 output = fc(embedded)
- 训练模型:
和其他 PyTorch 模块一样,你需要定义一个损失函数和一个优化器,然后训练你的模型。criterion = nn.CrossEntropyLoss() optimizer = torch.optim.SGD(model.parameters(), lr=0.001) # 在训练循环中 optimizer.zero_grad() output = model(data) loss = criterion(output, target) loss.backward() optimizer.step()
nn.EmbeddingBag
的一个主要优点是,它在计算“bag”中嵌入的平均值时,可以更高效地利用内存和计算资源,尤其是在处理大量短序列时。
三、实操
import torch
import torch.nn as nn
import torch.nn.functional as F
texts=['比较直观的编码方式是采用上面提到的字典序列。例如,对于一个有三个类别的问题,可以用1、2和3分别表示这三个类别。但是,这种编码方式存在一个问题,就是模型可能会错误地认为不同类别之间存在一些顺序或距离关系,而实际上这些关系可能是不存在的或者不具有实际意义的。',
'为了避免这种问题,引入了one-hot编码(也称独热编码)。one-hot编码的基本思想是将每个类别映射到一个向量,其中只有一个元素的值为1,其余元素的值为0。这样,每个类别之间就是相互独立的,不存在顺序或距离关系。例如,对于三个类别的情况,可以使用如下的one-hot编码:']
word_index = {}
index_word = {}
for i, word in enumerate(set("".join(texts))):
word_index[word] = i
index_word[i] = word
# 将文本转化为整数序列
sequences = [[word_index[word] for word in text] for text in texts]
# 获取词汇表大小
vocab_size = len(word_index)
embedding_dim = 8# 嵌入向量的维度
# 创建一个Embedding层
embedding = nn.Embedding(vocab_size, embedding_dim)
# 假设我们有一个包含两个单词索引的输入序列
input_sequence1 = torch.tensor([1,5,8], dtype=torch.long)
input_sequence2 = torch.tensor([2,4], dtype=torch.long)
# 使用Embedding层将输入序列转换为词嵌入
embedded_sequence1 = embedding(input_sequence1)
embedded_sequence2 = embedding(input_sequence2)
print(embedded_sequence1)
print(embedded_sequence2)