C ++の拡張機能でPyTorch

今日の話をするためには、C ++ PyTorchで拡張すること。

公式の開始前に、我々はPyTorchをカスタマイズする方法を理解する必要がありますmoduleこれらのうち、最も一般的には、Pythonで継承されtorch.nn.Module、独自のモジュールに組み立てることPyTorchすでに演算子で、。この方法は簡単ですが、我々は機能を実現したい場合は必ずしも最高の計算効率は、加えて、あまりにも複雑で、それらの既存の機能は、我々の要求を満たすことができないだろうPyTorchことがあります。このとき、Cで、C ++、CUDAはPyTorchモジュールを拡張するために最良の選択です。

これらのシステムは基本的にCに拡張インターフェースが存在しているので、電流による市場への深い学習システム(等TensorFlow、PyTorch)のほとんどは、バックエンドC、C ++の構築物に基づいて、C ++です。トーチPyTorchに基づいて構築が、底トーチは拡張することは困難ではないC PyTorchと、本質的に互換性のあるC言語を使用し、C PyTorchれます。PyTorch1.0のリリースに伴い、職員はcaffe2に置き換え基礎となるコードのPyTorchを検討し始めているので、彼らは徐々に現在、C ++の拡張ライブラリPyTorch使用であるATENし、再構築されています。一般的には、C ++は将来の傾向です。冒頭に建物の中にシステムの使用上のほぼすべての深さの調査ツールであるCUDA、については、そのCUDA拡張インタフェースが標準です。

特定の実装については、さらに簡単な例、櫛のステップC ++の拡張機能で、この記事を議論していません。

C、C ++のPyTorch、CUDA拡張

PyTorch Cの拡張については、を参照してください公式のチュートリアルや、このブログ、その操作は難しいことではない、オリジナルのトーチが提供する援助以外の何ものでもありません<TH/TH.h>し、<THC/THC.h>他のインタフェースで提供再利用PyTorch torch.util.ffi拡大のためのモジュール。PyTorchのバージョンアップとして、このアプローチは中PyTorchの新バージョンでは失敗する可能性があり、ことに留意すべきです。

本論文では、C ++(プラス将来CUDA)拡張メソッドを記述しています。

C ++の拡張機能

まず、基本的なプロセスについて教えてください。PyTorchでのC ++の拡張は/ CUDAのステップに分かれています。

  1. Pybind11は(等PIPまたはconda、によってインストール)モジュールをインストールし、このモジュールは、PythonとC ++との間の結合を担うであろう。
  2. C ++、伝搬フロント含む、層をカスタマイズする機能に書き込まforward伝播と逆backward;
  3. 書き込まsetup.pyを、とPythonを備えたsetuptoolsC ++コードをコンパイルしてロードします。
  4. コンパイルされ、インストール、PythonでC ++拡張インターフェースを呼び出します。

次に、簡単な例(使用Z = 2X + Yの手順を実証します)。

第一歩

インストールpybind11比較的単純な、それをスキップ。C ++に関連した私たちの最初の書かれた文書:

ヘッダーTEST.H

#include <torch/extension.h>
#include <vector>

// 前向传播
torch::Tensor Test_forward_cpu(const torch::Tensor& inputA,
                            const torch::Tensor& inputB);
// 反向传播
std::vector<torch::Tensor> Test_backward_cpu(const torch::Tensor& gradOutput);

なお、ここで参照<torch/extension.h>ヘッダファイル3つの主要モジュールを含む、必要不可欠です。

  • pybind11、C ++との相互作用のためのpython。
  • ATEN、テンソルその他の重要な機能とクラスを含み;
  • ATENとpybind11の間の相互作用を実装するためのいくつかの補助ヘッダファイル。

ソースファイルTEST.CPP次のように:

#include "test.h"

// 前向传播,两个 Tensor 相加。这里只关注 C++ 扩展的流程,具体实现不深入探讨。
torch::Tensor Test_forward_cpu(const torch::Tensor& x,
                            const torch::Tensor& y) {
    AT_ASSERTM(x.sizes() == y.sizes(), "x must be the same size as y");
    torch::Tensor z = torch::zeros(x.sizes());
    z = 2 * x + y;
    return z;
}

// 反向传播
// 在这个例子中,z对x的导数是2,z对y的导数是1。
// 至于这个backward函数的接口(参数,返回值)为何要这样设计,后面会讲。
std::vector<torch::Tensor> Test_backward_cpu(const torch::Tensor& gradOutput) {
    torch::Tensor gradOutputX = 2 * gradOutput * torch::ones(gradOutput.sizes());
    torch::Tensor gradOutputY = gradOutput * torch::ones(gradOutput.sizes());
    return {gradOutputX, gradOutputY};
}

// pybind11 绑定
PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) {
  m.def("forward", &Test_forward_cpu, "TEST forward");
  m.def("backward", &Test_backward_cpu, "TEST backward");
}

第二のステップ

新しいコンフィギュレーション・ファイルをコンパイルしてインストールするsetup.pyを次のようにファイルのディレクトリ:

└── csrc
    ├── cpu
    │   ├── test.cpp
    │   └── test.h
    └── setup.py

以下は、setup.pyコンテンツ:

from setuptools import setup
import os
import glob
from torch.utils.cpp_extension import BuildExtension, CppExtension

# 头文件目录
include_dirs = os.path.dirname(os.path.abspath(__file__))
# 源代码目录
source_cpu = glob.glob(os.path.join(include_dirs, 'cpu', '*.cpp'))

