Pytorch常用的函数(二)pytorch中nn.Embedding原理及使用

3、pytorch中nn.Embedding原理及使用

3.1 词向量介绍

图像数据表达不需要特殊的编码,并且有天生的顺序性和关联性,近似的数字会被认为是近似的特征。

正如图像是由像素组成,语言是由词或字组成,可以把语言转换为词或字表示的集合。

然而,不同于像素的大小天生具有色彩信息,词的数值大小很难表征词的含义。最初,人们为了方便,采用One-Hot编码格式。以一个只有10个不同词的语料库为例(这里只是举个例子,一般中文语料库的字平均在8000 ~ 50000,而词则在几十万左右),我们可以用一个10维的向量表示每个词,该向量在词下标位置的值为1,而其他全部为0。示例如下:

1个词:[1, 0, 0, 0, 0, 0, 0, 0, 0, 0]2个词:[0, 1, 0, 0, 0, 0, 0, 0, 0, 0]3个词:[0, 0, 1, 0, 0, 0, 0, 0, 0, 0]
……
第10个词:[0, 0, 0, 0, 0, 0, 0, 0, 0, 1]
  • 这种词的表示方法十分简单,也很容易实现,解决了分类器难以处理属性(Categorical)数据的问题。

  • 它的缺点也很明显:冗余太多、无法体现词与词之间的关系。可以看到,这10个词的表示,彼此之间都是相互正交的,即任意两个词之间都不相关,并且任何两个词之间的距离也都是一样的。同时,随着词数的增加,One-Hot向量的维度也会急剧增长,如果有3000个不同的词,那么每个One-Hot词向量都是3000维,而且只有一个位置为1,其余位置都是0,。虽然One-Hot编码格式在传统任务上表现出色,但是由于词的维度太高,应用在深度学习上时,常常出现维度灾难,所以在深度学习中一般采用词向量的表示形式。

词向量(Word Vector),也被称为词嵌入(Word Embedding),并没有严格统一的定义。从概念上讲,它是指把一个维数为所有词的数量的高维空间(几万个字,几十万个词)嵌入一个维度低得多的连续向量空间(通常是128或256维)中,每个单词或词组被映射为实数域上的向量。

词向量最重要的特征是相似词的词向量距离相近。每个词的词向量维度都是固定的,每一维都是连续的数。用词向量表示的词,不仅所用维度会变少(由10维变成2维),其中也会包含更合理的语义信息。英文一般是用一个向量表示一个词,也有使用一个向量表示一个字母的情况。中文同样也有一个词或者一个字的词向量表示,与英文采用空格来区分词不同,中文的词与词之间没有间隔,因此如果采用基于词的词向量表示,需要先进行中文分词。

在PyTorch中,针对词向量有一个专门的层nn.Embedding,用来实现词与词向量的映射。nn.Embedding具有一个权重,形状是(num_words,embedding_dim),例如对10个词,每个词用2维向量表征,对应的权重就是一个10 * 2的矩阵。Embedding的输入形状是N * W,N是batch size,W是序列的长度,输出的形状是N * W * embedding_dim。输入必须是LongTensor,FloatTensor必须通过tensor.long()方法转成LongTensor

3.2 nn.Embedding的API解释

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 (python:int) – 词典的大小尺寸,比如总共出现5000个词,那就输入5000。此时index为(0-4999)
  • embedding_dim (python:int) – 嵌入向量的维度,即用多少维来表示一个符号。
  • padding_idx (python:int, optional) – 填充id,比如,输入长度为100,但是每次的句子长度并不一样,后面就需要用统一的数字填充,而这里就是指定这个数字,这样,网络在遇到填充id时,就不会计算其与其它符号的相关性。(初始化为0)
  • max_norm (python:float, optional) – 最大范数,如果嵌入向量的范数超过了这个界限,就要进行再归一化。
  • norm_type (python:float, optional) – 指定利用什么范数计算,并用于对比max_norm,默认为2范数。
  • scale_grad_by_freq (boolean, optional) – 根据单词在mini-batch中出现的频率,对梯度进行放缩。默认为False.
  • sparse (bool, optional) – 若为True,则与权重矩阵相关的梯度转变为稀疏张量。

3.3 nn.Embedding的使用及理解

import torch
from torch.nn import functional as F
from torch import nn

