2 puan yazan GN⁺ 2023-08-10 | 1 yorum | WhatsApp'ta paylaş
  • Brian Kitano, TinyShakespeare ile küçültülmüş bir Llama'yı bizzat inşa ederek, makale uygulamalarında küçük bir modelden başlayıp parçaları tek tek değiştirmek ve her seferinde eğitip değerlendirmek gerektiğini, bunun daha güvenli olduğunu özetliyor
  • Veri bölme, batch oluşturma, kayıp değerlendirme ve üretim fonksiyonu gibi doğrulama yardımcı fonksiyonlarını önce hazırlayıp, basit bir modelle derleme ve eğitim yapılabildiğini doğruladıktan sonra Llama bileşenlerini ekliyor
  • RMSNorm, RoPE ve SwiGLU'yu sırayla eklerken tensor shape'leri, formül özellikleri ve attention map'leriyle her katmanın beklendiği gibi çalıştığını kontrol ediyor
  • RoPE attention'da causal mask çıkarılınca doğrulama kaybı 0.16'ya kadar düşüyor, ancak üretim kalitesi kötüleşiyor; nedenin gelecek token'ları görmeye dayalı bilgi sızıntısı olduğu anlaşılıyor
  • Nihai küçültülmüş Llama, 4 blok ve yaklaşık 2,37 milyon parametreyle doğrulama kaybını yaklaşık 1.0'a kadar düşürdü; gradient akışı ve learning rate schedule'ın da birlikte kontrol edilmesi gerekiyor

Küçük başlayıp yinelemeli biçimde güven oluşturmak

  • Makale uygulamalarında temel nokta, küçük bir modelden başlayıp bileşenleri tek tek değiştirmek ve her değişiklikte eğitim ile değerlendirmeyi yinelemektir
  • Önce modeli nicel olarak kontrol edecek yardımcı fonksiyonlar hazırlanır
    • Veri bölme
    • Eğitim döngüsü
    • Kayıp görselleştirme
    • Doğrulama kaybı değerlendirmesi
  • Makaledeki tüm bileşenleri tek seferde taşımak yerine, daha önce uygulama deneyimi olan basit ve hızlı bir modelle üretim sonucunu görmek için nitel değerlendirme fonksiyonu da hazırlanır
  • Tensor katmanları .shape, assert, plt.imshow ile kontrol edilir; en baştan matris çarpımı optimizasyonuna girmek yerine beklenen sonuç elle doğrulanır, ardından torch fonksiyonlarıyla verimli hale getirilir
  • Batch boyutu, sekans uzunluğu ve embedding boyutu değiştirilerek test yapılmalıdır; yalnızca tek bir boyutta doğru çalışan kod, çıkarım sırasında kırılabilir

Veri kümesi ve temel ayarlar

  • Uygulama hedefi, Meta AI'ın Llama modelinin büyük ölçüde küçültülmüş bir sürümüdür; eğitim verisi ise TinyShakespeare'dir
  • Llama 1,4T token ile eğitilirken, burada yaklaşık 1,11 milyon karakter boyutundaki TinyShakespeare kullanılır
  • Orijinal Llama, SentencePiece byte-pair encoding tokenizer kullanır; bu uygulama ise basit bir karakter düzeyi tokenizer kullanır
    • Vocabulary size 65'tir
    • Veri kümesi küçük olduğu için bellek depolama biçimi ayrıca optimize edilmez
  • MASTER_CONFIG sözlüğüyle vocab_size, batch_size, context_window, d_model gibi model ayarları yönetilir
    • Amaç, sabitleri ve magic number'ları azaltıp kodu daha okunabilir kılmaktır
  • get_batches fonksiyonu veriyi train %80, val %10, test %10 olarak böler ve rastgele başlangıç noktalarından girdi x ile bir karakter sonraki etiket y'yi üretir

