Apache MXNet - Sistem Bileşenleri

Burada Apache MXNet'teki sistem bileşenleri ayrıntılı olarak açıklanmıştır. İlk olarak, MXNet'teki yürütme motoru hakkında çalışacağız.

Yürütme Motoru

Apache MXNet'in yürütme motoru çok yönlüdür. Bunu derin öğrenmenin yanı sıra herhangi bir alana özgü problem için de kullanabiliriz: bağımlılıklarını takip eden bir dizi işlevi yürütmek Bağımlılıkları olan işlevler serileştirilirken, bağımlılığı olmayan işlevler paralel olarak çalıştırılabilecek şekilde tasarlanmıştır.

Çekirdek Arayüz

Aşağıda verilen API, Apache MXNet'in yürütme motoru için temel arayüzdür -

virtual void PushSync(Fn exec_fun, Context exec_ctx,
std::vector<VarHandle> const& const_vars,
std::vector<VarHandle> const& mutate_vars) = 0;

Yukarıdaki API aşağıdakilere sahiptir -

  • exec_fun - MXNet'in temel arayüz API'si, exec_fun adlı işlevi bağlam bilgisi ve bağımlılıkları ile birlikte yürütme motoruna göndermemize olanak tanır.

  • exec_ctx - Yukarıda belirtilen exec_fun işlevinin çalıştırılması gereken bağlam bilgisi.

  • const_vars - Bunlar, fonksiyonun okuduğu değişkenlerdir.

  • mutate_vars - Bunlar değiştirilecek değişkenlerdir.

Yürütme motoru, kullanıcısına, ortak bir değişkeni değiştiren herhangi iki işlevin yürütülmesinin, itme sırasına göre serileştirilmesi garantisini sağlar.

Fonksiyon

Apache MXNet yürütme motorunun işlev türü aşağıdadır -

using Fn = std::function<void(RunContext)>;

Yukarıdaki işlevde, RunContextçalışma zamanı bilgilerini içerir. Çalışma zamanı bilgileri, yürütme motoru tarafından belirlenmelidir. SözdizimiRunContext aşağıdaki gibidir−

struct RunContext {
   // stream pointer which could be safely cast to
   // cudaStream_t* type
   void *stream;
};

Aşağıda, yürütme motorunun işlevleri hakkında bazı önemli noktalar verilmiştir -

  • Tüm işlevler, MXNet'in yürütme motorunun dahili iş parçacıkları tarafından yürütülür.

  • İşlevin yürütme motoruna engellenmesini sağlamak iyi değildir çünkü bununla işlev yürütme iş parçacığını işgal edecek ve ayrıca toplam verimi azaltacaktır.

Bunun için MXNet, aşağıdaki gibi başka bir eşzamansız işlev sağlar−

using Callback = std::function<void()>;
using AsyncFn = std::function<void(RunContext, Callback)>;
  • Bunda AsyncFn işlevi iş parçacıklarımızın ağır kısmını geçebiliriz, ancak yürütme motoru, biz çağrılıncaya kadar işlevin bitmiş olduğunu düşünmez. callback işlevi.

Bağlam

İçinde Contextiçinde yürütülecek işlevin bağlamını belirleyebiliriz. Bu genellikle aşağıdakileri içerir -

  • İşlevin bir CPU veya GPU'da çalıştırılıp çalıştırılmayacağı.

  • Bağlamda GPU belirtirsek, hangi GPU'nun kullanılacağı.

  • Context ve RunContext arasında çok büyük bir fark var. Bağlam, cihaz türüne ve cihaz kimliğine sahipken, RunContext yalnızca çalışma süresi sırasında karar verilebilecek bilgilere sahiptir.

VarHandle

Fonksiyonların bağımlılıklarını belirtmek için kullanılan VarHandle, fonksiyonun değiştirebileceği veya kullanabileceği harici kaynakları temsil etmek için kullanabileceğimiz bir belirteç gibidir (özellikle yürütme motoru tarafından sağlanır).

