1. nn.Embedding的位置参数
官方文档 Embedding — PyTorch 2.0 documentation
以下正文来源于 关于nn.Embedding的解释,以及它是如何将一句话变成vector的_迷茫终会遇见光的博客-CSDN博客
nn.Embedding((num_embeddings,embedding_dim)
num_embeddings代表词典大小尺寸,int型,比如训练时所可能出现的词语一共50000个词,那么就有num_embedding=50000
embedding_dim表示嵌入向量的维度,int型,具体含义我们下面来解释。
2. embedding_dim的初步解释
现在有一句话(这句话有length个字),如果用one-hot的方式来编码,那我们需要一个(length,50000)的矩阵代表这句话。
那我们现在想用embedding方法来表示,我们指定一个embedding_dim,embedding_dim<50000。
也就是说,我们一共需要(length,embedding_dim)的矩阵代表这句话。相当于(length,50000)*(50000,embedding),做了个矩阵运算。
3. embedding_dim的举例说明
为了方便计算,我们将句子的最大长度设置为max_length (输入模型的所有语句不可能超过这个长度。)
当整个输入数据X只有一句话时 X(1, max_length, num_embeddings)
而字典为(num_embeddings, embedding_dim)
则经过字典翻译之后,这句话变成(1,max_length,embedding_dim)
当输入数据X有多句话时,即Batch_size不等于1,有X(batch_size, max_length, num_embeddings)
字典为仍然为(num_embeddings, embedding_dim)
则经过字典翻译之后,输入数据X变成(batch_size,max_length,embedding_dim)
— — — — — — — — 总结一下— — — — — — — —
nn.embedding(num_embeddings,embedding_dim)的作用就是将输入数据降维到embedding_dim的表示层上,得到了输入数据的另一种表现形式。
在GRU等模型的输入中,可认为是input_size
— — — — — — — — 举个例子— — — — — — — —
import torch.nn as nn
num_embeddings= 4 #词典长度
embedding_dim= 2
embed = nn.Embedding(num_embeddings,embedding_dim)
x = torch.LongTensor([[0,1,2],[3,2,1]])
x_embed = embed(x)
print(x_embed.size()) # torch.Size([2, 3, 2]) 即(batch_size,max_length,embedding_dim)
print(x_embed)结果如下:
— — — — — —
tensor([[[-0.0256, -0.2226],
[-0.5378, -0.1191],
[ 0.1259, -1.6695]],
[[ 0.6608, -0.7670],
[ 0.1259, -1.6695],
[-0.5378, -0.1191]]], grad_fn=<EmbeddingBackward0>)
— — — — — —
使用embedding去发现词隐藏的特征形式,使用神经网络的方法去自行学习?
— — — — — — — — 注意事项— — — — — — — —
num_embeddings表示嵌入的字典个数,如果输入的的是数组,那么num_embeddings至少要比数组中最大的元素要大
否则,会出现IndexError: index out of range in self
比如你的字典50000个词,你来一个输入是50009,这就是超出词典范围了!!!
4. 关于 输入<num_embeddings 的要求
例子来源于nn.Embedding的使用_nn.embedding的输入输出_YoJayC的博客-CSDN博客
— — — — — — — — 第一个位置参数num_embeddings — — — — — — — —
# embedding = nn.Embedding(10, 3) # num_embeddings,这里是10,至少要比输入元素中最大值要大
embedding = nn.Embedding(8, 4) # num_embeddings,这里是8
inputs = torch.LongTensor([[1, 2, 4, 5], [4, 3, 2, 9]])
print(inputs.shape)
outputs = embedding(inputs)
print(outputs.shape)
在上面的代码中,输入数组的最大元素是9。
当设置nn.Embedding(10, 3) 时,能够正常运行,得到输出
torch.Size([2, 4])
torch.Size([2, 4, 3])
当设置nn.Embedding(8, 4)时,报错IndexError: index out of range in self。
— — — — — — — — 第二个位置参数embedding_dim— — — — — — — —
第二个参数embedding_dim表示每一个嵌入向量的大小。
nn.Embedding的输入只能是LongTensor,大小为 (batch_size, sequence_length)
输出的大小为 (batch_size, sequence_length, embedding_dim),即输出在输入后增加了隐藏层的维度
5. 关于Embedding的weight
以下是参考torch.nn.Embedding()详解_Quinn-ntmy的博客-CSDN博客
我们还是继续来看4给出的例子的前半段
— — — — — — — — Embedding.weight为 可学习参数— — — — — — — —
import torch.nn as nn
num_embeddings= 4 #词典长度
embedding_dim= 2
embed = nn.Embedding(num_embeddings,embedding_dim)
print(embed.weight)
— — — — — — 这时我们得到输出:
Parameter containing:
tensor([[-0.2238, -0.5062],
[-1.2614, 1.1866],
[-0.9223, -0.7123],
[-1.5234, -0.4360]], requires_grad=True)
requires_grad=True,所以weight是可学习的。
— — — — — — — Enbedding Layer是如何初始化权重矩阵(即查找表)的??— — — — — — —
更新weight时主要使用了实例方法self.reset_parameters(),而这个实例方法又调用了初始化(init)模块中的normal_方法。
nn.Embedding对应的源码摘录:
class Embedding(Module):
............
if _weight is None:
self.weight = Parameter(torch.empty((num_embeddings, embedding_dim), **factory_kwargs))
self.reset_parameters()
else:
............
def reset_parameters(self) -> None:
init.normal_(self.weight)
............
————————————————
初始化为标准正态分布(均值为0,标准差是1) ,形状为 (num_embeddings, embedding_dim)
我们也可以自己写代码算下均值和方差
torch.mean(embed.weight)
torch.var(embed.weight)
这里由于设置的形状都比较小,所以乍一算起来与0相差比较远
— — — — — —
或者也可以用scipy.stats.shapiro可以检测数据是否符合正态分布。
import torch.nn as nn
num_embeddings= 1000 #词典长度
embedding_dim= 5
embed = nn.Embedding(num_embeddings,embedding_dim)
— — — — — — — —
from scipy.stats import shapiro
print('结果:',shapiro(embed.weight.detach().numpy()))
我们得到:
结果: (0.9997590184211731, 0.8683551549911499)
第一个是p值,越接近于1越显著
————————————————
题外话
对于CNN中的参数:
-可学习的参数:卷积层和全连接层的权重、bias、BatchNorm的 [公式] 等。
-不可学习的参数(超参数):学习率、batch_size、weight_decay、模型的深度宽度分辨率等。
6. nn.Embedding的全部参数
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)
— — — — — — — — 参数说明— — — — — — — —
(1)num_embeddings(int):语料库字典大小;
(2)embedding_dim(int):每个嵌入向量的大小;
(3)padding_idx(int, optional):输出遇到此下标时用零填充;
(4)max_norm(float, optional):重新归一化词嵌入,使它们的范数小于提供的值;
(5)norm_type(float, optional):对应max_norm选项计算p范数时的p,默认值为2;
(上面的4、5两个参数基本不用,通常使用kaiming和xavier初始化参数)
(6)scale_grad_by_freq(boolean, optional):将通过小批量中单词频率的倒数来缩放梯度,默认为False。注意!这里的词频指的是自动获取当前小批量中的词频,而非整个词典;
(7) sparse(bool, optional):如果为True,则与权重矩阵相关的梯度转变为稀疏张量。
稀疏张量指反向传播时只更新当前使用词的权重矩阵,以加快更新速度。但是,即使设置 sparse=True ,权重矩阵也未必稀疏更新,原因如下:
与优化器相关,使用SGD、Adam等优化器时包含momentum项,导致不相关词的Embedding依然会叠加动量,无法稀疏更新;
使用weight_decay,即正则项计入损失值。
基本上通常需要设置的参数是前三个
7. nn.Embedding的可学习性
以下参考Pytorch nn.Embedding的基本使用_nn.embedding使用_iioSnail的博客-CSDN博客
————————训练代码————————
import torch
import torch.nn as nn
num_embeddings= 1000 #词典长度
embedding_dim= 5
embed = nn.Embedding(num_embeddings,embedding_dim)
optimizer = torch.optim.SGD(embed.parameters(), lr=0.1)
criteria = nn.MSELoss()
for i in range(1000):
outputs = embed(torch.LongTensor([0,1,2,3,4]))
loss = criteria(outputs, torch.ones(5, 5))
loss.backward()
optimizer.step()
optimizer.zero_grad()
————————训练结果————————
在上面例子中,我们对nn.Embedding不断的计算损失和梯度下降,让其编码往1的方向靠近(torch.ones),而且前向传播总是编码同一个[0,1,2,3,4]
训练结束后,我们再次尝试使用embedding进行编码:
执行embed(torch.LongTensor([0,1,2,3,4])),得到:
tensor([[0.9996, 0.9999, 0.9999, 0.9994, 0.9996],
[1.0001, 0.9995, 0.9996, 0.9998, 0.9992],
[0.9998, 0.9994, 1.0006, 0.9999, 0.9995],
[0.9993, 0.9998, 0.9997, 0.9998, 0.9995],
[0.9998, 0.9990, 1.0000, 0.9994, 1.0005]],
grad_fn=<EmbeddingBackward0>)
可以看到,经过训练后,embedding的参数发生了变化,把它们都编码成了非常接近1。
————————训练之前————————
但是初始化的时候是这样的,和1没有那么接近:
最开始的时候,执行embed(torch.LongTensor([0,1,2,3,4]))是这样的:
tensor([[ 0.1397, 0.1655, -0.3371, 0.0109, -0.1257],
[-1.0671, 1.1212, 0.4947, 0.5827, -0.7198],
[-0.8692, -0.8575, 0.2471, 0.5753, 1.9026],
[-1.5764, -0.2137, -0.8645, -0.3380, -0.9576],
[-0.0838, 0.6767, -0.6519, 1.3508, 0.6306]],
grad_fn=<EmbeddingBackward0>)