SON DAKİKA

Nvdia

“NVIDIA OptiX ile İşbirlikçi Vektörler Kullanarak Sinirsel Render Alma”

NVIDIA OptiX 9.0’ın piyasaya sürülmesiyle birlikte, ray-tracing çekirdekleri içinde AI iş akışlarını mümkün kılan yeni bir özellik olan kooperatif vektörler tanıtılmıştır. Bu özellik, gölgeleme sırasında NVIDIA RTX Tensor Cores‘u kullanarak matris işlemleri ve sinir ağı hesaplamalarında donanım hızlandırma sağlar. Bu gelişme, NVIDIA RTX Sinir Şemaları ve NVIDIA RTX Sinirsel Doku Sıkıştırma (NTC) gibi AI tabanlı görselleştirme tekniklerinin kapılarını açarak, gerçek zamanlı render için film kalitesinde fotorealistik materyalleri mümkün hale getiriyor.

Kooperatif vektör API’leri, OptiX, DirectX, NVAPI, Slang ve Vulkan gibi platformlarda tanıtılmaktadır. Bu yazıda, tüm API’lerde geçerli olan kooperatif vektörlerin arkasındaki kavramları inceleyecek ve bir örnek üzerinden OptiX API’si kullanarak uygulamasını göstereceğiz.

Matris İşlemleri Neden Önemli?

Çok katmanlı algılayıcı (MLP), birçok sinir ağı algoritmasının temel yapı taşıdır. Araştırmalar, MLP’lerin eğitildikleri etkileri doğru bir şekilde yeniden üretebileceğini göstermiştir. MLP’ler yeterince küçük olduğunda gerçek zamanlı çalışabilir ve fiziksel tabanlı gölgeleme gibi ilginç efektleri genellikle daha hızlı ve daha az bellek kullanımıyla işleyebilir.

Bir MLP genellikle bir dizi girdi, birkaç tam bağlantılı katman ve bir çıktı vektöründen oluşur. Katman vektörlerinin boyutları aynı olmak zorunda değildir.

Diagram of a multilayer perceptron with one input layer, two hidden layers, and an output layer.
Şekil 1. Bir girdi katmanı, iki gizli katman ve bir çıktı katmanına sahip bir MLP’nin diyagramı.

Her MLP değerlendirme (çıkarsama) aşaması, önceki katmanın değerlerinin ağırlıklı ve yanlı bir lineer kombinasyonunu ve isteğe bağlı bir aktivasyon fonksiyonu içerir. Bu lineer kombinasyonu gerçekleştirmek, matris-vektör çarpımı ve bir yanlı vektör eklemesi olarak özetlenebilir; bu duruma afine dönüşüm denir.

Herhangi iki afine dönüşümün bileşimi bir afine dönüşüm oluşturur. Bu nedenle, her katmanda yalnızca bir afine lineer artı yanlı fazın bulunması durumunda, tüm MLP her zaman tek bir afine dönüşüme indirgenebilir. MLP’ler, her katmanın afine sonucundan sonra uygulanan nonlinear aktivasyon fonksiyonları sayesinde, bu kadar ifade edici değildir.

Tam bağlantılı bir MLP’de, her nöron önceki katmandaki tüm nöronların bir fonksiyonudur. Şekil 1’de bu durum gösterilmektedir. Tek bir katmanın tam kavramsal hesaplaması Şekil 2’de gösterilmektedir.

Diagram of the conceptual building blocks of one layer of MLP evaluation: vector-matrix multiply, then addition of a bias vector, and finally application of the nonlinear activation function.
Şekil 2. Bir MLP katmanının değerlendirilmesi tipik olarak bir vektör-matris çarpımı, yanlı vektör eklenmesi ve bir nonlinear aktivasyon fonksiyonunun uygulanmasından oluşur.

Kooperatif Vektörler Neden Gerekli?

