什么情况下使用Embedding
假设我们需要对单词进行编码,首先容易想到的是one-hot,简单复习一下:
- 首先要建立一个词典,例如:
单词 | 索引 |
---|---|
hello | 0 |
i | 1 |
am | 2 |
… | … |
- 建立好词典后,使用一个与词典一样大小的数组,将要编码的单词对应索引下表改为1即可,例如"am"的编码为
[0,0,1,0,0,...]
,该数组的大小与词典大小一致
one-hot的最大的缺点显而易见:词典有多大,一个单词对应的数组向量的维度就有多大。当然还有其他缺点,例如:词和词之间没有关系,做不到king - queen = man - women
。
所以需要有一个方法可以降维,可以将单词编码成指定的维度,例如,将"am"(3)编码成5维向量[-0.972,0.371,-0.172,0.581,0.134]
Embedding就是干这个事的
nn.Embedding的基本用法
nn.Embeddding
接受两个重要参数:
num_embeddings
:字典的大小。对应上面词典的大小,如果你的词典中包含5000个单词,那么这个字段就填5000embedding_dim
:要将单词编码成多少维的向量
例如,我们现在词典大小为20,现在要对hello, i, am,这三个单词进行编码,想将其编码为5维向量,则对应代码为:
import torch
from torch import nn
embedding = nn.Embedding(20, 5)
embedding(torch.LongTensor([0,1,2]))
tensor([[ 0.4471, 0.3875, -1.0195, -1.1125, 1.3481],
[-1.7230, -0.1964, -0.0420, 0.5782, 0.4514],
[-0.0310, -1.9674, -1.1344, -1.6752, 1.0801]],
grad_fn=<EmbeddingBackward0>)
使用nn.Embedding,将0(hello)编码成了[ 0.4471, 0.3875, -1.0195, -1.1125, 1.3481]
使用注意事项:
- embedding只接受LongTensor类型的数据
- embedding的数据不能大于等于词典大小,例如上面指定了词典大小为20,那么要编码的索引大小就不能>=20
nn.Embedding的其他常用参数
padding_idx
:填充索引,即,如果是这个所以,一律编码为0。有时我们的字典里会增加一项unknown
代表未知的单词,这样我们就可以使用该参数,对所有unknown的单词都编码成0。
embedding = nn.Embedding(20, 5, padding_idx=3) # 加上unknown对应的索引是3
embedding(torch.LongTensor([0,1,2,3,4]))
tensor([[ 0.3824, -0.6734, -2.1156, 1.7065, 1.2072],
[-0.5977, -1.0876, 0.6169, 1.4566, 0.0325],
[ 1.1299, 0.5794, -1.5166, 0.1036, 0.3793],
[ 0.0000, 0.0000, 0.0000, 0.0000, 0.0000],
[-1.0468, 1.3305, -2.1740, -0.5534, -0.1062]],
grad_fn=<EmbeddingBackward0>)
可以看到,3
索引的编码全为0
nn.Embedding的可学习性
nn.Embedding中的参数并不是一成不变的,它也是会参与梯度下降的。也就是更新模型参数也会更新nn.Embedding的参数,或者说nn.Embedding的参数本身也是模型参数的一部分。
举个例子:
embedding = nn.Embedding(20, 5, padding_idx=3) # 对3不进行编码
optimizer = torch.optim.SGD(embedding.parameters(), lr=0.1)
criteria = nn.MSELoss()
for i in range(1000):
outputs = embedding(torch.LongTensor([0,1,2,3,4]))
loss = criteria(outputs, torch.ones(5, 5))
loss.backward()
optimizer.step()
optimizer.zero_grad()
在上面例子中,我对nn.Embedding
不断的计算损失和梯度下降,让其编码往1的方向靠近,训练结束后,我们再次尝试使用embedding
进行编码:
embedding(torch.LongTensor([0,1,2,3,4]))
输出为:
tensor([[1.0004, 0.9999, 1.0000, 1.0000, 1.0000],
[0.9999, 0.9996, 0.9997, 0.9999, 0.9996],
[0.9999, 0.9996, 0.9994, 0.9993, 0.9991],
[0.0000, 0.0000, 0.0000, 0.0000, 0.0000],
[0.9991, 0.9999, 0.9999, 0.9998, 0.9997]],
grad_fn=<EmbeddingBackward0>)
可以看到,经过训练后,embedding的参数发生了变化,把它们都编码成了1。
参考资料
nn.Embedding官方文档:https://pytorch.org/docs/stable/generated/torch.nn.Embedding.html