TensorRT 배포 깊이 학습 모델

1. 배경

현재 주류 깊은 학습 프레임 워크의 속도 (등을 CAFFE, mxnet, tensorflow, pytorch는) 상대적으로 비효율적 그들이 경향이 실제 프로젝트와 모델의 구축을위한 프레임 워크를 추론하는 좋은 모델이되지 않습니다. 주류 프레임 워크를 구축 할 수있는 훈련 된 모델은 크게 엔비디아에 의해 추정 모델의 속도가 적어도 1 배 속도 증가를 가지고있는 원래의 프레임에 비해 자주 tensorRT 도구를 시작 향상시킬 수 있지만, 더 많은 메모리 장치를 차지합니다 적은. 필요한 모든 게이 모델을 배포 할 수 있도록 tensorRT 매우 유용와 함께, 방법의 숙달 깊은 학습 모델을 배포합니다.

2. 술

위의 TensorRT 공식 웹 사이트에서 가져온 그림은, 일부 기술 tensorRT 사용이 있습니다. 우리는 더 성숙 깊은 학습 착륙 기술을 볼 수 있습니다 모델은 동적 메모리 최적화를 정량화하기 위해, 통합 계층 기술은이 크게 추론 모델의 속도를 높일 수있는 이유입니다 tensorRT에 통합되었습니다. 최적화 기술의 시리즈를 통해 훈련 된 모델은 고성능 실행 코드로 변환 할 수 있습니다 전체 tensorRT는 특정 플랫폼 (GPU)의 마지막도 추론 엔진에서 생성됩니다. 같은 유사한 tensorRT 기능을 달성 할 수있는 다른 도구도있다 TVM은 , TensorComprehensions은 효과적으로 특정 플랫폼에 속도를 추정 할 수있는 모델을 향상시킬 수 있지만, 현재의 컴퓨팅 장치로 인해 이러한 장치에 엔비디아 생산 기업의 주류 사용됩니다 NVIDIA는 몇 가지의 장점을 갖습니다 다른 도구에 비해 tensorRT 성능을 출시했다. 더 간소화 할 다른 도구의 숫자 반대로 그리고 tensorRT 의존하는 코드베이스 만 C ++ 및 CUDA가 포함되어 있습니다.

3. tensorflow 모델 tensorRT 배포 자습서

배포의 실제 프로젝트 C ++의 사용에 배치,이 튜토리얼도 C를 tensorRT를 사용하므로 ++ 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

이러한 성공적인 전환 횟수 등의 정보가 입력 점과 추정 출력 노드를 포함하는도에 요약 다음 정보를 출력

tensorRT의 C ++ API 배포 모델

배포 네트워크 무게에 모델 구조에 저장 UFF 모델 UFF tensorRT 좋은를 사용하여 생성하고, 그리고 다음에 대한 이야기에 해당하는 추론 엔진의 요구를 생성하기 위해 최적화 알고리즘을 실행합니다. IBuilder * 빌더를 정의하려면 먼저 필요, 해결 네트워크 UFF 파서와 빌더 모델 매개 변수와 네트워크 구조 파서가 네트워크에 저장된 파일 UFF 구문 분석됩니다 만들어 사용하는 파일을 다음과 특정 코드로, 예언 파서 전에 해결해야 입력 네트워크 출력 노드. 엔진 빌더를 분석 한 후 정의 된 네트워크 구조를 기반으로 네트워크를 만들 수 있습니다. 엔진을 사용한 후, 그렇지 않으면 오류가 발생이 값을 초과하지 않아야 할 때 BATCHSIZE는 BATCHSIZE 입력 엔진을 만들기 전에 최대 크기를 지정해야합니다. 가장 높은 BATCHSIZE 및 설정 동일한 최대 효율을 추론합니다. 평균 시간은 추론도 이상이되면, 최대 10 BATCHSIZE로 설정된 경우 평균 시간 추정 당 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 코드

속도 향상 경우

사용할 때 실제 프로젝트 내가 테슬라 M40, 인 셉션 - resnet-V2, 구글 이미지 검색 모델 DELF (DEEP 지역의 특징)에 Resnet-50을 통해 가속화 tensorRT을 사용, (밀리 초) 아래 그림을 비교 추론의 단일 뷰를 주위 가속

4. CAFFE 모델 tensorRT 배포 자습서

모델 변환과 비교 tensorflow CAFFE UFF 구문 분석하고 직접 prototxt의 caffemodel 파일 네트워크 구조와 무거운 무게 모델을 취득 할 tensorRT이 수 모델 같은 동작을 설정 tensorflow 모델은 단순한 모델을 필요로하지 않습니다. 일관 특정 분석 절차는 이미 prototxt 입력 층으로 정의되는 사전 지정된 입력 층을 필요로하지 않는 파서 CAFFE 모델, 파서 자동 입력을 분석 할 수 있다는 것을 제외하고, 상기의 추가적인 네트워크가 파싱 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

속도 향상 경우

I는 VGG19 통해 tensorRT의 CAFFE와 테슬라 M40 가속 실제 프로젝트는 SSD 속도가 1.6 배, ResNet50 (밀리 초) 이하도 비교 MobileNetV2 단일 종 방향 가속도 추정된다 때

