日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

TensorRT(2)-基本使用:mnist手写体识别

發布時間:2024/9/27 编程问答 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 TensorRT(2)-基本使用:mnist手写体识别 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

結合 tensorRT官方給出的一個例程,介紹tensorRT的使用。

這個例程是mnist手寫體識別。例程位于目錄:?/usr/src/tensorrt/samples/sampleMNIST

文件結構:

1

2

3

4

5

tensorrt/samples/sampleMNIST

-common.cpp

-common.h

-Makefile

-sampleMNIST.cpp

主要是?sampleMNIST.cpp?文件,?common.cpp?文件主要提供 讀取文件的函數和 Logger對象。

main

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

#include <algorithm>

#include <assert.h>

#include <cmath>

#include <cuda_runtime_api.h>

#include <fstream>

#include <iostream>

#include <sstream>

#include <sys/stat.h>

#include <time.h>

#include "NvCaffeParser.h"

#include "NvInfer.h"

#include "common.h"

using namespace nvinfer1;

using namespace nvcaffeparser1;

//定義輸入輸出大小,創建Logger對象

//Logger是一個日志類,在common.h文件中定義

static Logger gLogger;

// Attributes of MNIST Caffe model

static const int INPUT_H = 28;

static const int INPUT_W = 28;

static const int OUTPUT_SIZE = 10;

//指定輸入輸出blob,和資源文件夾

const char* INPUT_BLOB_NAME = "data";

const char* OUTPUT_BLOB_NAME = "prob";

const std::vector<std::string> directories{"data/samples/mnist/", "data/mnist/"};

//查找文件

std::string locateFile(const std::string& input)

{

return locateFile(input, directories);

}

//讀取圖片

// Simple PGM (portable greyscale map) reader

void readPGMFile(const std::string& fileName, uint8_t buffer[INPUT_H * INPUT_W])

{

readPGMFile(fileName, buffer, INPUT_H, INPUT_W);

}

………………

………………

int main(int argc, char** argv)

{

if (argc > 1)

{

std::cout << "This sample builds a TensorRT engine by importing a trained MNIST Caffe model.\n";

std::cout << "It uses the engine to run inference on an input image of a digit.\n";

return EXIT_SUCCESS;

}

// Create TRT model from caffe model and serialize it to a stream

// 創建tensorRT流對象 trtModelStream,這個就跟文件流中的 ifstream 是類似的。

// trtModelStream是一塊內存區域,用于保存序列化的plan文件。

IHostMemory* trtModelStream{nullptr};

//1. build階段:調用caffeToTRTModel函數,傳入caffe模型文件和權值文件,創建 Ibuilder對象,調用模型解析函數,

//生成的plan文件保存在 gieModelStream 中

caffeToTRTModel("mnist.prototxt", "mnist.caffemodel", std::vector<std::string>{OUTPUT_BLOB_NAME}, 1, trtModelStream);

assert(trtModelStream != nullptr);

// 隨機讀取一張圖片

// Read a random digit file

srand(unsigned(time(nullptr)));

uint8_t fileData[INPUT_H * INPUT_W];

const int num = rand() % 10;

readPGMFile(locateFile(std::to_string(num) + ".pgm", directories), fileData);

//將原始圖片中的像素用二進制文本 “.:-=+*#%@”來輸出

// Print ASCII representation of digit

std::cout << "\nInput:\n" << std::endl;

for (int i = 0; i < INPUT_H * INPUT_W; i++)

std::cout << (" .:-=+*#%@"[fileData[i] / 26]) << (((i + 1) % INPUT_W) ? "" : "\n");

// 加載均值文件,將讀取的圖片統一減去平均值。

// Parse mean file

ICaffeParser* parser = createCaffeParser();

IBinaryProtoBlob* meanBlob = parser->parseBinaryProto(locateFile("mnist_mean.binaryproto", directories).c_str());

parser->destroy();

// Subtract mean from image

const float* meanData = reinterpret_cast<const float*>(meanBlob->getData());

float data[INPUT_H * INPUT_W];

for (int i = 0; i < INPUT_H * INPUT_W; i++)

data[i] = float(fileData[i]) - meanData[i];

meanBlob->destroy();

// Deserialize engine we serialized earlier

// 創建運行時環境 IRuntime對象,傳入 gLogger 用于打印信息

IRuntime* runtime = createInferRuntime(gLogger);

assert(runtime != nullptr);

ICudaEngine* engine = runtime->deserializeCudaEngine(trtModelStream->data(), trtModelStream->size(), nullptr);

assert(engine != nullptr);

trtModelStream->destroy();

//創建上下文環境,主要用于inference 函數中啟動cuda核

IExecutionContext* context = engine->createExecutionContext();

assert(context != nullptr);

//2.deploy 階段:調用 doInference 函數,進行推理過程

// Run inference on input data

float prob[OUTPUT_SIZE];

doInference(*context, data, prob, 1);

//銷毀無用對象

// Destroy the engine

context->destroy();

engine->destroy();

runtime->destroy();

//輸出分類結果

// Print histogram of the output distribution

std::cout << "\nOutput:\n\n";

float val{0.0f};

int idx{0};

for (unsigned int i = 0; i < 10; i++)

{

val = std::max(val, prob[i]);

if (val == prob[i]) idx = i;

std::cout << i << ": " << std::string(int(std::floor(prob[i] * 10 + 0.5f)), '*') << "\n";

}

std::cout << std::endl;

return (idx == num && val > 0.9f) ? EXIT_SUCCESS : EXIT_FAILURE;

}