Temel modelle derleme ve eğitimi doğrulama

  • İlk model, embedding ve basit bir feed-forward ağdan oluşan SimpleBrokenModel'dır
    • nn.Embedding
    • Linear
    • ReLU
    • Linear
  • Makale uygulamasında modelin “çalışması” iki koşulun da sağlanması anlamına gelir
    • Derleme: Tensor shape'leri katmanlar arasında uyumludur
    • Eğitim: Kayıp gerçekten düşer
  • evaluate_loss fonksiyonu, train ve val split'lerinden 10 batch örnekleyerek ortalama kaybı hesaplar
  • SimpleBrokenModel, 1000 epoch eğitimden sonra 3.94 civarında doğrulama kaybına sahipti; başlangıç cross-entropy değeri 4.17'den neredeyse hiç düşmedi
  • Neden, F.cross_entropy'ye zaten softmax uygulanmış değerlerin verilmesiydi
    • PyTorch'un F.cross_entropy fonksiyonu normalize edilmemiş logits'i doğrudan alır
    • Softmax'i kaldıran SimpleModel, doğrulama kaybını 2.51 seviyesine kadar düşürdü
  • Ardından modelin ürettiği karakterleri doğrudan kontrol etmek için generate fonksiyonu eklendi; temel model kusursuz olmasa da doğrulama kaybı düşen bir duruma geldi

Llama bileşeni 1: RMSNorm

  • Llama, orijinal Transformer'a kıyasla üç ana mimari değişiklik kullanır
    • RMSNorm pre-normalization
    • Rotary embeddings
    • SwiGLU activation function
  • Orijinal Transformer BatchNormalization kullanırken, Llama vektörü centering yapmadan variance ile ölçekleyen RMSNorm kullanır
  • Orijinal Transformer attention layer çıktısına normalization uygulayan post-normalization yaklaşımını kullanırken, Llama bunu önce girdiye uygulayan pre-normalization yaklaşımını kullanır
  • Uygulanan RMSNorm, girdi shape'inin (batch, seq_len, d_model) olduğunu varsayar
  • RMSNorm sonucu, layer norm'un katman eleman sayısının kareköküne eşit olması özelliğiyle test edilir
    • assert
    • satır bazlı karşılaştırma
    • torch.allclose
  • Temel modele RMSNorm ekleyen SimpleModel_RMS, doğrulama kaybını hafifçe 2.5015 seviyesine indirdi

Llama bileşeni 2: RoPE ve causal mask

  • RoPE, Transformer'lar için bir konum kodlama yöntemidir ve token konumlarını embedding rotasyonu olarak ifade eder
  • get_rotary_matrix, context window ve embedding dimension için konumlara göre rotasyon matrisi üretir
  • RoPE uygulaması şu özellikle test edilir
    • m, n konumlarında döndürülen iki vektörün iç çarpımı, göreli konum n-m rotasyonuyla eşleşmelidir
  • RoPEAttentionHead, w_q, w_k, w_v oluşturur; query ve key'e RoPE rotasyonu uygular, ardından F.scaled_dot_product_attention kullanır
  • Eğitim ve çıkarım aşamalarındaki tensor shape farklarına dikkat edilmelidir
    • Eğitim sırasında çoğu zaman (config['batch_size'], config['context_window'], config['d_model']) gibi ayarlarla uyumlu şekiller kullanılır
    • Çıkarım sırasında (1, 1, config['d_model']) gibi tekil bir örnek işlenebilir
    • forward içinde indeksleme, model ayar değerlerine değil girdiden elde edilen shape'e göre yapılmalıdır
  • Causal mask olmadan RoPE multi-head attention eklenen modelde doğrulama kaybı hızla 0.1623'e kadar düştü; ancak üretim sonucu OOOO..., IIII... gibi kötüydü
  • Attention map kontrol edildiğinde tüm konumların tüm konumlara başvurduğu görüldü ve sonraki token tahmininde gelecek token'ları görmeye dayalı bilgi sızıntısı oluştu
  • F.scaled_dot_product_attention içinde is_causal=True uygulayan RoPEMaskedAttentionHead'e geçilince geleceğe karşılık gelen upper triangular attention neredeyse 0 oldu
  • Causal mask uygulandıktan sonra doğrulama kaybı 2.0815 oldu; daha uzun eğitimle 1.8985'e kadar düştü

