torch.nn.embedding()
作者:top_小酱油
链接:https://www.jianshu.com/p/63e7acc5e890
来源:简书
内容:上述是以RNN为基础解析的
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)
意义
该方法定义了一个简单的存储固定大小的词典的嵌入向量的查找表,意思就是说,这就是一个词典,里面包含了各个单词的向量,如果要访问该查找表,需要你给定一个编号,嵌入层就能返回这个编号对应的嵌入向量,嵌入向量反映了各个编号代表的符号之间的语义关系。
注意这里是定义的查找表! 如果要访问还需要后面的操作! 也就是先定义后操作!
当在访问该查找表的时候:
输入为一个编号列表,输出为对应的符号嵌入向量列表。
shape:
- input:(∗),包含提取的编号的intTensor或任意形状的LongTensor。
- output:(∗,H),其中 * 是输入形状,H = embeddding_dim
参数:
- Num_embeddings (int) -词典的大小尺寸,比如总共出现5000个词,那就输入5000。此时index为(0-4999)
- embeddding_dim (int) - 嵌入向量的维度,即用多少维来表示一个符号(单词)。
- padding_idx (int, optional) -填充id,比如,输入长度为100,但是每次的句子长度并不一样,后面就需要用统一的数字填充,而这里就是指定这个数字,这样,网络在遇到填充id时,就不会计算其与其它符号的相关性。(初始化为0)。
- max_norm(float,optional)-最大范数,如果嵌入向量的范数超过了这个界限,就要进行再归一化。
- norm_type(float,optional)-指定利用什么范数计算,并用于对比max_norm,默认为2范数。
- scale_grad_by_freq(boolean,optional)—— 根据单词在mini-batch中出现的频率,对梯度进行放缩。默认为False.
- sparse (bool,optional)-若为True,则与权重矩阵相关的梯度转变为稀疏张量。
变量:
~Embedding.weight (Tensor) –形状为 (num_embeddings, embedding_dim) 的学习权重采用标准正态分布N(0,1)进行初始化
NOTE 1:
请记住,只有有限数量的优化器支持稀疏梯度:目前是optim.SGD(CUDA和CPU), optim.SparseAdam (CUDA和CPU)和optim.Adagrad (CPU)
NOTE 2:
当max_norm≠None时,Embedding的前向方法将就地修改权重张量。由于梯度计算所需的张量不能被就地修改,所以在Embedding上执行可微运算。在调用Embedding的前向方法之前,需要克隆Embedding。max_norm不是None时的权重。例如:
n, d, m = 3, 5, 7
embedding = nn.Embedding(n, d, max_norm=True)
W = torch.randn((m, d), requires_grad=True)
idx = torch.tensor([1, 2])
a = embedding.weight.clone() @ W.t() # weight must be cloned for this to be differentiable
b = embedding(idx) @ W.t() # modifies weight in-place(就地修改权重)
out = (a.unsqueeze(0) + b.unsqueeze(1))
loss = out.sigmoid().prod()
loss.backward()
整个举例:
实际上,Embedding通过随机初始化建立了词向量层后,建立了一个“二维表”,存储了词典中每个词的词向量。每个mini-batch的训练,都要从词向量表找到mini-batch对应的单词的词向量作为模型的输入放进网络。
>>> # an Embedding module 包含了10个张量,每个张量的大小为3
>>> embedding = nn.Embedding(10, 3)
>>> # a batch 含有两个样本,每个样本长度为4,也就是四个索引,索引是词典中的index序号
>>> input = torch.LongTensor([[1,2,4,5],[4,3,2,9]])
>>> embedding(input)
tensor([[[-0.0251, -1.6902, 0.7172],
[-0.6431, 0.0748, 0.6969],
[ 1.4970, 1.3448, -0.9685],
[-0.3677, -2.7265, -0.1685]],
[[ 1.4970, 1.3448, -0.9685],
[ 0.4362, -0.4004, 0.9400],
[-0.6431, 0.0748, 0.6969],
[ 0.9124, -2.3616, 1.1151]]])
>>> # example with padding_idx
>>> embedding = nn.Embedding(10, 3, padding_idx=0)
>>> input = torch.LongTensor([[0,2,0,5]])
>>> embedding(input)
tensor([[[ 0.0000, 0.0000, 0.0000],
[ 0.1535, -2.0309, 0.9315],
[ 0.0000, 0.0000, 0.0000],
[-0.1655, 0.9897, 0.0635]]])
>>> # example of changing `pad` vector
>>> padding_idx = 0
>>> embedding = nn.Embedding(3, 3, padding_idx=padding_idx)
>>> embedding.weight
Parameter containing:
tensor([[ 0.0000, 0.0000, 0.0000],
[-0.7895, -0.7089, -0.0364],
[ 0.6778, 0.5803, 0.2678]], requires_grad=True)
>>> with torch.no_grad():
... embedding.weight[padding_idx] = torch.ones(3)
>>> embedding.weight
Parameter containing:
tensor([[ 1.0000, 1.0000, 1.0000],
[-0.7895, -0.7089, -0.0364],
[ 0.6778, 0.5803, 0.2678]], requires_grad=True)
一些注意的点
- nn.embedding的输入只能是编号,不能是隐藏变量,比如one-hot,或者其它,这种情况,可以自己建一个自定义维度的线性网络层,参数训练可以单独训练或者跟随整个网络一起训练(看实验需要)
- 如果你指定了padding_idx,注意这个padding_idx也是在num_embeddings尺寸内的,比如符号总共有500个,指定了padding_idx,那么num_embeddings应该为501
- embedding_dim的选择要注意,根据自己的符号数量,举个例子,如果你的词典尺寸是1024,那么极限压缩(用二进制表示)也需要10维,再考虑词性之间的相关性,怎么也要在15-20维左右,虽然embedding是用来降维的,但是>- 也要注意这种极限维度,结合实际情况,合理定义
类方法(ClassMethod)
.from_pretrained(embeddings, freeze=True, padding_idx=None, max_norm=None, norm_type=2.0, scale_grad_by_freq=False, sparse=False)
和上面的使用是类似的,也是定义一个查找表!
从给定的2维FloatTensor创建嵌入实例。
参数:
- embeddings (Tensor)-包含嵌入权值的浮点数。第一个维度作为num_embeddings传递给Embedding,第二个维度作为embeding_dim。
- freeze (boolean,optional)-如果为True,张量不会在学习过程中得到更新。相当于embedding.weight。requires_grad = False。默认值:真正的
- padding_idx (int, optional) -如果指定了,则padding_idx上的项不会影响梯度;因此,在训练过程中,padding_idx处的嵌入向量并没有被更新,即它仍然是一个固定的“pad”。
- max_norm (float,optional)-参见模块初始化文档。
- norm_type (float,optional)——请参阅模块初始化文档。默认2。
- scale_grad_by_freq (boolean,optional)-参见模块初始化文档。默认的错误。
- sparse (bool,optional)-参见模块初始化文档。
举例
>>> # FloatTensor containing pretrained weights
>>> weight = torch.FloatTensor([[1, 2.3, 3], [4, 5.1, 6.3]])
>>> embedding = nn.Embedding.from_pretrained(weight)
>>> # Get embeddings for index 1
>>> input = torch.LongTensor([1])
>>> embedding(input)
tensor([[ 4.0000, 5.1000, 6.3000]])
实际代码举例:
if pretrained_word_embedding is None: #如果没有预训练词典,那么就用第一个
self.word_embedding = nn.Embedding(config.num_words,
config.word_embedding_dim,
padding_idx=0)
else: #我们就用预训练的词典
self.word_embedding = nn.Embedding.from_pretrained(
pretrained_word_embedding, freeze=False, padding_idx=0)
if pretrained_entity_embedding is None:
self.entity_embedding = nn.Embedding(config.num_entities,
config.entity_embedding_dim,
padding_idx=0)
else: #实体的嵌入也是一样的,同上
self.entity_embedding = nn.Embedding.from_pretrained(
pretrained_entity_embedding, freeze=False, padding_idx=0)
if config.use_context: #上下文嵌入也是一样的,同上
if pretrained_context_embedding is None:
self.context_embedding = nn.Embedding(
config.num_entities,
config.entity_embedding_dim,
padding_idx=0)
else:
self.context_embedding = nn.Embedding.from_pretrained(
pretrained_context_embedding, freeze=False, padding_idx=0)
上一篇: C#获取文件名和文件路径的两种方法