Transformer 中 比较晦涩难懂的东西

Transformer 中 比较晦涩难懂的东西

(若文章存在错误,还请读者批评指正、多多包涵)

keras 实现mask

encoder部分[对输入进行操作]
mask = Lambda(lambda x: K.cast(K.greater(K.expand_dims(x, 2), 0), 'float32'))  # 传入[batch, time_step]
# 如果生成的是相加后变得很小而用来减少对softmax影响的可以是这样的mask👇
# mask = Lambda(lambda x: (K.cast(K.greater(K.expand_dims(x, 2), 0), 'float32') - 1) * (1e9))(x)
x = [[1,4,5,1,2,0,0,0,0,0],[1,4,5,1,2,0,0,0,0,0]]  # shape -> [2, 10] == [batch, time_step] 

mask(x)

# output:
<tf.Tensor: shape=(2, 10, 1), dtype=float32, numpy=
array([[[1.],
        [1.],
        [1.],
        [1.],
        [1.],
        [0.],
        [0.],
        [0.],
        [0.],
        [0.]],
       
       [[1.],
        [1.],
        [1.],
        [1.],
        [1.],
        [0.],
        [0.],
        [0.],
        [0.],
        [0.]]], dtype=float32)>

# 得到这个之后,由于维度是(2, 10, 1)
# 当x进入嵌入层之后 维度是(2, 10, word_dim)
# 由于广播机制,x * mask(x) 或者相加 得到的矩阵表现为 padding部分会被影响,其余不会
decoder部分[对注意力矩阵操作]
def mask_martic(k):  
    # 获取常规的mask矩阵
    # 传入通过词嵌入的输入[batch, time_step, word_dim]  
    # 输出[time_step, time_step]
    seq_len = K.shape(k)[-2]
    idxs = K.arange(0, seq_len)  # 创建time_step长度的数组
    mask = idxs[None, :] <= idxs[:, None]  # 增加一个维度 利用对比产生二维矩阵
    mask = K.cast(mask, K.floatx())
    return 1 - mask  # 其实最后要乘上-1e9的,相当于未来数据被遮挡

# 例如:[batch, time_step, word_dim] = [2, 10, 2]
x = tf.constant([[[1., 2],[4, 2],[5, 2],[1, 2],[2, 2],[0, 2],[0, 2],[0, 2],[0, 2],[0, 2]],[[1, 2],[4, 2],[5, 2],[1, 2],[2, 2],[0, 2],[0, 2],[0, 2],[0, 2],[0, 2]]])

mask_matrix(x)

<tf.Tensor: shape=(10, 10), dtype=float32, numpy=
array([[0., 1., 1., 1., 1., 1., 1., 1., 1., 1.],
       [0., 0., 1., 1., 1., 1., 1., 1., 1., 1.],
       [0., 0., 0., 1., 1., 1., 1., 1., 1., 1.],
       [0., 0., 0., 0., 1., 1., 1., 1., 1., 1.],
       [0., 0., 0., 0., 0., 1., 1., 1., 1., 1.],
       [0., 0., 0., 0., 0., 0., 1., 1., 1., 1.],
       [0., 0., 0., 0., 0., 0., 0., 1., 1., 1.],
       [0., 0., 0., 0., 0., 0., 0., 0., 1., 1.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 1.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.]], dtype=float32)>

很幸运能够看到一个十分容易理解的对mask矩阵和attention可解释性的资源

来自知乎

Output的输入

