English· Español· Deutsch· Nederlands· Français· 日本語· ქართული· 繁體中文· 简体中文· Português· Русский· العربية· हिन्दी· Italiano· 한국어· Polski· Svenska· Türkçe· Українська· Tiếng Việt· Bahasa Indonesia

un

ضيف
1 / ?

التدرجات المحلية تتضاعف

Forward & Backward Kernels


الانتشار الأمامي

الانتشار الأمامي لـ ANDREA-120M يمرر الإدخال عبر سلسلة من العمليات:


x = embed(token_ids)         # تضمينات الرموز
for layer in 12_layers:
x = x + attn(LN(x))      # طبقة الاهتمام
x = x + mlp(LN(x))       # طبقة MLP
logits = LN(x) @ embed.T     # إسقاط الإخراج المشترك
loss   = cross_entropy(logits, targets)

كل عملية تقرأ تسنسورات الإدخال وتنتج تسنسورات الإخراج. تنتهي المرور الأمامي في قيمة واحدة: خسارة الإنتروبيا المتقاطعة لهذا الدفعة.


المرور الخلفي

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


∂L/∂W لكل W قابل للتعلم في النموذج

قاعدة السلسلة توفر هذا. بالنسبة لسلسلة loss = f(g(h(x))):


∂L/∂x = (∂L/∂f) * (∂f/∂g) * (∂g/∂h) * (∂h/∂x)

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


التفريق عكسي الاتجاه

يحسب الانتشار الخلفي التدرجات بترتيب عكسي: بدءًا من dL/dlogits = 1، ثم السير للخلف عبر الإنتروبيا المتقاطعة، ثم الإسقاط الخارجي، ثم تطبيع الطبقة، ثم اثنا عشر كتلة محول، ثم الترجمات. في كل خطوة، اضرب التدرج الوارد في يعقوب المحلي.


الوضع العكسي فعال عندما يكون الإخراج قيمة مفردة (الخسارة) وهناك العديد من المدخلات (الأوزان). تمريرة خلفية واحدة تنتج تدرجات لكل وزن في النموذج. الوضع الأمامي سيحتاج إلى تمريرة لكل وزن؛ بالنسبة لـ ANDREA-120M مع ~120M وزن، الوضع الأمامي غير عملي.

لماذا الوضع العكسي

يحتوي ANDREA-120M على ~120M وزن ويُنتج خسارة عددية واحدة لكل خطوة تدريب. قارن بين التفريق التلقائي عكسي الاتجاه والتفريق التلقائي أمامي الاتجاه. حدد (1) أي وضع يُنتج جميع تدرجات الأوزان في مرور عكسي واحد؛ (2) كم عدد المرور الأمامية اللازمة لحساب جميع تدرجات الأوزان الـ120M؛ (3) أي وضع يستخدمه ANDREA ولماذا.

كل عملية أمامية تحصل على توأم عكسي

قاعدة الاقتران

يحتوي microgpt_cuda.cu على نواتين CUDA لكل عملية: واحدة تحسب المخرج الأمامي، وأخرى تحسب تدرجات المدخلات معطاة تدرجات المخرجات. الاقتران واحد لواحد:


النواة الأماميةالنواة الخلفيةالعملية
k_embed_fwdk_embed_bwdالبحث عن تضمين الرموز
k_layernorm_fwdk_layernorm_bwdتطبيع الطبقة
k_attn_qkv_fwdk_attn_qkv_bwdإسقاطات Q, K, V
k_attn_fwdk_attn_bwdالانتباه بنقطة الضرب المقيسة
k_attn_out_fwdk_attn_out_bwdإسقاط الإخراج W_O
k_mlp_fwdk_mlp_bwdMLP (مع GELU)
k_residual_addk_residual_add_bwdالاتصال المتبقي
k_loss_fwdk_loss_bwdخسارة الإنتروبيا المتقاطعة

ثمانية أزواج عمليات تغطي المحول بالكامل. بالإضافة إلى بعض النوى الخدمية: k_grad_norm_partial، k_grad_norm_final، k_grad_scale لقص التدرج (انظر النشاط 75).


