【Day23】 Transformer 新手包 (三)

Positional Encoding 怎麽做的

书接昨日,我们说 Positional Encoding 是人工设计的,那它在原本的论文里面是怎麽设计的呢?

WHY?

我们来看看他们给的说法

We chose this function because we hypothesized it would allow the model to easily learn to attend by relative positions, since for any fixed offset k, PE(pos+k) can be represented as a linear function of PE(pos).

  • 翻译过来的意思就是 - 给任一位置 pos 的编码 PE(pos),跟它距离 k 的位置编码 PE(pos + k) 可表示为 PE(pos) 的一个线性函数。

所以作者们认为在 embedding 里加入这样的资讯可以帮助 Transformer 学会 model 序列中的相对位置关系,实际上很难想像出这种操作有用,但反正他们是想到了而且相当有用,不过就算我们无法理解,还是可以从 TensorFlow 官方的实作上面无情的把 Code 复制过来用 XD。

def get_angles(pos, i, d_model):
  angle_rates = 1 / np.power(10000, (2 * (i//2)) / np.float32(d_model))
  return pos * angle_rates

def positional_encoding(position, d_model):
  angle_rads = get_angles(np.arange(position)[:, np.newaxis],
                          np.arange(d_model)[np.newaxis, :],
                          d_model)

  # apply sin to even indices in the array; 2i
  angle_rads[:, 0::2] = np.sin(angle_rads[:, 0::2])

  # apply cos to odd indices in the array; 2i+1
  angle_rads[:, 1::2] = np.cos(angle_rads[:, 1::2])

  pos_encoding = angle_rads[np.newaxis, ...]

  return tf.cast(pos_encoding, dtype=tf.float32)

Mask in Decoder

  • 为了避免 Decoder 在做 Self-Attention 的时候偷看到不该看的未来资讯,在 Transformer 用了两种 masks 来做到这件事,但不管哪种 mask,值为 1 的位置就是mask 存在的地方。

Padding mask

让 Transformer 用来识别序列实际的内容到哪里,因为实际上我们的训练资料长度通常都是不一样的,比较短的会把它补 0 补到一样长,但这些资讯是毫无意义的,我们必须避免让 Transformer 关注到这些位置

  # padding mask 的工作就是把序列中为 0 的位置设为 1
  padding_mask = tf.cast(tf.equal(seq, 0), tf.float32)

Look ahead mask

用来确保 Decoder 在进行自注意力机制时只会关注自己之前的资讯,在 "做 attention" 的时候我们的 q 跟 k 是从嵌入张量 [a1..a4] 得到的,这个张量的维度是一个 2 维矩阵,它看起来可能像是这样

    [[0.01,0.02,0.03]
     [0.04,0.05,0.06]
     [0.07,0.08,0.09]]
     

我们的 Look ahead mask 就会长得像这样

    [[0,1,1]
     [0,0,1]
     [0,0,0]] 
    

写出来就是长这样

  look_ahead_mask = 1 - tf.linalg.band_part(tf.ones((size, size)), -1, 0)
  

实际计算

那实际上是怎麽操作的呢? 这两种 mask 是可以和在一起使用的

# 产生 comined_mask 只要把两个遮罩取大的即可
combined_mask = tf.maximum(padding_mask, look_ahead_mask)

计算一次 attention

def do_attention(q, k, v, mask):
    matmul_qk = tf.matmul(q, k, transpose_b=True) 
    seq_k_length = tf.cast(tf.shape(k)[-1], tf.float32) 
    scaled_attention_logits = matmul_qk / tf.math.sqrt(seq_k_length)  
    
    # 将 mask 「加」到被丢入 softmax 前的 logits
    # 这里的 mask 可以是纯 Padding mask 或是 combined_mask
    if mask != None:
        # (mask * -1e9) 见下方说明
        scaled_attention_logits += (mask * -1e9)

    # 取 softmax 是为了得到总和为 1 的比例之後对 `v` 做加权平均
    attention_weights = tf.nn.softmax(scaled_attention_logits, axis=-1)  
    # 以注意权重对 v 做加权平均(weighted average)
    return tf.matmul(attention_weights, v) 
    

(mask * -1e9) 这件事可以让这被加上极大负值的位置变得无关紧要,(就是有 1 的地方,我们是不想关注的),在经过 softmax 以後值变会趋近於 0。

整理一下 Transformer Decoder 的部分

小结

到此差不多可以准备做看看 Transformer 了,其实还有很多小细节(尤其是资料维度处理) 的以及 Multi-Head Attention 等到实际做的时候再继续讨论吧。

参考资料

Transformer model for language understanding


<<:  Day08:【TypeScript 学起来】物件型别 Object Types : object

>>:  Day 08 - Spring Boot 常用注释(上)

了解 Leetcode: 1627. Graph Connectivity With Threshold 那两层For回圈

再重新叙述一次问题: 假设n = 878,代表说现在给你1到878个点,在没有资讯前,你看每个点都是...

Day18-pytorch(1)认识tensor

tensor简介: tensor是在pytorch运算时所使用的资料型态 就像numpy一样,可支持...

[Day14] 建立订单交易API_7

更新一下get_iv这支程序 def get_iv(nonce): sha_nonce_value ...

Day29. 范例:运输系统 (抽象工厂模式)

本文同步更新於blog 前情提要:铁路运输系统,参考范例:运输系统(工厂方法模式) <?p...

Day29 NiFi 与其他工具的比较

这边我想特别写出这一篇的原因是当初我在学习与操作 NiFi 的过程中,我曾感到一些疑惑,会觉得感觉...