實際上從第93行創建 IRuntime對象時,就可以認為是屬于deploy了。

最后輸出是這樣的:讀進一張9,輸出一個結果:

正在上傳…重新上傳取消

其中最重要的兩個函數 caffeToTRTModel( ) 和 doInference( )分別完成的是build和deploy的功能。

Build Phase

正在上傳…重新上傳取消

將Caffe model 轉換為 TensorRT object,首先使用其他深度學習框架訓練好模型,然后丟進tensorRT優化器中進行優化,優化后會產生一個文件,這個文件可以認為是優化后的模型,接著使用序列化方法將這個優化好后的模型存儲在磁盤上,存儲到磁盤上的文件稱為?plan file

這個階段需要給tensorRT提供兩個文件,分別是

  • 網絡模型文件(比如,caffe的deploy.prototxt)
  • 訓練好的權值文件(比如,caffe的net.caffemodel)

除此之外,還需要明確 batch size,并指明輸出層。

mnist例程中的caffe模型解析代碼:標志是創建 IBuilder對象。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

// 解析caffemodel到tensorrt

void caffeToTRTModel(const std::string& deployFile, // Path of Caffe prototxt file

const std::string& modelFile, // Path of Caffe model file

const std::vector<std::string>& outputs, // Names of network outputs

unsigned int maxBatchSize, // Note: Must be at least as large as the batch we want to run with

IHostMemory*& trtModelStream) // Output buffer for the TRT model

{

// 1. Create builder

//創建一個 IBuilder,傳進gLogger參數是為了方便打印信息。

//builder 這個地方感覺像是使用了建造者模式。

IBuilder* builder = createInferBuilder(gLogger);

// Parse caffe model to populate network, then set the outputs

const std::string deployFpath = locateFile(deployFile, directories);

const std::string modelFpath = locateFile(modelFile, directories);

std::cout << "Reading Caffe prototxt: " << deployFpath << "\n";

std::cout << "Reading Caffe model: " << modelFpath << "\n";

//創建一個 network對象,但是這個network對象只是一個空架子,里面的屬性還沒有具體的數值。

INetworkDefinition* network = builder->createNetwork();

//創建一個caffe模型解析對象,parser,并調用解析函數,填充network對象,

//將caffe模型中的blob解析為tensorRT中的tensor,賦給blob_name_to_tensor變量。

//此處使用了模型文件和權值文件。

ICaffeParser* parser = createCaffeParser();

const IBlobNameToTensor* blobNameToTensor = parser->parse(deployFpath.c_str(),

modelFpath.c_str(),

*network,

DataType::kFLOAT);

//標記輸出blob.

// Specify output tensors of network

for (auto& s : outputs)

network->markOutput(*blobNameToTensor->find(s.c_str()));

// 設置batch size;設置工作空間 size。

builder->setMaxBatchSize(maxBatchSize);

builder->setMaxWorkspaceSize(1 << 20);

// 2.Build engine

//使用network創建 CudaEngine,優化方法在這里執行。

//至此,caffe模型已轉換為tensorRT object。

ICudaEngine* engine = builder->buildCudaEngine(*network);

assert(engine);

//銷毀沒用的network對象和parser對象。

// Destroy parser and network

network->destroy();

parser->destroy();

//將轉換好的tensorRT object序列化到內存中,trtModelStream是一塊內存空間。

//這里也可以序列化到磁盤中。

// Serialize engine and destroy it

trtModelStream = engine->serialize();

//銷毀無用對象

engine->destroy();

builder->destroy();

//關閉protobuf庫

shutdownProtobufLibrary();

}