مهمة نواة عكسية

معطى التدرج المتدفق من الطبقات اللاحقة (grad_output)، تحسب نواة عكسية:


1. grad_input: التدرج بالنسبة لتينسور الإدخال للعملية. يتم تمريره للخلف بشكل أكبر.

2. grad_weight: التدرج بالنسبة للمعاملات القابلة للتعلم في العملية. يذهب إلى حالة المحسن.


يتم حساب كلاهما في إطلاق نواة واحد. تتعاون خيوط CUDA على بلاطات تينسور التدرج بالتوازي.


التينسورات المحفوظة

غالباً ما تحتاج الحسابات الخلفية إلى قيم من المرور الأمامي. على سبيل المثال، k_layernorm_bwd تحتاج إلى المتوسط والتباين المحسوب أثناء الأمامي؛ k_mlp_bwd تحتاج إلى ما قبل تنشيط GELU. يخزن محرك التدريب هذه في مخازن مخصصة أثناء الأمامي، ثم يقرأها أثناء الخلفي.


تكلفة الذاكرة: تقريباً نفس شكل إخراج الـ forward لكل tensor محفوظ. بالنسبة لـ ANDREA-120M مع batch=8، seq=1024، d_model=768، tensor محفوظ واحد هو 8 × 1024 × 768 × 4 bytes = 25 MB. عبر 12 طبقة ومتعدد tensors محفوظة لكل طبقة، الـ activations تهيمن على VRAM أثناء التدريب (~5-10 GB على بطاقة 24 GB).

تتبع خطوة Backward واحدة

ANDREA-120M يكمل forward pass عبر كتلة transformer واحدة. تتبع ما يحدث أثناء backward pass عبر نفس الكتلة (في هيكل pre-norm: `x = x + Attention(LN(x))` ثم `x = x + MLP(LN(x))`). سمِّ kernels الـ backward بالترتيب الذي تُشغل بها، وحدد أي kernel forward يرتبط كل واحدة منها. غطِّ على الأقل 4 kernels.

أين تعيش التدرجات في الذاكرة

تدرج واحد لكل تينسور وزن

كل تينسور وزن قابل للتعلم في ANDREA-120M لديه تدرج متطابق الشكل. لكل كتلة:


W_Q       [768, 768]     ↔   grad_W_Q       [768, 768]
W_K       [768, 768]     ↔   grad_W_K       [768, 768]
W_V       [768, 768]     ↔   grad_W_V       [768, 768]
W_O       [768, 768]     ↔   grad_W_O       [768, 768]
W_1       [768, 3072]    ↔   grad_W_1       [768, 3072]
W_2       [3072, 768]    ↔   grad_W_2       [3072, 768]
LN1.gamma [768]          ↔   grad_LN1.gamma [768]
LN1.beta  [768]          ↔   grad_LN1.beta  [768]
LN2.gamma [768]          ↔   grad_LN2.gamma [768]
LN2.beta  [768]          ↔   grad_LN2.beta  [768]

بالإضافة إلى تضمينات الرموز، تضمينات الموضع، وطبقة تطبيع نهائية. إجمالي ذاكرة مخزن التدرج يطابق ذاكرة الأوزان: ~120M أرقام عائمة، ~480 ميغابايت عند FP32، ~240 ميغابايت عند FP16.


التجميع عبر الدفعات الفرعية الصغيرة

حجم الدفعة في ANDREA = 8 يتناسب مع VRAM عند FP16. الدفعات الفعالة الأكبر تتطلب تجميع التدرجات: تشغيل عدة تمريرات أمامية+خلفية على دفعات صغيرة، جمع التدرجات في نفس المخزن، ثم إجراء خطوة محسن واحدة.


for microbatch in range(n_microbatches):
forward(microbatch)
backward()           # يُضيف إلى مخازن التدرج، لا يُكتب فوقها
scale_grads(1.0 / n_microbatches)  # متوسط عبر الميكروباتشات
optimizer_step()
zero_grads()             # إعادة تعيين للخطوة التدريبية التالية