Kooperatif vektörlerin hedeflerinden biri, NVIDIA Tensor Cores‘u kullanarak matris işlemlerini hızlandırmaktır. Normalde, CUDA SIMT programlama modeli, bunun için aktif thread’lerin tam bir warpla çalışmasını gerektirir; ancak ray tracing programlama modeli, thread’leri bağımsız ele alır ve tam warplar sağlamaz. Ayrıca, Tensor Cores matris-matris çarpımı sağlar, ancak her ray tracing thread’i yalnızca vektör-matris çarpımına ihtiyaç duyar; bu da Tensor Cores’un tam kapasitesinin altında çalışmasına sebep olur.

Ayrıca, CUDA API’si belirli donanım sürümlerini hedef alır ve mimariden mimariye ileriye dönük uyumluluk garanti etmez. CUDA çok iş parçacıklı yaklaşımı ile matris çarpımının nasıl yapıldığı hakkında bilgi almak için Matris Çarpımı Arka Plan Kullanıcı Kılavuzu‘na göz atabilirsiniz.

Kooperatif vektörler, aşağıdaki özelliklerle bu sınırlamaları aşarak bir API sunmaktadır:

  • İşlemler için aktif olmayan thread’lerin bulunduğu warplarla matris işlemlerini yapabilme.
  • Donanım fraksiyonları arasında ileri ve geri uyumluluk sağlama.
  • Tensor Cores’un daha verimli kullanımını sağlamak için bir thread içinde vektör verilerini belirtme.

Kooperatif vektörler, bir warpdaki veri ve yürütme ayrışmasını ele alabilir; ancak bununla birlikte performansta bazı düşüşlere sebep olabilir. En iyi performans, bir warpdaki MLP ağırlıklarının aynı olduğu ve bir warpta tam bir thread sayısının bulunduğu durumlarda elde edilir. Shader Execution Reordering (SER) kullanmak, bu iki hedefe ulaşmaya yardımcı olabilir.

Bir MLP’yi değerlendirmenin bir dizi vektör-matris çarpımını içerdiğini göz önüne alırsak, bir warpdaki tüm thread’ler aynı MLP’yi yan yana değerlendirdiklerinde, kooperatif vektör API’si, birleşik warpin afine işlemini matris-matris çarpımı olarak ele alabilir. Burada “kooperatif” terimi; thread’lerin bir araya gelerek birkaç vektör-matris işlemini matris-matris işlemlerine dönüştürdüğünü ifade eder.

outputMatrix = inputMatrix × weightsMatrix + biasMatrix

Diagram of a matrix-matrix multiply, addition of a bias matrix, and  which can be used for parallel execution of a full warp of threads doing MLP layer evaluation.
Şekil 3. Bir warpin birleşik MLP katman değerlendirmesinin afine kısmı, matris-matris çarpımı artı yanlı bir matris içerir.

Burada, ağırlık matrisleri dışındaki tüm matrisler 32 satır yüksekliğinde olup, girdi, çıktı ve yanlı matrislerin her bir satırı ayrı bir thread için veriyi temsil etmektedir.

OptiX’de Kooperatif Vektörlerin Kullanımı

Kooperatif vektör, esasında bir dizi sınıfı olan şeffaf bir vektör türüdür ve herhangi bir uzunlukta olabilir. OptiX, MLP’lerin ve küçük sinir ağlarının değerlendirilmesini hızlandırmaya yardımcı olmak için belirli ve sınırlı bir işlem seti destekleyen “OptixCoopVec” adı verilen bir kooperatif vektör uygulaması sunmaktadır.

Kooperatif vektör API’sinde, yanlı ile matris çarpımının yapıldığı işlem optixCoopVecMatMul fonksiyonu ile gerçekleştirilir. Farklı aşamalarda farklı aktivasyon fonksiyonları kullanmak genellikle istenir, bu nedenle aktivasyon, vektör-matris çarpımından sonra ayrı olarak uygulanır ve kooperatif vektör API’si tarafından sağlanan vektör fonksiyonları setinden inşa edilebilir.

