编码器-解码器架构
一些编码器-解码器架构的思考
基本ai问题都可以归类为文字生文字(语言大模型),文字生图(midjourney),文字生视频(sora)。
输入和输出都是长度可变的序列,机器翻译语种问题就是这个架构的一个简单的应用。
图?
数学原理就是基本就是把输入序列映射为形状固定的编码状态S,然后映射成长度可变的序列。
编码器应该就是最基本的一个继承自nn.module的具有forward方法的类
解码器有点特殊,是将一个编码状态S,和前一个时间步生成的词元,映射成当前时间步的输出词元,所以初始化的时候需要把编码器的output state存起来
for example:
S(I love you !) 【】 -> Decoder -> 【我】 , t1时间步
S(I love you !) 【我】-> Decoder -> 【爱】,t2时间步
S(I love you!) 【我爱】-> Decoder -> 【我爱你】, t3时间步
S(I love you!) 【我爱你】-> Decoder -> 【我爱你 !】, t4时间步
Demo代码:
1 | from torch import nn |
落地encoder实现,就是序列的开始是以特殊词元 bos,和结束词元 eos 来完成。
1 输入的X:
示例:X = torch.zeros((4, 7), dtype=torch.long)
可以理解为批量为4,长度为7的,数字组成的序列。
【【3,4,5,1,5,7,8】【3,4,5,1,5,7,8】【3,4,5,1,5,7,8】【3,4,5,1,5,7,8】】2 经给embedding的X_emb会变成
示例:
【【【1,2,3,0,5,6】【1,3,4,0,5,6】…】【…】【…】【…】】3 经给 X_rnn = X = X_emb.permute(1, 0, 2)
这里更换成形状 num_steps, batch_size, emb_size,因为始终要按照时间步 t1,t2,t3->…
这样进行forward的,所以每个时间步可以处理多个不同批量的单个词元,这就是变换目的。
说到底,rnn和其变种接受的X形状是(时间步,批量,词表大小)4 output, state=self.rnn(X_rnn)
这个时候就是rnn的表演了。由于state的一开始在rnn里面规定的形状就是nxh,由于layer是多层,所以有多个隐藏状态,所以给出的形状(layer_num, n, h)
而output这里没有添加dens线性层去映射到vocab范围,所以也是nxh,由于每个时间步都有output,所以给出的形状是(n_steps, n, h)
demo代码:
1 | class Seq2SeqEncoder(d2l.Encoder): |
1 上下文变量是如何来?
通过取最后一个隐藏层(batch_size, num_hiddens),即然后赋值n份,以匹配n的批量值。2 怎么拼接?
以下是模拟拼接的代码:1
2
3
4
5
6
7
8state=torch.ones(2,4,3) #模拟一个state, num_layer=2, batch_size=4, hiddens=3
context=state[-1].repeat(5,1,1) #取最后一个layer,repeat steps次,变成(5,4,3)
print(context)
X=torch.rand(4,5,5) #本来是batch_size=4, steps=5, emb=5
X=X.permute(1,0,2) #变成(step=5,batch=4,emb=5)
X_and_context = torch.cat((X, context), 2)#所以对于X变成steps次,和batch,然后emb,而隐藏层本来就是batch x h,最后复制steps次,然后就是emb+h
print(X)
X_and_context可以认为隐藏层的nxh,对每个时间步都跟着X进去了。
落地decoder的实现
1 | class Seq2SeqDecoder(d2l.Decoder): |
后面的loss构建,训练,和评估虽然很重要,但是我感觉现在我无法搞定。