output的输入在训练和预测的时候是完全不一样的,对应的decoder的mask因此也在变化。

  • 训练
    • output输入的是[strat, word1, word2, …, wordn],decoder最终结果是[word1, word2, word3, …, end]
    • 详细点来说,这里的结果是一次性输出的,也就是训练的时候每个词都是并行训练出来的,输出维度是[batch, seq_len, vocabulary_size],交叉熵是使用每一个正确的词id,标记为1的,和训练输出的词中(每一个vocabulary中)对应此id的概率进行交叉熵计算。
  • 预测
    • output输入的一开始是开始标志,所以输入维度(不考虑batch的时候)shape是[batch, 1],然后预测出一次的维度是*(你可能会很奇怪为啥我说的是第一次预测的维度,而不是直接说是一个单词),因为要始终记住一点:decoder的输入和输出的始终是对齐的,对齐的意思是序列长度seq_len是相等的)*[batch, 1],然后把这个1加到过去的这一次输入里边,shape是[batch, 2],然后输出[batch, 2],注意:此时不是吧输出的全部加到上一次的输入中,而是把此处的最后一个词,也就是第二个词加到输入中,…, 如此反复直至预测的最后一个词是结束符。

来自我亲爱的师兄问的问题,让我学到很多很多!!!

transformer参数初始化是怎么样的

随机采样,截尾式正态分布采样结合了采样有界和多样性的两个特点【分别来自均匀分布和正态分布】

大致的思想形成的随机矩阵近似于一个正交矩阵,从而能保证初始阶段模型的稳定性。 Xavier初始化

普通的归一化

μ = 1 m ∑ i m x ( i ) x = x − μ σ 2 = 1 m ∑ i m x ( i ) 2 x = x σ 2 μ = \frac{1}{m} \sum^{m}_{i}x^{(i)} \\x = x - μ \\\sigma^2=\frac{1}{m}\sum^{m}_{i}x^{(i)^{2}} \\x = \frac{x}{\sigma^2} μ=m1imx(i)x=xμσ2=m1imx(i)2x=σ2x

LN公式(俗称横向归一化,一个batch中每一行特征作为一组)

同一个样本所有维度进行归一化。

LN会削弱残差易于训练的效果,也是让梯度消失的“元凶”之一

残差x+f(x)网络设计,x的方差为 σ 1 2 \sigma^2_1 σ12F(x)方差是 σ 2 2 \sigma^2_2 σ22,假设两者相互独立,那么残差网络的方差就是 σ 1 2 + σ 2 2 \sigma_1^2+\sigma_2^2 σ12+σ22,所以残差是放大方差的效果,当我们对应使用Normalization操作的时候(也就是Post Norm结构)导致传递梯度的通道被削弱了。

顺便回顾BN(俗称纵向归一化,一个batch中每一列特征作为一组)

作用:参数搜索问题变得更容易,使超参数的选择更加稳定,产生更大的超参数范围让网络也能工作很好,更容易训练和更深的网络

一般是在每一个层的激活函数之前进行BN,为了避免失去一些特征分布的特性,再一次变化
μ = 1 m ∑ i z ( i )               ( 1 ) σ 2 = 1 m ∑ i m ( z ( i ) − μ )       ( 2 ) z n o r m ( i ) = z ( i ) − μ σ 2 − ϵ            ( 3 ) z ~ ( i ) = γ z n o r m ( i ) + β              ( 4 ) γ   a n d   β   ( t w o   p a r a m s )   a r e   l e a r n a b l e   f r o m   m o d e l   o r   b e   s e t   t o   m a k e   s u r e   t h a t   z ( i )   v a l u e s   h a v e   t h e   r a n g e   o f   v a l u e s   t h a t   w e   w a n t . μ = \frac{1}{m}\sum_{i}z^{(i)}\ \ \ \ \ \ \ \ \ \ \ \ \ (1) \\\sigma^2 = \frac{1}{m}\sum_{i}^{m}(z^{(i)}-μ)\ \ \ \ \ (2) \\z^{(i)}_{norm} = \frac{z^{(i)}-μ}{\sqrt{\sigma^2-\epsilon}}\ \ \ \ \ \ \ \ \ \ (3) \\\tilde{z}^{(i)} = \gamma z^{(i)}_{norm} + \beta\ \ \ \ \ \ \ \ \ \ \ \ (4) \\\gamma\ and\ \beta\ (two\ params)\ are\ learnable\ from\ model\ \\or\ be\ set\ to\ make\ sure\ that\ z^{(i)}\ values\ have\ the\ range\ of\ values\ that\ we\ want. μ=m1iz(i)             (1)σ2=m1im(z(i)μ)     (2)znorm(i)=σ2ϵ z(i)μ          (3)z~(i)=γznorm(i)+β            (4)γ and β (two params) are learnable from model or be set to make sure that z(i) values have the range of values that we want.

