Operators in MXNet

11377 ワード

Operators in MXNet
MXNetのoperatorは、実際の計算と追加情報を含んでいるクラスです.これらの付加情報は、元の場所の更新や自動微分などの最適化を実現するためのシステムに役立ちます.この文書を続ける前に、まずmshadowライブラリを理解することを強く提案します.すべてのoperatorの計算は、システムから提供されるデータ構造mshadow::TBobに基づいているので、このデータ構造はテンソル(Tensor)に似ています.MXNetのoperatorのインターフェースは、霊活性に努めています.現在提供されている柔軟性は、以下の点を含みます.
  • 特定の元の場所でデータを更新することによってメモリを節約する.
  • pythonの端にいくつかの内部パラメータを隠します.コードが綺麗です.
  • 入力tenssorと出力tenssorの関係を定義します.このようにシステムはタイプ検査を助けてくれます.
  • は、システムから追加の空間を申請して計算を行う(e.g.calling cudnn routines).
  • Operator Interface
    operatorコアのインターフェースはForwardです.
    virtual void Forward(const OpContext &ctx,
                         const std::vector &in_data,
                         const std::vector &req,
                         const std::vector &out_data,
                         const std::vector &aux_states) = 0;
    
  • OpContextのデータ構造は以下のコードです.
    struct OpContext {
      int is_train;
      RunContext run_ctx;
      std::vector requested;
    }
    
    、オプラテラーがtrinかそれともtestかを知ることができます.operatorは、どのdevice(is_train)上で動作し、次のパラメータによって追加のリソースを申請するかどうかを決定します.
  • run_ctxin_dataは、それぞれ入力tenssorと出力tenssorを表しています.すべてのtenssorに必要な空間は、システムが申請と管理を行うものです.
  • out_dataは、計算された構造がreqにどのように書き込まれているかを示しています.言い換えれば、out_dataおよびreq.size() == out_data.size()は、req[i]にどのように書き込まれているかに関係しています.out_data[i]は、以下のように定義されています.
    enum OpReqType {
      kNullOp,
      kWriteTo,
      kWriteInplace,
      kAddTo
    };
    
  • 一般的には、OpReqTypeのすべてのタイプはout_dataであるべきです.kWriteToに代表されるtenssorが直接書き込むことができる元のメモリブロックを提供していることを示しています.場合によっては、例えばout_dataを表すtenssorを計算する時には、本来の結果をそのまま上書きするのではなく、勾配を積み重ねるのが一番いいです.このように計算するたびに必要なメモリ空間を申請する必要はありません.この場合、gradientのタイプはreqであるべきです.
  • kAddToは、計算を容易にするために必要な追加のtenssorを表しています.今は使われていません.
  • += operatorを除いて、ユーザーは場合によってはaux_statesインターフェースを実装する必要があります.以下のように定義されています.
    virtual void Backward(const OpContext &ctx,
                          const std::vector &out_grad,
                          const std::vector &in_data,
                          const std::vector &out_data,
                          const std::vector &req,
                          const std::vector &in_grad,
                          const std::vector &aux_states);
    
    BackwardのインターフェースはForwardと同じ設計原則に従っています.BackwardForwardout_gradはoperator計算in_dataに必須である以外に、他の入力はout_dataと同じです.命名ポリシーとtouchの約束は似ています.以下の図でまとめられます.
    [input/output semantics figure]
    一部のoperatorは、必要なin_gradForwardout_gradのパラメータではなく、この需要は、in_dataのインターフェースout_dataによって実現されてもよい.
    Operator Property
    このような可能性があります.convolutionにはいくつかの異なる実装があります.ユーザは、これらのアルゴリズムの中から最高性能を得るアルゴリズムを選択したいかもしれません.この目的を実現するために、operatorのsematicインターフェースを具体的な実装(OperatorPropertyクラス)から分離し、DeclareBackwardDependencyクラスに独立しています.Operatorインターフェースは以下の内容を含みます.
  • InferShape:
    virtual bool InferShape(std::vector *in_shape,
                            std::vector *out_shape,
                            std::vector *aux_shape) const = 0;
    
  • このインターフェースは2つの目的があります.(1)システムに各入力と出力のTensorのサイズを提供することで、システムはOperatorPropertyおよびOperatorPropertyを行う前に、対応するメモリを事前に申請することができます.(2)型式検査を行い、運転前に明らかなエラーがないことを確認します.Forwardのshopはシステムが自動的に設定されています.(依存する前のOperatorのBackwardによると)、このインターフェースはin_shapeに戻ります.システムが提供した情報がshopのプッシュオフを完了するのに足りないと思ったら、またはshopが一致しない時に異常を投げます.
  • Request Resource:一部の動作は、out_shapeなどの作業空間として追加のメモリを計算する必要があります.この場合、システムはこの部分のメモリを管理したほうがいいです.このようにシステムはメモリの重複利用などの最適化ができます.MXNetは2つのインターフェースを定義して目的を達成します.
    virtual std::vector ForwardResource(
        const std::vector &in_shape) const;
    virtual std::vector BackwardResource(
        const std::vector &in_shape) const;
    
    falseデータ構造です.(cudnnConvolutionForwardで)現在は一つのタイプのflagsしか含まれていません.
    struct ResourceRequest {
      enum Type {
        kRandom,  // get an mshadow::Random object
        kTempSpace,  // request temporay space
      };
      Type type;
    };
    
  • ResourceRequestおよびresource.hによって返された配列が非空である場合、システムは、ForwardResourceBackwardResourceおよびOperatorインターフェースのFowardパラメータを介して対応するリソースを提供する.簡単な例では、これらのリソースを取得するなら、以下のようにすることができる.
    auto tmp_space_res = ctx.requested[kTempSpace].get_space(some_shape, some_stream);
    auto rand_res = ctx.requested[kRandom].get_random(some_stream);
    
    具体的な例はBackwardを参照することができる.
  • Backward dependency:2つの異なるoperator signatureを見ましょう.ctxで使用されているsrc/operator/cudnn_convolution-inl.h変数は、FullyConnectedForwardで使用されていません.一方、out_dataではFullyConnectedBackwardのすべての変数が使用されています.したがって、このPoolingBackward tenssorは、backwardが必要でないときは、リリースする必要があります.ここでは、ゴミ回収(GC)の方法に挑戦があります.できるだけ速くしてください.このような状況に対して、インターフェースを提供します.
    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);
    
    ここのvectorのPoolingForward要素は、異なるarraysのidを区別しています.このインターフェースが、out_dataintとの異なる依存関係をどのように定義しているかを見てみましょう.
    virtual std::vector DeclareBackwardDependency(
        const std::vector &out_grad,
        const std::vector &in_data,
        const std::vector &out_data) const;
    
  • Inplace Option:メモリをさらに節約するために、私たちは元の場所で更新する傾向があります.これは主にelement-wise操作に使われます.この場合、入力tenssorと出力tenssorのshpeは一致します.このような状況に対して、次のインターフェースを提供しました.
    このインターフェースは、FullyConnectedおよびPoollingテンソスがin_data[0]の計算において同じメモリ空間を使用すべきであるとシステムに教えている.同様に、out_data[0]およびForwardは、同じメモリ空間を共有してout_grad[0]の計算において同じメモリ空間を使用すべきである.
    ATTENTION:Eveen with the abobove speciiiiication、it is not garanteted that input and outputtetesors will she the same spacace.In fact,thisisisisisisisis a hint for the sysstem forthe final dededesion the final dededeinininininininininininininininininininininininininininininininininininininininininininininininininininininininininindededededededededededesion.However.However,However the the the t need to considerthat.
  • Expose Operator to Python:c+プログラミング言語の制限のため、ユーザーが以下のインターフェースを実現する必要があります.
    Create Operator from Operator Property
    上記の内容でin_grad[0]に言及したことがありますが、すべての動作のセマンティカルトトリーを含みます.また、Backwardポインタを作成する必要があります.
    Create OperatorForwardの下のインターフェースを実装する:
    std::vector FullyConnectedProperty::DeclareBackwardDependency(
        const std::vector &out_grad,
        const std::vector &in_data,
        const std::vector &out_data) const {
      return {out_grad[0], in_data[0]};  // NOTE: out_data[0] is NOT included
    }
    std::vector PoolingProperty::DeclareBackwardDependency(
        const std::vector &out_grad,
        const std::vector &in_data,
        const std::vector &out_data) const {
      return {out_grad[0], in_data[0], out_data[0]};
    }
    
    For example:
    virtual std::vector<:pair void="">> ElewiseOpProperty::ForwardInplaceOption(
        const std::vector &in_data,
        const std::vector &out_data) const {
      return { {in_data[0], out_data[0]} };
    }
    virtual std::vector<:pair void="">> ElewiseOpProperty::BackwardInplaceOption(
        const std::vector &out_grad,
        const std::vector &in_data,
        const std::vector &out_data,
        const std::vector &in_grad) const {
      return { {out_grad[0], in_grad[0]} }
    }
    
    Parameeterize Operator
    私たちが畳み込みoperatorを実現するには、kenalサイズ、strideサイズ、paddingサイズなどの情報が必要です.これらはパラメータとしてoperatorに渡す必要があります.BackwardおよびOperatorProperty計算プロセスはこれらのパラメータが必要です.パラメータを伝えるためには、ユーザはOperatorデータ構造を定義する必要があります.
    // initial the property class from a list of key-value string pairs
    virtual void Init(const vector> &kwargs) = 0;
    // return the parameters in a key-value string map
    virtual map GetParams() const = 0;
    // return the name of arguments (for generating signature in python)
    virtual vector ListArguments() const;
    // return the name of output values
    virtual vector ListOutputs() const;
    // return the name of auxiliary states
    virtual vector ListAuxiliaryStates() const;
    // return the number of output values
    virtual int NumOutputs() const;
    // return the number of visible outputs
    virtual int NumVisibleOutputs() const;
    
    OperatorPropertyに入れて、クラス初期化の時に、このデータ構造をoperator類に伝えます.
    virtual Operator* CreateOperator(Context ctx) const = 0;
    
    Register Operator to MXNet
    次のマクロ定義を使用して、PArameter構造とOperatoProperty類をMXNetのシステムに登録します.
    class ConvolutionOp {
     public:
      void Forward( ... ) { ... }
      void Backward( ... ) { ... }
    };
    class ConvolutionOpProperty : public OperatorProperty {
     public:
      Operator* CreateOperator(Context ctx) const {
        return new ConvolutionOp;
      }
    };
    
    このマクロ定義の最初のパラメータはnameで、2番目のパラメータはPropertyクラスの名前です.
    All in a list
    Finally!We almost covered the interface we need to define a new operator.Let's do a recap in a list:
  • Use Forward interface to write your actual computation logic(Backward and ConvolutionParam).
  • Use ConvolutionOpProperty interface to:
  • Pass parameter to operator class(may use Operator interface).
  • Create operatousing Forward interface.
  • Correctly implement the operator description interface such as the names of argments,etc.
  • Correctly implement the Backward interface to set the output tensor shop.
  • [Optional]If additional resource are need,check OperatorProperty and Init.
  • [Optional]If CreateOperator does not need all the input and out put of InferShape,check ForwardResource.
  • [Optional]If inplace udate is supported,check BackwardResource and Backward.
  • Register the Forward class and the parameter class.
  • MXNetの旅を楽しんでください.