Nvidia Jetson Xavier TensorRT嘗試

在最近的項目中需要對Xavier上的tensorflow代碼進行加速,然后xavier中又自帶了TensorRT爱榕,所以就直接使用TensorRT進行加速瓣喊。本文針對tensorflow,如果使用其他的框架需要進行相應(yīng)的修改黔酥。

TensorRT簡介

TensorRT是NVIDIA 推出的一款基于CUDA和cudnn的神經(jīng)網(wǎng)絡(luò)推斷加速引擎藻三,相比于一般的深度學(xué)習(xí)框架洪橘,在CPU或者GPU模式下其可提供10X乃至100X的加速,極大提高了深度學(xué)習(xí)模型在邊緣設(shè)備上的推斷速度棵帽。

TensorRT主要是通過兩種方式對inference進行加速熄求,一種是將模型進行融合聚合,另外一種就是調(diào)整精度逗概,使用FP16,INT8等精度弟晚。

在Xavier平臺上,預(yù)先安裝了c++版本的TensorRT逾苫,所以本文不涉及TensorRT的安裝

使用TensorRT的整體流程如下:

  1. 在pc端首先訓(xùn)練好模型指巡,得到pb模型文件
  2. 在pc端將模型文件轉(zhuǎn)化為TensorRT能夠使用的模型格式,tensorflow的話將pb轉(zhuǎn)化為uff格式
  3. 在xavier端使用模型文件構(gòu)建出engine
  4. 在xavier端使用engine進行推理

導(dǎo)出pb模型文件

pb文件轉(zhuǎn)化為uff文件

參考我另外一篇文章ubuntu16.04安裝TensorRT5.1隶垮,安裝完TensorRT后,可以使用convert-to-uff命令進行模型的轉(zhuǎn)換秘噪,-o表示轉(zhuǎn)化完的模型的名稱

convert-to-uff model.pb -o model.uff

xavier端構(gòu)建engine

這一步我覺得最苦難的是CMakeLists.txt文件的編寫狸吞,需要加入cuda的一些動態(tài)庫,以及TensorRT的頭文件和動態(tài)庫指煎,我的CMakeLists.txt文件如下所示蹋偏。這一步和下一步我都使用這份CMakeLists.txt。

cmake_minimum_required(VERSION 2.8)
project(tensorrt)
find_package(OpenCV  REQUIRED )

# 添加cuda頭文件
include_directories(/usr/local/cuda/include)
# 添加tensorrt的動態(tài)庫
link_libraries("/usr/lib/aarch64-linux-gnu/libnvparsers.so")
link_libraries("/usr/lib/aarch64-linux-gnu/libnvinfer.so")
# 添加cuda動態(tài)庫
link_libraries("/usr/local/cuda/lib64/libcudart.so")
# 確定可執(zhí)行文件名稱
add_executable(tensorrt tensorrt.cpp) 
add_executable(uff_to_plan uff_to_plan.cpp)
# 添加opencv動態(tài)庫
target_link_libraries(tensorrt ${OpenCV_LIBS})

然后下面是構(gòu)建engine并保存的代碼
在這一步有一個問題至壤,tensorflow中輸入的格式為(height,width,channel) 即HWC威始,但是在TensorRT中是CHW格式,這一步在轉(zhuǎn)換模型的時候就自動完成了像街,不需要自己轉(zhuǎn)換黎棠,但是最后使用tensorrt推理的時候要注意把圖像的格式轉(zhuǎn)換為CHW。

class Logger : public ILogger
{
    void log(Severity severity, const char *msg) override
    {
        cout << msg << endl;
    }
} gLogger;

