【Transformer】学习笔记——Word Embedding词嵌入

个人笔记

首先,文本中的词汇在输入到词嵌入层之前,已经经过了一个数字化的表示,也就是说,传入到词嵌入层的是数字,而文本嵌入层(即词嵌入层)目的就是将文本中词汇的数字表示转变为向量表示,这样可以在高维空间捕捉文本词汇之间的关系。


# 构建Embedding类来实现文本嵌入层
class Embeddings(nn.Module):
    def __init__(self, d_model, vocab):
        # d_model: 词嵌入的维度
        # vocab: 词表的大小
        super(Embeddings, self).__init__()
        # 定义Embedding层
        self.lut = nn.Embedding(vocab, d_model)
        # 将参数传入类中
        self.d_model = d_model

    def forward(self, x):
        # x: 代表输入进模型的文本通过词汇映射后的数字张量
        return self.lut(x) * math.sqrt(self.d_model)

d_model = 512
vocab = 1000

x = torch.LongTensor([[100, 2, 421, 508], [491, 998, 1, 221]]) # 创建了一个包含两个样本的输入张量,每个样本包含四个词汇索引。

emb = Embeddings(d_model, vocab)
embr = emb(x)

print("embr:", embr)
print(embr.shape) # 打印嵌入后的张量和其形状。输出的形状应该是(2, 4, 512),表示有两个样本,每个样本有四个词汇,每个词汇映射到一个512维的向量

问:如何通俗理解这个词嵌入?

假设我们有一个非常简单的词汇表(vocab)和一些句子。词汇表如下:

vocab = {
    "hello": 0,
    "world": 1,
    "this": 2,
    "is": 3,
    "a": 4,
    "test": 5
}

我们想要将以下句子转换为嵌入向量:

sentences = [
    "hello world this is a test",
    "this is another test"
]

首先,我们需要将句子中的每个单词转换为词汇表中的索引。对于上面的句子,索引表示如下:

indexed_sentences = [
    [0, 1, 2, 3, 4, 5],
    [2, 3, 5]
]

现在,我们使用上面的代码来创建一个嵌入层,其中d_model(嵌入向量的维度)设置为4,vocab(词汇表的大小)设置为6(因为有6个单词)。

d_model = 4
vocab = 6

然后,我们创建一个嵌入层的实例,并将其应用于索引化的句子:

emb = Embeddings(d_model, vocab)
x = torch.LongTensor(indexed_sentences)
embr = emb(x)

在嵌入层内部,nn.Embedding会为每个索引查找一个4维的向量(因为我们设置了d_model=4)。例如,索引0(对应单词"hello")可能会映射到一个向量如[0.1, -0.2, 0.3, -0.4]。这个过程对每个索引都会发生,从而将整个句子转换为一个嵌入矩阵。

应用嵌入层后,embr将是一个形状为(2, 6, 4)的张量,其中2是句子的数量,6是每个句子中单词的数量(包括填充),4是嵌入向量的维度。每个单词都被转换为一个4维向量,这些向量可以捕捉单词的语义信息。

最后,我们打印嵌入结果和形状:

print("embr:", embr)
print(embr.shape)

输出可能会是这样的(注意,实际的嵌入向量会由模型随机初始化,并且在训练过程中学习):

embr: tensor([[[ 0.1, -0.2,  0.3, -0.4],
         [ 0.5,  0.6, -0.7,  0.8],
         [ 0.9, -1.0,  1.1, -1.2],
         [-0.1,  0.2, -0.3,  0.4],
         [-0.5,  0.6, -0.7,  0.8],
         [-0.9,  1.0, -1.1,  1.2]],

        [[ 0.9, -1.0,  1.1, -1.2],
         [-0.1,  0.2, -0.3,  0.4],
         [-0.5,  0.6, -0.7,  0.8]]])
embr.shape
torch.Size([2, 6, 4])

这里,每个单词都被转换为一个4维的向量,这些向量可以作为神经网络的输入,用于各种NLP任务。


问:为什么another没有对应索引?

在上面的例子中,我提供了一个简化的词汇表(vocab)和句子,用于说明嵌入层的工作原理。词汇表中只包含了几个单词,而"another"这个单词并没有包含在词汇表中,因此它没有对应的数字索引。

在实际应用中,我们会有一个更全面、包含大量单词的词汇表。如果遇到词汇表中没有的单词,即所谓的"未知单词"(unknown words),有几种处理方法:

  1. 添加到词汇表:如果"another"是一个常见的单词,我们可能会选择将其添加到词汇表中,并为其分配一个新的索引。

  2. 使用特殊标记:在词汇表中,通常会有一个特殊的标记来表示未知单词,例如<UNK>。当遇到未知单词时,我们可以使用这个特殊标记的索引来代替。

  3. 子词处理:在一些高级的NLP模型中,如BERT,可能会使用子词(subword)处理方法。这种方法会将单词分解为更小的单元(子词),并为这些子词创建嵌入。这样,即使遇到未知单词,模型也可以通过识别和组合这些子词来理解单词的含义。

  4. 嵌入层的初始化:在创建嵌入层时,我们可以指定一个额外的嵌入向量用于未知单词。例如,在PyTorch中,nn.Embedding允许我们指定padding_idxunk_idx参数,分别用于填充(padding)和未知单词。

让我们更新一下之前的例子,包括一个未知单词的处理:

# 假设词汇表中添加了<UNK>标记
vocab = {
    "hello": 0,
    "world": 1,
    "this": 2,
    "is": 3,
    "a": 4,
    "test": 5,
    "<UNK>": 6  # 用于未知单词
}

# 索引化句子,遇到未知单词"another"使用<UNK>的索引
indexed_sentences = [
    [0, 1, 2, 3, 4, 5],
    [2, 3, 6, 5]  # "another"用<UNK>的索引6代替
]

# 创建嵌入层实例
emb = Embeddings(d_model, vocab)
x = torch.LongTensor(indexed_sentences)
embr = emb(x)

print("embr:", embr)
print(embr.shape)

在这个更新后的例子中,如果"another"不在词汇表中,我们将其替换为<UNK>标记的索引6,然后嵌入层会为这个索引生成一个嵌入向量。这样,即使遇到未知单词,模型也能够继续处理并生成嵌入表示。


引申:位置编码和位置嵌入
见博文:Transformer学习笔记

词嵌入与位置编码的结合:位置嵌入是将词嵌入与位置编码相加得到的。这种结合使得模型既能够捕捉单词的语义信息,又能够捕捉单词的位置信息。

位置编码的作用:位置编码为模型提供了序列中每个位置的相对或绝对位置信息,这是位置嵌入的基础。

位置嵌入的作用:位置嵌入是模型最终使用的输入表示,它结合了词嵌入的语义信息和位置编码的位置信息,使得模型能够同时处理单词的语义和顺序。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值