Apache MXNet - Komponen Sistem

Di sini, komponen sistem di Apache MXNet dijelaskan secara mendetail. Pertama, kita akan mempelajari tentang mesin eksekusi di MXNet.

Mesin Eksekusi

Mesin eksekusi Apache MXNet sangat serbaguna. Kita dapat menggunakannya untuk pembelajaran mendalam serta masalah khusus domain apa pun: menjalankan banyak fungsi mengikuti dependensinya. Ini dirancang sedemikian rupa sehingga fungsi dengan dependensi diserialkan sedangkan, fungsi tanpa dependensi dapat dijalankan secara paralel.

Antarmuka Inti

API yang diberikan di bawah ini adalah antarmuka inti untuk mesin eksekusi Apache MXNet -

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

API di atas memiliki yang berikut -

  • exec_fun - API antarmuka inti MXNet memungkinkan kita untuk mendorong fungsi bernama exec_fun, bersama dengan informasi konteks dan ketergantungannya, ke mesin eksekusi.

  • exec_ctx - Informasi konteks di mana fungsi yang disebutkan di atas exec_fun harus dijalankan.

  • const_vars - Ini adalah variabel yang dibaca oleh fungsi.

  • mutate_vars - Ini adalah variabel yang akan dimodifikasi.

Mesin eksekusi memberikan jaminan kepada penggunanya bahwa eksekusi dua fungsi yang mengubah variabel umum diserialkan dalam urutan push mereka.

Fungsi

Berikut adalah jenis fungsi mesin eksekusi Apache MXNet -

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

Dalam fungsi di atas, RunContextberisi informasi runtime. Informasi runtime harus ditentukan oleh mesin eksekusi. Sintaks dariRunContext adalah sebagai berikut-

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

Di bawah ini diberikan beberapa poin penting tentang fungsi mesin eksekusi -

  • Semua fungsi dijalankan oleh thread internal mesin eksekusi MXNet.

  • Tidaklah baik untuk mendorong fungsi pemblokiran ke mesin eksekusi karena dengan itu fungsi tersebut akan menempati thread eksekusi dan juga akan mengurangi total throughput.

Untuk MXNet ini menyediakan fungsi asinkron lainnya sebagai berikut-

using Callback = std::function<void()>;
using AsyncFn = std::function<void(RunContext, Callback)>;
  • Di dalam AsyncFn fungsi kita dapat melewatkan bagian yang berat dari utas kita, tetapi mesin eksekusi tidak mempertimbangkan fungsi selesai sampai kita memanggil callback fungsi.

Konteks

Di Context, kita dapat menentukan konteks fungsi yang akan dijalankan di dalamnya. Ini biasanya termasuk yang berikut -

  • Apakah fungsi tersebut harus dijalankan pada CPU atau GPU.

  • Jika kita menentukan GPU dalam Konteks, lalu GPU mana yang akan digunakan.

  • Ada perbedaan besar antara Konteks dan RunContext. Konteks memiliki jenis perangkat dan id perangkat, sedangkan RunContext memiliki informasi yang hanya dapat ditentukan selama waktu proses.

VarHandle

VarHandle, digunakan untuk menentukan dependensi fungsi, seperti token (terutama disediakan oleh mesin eksekusi) yang dapat kita gunakan untuk mewakili sumber daya eksternal yang dapat dimodifikasi atau digunakan oleh fungsi.

Namun muncul pertanyaan, mengapa kita perlu menggunakan VarHandle? Itu karena, mesin Apache MXNet dirancang untuk dipisahkan dari modul MXNet lainnya.

Berikut adalah beberapa poin penting tentang VarHandle -

  • Ini ringan sehingga untuk membuat, menghapus, atau menyalin variabel memerlukan sedikit biaya pengoperasian.

  • Kita perlu menentukan variabel yang tidak dapat diubah yaitu variabel yang akan digunakan di const_vars.

  • Kita perlu menentukan variabel yang bisa berubah yaitu variabel yang akan dimodifikasi di mutate_vars.

  • Aturan yang digunakan oleh mesin eksekusi untuk menyelesaikan dependensi di antara fungsi adalah eksekusi dua fungsi saat salah satunya memodifikasi setidaknya satu variabel umum yang diserialkan dalam urutan push-nya.

  • Untuk membuat variabel baru, kita bisa menggunakan NewVar() API.

  • Untuk menghapus variabel, kita bisa menggunakan PushDelete API.

Mari kita pahami cara kerjanya dengan contoh sederhana -

Misalkan jika kita memiliki dua fungsi yaitu F1 dan F2 dan keduanya sama-sama memutasi variabel yaitu V2. Dalam hal ini, F2 dijamin akan dijalankan setelah F1 jika F2 didorong setelah F1. Di sisi lain, jika F1 dan F2 sama-sama menggunakan V2 maka urutan eksekusi sebenarnya bisa acak.

Dorong dan Tunggu

Push dan wait adalah dua API mesin eksekusi yang lebih berguna.

Berikut adalah dua fitur penting dari Push API:

  • Semua Push API bersifat asynchronous yang berarti bahwa panggilan API segera kembali terlepas dari apakah fungsi yang didorong sudah selesai atau belum.

  • Push API bukanlah thread safe, yang berarti hanya satu thread yang dapat melakukan panggilan API mesin pada satu waktu.

Sekarang jika kita berbicara tentang Wait API, poin berikut mewakilinya -

  • Jika pengguna ingin menunggu fungsi tertentu selesai, dia harus menyertakan fungsi panggilan balik di closure. Setelah disertakan, panggil fungsi tersebut di akhir fungsi.

  • Di sisi lain, jika pengguna ingin menunggu semua fungsi yang melibatkan variabel tertentu selesai, dia harus menggunakan WaitForVar(var) API.

  • Jika seseorang ingin menunggu semua fungsi yang didorong selesai, gunakan WaitForAll () API.

  • Digunakan untuk menentukan dependensi fungsi, seperti token.

