Pytorch 中 Embedding 类详解

在 NLP 领域,可以使用 Pytorch 的 torch.nn.Embeding() 类对数据进行词嵌入预处理。关于词嵌入的解释这里就不做解释咯,不明白的阔以先出门左拐找百度。

重点说下这个 Embeding 类怎么用的。其定义如下:

(class) Embedding(num_embeddings: int, embedding_dim: int, padding_idx: int | None = None, ...)

我这里只显示了三个参数:num_embeddingsembedding_dimpadding_idx,其他的实在不常用就忽略了。这三个参数里最常用的只有前两个参数,也是最容易理解的两个参数,先看下源码中的解释:

num_embeddings (int): size of the dictionary of embeddings
embedding_dim (int): the size of each embedding vector

容易看出,num_embeddings 表示字典的大小,embedding_dim 表示目标词向量的长度。

重点说下第三个参数:padding_idx

这个参数网上的解释比较少,源码中的解释是这样的:

padding_idx (int, optional):
If specified, the entries at :attr:`padding_idx` do not contribute to the gradient;
therefore, the embedding vector at :attr:`padding_idx` is not updated during training,
i.e. it remains as a fixed "pad". For a newly constructed Embedding,
the embedding vector at :attr:`padding_idx` will default to all zeros,
but can be updated to another value to be used as the padding vector.

就是说,这是一个索引值,整数类型。如果这个索引值被设置为了某个整数 padding_idx ,则该索引位置的参数不会随着训练而更新梯度也就一直固定了,进一步的,该位置对生成的词向量的作用是固定的。一般来说会默认把这个索引位置的参数设置为 0 ,也可以自定义为其他值。

一般来说,在词嵌入生成词向量的过程中,这个位置经常用在 PAD(词填充符号)类型的映射上。

举个例子来捋一下:

先看一个一般的例子,没用到 padding_idx 参数。

假设词汇表中共有 10 个词,想把每个词映射为一个 3 维的词向量,输入是一个 (2, 4) 的tensor,姑且认为 batchsize 为 2,句子长度都是 4。词嵌入程序如下:

import torch
embedding = torch.nn.Embedding(10, 3)
input = torch.autograd.Variable(torch.LongTensor([[1,2,3,4],[5,6,7,8]]))  # 原始输入
print(input.size())  # 原始输入的size:torch.Size([2, 4])
wod2vec = embedding(input)  # 词嵌入
print(wod2vec.size())  # 映射为词向量之后的size : torch.Size([2, 4, 3])
print(wod2vec )
print('\n')
print(embedding.weight)

可以打印出词嵌入的结果 wod2vec 以及 词嵌入矩阵的参数 embedding.weight 分别如下:

tensor([[[ 0.8613,  2.2960, -0.1203],
         [-0.1843, -0.2376, -0.7213],
         [-0.3100,  0.2543,  0.7514],
         [-0.5028,  1.1739,  0.3313]],

        [[-0.3697,  0.2790,  0.2608],
         [-0.8860, -0.7625,  1.4434],
         [ 1.1633, -1.4302, -0.9770],
         [-0.1257, -1.6048,  1.1272]]], grad_fn=)

Parameter containing:
tensor([[ 1.1076,  0.6360, -0.6193],
        [ 0.8613,  2.2960, -0.1203],
        [-0.1843, -0.2376, -0.7213],
        [-0.3100,  0.2543,  0.7514],
        [-0.5028,  1.1739,  0.3313],
        [-0.3697,  0.2790,  0.2608],
        [-0.8860, -0.7625,  1.4434],
        [ 1.1633, -1.4302, -0.9770],
        [-0.1257, -1.6048,  1.1272],
        [-0.0954, -0.8601, -0.4030]], requires_grad=True)

容易理解,参数矩阵是一个 (10, 3) 的矩阵。每一行都代表对一个特定字符(或单词)的映射,为了对句子有一个很好地表征,目前情况下,这个参数矩阵在训练过程中是随着梯度不断更新的。

那么问题来了:在数据预处理过程中我们会吧句子设置为固定的长度,当一个句子的长度不够时就需要进行填充处理,假设填充的字符设置为 ,该字符也是字典中的一员。在训练过程中我们又不想让填充的字符影响训练的结果,所以在词嵌入时需要设置填充字符位置的参数不随训练而更新,就是不计算梯度。怎么办呢?这时就可以使用 Embeding 中的 padding_idx 参数了。这个参数顾名思义就是 pad 的索引。假如  的索引是 1,程序如下:

import torch
embedding = torch.nn.Embedding(10, 3, padding_idx = 0)
input = torch.autograd.Variable(torch.LongTensor([[1,2,3,4],[5,6,7,8]]))  # 原始输入
wod2vec  = embedding(input)  # 词嵌入
print(embedding.weight)
""" 输出 """
tensor([[ 0.0000,  0.0000,  0.0000],
        [-0.7544,  0.2482, -0.8668],
        [-0.7597, -0.7937, -0.2294],
        [-1.4075,  0.5869, -0.3124],
        [ 1.7170,  0.0689,  0.3069],
        [ 0.5717, -2.0616, -0.4522],
        [ 0.8203, -0.7685,  0.2621],
        [ 1.8317,  0.9464, -2.0914],
        [ 1.1449, -0.2370, -0.2006],
        [ 0.2089,  0.2276, -1.4627]], requires_grad=True)

可以看出,权重矩阵中代表  的第 0 行的参数全部变成0了,因此  不会对词向量的结果造成影响。后续反向传播更新参数时也不会更新这个位置的参数。这就相当于隔离了  符号。

的索引怎么获取?这就要看用什么数据集了,如果是自制数据集,那你自己肯定知道,如果是 torchtext 中的,可以通过类似下面的代码获取:

src_pad_idx = data['vocab']['src'].vocab.stoi['']

stoi 的作用就是 返回字典中的字符(单词)对应的索引,另外可能还会遇到 itos,就是返回某个索引对应的字典值。

我不是搞 NLP 的,只是用到了相关的知识,学科交叉嘛,不寒掺,点到为止咯。

顺便插播一个笔记:NLP 中的几种常用术语及其意义

PAD:就是 padding ,与 CV 中的 padding 表达的意思类似,将不同的句子处理为相同长度,通常会在前或后补齐,用 pad 填充句子长度不足的部分。在 NLP 训练的过程中, 我们会将数据按 Batch 输入, 但是这些 Batch 必须拥有相同的长度。

UNK:即 unknow ,代表词汇表里不存在的字符, 通常是一些低频词, 或者低频字, 或者特殊符号。

BOSBegin Of Sentence,代表序列的开始。

EOSEnd Of Sentence,代表序列的结束。

你可能感兴趣的:(Pytorch,机器学习,NLP,词嵌入,pytorch)