embedding、LSTM、seq2seq+attention的知识总结
作者:互联网
一、 embedding
1. input : [ seqlen , batchsize ]
2. output: [ seq_len, batchsize, embed_dim ]
二、 LSTM
输入:
1. input: [ seq_len, batch, input_size]
2. h0 : [ num_layers * num_directions,batch_size,hidden_size ]
输出:
1. out: [ seq_len, batch, num_directions * hidden_size ]
2. hn : [ num_layers * num_directions, batch, hidden_size ]
三、seq2seq的架构图和维度
因为Encode的h和c会作为Decode的第一次的输入,所以Encode和Decode的LSTM中hiddensize必须一致。
在decode过程中,每次的input是target的每一项,或者是上一次的预测(output),而h和c则一直选取上次的。
但是这里我存在一个问题: 在decode时,我们从target中每次拿一项,循环放进lstm中拿出结果;
但是在encode时,我们直接把整个seqlen都扔到lstm中。
这两种放数据方式存在什么不同吗?
检验代码:
import torch
import torch.nn as nn
bilstm = nn.LSTM(input_size=10, hidden_size=20)
inputs = torch.randn(5, 3, 10) # [seq_len, batch, inputsize]
h0 = torch.randn(1, 3, 20) # layernum , batch , hiddensize
c0 = torch.randn(1, 3, 20)
output, (hn, cn) = bilstm(inputs, (h0, c0))
print('output shape: ', output.shape) # seqlen, batch, hiddensize
print('hn shape: ', hn.shape) # layer , batch, hiddensize
print('cn shape: ', cn.shape)
print(output)
print('----------')
outputs = torch.zeros(5, 3, 20)
seqlen = inputs.size(0)
for i in range(seqlen):
input = torch.Tensor(inputs[i])
input = input.unsqueeze(0)
output, (h0, c0) = bilstm(input, (h0, c0))
outputs[i] = output
print(outputs)
结论:
把一个seqlen的序列直接扔到lstm 和 for循环每次扔一个, 扔seqlen次,最后的结果是一样的。
通过这个例子,我顺便明白了LSTM的反人类设计之:为什么seqlen作为第一个参数?
因为LSTM内部也是通过for循环每次取出seqlen的一个,然后扔进LSTM中运行的,一共循环seqlen次,所以seqlen作为第一个维度比较好做。 (哈哈啊哈啊哈哈我真聪明!)
Pytorch中的torch.cat()函数
C = torch.cat( (A,B),0 ) #按维数0拼接(竖着拼)
C = torch.cat( (A,B),1 ) #按维数1拼接(横着拼)
import torch
>>> A=torch.ones(2,3) #2x3的张量(矩阵)
>>> A
tensor([[ 1., 1., 1.],
[ 1., 1., 1.]])
>>> B=2*torch.ones(4,3) #4x3的张量(矩阵)
>>> B
tensor([[ 2., 2., 2.],
[ 2., 2., 2.],
[ 2., 2., 2.],
[ 2., 2., 2.]])
>>> C=torch.cat((A,B),0) #按维数0(行)拼接
>>> C
tensor([[ 1., 1., 1.],
[ 1., 1., 1.],
[ 2., 2., 2.],
[ 2., 2., 2.],
[ 2., 2., 2.],
[ 2., 2., 2.]])
>>> C.size()
torch.Size([6, 3])
>>> D=2*torch.ones(2,4) #2x4的张量(矩阵)
>>> C=torch.cat((A,D),1)#按维数1(列)拼接
>>> C
tensor([[ 1., 1., 1., 2., 2., 2., 2.],
[ 1., 1., 1., 2., 2., 2., 2.]])
>>> C.size()
torch.Size([2, 7])
Pytorch中的torch.repeat()函数
相当于把某个整体,在某个方向广播。
import torch
x = torch.tensor([1, 2, 3])
print(x.repeat(4, 1))
print("###################################")
print(x.repeat(4, 2, 1))
print("###################################")
print(x.repeat(4, 1, 2))
output:
tensor([[1, 2, 3],
[1, 2, 3],
[1, 2, 3],
[1, 2, 3]])
###################################
tensor([[[1, 2, 3],
[1, 2, 3]],
[[1, 2, 3],
[1, 2, 3]],
[[1, 2, 3],
[1, 2, 3]],
[[1, 2, 3],
[1, 2, 3]]])
###################################
tensor([[[1, 2, 3, 1, 2, 3]],
[[1, 2, 3, 1, 2, 3]],
[[1, 2, 3, 1, 2, 3]],
[[1, 2, 3, 1, 2, 3]]])
Pytorch中的torch.bmm()函数
torch.bmm(input, mat2, out=None) → Tensor
torch.bmm()是tensor中的一个相乘操作,类似于矩阵中的A*B。
input,mat2:两个要进行相乘的tensor结构,两者必须是3D维度的,每个维度中的大小是相同的。
并且相乘的两个矩阵,要满足一定的维度要求:input(p,m,n) * mat2(p,n,a) ->output(p,m,a)。这个要求,可以类比于矩阵相乘。前一个矩阵的列等于后面矩阵的行才可以相乘。
四、初探Attention 机制
实现Attention的方式有很多种,这里展示比较常用的一种。在Encoder的过程中保留每一步RNN单元的隐藏状态h1……hn,组成编码的状态矩阵Encoder_outputs;在解码过程中,原本是通过上一步的输出yt-1和前一个隐藏层h作为输入,现又加入了利用Encoder_outputs计算注意力权重attention_weight的步骤。
相比于原始的Encoder-Decoder模型,加入Attention机制后最大的区别就是它不在要求编码器将所有输入信息都编码进一个固定长度的向量之中。而是,编码器需要将输入编码成一个向量的序列,在解码的时候,每一步都会选择性的从向量序列中挑选一个子集进行进一步处理。这样,在产生每一个输出的时候,都能够做到充分利用输入序列携带的信息。而且这种方法在翻译任务中取得了非常不错的成果。
这是一个基本的attention模型,encoder保存每次的hidden。在decoder端把input传入和隐藏单元拼接起来传入线性层,将其映射到seqlen维,每一维描述的是输入encoder中各位置元素对当前decoder输出单词的重要性占比。然后利用矩阵相乘获取到加权求和之后的注意力向量,用于描述“划了重点”之后的输入序列对当前预测这个单词的影响。然后将注意力向量和input拼接在一起,再利用一个线性层将其映射到RNN的输入维度,最后送入RNN,得到新的hidden和output。output作为下一个的输入。
标签:torch,attention,seqlen,seq2seq,embedding,output,print,input,size 来源: https://blog.csdn.net/weixin_40982849/article/details/120524421