قراية
وضع الحكايات

آلية الـ attention (الانتباه)

~24 دقيقة قراية الدرس 1 من 3 في الوحدة 9

من الـ vectors الثابتة للبحث الديناميكي

في آخر الوحدة التامنة، شفنا إزاي architecture الـ encoder–decoder بتضغط الـ input sequence كلها في context vector واحد حجمه ثابت، وبعدين بتستخدم الـ vector ده عشان تبدأ بيه الـ decoder. التصميم ده أنيق بس فيه عنق زجاجة أساسي: لـ input sequence طويلة، كل تفاصيل المعنى لازم تتضغط في vector فيه يمكن 512 رقم. والمعلومات المهمة بتضيع حتمًا كل ما طول الـ sequence يكبر، وجودة الترجمة بتقع معاها.

وآلية الـ attention (الانتباه)، اللي قدّمها بهداناو وتشو وبنجيو سنة 2015، حلّت المشكلة دي بإنها سابت عنق زجاجة الـ vector الواحد خالص. بدل ما الـ input يتضغط مرة واحدة وللأبد، الـ decoder بياخد وصول مباشر لكل hidden state في الـ encoder في كل خطوة decoding. فهو بيركّز انتباهه على الحالات الأكتر صلة بتوليد الـ output token الحالي، وبينتبه لأماكن أكتر من غيرها. وعملية البحث الناعمة اللي ينفع تتفاضل دي طلعت من أكتر الأفكار تأثيرًا في الـ Machine Learning الحديث.

الدرس ده بيبني آلية الـ attention من الأساسيات: الإحساس ورا الاسترجاع الموزون، والصياغة الرسمية بتاعة الـ query–key–value، والـ scaled dot-product attention، والـ multi-head attention، والـ self-attention، والـ cross-attention. والأفكار دي هي المفردات الكاملة اللي محتاجها عشان تفهم architecture الـ transformer في الدرس الجاي.

الـ attention كاسترجاع موزون للمعلومات

تخيّل مكتبة فيها N كتاب (hidden states الـ encoder h1, …, hN). إنت جاي بسؤال (حالة الـ decoder الحالية st) وعايز ترجّع المعلومات الأكتر صلة. الطريقة الساذجة إنك تقرا كل الكتب بنفس الوزن. والطريقة الأحسن إنك تدّي كل كتاب درجة صلة، وتعمل normalization للدرجات دي في توزيع احتمالي، وتعمل ملخّص موزون للمحتوى — فتقرا الكتب الأكتر صلة بتركيز وتقلّب الباقي بسرعة.

ودي بالظبط اللي الـ attention بيعمله. درجة الـ alignment etj بتقيس حالة الـ decoder الحالية بتطابق حالة الـ encoder hj قد إيه. وتطبيق softmax على كل درجات الـ alignment بيطلّع attention weights αtj مجموعها 1 على كل أماكن الـ encoder j. والـ context vector ct هو المجموع الموزون لحالات الـ encoder بالـ weights دي. وبعدين الـ decoder بيولّد الـ output token الجاي باستخدام الـ hidden state بتاعته st والـ context vector ده ct مع بعض.

الـ Bahdanau attention
\begin{aligned}e_{tj} &= a(s_{t-1},h_j)\\ \alpha_{tj} &= \frac{\exp(e_{tj})}{\sum_{k=1}^{N}\exp(e_{tk})}\\ c_t &= \sum_{j=1}^{N}\alpha_{tj}\,h_j\end{aligned}
درجة الـ alignment etj بتتحسب بـ feedforward network صغيرة a() بتاخد حالة الـ decoder اللي فاتت st−1 وحالة الـ encoder hj. والـ softmax بتعمل normalization للدرجات في weights αtj. والـ context vector ct هو المجموع الموزون لكل حالات الـ encoder، فالوزن بيتركّز على الأماكن الأكتر صلة. وparameters الـ a() بتتعلّم مع باقي الـ model.

والأهم من كده، إن الـ attention weights بتتفاضل: الـ gradients بترجع لورا على الـ softmax وشبكة الـ alignment، فبتدرّب الـ model يطلّع patterns attention كويسة من غير أي إشراف صريح على فين يبص. الـ model بيتعلّم أنهي أماكن تهمّه من هدف المهمة بس (زي التنبؤ بالـ token الجاي أو دقة الترجمة).

الـ soft attention قصاد الـ hard attention

الـ attention على طريقة بهداناو هو soft attention (انتباه ناعم): الـ context vector متوسط موزون على كل الأماكن، فكل حالة encoder بتساهم (بـ weights مختلفة). والـ hard attention (الانتباه الصلب) بيختار مكان واحد بالظبط في كل خطوة، ودي عملية متقطعة مبتتفاضلش. الـ hard attention أسهل في التفسير، بس تدريبه محتاج reinforcement learning أو حيلة REINFORCE، وده أقل استقرارًا. والـ soft attention هو الطريقة المسيطرة لأنه بيتفاضل تمامًا وبيركب مع الـ backpropagation بنعومة.

