1. 程式人生 > >pytorch求索(4): 跟著論文《 Attention is All You Need》一步一步實現Attention和Transformer

pytorch求索(4): 跟著論文《 Attention is All You Need》一步一步實現Attention和Transformer

寫在前面

此篇文章是前橋大學大神復現的Attention,本人邊學邊翻譯,借花獻佛。跟著論文一步一步復現Attention和Transformer,敲完以後收貨非常大,加深了理解。如有問題,請留言指出。

import numpy as np
import torch
import torch.nn as nn
import torch.nn.functional as F
import math, copy, time
from torch.autograd import Variable
import matplotlib.pyplot as plt
import seaborn
seaborn.
set_context(context="talk") %matplotlib inline

模型架構

大多數competitive neural sequence transduction models都有encoder-decoder架構(參考論文)。本文中,encoder將符號表示的輸入序列 x 1 ,

, x n x_1, \dots, x_n 對映到一系列連續表示 Z = ( z
1 , , z n ) Z=(z_1, \dots, z_n)
。給定一個z,decoder一次產生一個符號表示的序列輸出 ( y 1 , , y m ) (y_1, \dots, y_m) 。對於每一步來說,模型都是自迴歸的(自迴歸介紹論文),在生成下一個時消耗先前生成的所有符號作為附加輸入。

class EncoderDecoder(nn.Module):
    """
    A stanard Encoder-Decoder architecture.Base fro this and many other models.
    """
    
    def __init__(self, encoder, decoder, src_embed, tgt_embed, generator):
        super(EncoderDecoder, self).__init__()
        self.encoder = encoder
        self.decoder = decoder
        self.src_embed = src_embed
        self.tgt_embed = tgt_embed
        self.generator = generator
        
    def forward(self, src, tgt, src_mask, tgt_mask):
        """ Take in and process masked src and target sequences. """
        return self.decode(self.encode(src, src_mask), src_mask, tgt, tgt_mask)
    
    def encode(self, src, src_mask):
        return self.encoder(self.src_embed(src), src_mask)
    
    def decode(self, memory, src_mask, tgt, tgt_mask):
        return self.decoder(self.tgt_embed(tgt), memory, src_mask, tgt_mask)
class Generator(nn.Module):
    """Define standard linear + softmax generation step."""
    def __init__(self, d_model, vocab):
        super(Generator, self).__init__()
        self.proj = nn.Linear(d_model, vocab)
        
    def forward(self, x):
        return F.log_softmax(self.proj(x), dim=-1)

Transformer這種結構,在encoder和decoder中使用堆疊的self-attention和point-wise全連線層。如下圖的左邊和右邊所示:

Image(filename='images/ModelNet-21.png')

png

Encoder 和 Decoder Stacks

Encoder

編碼器由6個相同的layer堆疊而成

def clones(module, N):
    "Produce N identical layers."
    return nn.ModuleList([copy.deepcopy(module) for _ in range(N)])
class Encoder(nn.Module):
    "Core encoder is a stack of N layers"
    def __init__(self, layer, N):
        super(Encoder, self).__init__()
        self.layers = clones(layer, N)
        self.norm = LayerNorm(layer.size)
        
    def forward(self, x, mask):
        "Pass the input (and mask) through each layer in turn."
        for layer in self.layers:
            x = layer(x, mask)
        return self.norm(x)

這裡在兩個子層中都使用了殘差連線(參考論文),然後緊跟layer normalization(參考論文)

class LayerNorm(nn.Module):
    """ Construct a layernorm model (See citation for details)"""
    def __init__(self, features, eps=1e-6):
        super(LayerNorm, self).__init__()
        self.a_2 = nn.Parameter(torch.ones(features))
        self.b_2 = nn.Parameter(torch.zeros(features))
        self.eps = eps
        
    def forward(self, x):
        mean = x.mean(-1, keepdim=True)
        std = x.std(-1, keepdim=True)
        return self.a_2 * (x - mean) / (std + self.eps) + self.b_2

也就是說,每個子層的輸出是LayerNorm(x + Sublayer(x)),其中Sublayer(x)由子層實現。對於每一個子層,將其新增到子層輸入並進行規範化之前,使用了Dropout(參考論文)