outputVector = inputVector × matrix + bias

Diagram of a vector-matrix multiply followed by addition of a bias vector, the conceptual building block of one layer of MLP evaluation.
Şekil 4. Bir thread’in MLP katman değerlendirmesinin afine kısmı, bir vektör-matris çarpımı ve yanlı bir vektör eklenmesi içerir.

OptiX, tüm RTX cihazlarında ve belirli sunucu sınıfı GPU’larda kooperatif vektörleri destekler. Cihaz desteğini kontrol etmek için optixDeviceContextGetProperty ile OPTIX_DEVICE_PROPERTY_COOP_VEC‘yi kullanabilirsiniz. Desteklenmeyen cihazlarda kooperatif vektör kullanımı hatalara neden olacaktır, çünkü herhangi bir geri dönüş desteği mevcut değildir.

Örnek Uygulama

Bu bölümde, OptiX shader’ında çıkarım yapmak için kooperatif vektörler API’sinin nasıl kullanıldığını keşfedeceğiz. Üzerinde çalışacağımız örnek, OptiX SDK içindeki optixNeuralTexture örneğinden uyarlanmıştır. Bu örnek, NTC SDK‘yı kullanarak dokuları eğitim (sıkıştırma) yapar, ağırlıkları ve yanlıları belirtilen bir dosya formatında saklar ve çeşitli durumlarla farklı gölgeleme dillerinde çıkarım (açma) göstermektedir. Kendi dokularınızı sıkıştırmak üzere NTC SDK’yı kullanabilir ve ardından optixNeuralTexture kullanarak görüntüleyebilirsiniz.

Kooperatif vektör kullanan kodlar, yoğun bir şekilde C++ şablonları kullanır. Şablonlar, derleyicinin performanslı kod üretmesini sağlayarak compile-time’da bilinen statik dizi boyutları ve veri türleri sunar. Kodun okunabilirliğini artırmak için yaygın olarak kullanılan türleri önceden tanımlamak yararlıdır.

using T_OUT = OptixCoopVec<float, 16 /*çıktı kanalları*/>;
...
T_OUT texel = inferTexel<T_OUT> ( 
latents, weights, x, y, ... );

Böylece, örneğin, şablonlanmış OptixCoopVec<> türü yerine T_OUT kısayolunu kullanabilirsiniz. evalMLP fonksiyonu, ekranda belirli bir piksel için tam bir MLP’yi değerlendirir. Pseudo-kod olarak özetlersek, MLP’ye girdi verileri ayarladıktan sonra, her katmanı değerlendirir ve son katmanın çıktısını döndürür:

template <class T_OUT>
evalMLP( T_OUT& outLayer, latents, mlpWeights, x, y )
{
    using T_IN  = OptixCoopVec<half, 48 /* girdi vektör boyutu */ >;
    using T_HID = OptixCoopVec<half, 64 /* gizli katman boyutu */ >;
    T_IN networkInputs = prepareNetworkInputs_FP16<T_IN>(x, y, latents);
    T_HID hiddenOut1 = evalLayer<T_IN, T_HID>(
        networkInputs, mlpWeights, 0, scaleBiasOffset, hiddenOut1);
    T_HID hiddenOut2 = evalLayer<T_HID, T_HID>(
        hiddenOut1, mlpWeights, weightOffset1, scaleBiasOffset, hiddenOut2);
    T_HID hiddenOut3 = evalLayer<T_HID, T_HID>(
        hiddenOut2, mlpWeights, weightOffset2, scaleBiasOffset, hiddenOut3 );
    outLayer = evalLayer<T_HID, T_OUT>(
        hiddenOut3, mlpWeights, weightOffset3, scaleBiasOffset, outLayer);
    return true;
}

