TensorRT展開深学習モデル

1.背景

現在主流の深い学習フレームワーク(カフェ、mxnet、tensorflow、pytorchなど)の速度は、比較的非効率的になる傾向があり、実際のプロジェクトでモデルを展開するためのフレームワークを推測するための良いモデルではありません。主流のフレームワークを展開するための訓練を受けたモデルが大幅にNvidiaので推論モデルの速度は、少なくとも1回の速度の増加を持っているために、元のフレームに比べて多くの場合、tensorRTツールを発売向上させることができますが、より多くのメモリデバイスを取り上げます少ないです。すべてのゲイモデルを展開する必要がそう、tensorRTとの深い学習モデルを展開する方法の習得は非常に便利です。

【0002】【従来の技術

上記TensorRT公式サイトから取得された画像は、いくつかのテクニックtensorRTの使用があります。私たちは、より深い学習着陸技術を成熟見ることができます:モデルは、動的メモリの最適化を定量化するために、統合レイヤ技術は、それが大幅に推論モデルの速度を上げることができた理由であるtensorRT、に統合されています。最適化手法の一連の訓練されたモデルは、高性能の実行コードに変換することができる全体的なtensorRTは、特定のプラットフォーム(GPU)の最後図推論エンジンで発生されます。以下のような同様のtensorRT機能を達成することができますいくつかの他のツールもありますTVMはTensorComprehensionsは、効果的に、特定のプラットフォーム上での速度を推定するモデルを向上させることができますが、現在のコンピューティングデバイスのためには、これらのデバイス上のNvidiaの生産企業の主流使用されていますNVIDIAは、いくつかの利点があります他のツールに比べてtensorRT性能を立ち上げました。より簡素化される他のツールの数とは対照的とtensorRT依存コードベースは、唯一のC ++とCUDAを含みます。

3. tensorflowモデルtensorRT展開のチュートリアル

展開のため、実際のプロジェクトのC ++の使用で展開するので、このチュートリアルでは、tensorRT C ++ APIを使用して、tensorRTバージョン5.1.5。具体的な言及はtensorRTガイドを装着することができるTensorRTがインストール[深い学習]、およびインストール手順の公式サイト。

永続モデル

tensorflowモデルを展開する最初のステップは、それら.pbファイルへのモデルの永続性、モデル構造と軽量化です。

pb_graph = tf.graph_util.convert_variables_to_constants(sess, sess.graph.as_graph_def(), [v.op.name for v in outputs])
with tf.gfile.FastGFile('./pbmodel_name.pb', mode='wb') as f:
    f.write(pb_graph.SerializeToString())

唯一の重み関数呼び出しのtf.graph_util.convert_variables_to_constantsを一定に、上記のモデルのコードの定義と重量読取の後に行われる、前記出力されたテンソル必要な出力のリスト詳細な、そして最終的にpb_graph.SerializeToString()グラフシリアライゼーションそしてそれらPBファイルへの書き込み、これPBモデルを生成します。

UFF生成モデル

PBモデルでは、あなたが利用できるUFFモデルtensorRTに変換する必要があり、単純に変換するために、スクリプトが付属していますパッケージUFF呼び出します

python /usr/lib/python2.7/site-packages/uff/bin/convert_to_uff.py   pbmodel_name.pb

そのような成功した変換の数などの情報が入力点と出力ノードとを含む図1に要約以下の情報を出力する推論

tensorRT C ++ APIの展開モデル

展開は、ネットワークの重みにモデル構造に格納されたUFFモデルUFF tensorRT良いを使用して生成され、中、及びその後の話に対応する推論エンジンニーズを生成する最適化アルゴリズムを実行します。次のように特定のコード、あなたIBuilder *ビルダーを定義するための最初の必要性、UFF解決ネットワークパーサーに使用されるファイルやビルダーは、モデルパラメータとネットワーク構造のパーサは予告パーサ前に解決するためのネットワークに保存されたUFFファイルを、アウト解析します作成入力ネットワーク出力ノード。エンジンビルダーを解析した後定義されたネットワーク構造に基づいてネットワークを作成することができます。エンジンを使用した後、それ以外の場合はエラーが発生し、この値を超えてはならないときバッチサイズは、バッチサイズが入力されたエンジンを作成する前に、最大サイズを指定する必要があります。最高のバッチサイズを推定する場合と同じ最大効率を設定します。平均時間が推測図よりも高いときに最大値が10バッチサイズに設定されている場合の平均時間が推定当たり4msの場合に、例えば、バッチ実際の理由図10を入力し、その後、図当たり10未満のバッチを入力します。 4MS。