Llama bileşeni 3: SwiGLU ve blokları yığmak

  • Llama, ReLU nonlinearity'yi SwiGLU activation function ile değiştirir
  • Uygulanan SwiGLU, Swish-gated linear unit'tir ve iki linear dönüşüm ile learnable beta parametresi kullanır
  • Feed-forward bölümüne SwiGLU ekleyen RopeModel 592.706 parametreye sahipti ve doğrulama kaybı 1.8963 seviyesindeydi
  • Ardından LlamaBlock oluşturularak şu yapı tek bir blokta birleştirildi
    • RMSNorm pre-normalization
    • masked RoPE multi-head attention
    • residual connection
    • RMSNorm pre-normalization
    • SwiGLU feed-forward
    • residual connection
  • Nihai Llama modeli n_layers=4 olarak ayarlandı ve OrderedDict tabanlı nn.Sequential ile 4 adet LlamaBlock yığıldı
  • Nihai modelin parametre sayısı 2.370.246'dır ve eğitim sonuçları şöyledir
    • İlk 4-layer eğitimden sonra doğrulama kaybı 1.5532
    • 10.000 epoch daha eğitimden sonra doğrulama kaybı 1.1479
    • Ek eğitimden sonra doğrulama kaybı 0.9997
    • Test split'inden bir batch için kayıp 1.2358

Üretim sonuçları ve debug kontrolü

  • Nihai model, Shakespeare formatına benzer adlar, satır sonları ve kelime parçaları üretiyor; ancak gerçek cümle kalitesi sınırlı
  • Cross-entropy kaybı, token seçimi açısından sezgisel hale getirilebilir
    • Başlangıç kaybı 4.17, vocabulary size 65 iken rastgele seçime yakındır
    • 1.08 kayıp, rastgele yaklaşık 2,9 token arasından seçmekle aynı düzeyde yorumlanır
  • Gradient akışı show_grads fonksiyonuyla kontrol edilir
    • Her parametrede mutlak değeri küçük gradient oranı hesaplanır
    • Parametrelerin çoğunda gradient 0'a yakın değilse akış iyi durumdadır
  • Orijinal Llama Cosine Annealing learning schedule kullanır; ancak bu uygulamada deney sonuçları daha kötüydü
  • Cosine Annealing deneyinde, çok düşük tolerance değerlerinde bile attention bias neredeyse hiç sinyal alamadı; nedeni kesin olmadığı için gerçek uygulamada basit başlamak daha güvenlidir

