Yerel Gradyanlar Çarpılır
İleri Geçiş
ANDREA-120M'nin ileri geçişi girdiyi bir dizi işlemden geçirir:
x = embed(token_ids) # token gömme katmanları
for layer in 12_layers:
x = x + attn(LN(x)) # dikkat alt katmanı
x = x + mlp(LN(x)) # MLP alt katmanı
logits = LN(x) @ embed.T # bağlı çıktı projeksiyonu
loss = cross_entropy(logits, targets)
Her işlem giriş tensörlerini okur ve çıkış tensörleri üretir. İleri geçiş, bu toplu iş için çapraz entropi kaybı olan tek bir skaler ile sonlanır.
Geriye Dönük Geçiş
Eğitim, kaybı azaltan yönde ağırlıkları günceller. Güncelleme yönlerini elde etmek için motorun ihtiyacı olan:
Modeldeki her öğrenilebilir W için dL/dW
Zincir kuralı bunu verir. loss = f(g(h(x))) zinciri için:
dL/dx = (dL/df) * (df/dg) * (dg/dh) * (dh/dx)
Her faktör yerel bir gradyandır: bir işlemin çıktısının, girdisi küçük bir miktar değiştiğinde nasıl değiştiği. Yerel gradyanları graf üzerinden geriye doğru çarparak, kayıp sinyalini her ağırlığa yayar.
Ters-Mod Ayrıştırma
Backprop, gradyanları ters sırada hesaplar: dL/dlogits = 1'den başlayarak, çapraz-entropi, çıktı projeksiyonu, katman normalizasyonu, on iki transformer bloğu ve ardından gömme katmanları üzerinden geriye doğru ilerler. Her adımda, gelen gradyanı yerel Jacobian ile çarpar.
Ters-mod, çıktı tek bir skaler (kayıp) olduğunda ve çok sayıda girdi (ağırlıklar) olduğunda verimlidir. Tek bir geriye doğru geçiş, modeldeki her ağırlık için gradyan üretir. İleri-mod, ağırlık başına bir geçiş gerektirir; ~120M ağırlıklı ANDREA-120M için ileri-mod uygulanamaz.
Neden Ters-Mod
Her İleri İşlem Bir Geriye Çift Alır
Eşleştirme Disiplini
microgpt_cuda.cu her işlem için iki CUDA çekirdeği gönderir: biri ileri çıktıyı hesaplayan, diğeri çıktı gradyanları verildiğinde girdi gradyanlarını hesaplayan. Eşleştirme birebirdir:
| İleri çekirdek | Geri çekirdek | İşlem |
|---|---|---|
k_embed_fwd | k_embed_bwd | Token gömme bakımı |
k_layernorm_fwd | k_layernorm_bwd | Katman normalizasyonu |
k_attn_qkv_fwd | k_attn_qkv_bwd | Q, K, V projeksiyonları |
k_attn_fwd | k_attn_bwd | Ölçekli nokta-çarpım dikkati |
k_attn_out_fwd | k_attn_out_bwd | Çıkış projeksiyonu W_O |
k_mlp_fwd | k_mlp_bwd | MLP (GELU ile) |
k_residual_add | k_residual_add_bwd | Kalıntı bağlantısı |
k_loss_fwd | k_loss_bwd | Çapraz-entropi kaybı |
Sekiz işlem çifti tam transformeri kapsar. Artı birkaç yardımcı kernel: k_grad_norm_partial, k_grad_norm_final, k_grad_scale gradyan kırpması için (bkz. aktivite 75).
Bir Geri Kernel'in Görevi
Sonraki katmanlardan gelen gradyan (grad_output) verildiğinde, bir geri kernel hesaplar:
1. grad_input: İşlem giriş tensörü ile ilgili gradyan. Bu, daha fazla geriye doğru iletilir.
2. grad_weight: İşlemdeki öğrenilebilir parametrelerle ilgili gradyan. Bu, optimizer durumuna gider.
Her ikisi de tek bir kernel başlatmasında hesaplanır. CUDA thread'leri, gradyan tensörünün karoları üzerinde paralel olarak işbirliği yapar.
Kaydedilmiş Tensörler
Geriye doğru hesaplama genellikle ileri geçişten değerlere ihtiyaç duyar. Örneğin, k_layernorm_bwd ileri geçiş sırasında hesaplanan ortalama ve varyansa ihtiyaç duyar; k_mlp_bwd GELU öncesi aktivasyona ihtiyaç duyar. Eğitim motoru bunları ileri geçiş sırasında özel tamponlarda depolar, sonra geriye doğru sırasında okur.
Bellek maliyeti: her kaydedilen tensör için ileri geçiş çıktısının kabaca aynı şekli. ANDREA-120M için batch=8, seq=1024, d_model=768 ile, bir kaydedilen tensör 8 × 1024 × 768 × 4 bayt = 25 MB. 12 katman boyunca & katman başına birden fazla kaydedilen tensör ile, aktivasyonlar eğitim sırasında VRAM'i domine eder (~24 GB kartta 5-10 GB).
Bir Geriye Dönük Adımı İzleme
Gradients Bellekte Nerede Saklanır
Her Ağırlık Tensörü İçin Bir Gradyan Tensörü
ANDREA-120M'deki her öğrenilebilir ağırlık tensörünün, aynı şekle sahip eşleşen bir gradyan tensörü vardır. Her blok için:
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]
Artı token gömme katmanları, pozisyon gömme katmanları ve son bir katman normalizasyonu. Gradyan tamponu toplam bellek kullanımı ağırlık belleğiyle eşleşir: ~120M float, FP32'de ~480 MB, FP16'da ~240 MB.
Mikrotopaklar Arasında Birikim
ANDREA'nın batch_size = 8 değeri FP16'da VRAM'e sığar. Daha büyük etkili toplu işler için gradyan birikimi gereklidir: küçük toplu işler üzerinde birden fazla ileri+geri geçiş çalıştırın, gradyanları aynı tampona toplayın, ardından bir optimizatör adımı atın.
for microbatch in range(n_microbatches):
forward(microbatch)
backward() # Grad tamponlarına EKLER, üzerine yazmaz
scale_grads(1.0 / n_microbatches) # Mikro partiler arasında ortalama al
optimizer_step()
zero_grads() # Bir sonraki eğitim adımı için sıfırla
Backward kernel'ları += semantiğini kullanır, = değil. Her çağrı, mevcut tampona gradyan katkılarını ekler; tampon, zero_grads() temizleyene kadar çalışan toplamı tutar.
Optimizör Durumu
AdamW (etkinlik 73) her ağırlık için iki tampon daha tutar: birinci moment m & ikinci moment v. Toplam eğitim zamanı belleği:
ağırlıklar: 1× ağırlık sayısı
gradyanlar: 1× ağırlık sayısı
Adam m: 1× ağırlık sayısı
Adam v: 1× ağırlık sayısı
kaydedilmiş akt: katmanlara & batch'e göre ~2-4×
──────────────────────────────────────────
toplam: ~6-8× ağırlık sayısı
ANDREA-120M FP16'da: ~240 MB × 4 tampon (ağırlık, grad, m, v) + ~5-10 GB aktivasyonlar = ~10-12 GB toplam. RTX 4090'ın 24 GB sınırının rahat altında. ANDREA-12M 1.4 GB'da eğitildi; 10× parametre ölçeklemesi ~10× bellek getirir.
Gradient Tamponlarını Boyutlandırma
Bellek ve Hassasiyet Üzerinde Tam Kontrol
Genel Çerçevelerin Maliyeti Nedir
PyTorch ve JAX autograd'ı kolaylaştırır: Python kodu yazın, gradyanları otomatik alın. Maliyet: kodunuz ile CUDA arasında genel bir dağıtım katmanı. Her işlem Python yorumlayıcısı yükü, çerçeve defter tutma ve dinamik çekirdek seçimi arasından geçer. Tek GPU'da küçük bir dil modelini eğitirken bu yük önemli hale gelir.
ANDREA'nın önlediği somut maliyetler:
1. Python yorumlayıcısı gecikmesi. Her PyTorch işlemi Python/C++ sınırını geçer. Eğitim adım başına ~100 çekirdek başlatma ile ~9 adım/dakika için, bu dakikada ~900 sınır geçişi anlamına gelir. C-seviyesi dağıtım bunu ortadan kaldırır.
2. Çerçeve tahsisçisi öngörülemezliği. PyTorch'un önbellek tahsisçisi ortalama iyi verimlilik sağlar ancak öngörülemez tepe bellek kullanımı. ANDREA'nın eğitim motoru her tamponu başlangıçta önceden tahsis eder; eğitim sırasında yeniden tahsis yok, parçalanma yok, 100K. adımda sürpriz OOM yok.
3. Genel çekirdek seçimi. PyTorch çekirdekleri çalışma zamanında sezgisel kurallarla seçer. ANDREA çekirdekleri derleme zamanında seçer, RTX 4090 tensör çekirdeği karo boyutlarına göre ayarlanmış.
4. Karma hassasiyet borulama. ANDREA-120M'nin FP16 cuBLAS yolu ve ANDREA'nın FP8 E4M3 tensor core deneyleri, hangi tensörlerin hangi hassasiyette yaşayacağı üzerinde kesin kontrol gerektirir. Genel çerçeveler bu kontrolü katmanlı API'ler aracılığıyla sunar; özel CUDA yazımı bunu doğrudan yazar.
Takas
Özel CUDA maliyetleri: yazılacak daha fazla kod, bulunacak daha fazla hata, topluluk ekosistemi yok. ANDREA'nın microgpt_cuda.cu dosyası ~6000 satır el yazımı CUDA olup aylarca hata ayıklama aldı. Her yeni işlem için bir ileri kernel, bir geri kernel ve testler yazmak gerekir.
ANDREA'nın kazandığı:
- Tam yeniden üretilebilirlik. Eğitim hattı bir C ikili dosyası ve bir Python vekilinden oluşur. PyTorch sürümleri arasında sürüm kayması yok, çerçeve tekerlekleriyle CUDA sürüm uyumsuzluğu yok.
- Bit tam özetlemeler. SIGTERM, GPU'nun gördüğü her tensörü tam olarak yakalayan bir kontrol noktası yazmasını tetikler. Özetleme, çalışmanın olduğu aynı kayıp yörüngesinden devam eder.
- Öngörülebilir bellek. ANDREA-120M 200K adım için eğitildi, hiç OOM yok. Bellek motor başlangıcında hesaplandı.
- Doğrudan donanım erişimi. Tensör çekirdek karo boyutları, FP8 E4M3 ayarları, asenkron bellek kopyaları: hepsi CUDA'da doğrudan erişilebilir, genel çerçevelerde opak.
Yeniden Üretilebilirlik Görevi Olarak
ANDREA beyaz kağıt bölüm 9, tam yeniden üretilebilirlik yığınını listeler:
Eğitim motoru: microgpt/microgpt_cuda.cu
Eğitim vekili: microgpt/training_proxy.py
Deneyim konfigürasyonları: experiments/ANDREA-*-TRAIN.json
Veri hattı: scripts/pull-hermes3.py, scripts/prep-megachat.py
Pano: scripts/live-loss-dashboard.html
Bandit spesifikasyonu: docs/FIREHOSE-BANDIT.md
Model belgeleri: docs/ANDREA.md
Donanım gereksinimi: ≥8 GB VRAM'e sahip bir NVIDIA GPU (RTX 3060 veya daha iyisi). Herkes bu artefaktlerden ANDREA-12M'i tekrarlayabilir. Özel CUDA yolu bunun bir parçası: framework sürüm dondurmaları yok, beş yıl sonra bağımlılık sürprizleri yok.
Sinyaller & Kontrol Noktaları
CUDA eğitim döngüsü iki POSIX sinyaline yanıt verir:
- SIGTERM: anında bir kontrol noktası yaz, sonra çık. Eğitimi temiz bir şekilde durdururken kullanılır.
- SIGUSR1: anında bir kontrol noktası yaz, eğitimi devam ettir. v3'teki polish pivot sırasında çalışmayı kesmeden durumu yakalamak için kullanılır.
Kontrol noktası formatı: [int32 step][int32 n_params][n_params × float32 weights][n_params × float32 m][n_params × float32 v]. Adım sayacı, ağırlık sayısı, ardından ağırlıklar ve Adam momentleri. Bit tam olarak devam eder. Proxy, polish sırasında .samples.json & .state.json'ı ayrı arşivler; .loss.json asla arşivlenmez (tam eğitim geçmişini biriktirir).