VGG 모델을 TensorRT로 포팅을 진행해보겠습니다.

 

Weights 추출하기

먼저 TensorRT 에서 모델을 사용하기 위해서는 Weights 파일을 추출해야 합니다.

 

import torch
import struct
from torchsummary import summary


def main():
    print('cuda device count: ', torch.cuda.device_count())
    net = torch.load('vgg.pth')
    net = net.to('cuda:0')
    net = net.eval()
    print('model: ', net)
    # print('state dict: ', net.state_dict().keys())
    tmp = torch.ones(1, 3, 224, 224).to('cuda:0')
    print('input: ', tmp)
    out = net(tmp)

    print('output:', out)

    summary(net, (3, 224, 224))
    # return
    f = open("vgg.wts", 'w')
    f.write("{}\n".format(len(net.state_dict().keys())))
    for k, v in net.state_dict().items():
        print('key: ', k)
        print('value: ', v.shape)
        vr = v.reshape(-1).cpu().numpy()
        f.write("{} {}".format(k, len(vr)))
        for vv in vr:
            f.write(" ")
            f.write(struct.pack(">f", float(vv)).hex())
        f.write("\n")


if __name__ == '__main__':
    main()

※ 출처 : https://github.com/wang-xinyu/pytorchx/blob/master/vgg/inference.py

 

먼저 Pytorch에서 제공하는 Pretrained VGG를 불러옵니다.

 

위 코드에서는 .pth 로 불러왔지만 실제로 테스트 할때는 torchvision.models 에서 Pretrained VGG11을 가져왔습니다.

 

TensorRT에서 사용하기 위해서 key, value로 나누어 .wts 형식으로 저장해줍니다.

 

이중 하나를 print 해보면 아래와 같습니다.

  • key:  features.0.weight
  • value:  torch.Size([64, 3, 3, 3])

또한 Value 값을 저장해 줄 때 빅라디안 방식의 float 형식, 16진수로 값을 하나씩 띄어쓰기를 기준으로 저장해줍니다.

 

 

저장되어 있는 형태를 간단히 보자면 위와 같습니다.

 

※ 22개의 key, value 정보

더보기

key:  features.0.weight   value:  torch.Size([64, 3, 3, 3])
key:  features.0.bias   value:  torch.Size([64])
key:  features.3.weight   value:  torch.Size([128, 64, 3, 3])
key:  features.3.bias   value:  torch.Size([128])
key:  features.6.weight   value:  torch.Size([256, 128, 3, 3])
key:  features.6.bias   value:  torch.Size([256])
key:  features.8.weight   value:  torch.Size([256, 256, 3, 3])
key:  features.8.bias   value:  torch.Size([256])
key:  features.11.weight   value:  torch.Size([512, 256, 3, 3])
key:  features.11.bias   value:  torch.Size([512])
key:  features.13.weight   value:  torch.Size([512, 512, 3, 3])
key:  features.13.bias   value:  torch.Size([512])
key:  features.16.weight   value:  torch.Size([512, 512, 3, 3])
key:  features.16.bias   value:  torch.Size([512])
key:  features.18.weight   value:  torch.Size([512, 512, 3, 3])
key:  features.18.bias   value:  torch.Size([512])
key:  classifier.0.weight   value:  torch.Size([4096, 25088])
key:  classifier.0.bias   value:  torch.Size([4096])
key:  classifier.3.weight   value:  torch.Size([4096, 4096])
key:  classifier.3.bias   value:  torch.Size([4096])
key:  classifier.6.weight   value:  torch.Size([1000, 4096])
key:  classifier.6.bias   value:  torch.Size([1000])

 


TensorRT 사용하기

 

이제 TensorRT를 사용해보겠습니다.

 

먼저 모델에 관련된 변수들을 선언해줍니다.

 

// 1. 변수 선언
input, output data shape
static const int INPUT_H = 224;
static const int INPUT_W = 224;
static const int INPUT_C = 3;
static const int OUTPUT_SIZE = 1000;
unsigned int batch_size = 1;		// 사용할 배치 사이즈 값

bool serialize = false;				// Engine 생성 유무, true : 엔진 생성
char engineFileName[] = "vgg11";	// Engine 이름

const char* INPUT_BLOB_NAME = "data";
const char* OUTPUT_BLOB_NAME = "prob";

char engine_file_path[256];
sprintf(engine_file_path, "../Engine/%s.engine", engineFileName);	// Engine 저장 경로
  • Input 이미지의 H, W, C
  • Output Size
  • Batch Size
  • Serialize : Engine 강제 생성 유무
  • Engine Name
  • Input Name
  • Output Name

 

// Builds an engine from a network definition.
IBuilder* builder = createInferBuilder(gLogger);

// Holds properties for configuring a builder to produce an engine.
IBuilderConfig* config = builder->createBuilderConfig();

// Create Engine
createEngine(batch_size, builder, config, DataType::kFLOAT, engine_file_path);

// Destroy builder, config
builder->destroy();
config->destroy();
  • Builder, Config : 엔진 생성을 위한 변수

엔진 생성을 위해 createEngine 함수를 만들어 줍니다.

 

// Create Engine

std::cout << "------------ Model Build Start ------------" << std::endl;
// A network definition for input to the builder.
INetworkDefinition* network = builder->createNetworkV2(0U);

// Load .wts File
// Weights : [DataType type, const void* values, int64_t count]
std::map<std::string, Weights> weightMap = loadWeights("../Weights/vgg11.wts");

