欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 财经 > 创投人物 > 第N4周:NLP中的文本嵌入

第N4周:NLP中的文本嵌入

2025/3/13 5:03:31 来源:https://blog.csdn.net/weixin_43414521/article/details/145219434  浏览:    关键词:第N4周:NLP中的文本嵌入

  本人往期文章可查阅: 深度学习总结

 

       词嵌入是一种用于自然语言处理(NLP)的技术,用于将单词表示为数字,以便计算机可以处理它们。通俗的讲就是,一种把文本转为数值输入到计算机中的方法。之前文章中提到的将文本转换为字典序列、one-hot编码就是最早期的词嵌入方法。

       Embedding EmbeddingBag 则是PyTorch 中的用来处理文本数据中词嵌入(word embedding)的工具,它们将离散的词汇映射到低维的连续向量空间中,使得词汇之间的语义关系能够在向量空间中得到体现。

1. Embedding 嵌入

        Embedding 是 PyTorch 中最基本的词嵌入操作, Embedding 的输入是一个整数张量,每个整数都代表着一个词汇的索引,输出是一个浮点型的张量,每个浮点数都代表着对应词汇的词嵌入向量。

  • 输入shape: [batch,seqSize] (seqSize为单个文本长度)
  • 输出shape: [batch,seqSize,embed_dim] (embed_dim嵌入维度)

       嵌入层使用随机权重初始化,并将学习数据集中所有词的嵌入。它是一个灵活的层,可以以各种方式使用,如:

  • 它可以用作深度学习模型的一部分,其中嵌入于模型本身一起被学习。
  • 它可以用于加载训练好的词嵌入模型。

嵌入层被定义为网络的第一个隐藏层

函数原型:

import torchtorch.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,_freeze=False,device=None,dtype=None
)

官方API地址:Embedding——PyTorch 2.0 documentation

常见参数:

  • num_embeddings:嵌入的总数,也就是离散变量的类别数。例如在单词嵌入中,就是词汇表的大小,即,最大整数index+1。

  • embedding_dim:每个嵌入向量的维度,即转换后的连续向量的维度。这个维度决定了嵌入向量的表达能力,维度越高,理论上表达能力越强,但计算成本和参数量也会增加。

  • padding_idx(可选):如果设置,那么索引为 padding_idx 的嵌入向量会被初始化为全零向量,并且在训练过程中保持不变。这在处理填充(padding)操作时很有用,比如在处理不同长度的文本序列时,可以用 padding_idx 来填充较短的序列,使其长度一致。

  • max_norm(可选):如果设置,每个嵌入向量的范数会被限制在 max_norm 以内。这有助于控制嵌入向量的大小,避免过大的数值导致数值不稳定等问题。

  • norm_type(可选):与 max_norm 一起使用,指定范数的类型,默认为 2,即 L2 范数。

  • scale_grad_by_freq(可选):如果设置为 True,那么在反向传播时,嵌入向量的梯度会被其频率缩放。这在处理稀疏数据时可能有助于稳定训练。

  • sparse(可选):如果设置为 True,则梯度会被稀疏化,这在某些情况下可以节省内存和计算资源,但可能会导致一些优化器无法使用。

下面是一个简单的例子,用 Embedding 将两个句子转换为词嵌入向量:

1.1 自定义数据集类

import torch
from torch import nn
import torch.nn.functional as F
import torch.optim as optim
from torch.utils.data import DataLoader,Datasetclass MyDataset(Dataset):def __init__(self,texts,labels):self.texts=textsself.labels=labelsdef __len__(self):return len(self.labels)def __getitem__(self,idx):texts=self.texts[idx]labels=self.labels[idx]return texts,labels

1.2 定义填充函数

def collate_batch(batch):texts,labels=zip(*batch)max_len=max(len(text) for text in texts)padded_texts=[F.pad(text,(0,max_len-len(text)),value=0) for text in texts]padded_texts=torch.stack(padded_texts)labels=torch.tensor(labels,dtype=torch.float).unsqueeze(1)return padded_texts,labels