int main(int argc, char *argv[])
{
    /* parse uff */
    IBuilder *builder = createInferBuilder(gLogger);
    INetworkDefinition *network = builder->createNetwork();
    IUffParser *parser = createUffParser();
    /* register input and output */
    parser->registerInput(inputName.c_str(), DimsCHW(3, inputHeight, inputWidth), UffInputOrder::kNCHW);
    parser->registerOutput(outputName.c_str());
    if (!parser->parse(modelName.c_str(), *network, DataType::kFLOAT))
    {
        cout << "Failed to parse UFF\n";
        builder->destroy();
        parser->destroy();
        network->destroy();
        return 1;
    }

    /* build engine */
    builder->setMaxBatchSize(maxBatchSize);
    builder->setMaxWorkspaceSize(maxWorkspaceSize);
    /* use FP16 */
    builder->setFp16Mode(true);
    // builder->setInt8Mode(true);

    ICudaEngine *engine = builder->buildCudaEngine(*network);
    /* serialize engine and write to file */
    ofstream planFile;
    planFile.open(planFilename);
    IHostMemory *serializedEngine = engine->serialize();
    planFile.write((char *)serializedEngine->data(), serializedEngine->size());
    planFile.close();

    /* break down */
    builder->destroy();
    parser->destroy();
    network->destroy();
    engine->destroy();
    serializedEngine->destroy();

    return 0;
}

使用TensorRT進行推理

在上一步中構(gòu)建出了engine并進行序列化保存成了plan文件镰绎,這一步就是讀取plan文件并且反序列化構(gòu)建出engine脓斩,使用engine進行推理

void cvImageToTensor(const cv::Mat &image, float *tensor, nvinfer1::Dims dimensions)
{
    const size_t channels = dimensions.d[0];
    const size_t height = dimensions.d[1];
    const size_t width = dimensions.d[2];
    // TODO: validate dimensions match
    const size_t stridesCv[3] = {width * channels, channels, 1};
    const size_t strides[3] = {height * width, width, 1};

    for (int i = 0; i < height; i++)
    {
        for (int j = 0; j < width; j++)
        {
            for (int k = 0; k < channels; k++)
            {
                const size_t offsetCv = i * stridesCv[0] + j * stridesCv[1] + k * stridesCv[2];
                const size_t offset = k * strides[0] + i * strides[1] + j * strides[2];
                tensor[offset] = (float)image.data[offsetCv];
            }
        }
    }
}
void solve()
{
    /* build the engine */
    ifstream planFile(planFileName);
    stringstream planBuffer;
    planBuffer << planFile.rdbuf();
    string plan = planBuffer.str();
    IRuntime *runtime = createInferRuntime(gLogger);
    ICudaEngine *engine = runtime->deserializeCudaEngine((void*)plan.data(),
        plan.size(), nullptr);
    IExecutionContext *context = engine->createExecutionContext();

    // get the input and output dimensions
    int inputBindingIndex, outputBindingIndex;
    inputBindingIndex = engine->getBindingIndex(inputName.c_str());
    outputBindingIndex = engine->getBindingIndex(outputName.c_str());
    Dims inputDims, outputDims;
    inputDims = engine->getBindingDimensions(inputBindingIndex);
    outputDims = engine->getBindingDimensions(outputBindingIndex);
    // get the input and output size
    int inputWidth, inputHeight, outputHeight, outputWidth;
    inputHeight = inputDims.d[1];
    inputWidth = inputDims.d[2];
    outputHeight = outputDims.d[1];
    outputWidth = outputDims.d[2];

    /* get the number of input and output */
    float *inputDataHost, *outputDataHost;
    size_t numInput, numOutput;
    numInput = numTensorElements(inputDims);
    numOutput = numTensorElements(outputDims);
    
    inputDataHost = (float *)malloc(numInput * sizeof(float));
    outputDataHost = (float *)malloc(numOutput * sizeof(float));
    
    /* transfer to device */
    void *inputDataDevice, *outputDataDevice;
    cudaMalloc(&inputDataDevice, numInput * sizeof(float));
    cudaMalloc(&outputDataDevice, numOutput * sizeof(float));
    if (inputDataDevice == nullptr || outputDataDevice == nullptr)
    {
        std::cerr << "Out of memory" << std::endl;
        exit(1);
    }

    void *bindings[2];
    bindings[inputBindingIndex] = inputDataDevice;
    bindings[outputBindingIndex] = outputDataDevice;

    // get the image name
    vector<string> images;
    getImages(imageFolderName, images);

    cout << "Executing inference engine..." << endl;
    for(int i = 0; i < images.size(); ++i) {
        string imageFileName = images[i];
        cv::Mat image = cv::imread(imageFolderName + imageFileName);
        /* BGR to RGB */
        cv::cvtColor(image, image, cv::COLOR_BGR2RGB);
        /* resize */
        cv::resize(image, image, cv::Size(inputWidth, inputHeight));
        /* convert HWC to float CHW */
        cvImageToTensor(image, inputDataHost, inputDims);
        cudaMemcpy(inputDataDevice, inputDataHost, numInput * sizeof(float), cudaMemcpyHostToDevice);
        /* execute engine */
        context->execute(kBatchSize, bindings);
        /* transfer output back to host */
        cudaMemcpy(outputDataHost, outputDataDevice, numOutput * sizeof(float), cudaMemcpyDeviceToHost);
        
        cv::Mat preds(outputHeight, outputWidth, CV_8UC1);
        TensorToImage(outputDataHost, preds, outputDims);
        // 后續(xù)處理省略
        cout << i << "/" << images.size() << " is over" << endl;
    }
    
    engine->destroy();
    context->destroy();
    free(inputDataHost);
    free(outputDataHost);
    cudaFree(inputDataDevice);
    cudaFree(outputDataDevice);
}