Deploy Phase

正在上傳…重新上傳取消

Deploy 階段需要文件如下:

  • 標簽文件(這個主要是將模型產生的數字標號分類,與真實的名稱對應起來),不過這個例子中就不需要了,因為MNIST的真實分類就是數字標號。

Deploy 階段可以認為從主函數中就已經開始了。標志是創建 IRuntime 對象。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

int main(int argc, char** argv)

{

………………

………………

// Deserialize engine we serialized earlier

// 創建運行時環境 IRuntime對象,傳入 gLogger 用于打印信息

IRuntime* runtime = createInferRuntime(gLogger);

assert(runtime != nullptr);

ICudaEngine* engine = runtime->deserializeCudaEngine(trtModelStream->data(), trtModelStream->size(), nullptr);

assert(engine != nullptr);

trtModelStream->destroy();

//創建上下文環境,主要用于inference 函數中啟動cuda核

IExecutionContext* context = engine->createExecutionContext();

assert(context != nullptr);

//2.deploy 階段:調用 inference 函數,進行推理過程

// Run inference on input data

float prob[OUTPUT_SIZE];

doInference(*context, data, prob, 1);

………………

………………

}

其中 doInference函數的詳細內容如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

void doInference(IExecutionContext& context, float* input, float* output, int batchSize)

{

//使用傳進來的context恢復engine。

const ICudaEngine& engine = context.getEngine();

//engine.getNbBindings()是為了獲取與這個engine相關的輸入輸出tensor的數量。

//這個地方,輸入+輸出 總共就2個,所以做個驗證。

// Pointers to input and output device buffers to pass to engine.

// Engine requires exactly IEngine::getNbBindings() number of buffers.

assert(engine.getNbBindings() == 2);

//void* 型數組,主要用于下面GPU開辟內存。

void* buffers[2];

//獲取與這個engine相關的輸入輸出tensor的索引。

// In order to bind the buffers, we need to know the names of the input and output tensors.

// Note that indices are guaranteed to be less than IEngine::getNbBindings()

const int inputIndex = engine.getBindingIndex(INPUT_BLOB_NAME);

const int outputIndex = engine.getBindingIndex(OUTPUT_BLOB_NAME);

//為輸入輸出tensor開辟顯存。

// Create GPU buffers on device

CHECK(cudaMalloc(&buffers[inputIndex], batchSize * INPUT_H * INPUT_W * sizeof(float)));

CHECK(cudaMalloc(&buffers[outputIndex], batchSize * OUTPUT_SIZE * sizeof(float)));

//創建cuda流,用于管理數據復制,存取,和計算的并發操作

// Create stream

cudaStream_t stream;

CHECK(cudaStreamCreate(&stream));

//從內存到顯存,從CPU到GPU,將輸入數據拷貝到顯存中

//input是讀入內存中的數據;buffers[inputIndex]是顯存上的存儲區域,用于存放輸入數據

// DMA input batch data to device, infer on the batch asynchronously, and DMA output back to host

CHECK(cudaMemcpyAsync(buffers[inputIndex], input, batchSize * INPUT_H * INPUT_W * sizeof(float), cudaMemcpyHostToDevice, stream));

//啟動cuda核,異步執行推理計算

context.enqueue(batchSize, buffers, stream, nullptr);

//從顯存到內存,將計算結果拷貝回內存中

//output是內存中的存儲區域;buffers[outputIndex]是顯存中的存儲區域,存放模型輸出.

CHECK(cudaMemcpyAsync(output, buffers[outputIndex], batchSize * OUTPUT_SIZE * sizeof(float), cudaMemcpyDeviceToHost, stream));

//這個是為了同步不同的cuda流。

cudaStreamSynchronize(stream);

//銷毀流對象和釋放顯存

// Release stream and buffers

cudaStreamDestroy(stream);

CHECK(cudaFree(buffers[inputIndex]));

CHECK(cudaFree(buffers[outputIndex]));

}