Ancak şu soru ortaya çıkıyor, neden VarHandle'ı kullanmamız gerekiyor? Bunun nedeni, Apache MXNet motorunun diğer MXNet modüllerinden ayrıştırılacak şekilde tasarlanmış olmasıdır.

Aşağıda, VarHandle hakkında bazı önemli noktalar verilmiştir -

  • Hafiftir, bu nedenle bir değişkeni oluşturmak, silmek veya kopyalamak çok az işletim maliyeti gerektirir.

  • Değişmez değişkenleri, yani burada kullanılacak değişkenleri belirtmemiz gerekir. const_vars.

  • Değişken değişkenleri, yani içinde değiştirilecek değişkenleri belirtmemiz gerekir. mutate_vars.

  • Yürütme motoru tarafından işlevler arasındaki bağımlılıkları çözmek için kullanılan kural, herhangi iki işlevin, bunlardan biri en az bir ortak değişkeni değiştirdiğinde çalıştırılmasının, itme sırasına göre serileştirilmesidir.

  • Yeni bir değişken oluşturmak için, NewVar() API.

  • Bir değişkeni silmek için, PushDelete API.

İşleyişini basit bir örnekle anlayalım -

Farz edelim ki F1 ve F2 olmak üzere iki fonksiyonumuz var ve her ikisi de değişkeni, yani V2'yi değiştiriyorlar. Bu durumda, F1'den sonra F2'ye basılırsa, F2'nin F1'den sonra yürütülmesi garanti edilir. Öte yandan, F1 ve F2'nin her ikisi de V2 kullanıyorsa, gerçek yürütme sırası rastgele olabilir.

İt ve Bekle

Push ve wait yürütme motorunun iki kullanışlı API'sidir.

Aşağıdakiler iki önemli özelliktir: Push API:

  • Tüm Push API'leri eşzamansızdır, yani API çağrısı, itilen işlevin bitmiş olup olmadığına bakılmaksızın hemen geri döner.

  • Push API, iş parçacığı açısından güvenli değildir, bu, bir seferde yalnızca bir iş parçacığının motor API çağrıları yapması gerektiği anlamına gelir.

Şimdi Wait API hakkında konuşursak, aşağıdaki noktalar bunu temsil eder -

  • Bir kullanıcı belirli bir işlevin tamamlanmasını beklemek isterse, kapanışa bir geri arama işlevi eklemelidir. Dahil edildikten sonra, işlevin sonundaki işlevi çağırın.

  • Öte yandan, bir kullanıcı belirli bir değişkeni içeren tüm fonksiyonların bitmesini beklemek istiyorsa, kullanmalıdır WaitForVar(var) API.

  • Birisi tüm itilen işlevlerin bitmesini beklemek isterse, WaitForAll () API.

  • Fonksiyonların bağımlılıklarını belirtmek için kullanılır, bir simge gibidir.

Operatörler

Apache MXNet'teki Operatör, gerçek hesaplama mantığının yanı sıra yardımcı bilgiler içeren ve sistemin optimizasyonu gerçekleştirmesine yardımcı olan bir sınıftır.

Operatör arayüzü

Forward sözdizimi aşağıdaki gibi olan çekirdek operatör arayüzüdür:

virtual void Forward(const OpContext &ctx,
const std::vector<TBlob> &in_data,
const std::vector<OpReqType> &req,
const std::vector<TBlob> &out_data,
const std::vector<TBlob> &aux_states) = 0;

Yapısı OpContext, içinde tanımlanmıştır Forward() Şöyleki:

struct OpContext {
   int is_train;
   RunContext run_ctx;
   std::vector<Resource> requested;
}

OpContextOperatörün durumunu (ister tren ister test aşamasında olsun), operatörün hangi cihazda çalıştırılması gerektiğini ve ayrıca talep edilen kaynakları açıklar. iki daha kullanışlı yürütme motoru API'si.

