本周报名学习了datawhale的团队学习课程,从毕业到现在工作有六年的时间了,大部分时间都被忙于做业务,读代码,写代码,理论方面的思考较少,这次希望做回学生,把cv知识当作新知识重新认识。看参考资料中的datawhale总结的知识点已经非常全面了,笔记里面就不做过多的证明,只对尚未证明的思考题做一些自己的理解。
transformer自从问世以来得到很多关注,但是作为一个cv从业人员理解起来会有“天然”的难度,我觉得有两点,模型结构,输入数据格式。本篇笔记也就从这两点出发,结合datawhale的笔记内容来谈谈。
一 模型结构
1. Encoder
Encoder是由若干基本单元堆叠组成的,个人认为是增加深度语义信息的表达能力。基本单元中包含attention层,feed forward层和残差、归一化层。其中attention层和feed层使用相同的残差、归一化层。在教程中有很优雅的代码实现。
2. Decoder
Decoder与Encoder非常相似,不同在于attention层的使用,经过自注意力机制和encoder输入的注意力机制分别经过残差层再经过feed层结束。
二 输入输出格式
transformer训练和推断部分理解困难一大部分原因是输入输出的格式复杂导致的。我们首先聚焦输入格式,参考datawhale[1]中的代码:
1. 输入格式
position encoding + embedding 词表
这里来详细学习一下位置编码的理论公式和pytorch实现,位置编码实质上是一个“二维”频谱图,两个坐标分别是当前时刻P和当前维度D。固定P,i在[0, d_model]变化,从而分母在[1, 10000]间变化,d在奇数和偶数位置sin和cos函数相差90度相位;固定D,指数数值不变,pos即分子在[0, T]区间变化。
公式:
代码:
class PositionalEncoding(nn.Module):
def __init__(self, d_model, dropout, max_len=5000):
"""
位置编码器类的初始化函数
共有三个参数,分别是
d_model:词嵌入维度
dropout: dropout触发比率
max_len:每个句子的最大长度
"""
super(PositionalEncoding, self).__init__()
self.dropout = nn.Dropout(p=dropout)
# Compute the positional encodings
# 这样计算是为了避免中间的数值计算结果超出float的范围,
##### positional encoding 的 torch 核心逻辑 #####
pe = torch.zeros(max_len, d_model)
position = torch.arange(0, max_len).unsqueeze(1)
div_term = torch.exp(torch.arange(0, d_model, 2) *
-(math.log(10000.0) / d_model))
pe[:, 0::2] = torch.sin(position * div_term)
pe[:, 1::2] = torch.cos(position * div_term)
pe = pe.unsqueeze(0)
##### positional encoding 的 torch 核心逻辑 #####
self.register_buffer('pe', pe)
def forward(self, x):
x = x + Variable(self.pe[:, :x.size(1)], requires_grad=False)
return self.dropout(x)
2. 输出格式
解码器通过mask 获得当前时间T可以学习的序列做loss。最终的输出为softmax后的词表probability,通过greedy的方式(argmax) 求出解码序列。这里通过mask 连接输入输出,并且构建mask的方法需要跑完源码才能理解更深入,列入TODO list.
参考资料