dropout放哪里的

transformer原文是这么写的。下方的图中标注应该是对的吧?

We apply dropout to the output of each sub-layer, before it is added to the sub-layer input and normalized. In addition, we apply dropout to the sums of the embeddings and the positional encodings in both the encoder and decoder stacks. For the base model, we use a rate of Pdrop = 0.1.

dropout放哪里的

位置编码有几种,是什么
  • 绝对位置编码

    一般直接加到输入当中

    • 训练式

      如果input_shape(512, 768),则初始化一个同型矩阵作为位置矩阵,随着训练进行更新。

    • 三角式

      《Attention is all you need》中提出的位置编码。

      { p k , 2 i = sin ⁡ ( k / 1000 0 2 i / d ) p k , 2 i + 1 = cos ⁡ ( k / 1000 0 2 i / d ) \left\{ \begin{aligned} p_{k, 2i} & = \sin(k/10000^{2i/d}) \\ p_{k, 2i+1} & = \cos(k/10000^{2i/d}) \\ \end{aligned} \right. {pk,2ipk,2i+1=sin(k/100002i/d)=cos(k/100002i/d)

              indices = K.arange(0, self.output_dim // 2, dtype=K.floatx())
              indices = K.pow(10000.0, -2 * indices / self.output_dim)
              embeddings = tf.einsum('bn,d->bnd', position_ids, indices)
              embeddings = K.stack([K.sin(embeddings), K.cos(embeddings)], axis=-1)
              embeddings = K.reshape(embeddings, (-1, seq_len, self.output_dim))
      

      前半部分是sin,后半部分是cos,然后加到或者**乘(相乘式)**到对应输入中。

    • 递归式

      使用rnn学习一种绝对位置编码,从一个向量 p 0 p_0 p0开始,递归公式为 p k + 1 = f ( p k ) p_{k+1} = f(p_k) pk+1=f(pk)来得到各个位置的编码

    • 相乘式(逐位相乘)

  • 相对位置编码

  • 其他位置编码

pre norm 和 post norm 作用有啥区别

前者更容易训练,因为公式推导之后结果就是把深度减少了,而宽度增加了;但是效果还是后者更好,因为深度比宽度更加重要。

注意力机制中的head_size为什么要64,或者说,多少才合适?
  • 最小化熵角度解释

    这和词向量维度多少才合适是一样的,总结维度公式如下:
    n > 8.33 l o g N n>8.33logN n>8.33logN
    其中,n表示维度,N表示词表长度或者seq长度

为什么要warmup

没有他可能不会收敛,“梯度衡量了输出对输入的依赖程度。如果梯度消失,那么意味着模型的输出对输入的依赖变弱了”,“Warmup是在训练开始阶段,将学习率从0缓增到指定大小,而不是一开始从指定大小训练。如果不进行Wamrup,那么模型一开始就快速地学习,由于梯度消失,模型对越靠后的层越敏感,也就是越靠后的层学习得越快,然后后面的层是以前面的层的输出为输入的,前面的层根本就没学好,所以后面的层虽然学得快,但却是建立在糟糕的输入基础上的。”,“进行Wamrup,那么留给模型足够多的时间进行“预热”,在这个过程中,主要是抑制了后面的层的学习速度,并且给了前面的层更多的优化时间,以促进每个层的同步优化。”

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

JamePrin

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值