Yukarıdan Forward çekirdek arayüz, istenen kaynakları aşağıdaki gibi anlayabiliriz -

  • in_data ve out_data giriş ve çıkış tensörlerini temsil eder.

  • req hesaplamanın sonucunun nasıl yazıldığını gösterir out_data.

OpReqType şu şekilde tanımlanabilir -

enum OpReqType {
   kNullOp,
   kWriteTo,
   kWriteInplace,
   kAddTo
};

Gibi Forward operatör, isteğe bağlı olarak uygulayabiliriz Backward arayüz aşağıdaki gibidir -

virtual void Backward(const OpContext &ctx,
const std::vector<TBlob> &out_grad,
const std::vector<TBlob> &in_data,
const std::vector<TBlob> &out_data,
const std::vector<OpReqType> &req,
const std::vector<TBlob> &in_grad,
const std::vector<TBlob> &aux_states);

Çeşitli görevler

Operator arayüz, kullanıcıların aşağıdaki görevleri yapmasına izin verir -

  • Kullanıcı yerinde güncellemeleri belirleyebilir ve bellek ayırma maliyetini düşürebilir

  • Daha temiz hale getirmek için, kullanıcı bazı dahili argümanları Python'dan gizleyebilir.

  • Kullanıcı, tensörler ve çıkış tensörleri arasındaki ilişkiyi tanımlayabilir.

  • Hesaplama yapmak için, kullanıcı sistemden ek geçici alan elde edebilir.

Operatör Özelliği

Evrişimli sinir ağında (CNN), bir evrişimin birkaç uygulamaya sahip olduğunun farkındayız. Bunlardan en iyi performansı elde etmek için, bu birkaç evrişim arasında geçiş yapmak isteyebiliriz.

Apache MXNet'in operatör semantik arayüzünü uygulama arayüzünden ayırmasının nedeni budur. Bu ayrılık şeklinde yapılırOperatorProperty Aşağıdakilerden oluşan sınıf−

InferShape - InferShape arayüzünün aşağıda verildiği gibi iki amacı vardır:

  • İlk amaç, sisteme her bir giriş ve çıkış tensörünün boyutunu söylemektir, böylece alan daha önce tahsis edilebilir. Forward ve Backward aramak.

  • İkinci amaç, çalıştırmadan önce herhangi bir hata olmadığından emin olmak için bir boyut kontrolü yapmaktır.

Sözdizimi aşağıda verilmiştir -

virtual bool InferShape(mxnet::ShapeVector *in_shape,
mxnet::ShapeVector *out_shape,
mxnet::ShapeVector *aux_shape) const = 0;

Request Resource- Sisteminiz cudnnConvolutionForward gibi işlemler için hesaplama çalışma alanını yönetebiliyorsa ne olur? Sisteminiz, alanı yeniden kullanma ve daha pek çok optimizasyon gerçekleştirebilir. Burada MXNet, iki arayüzün yardımıyla bunu kolayca başarır

virtual std::vector<ResourceRequest> ForwardResource(
   const mxnet::ShapeVector &in_shape) const;
virtual std::vector<ResourceRequest> BackwardResource(
   const mxnet::ShapeVector &in_shape) const;

Ama ya ForwardResource ve BackwardResourceboş olmayan diziler döndürülsün mü? Bu durumda, sistem ilgili kaynakları aşağıdakiler aracılığıyla sunar:ctx parametresindeki Forward ve Backward arayüzü Operator.

Backward dependency - Apache MXNet, geriye dönük bağımlılıkla başa çıkmak için aşağıdaki iki farklı operatör imzasına sahiptir -

void FullyConnectedForward(TBlob weight, TBlob in_data, TBlob out_data);
void FullyConnectedBackward(TBlob weight, TBlob in_data, TBlob out_grad, TBlob in_grad);
void PoolingForward(TBlob in_data, TBlob out_data);
void PoolingBackward(TBlob in_data, TBlob out_data, TBlob out_grad, TBlob in_grad);

Burada dikkat edilmesi gereken iki önemli nokta -

  • FullyConnectedForward'daki out_data, FullyConnectedBackward tarafından kullanılmaz ve

  • PoolingBackward, PoolingForward'ın tüm bağımsız değişkenlerini gerektirir.