為了方便殘差連線,模型中的所有子層和embedding層輸出維度都是512

class SublayerConnection(nn.Module):
    """ 
    A residual connection followed by a layer norm. Note for 
    code simplicity the norm is first as opposed to last .
    """
    def __init__(self, size, dropout):
        super(SublayerConnection, self).__init__()
        self.norm = LayerNorm(size)
        self.dropout = nn.Dropout(dropout)
        
    def forward(self, x, sublayer):
        """Apply residual connection to any sublayer with the sanme size. """
        return x + self.dropout(sublayer(self.norm(x)))

每層有兩個子層。第一個子層是multi-head self-attention機制,第二層是一個簡單的position-wise全連線前饋神經網路。

class EncoderLayer(nn.Module):
    """Encoder is made up of self-attention and feed forward (defined below)"""
    def __init__(self, size, self_attn, feed_forward, dropout):
        super(EncoderLayer, self).__init__()
        self.self_attn = self_attn
        self.feed_forward = feed_forward
        self.sublayer = clones(SublayerConnection(size, dropout), 2)
        self.size = size
    
    def forward(self, x, mask):
        """Follow Figure 1 (left) for connection """
        x = self.sublayer[0](x, lambda x : self.self_attn(x, x, x, mask))
        return self.sublayer[1](x, self.feed_forward)

Decoder

Decoder由6個相同layer堆成

class Decoder(nn.Module):
    """Generic N layer decoder with masking """
    def __init__(self, layer, N):
        super(Decoder, self).__init__()
        self.layers = clones(layer, N)
        self.norm = LayerNorm(layer.size)
        
    def forward(self, x, memory, src_mask, tgt_mask):
        for layer in self.layers:
            x = layer(x, memory, src_mask, tgt_mask)
        return self.norm(x)

每個encoder層除了兩個子層外,還插入了第三個子層,即在encoder堆的輸出上上執行multi-head注意力作用的層。類似於encoder,在每一個子層後面使用殘差連線,並緊跟norm

class DecoderLayer(nn.Module):
    """Decoder is made of self-attn, src-attn, and feed forward (defined below)"""
    def __init__(self, size, self_attn, src_attn, feed_forward, dropout):
        super(DecoderLayer, self).__init__()
        self.size = size
        self.self_attn = self_attn
        self.src_attn = src_attn
        self.feed_forward = feed_forward
        self.sublayer = clones(SublayerConnection(size, dropout), 3)
        
    def forward(self, x, memory, src_mask, tgt_mask):
        """Follow Figure 1 (right) for connections"""
        m = memory
        x = self.sublayer[0](x, lambda x : self.self_attn(x, x, x, tgt_mask))
        x = self.sublayer[1](x, lambda x : self.src_attn(x, m, m, src_mask))
        return self.sublayer[2](x, self.feed_forward)

修改在decoder層堆中的self-atention 子層,防止位置關注後續位置。masking與使用一個position資訊偏移的輸出embedding相結合,確保對於position i i 的預測僅依賴於小於 i i 的position的輸出

def subsequent_mask(size):
    """Mask out subsequent positions. """
    attn_shape = (1, size, size)
    subsequent_mask = np.triu(np.ones(attn_shape), k=1).astype('uint8')
    return torch.from_numpy(subsequent_mask) == 0
plt.figure(figsize=(5, 5))
plt.imshow(subsequent_mask(20)[0])
None

png

Attention

注意力功能可以看做將一個query和一組key-value對對映到一個output,其中query、keys、values和output都是向量(vector),輸出是values的加權和,其中權重可以通過將query和對應的key輸入到一個compatibility function來計算分配給每一個value的權重。

這裡的attention其實可以叫做“Scaled Dot-Product Attention”。輸入由 d k d_k 維度的queries和keys組成,values的維度是 d v d_v 。計算query和所有keys的點乘,然後除以 d k \sqrt{d_k} ,然後應用softmax函式來獲取值的權重。 d k \sqrt{d_k} 起到調節作用,使得內積不至於太大(太大的話softmax後就非0即1了,不夠“soft”了)。

