PyTorch Profil Oluşturma (Bölüm 2): nn.Linear'dan Birleşik MLP'ye
newspaper Haber schedule 14 Haziran 2026 · 10:56 timer 5 dk okuma

PyTorch Profil Oluşturma (Bölüm 2): nn.Linear'dan Birleşik MLP'ye

PyTorch profil oluşturma serisinin ikinci bölümünde, nn.Linear katmanının iç işleyişini, bias eklemenin matris çarpımına nasıl entegre edildiğini ve torch.compile ile el yapımı kernel'lerin performans karşılaştırmasını inceliyoruz. GPU kernel'leri, epilogue kavramı ve MLP bloklarının profil analizi ile derinlemesine bir keşif.

nn.Linear Katmanının Derinlemesine İncelenmesi

PyTorch profil oluşturma serimizin ilk bölümünde, `torch.add(torch.matmul(x, w), b)` işlemiyle başlamış ve CPU dağıtım zinciri, başlatma yükü, hesaplama sınırı gibi kavramları ele almıştık. Şimdi bir adım yukarı çıkıyoruz: elle yazılmış matmul-add ikilisi yerine, derin öğrenme modellerinin temel yapı taşı olan `nn.Linear` (bias=True) katmanını kullanıyoruz. Bu katman, kendi ağırlık (weight) ve bias parametrelerine sahiptir ve `forward` metodu ile tanıdık bir arayüz sunar. Örneğin, `y = linear_layer(x)` çağrısı aslında `y = x @ w.T + b` işlemini gerçekleştirir. Profil çıktısında, `aten::t` (transpoze) işleminin `aten::addmm` (çarpma ve toplama) işleminden önce geldiğini görürüz. Bu, `nn.Linear`'ın ağırlık matrisini transpoze edip ardından girişle çarptığını gösterir. Önemli bir nokta: `aten::t` aslında veriyi kopyalamaz, sadece tensör metaverilerini (şekil ve adım) yeniden yazar. GPU'da herhangi bir kernel başlatmaz. Bunu, GPU şeridinde hiçbir işlem olmamasından veya profil tablosundaki CUDA süresinin 0.000 µs olmasından anlayabiliriz.

Bias Toplamasının Epilogue ile Birleştirilmesi

`nn.Linear` katmanının dağıtım zincirinde ayrı bir `aten::add` (bias toplama) işlemi görmeyiz. Bunun nedeni, bias toplamanın matris çarpımı kernel'inin içine, epilogue adı verilen bir mekanizmayla entegre edilmiş olmasıdır. Epilogue, bir GEMM (General Matrix Multiply) kernel'inin sonunda, sonucu HBM'e (High Bandwidth Memory, GPU'nun ana belleği) yazmadan hemen önce yaptığı küçük bir hesaplamadır. Bias ekleme, aktivasyon uygulama veya bir sabitle çarpma gibi işlemler klasik epilogue örnekleridir. Epilogue'un amacı, HBM'e ikinci bir yükleme/yazma işlemini önlemektir çünkü bellek trafiği işlemi pahalı hale getirir. `nn.Linear`, `torch.nn.functional.linear`'ı çağırır, o da `aten::linear`'ı çağırır. `aten::linear`, bias parametresinin varlığını fark eder ve ayrı bir matmul ve add yapmak yerine `aten::addmm(bias, x, weight)` işlemini dağıtır. `addmm` şu hesaplamayı yapar: `out = x @ weight.T + bias`. GPU'da çalışan cuBLAS GEMM kernel'inin bias ekleme varyantı zaten vardır ve `aten::addmm` bu kernel'i seçer. Bu nedenle, bias toplama ayrı bir kernel olarak görünmez; matmul kernel'inin yazma işleminin bir parçasıdır.

torch.compile ile Derleme: Ne Kadar Kazanç?

Tek bir `nn.Linear` katmanı için `torch.compile` kullanmak, çoğu durumda önemli bir hızlanma sağlamaz. Bunun nedeni, `nn.Linear`'ın zaten tek bir birleşik kernel (`addmm`) kullanmasıdır. Derlenmiş ve derlenmemiş (eager) izlemeleri karşılaştırdığımızda, GPU'da aynı cuBLAS GEMM kernel'ini ve CPU'da aynı `aten::addmm` işlemini görürüz. Derleme, yalnızca CPU üzerindeki bazı ek yükleri kaldırır. Örneğin, `aten::t` işlemi derleme sırasında Inductor tarafından izlenir ve adımlar önceden hesaplanarak doğrudan `aten::addmm` çağrısına gömülür. Bu, birkaç mikrosaniyelik CPU iş yükünü ortadan kaldırırken GPU aynı işi yapmaya devam eder. Ancak, giriş verisi derleyicinin önceden hesapladığı adımlarla uyuşmazsa hata alınabilir. Bu nedenle, `torch.compile` tek bir işlem için değil, birden çok işlemin birleştirilebildiği durumlarda daha etkilidir.