تجريد الـ query–key–value

صياغة بهداناو الأصلية استخدمت شبكة alignment بتتعلّم عشان تحسب درجات الصلة. وورقة فاسواني وزمايله (2017) — ورقة “Attention Is All You Need” — أعادت صياغة الـ attention بشكل أعم وأكفأ حسابيًا باستخدام تلات linear projections: الـ queries (الاستعلامات) (Q)، والـ keys (المفاتيح) (K)، والـ values (القيم) (V).

والإحساس جاي من استرجاع المعلومات: لما بتدوّر في database، الـ query بتاعك بيتقارن بـ keys في الـ database، والـ keys اللي بتطابق بترجّع الـ values المرتبطة بيها. وفي الـ neural attention، التلاتة vectors مستمرة الـ model بيتعلّمها. كل مكان input بيطلّع key (أنا فيّ إيه) وvalue (أرجّع إيه لو اتختارت). وكل مكان query بيسأل (أنا بدوّر على إيه؟). والصلة بتتقاس بالـ dot product بين query وكل key. والـ output هو المجموع الموزون للـ values، والـ weights جاية من تشابهات الـ query–key.

وبشكل ملموس، لو عندك matrix الـ input X (شكلها: طول الـ sequence × بُعد الـ model dmodel)، تلات matrices weights بتتعلّم WQ, WK, WV بتعمل projection للـ inputs في فضاءات الـ query والـ key والـ value: Q = XWQ, K = XWK, V = XWV. والـ linear projection ده رخيص حسابيًا وبيسمح بمعالجة كل الأماكن بالتوازي كعمليات matrices — ودي ميزة أساسية قصاد الـ recurrent networks اللي بتشتغل مكان ورا مكان.

الـ scaled dot-product attention

بعد ما ناخد Q وK وV، output الـ attention بيتحسب كده:

الـ scaled dot-product attention
\text{Attention}(Q,K,V)=\text{softmax}\!\left(\frac{QK^{\top}}{\sqrt{d_k}}\right)V
الـ QKᵀ بيحسب كل الـ dot products بين الـ queries والـ keys مرة واحدة كضرب matrices (شكله: طول_الـ sequence × طول_الـ sequence). والقسمة على √dk بتمنع الـ dot products إنها تكبر أوي، وده نمو بيزق الـ softmax لمناطق الـ gradients فيها صغيرة جدًا. والـ softmax بتحوّل كل صف لـ attention weights مجموعها 1. والضرب في V بيجمع الـ values الموزونة. والعملية كلها حساب matrices واحد متجمّع على الـ batch.

معامل الـ scaling √dk حاسم. لما dk يبقى كبير (64 أو 128 مثلًا)، الـ dot products العشوائية بين vectors query وkey متوزّعة توزيع طبيعي معياري بيبقى الـ variance بتاعها dk، يعني مقدارها بيكبر مع البُعد. ومن غير الـ scaling، القيم الكبيرة دي بتخلّي الـ softmax تقريبًا one-hot، فبتعطّل الـ gradient وبتبطّأ التدريب. والقسمة على √dk بترجّع الـ variance لواحد وبتخلّي الـ gradients سليمة طول التدريب.

وممكن تزوّد mask (قناع) اختياري قبل الـ softmax. للـ causal attention (الـ autoregressive)، الـ mask بيحط الأماكن الجاية بـ −∞ بحيث كل مكان ينتبه للأماكن اللي قبله بس، فبيطبّق الخاصية الـ autoregressive المطلوبة لتوليد اللغة: الـ model ميقدرش “يشوف” الـ tokens الجاية وهو بيتنبأ بالـ token الحالي. وللـ padding، الـ mask بيحط أماكن الـ padding بـ −∞ عشان متساهمش في الـ attention weights.

الـ attention كضرب matrices

من أسباب سيطرة الـ scaled dot-product attention كفاءته الحسابية. حساب QKᵀ ضرب matrices واحد، ودي عملية متحسّنة جدًا على الـ GPUs/TPUs الحديثة. أما شبكة الـ alignment في الـ Bahdanau attention فمحتاجة تشغّل feedforward network لكل جوز (مكان في الـ decoder، مكان في الـ encoder)، يعني O(Tout × Tin) عملية متتالية. والـ scaled dot-product attention بيحسب كل التشابهات بالتوازي كضرب matrices، فبيسمح بتدريب متوازي جدًا على كل أماكن الـ sequence في نفس الوقت.

الـ multi-head attention