Gözlemlenen bir durum; her katman değerlendirmesinin çıkışının bir sonraki katman değerlendirmesinin girişi haline gelmesidir. Katman değerlendirmesine daha yakından bakalım:

template <class T_IN, class T_OUT> evalLayer(
    T_IN&           inputArray,
    uint8_t*        weights,
    uint32_t        weightsOffsetInBytes,
    uint32_t&       biasOffsetInBytes,
    T_OUT&          outputArray )
{
  outputArray = optixCoopVecMatMul <
    T_OUT,
    T_IN,
    OPTIX_COOP_VEC_ELEM_TYPE_FLOAT8_E4M3, // girdi yorumlaması
    MAT_LAYOUT,                           // matris düzeni
    false,                                // transpose
    T_OUT::size,                          // N
    T_IN::size,                           // K
    OPTIX_COOP_VEC_ELEM_TYPE_FLOAT8_E4M3, // matris eleman türü
    OPTIX_COOP_VEC_ELEM_TYPE_FLOAT16      // yanlı eleman türü
  >(
    inputArray,                           // girdi vektörü
    weights,                              // matris temel ptr
    weightsOffsetInBytes,                 // matris ofseti
    weights,                              // yanlı temel ptr, aynı ağırlık
    biasOffsetInBytes                     // yanlı ofset
  );

  // bir sonraki katmanın ofsetini artır
  biasOffsetInBytes += T_OUT::size * sizeof( T_OUT::value_type );

  outputArray = activate<T_OUT>( outputArray );
}

Tek katman değerlendirmesi, optixCoopVecMatMul çevresındaki bir sargı gibidir. Matris çarpımından sonra, bir sonraki katman için yanlı vektörü hazırlamak üzere yanlı ofsetini artırmaktayız (bu durumda yanlı ofset bir referansla geçilmektedir). Sonrasında katmanın aktivasyon fonksiyonunu çağırırız.

Bu kod örneklerinde dikkat çeken noktaların başında, evalLayer fonksiyonuna birden çok çağrıda aynı ağırlık temel işaretçisini geçtiklerini görebilirsiniz. Aynı temel işaretçiyi kullandığımız için, her katmanda doğru verileri bulmak amacıyla temel işaretçisine sabit ofset değerleri ekliyoruz; bu durumda ya weightsOffsetInBytes ya da biasOffsetInBytes ekliyoruz.

Bu kod yazım tarzının iki ana nedeni bulunmaktadır. Öncelikle, NTC formatındaki dosyaları okuduğunuzda API, ağırlık matrisleri ve yanlı vektörlerin sıkı bir şekilde paketlendiği bir bellek bloğu verir ve her katmanın verileri için basit aritmetik kullanarak veri üzerinde dönebilirsiniz. İkinci olarak ise, kooperatif vektör API’si, aynı ağırlık temel işaretçilerini ardışık optixCoopVecMatMul çağrılarında bileşiktir. Derleyici, yeniden kullanılan temel işaretçileri fark eder (sabit sabit ofsetler kullanılsa bile) ve programınızı optimize eder ki gereksiz shuffling ve unshuffling işlemleri katmanlar arasında gerçekleşmesin.

Son olarak, aktivasyon fonksiyonuna bakalım:

template<class T_IN> 
VecT activate(const T_IN& x, bool scaleActivation=true)
{
    T_IN tmp    = optixCoopVecFFMA( x, T_IN( 1.0f/3.0f ), T_IN( 0.5f ) );
    tmp         = optixCoopVecMin( optixCoopVecMax( tmp, 0.0f ), 1.f ); // kısıtlama
    T_IN result = optixCoopVecMin( x, 3.0f );
    result      = optixCoopVecMul( result, tmp );
    if( scaleActivation )
         result = optixCoopVecFFMA( result, T_IN(invStep), T_IN(bias) );
    return result;
}