사용자 정의 계층 tensorRT 추가 (5)

tensorRT는 현재 이러한 작업이 tensorRT에 사용에서 지원되지 않을 수 그래서, 그것은 이러한 샘플링 업 샘플링 작업을 지원하지 않습니다 많은 연산이있다, 당신은 tensorRT 층에 대한 우리 자신의 플러그인을 작성해야이 시간을 매우 일반적인 작업을 지원합니다 . , 업 샘플링 층을 정의 예를 들어, 우리는 먼저 기본 클래스 플러그인 tensorRT 업 샘플링에서 클래스 상속을 정의하려면

class Upsample: public IPluginExt

클래스의 몇 가지 방법을 구현하는 그런 필요,이 첫 번째 생성자는 매개 변수는 빌드에 전달하고, 다른 하나는 직렬 비트 스트림에서 내장되어 있습니다.

 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) 번에 샘플을 정의합니다. 제 1 입력 addPluginExt는 다중 출력을 지원하는 ITensor ** 카테고리이고, 두 번째 파라미터는 입력 개수, 플러그인 클래스 Object 세번째 파라미터 생성 할 필요가있다.

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

사용자 정의 계층 지원 CaffeParser 추가 (6)

오류를 줄 것이다 분석 모델을 배포 할 때 우리의 사용자 층을 CAFFE 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);

실수있을 것입니다

우리는 이전에 업 샘플링 플러그인은 어떻게 작성 층 prototxt 자동 빌드 우리 자신의 플러그인을 업 샘플링 식별 CAFFE 파서 tensorRT를 만들기 위해 쓴? 그런 다음 우리는 기본 클래스 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层的名字

이름에 따른 방법이 체중에 의해 복원 된 두 가지 방법 중 하나는, 상기 비트 스트림에 의해 다른 두 생성자에 대응 될 수 중량 플러그가 다른 경우, 어떤 가중치를 업 샘플링되지 위젯 직렬화 만든 위젯 생성 수신 가중치는 계층을 초기 환경. 벡터를 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 방법을 추가 할 수있다.

PluginFactory의 실현 호출을 사용하도록 설정할 필요 caffeparser 호출 할 때 parser-> 파서 앞에 다음 코드를 추가

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

이러한 업 샘플링 층의 출현이 다시 발생하지 전에 플러그인 레이어를 만들 수 pluginFactory 내부에 따라 정의 파서 당신은 규칙을 설정할 수 있습니다, 오류가 해결 될 수 없다.

공식 첨가 플러그 층 샘플 samplePlugin는 기준으로서 사용될 수있다

7. 경험 (강화 구덩이 기록)

그렇지 않으면 결국 오류 파서 구문 분석 1. 회전 tensorflow 모델, PB 세대 모델과 일치의 과정에주의를 지불해야합니다 세 가지 이름의 uffparser 레지스터 입력, 출력, 입력 및 출력 노드를 호출 할 때 UFF 모델 변환, 입력 및 출력 노드를 찾을 수 없습니다.

2. 본 명세서에서 예시 이외에 pluginExt 특별히 자기 설치보기 tensorRT tensorRT 플러그인 기본 클래스도 IPlugin, IPluginV2 클래스 방법은 미묘한 차이를 달성하기 위해 필요한을 상속베이스 클래스 / NvInfer.h 포함 폴더 파일. 네트워크 기능으로 쓰기에 자체 레이어를 추가하는 동안 addPlugin, addPluginExt, addPluginV2 이러한 유형 및 IPlugin, IPluginExt을 가지고 IPluginV2 대응 그렇지 않으면 몇 가지 기본 클래스 메서드 호출이 같은 addPlugin를 추가로 호출하지 않습니다, 혼합 할 수 없다 이 방법은 클래스를 IPlugin하지 않기 때문에 PluginExt configureWithFormat 층은 메소드를 호출하지 않습니다. 또한도 setPluginFactory 및 setPluginFactoryExt의이 caffeparser에 혼합 할 수 없습니다.

하여 복사 버퍼 공간의 크기와 과거 데이터 일관성을 열어 여부를 확인하기 위해 불법적 인 메모리 액세스,주의를 기울이있을 때 3. 실행 프로그램 CUDA 고장으로 인해 디스크에 복사 메모리 데이터에 대한 정상적인 상황에서 발생합니다.

4. 일부 작업이 결합하지만, 일부는 같은 tensorRT에서 교대로 운영 지원에 의해 지원되지 않을 수 있습니다  [공식] 때문에 사용자 층을 쓸 시간이 저장.

5. tensorflow 패턴 화에서 동작 할 때 디폴트 keepdims = 거짓, 그러나 변환 UFF 기본 텍스트 변환 keepdims = TRUE를 따른 경우에는, 따라서 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. 참고 문헌

엔비디아 TensorRT 샘플

tensorrt-개발자 가이드

TensorRT API 문서

TensorRT Github에서

게시 된 482 개 원래 기사 · 원 찬양 789 · 조회수 1,710,000 +

추천

출처blog.csdn.net/weixin_42137700/article/details/105214333