實際計算中,一次計算一組queries的注意力函式,將其組成一個矩陣 Q Q , 並且keys和values也分別組成矩陣 K K V V 。此時,使用如下公式進行計算:
A t t e n t i o n ( Q , K , V ) = s o f t m a x ( Q K T d k ) V Attention(Q, K, V) = softmax(\frac{QK^T}{\sqrt{d_k}})V

def attention(query, key, value, mask=None, dropout=None):
    """Compute 'Scaled Dot Product Attention ' """
    d_k = query.size(-1)
    scores = torch.matmul(query, key.transpose(-2, -1)) / math.sqrt(d_k) # matmul矩陣相乘
    if mask is not None:
        scores = scores.masked_fill(mask == 0, -1e9)
    p_attn = F.softmax(scores, dim = -1)
    if dropout is not None:
        p_attn = dropout(p_attn)
    return torch.matmul(p_attn, value), p_attn

最常用的兩種注意力實現機制包括: additive attention (cite), and dot-product (multiplicative) attention.
此處的實現是dot-product attention,不過多了 d k \sqrt{d_k} 。additive attention計算函式使用一個但隱藏層的前饋神經網路。
這兩種實現機制在理論上覆雜度是相似的,但是dot-product attention速度更快和更節省空間,因為可以使用高度優化的矩陣乘法來實現。