輔助函數

用到 common.cpp 文件中的輔助函數:locateFile( ) 和 readPGMFile( )

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

#include "common.h"

// Locate path to file, given its filename or filepath suffix and possible dirs it might lie in

// Function will also walk back MAX_DEPTH dirs from CWD to check for such a file path

//查找文件

inline std::string locateFile(const std::string& filepathSuffix, const std::vector<std::string>& directories)

{

const int MAX_DEPTH{10};

bool found{false};

std::string filepath;

for (auto& dir : directories)

{

filepath = dir + filepathSuffix;

for (int i = 0; i < MAX_DEPTH && !found; i++)

{

std::ifstream checkFile(filepath);

found = checkFile.is_open();

if (found) break;

filepath = "../" + filepath; // Try again in parent dir

}

if (found)

{

break;

}

filepath.clear();

}

if (filepath.empty()) {

std::string directoryList = std::accumulate(directories.begin() + 1, directories.end(), directories.front(),

[](const std::string& a, const std::string& b) { return a + "\n\t" + b; });

throw std::runtime_error("Could not find " + filepathSuffix + " in data directories:\n\t" + directoryList);

}

return filepath;

}

//讀取圖片

inline void readPGMFile(const std::string& fileName, uint8_t* buffer, int inH, int inW)

{

std::ifstream infile(fileName, std::ifstream::binary);

assert(infile.is_open() && "Attempting to read from a file that is not open.");

std::string magic, h, w, max;

infile >> magic >> h >> w >> max;

infile.seekg(1, infile.cur);

infile.read(reinterpret_cast<char*>(buffer), inH * inW);

}

日志類

common.h文件中有個日志類:?class Logger : public nvinfer1::ILogger

這是一個日志類,繼承自?nvinfer1::ILogger

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

// Logger for TensorRT info/warning/errors

class Logger : public nvinfer1::ILogger

{

public:

Logger(): Logger(Severity::kWARNING) {}

Logger(Severity severity): reportableSeverity(severity) {}

void log(Severity severity, const char* msg) override

{

// suppress messages with severity enum value greater than the reportable

if (severity > reportableSeverity) return;

switch (severity)

{

case Severity::kINTERNAL_ERROR: std::cerr << "INTERNAL_ERROR: "; break;

case Severity::kERROR: std::cerr << "ERROR: "; break;

case Severity::kWARNING: std::cerr << "WARNING: "; break;

case Severity::kINFO: std::cerr << "INFO: "; break;

default: std::cerr << "UNKNOWN: "; break;

}

std::cerr << msg << std::endl;

}

Severity reportableSeverity{Severity::kWARNING};

};

nvinfer1::ILogger?這個類位于 tensorRT頭文件?NvInfer.h?中,此文件路徑:?/usr/include/x86_64-linux-gnu/NvInfer.h

把 ILogger 類摘出來:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

class ILogger

{

public:

//!

//! \enum Severity

//!

//! The severity corresponding to a log message.

//!

enum class Severity

{

kINTERNAL_ERROR = 0, //!< An internal error has occurred. Execution is unrecoverable.

kERROR = 1, //!< An application error has occurred.

kWARNING = 2, //!< An application error has been discovered, but TensorRT has recovered or fallen back to a default.

kINFO = 3 //!< Informational messages.

};

//!

//! A callback implemented by the application to handle logging messages;

//!

//! \param severity The severity of the message.

//! \param msg The log message, null terminated.

//!

virtual void log(Severity severity, const char* msg) = 0;

protected:

virtual ~ILogger() {}

};

可見這個類 是 builder, engine and runtime 的一個日志接口,這個類應該以單例模式使用。即當有多個IRuntime 和/或 IBuilder 對象時,也只能使用同一個ILogger接口。