參考代碼

  1. https://github.com/NVIDIA-AI-IOT/tf_to_trt_image_classification
  2. xavier中自帶的tensorrt實例,在/usr/src/tensorrt/examples中
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末畴栖,一起剝皮案震驚了整個濱河市随静,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌吗讶,老刑警劉巖燎猛,帶你破解...
    沈念sama閱讀 218,204評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異照皆,居然都是意外死亡重绷,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,091評論 3 395
  • 文/潘曉璐 我一進店門膜毁,熙熙樓的掌柜王于貴愁眉苦臉地迎上來论寨,“玉大人星立,你說我怎么就攤上這事≡岬剩” “怎么了绰垂?”我有些...
    開封第一講書人閱讀 164,548評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長火焰。 經(jīng)常有香客問我劲装,道長,這世上最難降的妖魔是什么昌简? 我笑而不...
    開封第一講書人閱讀 58,657評論 1 293
  • 正文 為了忘掉前任占业,我火速辦了婚禮,結(jié)果婚禮上纯赎,老公的妹妹穿的比我還像新娘谦疾。我一直安慰自己,他們只是感情好犬金,可當我...
    茶點故事閱讀 67,689評論 6 392
  • 文/花漫 我一把揭開白布念恍。 她就那樣靜靜地躺著,像睡著了一般晚顷。 火紅的嫁衣襯著肌膚如雪峰伙。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,554評論 1 305
  • 那天该默,我揣著相機與錄音瞳氓,去河邊找鬼。 笑死栓袖,一個胖子當著我的面吹牛匣摘,可吹牛的內(nèi)容都是我干的裹刮。 我是一名探鬼主播恋沃,決...
    沈念sama閱讀 40,302評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼囊咏,長吁一口氣:“原來是場噩夢啊……” “哼塔橡!你這毒婦竟也來了梅割?” 一聲冷哼從身側(cè)響起葛家,我...
    開封第一講書人閱讀 39,216評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎底燎,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體双仍,經(jīng)...
    沈念sama閱讀 45,661評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡朱沃,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,851評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了逗物。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,977評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡契邀,死狀恐怖失暴,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情锐帜,我是刑警寧澤畜号,帶...
    沈念sama閱讀 35,697評論 5 347
  • 正文 年R本政府宣布,位于F島的核電站简软,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏建炫。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,306評論 3 330
  • 文/蒙蒙 一肛跌、第九天 我趴在偏房一處隱蔽的房頂上張望察郁。 院中可真熱鬧,春花似錦皮钠、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,898評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽末荐。三九已至,卻和暖如春鞠评,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背聋涨。 一陣腳步聲響...
    開封第一講書人閱讀 33,019評論 1 270
  • 我被黑心中介騙來泰國打工负乡, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人抖棘。 一個月前我還...
    沈念sama閱讀 48,138評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像最岗,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子般渡,可洞房花燭夜當晚...
    茶點故事閱讀 44,927評論 2 355

推薦閱讀更多精彩內(nèi)容