attention head واحد يقدر يتعلّم نوع واحد من علاقات الصلة — التوافق النحوي مثلًا، أو التشابه في المعنى. بس اللغة وغيرها من البيانات التسلسلية مبنية بطرق كتير متشابكة في نفس الوقت. والـ multi-head attention (الانتباه متعدد الرؤوس) بيشغّل h عملية attention مستقلة بالتوازي، لكل واحدة الـ projections بتاعتها من Q وK وV، فبيخلّي الـ model ينتبه مع بعض لمعلومات من subspaces تمثيل مختلفة.

الـ multi-head attention
\begin{aligned}\text{head}_i &= \text{Attention}(QW_i^Q,\,KW_i^K,\,VW_i^V)\\ \text{MultiHead}(Q,K,V) &= \text{Concat}(\text{head}_1,\ldots,\text{head}_h)\,W^O\end{aligned}
كل head i ليه matrices projection خاصة بيه WiQ, WiK, WiV (بُعدها dk = dmodel/h). وoutputs الـ h heads بتتوصّل وتتعمل لها projection لـ dmodel عن طريق WO. وفي الـ transformer الأصلي مع h=8 heads وdmodel=512، كل head بيشتغل في subspace من 64 بُعد. والعدد الإجمالي للـ parameters بيساوي attention head واحد بالأبعاد الكاملة.

وتجريبيًا، الـ attention heads المختلفة بتتخصّص في patterns لغوية مختلفة. في الـ language models المتدرّبة، شوية heads بتتابع البنية النحوية (توافق الفاعل والفعل، والإحالة)، وتانيين بيتابعوا القرب في المكان، وتانيين بينتبهوا لكلمات قريبة في المعنى مهما كان مكانها. والتخصّص ده بيطلع من التدريب على التنبؤ بالـ token الجاي بس من غير أي إشراف صريح على كل head المفروض يعمل إيه.

وتوصيل outputs الـ h heads (كل واحد بُعده dk) بيطلّع vector بُعده h×dk = dmodel. والـ projection الأخير WO بيخلط المعلومات بين الـ heads، فالـ model يقدر يجمع patterns العلاقات المختلفة اللي كل head اكتشفها في تمثيل واحد موحّد بيسلّمه للـ layer اللي بعدها.

الـ self-attention: الانتباه للـ sequence بتاعتك

في سياق الـ encoder–decoder، الـ attention خلّى الـ decoder يسأل حالات الـ encoder. بس نفس الآلية ينفع تتطبّق جوه sequence واحدة: الـ self-attention (الانتباه الذاتي) (واسمه كمان intra-attention) بيخلّي كل مكان في sequence ينتبه لكل مكان تاني في نفس الـ sequence. وQ وK وV كلهم طالعين من نفس الـ input X.

والـ self-attention بيحسب تمثيل غني وواعي بالسياق لكل token بإنه يجمّع المعلومات من كل الـ tokens التانية. خد كلمة “bank” في جملة إنجليزي: الـ self-attention يقدر يجمع في نفس الوقت أدلة من “river” (فيرجّح المعنى الجغرافي: الضفة) أو من “loan” (فيرجّح المعنى المالي: البنك)، ويوزن كل قرينة سياقية على حسب صلتها بحل اللبس. والحساسية للسياق دي بتتكسب كلها من البيانات — الـ model بيبني مفهومه الخاص للصلة من هدف التدريب.

وعلى عكس الـ hidden states في الـ recurrent networks اللي بتتحسب ورا بعض (المكان t بيعتمد على المكان t−1)، الـ self-attention بيتحسب بالتوازي على كل الأماكن. كل مكان بيسأل كل الأماكن التانية لوحده وبيجمّع الـ context vector بتاعه. والتوازي ده هو السبب الأساسي إن الـ transformers بتتدرّب أسرع بكتير من الـ recurrent networks على الأجهزة الحديثة، اللي التوازي في الـ matrices هو المورد الحسابي الأول فيها.

تعقيد O(n²)

الـ self-attention تعقيده في الوقت والمساحة O(n²d)، وn هو طول الـ sequence وd هو بُعد الـ model. matrix الـ attention اللي مقاسها n×n لازم تتحسب وتتخزّن لكل layer ولكل head. ولـ sequences طولها 512، ده معقول. بس للـ sequences الطويلة جدًا (مستندات من آلاف الـ tokens، أو صور دقتها عالية)، التكلفة التربيعية بتبقى عقبة. وأبحاث الـ sparse attention والـ linear attention والـ attention المقسوم لـ blocks (زي FlashAttention) بتعالج القيد ده وهي محافظة على أغلب قدرة الـ attention الكامل على التعبير. والدرس الجاي بيرجع لعنق الزجاجة التربيعي ده لما يقارن الـ transformers والـ recurrent networks بيكبروا إزاي.

الـ cross-attention: الانتباه لـ sequence تانية

الـ cross-attention (الانتباه المتقاطع) هو التعميم اللي بيربط sequenceين مختلفين. الـ queries جاية من sequence (مثلًا الـ decoder)، والـ keys والـ values جاية من تانية (مثلًا الـ encoder). وده بالظبط pattern الـ attention بين الـ encoder والـ decoder: الـ decoder بيسأل تمثيلات الـ encoder عشان يطلّع المعلومات اللي محتاجها يولّد كل output token.

والـ cross-attention بيتعمّم طبيعي بره الترجمة. في وصف الصور، queries من decoder اللغة بتنتبه للـ features البصرية اللي encoder الرؤية طلّعها. وفي الـ speech recognition، queries من decoder النص بتنتبه لتمثيلات encoder الصوت. وفي الإجابة على الأسئلة من المستندات، queries من decoder الإجابة بتنتبه لتمثيلات المستند. وفي أي حتة sequenceين أو modalityين بيتفاعلوا، الـ cross-attention بيوفّر الآلية.

والفرق بين الـ self-attention والـ cross-attention فرق في البنية مش في الرياضة. الاتنين بيستخدموا نفس معادلة الـ scaled dot-product. والفرق الوحيد هو مصدر Q وK وV: نفس الـ sequence في الـ self-attention، وsequences مختلفة في الـ cross-attention. والتوحيد ده من أحلى حاجة في الـ transformer (المحوّل) — primitive واحدة بتتركّب منها التفاعلات جوه الـ sequence وبين الـ sequences مع بعض.

ملحوظة عن المكان

الـ self-attention permutation-equivariant (متماثل مع التباديل): لو لخبطت tokens الـ input هتاخد نفس outputs الـ attention بنفس الترتيب الملخبط. الآلية مفيهاش مفهوم أصيل للمكان — الانتباه للمكان 1 شكله في معادلة الـ attention زي الانتباه للمكان 100 بالظبط. وده عكس الـ recurrent networks اللي المكان فيها بيتشفّر ضمنيًا بالترتيب اللي الـ hidden states بتتحسب بيه.

وعشان تحقن معلومات المكان، الـ transformers بتزوّد positional encodings (ترميزات موضعية) على embeddings الـ input قبل أي attention layer. الـ transformer الأصلي استخدم functions جيبية بترددات مختلفة عشان يطلّع ترميز مميّز لكل مكان. والـ models الحديثة غالبًا بتستخدم positional embeddings بتتعلّم، أو relative positional encodings بتميّل درجات الـ attention على طول على حسب المسافة بين الأماكن. وهنفحص الـ positional encoding بالتفصيل في الدرس 9.2 لما ندرس architecture الـ transformer كاملة.

أهم النقط
  • الـ attention بيعدّي عنق زجاجة الـ vector الثابت في الـ recurrent networks بإنه يدّي وصول مباشر وموزون لكل hidden states الـ encoder في كل خطوة decoding. والـ weights بتتحسب بـ alignment function بتتعلّم وبتتعمل لها normalization بـ softmax.
  • تجريد الـ query–key–value بيعيد صياغة الـ attention كاسترجاع معلومات بيتفاضل: الـ queries بتسأل بتدوّر على إيه، والـ keys بتوصف كل مكان فيه إيه، والـ values بتشيل المعلومات الفعلية. والتلاتة linear projections للـ input.
  • الـ scaled dot-product attention بيحسب كل تشابهات الـ query–key كضرب matrices واحد وبيقسم على √dk عشان يمنع تشبّع الـ gradient. والـ mask بيطبّق القيود الـ causal أو بيتجاهل الـ padding.
  • الـ multi-head attention بيشغّل h attention heads مستقلين بالتوازي، كل واحد بيعمل projection في subspace أبعاده أقل. والـ heads بتتعلّم تتخصّص في أنواع علاقات مختلفة (نحوية، ومعنوية، ومكانية). وoutputs بتاعتهم بتتوصّل وتتعمل لها projection لـ dmodel.
  • الـ self-attention بيطبّق نفس الآلية جوه sequence واحدة، فبيخلّي كل مكان ينتبه لكل مكان تاني في نفس الوقت وبالتوازي، على عكس الـ recurrent networks اللي بتعالج الأماكن ورا بعض. والتوازي ده هو ميزة الكفاءة الأساسية في الـ transformers.
  • الـ cross-attention بيربط sequenceين بإنه ياخد الـ queries من واحدة والـ keys والـ values من التانية، فبيدّي إطار موحّد لتفاعل الـ encoder–decoder ودمج الـ modalities المختلفة وأبعد.
اللي فات تطبيقات الـ sequences (التسلسلات) نظرة عامة اللي بعده architecture الـ transformer (المحوّل)