此处定义了一个自定义的批处理函数,用于将数据加载到 DataLoader 时对文本数据进行填充(padding)和标签处理。这个函数的目的是确保每个批次中的文本数据长度一致,并将标签转换为适合模型输入的格式。

  • texts, labels = zip(*batch): 将批次中的数据解包为文本和标签。

  • max_len = max(len(text) for text in texts): 计算批次中所有文本的最大长度。

  • padded_texts: 计算每个文本需要填充的长度:max_len - len(text),使用 F.pad 在文本的右侧填充(padding)零,使其长度与最大长度一致(max_len)。

(1)F.pad: 这是 PyTorch 的 torch.nn.functional.pad 函数,用于对张量进行填充。

(2)text: 这里假设 text 是一个一维张量,表示一个文本序列(例如,经过编码的 input_ids

(3)max_len: 批次中所有文本的最大长度

(4)len(text): 当前文本的长度

(5)value=0: 填充的值为 0

  • padded_texts=torch.stack(padded_texts):将填充后的张量列表堆叠成一个更高维度的张量。这一步是必要的,因为 torch.stack 可以将多个张量沿着一个新的维度堆叠起来,从而形成一个批次化的张量。

       假设 padded_texts 是一个填充后的张量列表,每个张量的形状为 [sequence_length]torch.stack 会将这些张量堆叠成一个更高维度的张量,形状为 [batch_size, sequence_length]

示例:

padded_texts = [torch.tensor([101, 2023, 2003, 102]),torch.tensor([101, 2003, 102, 0]),torch.tensor([101, 2054, 2000, 102])
]

使用 torch.stack(padded_texts) 后,结果为:

tensor([[101, 2023, 2003, 102],[101, 2003, 102,   0],[101, 2054, 2000, 102]
])
  • labels=torch.tensor(labels,dtype=torch.float).unsqueeze(1):将标签转换为浮点型张量,并增加一个维度。这种操作在某些任务中非常常见,尤其是在处理分类任务时,需要将标签转换为适合模型输出的形式。

(1)torch.tensor(labels, dtype=torch.float): 将标签列表转换为浮点型张量。这通常用于二分类任务,其中标签通常是 0 和 1。

(2).unsqueeze(1): 在第 1 维度(索引从 0 开始)增加一个维度。这会将标签从一维张量 [batch_size] 转换为二维张量 [batch_size, 1] 。


为什么需要 .unsqueeze(1)

在某些情况下,模型的输出是一个二维张量,形状为 [batch_size, num_classes],而标签需要与模型输出的形状一致。例如:

  • 在二分类任务中,模型输出的形状可能是 [batch_size, 1]

  • 在多分类任务中,模型输出的形状可能是 [batch_size, num_classes],但标签需要从 [batch_size] 转换为 [batch_size, 1]

通过使用 .unsqueeze(1),可以确保标签的形状与模型输出一致,从而避免形状不匹配的错误。

1.3 准备数据和数据加载器

# 假设我们有以下三个样本,分别由不同数量的单词索引组成
text_data=[torch.tensor([1,1,1,1],dtype=torch.long),  # 样本1torch.tensor([2,2,2],dtype=torch.long),  # 样本2torch.tensor([3,3],dtype=torch.long)   # 样本3
]# 对应的标签
labels=torch.tensor([4,5,6],dtype=torch.float)# 创建数据集和数据加载器
my_dataset=MyDataset(text_data,labels)
data_loader=DataLoader(my_dataset,batch_size=2,shuffle=True,collate_fn=collate_batch)for batch in data_loader:print(batch)

代码输出:

(tensor([[2, 2, 2],[3, 3, 0]]), tensor([[5.],[6.]]))
(tensor([[1, 1, 1, 1]]), tensor([[4.]]))

1.4 定义模型

class EmbeddingModel(nn.Module):def __init__(self,vocab_size,embed_dim):super(EmbeddingModel,self).__init__()self.embedding=nn.Embedding(vocab_size,embed_dim)self.fc=nn.Linear(embed_dim,1) # 假设我们做一个二分类任务def forward(self,text):print("embedding输入文本是:",text)print("embedding输入文本是shape:",text.shape)embedding=self.embedding(text)embedding_mean=embedding.mean(dim=1) # 对每个样本的嵌入向量进行平均print("embedding输出文本shape:",embedding_mean.shape)return self.fc(embedding_mean)

 (1)nn.Embedding(vocab_size, embed_dim): 这是一个嵌入层,将输入的索引序列(text)映射到一个固定维度的嵌入向量

  • vocab_size: 词汇表大小,即输入索引的最大值加1。

  • embed_dim: 嵌入向量的维度。

(2)embedding_mean = embedding.mean(dim=1)

  • 这一步对每个样本的嵌入向量沿着序列长度维度(dim=1)进行平均,得到每个样本的固定长度嵌入表示。

  • 输出的形状为 [batch_size, embed_dim]

(3)nn.Linear(embed_dim, 1)
  • 这是一个全连接层,将嵌入向量映射到输出维度为 1 的结果。

  • 输出的形状为 [batch_size, 1],适用于二分类任务。

特别注意:

       如果使用 embedding_mean=embedding.mean(dim=1) 语句对每个样本的嵌入向量求平均,输出shape为 [batch,embed_dim] 。若注释掉该语句,输出shape则为 [batch,seqSize,embed_dim]

1.5 训练模型

# 示例词典大小和嵌入维度
vocab_size=10
embed_dim=6# 创建模型实例
model=EmbeddingModel(vocab_size,embed_dim)# 定义一个简单的损失函数和优化器
criterion=nn.BCEWithLogitsLoss()
optimizer=optim.SGD(model.parameters(),lr=0.01)# 训练模型
for epoch in range(1): # 训练1个epochfor batch in data_loader:texts,labels=batch# 前向传播outputs=model(texts)loss=criterion(outputs,labels)# 反向传播和优化optimizer.zero_grad()loss.backward()optimizer.step()print(f'Epoch {epoch+1},Loss:{loss.item()}')

代码输出:

embedding输入文本是: tensor([[3, 3, 0],[2, 2, 2]])
embedding输入文本是shape: torch.Size([2, 3])
embedding输出文本shape: torch.Size([2, 6])
embedding输入文本是: tensor([[1, 1, 1, 1]])
embedding输入文本是shape: torch.Size([1, 4])
embedding输出文本shape: torch.Size([1, 6])
Epoch 1,Loss:3.57496976852417

2. EmbeddingBag嵌入

        EmbeddingBag 是在 Embedding 基础上进一步优化的工具,其核心思想是将每个输入序列的嵌入向量进行合并,能够处理可变长度的输入序列,并且减少了计算和存储的开销,并且可以计算句子中所有词汇的词嵌入向量的均值或总和。

       在PyTorch中, EmbeddingBag 的输入是一个 整数张量 和一个 偏移量张量,每个整数都代表着一个词汇的索引,偏移量则表示句子中每个词汇的位置,输出是一个浮点型的张量,每个浮点数都代表着对应句子的词嵌入向量的均值或总和。

  • 输入shape: [seqSize] (seqSize为单个文本长度)
  • 输出shape: [batch, embed_dim] (embed_dim嵌入维度)

假定原始输入数据为: [[1,1,1,1],[2,2,2],[3,3]] 

1. 输入:

  • 输入是一个展平的词汇索引张量( input ),例如 [2,2,2,1,1,1,1] 
  • 对应的偏移量( offsets ),例如 [0,3] ,表示每个样本在展平张量中的起始位置。

2. 合并操作:

  • 根据偏移量,将嵌入向量进行合并操作。
  • 合并操作可以是求和、平均或取最大值,默认是平均 ( mean )。

 函数原型:

torch.nn.EmbeddingBag(num_embeddings=*,embedding_dim,max_norm=None,norm_type=2.0,scale_grad_by_freq=False,mode='mean',sparse=False,_weight=None,include_last_offset=False,padding_idx=None,device=None,dtype=None
)

主要参数:

  •  num_embeddings int ):词典的大小。
  •  embedding_dim int ):每个词向量的维度,即嵌入向量的长度。
  •  mode str ):指定嵌入向量的聚合方式。可选值为 'sum' 'mean' 'max'
    • (假设有一个序列 [2,3,1]  ,每个数字表示一个离散特征的索引,对应的嵌入向量分别为 [[0.1,0.2,0.3],[0.2,0.3,0.4],[0.3,0.4,0.5]]
    •  'sum' :对所有的嵌入向量求和,则使用 'sum' 模式汇总后的嵌入向量为 [0.6,0.9,1.2]
    •  'mean'  :对所有的嵌入向量求平均值,使用 'mean' 模式汇总后的嵌入向量为 [0.2,0.3,0.4]
    •  'max' :对所有的嵌入向量求最大值,使用 'max' 模式汇总后的嵌入向量为 [0.3,0.4,0.5] 

       下面是一个简单的例子,用 EmbeddingBag 将两个句子转换为词嵌入向量并计算它们的均值。

       先放上上周的数据集构建的代码,不清楚的同学可以回过头看看上周的代码。 

2.1 自定义数据集类

import torch
from torch import nn
import torch.nn.functional as F
import torch.optim as optim
from torch.utils.data import DataLoader,Datasetclass MyDataset(Dataset):def __init__(self,texts,labels):self.texts=textsself.labels=labelsdef __len__(self):return len(self.labels)def __getitem__(self,idx):texts=self.texts[idx]labels=self.labels[idx]return texts,labels

2.2 准备数据和数据加载器

# 假设我们有以下三个样本,分别由不同数量的单词索引组成
text_data=[torch.tensor([1,1,1,1],dtype=torch.long),  # 样本1torch.tensor([2,2,2],dtype=torch.long),  # 样本2torch.tensor([3,3],dtype=torch.long)   # 样本3
]# 对应的标签
labels=torch.tensor([4,5,6],dtype=torch.float)# 创建数据集和数据加载器
my_dataset=MyDataset(text_data,labels)
data_loader=DataLoader(my_dataset,batch_size=2,shuffle=True,collate_fn=lambda x:x)for batch in data_loader:print(batch)

2.3 定义模型

class EmbeddingBagModel(nn.Module):def __init__(self,vocab_size,embed_dim):super(EmbeddingBagModel,self).__init__()self.embedding_bag=nn.EmbeddingBag(vocab_size,embed_dim,mode='mean')self.fc=nn.Linear(embed_dim,1)def forward(self,text,offsets):print("embedding_bag输入文本是:",text)print("embedding_bag输入文本shape:",text.shape)embedded=self.embedding_bag(text,offsets)print("embedding_bag输出文本shape:",embedded.shape)return self.fc(embedded)

2.4 训练模型

# 示例词典大小和嵌入维度
vocab_size=10
embed_dim=6# 创建模型实例
model=EmbeddingBagModel(vocab_size,embed_dim)# 定义一个简单的损失函数和优化器
criterion=nn.BCEWithLogitsLoss()
optimizer=optim.SGD(model.parameters(),lr=0.01)# 训练模型
for epoch in range(1): # 训练1个epochfor batch in data_loader:# 将批处理的数据展平并计算偏移量texts,labels=zip(*batch)offsets=[0]+[len(text) for text in texts[:-1]]offsets=torch.tensor(offsets).cumsum(dim=0)texts=torch.cat(texts)labels=torch.tensor(labels).unsqueeze(1)# 前向传播outputs=model(texts,offsets)loss=criterion(outputs,labels)# 反向传播和优化optimizer.zero_grad()loss.backward()optimizer.step()print(f'Epoch {epoch+1},Loss:{loss.item()}')

代码输出:

embedding_bag输入文本是: tensor([2, 2, 2, 1, 1, 1, 1])
embedding_bag输入文本shape: torch.Size([7])
embedding_bag输出文本shape: torch.Size([2, 6])
embedding_bag输入文本是: tensor([3, 3])
embedding_bag输入文本shape: torch.Size([2])
embedding_bag输出文本shape: torch.Size([1, 6])
Epoch 1,Loss:-2.8094725608825684

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词