對於小規模values兩種機制效能類差不多,但是對於大規模的values上,additive attention 效能優於 dot poduct。
原因分析:猜測可能是對於大規模values,內積會巨幅增長,將softmax函式推入有一個極小梯度的區域,造成效能下降(為了說明為什麼內積變大,假設 q k q和k 是獨立且平均值為0方差為1的隨機變數,那麼點乘 q k = i = 1 d k q i k i q*k = \sum^{d_k}_{i=1}q_ik_i

相關推薦

pytorch求索(4): 跟著論文Attention is All You Need實現AttentionTransformer

寫在前面 此篇文章是前橋大學大神復現的Attention,本人邊學邊翻譯,借花獻佛。跟著論文一步一步復現Attention和Transformer,敲完以後收貨非常大,加深了理解。如有問題,請留言指出。 import numpy as np import torch import

#論文閱讀#attention is all you need

ali 計算 str red read required ado 論文 uci Vaswani A, Shazeer N, Parmar N, et al. Attention is all you need[C]//Advances in Neural Infor

Attention is all you need 論文詳解(轉)

一、背景 自從Attention機制在提出之後,加入Attention的Seq2Seq模型在各個任務上都有了提升,所以現在的seq2seq模型指的都是結合rnn和attention的模型。傳統的基於RNN的Seq2Seq模型難以處理長序列的句子,無法實現並行,並且面臨對齊的問題。 所以之後這類模型的發展大

釋出年了,做NLP的還有沒看過這篇論文的嗎?--“Attention is all you need

筆記作者:王小草 日期:2018年10月30日 歡迎關注我的微信公眾號“AI躁動街” 1 Background 說起深度學習和神經網路,影象處理一呼百應的“卷積神經網路CNN“也好,還是自然語言處理得心應手的”迴圈神經網路RNN”,都簡直是膾炙人口、婦孺皆知

論文閱讀-attention-is-all-you-need

都是 所有 for 表示 權重 all osi max forward 1結構介紹 是一個seq2seq的任務模型,將輸入的時間序列轉化為輸出的時間序列。 有encoder和decoder兩個模塊,分別用於編碼和解碼,結合時是將編碼的最後一個輸出 當做 解碼的第一個模塊的輸

Paper Reading - Attention Is All You Need ( NIPS 2017 )

int tput represent enc perf task desc compute .com Link of the Paper: https://arxiv.org/abs/1706.03762 Motivation: The inherently sequen

Attention is all you need及其在TTS中的應用Close to Human Quality TTS with TransformerBERT

ips fas 缺點 不同的 stand 進入 簡單 code shang 論文地址:Attention is you need 序列編碼 深度學習做NLP的方法,基本都是先將句子分詞,然後每個詞轉化為對應的的詞向量序列,每個句子都對應的是一個矩陣\(X=(x_1,x_2,

Attention Is All You NeedTransformer)原理小結

1. 前言 谷歌在2017年發表了一篇論文名字教Attention Is All You Need,提出了一個只基於attention的結構來處理序列模型相關的問題,比如機器翻譯。傳統的神經機器翻譯大都是利用RNN或者CNN來作為encoder-decoder的模型基礎,而谷歌最新的只基於Attention

[閱讀筆記]Attention Is All You Need - Transformer結構

例如 position 頻率 product 結構圖 上一個 預測 獲得 line Transformer 本文介紹了Transformer結構, 是一種encoder-decoder, 用來處理序列問題, 常用在NLP相關問題中. 與傳統的專門處理序列問題的encoder

Attention Is All You Need

本文是對Google2017年發表於NIPS上的論文"Attention is all you need"的閱讀筆記. 對於深度學習中NLP問題,通常是將句子分詞後,轉化詞向量序列,轉為seq2seq問題. RNN方案 採用RNN模型,通常是遞迴地進行

Attention is All You Need -- 淺析

       由於最近bert比較火熱,並且bert的底層網路依舊使用的是transformer,因此再學習bert之前,有必要認真理解一下Transformer的基本原理以及self-attention的過程,本文參考Jay Alammar的一篇博文,翻譯+

TransformerAttention is all you need

nsf 打開 enc 一個 png 分別是 att 參考 for 前言 Transfomer是一種encoder-decoder模型,在機器翻譯領域主要就是通過encoder-decoder即seq2seq,將源語言(x1, x2 ... xn) 通過編碼,再解碼的方式映射

bert之transformerattention is all you need

Attention Is All You Need 自從Attention機制在提出之後,加入Attention的Seq2Seq模型在各個任務上都有了提升,所以現在的seq2seq模型指的都是結合rnn和attention的模型。傳統的基於RNN的Seq2Seq模型難以處理長序列的句子,無法實現

Attention is all you need閱讀筆記

xinxinzhang 每個單元的介紹: 一、add&norm (1).norm(層正則化): 原文:http://blog.csdn.net/zhangjunhit/article/details/53169308 本文主要是針對 batch normaliza

[NIPS2017]Attention is all you need

這篇文章是火遍全宇宙,關於網上的解讀也非常多,將自己看完後的一點小想法也總結一下。 看完一遍之後,有很多疑問,我是針對每個疑問都瞭解清楚後才算明白了這篇文章,可能寫的不到位,只是總結下,下次忘記了便於翻查。 一:Q,K, V 到底是什麼? 在傳統的seq2seq

文讀懂「Attention is All You Need」| 附程式碼實現

前言 2017 年中,有兩篇類似同時也是筆者非常欣賞的論文,分別是 FaceBook 的Convolutional Sequence to Sequence Learning和 Google 的Attention is All You Need,它們都算是 Seq2Se

谷歌機器翻譯Attention is All You Need

通常來說,主流序列傳導模型大多基於 RNN 或 CNN。Google 此次推出的翻譯框架—Transformer 則完全捨棄了 RNN/CNN 結構,從自然語言本身的特性出發,實現了完全基於注意力機制的 Transformer 機器翻譯網路架構。   論文連結:

Day3_attention is all you need 論文閱讀

感覺自己看的一臉懵b; 但看懂了這篇文章要講啥: 以RRN為背景的神經機器翻譯是seq2seq,但這樣帶來的問題是不可以平行計算,拖長時間,除此之外會使得尋找距離遠的單詞之間的依賴關係變得困難。而本文講的Attention機制就很好的解決了這個問題,並且也解決了遠距離之間的依賴關係問題。 前饋神

All you need is attention(Tranformer) --學習筆記

1、回顧 傳統的序列到序列的機器翻譯大都利用RNN或CNN來作為encoder-decoder的模型基礎。實際上傳統機器翻譯基於RNN和CNN進行構建模型時,最關鍵一步就是如何編碼這些句子的序列。往往第一步是先將句子進行分詞,然後每個詞轉化為對應的詞向量,那麼每

Attention all you need

2018年11月05日 14:30:02 聶小閒 閱讀數:4 個人分類: 演算法