نوى الانتشار العكسي تستخدم دلالة +=، وليس =. كل استدعاء يضيف مساهمات التدرج إلى المخزن الموجود؛ يحتفظ المخزن بالمجموع التراكمي حتى يمسحه zero_grads().


حالة المحسن

AdamW (النشاط 73) يحتفظ بمخزنين إضافيين لكل وزن: اللحظة الأولى m واللحظة الثانية v. إجمالي الذاكرة أثناء التدريب:


الأوزان:    1× عدد الأوزان
التدرجات:  1× عدد الأوزان
Adam m:     1× عدد الأوزان
Adam v:     1× عدد الأوزان
الأفعال المحفوظة: ~2-4× حسب الطبقات والباتش
──────────────────────────────────────────
الإجمالي:      ~6-8× عدد الأوزان

ANDREA-120M عند FP16: ~240 ميغابايت × 4 مخازن (الأوزان، التدرج، m، v) + ~5-10 جيجابايت تنشيطات = ~10-12 جيجابايت إجمالي. بشكل مريح أقل من سقف RTX 4090 البالغ 24 جيجابايت. تم تدريب ANDREA-12M في 1.4 جيجابايت؛ التوسع 10× في المعاملات يجلب ~10× ذاكرة.

تحديد حجم مخازن التدرج

يحتوي ANDREA-120M على ~120,000,000 وزن & يستخدم تراكم التدرج عبر 4 دفعات فرعية لكل خطوة تدريب. الحوسبة: (أ) حجم مخزن التدرج بالميجابايت عند FP16؛ (ب) الذاكرة الإجمالية للأوزان + التدرجات + Adam m + Adam v عند FP16؛ (ج) كم عدد استدعاءات `forward()` + `backward()` المنفصلة التي تُطلق لكل خطوة تدريب. أظهر حساباتك.

التحكم الكامل في الذاكرة والدقة

تكلفة الإطارات العامة

يجعل PyTorch & JAX autograd مريحًا: اكتب كود Python، احصل على التدرجات تلقائيًا. التكلفة: طبقة توجيه عامة بين كودك & CUDA. كل عملية تمر عبر عبء تفسير Python، تسجيل الإطار، & اختيار النواة الديناميكي. لتدريب نموذج لغة صغير على وحدة معالجة رسوميات واحدة، يهم ذلك العبء.


التكاليف الملموسة التي يتجنبها ANDREA:


1. تأخير مفسر Python. كل عملية PyTorch تعبر حد Python/C++. لـ ~100 إطلاق نواة لكل خطوة تدريب عند ~9 خطوات/دقيقة، هذا ~900 عبور حد لكل دقيقة. الإرسال على مستوى C يقضي على هذا.


2. غير المتوقع في مضعِّب الإطار. مضعِّب التخزين المؤقت في PyTorch يعطي تدفقًا جيدًا في المتوسط لكن ذاكرة قمة غير متوقعة. محرك تدريب ANDREA يخصص كل مخزن مسبقًا عند البدء؛ لا إعادة تخصيص أثناء التدريب، لا تفتيت، لا OOM مفاجئ في الخطوة 100K.


3. اختيار النواة العام. PyTorch يختار النوى في وقت التشغيل عبر ال clifford clifford clifford clifford clifford clifford clifford clifford clifford clifford clifford clifford clifford clifford clifford clifford clifford clifford clifford clifford clifford clifford clifford clifford clifford clifford clifford clifford clifford clifford clifford clifford clifford clifford clifford clifford clifford clifford clifford clifford clifford clifford clifford clifford clifford clifford clifford clifford clifford


4. أنابيب الدقة المختلطة. مسار cuBLAS FP16 في ANDREA-120M وتجارب نوى التنسور FP8 E4M3 في ANDREA تتطلب تحكمًا دقيقًا في التنسورات التي تعيش بأي دقة. الإطارات العامة تعرض هذا التحكم من خلال واجهات برمجية طبقية؛ الكتابة المخصصة لـ CUDA تكتبها مباشرة.


