Attention: دسترسی مستقیم Decoder به همهٔ خروجیهای Encoder
در Encoder-Decoder ساده، تمام Sentence ورودی در Final State فشرده میشود. Attention این Bottleneck را حذف میکند. در هر Time Step، Decoder برای هر Output از Encoder یک Weight میسازد، سپس Weighted Sum آنها را بهعنوان Context استفاده میکند. اگر Model در حال ترجمهٔ واژهای مانند «soccer» باشد، Weight مربوط به Representation همان Word باید از بقیه بیشتر شود.
شکل 16-7. Attention: دسترسی مستقیم Decoder به همهٔ خروجیهای Encoder
Weightهای α(t,i) توسط یک شبکهٔ کوچک به نام Alignment Model یا Attention Layer ساخته میشوند. ابتدا برای Pair میان Decoder State و هر Encoder Output یک Score یا Energy محاسبه میشود، سپس Softmax این Scoreها را به Weightهایی تبدیل میکند که مجموعشان 1 است.
Bahdanau Attention و Luong Attention
Bahdanau Attention یا Additive/Concatenative Attention، Encoder Output و Decoder State را Concatenate میکند و با یک شبکهٔ کوچک Score میسازد. Luong Attention یا Multiplicative Attention از Dot Product استفاده میکند؛ این روش سادهتر و روی Hardware مدرن بسیار سریع است.
h̃(t) = Σᵢ α(t,i) y(i)
α(t,i) = exp(e(t,i)) / Σᵢ' exp(e(t,i'))
e(t,i) = h(t)ᵀ y(i) # dot
e(t,i) = h(t)ᵀ W y(i) # general
e(t,i) = vᵀ tanh(W [h(t); y(i)]) # concat
Keras برای Luong Layer به نام Attention و برای Bahdanau Layer به نام AdditiveAttention دارد:
encoder = tf.keras.layers.Bidirectional(
tf.keras.layers.LSTM(256, return_sequences=True, return_state=True))
attention_layer = tf.keras.layers.Attention()
attention_outputs = attention_layer([decoder_outputs, encoder_outputs])
output_layer = tf.keras.layers.Dense(vocab_size, activation="softmax")
Y_proba = output_layer(attention_outputs)
با Attention، Model میتواند Sentenceهای بسیار بلندتری را ترجمه کند.
Attention بهعنوان Memory Retrieval قابل Differentiation
میتوان Attention را مثل Lookup در یک Dictionary پیوسته در نظر گرفت. Query از Decoder میآید؛ Encoder Outputها هم Key و هم Value هستند. Similarity بین Query و Keyها محاسبه میشود، Softmax آنها را به Weight تبدیل میکند و Weighted Sum Valueها Output را میسازد. چون همهٔ Operationها Differentiable هستند، شبکه خودش یاد میگیرد چه چیزی را Query کند و چه Representationهایی را بهعنوان Key/Value بسازد.
Keras Attention Layer میتواند سه Input بگیرد: Query، Key و Value. اگر Value جداگانه داده نشود، همان Keyها بهعنوان Value استفاده میشوند.
Transformer: «Attention Is All You Need»
در مقالهٔ مشهور سال ۲۰۱۷، معماری Transformer بدون RNN و بدون Convolution معرفی شد. این معماری از Attention، Embedding، Dense، Normalization و Skip Connection استفاده میکند. حذف Recurrence چند مزیت دارد: Parallelization آسانتر، Training سریعتر، کاهش مشکل Gradientهای دوربرد و توانایی بهتر برای Capture کردن Dependencyهای طولانی.
شکل 16-8. Transformer: «Attention Is All You Need»
Transformer اصلی یک Encoder در سمت چپ و Decoder در سمت راست دارد. Input و Output Embeddingها Tensorهایی با Shape برابر [batch, sequence length, embedding size] میسازند و این Shape در طول Stack تقریباً ثابت میماند. در نسخهٔ اصلی هر Block شش بار تکرار میشود.
- Encoder Representation هر Word را با توجه به تمام Wordهای Sentence غنی میکند.
- Decoder Representation هر Token ترجمه را طوری Transform میکند که Word بعدی را پیشبینی کند.
- هر Sub-layer با Skip Connection و Layer Normalization احاطه شده است.
- Feed Forward Block از دو Dense Layer تشکیل میشود؛ اولی ReLU و دومی Linear است.
- Output نهایی با Dense+Softmax Probability Vocabulary مقصد را تولید میکند.
Positional Encoding
Transformer Recurrence ندارد، بنابراین بهطور طبیعی Order Tokenها را نمیداند. برای انتقال Position، یک Positional Encoding به Word Embedding اضافه میشود. سادهترین روش استفاده از Embedding قابل Train برای Position است:
max_length = 50
embed_size = 128
pos_embed_layer = tf.keras.layers.Embedding(max_length, embed_size)
positions = tf.range(tf.shape(token_ids)[1])
pos_embeddings = pos_embed_layer(positions)
embeddings = token_embeddings + pos_embeddings
Transformer اصلی بهجای Parameter قابل Train از ترکیب Sin و Cos با Frequencyهای مختلف استفاده میکند:
P(p,2i) = sin(p / 10000^(2i/d))
P(p,2i+1) = cos(p / 10000^(2i/d))
شکل 16-9. Positional Encoding
این Representation باعث میشود Relative Distance بین Positionها Pattern منظم داشته باشد و Model بتواند Sequenceهایی طولانیتر از نمونههای Training را بهتر Generalize کند.
Scaled Dot-Product Attention
قلب Transformer شکل Vectorized از Luong Attention است. سه Matrix داریم: Query با نماد Q، Key با K و Value با V:
Attention(Q,K,V) = softmax(Q Kᵀ / √d_keys) V
تقسیم بر √d_keys Scale Dot Product را کنترل میکند؛ بدون آن، برای Dimensionهای بزرگ Scoreها ممکن است بسیار بزرگ شوند و Softmax وارد ناحیهٔ Saturation شود. در Keras، Attention(use_scale=True) شکل قابل Train از Scale را فعال میکند.
Multi-Head Attention
یک Attention Head ممکن است فقط یک نوع Relation را خوب یاد بگیرد. Multi-Head Attention چند Projection متفاوت از Q، K و V میسازد و Attention را بهصورت Parallel در چند Subspace اجرا میکند. سپس Output Headها Concatenate و با یک Linear Projection ترکیب میشوند.
شکل 16-10. Multi-Head Attention
مثلاً یک Head میتواند Relation میان Verb و Subject را دنبال کند، Head دیگر Reference ضمیرها را پیدا کند و Head دیگری Dependencyهای Long-range را بگیرد. Keras پیادهسازی آماده دارد:
attn_layer = tf.keras.layers.MultiHeadAttention(
num_heads=8,
key_dim=embed_size,
dropout=0.1)
Z = attn_layer(query=Z, value=Z, key=Z,
attention_mask=pad_mask)
Masking در Transformer Encoder
MultiHeadAttention یک attention_mask Boolean با Shape منطقی [batch, query length, value length] میپذیرد. Padding Tokenها باید Mask شوند تا هیچ Query روی آنها Attention ندهد. Broadcasting اجازه میدهد Mask کوچکتر در Dimensionهای مناسب تکرار شود.
Encoder Block شامل Self-Attention، Add+Norm، Feed Forward و Add+Norm است:
for _ in range(N):
skip = Z
Z = tf.keras.layers.MultiHeadAttention(
num_heads=num_heads, key_dim=embed_size,
dropout=dropout_rate)(Z, value=Z, attention_mask=encoder_pad_mask)
Z = tf.keras.layers.LayerNormalization()(
tf.keras.layers.Add()([Z, skip]))
skip = Z
Z = tf.keras.layers.Dense(n_units, activation="relu")(Z)
Z = tf.keras.layers.Dense(embed_size)(Z)
Z = tf.keras.layers.LayerNormalization()(
tf.keras.layers.Add()([Z, skip]))
Causal Mask در Decoder
Decoder باید هنگام پیشبینی Token زمان t فقط Tokenهای قبلی را ببیند. بنابراین Self-Attention اول Decoder از Causal Mask استفاده میکند: Query اول فقط Value اول، Query دوم فقط دو Value اول و به همین ترتیب. علاوه بر Causal Mask، Padding Mask مقصد نیز اعمال میشود. سپس Cross-Attention Decoder Queryها را روی Outputهای Encoder اجرا میکند.
در Cross-Attention، Query از Decoder میآید، ولی Key و Value از Encoder میآیند. این همان Bridge میان Sentence مبدأ و ترجمه است.