MLP Bloğunun Profil Analizi

Şimdi, üç adet `nn.Linear` katmanı ve GeGLU aktivasyonu içeren bir MLP (Multilayer Perceptron) bloğunu profilliyoruz. Beklentimiz, her bir `nn.Linear` için bir `aten::linear` dağıtımı ve GeLU ile çarpma için iki noktasal (pointwise) kernel olmak üzere toplam 5 kernel görmektir. Profil çıktısı bu beklentiyi doğrular. GPU'da her ileri geçişte (mlp_fwd) tam olarak 5 kernel çalışır. GEMM kernel'leri, başlatma öncesinde `cudaOccupancyMaxActiveBlocksPerMultiprocessor` çağrısı yaparak grid boyutunu belirlerken, noktasal işlemler doğrudan başlatılır. Ayrıca, `aten::t`, `aten::transpose`, `aten::reshape`, `aten::view`, `aten::as_strided` ve `aten::_unsafe_view` gibi işlemler yalnızca metaverileri değiştirdiği için GPU'da hiçbir kernel başlatmaz ve CUDA süreleri 0.000 µs'dir. İlginç bir detay: `down_proj` katmanı, aynı FLOP sayısına sahip olmasına rağmen diğerlerinden yaklaşık %10 daha hızlıdır. Bunun nedeni, farklı şekil (N=768) nedeniyle cuBLAS'ın daha uygun bir döşeme (tile) boyutu seçmesidir.

torch.compile ile MLP: Birleşik Kernel

MLP'yi `torch.compile` ile derlediğimizde, eager moddaki dağıtım zinciri (aten::linear → aten::t → aten::transpose → aten::matmul → aten::reshape → aten::mm) ortadan kalkar ve yerine doğrudan üç adet `aten::mm` dış çağrısı gelir. GPU kernel isimleri eager modla birebir aynıdır, yani GPU aynı işi yapar. Ancak, iki noktasal kernel (GeLU ve çarpma) ile bir yeniden şekillendirme (reshape) işlemi, `triton_poi_fused__unsafe_view_gelu_mul_0` adlı tek bir kernel'de birleşir. Bu birleşme sayesinde, ara sonuç (h = gelu(g)) HBM'e yazılıp okunmak yerine kayıtlarda (register) tutulur, bu da bellek trafiğini azaltarak performansı artırır. Bu, derlemenin asıl gücünü gösterir: birden fazla işlemi birleştirerek bellek erişimlerini en aza indirir.

El Yapımı Kernel: LigerGEGLUMLP

Son olarak, insan uzmanlar tarafından yazılmış ve özel olarak ayarlanmış bir kernel olan LigerGEGLUMLP'yi kullanıyoruz. Bu kernel, Hugging Face Hub'dan `kernels` kütüphanesi ile indirilebilir. LigerGEGLUMLP, `LigerGELUMulFunction` ile GeLU ve çarpma işlemini tek bir Triton kernel'de birleştirir. Bu, `torch.compile`'ın yaptığına benzer bir birleştirmedir, ancak derleme olmadan gerçekleşir. Liger kernel'inin başlatma parametreleri, sütun sayısına göre `calculate_settings` ile seçilir. Performans karşılaştırmasında, Liger kernel'i 92.8 µs'de çalışırken, Inductor'ın birleşik kernel'i 89.4 µs'de çalışır. İlk bakışta Liger daha yavaş görünse de, bu karşılaştırma eksiktir. `torch.compile` statik bir şekil için özelleşmiştir; batch boyutu, dizi uzunluğu veya gizli boyut değiştiğinde yeniden derleme maliyeti ortaya çıkar. Liger kernel'i ise herhangi bir şekil için yeniden derleme gerektirmeden çalışır. Bu, son birkaç mikrosaniyeden feragat ederek değişken şekillere karşı sağlamlık sağlar. Gerçek seçim, "yavaş insan kernel'i vs hızlı derlenmiş kernel" değil, "hızlı genel kernel vs belirli bir şekil için özelleşmiş kernel" arasındadır.

Neden Önemli?

Bu yazı, PyTorch profil oluşturmanın temel kavramlarını ve pratik uygulamalarını göstermektedir. `nn.Linear` katmanının iç işleyişini, bias toplamanın epilogue ile birleştirilmesini, `torch.compile`'ın ne zaman ve neden faydalı olduğunu ve el yapımı kernel'lerin avantajlarını anlamak, derin öğrenme modellerinizi optimize etmek için kritik öneme sahiptir. Profil oluşturma alışkanlığı kazanmak - önce tahmin et, sonra bak - performans sorunlarını hızlıca tespit etmenize yardımcı olur. Bir sonraki bölümde, MLP'den dikkat mekanizmasına ve tam bir modele doğru ilerleyeceğiz.

tag PyTorch tag profil oluşturma tag nn.Linear tag MLP tag torch.compile tag GPU kernel