IBuilder* builder = createInferBuilder(gLogger.getTRTLogger());
auto parser = createUffParser();
parser->registerInput(inputtensor_name, Dims3(INPUT_C, INPUT_H, INPUT_W), UffInputOrder::kNCHW);
parser->registerOutput(outputtensor_name);
    INetworkDefinition* network = builder->createNetwork();
    if (!parser->parse(uffFile, *network, nvinfer1::DataType::kFLOAT))
    {
        gLogError << "Failure while parsing UFF file" << std::endl;
        return nullptr;
    }  
    builder->setMaxBatchSize(maxBatchSize);
    builder->setMaxWorkspaceSize(MAX_WORKSPACE);
    ICudaEngine* engine = builder->buildCudaEngine(*network);
    if (!engine)
    {
        gLogError << "Unable to create engine" << std::endl;
        return nullptr;
    }

実行されるコンテキスト実行コンテキストIExecutionContext *コンテキスト推論の必要性を推測することができるエンジンを生成した後、エンジン - > createExecutionContextことによって得ることができます()。コアコードは、推論を実行しています

 context->execute(batchSize, &buffers[0]);  

バッファは、モデル入力及びテンソルデバイスアドレス出力に対応するボイド*配列であり、対応するポインタは、入力データ実行動作の前にcudaMemcpyによって必要とされる機器スペース(メモリ)、入力と出力を開きcudaMallocによってバッファ配列に格納されていますコピーの入力に対応するデバイス空間への、または実行後cudaMemcpyによって(入力画像)がコピー装置からの結果の出力を実行します。

より詳細なルーチンがでTensorRT公式のサンプルを参照することができsampleUffMNISTコード

スピードアップケース

私はインセプション-resnet-V2、Googleの画像検索モデルDELF(DEEPローカルな特長)、テスラM40にResnet-50上で加速するtensorRTを使用し、実際のプロジェクトでは、使用している場合(ミリ秒)以下の図を比較推測さの単一のビューを周りに加速しました

4.カフェモデルtensorRT展開のチュートリアル

