انتقل إلى المحتوى الرئيسي

الوحدة 4 — الانتشار الخلفي مشروحًا خطوة خطوة

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

المسألة المطلوب حلّها

بعد الانتشار الأمامي صار لدينا خسارة L\mathcal{L}، عدد واحد. ولتحسينها يجب معرفة الاتّجاه الذي نُزيح فيه كلّ وسيط، أي حساب L/w\partial \mathcal{L} / \partial w من أجل كلّ وزن — وقد تكون ملايين.

المقاربة الساذجة تقوم على تعديل كلّ وزن قليلًا ومراقبة الأثر على الخسارة. وهذا يستلزم انتشارًا أماميًا كاملًا لكلّ وسيط: فمن أجل مليون وزن، مليون مرور. غير قابل للتطبيق.

يحصل الانتشار الخلفي على جميع التدرّجات في مرور خلفي واحد، كلفته من رتبة كلفة الانتشار الأمامي نفسها. وعامل التسريع هذا هو ما يغيّر كلّ شيء.

قاعدة السلسلة، المتطلّب الوحيد

إذا كان yy يعتمد على uu الذي يعتمد على xx، فإنّ:

yx=yuux\frac{\partial y}{\partial x} = \frac{\partial y}{\partial u} \cdot \frac{\partial u}{\partial x}

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

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

المرور الخلفي، طبقة بطبقة

المقدار المركزي هو الخطأ المحلّي للطبقة، ويُرمز له δ()\delta^{(\ell)}: أي حساسية الخسارة تجاه التنشيط القبلي z()z^{(\ell)}.

نبدأ بالطبقة الأخيرة. ومع سوفت‌ماكس متبوعة بإنتروبيا متقاطعة، تتبسّط النتيجة تبسّطًا لافتًا:

δ(L)=y^y\delta^{(L)} = \hat{y} - y

فخطأ طبقة المُخرَج هو ببساطة الفارق بين التوقّع والحقيقة. وهذه الأناقة ليست مصادفة: بل يُنتجها الاقتران بين سوفت‌ماكس والإنتروبيا المتقاطعة، وهو سبب إضافي لاحترام الأزواج المذكورة في الوحدة 2.

ثم نصعد من طبقة إلى أخرى:

δ()=(W(+1)δ(+1))f(z())\delta^{(\ell)} = \left(W^{(\ell+1)\top} \delta^{(\ell+1)}\right) \odot f'\left(z^{(\ell)}\right)

عمليتان، لكلّ منهما معنى واضح. فالجداء بـW(+1)W^{(\ell+1)\top} يُعيد توزيع خطأ الطبقة التالية على الطبقة الحالية، بنسبة الأوزان: فالعصبون الذي أسهم كثيرًا يستقبل حصّة خطأ متناسبة. والضرب حدًّا بحدّ في f(z())f'(z^{(\ell)}) يُرشّح هذا الخطأ حسب الحساسية المحلّية للتنشيط. فإذا كان المُخرَج مُتشبّعًا كان ff' قريبًا من الصفر ولم يعد الخطأ يصعد.

وتُستنتج تدرّجات الوسائط مباشرة:

LW()=δ()a(1),Lb()=δ()\frac{\partial \mathcal{L}}{\partial W^{(\ell)}} = \delta^{(\ell)} a^{(\ell-1)\top}, \qquad \frac{\partial \mathcal{L}}{\partial b^{(\ell)}} = \delta^{(\ell)}

تستحقّ الصيغة الأولى قراءة متمعّنة: فتدرّج وزن هو جداء الخطأ في المجرى النازل في التنشيط في المجرى الصاعد. فلا يُصحَّح وزن إلّا إذا كان الاثنان غير معدومين. وإذا كان عصبون المُدخَل غير نشط فهذا الوزن لا يتحرّك — وهذا يفسّر العصبون الميت في الوحدة 2 تفسيرًا ملموسًا.

تنفيذ مُصغَّر

def passe_arriere(activations, pre_activations, poids, y_vrai):
"""يعيد تدرجات شبكة ReLU بمخرج سوفت ماكس."""
gradients_W, gradients_b = [], []
# طبقة المخرج: اقتران سوفت ماكس مع الإنتروبيا المتقاطعة يتبسط.
delta = activations[-1] - y_vrai

for i in reversed(range(len(poids))):
gradients_W.insert(0, delta.T @ activations[i])
gradients_b.insert(0, delta.sum(axis=0))
if i > 0:
# إعادة التوزيع بالأوزان، ثم الترشيح بمشتق ReLU.
delta = (delta @ poids[i]) * (pre_activations[i - 1] > 0)

return gradients_W, gradients_b

يُختزل مشتقّ ReLU إلى الاختبار > 0، الذي يساوي 1 للمُدخلات الموجبة و0 لغيرها. وهذه إحدى المزايا العملية لدالة التنشيط هذه.

ما تفعله المكتبات فعلًا

لن تكتب هذا الكود في الإنتاج أبدًا، ولحسن الحظّ. فـPyTorch وTensorFlow تمارسان الاشتقاق الآلي: تُسجَّل كلّ عملية من الانتشار الأمامي في مخطّط حسابي، ونداء loss.backward() يجتازه بالمقلوب مطبّقًا المشتقّات المحلّية المعروفة لكلّ عملية.

ونتيجتان تجدر الإشارة إليهما. أوّلًا، الاشتقاق الآلي ليس حسابًا رمزيًا ولا تقريبًا بالفوارق المنتهية: فالتدرّجات المُحصَّلة دقيقة، بحدود دقّة الآلة. وثانيًا، يجب أن يحفظ المخطّط التنشيطات الوسيطة للمرور الخلفي، وهذا يفسّر لماذا يستهلك التدريب ذاكرة أكثر بكثير من الاستدلال — ولماذا تقليص حجم الحزمة أوّل ردّ فعل أمام امتلاء ذاكرة المعالج الرسومي.

لماذا نفهم خوارزمية لن نكتبها

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

الخلاصة

  • يحصل الانتشار الخلفي على جميع التدرّجات في مرور خلفي واحد، حيث تطلب المقاربة الساذجة مرورًا لكلّ وسيط.
  • ليس إلّا قاعدة السلسلة: فتدرّج وزن عميق هو جداء المشتقّات المحلّية على طول المسار.
  • يُعيد المرور الخلفي توزيع الخطأ بـWW^{\top} ثم يُرشّحه بـf(z)f'(z)؛ فالتنشيط المُتشبّع يحجب الصعود إذن.
  • تدرّج وزن هو جداء الخطأ النازل في التنشيط الصاعد؛ وتُؤتمت المكتبات كلّ ذلك، بكلفة استهلاك ذاكرة كبير أثناء التدريب.

الوحدة التالية: المُحسِّنات، التي تُقرّر ما يُفعَل بهذه التدرّجات بعد حسابها.