// Create Input Data
ITensor* data = network->addInput(INPUT_BLOB_NAME, dt, Dims3{ INPUT_H, INPUT_W, INPUT_C });
assert(data);

// Create Model Layers
IConvolutionLayer* conv1 = network->addConvolutionNd(*data, 64, DimsHW{ 3, 3 }, weightMap["features.0.weight"], weightMap["features.0.bias"]);

...

fc1->getOutput(0)->setName(OUTPUT_BLOB_NAME);
std::cout << "set name out" << std::endl;
network->markOutput(*fc1->getOutput(0));

// Build engine
builder->setMaxBatchSize(batch_size);   // 사용할 Batch Size 설정
config->setMaxWorkspaceSize(1 << 20);   // 사용할 메모리 크기 설정

std::cout << "Building engine, please wait for a while..." << std::endl;
IHostMemory* engine = builder->buildSerializedNetwork(*network, *config);
std::cout << "==== model build done ====" << std::endl << std::endl;

std::cout << "==== model selialize start ====" << std::endl << std::endl;
std::ofstream p(engineFileName, std::ios::binary);
if (!p) {
    std::cerr << "could not open plan output file" << std::endl << std::endl;
}
p.write(reinterpret_cast<const char*>(engine->data()), engine->size());
std::cout << "==== model selialize done ====" << std::endl << std::endl;

engine->destroy();
network->destroy();
p.close();
// Release host memory
for (auto& mem : weightMap)
{
    free((void*)(mem.second.values));
}

모델 Layer는 너무 길어서 생략했습니다.

 

이제 만들거나 불러온 Engine File이 올바른지 확인을 해봅니다.

 

// 4. Engine file 로드 
char* trtModelStream{ nullptr };// 저장된 스트림을 저장할 변수
size_t size{ 0 };
std::cout << "------------ Engine file load ------------" << std::endl << std::endl;
std::ifstream file(engine_file_path, std::ios::binary);
if (file.good()) {							// good() : 입출력 가능 여부 확인
    file.seekg(0, file.end);				// seekg() : 포인터 이동
    size = file.tellg();					// tellg() : 크기
    file.seekg(0, file.beg);
    trtModelStream = new char[size];
    file.read(trtModelStream, size);
    file.close();
}
else {
    std::cout << "[ERROR] Engine file load error" << std::endl;
}

Engine File을 읽어와 trModelStream 변수에 저장시켜 주고

 

// 5. Engine File 로드 후 Engine 생성
std::cout << "------------ Engine file deserialize ------------" << std::endl << std::endl;
IRuntime* runtime = createInferRuntime(gLogger);
ICudaEngine* engine = runtime->deserializeCudaEngine(trtModelStream, size);
IExecutionContext* context = engine->createExecutionContext();
delete[] trtModelStream;

void* buffers[2];
const int inputIndex = engine->getBindingIndex(INPUT_BLOB_NAME);
const int outputIndex = engine->getBindingIndex(OUTPUT_BLOB_NAME);

// GPU에서 입력과 출력으로 사용할 메모리 공간할당
CHECK(cudaMalloc(&buffers[inputIndex], batch_size * INPUT_C * INPUT_H * INPUT_W * sizeof(uint8_t)));
CHECK(cudaMalloc(&buffers[outputIndex], batch_size * OUTPUT_SIZE * sizeof(float)));

Engine을 생성하게 됩니다.

 

이제 데이터를 넣어 연산을 진행해봅니다.

 

// 7. Inference
// CUDA 스트림 생성
cudaStream_t stream;
CHECK(cudaStreamCreate(&stream));

for (int i = 0; i < iter_count; i++) {
    auto start = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count();

    CHECK(cudaMemcpyAsync(buffers[inputIndex], input.data(), batch_size * INPUT_C * INPUT_H * INPUT_W * sizeof(uint8_t), cudaMemcpyHostToDevice, stream));
    context->enqueue(batch_size, buffers, stream, nullptr);
    CHECK(cudaMemcpyAsync(outputs.data(), buffers[outputIndex], batch_size * OUTPUT_SIZE * sizeof(float), cudaMemcpyDeviceToHost, stream));
    cudaStreamSynchronize(stream);

    auto dur = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count() - start;
    dur_time += dur;
    std::cout << dur << " milliseconds" << std::endl;
}

먼저 GPU를 사용하기 위해 CUDA Stream을 생성해줍니다.

 

CUDA 관련 내용은 다루지 않겠습니다.

 

이제 결과를 출력해주면 끝입니다!

 

// 8. 결과 출력
int max_index = max_element(outputs.begin(), outputs.end()) - outputs.begin();

// Release stream and buffers ...
cudaStreamDestroy(stream);
CHECK(cudaFree(buffers[inputIndex]));
CHECK(cudaFree(buffers[outputIndex]));
context->destroy();
engine->destroy();
runtime->destroy();

 

 

 

 

 

 

 

 

 

※ 참고 자료 : 

https://docs.nvidia.com/deeplearning/tensorrt/api/python_api/infer/Core/Logger.html

 

https://github.com/yester31/TensorRT_EX

↑ 굉장히 잘나와있다 추천!

'Deep Learning > TensorRT' 카테고리의 다른 글

06. TensorRT Resnet18  (0) 2023.03.21
04. TensorRT Custom Layer 만들기  (0) 2022.02.14
02. TensorRT 다루기  (0) 2022.02.11
01. TensorRT 설치 및 다운로드  (0) 2022.02.11

+ Recent posts