1、embedding官方示例

# an Embedding module containing 10 tensors of size 3
# 构造一个vocab size=10,每个vocab用3-d向量表示的table
embedding = nn.Embedding(10, 3)
print(embedding.weight)
Parameter containing:
tensor([[ 0.6694, -0.8900, -0.1363],
        [ 0.4114,  0.4581, -0.3432],
        [ 0.0981, -0.4604, -0.4365],
        [-0.2197,  0.0687,  0.8132],
        [-0.2669, -0.8082, -0.2576],
        [-0.1423, -1.8045, -0.8497],
        [ 1.1308, -0.3347,  1.1927],
        [-0.4787, -0.3431, -1.1348],
        [ 0.3516,  0.1263, -0.8202],
        [-0.6134,  0.1587, -0.7071]], requires_grad=True)
print(embedding.weight.shape) # 可以看做每行是一个词汇的向量表示!
torch.Size([10, 3])
input = torch.LongTensor([
                          [1,2,4,5],
                          [4,3,2,9]
                         ])  # 表示这批有2个句子,每个句子由4个单词构成

print(input.shape)
torch.Size([2, 4])
output = embedding(input)

# 维度的前两维和input是一致的。可见多了一个第三维,这就是词向量维度。
print(output.shape)

# output = embedding(input)是去embedding.weight中取对应index的词向量!
# 看output的第一行,input处index=1,对应取出weight中index=1的那一行。其实就是按index取词向量!
print(output)
torch.Size([2, 4, 3])
tensor([[[ 0.4114,  0.4581, -0.3432],
         [ 0.0981, -0.4604, -0.4365],
         [-0.2669, -0.8082, -0.2576],
         [-0.1423, -1.8045, -0.8497]],

        [[-0.2669, -0.8082, -0.2576],
         [-0.2197,  0.0687,  0.8132],
         [ 0.0981, -0.4604, -0.4365],
         [-0.6134,  0.1587, -0.7071]]], grad_fn=<EmbeddingBackward0>)

2、embedding计算过程

  • embedding的前向过程其实是一个索引的(即查表)的过程
    • 表的形式是一个矩阵
      • matrix.shape: (v, h)
        -v: vocabulary size
        -h: hidden dimension
    • 具体的索引过程是通过one hot + 矩阵乘法实现
      • input.shape:(b,s)
        • b: batch size
        • s: seq len
      • embedding(input)
        • (b,s) ==> (b,s,h)
        • 即:(b,s) 和 (v, h) ===》 (b,s,h)
# num_classes=vocab size=10
# 进行one-hot变换
input_onehot = F.one_hot(input, num_classes=10)
print(input_onehot.shape) # (b,s,v)
print(input_onehot.dtype)
print(input_onehot)
torch.Size([2, 4, 10])
torch.int64
tensor([[[0, 1, 0, 0, 0, 0, 0, 0, 0, 0],
         [0, 0, 1, 0, 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, 1, 0, 0, 0, 0, 0],
         [0, 0, 0, 1, 0, 0, 0, 0, 0, 0],
         [0, 0, 1, 0, 0, 0, 0, 0, 0, 0],
         [0, 0, 0, 0, 0, 0, 0, 0, 0, 1]]])
print(embedding.weight.dtype)
print(embedding.weight.shape) # (v,h)
torch.float32
torch.Size([10, 3])
# one-hot变换后进行矩阵乘法
output = torch.matmul(input_onehot.type(torch.float32),embedding.weight)

# 可以发现得到的结果和output = embedding(input)一致
# 说明其实质就是one hot + 矩阵乘法  进行实现的
print(output) # (b,s,v) @  (v,h) ===》 (b,s,h)
tensor([[[ 0.4114,  0.4581, -0.3432],
         [ 0.0981, -0.4604, -0.4365],
         [-0.2669, -0.8082, -0.2576],
         [-0.1423, -1.8045, -0.8497]],

        [[-0.2669, -0.8082, -0.2576],
         [-0.2197,  0.0687,  0.8132],
         [ 0.0981, -0.4604, -0.4365],
         [-0.6134,  0.1587, -0.7071]]], grad_fn=<UnsafeViewBackward0>)

猜你喜欢

转载自blog.csdn.net/qq_44665283/article/details/131304681