Operator

Operator di Apache MXNet adalah kelas yang berisi logika komputasi aktual serta informasi tambahan dan membantu sistem dalam melakukan pengoptimalan.

Antarmuka Operator

Forward adalah antarmuka operator inti yang sintaksnya adalah sebagai berikut:

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;

Struktur OpContext, didefinisikan di Forward() adalah sebagai berikut:

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

Itu OpContextmenjelaskan status operator (baik dalam fase train atau pengujian), perangkat mana yang harus dioperasikan oleh operator dan juga resource yang diminta. dua API mesin eksekusi yang lebih berguna.

Dari atas Forward antarmuka inti, kita dapat memahami sumber daya yang diminta sebagai berikut -

  • in_data dan out_data mewakili tensor input dan output.

  • req menunjukkan bagaimana hasil komputasi ditulis ke dalam out_data.

Itu OpReqType dapat didefinisikan sebagai -

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

Seperti Forward operator, kami secara opsional dapat mengimplementasikan Backward antarmuka sebagai berikut -

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);

Berbagai tugas

Operator antarmuka memungkinkan pengguna untuk melakukan tugas-tugas berikut -

  • Pengguna dapat menentukan pembaruan di tempat dan dapat mengurangi biaya alokasi memori

  • Untuk membuatnya lebih bersih, pengguna dapat menyembunyikan beberapa argumen internal dari Python.

  • Pengguna dapat menentukan hubungan antara tensor dan tensor keluaran.

  • Untuk melakukan komputasi, pengguna dapat memperoleh ruang sementara tambahan dari sistem.

Properti Operator

Seperti yang kita ketahui bahwa dalam jaringan saraf konvolusional (CNN), satu konvolusi memiliki beberapa implementasi. Untuk mencapai kinerja terbaik dari mereka, kita mungkin ingin beralih di antara beberapa konvolusi tersebut.

Itulah alasannya, Apache MXNet memisahkan antarmuka semantik operator dari antarmuka implementasi. Pemisahan ini dilakukan dalam bentukOperatorProperty kelas yang terdiri dari berikut-

InferShape - Antarmuka InferShape memiliki dua tujuan seperti yang diberikan di bawah ini:

  • Tujuan pertama adalah memberi tahu sistem ukuran setiap input dan output tensor sehingga ruang dapat dialokasikan sebelumnya Forward dan Backward panggilan.

  • Tujuan kedua adalah melakukan pemeriksaan ukuran untuk memastikan tidak ada kesalahan sebelum menjalankan.

Sintaksnya diberikan di bawah ini -

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

Request Resource- Bagaimana jika sistem Anda dapat mengelola ruang kerja komputasi untuk operasi seperti cudnnConvolutionForward? Sistem Anda dapat melakukan pengoptimalan seperti penggunaan kembali ruang dan banyak lagi. Di sini, MXNet dengan mudah mencapai ini dengan bantuan dua antarmuka berikut-

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

Tapi, bagaimana jika file ForwardResource dan BackwardResourcemengembalikan array yang tidak kosong? Dalam hal ini, sistem menawarkan sumber daya yang sesuai melaluictx parameter di Forward dan Backward antarmuka Operator.

Backward dependency - Apache MXNet memiliki dua tanda tangan operator yang berbeda untuk menangani ketergantungan mundur -

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);

Di sini, dua hal penting yang perlu diperhatikan -

  • Out_data di FullyConnectedForward tidak digunakan oleh FullyConnectedBackward, dan

  • PoolingBackward membutuhkan semua argumen PoolingForward.

Itulah mengapa untuk FullyConnectedForward, itu out_datatensor yang pernah dikonsumsi dapat dibebaskan dengan aman karena fungsi mundur tidak memerlukannya. Dengan bantuan sistem ini dapat mengumpulkan beberapa tensor sampah sedini mungkin.

In place Option- Apache MXNet menyediakan antarmuka lain kepada pengguna untuk menghemat biaya alokasi memori. Antarmuka sesuai untuk operasi bijak-elemen di mana tensor input dan output memiliki bentuk yang sama.

Berikut ini adalah sintaks untuk menentukan pembaruan di tempat -

Contoh Membuat Operator

Dengan bantuan OperatorProperty kita bisa membuat operator. Untuk melakukannya, ikuti langkah-langkah yang diberikan di bawah ini -

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]} }
}

Langkah 1

Create Operator

Pertama-tama, terapkan antarmuka berikut di OperatorProperty:

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

Contoh diberikan di bawah ini -

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

Langkah 2

Parameterize Operator

Jika Anda akan menerapkan operator konvolusi, Anda wajib mengetahui ukuran kernel, ukuran langkah, ukuran bantalan, dan sebagainya. Mengapa, karena parameter ini harus diteruskan ke operator sebelum memanggil apapunForward atau backward antarmuka.

Untuk ini, kita perlu mendefinisikan file ConvolutionParam struktur seperti di bawah ini -

#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;
};

Sekarang, kita perlu memasukkan ini ConvolutionOpProperty dan berikan ke operator sebagai berikut -

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_;
};

LANGKAH 3

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

Terakhir, kita perlu mendaftarkan Kelas Properti Operator dan Kelas Parameter ke MXNet. Itu dapat dilakukan dengan bantuan makro berikut -

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

Dalam makro di atas, argumen pertama adalah string nama dan yang kedua adalah nama kelas properti.