Bu yüzden FullyConnectedForward, out_datatensör bir kez tüketildiğinde güvenli bir şekilde serbest bırakılabilir çünkü geri işlevin buna ihtiyacı olmayacaktır. Bu sistemin yardımıyla, bazı tensörleri olabildiğince erken bir zamanda çöp olarak toplamaya başladık.

In place Option- Apache MXNet, bellek ayırma maliyetinden tasarruf etmek için kullanıcılara başka bir arayüz sağlar. Arayüz, hem giriş hem de çıkış tensörlerinin aynı şekle sahip olduğu eleman bazlı işlemler için uygundur.

Yerinde güncellemeyi belirtmek için sözdizimi aşağıdadır -

Operatör Oluşturma Örneği

OperatorProperty yardımı ile bir operatör oluşturabiliriz. Bunu yapmak için aşağıda verilen adımları izleyin -

virtual std::vector<std::pair<int, void*>> ElewiseOpProperty::ForwardInplaceOption(
   const std::vector<int> &in_data,
   const std::vector<void*> &out_data) 
const {
   return { {in_data[0], out_data[0]} };
}
virtual std::vector<std::pair<int, void*>> ElewiseOpProperty::BackwardInplaceOption(
   const std::vector<int> &out_grad,
   const std::vector<int> &in_data,
   const std::vector<int> &out_data,
   const std::vector<void*> &in_grad) 
const {
   return { {out_grad[0], in_grad[0]} }
}

Aşama 1

Create Operator

İlk önce OperatorProperty'de aşağıdaki arayüzü uygulayın:

virtual Operator* CreateOperator(Context ctx) const = 0;

Örnek aşağıda verilmiştir -

class ConvolutionOp {
   public:
      void Forward( ... ) { ... }
      void Backward( ... ) { ... }
};
class ConvolutionOpProperty : public OperatorProperty {
   public:
      Operator* CreateOperator(Context ctx) const {
         return new ConvolutionOp;
      }
};

Adım 2

Parameterize Operator

Bir evrişim operatörü uygulayacaksanız, çekirdek boyutunu, adım boyutunu, dolgu boyutunu vb. Bilmek zorunludur. Neden, çünkü bu parametreler herhangi bir çağrı yapılmadan önce operatöre iletilmelidir.Forward veya backward arayüz.

Bunun için bir tanımlamamız gerekiyor ConvolutionParam aşağıdaki gibi yapı -

#include <dmlc/parameter.h>
struct ConvolutionParam : public dmlc::Parameter<ConvolutionParam> {
   mxnet::TShape kernel, stride, pad;
   uint32_t num_filter, num_group, workspace;
   bool no_bias;
};

Şimdi bunu içine koymalıyız ConvolutionOpProperty ve operatöre aşağıdaki şekilde iletin -

class ConvolutionOp {
   public:
      ConvolutionOp(ConvolutionParam p): param_(p) {}
      void Forward( ... ) { ... }
      void Backward( ... ) { ... }
   private:
      ConvolutionParam param_;
};
class ConvolutionOpProperty : public OperatorProperty {
   public:
      void Init(const vector<pair<string, string>& kwargs) {
         // initialize param_ using kwargs
      }
      Operator* CreateOperator(Context ctx) const {
         return new ConvolutionOp(param_);
      }
   private:
      ConvolutionParam param_;
};

Aşama 3

Register the Operator Property Class and the Parameter Class to Apache MXNet

Son olarak, Operatör Özellik Sınıfını ve Parametre Sınıfını MXNet'e kaydetmemiz gerekiyor. Aşağıdaki makroların yardımı ile yapılabilir -

DMLC_REGISTER_PARAMETER(ConvolutionParam);
MXNET_REGISTER_OP_PROPERTY(Convolution, ConvolutionOpProperty);

Yukarıdaki makroda, ilk argüman ad dizesidir ve ikincisi özellik sınıfı adıdır.