setup(
    name='test_cpp',  # 模块名称,需要在python中调用
    version="0.1",
    ext_modules=[
        CppExtension('test_cpp', sources=source_cpu, include_dirs=[include_dirs]),
    ],
    cmdclass={
        'build_ext': BuildExtension
    }
)

これはC ++の拡張機能ということに注意してくださいtest_cpppythonでできる、ということを意味し、test_cppC ++の機能モジュールと呼ばれます。

第三段階

では、CPUは、このディレクトリ、C ++コードをコンパイルしてインストールするには、次のコマンドを実行します。

python setup.py install

その後、杭はC ++モジュールの出力は、Pythonのサイトのパッケージに搭載されていることが分かります。

上記の手順が完了した後、あなたはPythonでC ++コードを呼び出すことができます。C ++での最初のフロント伝播の慣行に従ってPyTorchで、逆拡散関数にカプセル化op(のコード下記test.pyファイル)。

from torch.autograd import Function

import test_cpp

class TestFunction(Function):

    @staticmethod
    def forward(ctx, x, y):
        return test_cpp.forward(x, y)

    @staticmethod
    def backward(ctx, gradOutput):
        gradX, gradY = test_cpp.backward(gradOutput)
        return gradX, gradY

その結果、我々はPyTorch自身の枠組みの中で機能するように埋め込まれたC ++の拡張機能と同等のものを持っています。

私が見てFunction、クラスのコード非常に興味深いものを見つけました:

class Function(with_metaclass(FunctionMeta, _C._FunctionBase, _ContextMethodMixin, _HookMixin)):
  
    ...

    @staticmethod
    def forward(ctx, *args, **kwargs):
        r"""Performs the operation.

        This function is to be overridden by all subclasses.

        It must accept a context ctx as the first argument, followed by any
        number of arguments (tensors or other types).

        The context can be used to store tensors that can be then retrieved
        during the backward pass.
        """
        raise NotImplementedError

    @staticmethod
    def backward(ctx, *grad_outputs):
        r"""Defines a formula for differentiating the operation.

        This function is to be overridden by all subclasses.

        It must accept a context :attr:`ctx` as the first argument, followed by
        as many outputs did :func:`forward` return, and it should return as many
        tensors, as there were inputs to :func:`forward`. Each argument is the
        gradient w.r.t the given output, and each returned value should be the
        gradient w.r.t. the corresponding input.

        The context can be used to retrieve tensors saved during the forward
        pass. It also has an attribute :attr:`ctx.needs_input_grad` as a tuple
        of booleans representing whether each input needs gradient. E.g.,
        :func:`backward` will have ``ctx.needs_input_grad[0] = True`` if the
        first input to :func:`forward` needs gradient computated w.r.t. the
        output.
        """
        raise NotImplementedError

これは、約注意しなければならないbackwardルールの実現。インターフェースは、2つのパラメータを含んでいる:ctx二次環境変数を、grad_outputs勾配は、ネットワーク層の前のリストからであり、勾配のこの番号リストforward同じ機能は、チェーンので、また、チェーンルールの原理と一致するパラメータの数を返します式法は乗算または現在階層の勾配に添加する前の層に関連するすべての必要とします。一方、backward必要て戻るforward場合、各入力パラメータの勾配forwardnパラメータに含まれ、nは11勾配を返す必要があります。したがって、上記の例では、我々はbackward機能入力としてパラメータ(受信forwardのみ出力変数)、および(2つの勾配返すforward二つの入力変数の1つで受信します)。

定義した後Functionの後に、次のことが可能Moduleで、このカスタムを使用しますop

import torch

class Test(torch.nn.Module):

    def __init__(self):
        super(Test, self).__init__()

    def forward(self, inputA, inputB):
        return TestFunction.apply(inputA, inputB)

今、私たちのディレクトリは次のようになります。

├── csrc
│   ├── cpu
│   │   ├── test.cpp
│   │   └── test.h
│   └── setup.py
└── test.py

その後、私たちはすることができtest.pyと呼ばれる一般的なPyTorchモジュールとして。

テスト

ここでは、フロントをテストし、伝播の広がりを逆:

import torch
from torch.autograd import Variable

from test import Test

x = Variable(torch.Tensor([1,2,3]), requires_grad=True)
y = Variable(torch.Tensor([4,5,6]), requires_grad=True)
test = Test()
z = test(x, y)
z.sum().backward()
print('x: ', x)
print('y: ', y)
print('z: ', z)
print('x.grad: ', x.grad)
print('y.grad: ', y.grad)

次のように出力されます。

x:  tensor([1., 2., 3.], requires_grad=True)
y:  tensor([4., 5., 6.], requires_grad=True)
z:  tensor([ 6.,  9., 12.], grad_fn=<TestFunctionBackward>)
x.grad:  tensor([2., 2., 2.])
y.grad:  tensor([1., 1., 1.])

図から分かるように、前方に伝搬満たすZ = 2X + Y、及び結果も逆伝播を期待されています。

CUDAエクステンション

C ++コードで記述されたが、GPU上で直接実行されますが、すべてのATENはアルゴリズムの性能を最適化する方法を知らないの後にその性能は、CUDAで書かれたコードを直接使用するようではありませんすることができます。しかし、私はまだCUDAについて何も知らないので、のみ、このステップをスキップすることができ、その後、~~恥ずかしい補充するために残しました。

参照

おすすめ

転載: www.cnblogs.com/jermmyhsu/p/10962987.html