Transformer[Attention Is All You Need]
45229 ワード
0. Introduction
TransformerはGoogle Brainチームが2017年に発表した論文で、NLP分野だけでなく、コンピュータビジュアル分野でも大きな影響力を発揮している.Transforemrの主な機能は次のとおりです.
1.Transformerモデルの全体アーキテクチャ
2. Self-Attentnion
3. Encoder, Decoder
1. Transformer Architecture
Transformerは、次の図のようにエンコーダとデコーダで構成されています.エンコーダは2つのサブレイヤからなり、デコーダは3つのサブレイヤからなる.
[出典]:https://arxiv.org/abs/1706.03762
また、エンコーダとデコーダは、下図に示すように積み重ねられており、各サブレイヤは合計6回通過する.
[ソース]:https://cpm0722.github.io/pytorch-implementation/transformer
入力部では、TransformerがRNNを除外しているため、位置情報を含まないことにのみ注目し、Input Embeddingに位置符号化値を追加するすべての値がEncoderとDecoderに入ります.
したがって、Transformerは位置符号化を用いて位置情報を含む.
2. Self-Attention
Transformerの最も重要な部分はこの部分です.一つ一つ詳しく説明します.
位置コードへの入力は、次の図のように複数のヘッド注意層に入ります.Scaled Dot-Productの注目を集めます.
[出典]:https://arxiv.org/abs/1706.03762
まず、「Scaled Dot Product Attention」の前に隣接層を通過するので、Q、K、Vは同じレベルに投影されます.
[ソース]:https://jalammar.github.io/illustrated-transformer/
このときの各Q,K,Vの次元をdk=3 dk=3 dk=3と呼ぶ.ここでQ,K,Vが同じ階層である理由は,自己の意味を再指摘し,最後に同じ文内の他の単語(Token)との注意力scoreを求める必要があるため,Q,K,Vは同じ階層でしかない.最初は同じ文に同じTokenを投射するのでここで、Q、K、Vは、以下の意味を有する.
Attentionは上記のように図で説明します.
Q,K,VともにLinear演算により同一次元に埋め込まれているが,このときの次元をdk=3 dk=3 dk=3 dk=3と呼ぶ.
[出典]:https://cpm0722.github.io/pytorch-implementation/transformer
上の式に従って、まずQにKを乗じる.
[出典]:https://cpm0722.github.io/pytorch-implementation/transformer
乗算とはQとKの関係を求めること,すなわちscoreに注意することを意味する.そこで,ここではKを拡張してQと他のKの関係を求める.
[出典]:https://cpm0722.github.io/pytorch-implementation/transformer
このように,「it」という語と残りの語との関係(注意スコア)は,以下のように解く.
[出典]:https://cpm0722.github.io/pytorch-implementation/transformer
[出典]:https://cpm0722.github.io/pytorch-implementation/transformer
マトリクス乗算のサイズが大きくなると、オーバーフロー(Overflow)または小さな勾配(Slope)の問題が発生し、独自の次元のルート値に分割されます.そこで、「Dot-Product」という言葉にScaledが追加されました.これは、すべての値を標準化することを意味します.
z=x−uσz =\frac{x-u}{\sigma}z=σx−u
標準化された式は次のとおりです.
Q⋅Kdk=Q⋅Kσ\frac{Q\cdot K}{\sqrt{d_k}} =\frac{Q\cdot K}{\sigma}dkQ⋅K=σQ⋅K
同じ数学的証明がある.もしそうであれば、文のすべてのToken(Queryではなく)をQueryとして作成してAttention Scoreを計算することもできます.
[出典]:https://cpm0722.github.io/pytorch-implementation/transformer
[出典]:https://cpm0722.github.io/pytorch-implementation/transformer
上記の結果は,最終注意力値の階層がinputと同じであることを示した.
[出典]:https://cpm0722.github.io/pytorch-implementation/transformer
ここでもう少し拡張しましょう.論文ではMulti-Head Attentionと命名し,headの個数を8個に設定した.
[出典]:https://arxiv.org/abs/1706.03762
Transformerは、エンコーダレイヤごとにScaled Dot Attentionを1回実行するのではなく、h回実行した後に結果を統合する.一つの注意を反映するよりも、いろいろな注意を総合的に反映したほうが結果にいいからです.しかし,h次Scaled Dot Attentiondの実行は計算上あまりにも非効率である.マトリクスの使い方をよく知っているので、最初から入力の次元を8回に増やしました.すなわち、新しい次元をdmodel{model}dmodelと呼ぶと、dmodel=h87 dkd{model}=h*d kdmodel=h87 dkとなる.画像で以下のように表現します.
[出典]:https://cpm0722.github.io/pytorch-implementation/transformer
上記のScaled dot-productは、1つのdmodeld{model}dmodel単位で実行されます.最終的なMulti-Head ATTION層の入出力は以下の通りである.
コードから次のように表示されます.
def cal_attention(q, k, v, d_k, mask=None, dropout=None):
scores = torch.matmul(q, k.transpose(-2, -1)) / math.sqrt(d_k)
if mask is not None:
scores = scores + mask
scores = F.softmax(scores, dim=-1)
if dropout is not None:
scores = dropout(scores)
output = torch.matmul(scores, v)
return output
class MultiHeadAttention(nn.Module):
def __init__(self, hid_dim, n_heads):
super().__init__()
assert hid_dim % n_heads == 0
self.hid_dim = hid_dim # 768
self.n_heads = n_heads # 12
self.head_dim = hid_dim // n_heads ## 64
self.query = nn.Linear(hid_dim, hid_dim)
self.key = nn.Linear(hid_dim, hid_dim)
self.value = nn.Linear(hid_dim, hid_dim)
self.dropout = nn.Dropout(0.1)
def forward(self,hidden_states,attention_mask = None):
# 변수 설명
# hidden_states : BertEmbedding output [2, 9, 784] [Batch size, Max_sequence_length,model size ]
# self.n_heads : multi-head attention에서의 head의 개수 여기서는 head수가 12
# self.head_dim : 하나의 임베딩된 벡터의 차원 여기서는 64이다.
# self.hid_dim : model size를 의미 정확히 말하면 n_heads(헤드수 ) * head_dim(하나의 임베딩된 벡터의 차원)
# 의미 : Q, K, V 자체를 n(batch size) x head_dim가 아닌, n× hid_dim로 생성해내서 한 번의 Self-Attention 계산으로 n× hid_dim의 output을 만들어내게 된다.
# 때문에 Q, K, V를 생성해내기 위한 dembed×head_dim의 weight matrix를 갖는 FC layer를 3∗h개 운용할 필요 없이 hid_dim× hid_dim의 weight matrix를 갖는 FC layer를 3개만 운용하면 된다.
bs = hidden_states.size(0) # batch size를 얻는다.
# Step 1
# Input vector로부터 Query, Key, Value vector 생성
# 그리고 shape를 모두 동일하게 [batch size,max_sequence_length,n_heads,head_dim]으로 통일한다.
q = self.query(hidden_states).view(bs, -1, self.n_heads, self.head_dim)
k = self.key(hidden_states).view(bs, -1, self.n_heads, self.head_dim)
v = self.value(hidden_states).view(bs, -1, self.n_heads, self.head_dim)
# attention score 계산을 위해 shape를 [batch size,n_heads,max_sequence_length,head_dim]로 변경시킨다.
q = q.transpose(1,2) # q shape : torch.Size([2, 12, 9, 64])
k = k.transpose(1,2) # k shape : torch.Size([2, 12, 9, 64])
v = v.transpose(1,2) # v shape : torch.Size([2, 12, 9, 64])
# Step 2. Query · Key (matrix product) 하여 계산
# Step 3. Score를 Key vector 차원수의 제곱근으로 나눔
# Step 4. Softmax 계산
# Step 5. Value vector에 softmax score를 곱함
# attention score를 계산한다.
scores = cal_attention(q,k,v,self.head_dim, attention_mask, self.dropout)
# Permute와 Reshape를 하여 최종적인 output size[batch size, max_sequence,hid_dim]으로 맞춘다.
# concatenate heads and put through final linear layer
concat = scores.transpose(1,2).contiguous().view(bs, -1, self.hid_dim)
return concat
3. Encoder, Decoder
3-1. Encoder
[出典]:https://arxiv.org/abs/1706.03762
エンコーダは、マルチヘッダエージェントとプリアンブル転送(MLP)の2つのサブレイヤから構成される.そして各層の結果に対してLayer NormalizationとResidual接続を行う
ここではLayer NormalizationとResidual Connectionについて詳しく説明しません.簡単に言えば,層正規化をNLPなどの分野で用いることで,特にTransformerなどの高速/並列計算の主な特性において,より優れた性能が得られる.「リアルコネクション」については、層の深さによる勾配の消失の問題を防止するために、かえって試験誤差が増大することが理解される.
マルチヘッド注意事項を紹介したので,位置抽出転送層のみを簡単に紹介した.この層は2つのFC層を有する単純な層であり、マルチヘッド注意と同様に入力形状を保持することを特徴とする.
実装コードは次のとおりです.
class PositionwiseFeedforwardLayer(nn.Module):
def __init__(self, hidden_dim, pf_dim, dropout_ratio):
super().__init__()
self.fc_1 = nn.Linear(hidden_dim, pf_dim)
self.fc_2 = nn.Linear(pf_dim, hidden_dim)
self.dropout = nn.Dropout(dropout_ratio)
def forward(self, x):
# x: [batch_size, seq_len, hidden_dim]
x = self.dropout(torch.relu(self.fc_1(x)))
# x: [batch_size, seq_len, pf_dim]
x = self.fc_2(x)
# x: [batch_size, seq_len, hidden_dim]
return x
[ソースソース]:https://colab.research.google.com/drive/1mt5G4MMneREGuQbaYYIfL_C1cvTTKIK1#scrollTo=0jn4VCWdXhK53-2. Decoder
デコーダは次のように構成されています.
[ソース]:https://arxiv.org/abs/1706.03762
主な特徴は以下の通りです.
一番下のマルチヘッダエージェント層でmaskが使用されるため、現在の時点以降のtokenに対してmaskを加算して単語予測に使用しないようにします.
2番目のmulti-head注意層が入力するQ値は最下位の結果値であり、K、Vはエンコーダ層の最終結果値である.
次の図に示すように、現在の時点以降のtokenはmaskを使用して、次の値の予測を回避します.
[ソース]:https://jalammar.github.io/illustrated-gpt2/
マスクはsoftmax関数を適用する前に完了しなければならないことに注意してください.マスクの仕方を簡単に見ると、まず下三角行列でマスクが生成され、マスク値の0の部分のインデックスに負の値の無限大(実際にはコンピュータでは表現できず、負の値の小さい小数を取る)があり、そうでない部分は元のattention score値を維持します.
Decoderを次のコードに実装します.
class DecoderLayer(nn.Module):
def __init__(self, hidden_dim, n_heads, pf_dim, dropout_ratio, device):
super().__init__()
self.self_attn_layer_norm = nn.LayerNorm(hidden_dim)
self.enc_attn_layer_norm = nn.LayerNorm(hidden_dim)
self.ff_layer_norm = nn.LayerNorm(hidden_dim)
self.self_attention = MultiHeadAttentionLayer(hidden_dim, n_heads, dropout_ratio, device)
self.encoder_attention = MultiHeadAttentionLayer(hidden_dim, n_heads, dropout_ratio, device)
self.positionwise_feedforward = PositionwiseFeedforwardLayer(hidden_dim, pf_dim, dropout_ratio)
self.dropout = nn.Dropout(dropout_ratio)
# 인코더의 출력 값(enc_src)을 어텐션(attention)하는 구조
def forward(self, trg, enc_src, trg_mask, src_mask):
# trg: [batch_size, trg_len, hidden_dim]
# enc_src: [batch_size, src_len, hidden_dim]
# trg_mask: [batch_size, trg_len]
# src_mask: [batch_size, src_len]
# self attention
# 자기 자신에 대하여 어텐션(attention)
_trg, _ = self.self_attention(trg, trg, trg, trg_mask)
# dropout, residual connection and layer norm
trg = self.self_attn_layer_norm(trg + self.dropout(_trg))
# trg: [batch_size, trg_len, hidden_dim]
# encoder attention
# 디코더의 쿼리(Query)를 이용해 인코더를 어텐션(attention)
_trg, attention = self.encoder_attention(trg, enc_src, enc_src, src_mask)
# dropout, residual connection and layer norm
trg = self.enc_attn_layer_norm(trg + self.dropout(_trg))
# trg: [batch_size, trg_len, hidden_dim]
# positionwise feedforward
_trg = self.positionwise_feedforward(trg)
# dropout, residual and layer norm
trg = self.ff_layer_norm(trg + self.dropout(_trg))
# trg: [batch_size, trg_len, hidden_dim]
# attention: [batch_size, n_heads, trg_len, src_len]
return trg, attention
[ソースソース]:https://colab.research.google.com/drive/1mt5G4MMneREGuQbaYYIfL_C1cvTTKIK1#scrollTo=0jn4VCWdXhK5class MaskedSelfAttention(nn.Module):
def __init__(self, config):
super().__init__()
assert config.hidden_size % config.num_attention_heads == 0
self.use_cache = config.use_cache # TRUE
self.hidden_size = config.hidden_size # 768
self.num_attention_heads = config.num_attention_heads # 12
self.head_dim = self.hidden_size // config.num_attention_heads # 64
self.attn = Conv1D(3 * self.hidden_size, self.hidden_size)
self.proj = Conv1D(self.hidden_size, self.hidden_size)
self.attenton_dropout = nn.Dropout(config.attn_pdrop)
self.residual_dropout = nn.Dropout(config.resid_pdrop)
def forward(self, hidden_states, attention_mask=None):
Q, K, V = self.attn(hidden_states).split(self.hidden_size, dim=2)
batch_size = hidden_states.shape[0]
# [batch_size, n_heads, seq_len, head_dim]
Q = Q.view(batch_size, -1, self.num_attention_heads, self.head_dim).permute(0, 2, 1, 3)
K = K.view(batch_size, -1, self.num_attention_heads, self.head_dim).permute(0, 2, 1, 3)
V = V.view(batch_size, -1, self.num_attention_heads, self.head_dim).permute(0, 2, 1, 3)
if self.use_cache is True:
present = (K, V) # 현재 상태 저장
else:
present = None
### Masking 적용 ###
# QK^T / sqrt(d_k) 계산
attention_score = torch.matmul(Q, K.transpose(-2, -1)) / math.sqrt(self.head_dim)
# mask 만들기
Max_seq_len = hidden_states.size(-2)
# mask shape : [1,1,max_seq,max_seq]
mask = torch.tril(torch.ones((Max_seq_len, Max_seq_len), dtype=torch.uint8)).view(1,1,Max_seq_len,Max_seq_len)
# attention_score에서 mask 값이 0인 부분의 위치는 음수의 아주 작은 값(-1e4)으로 채운다
attention_score = torch.where(mask, attention_score,torch.tensor(-1e4))
# softmax 적용
attention_score = F.softmax(attention_score, dim=-1)
# 최종 Scaled Dot-Product Attention
# 1. softmax 적용 결과에 dropout을 수행.
attention_score = self.attenton_dropout(attention_score)
# 2. softmax * V 수행(행렬 곱셈) - Matrix Multiplication
outputs = torch.matmul(attention_score, V)
# outputs size[batch size, max_sequence,hid_dim]에 맞게 변경
outputs = outputs.transpose(1,2).contiguous().view(batch_size, -1, self.hidden_size)
outputs = self.proj(outputs)
outputs = self.residual_dropout(outputs)
outputs = (outputs, present)
return outputs
Positional Encoding
最後にPositional Encodeについて説明して最後にします.前述したようにTransformerは注意力のみに基づいているため,各単語の位置に関する情報はない.そこで、これらの位置情報を含むpositionテーブルを作成します.以下の資料で説明します.
[出典]:https://github.com/ndb796/Deep-Learning-Paper-Review-and-Practice/blob/master/lecture_notes/Transformer.pdf
上図に示すようにposはtokenの位置であり、iは各tokenの階層を表す.従って、上記のsin、cons関数式を代入することにより、右側のテーブルと同じ位置符号化(正確には入力と次元が同じマトリクス)を生成し、位置情報を加えることができる.Transformerの後,位置符号化そのもの(例えばsinとcos)を学習目標とし,Embedding layerを用いて処理した.
実習コードリンク
この練習コードはTransformerのエンコーダに関するマルチヘッドの注意事項である.colab環境で駆動してみることができます.
https://colab.research.google.com/drive/1mAbJBCNpVjK4vQDbuWBTIs4gDq9RsYKa?usp=sharing
参考資料
https://jalammar.github.io/illustrated-gpt2/
https://cpm0722.github.io/pytorch-implementation/transformer
https://jalammar.github.io/illustrated-transformer/
https://arxiv.org/abs/1706.03762
Reference
この問題について(Transformer[Attention Is All You Need]), 我々は、より多くの情報をここで見つけました https://velog.io/@seven7724/TransformerAttention-Is-All-You-Needテキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol