关于torchtext
之前介绍过torchtext,在Pytorch-tutorial-学习(四)中,本以为可以省很多事,但用户体验太差….(也可能是我太菜).
它主要的流程是,先定义一个Field,在定义的同时可以包括各种文本预处理操作,比如分词、padding、初始化
反正我下来体验挺不好的.
所以放弃了,还是老老实实子集处理吧,还能夯实基础
Seq2Seq实验
文本预处理
手上的是中英文文本存在一个text文件中,一行一句话,先是英文,然后用’\t’隔开,然后是中文.
很显然,现在我们这里的源语言是英文,目标语言是中文.
列举我们要做的操作
- 分词
- 去符号
- 构建词表
- 数字化
- 加
、 、
实操记录
实操了一把seq2seq,首先看了一下EncoderRNN的输出,两个变量一个是output一个是hidden.
output的shape是[batch,sequence,hidden_dim],而hidden的形状是[batch,1,hidden_dim]
也就是说,每个时间点的输出都会被记录下来,而hidden表示的最后一个时间点的hidden_state.
还有就是实操验证了一下,hidden等于最后一个output,这也是在之前有提到过的.hidden实际上是与output相等的,只不过每一次我们只记录下了output,而将hidden覆盖了.
1 | a=torch.ones((1,10)).long() |
这张图里包含了o里最后一个时间点的输出和h的值。可以看到两者是相等的.
P.S.一个好玩的现象是,这里的a的形状是(1,10),而不是我本来认为的(1,10,1).当我采用(1,10,1)输入到embedding层中会报错,也就是说,embedding层接受的输入就是二维的形式,且只要把每个batch里的每个单词视为一个标量就行了.(是我多此一举了)
SeqAttention
今天终于把attention的代码实现了,奈何数据量小,而且没有用预训练,效果一般…
总结一下这次遇到的问题.
Embedding层
之前写过embedding的代码,但是还是第一次调用torch里的embedding layer.embedding layer接收的数据形式为(batch_size,sequence_length),本来我以为后面要多一维,后来发现其实这是多此一举.输入时(batch_size,Sequence_length),输出是(batch_size,Sequence_length,hidden_size).可以看到在embedding layer的时候这个维度还是正常的.
Tensor形式
一般我们见到的(CV中)tensor的形式是(batch,channel,w,h),但在pytorch的NLP中并非如此,一些RNN组件默认接受的的tensor形式是(Sequence_length×Batch×hidden_side),也就是说Batch不是第一维度的.当然pytorch也很人性,在RNN组件中增加了Batch_first的设置.所以在使用Pytorch的时候,要注意许多换维度的情况.这里就会用到transpose,transpose(0,1)能将第0维与第1维互换.
Pack_Padded_Sequence和Pad_Packed_Sequence
之前的许多RNN的训练形式是都是用for循环的方式,将数据序列中的数据反复输入倒RNN中.而用Pack_Padded_Sequence和Pad_Packed_Sequence可以简化这个过程.因为我们在预处理的时候,往往会处理一句话将其Pad为等长的序列.所以我们在模型的输入端拿到的就是Padded_Sequence.当要输入到RNN的时候,我们只要将有效长度的数据输入到RNN中即可,而后续的Pad是无效的可以不输入到RNN中.所以我们在输入到RNN之前需要Pack操作,它是对Padded_sequence做压缩操作,压缩到有效长度.而这个有效长度是多少,我们往往会在输入的时候就告诉这个长度是多少(在dataloader的时候会统计).Pack_padded_Sequence希望接收到的一个batch的句子,是经过长度由大到小排序过的,在Pack_padded_sequence中会默认进行排序.在经过RNN之后,我们得到的输出(output)还是Pack_Padded_Sequence形式的数据,此时需要变成tensor形式或者说是带pad的tensor数据通过Pad_Packed_Sequence.(好像发现从RNN中输出的hidden的形式仍是tensor数据,可能是因为hidden一个batch里的数据的sequence_length都是1.)
Attention layer
如果用一般的RNN或许可以用Pack_Padded_Sequence和Pad_Packed_Sequence操作.但是我们要用到Attention,只能用for循环.在Encoder的RNN部分,我们可以采用上述操作.但是在Decoder的RNN部分,由于要计算attention,即将待求状态$h_t$的前一个状态$h_{t-1}$与Encoder的输出output里的每个时间点隐状态进行点积或者其他的一些操作,计算出$h_{t-1}$与Encoder中各个时间点的hidden state的关联程度(权重),然后将其作为一种权重,对Encoder_output进行加权求和参与到求$h_t$的过程中.
在Attention的计算过程中,我们需要输入Decoder_Pre_hidden(1×batch_size×hidden_size),和Encoder_output(sequence_length×batch_size×hidden_size).这里有时会需要加入矩阵变换,但是输入的数据是3维的,我们不能用Linear,因为Linear的输入形式是(batch_size,hidden_size).所以这里我才用的是矩阵运算.直接用matmul(W)
区分bmm,matmul和mm,mul
bmm是在batch上进行的矩阵相乘.(batch_wise).这个操作是batch_first,而且要求两个数据在batch维度上的大小相同.1
2
3
4
510, 3, 4) batch1 = torch.randn(
10, 4, 5) batch2 = torch.randn(
res = torch.bmm(batch1, batch2)
res.size()
torch.Size([10, 3, 5])
matmul则是可以进行自动补全(广播),当A(C×Q×P×M),B(M×N),则A.matmul(B)得到的形式为(C×Q×P×N).
mm则只能进行2维的矩阵乘法操作,A(m×n),B(n×p), A.mm(B)得到形式为m×p
mul则是对应位数字相乘,A(C×Q×P×M),B(C×Q×P×M),则A.mul(B)得到的形式为(C×Q×P×M)
其他
首先是Tanh激活函数
pytorch中这tanh激活函数的输入为(N, ),这里意味着后面的维度是任意的.其输出也是(N, * ).
其次是损失函数,这里用的是log_softmax+NLLLoss,事实上log_softmax+NLLLoss等价于cross_entropy.