Çünkü bir MLP aktivasyon fonksiyonu, katmanın çıktı vektöründeki her elemanına non-lineer bir eşleme uygular, yukarıda çağrılan kooperatif vektör fonksiyonları, matris işlemleri yerine vektör işlemleri olarak tasarlanmıştır. Aktivasyon uygulamak, katman ağırlık matrisi uygulamak gibi genellikle çok daha küçük ve daha maliyetli bir işlemdir. Kooperatif vektörlerde, MLP aktivasyonları bağlamında genellikle bulabileceğiniz sınırlı bir vektör fonksiyonları seti mevcuttur; örneğin tanh, log2, exp2, min, max, ffma vb. gibi.

Bazı kooperatif vektör fonksiyonları, skalar parametreler alan varyantlara sahip olsa da, hepsi böyle değildir. Bazı durumlarda, bir skalari kooperatif vektörde sabit bir değerle oluşturmanız gerekecektir. Bu, örneğin T_IN(0.5f) parametresi ile gerçekleştirilebilir. Bu özel aktivasyon fonksiyonu, NTC SDK‘dan gelmektedir. Kendi ağınızı tasarlarken, aktivasyon çağrısı, iyi bilinen ReLU aktivasyonu simüle etmek amacıyla optixCoopVecMax çağrısını kullanmak kadar basit olabilir.

Sinirsel Grafikler

Kooperatif vektörler, RTX Sinir Şemaları ve RTX Sinirsel Doku Sıkıştırma uygulamalarında kullanılmaktadır. Bu uygulamalar, bu teknolojileri kullanmayı ve entegre etmeyi kolaylaştırmak için bir açık kaynak kodlar dizisi olan NVIDIA RTX Kit içinde mevcuttur. NVIDIA RTX Kit ile sinirsel render kullanmaya başlamak için bağlantılar ve kaynaklar dahil olmak üzere yeterli bilgiyi Neural Rendering Using NVIDIA RTX Kit başlıklı yazıda bulabilirsiniz.

Rendered scene with a dragon flying over craggy rock cliffs, with a blue sky background.
Şekil 5. NVIDIA NTC SDK ve NVIDIA RTX Mega Geometry kullanılarak render edilmiş ejderha sahnesi.

Şekil 5’te yer alan ejderhanın dokuları, bir GeForce RTX 5080 GPU’nun 16 GB’lık video belleğine sığacak şekilde sıkıştırılması gerekmekteydi. Ejderhanın kendisi, beş katmanlı 100’den fazla 8K UDIM dokusuna sahiptir. Dokular dosyalardan belleğe açıldığında, 32 GB’tan fazla VRAM tüketmektedir; bu ise 5080 üzerindeki mevcut bellekten neredeyse iki kat fazladır.

NTC ile birlikte, ejderha dokularının bellek ayak izi 3 GB altında bir değere düşmekte ve bu da BC sıkıştırmalı dokulara göre yaklaşık iki kat daha az yer kaplamaktadır. Bu, sahnedeki diğer dokuların yanı sıra geometri, animasyon, BVH ve gölgelere yer açarak, bu devasa üretim ölçeğindeki sahnenin tek bir 5080 GPU’su ile gerçek zamanlı render edilmesini sağlamaktadır.

Sinir şemaları, RTX Sinir Şeması SDK‘sında sergilenmektedir; bu örnek, kendi sinirsel gölgeleme ağlarınızı eğitmek ve ardından bunları normal grafik renderleme sırasında kullanımınıza sunmak için yardımcı olur. Kooperatif vektörler, Gerçek Zamanlı Sinirsel Görünüm Modelleri‘ni uygulamak için bir yol olabilir.

Performans Dikkat Noktaları

