تخطَّ إلى المحتوى
9/30الفصل 9 من 30

Attention وكتلة Transformer، مشتقان من المتوسط

ابدأ بأرخص تلخيص للسياق: المتوسط. قِس فشله، ثم دع صيغة attention تظهر من إصلاحه.

في هذه الصفحة

تصل إلى هنا ومعك tokenizer من الفصل 7، وجدول embedding من الفصل 8، والهدف المرتبط بهما: بالنظر إلى tokens الموجودة حتى الآن، ضع احتمالًا على token التالية.

ما ينقص هو الوسط. للتنبؤ بـ token tt يحتاج النموذج إلى متجه واحد يلخّص كل ما قبله، ولا شيء مما بنيته ينتج واحدًا. embedding الخاصة بـ token t1t-1 ليست ذلك — فهذا نموذج bigram، ولا يمكنه معرفة أن الجملة بدأت بسؤال. كما أن ربط كل embeddings السابقة معًا ليس هو الحل أيضًا: عددها يتغير في كل خطوة، ومصفوفة أوزان ثابتة لا تستطيع أخذ مُدخل بطول متغير.

إذن: متجه واحد بحجم ثابت، يلخّص عددًا متغيرًا من المتجهات. هذه هي المشكلة كلها، وattention هو ما تحصل عليه عندما تحلها بأكسل طريقة ممكنة ثم تصلح الشيئين اللذين ينكسران.

الإجابة التي امتلكها المجال، ولماذا لا نبنيها هنا

رابط إلى القسم: الإجابة التي امتلكها المجال، ولماذا لا نبنيها هنا

من عام 1997 إلى نحو 2017 كان الملخص حالة recurrent: احتفظ بمتجه h\mathbf{h} وحدّثه عند كل token، ht=f(ht1,xt)\mathbf{h}_t = f(\mathbf{h}_{t-1}, \mathbf{x}_t). حجم ثابت، مُدخل متغير، والشكل الصحيح تمامًا.

فشل ذلك بثلاث طرق، ومعمارية هذا الفصل تجيب عن الثلاث. إجراء backpropagation عبر TT خطوة يضرب TT من Jacobians، فيختفي gradient أو ينفجر — المرض الذي قاسه الفصل 5 داخل عقدة tanh\tanh واحدة. صُممت LSTM1 لمقاومة ذلك بالضبط ودفعت النطاق القابل للاستخدام من عشرات الخطوات إلى مئاتها، من دون تغيير حقيقة أن المعلومة من token 5 لا تصل إلى token 500 إلا إذا نجت من 495 تحديثًا متسلسلًا. كان يجب أن يتسع المصدر كله في متجه واحد: في ترجمة sequence-to-sequence2 يضغط encoder المُدخل في حالته النهائية. سمّى Bahdanau وCho وBengio ذلك الاختناق وأصلحوه عام 2014، قبل transformer بثلاث سنوات، عبر السماح للـ decoder بأخذ مجموع موزون من كل حالات encoder بأوزان يحسبها بنفسه.3 كل ما يلي هو تلك الفكرة، مطبقة من تسلسل على نفسه، مع حذف recurrence. والتحديث متسلسل بحكم بنائه: ht\mathbf{h}_t يحتاج إلى ht1\mathbf{h}_{t-1}، ولا تستطيع GPU بعشرة آلاف نواة فعل شيء مع ذلك. المعمارية التي انتصرت ليست أذكى بوضوح؛ إنها التي تكون خطوتها المكلفة ضرب مصفوفات.

أما الانحياز الاستقرائي الكلاسيكي الآخر، convolution — مرّر مرشحًا صغيرًا واحدًا فوق كامل المُدخل، بحيث تُكتشف الميزة في أي مكان كما تُكتشف في كل مكان — فلا يُبنى هنا أيضًا؛ إنه مناسب تقريبًا تمامًا للصور ويُترك لدورة رؤية حاسوبية. لا يعود recurrence ولا convolution بعد هذه الصفحة، ولذلك لا يحصل أي منهما على فصل: الفصل 1 وعد بأن تُعلَن المحذوفات لا أن تُترك بصمت.

أوضح دالة تأخذ عددًا متغيرًا من المتجهات وتعيد متجهًا واحدًا هي المتوسط:

ct=1ti=1txi\mathbf{c}_t = \frac{1}{t}\sum_{i=1}^{t} \mathbf{x}_i

أي عدد من المُدخلات، حجم خرج ثابت، قابلة للاشتقاق، ومجانية. جدول embedding زائد هذا المتوسط زائد طبقة خطية إلى المفردات يكوّن نموذج لغة كاملًا في خمسة عشر سطرًا. وهو أيضًا رديء جدًا، وطريقة رداءة أدائه هي الاشتقاق كله.

المتن أدناه هو ميغابايت واحد من شكسبير، 1,115,394 حرفًا، عبر tokenizer من نوع byte-level BPE كما بُني في الفصل 7 بمفردات حجمها 1024: 459,760 tokens بمعدل 2.43 حرف لكل token، مقسمة 90/10. كل نموذج عرضه 128، يرى 128 tokens، ويتدرب 3000 خطوة من AdamW عند 10310^{-3} بدفعة حجمها 64. perplexity محسوبة على قسم التحقق المحجوز.4

النموذجالمعلماتvalidation perplexity
token الحالية فقط، بلا سياق إطلاقًا263,16859.71
زائد المتوسط الموحد لكل ما قبلها263,168248.07
زائد learned position embeddings279,552245.93
المتوسط الموحد مضافًا إلى token بدل استبدالها263,16860.45

اقرأ الصف الثاني مرتين. متوسط السياق لا يساعد قليلًا؛ بل يجعل النموذج أسوأ بأربع مرات من تجاهل السياق بالكامل. والسببان كلاهما قابلان للإثبات لا مجرد تجريبيين.

المتوسط لا يرى الترتيب. الجمع تبادلي، لذا فإن خلط النافذة يترك الملخص بلا تغيير — ليس تقريبًا:

order.pyPYTHON
A = torch.tril(torch.ones(T, T))
A = A / A.sum(1, keepdim=True)          # rows of the averaging matrix
y = x[torch.randperm(T)]                # the same tokens, shuffled
print((A[-1] @ x - A[-1] @ y).abs().max().item())
TEXT
2.9802322387695312e-08

ضجيج floating-point على مجموع مُعاد ترتيبه: الملخصان هما المتجه نفسه. نموذج لا يرى السياق إلا كمتوسط لا يستطيع تمييز الكلب عض الرجل من الرجل عض الكلب. الصف الثالث يثبت أن هذا لا يُصلح بإضافة المواضع إلى المُدخلات — embedding موضعية متعلّمة على كل token قبل المتوسط اشترت 2.14 نقطة من أصل 188. المواضع تدخل في المجموع، والمجموع ينساها.

والمتوسط يُغرق الحاضر. عند الموضع 100 تكون token الحالية واحدًا من مئة من الملخص. لهذا إصلاح رخيص تملكه بالفعل: أبقِ token وأضف الملخص إليها — residual connection، من الفصل 6، والصف الرابع يبيّن أثره. بعد إصلاح التخفيف، لا يضيف المتوسط الموحد أي شيء إطلاقًا: 60.45 مقابل خط أساس 59.71. كل token موجودة هناك، موزونة بالتساوي، والتوزين المتساوي هو نفسه غياب المعلومات.

المشكلة ليست في أخذ المتوسط. إنها في الأوزان.

المتوسط ضرب مصفوفات، والقناع softmax

رابط إلى القسم: المتوسط ضرب مصفوفات، والقناع softmax

أخذ المتوسط على بادئة متنامية يبدو كحلقة. لكنه عملية ضرب واحدة بمصفوفة مثلثية سفلية مجموع صفوفها واحد — وهو أيضًا، بالضبط، softmax:

mechanics.pyPYTHON
loop = torch.stack([x[:t + 1].mean(0) for t in range(T)])   # the obvious version

A = torch.tril(torch.ones(T, T))
A = A / A.sum(1, keepdim=True)
mat = A @ x                                                  # the same thing

S = torch.zeros(T, T).masked_fill(torch.tril(torch.ones(T, T)) == 0, float("-inf"))
soft = F.softmax(S, dim=-1) @ x                              # and the same thing again
TEXT
loop vs matmul   max |diff| = 5.960464477539063e-08
loop vs softmax  max |diff| = 5.960464477539063e-08

the averaging matrix A (rows sum to 1, upper triangle is zero):
  1.000 0.000 0.000 0.000 0.000 0.000
  0.500 0.500 0.000 0.000 0.000 0.000
  0.333 0.333 0.333 0.000 0.000 0.000
  0.250 0.250 0.250 0.250 0.000 0.000
  0.200 0.200 0.200 0.200 0.200 0.000
  0.167 0.167 0.167 0.167 0.167 0.167

تظهر الآن ثلاثة مكونات مسماة من transformer على الشاشة. المثلث هو causal mask، مفروض بسبب الهدف: لو استطاع الموضع tt رؤية الموضع t+1t{+}1 لكانت الإجابة موجودة في المُدخل — التسريب الذي أخبرك الفصل 6 أن تدقّق فيه، لكن داخل المعمارية. softmax هو طريقة تنفيذ القناع: ضبط الخانات الممنوعة إلى -\infty يرسلها إلى صفر تمامًا ويطبّع ما تبقى، لذا فالحجب والتطبيع عملية واحدة. (استخدم -\infty، لا -1e9: إنها القيمة التي يعنيها الحجب، وتنجو من التحويل إلى float16 كـ -\infty، وتجنّبك تقرير ما إذا كان الثابت الذي اخترته كبيرًا بما يكفي للنطاق الذي تصادف أنك فيه — وهذا صندوق floating-point في الفصل 2 يطرح سؤالًا لا تحتاج إلى الإجابة عنه.) والدرجات هي المعلمة الحرة. المتوسط الموحد هو ما تحصل عليه عندما تكون كل درجة مسموحة الرقم نفسه؛ ضع أي أرقام هناك وسيحوّلها softmax إلى أوزان صالحة.

بقية هذا الفصل سؤال واحد: من أين تأتي تلك الأرقام؟

لا يمكن أن تكون معلمات عادية. مصفوفة T×TT \times T متعلّمة ستكون متطابقة لكل جملة — يمكنها ترميز «انظر أربع tokens إلى الخلف» لكنها لا تستطيع أبدًا «انظر إلى الاسم الذي يشير إليه هذا الضمير». الوزن الذي يربط الموضع tt بالموضع ii يجب أن يعتمد على ما يوجد في كلا الموضعين، لأن الصلة علاقة لا خاصية: كلمة it ليست ذات صلة بذاتها، بل هي ذات صلة بـ شيء.

أرخص دالة من متجهين تعيد رقمًا هي dot product من الفصل 1. قيّم الموضع ii للموضع tt كـ xtxi\mathbf{x}_t \cdot \mathbf{x}_i وستعمل الآلية — بشكل سيئ، بطريقتين تفرضان كل ما تبقى. dot product للمتجه مع نفسه هو مربع معياره، لذلك ستحضر كل token غالبًا إلى نفسها. كما أن العلاقة ستكون متماثلة: إذا حضرت it بقوة إلى animal، فستحضر animal بقوة إلى it، وهذا غير صحيح في اللغة، حيث تحتاج الصفة إلى اسمها أكثر بكثير مما يحتاج الاسم إلى الصفة.

لذا أعطِ كل token دورين، كتحويلين خطيين متعلمين لها: ما الذي يبحث عنه هذا الموضع، qt=Wqxt\mathbf{q}_t = W_q\mathbf{x}_t، أي query؛ وما الذي يقدمه كي يُعثر عليه به، ki=Wkxi\mathbf{k}_i = W_k\mathbf{x}_i، أي key. قيّم qtki\mathbf{q}_t \cdot \mathbf{k}_i ويختفي التناظر، لأن WqWkW_q \neq W_k: تستطيع token أن تعلن شيئًا وتبحث عن آخر.

يبقى شيء واحد خاطئًا. كان المجموع الموزون على xi\mathbf{x}_i نفسها، وهذا يفرض أن يكون الشيء الذي يُنسخ هو الشيء الذي يُطابق. المطابقة تريد الميزات التي تعرّف token؛ والنسخ يريد الميزات المفيدة لاحقًا. لذا تعلّم خريطة ثالثة، vi=Wvxi\mathbf{v}_i = W_v\mathbf{x}_i، أي value، واجمع تلك.

الصيغة الآن مجرد محاسبة:

Attention(Q,K,V)=softmax ⁣(QKdk+M)V\mathrm{Attention}(Q, K, V) = \mathrm{softmax}\!\left(\frac{QK^\top}{\sqrt{d_k}} + M\right)V

حيث MM هو causal mask، صفر على القطر وتحته و-\infty فوقه. في الكود هي ثلاثون سطرًا، عشرون منها أشكال:

attention.pyPYTHON
class Head(nn.Module):
    """One head of causal self-attention."""

    def __init__(self, d_model, d_head, block):
        super().__init__()
        self.q = nn.Linear(d_model, d_head, bias=False)      
        self.k = nn.Linear(d_model, d_head, bias=False)      
        self.v = nn.Linear(d_model, d_head, bias=False)      
        self.d_head = d_head
        self.register_buffer("mask", torch.tril(torch.ones(block, block)).bool())

    def forward(self, x):
        T = x.shape[1]
        q, k, v = self.q(x), self.k(x), self.v(x)
        s = q @ k.transpose(-2, -1) / math.sqrt(self.d_head)          
        s = s.masked_fill(~self.mask[:T, :T], float("-inf"))          
        w = F.softmax(s, dim=-1)                                      
        return w @ v                                                  

قيّم، احجب، طبّع، اخلط. كل شيء آخر إسقاط.

القسمة على الجذر التربيعي، وما الذي تدافع ضده

رابط إلى القسم: القسمة على الجذر التربيعي، وما الذي تدافع ضده

تقريبًا كل شرح لـ dk\sqrt{d_k} يقول «لمنع softmax من التشبع»، وهذا صحيح ولا يشرح شيئًا. الحجة سطران من التباين في الفصل 2. إذا كانت مداخل q\mathbf{q} وk\mathbf{k} مستقلة بمتوسط صفر وتباين واحد، فكل حاصل ضرب qjkjq_j k_j له تباين واحد، وتباينات الأشياء المستقلة تُجمع:

Var(qk)=j=1dkVar(qjkj)=dk\mathrm{Var}(\mathbf{q}\cdot\mathbf{k}) = \sum_{j=1}^{d_k}\mathrm{Var}(q_j k_j) = d_k

إذن للدرجات انحراف معياري dk\sqrt{d_k}. مقاسًا على عشرين ألف زوج عشوائي:

TEXT
     d     Var(q.k)         std   sqrt(d)
     4        3.975       1.994     2.000
    16       16.071       4.009     4.000
    64       64.249       8.016     8.000
   256      253.065      15.908    16.000
  1024     1015.562      31.868    32.000

سبب أهمية ذلك: softmax حساس للمقياس بطريقة لا تكون فيها الطبقة الخطية كذلك. مضاعفة مُدخل طبقة خطية تضاعف خرجها؛ أما ضرب الدرجات في عشرة قبل softmax فيحوّل مزيجًا لينًا إلى اختيار حاد. صف واحد من 64 درجة، مع القسمة ومن دونها:

dkd_kأكبر وزن، بلا قسمةentropytokens فعالةأكبر وزن، مع القسمةentropytokens فعالة
40.2052.94419.00.0813.75842.9
160.4381.6925.40.0753.84946.9
640.4890.8742.40.0853.67339.4
2560.99990.00071.00.1433.54734.7
10241.00000.00001.00.1323.64438.3

«tokens الفعالة» هي أس entropy: كم موضعًا يوسّط الصف فعليًا. من دون القسمة، عند dk=256d_k = 256، يحضر head مهيأ حديثًا إلى token واحدة بالضبط من أصل 64، اختارها السحب العشوائي وحده.

هذا سيئ في المرور الأمامي وأسوأ في الخلفي، بالشكل الذي قاسه الفصل 5 بالفعل على tanh\tanh. softmax ملتزم بخانة واحدة لديه مشتقة شبه معدومة: قطر Jacobian الخاص به هو wi(1wi)w_i(1-w_i)، صفر عند الطرفين. عبر ألفي صف عشوائي:

dkd_kiwi(1wi)\sum_i w_i(1-w_i) بلا قسمةمع القسمةصفوف مشبعة (أكبر وزن فوق 0.99)
40.84270.95680.2 % → 0.0 %
640.29400.960917.9 % → 0.0 %
2560.14060.960949.1 % → 0.0 %
10240.06810.961170.4 % → 0.0 %

عند dk=1024d_k = 1024، سبعة صفوف من كل عشرة تكون مجمدة قبل بدء التدريب، وhead يبدأ مجمدًا لا يستطيع تعلم أين ينظر. مع القسمة، تبقى الكمية مسطحة عند 0.96 في كل عرض ولا يتشبع شيء.

والآن الجزء الذي لا ينشره أحد: هل يغيّر perplexity النهائية؟ احذف القسمة ودرّب، عند أربعة عروض للـ head:

عرض headبلا قسمةمقسوم على dk\sqrt{d_k}مقسوم على dkd_k
أربعة heads، dk=32d_k = 3237.2938.0737.89
head واحد، dk=128d_k = 12848.5146.1045.99
head واحد، dk=256d_k = 25665.3747.53
head واحد، dk=512d_k = 51267.0649.15
head واحد، dk=1024d_k = 102476.6959.17

أول صفين من ميزانية 3000 خطوة أعلاه؛ وآخر ثلاثة من تشغيل أقصر — 1500 خطوة، دفعة 32، head واحد، بلا تطبيع قبل الإسقاطات — مع كلا المتغيرين تحت إعدادات متطابقة.

عند dk=32d_k = 32 لا تساوي القسمة شيئًا والتشغيل من دونها متقدم قليلًا جدًا. هذه ليست رخصة لإسقاطها، لأنها عند 256 تساوي 18 نقطة perplexity وعند 1024 تساوي 17. الآلية مرئية في الدرجات نفسها:

dkd_kانحراف معياري للدرجات عند التهيئةبعد 1500 خطوة، بلا قسمةبعد 1500 خطوة، مع القسمةصفوف مشبعة، بلا قسمةمع القسمة
25610.49121.672.1391.9 %0.8 %
51215.13836.852.6698.7 %1.3 %
102421.155147.463.4499.9 %16.5 %

الـ head غير المقسوم لا يتعافى. إنه ينفلت: ينمو الانحراف المعياري لدرجاته من 21 عند التهيئة إلى 5147، وتهبط entropy الخاصة بـ attention إلى الصفر، وتضع 99.9 % من الصفوف أكثر من 0.99 من وزنها على token واحدة. حالما يصبح head محددًا حادًا تكون gradient الخاصة به شبه صفرية ولا شيء يسحبه للعودة، لذا يكون الانهيار مستقرًا. أما head المقسوم فيجلس عند انحراف معياري للدرجات قدره 3.44 بعد التدريب نفسه، وهو مزيج لين ما زال قابلًا للتغيير.

يقول Vaswani وآخرون هذا بالضبط ولا أكثر — يشتبهون بأن حواصل الضرب «تكبر في المقدار عند قيم كبيرة من dkd_k» ويقسمون.5 كلمة كبيرة تحمل العبء، والجداول تقول أين يبدأ الكِبر: لا شيء عند 32، وكل شيء بحلول 256.

أكثر من رأي واحد، والثلثان اللذان لا يتحدث عنهما أحد

رابط إلى القسم: أكثر من رأي واحد، والثلثان اللذان لا يتحدث عنهما أحد

head واحد هو صف softmax واحد لكل موضع، لذا يحمل إجابة واحدة عن «ما ذو الصلة هنا». التنبؤ بالكلمة بعد the في the animal that crossed the wet street يحتاج الخانة النحوية، والفاعل، وtoken السابقة في آن واحد، ولا يمكن لتوزيع احتمالي واحد أن يتركز في ثلاثة أماكن. لذا شغّل عدة heads بالتوازي، كل واحد بعرض dmodel/hd_{\text{model}}/h، ثم اربطها وامزجها بمصفوفة أخرى WoW_o: لقد قسّمت العرض، لا أضفت إليه.

attention يفعل شيئًا واحدًا بالضبط — ينقل المعلومات بين المواضع. كل عملية في الكود أعلاه خطية على محور الميزات، وقد أثبت الفصل 5 ما تكونه كومة من الخرائط الخطية. لذلك تحمل كل كتلة أيضًا MLP صغيرة مطبقة على كل موضع بشكل مستقل، توسّع العرض أربع مرات ثم تعود، مع GELU في الوسط. تقسيم العمل يستحق الحفظ: attention يمزج عبر المواضع، وشبكة feed-forward تحسب داخل الموضع.

السلم الكامل، كل صف يضيف قطعة واحدة إلى الصف الذي فوقه:

النموذجالمعلماتvalidation perplexity
متوسط موحد، مضاف279,55260.45
head attention واحد، يستبدل token328,70455.47
head attention واحد، مضاف328,70446.10
أربعة heads بدل واحد345,21643.21
زائد شبكة feed-forward476,92839.87
زائد LayerNorm — الكتلة الكاملة477,69638.07

الأوزان المتعلّمة تهزم الأوزان الموحدة بـ 14 نقطة perplexity، وهذه حجة هذا الفصل كاملة في صف واحد. أربعة heads تشتري 3 نقاط أخرى مقابل 16,512 معلمة إضافية. ونفس head يساوي 9 نقاط أكثر عندما يكون مضافًا لا مستبدلًا: attention يجلب المعلومات إلى الداخل، ولا يقرر ما يكونه الموضع.

والآن أين تجلس المعلمات فعليًا، وهذا يفاجئ من لم يروا إلا الرسم:

العرضheadsattentionfeed-forwardالإجمالي لكل كتلة
128465,664 (33.2 %)131,712 (66.6 %)197,888
768122,360,064 (33.3 %)4,722,432 (66.6 %)7,085,568
40963267,112,960 (33.3 %)134,238,208 (66.7 %)201,367,552

ثلثا كل كتلة transformer هما شبكة feed-forward، في كل مقياس، لأن attention لديه أربع مصفوفات d×dd \times d، ولدى MLP ما يعادل ثمانيًا. أيًا كان ما يعرفه نموذج، فمعظم المعلمات التي تحمله موجودة في MLP لكل موضع.

بُني LayerNorm وقيس في الفصل 6، ويستخدمه هذا الفصل كما تُرك هناك؛ وسُمّيت residual connections وحُذفت تجريبيًا هناك، وتُبنى هنا. صفوف «مضاف، لا مستبدل» أعلاه هي residual connections، تساوي 188 نقطة perplexity للمتوسط و9 لـ head واحد. LayerNorm7 يطبّع كل مثال عبر ميزاته، وقد أعطى الفصل 6 الأسباب التي جعلته هو لا BatchNorm يبقى هنا — لا اعتماد على الدفعة، لا إحصاءات جارية، مطابق في التدريب والاستدلال، غير مكترث بطول التسلسل — وكل واحد منها يصبح مطلبًا عندما تولّد token واحدة في كل مرة لمستخدم واحد، وهذا ما ينتهي إليه الفصل 13. يكلف 768 معلمة ويشتري 1.8 نقطة perplexity.

block.pyPYTHON
class Block(nn.Module):
    def forward(self, x):
        x = x + self.att(self.ln1(x))     
        x = x + self.ff(self.ln2(x))      
        return x

انظر إلى مكان التطبيع: على مُدخل كل طبقة فرعية، مع مسار residual من المُدخل إلى الخرج لا يُطبّع أبدًا. هذا هو pre-norm. ورقة 2017 تفعل العكس، x = LayerNorm(x + Att(x))post-norm، الذي يضع LayerNorm على مسار residual نفسه.

شرح Xiong وآخرون الفرق عبر gradient عند التهيئة، والذي يكون في شبكة post-norm سيئ القياس مع العمق — السبب الذي جعل transformer الأصلي يحتاج إلى warmup لمعدل التعلم كي يتدرب أصلًا.8 اثنتا عشرة كتلة، 1000 خطوة، معدل تعلم 3×1033 \times 10^{-3}:

TEXT
gradient norm per block at initialisation, before any step
  pre-norm    block 1 0.0498 ... block 12 0.0657   ratio last/first  1.32
  post-norm   block 1 0.0977 ... block 12 0.1613   ratio last/first  1.65

  pre-norm,  no warmup          perplexity   37.82
  pre-norm,  200-step warmup    perplexity   37.62
  post-norm, no warmup          perplexity  308.05
  post-norm, 200-step warmup    perplexity   37.88

post-norm بلا warmup أسوأ بثماني مرات، وpost-norm مع warmup يطابق pre-norm تمامًا. warmup ليس ممارسة جيدة عامة هنا؛ إنه رقعة لترتيب محدد للتطبيع، ونقل LayerNorm يزيل الحاجة إليه. لهذا أصبح عمليًا كل نموذج منذ 2019 pre-norm، ولهذا ينبغي قراءة رسم 2017 كتاريخ لا كمواصفة.

احذف position embeddings وسيظل النموذج يتدرب؛ لكنه ببساطة لا يستطيع معرفة مكان أي شيء، وهذا تناظر لا فشل تدريب. لا شيء في درجة attention يذكر tt أو ii نفسيهما، لذا فإن تبديل المُدخل يبدّل الخرج: self-attention مكافئ للتبديل. إنه عمى المتوسط عن الترتيب في تنكر أفضل — يعيد causal mask بعض الترتيب، لأن كل موضع يرى بادئة مختلفة، لكن داخل البادئة كل الترتيبات متشابهة.

أربع طرق لحقن الموضع، دُرّبت على نوافذ 64-token وقُيّمت عند 64 و128 و256 — بعد أي طول رأته:

المواضعperplexity عند 64عند 128عند 256
لا شيء إطلاقًا48.7952.6357.52
learned absolute embeddings38.63108.47181.94
fixed sinusoids42.9695.26152.25
RoPE44.1250.5284.84
ALiBi44.9543.5142.49

Learned absolute embeddings — متجه واحد لكل موضع، يُضاف إلى token — تفوز عند الطول المدرّب ثم تسقط من حافة، لأن الموضع 100 لم يكن أبدًا في دفعة وembedding الخاصة به ما زالت المتجه العشوائي الذي بدأت به. Sinusoids، الخيار الأصلي، تُحسب ولا تُتعلّم، من جيوب وجيوب تمام عند ترددات متباعدة هندسيًا؛ أملت ورقة 2017 أن يستنتج ذلك خارج النطاق، والجدول يقول إنه لا يفعل — الدالة معرّفة عند الموضع 200، لكن النموذج لم يتعلم قراءتها هناك. RoPE9 لا يضيف شيئًا بل يدوّر query وkey بزاوية متناسبة مع الموضع، في شرائح ثنائية الأبعاد؛ وبما أن تدوير طرفي dot product بالتساوي يتركه بلا تغيير، تنتهي الدرجة إلى الاعتماد فقط على tit - i، فيصبح الموضع نسبيًا مجانًا ولا يوجد جدول ينفد. يتدهور، لكنه يتدهور فقط. ALiBi10 هو أبسط وأغرب نتيجة هنا: عقوبة خطية على الدرجة متناسبة مع المسافة، بميل مختلف لكل head. تتحسن perplexity الخاصة به مع نمو النافذة بعد طول التدريب، من 44.95 إلى 42.49، لأن العقوبة معرّفة عند أي مسافة وكل head يواصل فعل ما تدرب عليه.

الدرس يعيش أطول من الجدول: معمارية لا تستطيع تمثيل شيء هي مشكلة مختلفة عن معمارية لم تتعلم ذلك النطاق قط، والثانية هي التي تعض. وهي أيضًا الآلية خلف كل إعلان «مددنا context إلى 128K» — تلك غالبًا إعادة تحجيم لترميز rotary، ولهذا يقول الفصل 16 إن حد السياق يتحرك لا يختفي.

Dropout موروث بالطريقة نفسها: يظهر على أوزان attention بعد softmax، وعلى خرج كل طبقة فرعية قبل إضافة residual، وعلى مجموع embedding، ويفعل بالضبط ما وصفه الفصل 6. في عمليات pretraining الكبيرة يُضبط غالبًا إلى صفر، لأن نموذجًا يرى كل token مرة واحدة ليس في وضع يسمح له بفرط الملاءمة.

يوجد موتران في الطبقة بشكل n×nn \times n، حيث nn هو عدد tokens: الدرجات والأوزان بعد softmax. كل شيء آخر — كل إسقاط، وMLP كلها — خطي في nn.

طبقة attention واحدة، عرضها 512، فيها 8 heads، دفعة من واحد، float32، على GPU حاسوب محمول. اقرأ عمودي الميلي ثانية من أجل النِّسب فقط: إنها زمن حائط على بطاقة حاسوب محمول 8 GB تخنق نفسها من 1,785 MHz إلى أقل من 300 MHz عندما تسخن، لذا فإن تشغيلًا باردًا للكود نفسه يعود أسرع بسبع إلى عشر مرات، وتشغيلًا مشغولًا أبطأ كذلك. أعمدة الميغابايت هي أعداد بايتات المخصّص ولا تتحرك.

TEXT
  tokens   ms total    ms x4   ms projections   attn matrix MB    peak MB    MB x4
     128      2.246        -            1.324              0.5       14.6        -
     256      2.855     1.27            2.113              2.0       19.2     1.31
     512      5.761     2.02            3.105              8.0       34.4     1.79
    1024     16.414     2.85            4.008             32.0       89.1     2.59
    2048     51.573     3.14            9.989            128.0      296.1     3.32
    4096    225.432     4.37           20.176            512.0     1100.1     3.72
    8192    832.838     3.69           40.106           2048.0     4300.1     3.91
   16384   OUT OF MEMORY                                 8192.0

fitted exponent (log-log slope, last four rows):  time ~ n^1.91   memory ~ n^1.87

أعمدة x4 هي النسبة إلى الصف الذي فوقها، ومضاعفة nn تتقارب إلى 4 بالضبط للزمن والذاكرة معًا — 3.91 في الخطوة الأخيرة مقابل 4 نظريًا. عمود الإسقاطات هو الضابط: من 4.0 ms عند 1024 tokens إلى 40.1 ms عند 8192، عامل عشرة مقابل عامل ثمانية. خطي، كما وُعد.

ثم الصف الأخير. طبقة attention واحدة، تسلسل واحد، بلا نموذج حولها، تنفد ذاكرتها على GPU بسعة 8 GB عند 16,384 tokens — مصفوفة الدرجات وحدها ستكون 8 GB، لأنها 8 heads ضرب 16,384 ضرب 16,384 ضرب 4 بايتات. ليس النموذج؛ موتر وسيط واحد في طبقة واحدة.

هذه هي الحقيقة الفيزيائية تحت ثلاثة فصول لاحقة. إنها سبب وجود حد لـ context window أصلًا، والذي يحوله الفصل 16 إلى سعر. وهي سبب وجود FlashAttention، الذي يحسب النتيجة نفسها في بلاطات من دون تخزين المصفوفة أبدًا — تحسين ذاكرة قبل أن يكون تحسين سرعة.11 وهي الحساب خلف سعر prompt طويل، الذي يدفعه الفصل 24 في حلقة agent — مسألة منفصلة عن اكتشاف ذلك الفصل الآخر، وهو أن النموذج أيضًا يستخدم السياق الطويل بشكل أسوأ، ويقيس ذلك ويرفض لوم هذه الصيغة عليه.

عرض التفاصيل

المتغيران اللذان يصغّران cache، نسميهما هنا وندفع كلفتهما في الفصل 13.

التوليد يخزّن keys وvalues الخاصة بـ tokens التي عولجت بالفعل — key واحد وvalue واحدة لكل token، لكل head في كل طبقة. Multi-query attention12 يحتفظ بـ hh من إسقاطات query لكن بإسقاط key وvalue واحد مشترك بين كل heads، مقسمًا ذلك cache على hh. Grouped-query attention13 يستوفي بينهما: تُجمّع heads، كل مجموعة تشارك key وvalue واحدين، بحيث g=hg = h هو attention عادي وg=1g = 1 هو multi-query. تقريبًا كل نموذج مفتوح منذ 2023 يستخدمه مع 4 أو 8 مجموعات. لا يوجد أي منهما لأجل الجودة؛ كلاهما موجود لأجل حجم ذلك cache، والفصل 13 يجري الحساب الذي يحوله إلى «أي نموذج يتسع في GPU لديك».

تصف ورقة 2017 encoder-decoder: مكدس يقرأ المصدر بـ attention غير محجوب، وثانٍ يولّد الهدف سببيًا، ونوع ثالث من attention في الوسط حيث تلتقي queries الخاصة بـ decoder مع keys الخاصة بـ encoder. هذا صحيح للترجمة، حيث المُدخل والخرج تسلسلان.

ما فاز كان نصف decoder-only — مكدس واحد، سببي بالكامل، المُدخل والخرج في التسلسل نفسه — والسبب ليس الأناقة. «تنبأ بـ token التالية» يعمل على أي نص، لذا تكون مجموعة التدريب هي الإنترنت لا متنًا متوازيًا، ويصبح كل شيء تلك المهمة الواحدة: الترجمة وثيقة تحتوي المصدر ثم الهدف، والسؤال وجوابه وثيقة، ومحادثة فيها tool call في الوسط وثيقة. الفصل 11 عن كيفية تصنيع الأخيرة. لم تختفِ encoders — أحدها يرى المُدخل كله دفعة واحدة، وهذا ما تريده عندما تكون المهمة تمثيل نص لا متابعته، ولهذا تأتي retrieval embeddings في الفصل 19 من encoders لا من النموذج الذي يجري المحادثة.

بعد تعريف الكتلة، يصبح حجم النموذج حسابًا. لكل كتلة، بعرض dd وتوسيع أربع مرات: 4d2+4d4d^2 + 4d من أجل Wq,Wk,Wv,WoW_q, W_k, W_v, W_o مع biases على الأربعة كلها، كما في GPT-2 — الجدول أعلاه يترك bias خارج ثلاثة منها، ومن هنا 2,304 أقل لكل كتلة عند d=768d = 768؛ 8d2+5d8d^2 + 5d من أجل MLP؛ 4d4d من أجل LayerNorms اثنين — 12d2+13d12d^2 + 13d، زائد جدول token قدره V×dV \times d، وللمواضع المطلقة، nctx×dn_{\text{ctx}} \times d. لشكل GPT-2 small — d=768d = 768، 12 كتلة، مفردات 50,257، سياق 1024، وطبقة الخرج تشارك أوزان embedding:

TEXT
  token embeddings     50,257 x 768 = 38,597,376
  position embeddings   1,024 x 768 =    786,432
  one block                             7,087,872
  12 blocks                            85,054,464
  final LayerNorm         2 x 768 =        1,536
  total (weights tied)                124,439,808

وهذا هو الحجم المنشور لذلك النموذج. الصيغة ليست تقريبًا؛ إنها النموذج. لاحظ أيضًا أن قرابة ثلث النموذج الصغير هو جدول embedding، ولهذا يكون حجم المفردات قرارًا معماريًا لا قرار preprocessing — المقايضة التي أسسها الفصل 7.

perplexity رقم عن متن. ما يفعله head واحد سؤال مختلف، ونموذج مدرّب على ميغابايت من شكسبير أداة خاطئة له: القول الصادق عن خريطة attention لنموذج 500,000 معلمة هو أنها في معظمها غير قابلة للتفسير. لذا: لغة يكون للسؤال فيها جواب صحيح.

المثال الكلاسيكي هو the animal did not cross the street because it was too tired، حيث it هي animal، مقابل …because it was too wet، حيث تنقل كلمة واحدة المرجع إلى street. هذه Winograd schemas14 — أزواج جمل متطابقة إلا في كلمة واحدة، وتلك الكلمة تقرر إلى ماذا يشير الضمير.

وهي أيضًا قابلة للحل بالغش، وهذا هو الجزء الذي تتخطاه الشروحات. إذا كان المرشحان animal ومكانًا، فإن tired وwet يحددان المرجع حسب الفئة، ونموذج لا يعرف إلا أي الكلمات موجودة يجيب صح من دون أن يعرف شيئًا عن الترتيب. مقاسًا على تلك النسخة من المهمة، مع أزواج animal/place محجوزة:

TEXT
uniform causal average           held-out referent accuracy 100.0 %
one transformer block            held-out referent accuracy  91.7 %

حقيبة الكلمات تهزم transformer. أي عرض مبني على تلك الجملة لا يثبت شيئًا عن attention.

لذا أغلق الثغرة: اسحب كلا المرشحين من حوض واحد من ستة عشر اسمًا، يمكن لأي منهما الظهور في أي خانة، واقسم الصفات حسب الدور بدل الفئة — أربع تجعل it هو العابر (tired, scared, slow, weak)، وأربع تجعله الشيء المعبر عليه (wet, wide, busy, steep).

TEXT
the {x} did not cross the {y} because it was too {adj} , so the {ref} waited .

درّب كمتنبئ عادي بـ next-token، وقيّم موضعًا واحدًا — الكلمة بعد so the — وابنِ المجموعة المحجوزة من أزواج أسماء كان ترتيبها المعكوس في التدريب، بحيث إن أي شيء يعرف أي اسمين موجودان لا أيهما جاء أولًا لا بد أن يجيب بالعكس.

النموذجالمعلماتالمحجوزيذكر الاسم الآخر
token الحالية فقط5,7965.2 %5.2 %
متوسط سببي موحد5,79627.9 %50.0 %
head واحد من learned attention18,08435.4 %64.6 %
أربعة heads22,24475.0 %15.6 %
كتلة transformer واحدة55,71692.7 %4.2 %
كتلتان transformer105,508100.0 %0.0 %

الصدفة بين الاسمين الموجودين هي 50 %. المتوسط الموحد يقع عند 27.9 % ويجيب بـ الاسم الخطأ من الزوج نصف الوقت بالضبط — توقيع شيء يعرف أي الكلمات موجودة ولا يعرف شيئًا عن ترتيبها، كما تنبأ اختبار الخلط قبل ثلاثة أقسام.

والآن الخريطة: attention عند الموضع الذي يجب أن يسمّي المرجع، بمتوسط عبر heads الأربعة لكل كتلة، للجملتين اللتين تختلفان بكلمة واحدة. المتوسط الموحد سيضع 0.067 على كل token من الخمس عشرة المرئية.

TEXT
the animal did not cross the street because it was too tired , so the animal waited .
  blk 1  the:0.00 animal:0.70 did:0.00 not:0.00 cross:0.00 the:0.00 street:0.06
         because:0.00 it:0.00 was:0.00 too:0.00 tired:0.00 ,:0.05 so:0.00 the:0.19
  blk 2  the:0.00 animal:0.00 did:0.00 not:0.00 cross:0.00 the:0.00 street:0.00
         because:0.00 it:0.00 was:0.00 too:0.00 tired:1.00 ,:0.00 so:0.00 the:0.00

the animal did not cross the street because it was too wet , so the street waited .
  blk 1  the:0.00 animal:0.70 did:0.00 not:0.00 cross:0.00 the:0.00 street:0.06
         because:0.00 it:0.00 was:0.00 too:0.00   wet:0.00 ,:0.05 so:0.00 the:0.19
  blk 2  the:0.00 animal:0.00 did:0.00 not:0.00 cross:0.03 the:0.00 street:0.49
         because:0.00 it:0.00 was:0.00 too:0.20   wet:0.03 ,:0.00 so:0.00 the:0.25

الكتلة 1 متطابقة في الجملتين — 0.70 على الاسم الأول، أيًا كانت الصفة. هذا ليس فشلًا بل برهان: في الطبقة الأولى تكون query عند موضع دالة في token ذلك الموضع وفهرسه، وthe عند الموضع 14 هي token نفسها في الجملتين. لا يستطيع head في الطبقة الأولى أن يشترط على كلمة لم يجلبها بعد. لذلك تفعل الكتلة 1 الشيء المفيد الوحيد المتاح لها وتسحب الاسم الأول إلى الأمام.

الكتلة 2 هي حيث تفترق الجملتان، والصف نفسه عبر الصفات الثماني يبيّن القاعدة التي وجدها النموذج:

الصفةالكتلة 2 على animalعلى streetعلى الصفةالإجابة
tired, scared, slow, weak0.0000.0001.000animal
wet, wide, busy, steep0.0000.4910.00–0.03street

لصفة العابر تنفق الكتلة الثانية وزنها كله على الصفة، لأن الإجابة موجودة بالفعل في residual stream — وضعتها الكتلة 1 هناك — وكل ما تحتاج إليه هو التأكيد. ولصفة المعبر عليه تذهب وتجلب الاسم الآخر بدلًا من ذلك. هذه دائرة من قفزتين: head ينقل مرشحًا إلى الأمام، وhead في طبقة لاحقة يقرأ token تقرر هل يُبقيه. التركيب عبر الطبقات هو الآلية، ولهذا وصلت كتلة واحدة إلى 92.7 % وكتلتان إلى 100 %.

وهو أيضًا شكل أفضل دائرة موثقة في النماذج الحقيقية. Induction heads — head للـ previous-token يغذي head في الطبقة التالية يكمل النمط [A][B] … [A] → [B] — هي ما تحدده أعمال Anthropic في قابلية التفسير خلف جزء كبير من in-context learning، وهي تتشكل في لحظة قابلة للتحديد أثناء pretraining. لا يحاول هذا الفصل ذلك التحليل: يتركه، مع الورقتين في المراجع، لأن قراءة الدوائر من نموذج حقيقي مجال بحث لا قسمًا.

أخيرًا، التنفيذ. الأسطر الثلاثون أعلاه، مع أوزانها منسوخة من PyTorch نفسها:

TEXT
ours vs nn.MultiheadAttention           max |diff| = 1.7881393432617188e-07
ours vs F.scaled_dot_product_attention  max |diff| = 1.7881393432617188e-07

1.8×1071.8 \times 10^{-7} على مخرجات متوسط مقدارها 0.159: الحساب نفسه بترتيب مختلف، عند دقة float32.

لديك الآن المعمارية التي يُبنى منها كل نموذج في بقية هذه الدورة، وهي أصغر من سمعتها: متوسط موزون بأوزان متعلّمة، وMLP لكل موضع تحمل ثلثي المعلمات، وتطبيعان وإضافتان، مكدسة.

ما لا تملكه هو نموذج يعرف أي شيء، والتكديس وحده لن يصلح ذلك. كتلتان على هذا المتن تصلان إلى training perplexity قدرها 14.49 وvalidation perplexity قدرها 40.57، مقابل 18.77 و38.07 لكتلة واحدة — سعة أكبر، أفضل على ما رآه، أسوأ على ما لم يره، وهذا جدول الفصل 6 وفيه transformer. المسافة بين هذا النموذج والنماذج التي تتحدث إليها الفصول 14 إلى 30 ليست معمارية. إنها الكتلة نفسها، مكررة مرات أكثر، فوق نص أكثر بكثير.

وهذا يجعله مسألة محاسبة، والمحاسبة أغرب مما تبدو. كم من النص، ومن أين يحصل عليه أي أحد؟ كم من الحساب، وكيف تقدّره قبل أن يُنفق المال؟ عند ميزانية ثابتة، هل الأفضل جعل النموذج أكبر أم عرضه على بيانات أكثر — وهل هناك جواب صحيح، أم مجرد موضة؟ يجيب الفصل 10 عن الثلاثة بالقياس، ويضع سعرًا لأرخص صيغة مفيدة من السؤال: كم يكلف اليوم تدريب نموذج مثل GPT-2 من الصفر؟


ثلاثة شروح لهذه المادة أفضل من هذا الشرح فيما صُممت له، وهذا الفصل مكتوب ليُقرأ بجانبها. عمل Jay Alammar، The Illustrated Transformer، هو أفضل صورة لتدفق البيانات رُسمت على الإطلاق. وThe Annotated Transformer من Harvard NLP هي ورقة 2017 مع كود عامل متداخل سطرًا بسطر. وLet's build GPT: from scratch, in code, spelled out لـ Andrej Karpathy يبني النموذج نفسه مباشرة خلال ساعتين، وسلم ablations أعلاه هو العمود نفسه مقاسًا على متن مختلف. أما سؤال قابلية التفسير الذي لا يلمسه هذا الفصل إلا قليلًا، فمصادره الأولية هي Elhage et al., A Mathematical Framework for Transformer Circuits (2021) وOlsson et al., In-context Learning and Induction Heads (2022)، وكلاهما من مجموعة قابلية التفسير في Anthropic.

  1. Hochreiter, S. and Schmidhuber, J. Long Short-Term Memory. Neural Computation 9(8), pp. 1735–1780 (1997).

  2. Sutskever, I., Vinyals, O. and Le, Q. V. Sequence to Sequence Learning with Neural Networks. arXiv:1409.3215 (2014). الـ encoder-decoder الذي يكون متجه السياق الواحد فيه هو الاختناق.

  3. Bahdanau, D., Cho, K. and Bengio, Y. Neural Machine Translation by Jointly Learning to Align and Translate. arXiv:1409.0473 (2014). attention، قبل transformer بثلاث سنوات.

  4. Perplexity هي أس متوسط cross-entropy لكل token، من الفصل 8. كل رقم هنا يستخدم tokenizer نفسه وقسم التحقق نفسه، وهذا هو الشرط الوحيد الذي يمكن عنده مقارنة قيمتي perplexity أصلًا.

  5. Vaswani, A., Shazeer, N., Parmar, N., Uszkoreit, J., Jones, L., Gomez, A. N., Kaiser, Ł. and Polosukhin, I. Attention Is All You Need. arXiv:1706.03762 (2017). القسم 3.2.1 هو الجملة الوحيدة عن dk\sqrt{d_k} التي يقضي هذا الفصل قسمًا في قياسها.

  6. Shazeer, N., Mirhoseini, A., Maziarz, K., Davis, A., Le, Q., Hinton, G. and Dean, J. Outrageously Large Neural Networks: The Sparsely-Gated Mixture-of-Experts Layer. arXiv:1701.06538 (2017).

  7. Ba, J. L., Kiros, J. R. and Hinton, G. E. Layer Normalization. arXiv:1607.06450 (2016). قُدّم وقيس في الفصل 6؛ ويُستخدم هنا بلا تغيير.

  8. Xiong, R., Yang, Y., He, D., Zheng, K., Zheng, S., Xing, C., Zhang, H., Lan, Y., Wang, L. and Liu, T.-Y. On Layer Normalization in the Transformer Architecture. arXiv:2002.04745 (2020). تحليل gradient خلف pre-norm، والحجة أن warmup عرض لا علاج عام.

  9. Su, J., Lu, Y., Pan, S., Murtadha, A., Wen, B. and Liu, Y. RoFormer: Enhanced Transformer with Rotary Position Embedding. arXiv:2104.09864 (2021).

  10. Press, O., Smith, N. A. and Lewis, M. Train Short, Test Long: Attention with Linear Biases Enables Input Length Extrapolation. arXiv:2108.12409 (2021). نتيجة الاستقراء خارج النطاق المعاد إنتاجها أعلاه.

  11. Dao, T., Fu, D. Y., Ermon, S., Rudra, A. and Ré, C. FlashAttention: Fast and Memory-Efficient Exact Attention with IO-Awareness. arXiv:2205.14135 (2022).

  12. Shazeer, N. Fast Transformer Decoding: One Write-Head is All You Need. arXiv:1911.02150 (2019).

  13. Ainslie, J., Lee-Thorp, J., de Jong, M., Zemlyanskiy, Y., Lebrón, F. and Sanghai, S. GQA: Training Generalized Multi-Query Transformer Models from Multi-Head Checkpoints. arXiv:2305.13245 (2023).

  14. Levesque, H. J., Davis, E. and Morgenstern, L. The Winograd Schema Challenge. KR (2012). البناء خلف جملة animal / street التي يستخدمها كل شرح لـ attention.

هل أنت مستعد لتترك الاختيار لـ LIA؟

ابنِ بكل نماذج الذكاء الاصطناعي في مكان واحد — ابدأ مجانًا اليوم.