這個接口中有個枚舉類?enum class Severity?定義了日志報告級別,分別為 kINTERNAL_ERROR,kERROR,kWARNING和kINFO;然后還有一個純虛函數 log( ) ,用戶可以自定義這個函數,以實現不同效果的打印。

比如common.h 文件中Logger類的 log()函數,就是根據不同的報告級別向標準錯誤輸出流輸出帶有不同前綴的信息。這個地方是可以自己定義的,比如你可以設置為輸出信息到文件流然后把信息保存到txt文件中等。

以上就是使用tensorRT優化MNIST的LeNet的一個簡單的例子,其實對于mnist來說,使用tensorRT加速的意義不大,因為這個模型本來就比較小,這里使用這個例子主要是為了學習tensorRT的用法。

參考

  • http://wiki.jikexueyuan.com/project/java-design-pattern/builder-pattern.html
  • 史上最全設計模式導學目錄(完整版)
  • NVIDIA TensorRT | NVIDIA Developer
  • Deploying Deep Neural Networks with NVIDIA TensorRT
  • TensorRT Developer Guide
  • TensorRT C++ API
  • 總結

    以上是生活随笔為你收集整理的TensorRT(2)-基本使用:mnist手写体识别的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

    主站蜘蛛池模板: 色网在线观看 | 日本精品久久久久久久 | 日韩av一区二区三区四区 | 人妻激情文学 | 色婷婷精品久久二区二区密 | 无码人妻精品一区二区三区不卡 | www男人的天堂 | 成人看片泡妞 | 欧美在线视频免费播放 | 波多野结衣av在线播放 | 青娱乐最新视频 | 日韩不卡一区二区三区 | 91免费观看视频 | 日本不卡在线观看 | 国产白浆一区二区 | 黄色小说在线观看视频 | 九九自拍视频 | 国产精品一区二区不卡 | 女人久久久 | 亚洲精品一线 | 岛国精品一区二区 | 双性尿奴穿贞c带憋尿 | 一区二区在线视频免费观看 | 午夜激情一区二区 | 亚洲一区二区三区蜜桃 | 激情啪啪网站 | 六月激情网 | 麻豆久久久午夜一区二区 | 欧美猛交xxx | 天天谢天天干 | 男男免费视频 | 国产剧情在线 | 日本猛少妇色xxxxx | 视频在线观看一区二区三区 | 视频二区中文字幕 | 免费午夜人成电影 | 手机在线看a | 成人无码av片在线观看 | 黄色福利在线观看 | 美女尻逼视频 | 亚洲国产精品视频一区 | 不卡的av电影 | 日本一区二区视频免费 | 中文字幕无码乱码人妻日韩精品 | 中文在线中文资源 | 日韩免费av片 | 国产乱淫视频 | 欧美不卡二区 | 欧美成人精品在线观看 | 黄色小视频在线观看 | 国产三区四区视频 | 欧美日本国产在线 | 一级影片在线观看 | 欧美淫| cao在线视频 | 国产伦精品一区二区三区照片 | www.爱操| 亚洲欧美在线一区二区 | 成人久久久精品国产乱码一区二区 | 亚洲九色 | 国产wwwwww | 黄色a级免费 | 91视频这里只有精品 | 超碰首页| 成年人黄色片 | 日韩欧美在线第一页 | 午夜激情视频 | 黄片毛片在线 | 在线观看成年人网站 | 日本午夜激情 | 99久久网站| 久久亚洲AV无码 | 九久久| 波多野结衣一区二区三区免费视频 | 青青草视频播放 | 国产麻豆精品一区二区 | 色花堂在线 | 中文字幕第27页 | 最新在线视频 | 亚洲熟女一区 | 操极品 | 欧美精品导航 | 伊人www| 色综合久久天天综合网 | 久久黄色片视频 | 欧美成人久久久免费播放 | 国产18av| 一道本在线播放 | 欧美大片在线看免费观看 | 妺妺窝人体色www聚色窝仙踪 | 国模大胆一区二区三区 | 激情网页 | 永久视频在线观看 | 国产青青青 | 国产国语亲子伦亲子 | 高h乱l高辣h文短篇h | 中文字幕一区二区不卡 | 国产伦精品一区三区精东 | 国产综合无码一区二区色蜜蜜 |