En iyi performansı elde etmek için aşağıdakilere dikkat edilmelidir:

  • Shuffling ve Unshuffling: Tensor Cores kullanmak için, optixCoopVecMatMul çağrısından önce veriler warplar arasında shuffle edilir ve sonrasında unshuffled edilir. İki çağrı arasında yalnızca desteklenen vektör işlemleri kullanıldığında, unshuffling ve reshuffling ihtiyacını ortadan kaldırmak, performansı artırır.
  • Tam Warplar: Performans, tam warplar kullanıldığında en iyi şekilde oluşur. Thread’leri bir araya getirmek için SER kullanın ve optixCoopVecMatMul çağrısını dinamik koşullar içindeki durumlarda yapmaktan kaçının.
  • Bellek Düzeni: Ağırlık matrislerinin düzeni performansı önemli ölçüde etkiler. OptiX, en iyi performansı sağlamak için eğitim ve çıkarım için optimal düzenleri desteklemektedir. Matrisleri optimal düzenlere dönüştürmek için optixCoopVecMatrixConvert kullanın.

OptiX Kooperatif Vektörlerle Eğitim

OptiX, kooperatif vektörlerle eğitim desteği sunmakta olup, ileri ve geri yayılım işlemlerini kapsamaktadır. Daha fazla bilgi için OptiX Programlama Kılavuzu‘na göz atabilirsiniz. Özellikle, optixCoopVecReduceSumAccumulate ve optixCoopVecOuterProductAccumulate adlı iki cihaz içi intristik, yanlı vektörler ve ağırlık matrisleri üzerinden kayıp değerlerini biriktirmeye yardımcı olmaktadır.

Başlayın!

Kooperatif vektörler, OptiX shader programları içinde yüksek performanslı vektör ve matris işlemleri yapmak için kullanılan bir NVIDIA OptiX veri türü ve API’sidir. Bu vektör ve matris işlemleri, çok katmanlı algılayıcılar (MLP’ler) gibi yaygın makine öğrenimi algoritmalarının kalbinde yer almaktadır. Kooperatif vektörler, NVIDIA RTX GPU’larında Tensor Cores kullanımını kolaylaştırmaktadır; bu, önceki uygulamalar için thread’ler arasında açık bir koordinasyonu gerektiriyordu. Artık geliştirmecilerin senkronize çok iş parçacıklı teknikler kullanmasına gerek kalmadan, bu sinir ağı algoritmaları için gerekli etkili matris-vektör çarpım işlemlerini daha kolay bir tek thread programlama tarzıyla gerçekleştirebilmektedirler.

Kooperatif vektörler, NVIDIA OptiX SDK 9.0 itibarıyla kullanılabilir hale gelmiştir. Kooperatif vektör API’leri, DirectX’te Agility SDK önizlemesi üzerinden en geç Nisan ayı sonunda, Vulkan ve Slang gibi platformlarda da tanıtılmaktadır; böylece, donanım hızlandırmalı ray-tracing’in desteklendiği her yerde kullanılabilirler. OptiX kooperatif vektör API’si için belgeler, OptiX Programlama Kılavuzu‘nda, hem çevrimiçi hem de SDK ile birlikte dağıtılan PDF formatında mevcuttur.

OptiX SDK’sında, RTX Sinirsel Doku Sıkıştırma için bir çıkarım örneği olan optixNeuralTexture bulunmaktadır. Bu örnek, kooperatif vektörleri kullanarak, sinyal anlık gölgeleme sırasında sinir sıkıştırmalı dokuları açar ve popüler BC5 veya BC6 sıkıştırma formatına göre 20 kat bellekle ilgili tasarruf sağlamaktadır; veya optixMeshViewer örneğinde kullanılan sıkıştırılmamış dokulara göre 80 katlık dokusal ayak izi tasarrufu sağlamaktadır.

Kooperatif vektörlerin yeni ve ilginç kullanım örneklerinin zamanla ortaya çıkması heyecan verici olacaktır. Daha fazla bilgi edinmek ve yaşadıklarınızı paylaşmak üzere OptiX NVIDIA Geliştirici Forumu‘na katılın.

Kaynak

Nvdia Blog

Düşüncenizi Paylaşın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir

İlgili Teknoloji Haberleri