モデル変換と比較しtensorflowカフェUFFを解析し、直接prototxtのcaffemodelファイルネットワーク構造と重いモデルを取得するtensorRTができるモデルなどの操作を、ターンtensorflow、モデルは単純なモデルを必要としません。一貫して、特定の分析手順は既にprototxt入力層として定義される予め指定された入力層を必要としないパーサーカフェモデルは、パーサは自動的に入力を解析することができることを除いて、上述した、追加のネットワークが解析されcaffeparserを返しますIBlobNameToTensor * blobNameToTensorは、ネットワークとテンソルpototxtの名前との対応を記録し、出力としてそれをマークする[ネットワーク] - > [markOutput関数を使用して解析した後、対応するテンソルを見つけるために、出力出力テンソル順の名前リストによると、この対応を渡す必要があります、あなたはエンジンを生成することができます。

IBuilder* builder = createInferBuilder(gLogger);
    INetworkDefinition* network = builder->createNetwork();
    ICaffeParser* parser = createCaffeParser();
    DataType modelDataType = DataType::kFLOAT;
    const IBlobNameToTensor *blobNameToTensor =	parser->parse(deployFile.c_str(),
                                                              modelFile.c_str(),
                                                              *network,
                                                              modelDataType);
    assert(blobNameToTensor != nullptr);
    for (auto& s : outputs) network->markOutput(*blobNameToTensor->find(s.c_str()));

    builder->setMaxBatchSize(maxBatchSize);
    builder->setMaxWorkspaceSize(1 << 30);
    engine = builder->buildCudaEngine(*network);

生成モードは、エンジンの後に実行され、一貫性ルーチンを参照して詳細に説明してもよいSampleMNIST

スピードアップケース

私はVGG19介しtensorRTのカフェでテスラM40加速実際のプロジェクトでは、SSDの速度は1.6倍、ResNet50、(ミリ秒)以下、図を比較MobileNetV2単一前後加速度推定値となったとき

カスタム層tensorRTを追加5.

tensorRTは現在、これらの操作はtensorRTでの使用にサポートすることができないのでこと、それは、このようなサンプリングアップサンプルオペレーションをサポートしていない多くの操作がある、あなたはtensorRT層のための当社独自のプラグインを作成する必要があり、この時間は、いくつかの非常に一般的な操作をサポートしています。アップサンプル層を定義するには、例えば、まず、ベースクラスプラグインtensorRTアップサンプルからクラス継承を定義します

class Upsample: public IPluginExt

その後、クラスのいくつかのメソッドを実装するために必要な、2つの最初のコンストラクタは、パラメータは、ビルドに渡され、他方はシリアル化されたビットストリームから構築されています。

 Upsample(int scale = 2) : mScale(scale) {
        assert(mScale > 0);
    }
//定义上采样倍数
 Upsmaple(const void *data, size_t length) {
        const char *d = reinterpret_cast<const char *>(data), *a = d;
        mScale = read<int>(d);
        mDtype = read<DataType>(d);
        mCHW = read<DimsCHW>(d);
        assert(mScale > 0);
        assert(d == a + length);
    }
~Upsample()
    {

    }

情報出力方法のいくつかの層は、以下のように定義します

   int getNbOutputs() const override {
        return 1;
    }
//模型的输出个数

    Dims getOutputDimensions(int index, const Dims *inputs, int nbInputDims) override {
       // std::cout << "Get ouputdims!!!" << std::endl;
        assert(nbInputDims == 1);
        assert(inputs[0].nbDims == 3);
        return DimsCHW(inputs[0].d[0], inputs[0].d[1] * mScale, inputs[0].d[2] * mScale);
    }
//获取模型输出的形状

方法及び入力データの数の形状および使用の層構成パラメータの種類の妥当性を確認

    bool supportsFormat(DataType type, PluginFormat format) const override {
        return (type == DataType::kFLOAT || type == DataType::kHALF || type == DataType::kINT8)
               && format == PluginFormat::kNCHW;
    }
//检查层是否支持当前的数据类型和格式
    void configureWithFormat(const Dims *inputDims, int nbInputs, const Dims *outputDims, int nbOutputs,
                             DataType type, PluginFormat format, int maxBatchSize) override
       {
         mDtype = type;
         mCHW.c() = inputDims[0].d[0];
         mCHW.h() = inputDims[0].d[1];
         mCHW.w() = inputDims[0].d[2];
        }
//配置层的参数

方法層列

 size_t getSerializationSize() override {
        return sizeof(mScale) + sizeof(mDtype) + sizeof(mCHW);
    }
//输出序列化层所需的长度
    void serialize(void *buffer) override {
        char *d = reinterpret_cast<char *>(buffer), *a = d;
        write(d, mScale);
        write(d, mDtype);
        write(d, mCHW);
        assert(d == a + getSerializationSize());
    }
//将层参数序列化为比特流

層を算出する方法

 size_t getWorkspaceSize(int maxBatchSize) const override {
        return 0;
    }
//层运算需要的临时工作空间大小
 int enqueue(int batchSize, const void *const *inputs, void **outputs, void *workspace,
                cudaStream_t stream) override;
//层执行计算的具体操作

エンキューでは、アップサンプリングの計算に書き込み良いのCUDAのkenerlを呼び出します

クラスの定義を完了するアップサンプリング、我々はサンプル層の上に2回のサンプルを定義し、次のステートメントによって、我々は準備プラグのネットワークに直接追加することができます。マルチ出力をサポートする最初の入力addPluginExt ** ITensorあるカテゴリと、2番目のパラメータは入力の数であり、プラグインクラスオブジェクト第三のパラメータは、作成する必要があることです。

Upsample up(2);
auto upsamplelayer=network->addPluginExt(inputtensot,1,up)

カスタム層のサポートCaffeParserを追加6

分析モデルを展開する際に、当社独自の層はカフェprototxt、コールcaffeparserを書いた場合については、エラーが発生します。

またはアップサンプルの例では、次の作品を持っている場合prototxtにアップサンプリングして層を追加します

layer {
  name: "upsample0"
  type: "Upsample"
  bottom: "ReLU11"
  top: "Upsample1"
}

お電話次回

const IBlobNameToTensor *blobNameToTensor =	parser->parse(deployFile.c_str(),
                                                              modelFile.c_str(),
                                                              *network,
                                                              modelDataType);

ミスがあるでしょう

我々は以前tensorRTがアップサンプル層は、その中に書かれた自動ビルド当社独自のプラグインをprototxt識別カフェパーサーを作る方法を、アップサンプルプラグインを書かれていますか?その後、我々はプラグインエンジニアリングクラスはnvinfer1 :: IPluginFactory、nvcaffeparser1 :: IPluginFactoryExt基本クラスを継承して定義する必要があります。

class PluginFactory : public nvinfer1::IPluginFactory, public nvcaffeparser1::IPluginFactoryExt

層は、レイヤ名によって判断されるように、入力パラメータは、層内の名前prototxtウィジェットとして登録されているプラ​​グインのアプローチか否かが決定される方法が実施されなければなりません

bool isPlugin(const char *name) override {
        return isPluginExt(name);
    }

bool isPluginExt(const char *name) override {

        char *aa = new char[6];
        memcpy(aa, name, 5);
        aa[5] = 0;
        int res = !strcmp(aa, "upsam");
        return res;
}
//判断层名字是否是upsample层的名字

ウィジェット作成名前に係る方法は、一つは重量によって再構築2つの方法があり、ビットストリームによって他のシリアライズ重量プラグを有する他の人がすることができるため、2つのコンストラクタに対応する、全く重みをアップサンプリングしないウィジェット作成しました受信重みは層を初期化。ベクターmpluginすると、すべてのプラグイン作成層を格納するのに使用されます。

IPlugin *createPlugin(const char *layerName, const nvinfer1::Weights *weights, int nbWeights) override {
        assert(isPlugin(layerName));
        mPlugin.push_back(std::unique_ptr<Upsample>(new Upsample(2)));
        return mPlugin[mPlugin.size() - 1].get();
    }
IPlugin *createPlugin(const char *layerName, const void *serialData, size_t serialLength) override {
        assert(isPlugin(layerName));

        return new Upsample(serialData, serialLength);
    }
 std::vector <std::unique_ptr<Upsample>> mPlugin;

最後に、我々はすべてのプラグイン作成された層を解放するために破壊する方法を定義する必要があります。

 void destroyPlugin() {
        for (unsigned int i = 0; i < mPlugin.size(); i++) {
            mPlugin[i].reset();
        }
}

様々なプラグprototxtが複数ある場合については、レイヤ名の層に対応するプラグインを作成するために応じて、新たな条件分岐isPlugin、createPluginメソッドを追加することができます。

parser-を呼び出し、それを使用するように設定する必要caffeparserを呼び出しPluginFactoryの実現は、>パーサの前に次のコードを追加します

PluginFactory pluginFactory;
parser->setPluginFactoryExt(&pluginFactory);

あなたがプラグインの層を作成するためにpluginFactory内側に従って定義されたルールパーサーを設定することができ、このようなアップサンプリング層の出現が再び発生しない前に、エラーが解決することはできません。

公式の追加プラグ層サンプルsamplePluginを基準として使用することができます

7.経験(ステップピットレコード)

1.ターンtensorflowモデル、PB世代モデル、およびパーサを解析するときに、これらの3つの名前のuffparserレジスタの入力、出力、入力と出力のノードを呼び出すと、そうでない場合、最終的なエラー、一貫性のプロセスに注意を払う必要がある場合にUFFモデル変換、入力と出力のノードを見つけることができません。

またIPlugin、IPluginV2、クラスのメソッドを継承ベースクラスは微妙な違いを達成するために必要とされるベースクラスプラグインが本明細書に例示pluginExt、tensorRTに加えて2、特に自己インストールビューtensorRTフォルダ含む/ NvInfer.hファイル。ネットワーク機能への書き込みに独自の層を添加しながらaddPlugin、addPluginExt、addPluginV2これらのタイプとIPlugin、IPluginExtを有する、IPluginV2対応は、そうでなければ、いくつかのデフォルトクラスのメソッド呼び出しは、addPluginを追加するなど、呼び出さない、混在させることはできませんメソッドは、クラスをIPluginていないため、PluginExt configureWithFormat層は、メソッドを呼び出すことはありません。またsetPluginFactoryとsetPluginFactoryExtのそこcaffeparserでも混在させることはできません。

3.ファイル名を指定して実行プログラムCUDA障害がコピーバッファ・スペースの大きさかどうかをチェックし、過去のデータの一貫性を開いたために、不正なメモリアクセスがあったときにディスクにメモリデータをコピーすることにより、通常の状況下で注意を払うが発生します。

4.いくつかの操作を組み合わせた、しかし、いくつかののような、tensorRTに交互に操作するサポートによってサポートされていないこともあります  [公式] ので、カスタム層を書くためにいくつかの時間を節約、。

5. tensorflow平坦化で動作しているとき、デフォルトのkeepdims = Falseのが、変換UFFデフォルトテキスト変換keepdims =真によれば、従ってUFFに変換した後、ベクトル転置をtensorflowにおける動作等expanddimsを平坦化エラーを起こしやすいような「オーダーサイズはtensorRTの数の寸法に一致されていない」として、tensorRTで解析します。好ましくは、常に効果的tensorRTに様々な奇妙なエラーを回避することができ、出力層4次元の形状を維持し、keepdims = Trueの平坦化動作、低減tensorflow設けられています。

特定の問題は、スライス層6.tensorRTである後にエラーがnvinfer1 ::ビルダー:: checkSanity(CONST nvinfer1 ::ビルダー::グラフ・)が発生した場合、Iネットワーク - > addSlice、ネットワーク層にbuildengineの実装で、このステップをスライスを追加:アサーション `tensors.size()== g.tensors.sizeは()「スライス層の使用を避けるために、最善を失敗した彼またはカスタム層スライス操作が行われている実現するために、ネットワークを構築しています。

githubのオープンソース7. tensorRTとサンプルコードセクションの富を持って、そして助けに学習の多くはすぐに使用tensorRTを把握します

8.参照文献

NvidiaのTensorRTサンプル

tensorrt-開発者ガイド

TensorRT APIドキュメント

TensorRTのGithub

公開された482元の記事 ウォンの賞賛789 ビュー171万+

おすすめ

転載: blog.csdn.net/weixin_42137700/article/details/105214333