التسوية

تكاليف CUDA المخصصة: كود أكثر للكتابة، أخطاء أكثر للعثور عليها، لا نظام بيئي مجتمعي. ملف microgpt_cuda.cu في ANDREA يتكون من ~6000 سطر من CUDA المكتوب يدويًا استغرق أشهرًا في التصحيح. كل عملية جديدة تتطلب كتابة نواة أمامية، نواة خلفية، واختبارات.


ما يكسبه ANDREA:


- إعادة الإنتاج الكاملة. خط أنابيب التدريب هو ثنائي C واحد بالإضافة إلى وكيل Python واحد. لا انجراف في الإصدارات عبر إصدارات PyTorch، لا تناقضات في إصدار CUDA مع عجلات الإطار.

- استئنافات دقيقة على مستوى البت. SIGTERM يُطلق كتابة نقطة تفتيش تلتقط كل تنسور بالضبط كما يراه GPU. الاستئناف يلتقط نفس مسار الخسارة الذي كان عليه التشغيل.

- ذاكرة متوقعة. تم تدريب ANDREA-120M لمدة 200 ألف خطوة بدون OOMs. تم حساب الذاكرة عند بدء تشغيل المحرك.

- الوصول المباشر إلى الأجهزة. أحجام بلاطات نواة التنسور، إعدادات FP8 E4M3، نسخ الذاكرة غير المتزامنة: كلها قابلة للعنوان مباشرة في CUDA، غامضة في الإطارات العامة.


إعادة الإنتاج كمهمة

يُسرد القسم 9 من ورقة ANDREA البيضاء مكدس إعادة الإنتاج الكامل:


محرك التدريب: microgpt/microgpt_cuda.cu
وكيل التدريب: microgpt/training_proxy.py
إعدادات التجربة: experiments/ANDREA-*-TRAIN.json
خط أنابيب البيانات: scripts/pull-hermes3.py, scripts/prep-megachat.py
لوحة التحكم: scripts/live-loss-dashboard.html
مواصفات Bandit: docs/FIREHOSE-BANDIT.md
وثائق النموذج: docs/ANDREA.md

متطلبات الأجهزة: وحدة معالجة NVIDIA GPU واحدة بذاكرة VRAM ≥8 جيجابايت (RTX 3060 أو أفضل). يمكن لأي شخص إعادة إنتاج ANDREA-12M من هذه الملفات. المسار المخصص لـ CUDA هو جزء من السبب: لا تجميد إصدارات الإطار، لا مفاجآت في التبعيات بعد خمس سنوات.


الإشارات & نقاط التفتيش

يستجيب حلقة تدريب CUDA لإشارتين POSIX:


- SIGTERM: كتابة نقطة تفتيش فورية، ثم الخروج. يُستخدم عند إيقاف التدريب بشكل نظيف.

- SIGUSR1: كتابة نقطة تفتيش فورية، الاستمرار في التدريب. تم استخدامها أثناء الدوران التلميعي في v3 لالتقاط الحالة دون مقاطعة التشغيل.


تنسيق نقطة التفتيش: [int32 step][int32 n_params][n_params × float32 weights][n_params × float32 m][n_params × float32 v]. عداد الخطوة، عدد الأوزان، ثم الأوزان متبوعة بلحظات Adam. يستأنف بدقة البت. الوكيل يحفظ .samples.json & .state.json بشكل منفصل في التلميع؛ .loss.json لا يتم حفظه أبدًا (يتراكم تاريخ التدريب الكامل).

لماذا ليس PyTorch

كان بإمكان ANDREA استخدام autograd الخاص بـPyTorch بدلاً من كتابة `microgpt_cuda.cu` يدويًا. قدم سببين هندسيين متميزين لماذا اختارت ANDREA CUDA مخصص. يجب أن يشير أحد الأسباب إلى التحكم في الذاكرة أو الدقة؛ والآخر يشير إلى القابلية للتكرار، تبعيات الإطار، أو الصيانة طويلة الأمد.