1 yorum

 
GN⁺ 2023-08-10
Hacker News yorumları
  • SwiGLU uygulamasında bir hata var gibi görünüyor: referans makalede feed-forward network’ün beta’sı öğrenilebilir bir değer değil, sabit; FFnSwiGLU = Swish1... olarak bırakılmış
    https://arxiv.org/pdf/2002.05202.pdf içindeki Denklem 6’ya göre
    Resmî llama uygulamasında da sabit beta kaldırılmış: https://github.com/facebookresearch/llama/blob/main/llama/mo...
    Blog günlüklerindeki "feedforward.1.beta', 0.0" satırlarına bakılırsa, eğitim sırasında beta 0’a dejener olmuş; oysa aslında sabit 1 olmalıydı

    • Transformer sinir ağlarını doğru uygulamanın ne kadar zor olduğunu gösteriyor. Birçok aşamada hata yapılabilir ve genelde yalnızca “orijinalinden biraz daha kötü performans” şeklinde ortaya çıktığı için kesin olarak anlamak zor olur
      Ağ çoğu zaman, amaçlanmış olsun ya da olmasın, değişikliklere uyum sağlar; eğitimden sonra çeşitli mimari varyantlar benzer davranabildiğinden, orijinalle birebir aynı olması gerekip gerekmediği de bazen belirsizleşir
      Bu tür hataları bulmanın bir yolu, referans uygulamayla çıktı değerlerini tam olarak eşleştirmektir. HuggingFace’in tiny-random modeli gibi rastgele ağırlıklar olsa bile çıktılar tam olarak aynı olmalı; farklıysa bu bir hata işaretidir
      Ancak bu yöntem daha çok çıkarım sırasında ortaya çıkan hatalarda işe yarar; veri işleme, optimizer veya yalnızca eğitim sırasında oluşan sorunları yakalamak daha zordur
    • Transformer’larda bias değerlerinin genelde pek iyi oturmadığını düşünüyorum
      Kişisel olarak bunun otoregresif ve ODE benzeri özelliklerinden kaynaklandığını düşünüyorum ama bundan emin değilim
  • Çalışma harika, ancak ilk SimpleBrokenModel ve SimpleModel içinde epey fazla boşa giden hesaplama var. Sıra embedding 65 -> 128, linear 128 -> 128, ReLU, linear 128 -> 65 şeklinde; ilk iki katman arasında doğrusal olmayanlık yok ve ikisi de lineer olduğu için ikinci lineer katman aslında fiilen işe yaramıyor
    Bu model sonuçta klasik tek gizli katmanlı MLP ile aynı ve FLOPS açısından 128*128=16k işlemi toplam 128*128+65*128=24k işlem içinde boşa harcamış oluyor

    • Doğrusal olmayanlığı hâlâ öğrenmekte olan tek kişi ben değilim gibi görünüyor. Buradaki en iyi düzeltmenin embedding ile ilk lineer katman arasına ReLU veya SwiGLU koymak mı, yoksa lineer katmanı doğrudan silmek mi olduğunu merak ediyorum
      Embedding katmanı token indekslerini embedding vektörlerine çeviren özel bir yapı olduğu için kaldırılamaz gibi geliyor
  • Genel olarak temel ilkeleri iyi gösteriyor. Özellikle “.shape’i dinî bir titizlikle kullan. assert ve plt.imshow dostundur” sözü hoşuma gitti; shape’in ön ve son koşulları her zaman assert edilmeli
    bear veya typeguard’ın dekoratör olarak bu tür kontrolleri destekleyip desteklemediğini de merak ediyorum
    Ancak “küçük, basit ve hızlı bir model seçip niteliksel olarak değerlendiren bir yardımcı oluşturun” kısmı sanki nicel değerlendirme demek istiyor. Böylece daha ileri tekniklerle karşılaştırılabilecek sayısal bir baseline oluşur
    Makaledeki bileşenleri tek tek uygulama tavsiyesi de daha kesin olmalı. Makaleler genelde birden fazla değişikliği aynı anda deneyip ardından ablasyon deneyleriyle her öğenin katkısını gösterdiği için, bence çekirdek mimari değişiklikten başlamak ve ablasyon deneylerinde etkisi büyük olandan küçük olana doğru, bağımlılıkları koruyarak her atomik değişikliği değerlendirmek daha iyi olur

    • bear veya typeguard yerine https://peps.python.org/pep-0646/ sayesinde bunun bir kısmı doğrudan Python tip anotasyonlarına itilebilir
      Örneğin ndarray[float, Dim1, *Shape] gibi eksen bazında shape’i tipe ifade edip, axis değerine göre dönen shape’i overload edebilirsiniz
    • PyTorch’u iyi bilmiyorum ama son kontrol ettiğimde öyle değildi; Jax ise bear / typeguard üzerinden matris shape’leri için temel düzeyde runtime kontrolü destekliyor
      Yine de Python’ın Julia kadar iyi olması zor görünüyor. Julia’nın tip sistemi matris boyutlarının uyumlu olduğunu çok daha kolay garanti edebiliyor
  • ReLU yerine SwiGLU kullanma ilkesinin ne olduğunu merak ediyorum. Yazarların olası tüm doğrusal olmayan fonksiyonları sadece denemiş mi olduğu, yoksa daha derin bir nedeni mi bulunduğu belli değil

    • Pek çok araştırmada olduğu gibi, titiz bir araştırmayla desteklenen net bir açıklama yoksa, muhtemelen havalı görünen tek satırlık değişiklikleri rastgele tepe tırmanışı tarzı aramayla deneyip, makaleyi yazmaya ve ablasyon deneylerine başlamanın zamanı geldiğinde durmuşlardır
  • bearblog DDoS altında olduğu için depoyu bırakıyorum: https://github.com/bkitano/llama-from-scratch

  • Yapay zekayı öğrenen biri olarak yazıda geçen terimleri kısaca toparlamayı denedim. Token, metin parçalarını temsil eden tamsayı kimliğidir; LLM’lerde sınırlı bir söz varlığı boyutu içinde sık kullanılan karakter parçaları gruplanarak kullanılır.
    Kayıp fonksiyonu, tahmin ile doğru cevap arasındaki farkı ölçen değerdir; ne kadar düşükse o kadar iyidir. PyTorch, tensörler ve sinir ağlarıyla çalışmaya yarayan bir kütüphanedir; tensör ise skaler, vektör ve matrisleri de kapsayan çok boyutlu bir sayı dizisidir.
    Sinir ağı, ağırlık ve bias’lara sahip nöron bağlantılarından oluşan bir yapıdır; doğrusal katman ise tüm giriş ve çıkışların birbirine bağlı olduğu basit bir yapıdır. ReLU, Math.max(0, x) gibi bir aktivasyon fonksiyonudur; yalnızca doğrusal katmanlar üst üste konduğunda sonuçta tek bir doğrusal fonksiyona eşdeğer olacağı için, doğrusal olmama özelliği ekleyerek öğrenilebilirliği artırır.
    Gradyan, öğrenme sırasında modeli daha doğru hâle getirmek için hesaplanan sayısal değişim miktarıdır; batch normalization ise akan sayıları ayarlayarak öğrenmeye yardımcı olan bir yöntemdir. Konum kodlaması, token’ların göreli konumlarını vektör olarak bildirir.
    Python’daki @ operatörü, __matmul__ için bir takma addır ve matris çarpımında kullanılır. Epoch, veri kümesinin tamamı üzerinde bir kez eğitim yapmak; batch ise parametre güncellemesinden önce tek seferde verilen veri sayısıdır.
    Attention, LLM’leri çalıştıran temel mekanizmadır; giriş token’larını paralel işleyerek ara tensörler oluşturur ve bunları çıktı token’larını üretmek için kullanır.

    • Alanın dışındakiler “Karpathy”nin ne anlama geldiğini bilmeyebilir. Andrej Karpathy’yi “bilim iletişimcisi ve araştırmacı” gibi bir bağlamla tanıtmak, onun yazılarına veya videolarına başvurulması gerektiğini daha anlaşılır kılar.
    • Token’ı yalnızca bir metin parçasının tamsayı kimliği olarak görmektense, kendi başına yararlı olacak kadar sık görülen bir kelime parçası olarak düşünmek yeni başlayanlar için de daha doğru olur.
      Örneğin writing, written, writer içinde ortak geçen writ tek bir token olabilir; writer ise writ ve er olarak tokenize edilebilir.
      Embedding, bu token’ları kendilerine özgü sayısal temsillere dönüştürme aşamasıdır.
    • Doğrusal fonksiyonlar bileşkelendiğinde yine doğrusal bir fonksiyon elde edilir. Bu yüzden her şey doğrusal olursa, çok katman yığılsa bile biri dışındaki katmanlar boşa gider; bundan kaçınmak için doğrusal olmama gerekir.
    • Karpathy’nin video serisi ve accompanying repo dışında, öğrenme yolculuğunda özellikle yardımcı olan başka kaynaklar veya kitaplar olup olmadığını merak ediyorum.
    • Batch normalization tam olarak ne yapıyor ve nasıl yardımcı oluyor, merak ediyorum.
  • Modelin mevcut bir implementasyonu ve checkpoint’i varsa, kendi implementasyonunuzun doğru olup olmadığını kontrol etmenin en etkili yolu o checkpoint’i yükleyip çıktı değerlerini karşılaştırmaktır.
    Çıktı tutmuyorsa genellikle ayrıntı implementasyonunda bir hata vardır; her katmanı sistemli biçimde izleyerek gerçek farkı bulabilirsiniz. Bu sırada mevcut implementasyonda tuhaf bir nokta da keşfedebilirsiniz.
    Bu, modelin kendisiyle ilgili bir konu; eğitim ise ayrı bir eksen. Yine de hiperparametreleri bir ölçüde benzer ayarladıysanız, model implementasyonu doğru olduğunda genelde işler yoluna girer.

  • Hem makale okuma yöntemi hem de söz konusu makalenin içeriği iyi; Karpathy’nin Makemore serisini de öneririm.

  • Özet tavsiyeler çok iyi; tensör shape’lerini assert etme önerisinin her tür genel lineer cebir kütüphanesine uygulanabileceğini düşünüyorum. Karmaşık lineer cebir kodu yazarken küçük adımlarla ilerlemek ve savunmacı kodlamak çok önemli.
    Yaygın dillerde lineer cebir programlamak, derleme zamanında shape denetimi olmadığı için berbat. Tensörün shape’i tipin bir parçası olmalı; 3x4 ile 3x4’ü transpoze etmeden çarpmaya çalışırsanız kod daha derlenmemeli.
    Uzun bir hesaplama çalıştırdıktan sonra boyut uyuşmazlığı olan bir işlemde patlamak gerçekten en kötüsü.
    PyTorch tensörlerinde de cihazın statik olarak tiplendirilmesi gerektiğini düşünüyorum. Şu anda CPU belleğindeki bir tensörle GPU belleğindeki bir tensörü çarpmaya çalışırsanız runtime hatası alıyorsunuz.