8/30/2019

Lenovo Laptop 구입완료 및 기본테스트(Window10 home)

1. Ubuntu Laptop 구입완료 및 사양확인

매번 구입 미루다가 드디어 Laptop를 구입하기로 결정하고 주문하고 방문수령으로 하기로 결정했다
조금 더 지나면 가격이 떨어지기를 바랬는데, 가격이 이상하게 쉽게 떨어지지가 않았다. 




구입도중 알게되었는데, 같은 옥션 사이트이고, 같은 판매점인데, 접속할때 마다 동일모델에 동일옵션인데, 매번가격이 2만원 ~ 3만원사이로
차이가 나서 여러번 접속 끝에 최저가를 겨우 찾아 동일한 모델 및 옵션설정완료 후 구입완료

가격은 여러번 고민끝에 174만원을 주고 사기로 결정했지만, 이전의 산 아주비싼 삼성노트북도 있는데 이 노트북을 사는게 돈이 너무 안까워 고민을 하고 또 여러번 고민했지만, 
나의 마음이 한번 사기로 결정이 되었고, 여러번의 비교 끝에 상위 노트북으로 확정되어서 구입함 

이곳에 노트북 비교에 관련된 것들을 기록하도록 하며, 우선적으로 NVIDIA-GPU관련사항일 것 같으며, CPU의 실제성능 및 실제클락 비롯하여 Cache기능 
비롯하여, NVMe의 Bandwidth를 확인 후 실제 구입할 NVMe의 성능확인 (랩탑은 PCIe->M.2로 변경됨) 

NVMe 관련내용 

만약  이 노트북을 산게  후회가 된다면, 나중에 이 가격과 이글을 다시 보도록하자. 





  • Laptop 모델사양 : Lenovo Y540-15IRH LEGION i7 Prime 2060
  1. OS: FreeDos (직접 설치)
  2. CPU: i7-9750H 
  3. GPU: Geforce RTX 2060 (아래 링크로 세부확인)
  4. Storage: NVMe 1T, HDD 1T 변경 
  5. RAM: DDR4 32G 변경 (16G->32G)
  6. DISPLAY: FULL HD   (주사율을 144HZ 변경해준다고했는데, 반드시 검증)

노트북의 DDR4의 32G로 변경했으며, GPU는 Transfer Learning을 위해서 노트북에서 최고사양인 GPU로 결정완료

  • 노트북 관련정보
Laptop은 Gaming Laptop이라 성능은 괜찮지만, Pan소리는 너무 크며, 이동식으로 사용하기는 좀 무리가 있다. 

Lenovo-Legion-Y540의 성능비교 (가격대비 성능은 최고)
  https://www.notebookcheck.net/Lenovo-Legion-Y540-with-RTX-2060-laptop-review-Gaming-laptop-with-good-sound-and-144-Hz-panel.428659.0.html

Laptop NVIDIA-GPU 비교 및 BIOS 지원문제 (반드시 참조)
  https://ahyuo79.blogspot.com/2019/08/nvidia-gpu-laptop.html

  • 수령한 레노버 노트북 모델 및 기본사양확인
  1. BIOS가 F2로시작 상위 Interface들을 점검시작
  2. Information-> Memory확장 과 Storage 연결상태확인 완료 
  3. Configuration->Graphic Device에서는 GPU 확인불가 (Discrete Graphic)

GPU는 NVIDIA Geforce말고 Intel Graphic도 있을텐데, 그부분도 확인을해야하는데, 아직 완벽히 확인하지못함 
(추후 인텔용은 기본 그래픽카드로 사용하며, NVIDIA는 개발을 위해 별도로 이용)


1.1 Window 10 설치 및 USB Boot 만들기

Laptop을 Ubuntu용으로 사용할 것이지만, 판매자도 불안하게 고장에 대해서 여러번 언급해서 확실하게 기본 테스트하기로 결정하고,

인증받지 않은 Window 10 Home or Pro 를 설치해서 기본테스트 Laptop 테스트 하기로 결정하였다. 
참고로 Window Key가 없어도 기본적인 Window는 모두 동작하니 걱정하지 마시길. 
인터넷도 잘되며, 기본적인 것은 다 잘되지만, Window 인증이 되지 않아 Update 가 안된다. 


원래생각은 Linux Image 처럼 Window 10 ISO Image만 Download 한 다음 Image Writer인 아래 두 프로그램 중 하나로 USB 에 Write 할 생각이였다. 
  1. Win32DiskImager 
  2. Echer

하지만,  재미있게도, 아래의 Window 사이트에서 USB Write까지 자동으로 생성해준다. 
다만 , 현재 사용 중인 OS는 Window 7 기반의 나의 삼성노트북이므로, Ubuntu에서 될지는 모르겠다.

Window 10 Image Download 및 USB Image 생성
  https://www.microsoft.com/ko-kr/software-download/windows10

  • 지금 도구 다운로드


  • MediaCreationTool21H2.exe 실행 



  • USB 플래시에 직접 Flash 진행
  1. USB 플래시 드라이브 
  2. ISO 파일

마지막에서  많이 고민을 많이하게 되었는데, 왜냐하면 기존에 내가 사용하던 USB가 전부 다 4G만 가지고 있었는데, 
USB 8G 가 필요하다고 하니, 8G USB가 없어서 문제가 발생
한참을 뒤지고 생각하다가 예전에 개발보드의 SDcard 와  SDcard to USB로  USB32G 만들어서 이문제를 해결하고 진행했다. 
8G Image를  생성하는데 시간이 상당히 오래걸림


  • 최종 생성된 USB 의 UEFI로 구성확인 
USB의 EFI System Partion 은 FAT로 구성



상위 그림 참조 
  1. boot code:   boot  directory 
  2. boot manager:  bootmgr.efi / bootmgr 

  • UEFI Booting  
efi->boot->bootx64.efi 

UEFI의 사용할 경우 기존 처럼 Boot Sector에 의존적이 않으며, 상위 UEFI Booting 참고 




  • UEFI 관련내용 
확장 Firmware Interface로 BIOS의 확장기능이라고 보면 될 것 같다. 
간단하게 설명하면, Intel에서 역시 개발했으며 Legacy BIOS를 대체를 하며, 또한 지원을 해준다. 


  • FDISK로 ESD-USB 파티션구조확인
FDISK로 볼수 있으니, MBR Partition 



UEFI 를 지원하게되면 처음 Boot Sector를 찾는것이 아니라, 
EFI Partition에 존재하는 상위 정보기반으로 Booting 하게 되는데, GPT Partition으로 점점 변경되어 가는 추세인 것 같다. 
물론 기존의 MBR도 지원을 해주고 있다고 한다. 

UEFI Booting에 대해서는 상단의 wiki에 잘 설명이 되어있다. 
재미 있는 것은 다양한 Booting (Network Booting) 도 지원은 물론 Shell도 실행하여 Command 입력도 가능하니,  나중에 시간되면 해보자.


1.2 Window 10 home 설치 및 Laptop의 기본확인 

위에서 설명했듯이 제품키가 없으며,  기본적인 설정 기반으로 Window 10 Home를 Laptop에  설치 완료 

BIOS에서 UEFI 모드가 아닌 BIOS에서 Legacy Mode 설치

기존 Window 7과 다르게 Window 10 귀찮게 기본적으로 설정하는 것도 많음
(SKYPE, PIN, ONE DRIVE, 기타등등, 다 대충으로 넘김)

  1. WIFI 연결 및 각종 Driver Upgrade 
  2. SKYPE: 오디오/마이크/웹캠동작확인 
  3. 컴퓨터관리->장치관리자 각 기능확인 
  4. 블루투스 테스트진행완료 
  5. 모니터의 주사율을 확인이 불가 
    1. 디스플레이에서 확인불가  ( 문제 주사율 144Hz 미지원)

디스크 관리 부분 확인 (추후 다시 0번 DISK에 Ubuntu 설치)
  1. 디스크-0 : NVMe
  2. 디스크-1 : HDD 
장치관리자에서 기본테스트 진행


드라이버 업데이트 확인
추가업데이트 진행
인증하지 못함

CD키가 없어도 기본으로 잘동작하며, 드라이버도 자동으로 잘 찾아 설치를 한다.
추후에 인증을 받으면 되니, 상관이 없다

레노버노트북을 사용한다면, 레노버웹사이트에서 가입하고 본인 Laptop SN를 입력하면 쉽게 노트북관리가 된다.



다른것은 확인했는데, 나의 랩탑모니터가 144Hz/60Hz 확인못했다. 144Hz동작은 왠지 안되는 것 같다.
나에게 꼭 필요한 사항이 아니라서, 반품 및 교환 생각만 하고 있지만 귀찮다.

8/29/2019

NVIDIA 관련정보 및 Pegasus 정보수집 중

  • NVIDIA 자율주행부분 데모
우연히 FACEBOOK에서 찾은 정보인데, DeepStream으로 구현된 것 같은데,  정말구현이 잘되어있다.
궁금한것은 HW정보인데, 아래의 NVIDIA PX/AGX Pegasus를 사용하는 지 궁금하다
  https://blogs.nvidia.com/blog/2019/08/21/drive-labs-autonomous-vehicle-ride/?fbclid=IwAR2OMul2hCheqhNqIyrpBRZn_v48yvZ4eY8xsopqeo_8i9eRLw_Uoai03r0
  https://youtu.be/1W9q5SjaJTc


  • NVIDIA Pegasus 관련정보수집 
NVIDIA PX/AGX Pegasus는 Xavier 다음으로 나온 HW라고하는데 Xavier를 두개 연결하여 만든 것 같은데, 성능은 10배까지 차이가 나는 것 같으니,어떻게 돌아가는지 궁금하다

NVIDIA PX Pegasus
  https://blogs.nvidia.co.kr/2017/10/11/nvidia-drive-px-pegasus/

NVIDIA PX Pegasus 와 PX Xavier 정보
  https://en.wikipedia.org/wiki/Nvidia_Drive

NVIDIA DRIVE AGX Pegasus 와 AGX Xavier 정보
  https://www.nvidia.com/ko-kr/self-driving-cars/drive-platform/hardware/

8/17/2019

TensorRT 5 Python 기본구조 파악

1.  TensorRT Python 분석 

TensorRT C++ 기준으로 작성된 Inference 엔진이며,  최신 Version 부터 Python를 제공하고 있어 쉽게 TensorRT를 동작방식가능하고 이를 수정가능하다.
이 TensorRT Python을 이용하여 C++ 대신 손쉽게 TensorRT를 Control 하는 방법들을 알고자하여  테스트 진행한다
일단 C++ API는 골치아파서 넘어가자


1.1 TensorRT Python 관련사항정리 

  • TensorRT 5.0  기본구조 및 동작 (C++/Python 참조)
TensorRT는 기본동작을 C++과 Python을 이용하여 설명
  https://ahyuo79.blogspot.com/2019/06/tensorrt-50-jetson-agx-xavier.html

  • Jetpack 4.2.1 설치기준 
  https://ahyuo79.blogspot.com/2019/07/jetpack-421.html


우선 기존에도 설명을 했지만, TensorRT는 각각의 Layer를 지원하며, 이에 맞추어 각각의 Model에 맞는 network를  지원을 해준다고 한다
그러므로, Model 안에 TensorRT의 Layer가 지원이 안되는 경우는 직접 Custom Layer로 구현을 해야한다 (이 부분 이전에도 설명)
  https://docs.nvidia.com/deeplearning/sdk/tensorrt-developer-guide/index.html#extending
  https://docs.nvidia.com/deeplearning/sdk/tensorrt-api/python_api/infer/Plugin/pyPlugin.html

DeepStream 의 Yolo의 Custom Layer 부분참조 ( C++ 로 구현) 와 IPlugin 확인
  https://ahyuo79.blogspot.com/2019/08/ds-sdk-40-test4-iplugin-sample.html
  https://ahyuo79.blogspot.com/2019/08/deepstream-sdk-40-plugin-gstreamer.html

TensorRT Support Matrix
  https://docs.nvidia.com/deeplearning/sdk/tensorrt-support-matrix/index.html

TensorRT Layer
  https://docs.nvidia.com/deeplearning/sdk/tensorrt-api/python_api/infer/Graph/Layers.html


1.2 TensorRT 기본구조  

TensorRT는 NVIDIA에서 제공하는 Inference 를 위한 Engine이며, 기본소스가 C++로 구성되며, TensorRT의 기본구조는 C++/Python API 동일하며, 두가지 모드로 나누어 생각해볼 수 있다.

  • Caffe/UFF/Onnx  Parser 이용방법 
기존의 Framwork과 호환성을 위해서 Training된 Model를 존재하다면, TensorRT의 UFF/Caffe/Onnx Parser를 이용하여 Model의 Network를 쉽게 구성하고
이를 기반으로 TensorRT Engine을 만들어 추론으로 바로 가능하다.
다만, TensorRT가 지원되지 않는 Layer는 Custom Layer로 구현하여 연결해야한다.


  • TensorRT에서 직접 Network 구성 
TensorRT도 Network를 Layer를 추가하여 구성을 직접할수 있지만, 문제는 Training된 weight와 bias 값을 얻어올 수가 없으므로, 이 부분도 다른 Framework에서 가져와야한다



1.3 TensorRT Python 필요사항 (pyCuda 설치)

Sample의 TensorRT Python 실행을 위해서는 필수로 필요하기 때문에 아래사항을들을 설치하자

$ cd /usr/src/tensorrt/samples/python/introductory_parser_samples
$ cat requirements.txt   
numpy
Pillow
pycuda
$ export CUDA_INC_DIR=/usr/local/cuda-10.0/include

$ sudo python2 -m pip install -r requirements.txt   //pycuda 설치 중 cuda 문제로 에러발생 (#include cuda .h)
or  
$ sudo python3 -m pip install -r requirements.txt  //pycuda 설치 중 cuda 문제로 에러발생 (#include cuda .h)

$ pip list or pip3 list  // 상위 requrement.txt package 확인 
...


TensorRT를 C++로 작성하면 pycuda는 필요가 없을 텐데,  TensorRT를 python으로하니, pycuda문제가 발생하며, 이는 필수로 설치를 해야한다 

  • pycuda 직접설치 (필수 설치)
pycuda는 필수로 필요하므로, 상위에서 매번에러가 발생하여, Package대신 아래와 같이 직접 다운받아 설치로 결정

//pycuda 직접설치로 결정 (pip list 검색불가)  아래 사이트참조 
$ cd ~ 
//https://pypi.org/project/pycuda/#files
$ wget https://files.pythonhosted.org/packages/5e/3f/5658c38579b41866ba21ee1b5020b8225cec86fe717e4b1c5c972de0a33c/pycuda-2019.1.2.tar.gz
$ tar zxvf pycuda-2019.1.2.tar.gz 
$ cd pycuda-2019.1.2/

$ python configure.py --cuda-root=/usr/local/cuda-10.0   //python2 로 진행 
or 
$ python3 configure.py --cuda-root=/usr/local/cuda-10.0
//pycuda 설치 
$ sudo make install
........
Using /usr/local/lib/python2.7/dist-packages
Finished processing dependencies for pycuda==2019.1.2
//설치완료 

pycuda 설치방법
  https://docs.nvidia.com/deeplearning/sdk/tensorrt-install-guide/index.html#installing-pycuda
  https://devtalk.nvidia.com/default/topic/1013387/jetson-tx2/is-the-memory-management-method-of-tx1-and-tx2-different-/post/5167500/#5167500
  https://docs.nvidia.com/deeplearning/sdk/tensorrt-api/python_api/gettingStarted.html


2. TensorRT Caffe/UFF/Onnx Parser 실행  

아래와 같이 Parser Sample로 찾아가서 Python으로 실행해보면,  /usr/src/tensorrt/data/resnet50 의 각각의 caffe/uff/onnx format의 모델을 사용하여 동일하게 추론을 한다
그리고 아래의위치 /usr/src/tensorrt/data/resnet50/*.jpg Test Sample 그림을 인식하는 것이다

$ cd /usr/src/tensorrt/samples/python/introductory_parser_samples

$ find / -name ResNet50_fp32.caffemodel 2> /dev/null 
/usr/src/tensorrt/data/resnet50/ResNet50_fp32.caffemodel

$ python caffe_resnet50.py    // 기본설정, /usr/src/tensorrt/data
Correctly recognized /usr/src/tensorrt/data/resnet50/reflex_camera.jpeg as reflex camera

$ python uff_resnet50.py 
Correctly recognized /usr/src/tensorrt/data/resnet50/binoculars.jpeg as binoculars

$ python onnx_resnet50.py 
Correctly recognized /usr/src/tensorrt/data/resnet50/binoculars.jpeg as binoculars

각각의 caffe/uff/onnx의 reset50 동작확인이 가능하며 기본적인 구조들이 비슷하기 때문에, 아래의 UFF기준으로만 분석한다.( Tensorflow)

Resnet50의 정보
  https://datascienceschool.net/view-notebook/958022040c544257aa7ba88643d6c032/


2.1 uff_resnet50.py 소스분석 

TensorRT는 Framework와 호환성을 위해서 3개의 Parser를 지원을 해주고 있으며, 이 Parser를 이용하여 동작되는 구조는 3개가 거의 유사하므로,
현재 3개중 UFF만을 선택해서 소스분석를 세부분석 해보고, 돌아가는 원리를 파악해보자.


다음 소스구조는 resenet50-infer-5.uff model을 UFF Parser 이용하여 Engine생성 후 실제 적용하여추론하는 소스이다.

$ cat uff_resnet50.py 
# This sample uses a UFF ResNet50 Model to create a TensorRT Inference Engine
import random
from PIL import Image
import numpy as np

import pycuda.driver as cuda
# This import causes pycuda to automatically manage CUDA context creation and cleanup.
import pycuda.autoinit

import tensorrt as trt

import sys, os
sys.path.insert(1, os.path.join(sys.path[0], ".."))

## common.py 부분으로 생략
import common

## class를 이용하여 각 설정값을 정의 
class ModelData(object):
    MODEL_PATH = "resnet50-infer-5.uff"
    INPUT_NAME = "input"
    INPUT_SHAPE = (3, 224, 224)
    OUTPUT_NAME = "GPU_0/tower_0/Softmax"
    # We can convert TensorRT data types to numpy types with trt.nptype()
    DTYPE = trt.float32

# You can set the logger severity higher to suppress messages (or lower to display more messages).
TRT_LOGGER = trt.Logger(trt.Logger.WARNING)

## host(CPU) 와 device(GPU)  buffer를 분리해서 할당하며, 할당방식도 다르다 
## https://docs.nvidia.com/deeplearning/sdk/tensorrt-developer-guide/index.html#perform_inference_python
## 상위 링크에서 아래의 기능확인가능 
## Host(CPU) 와 Device(GPU) 의 Buffer를 설정하고, Stream의 생성 
# Allocate host and device buffers, and create a stream.
def allocate_buffers(engine):
    # Determine dimensions and create page-locked memory buffers (i.e. won't be swapped to disk) to hold host inputs/outputs.
    h_input = cuda.pagelocked_empty(trt.volume(engine.get_binding_shape(0)), dtype=trt.nptype(ModelData.DTYPE))
    h_output = cuda.pagelocked_empty(trt.volume(engine.get_binding_shape(1)), dtype=trt.nptype(ModelData.DTYPE))
    # Allocate device memory for inputs and outputs.
    d_input = cuda.mem_alloc(h_input.nbytes)
    d_output = cuda.mem_alloc(h_output.nbytes)
    # Create a stream in which to copy inputs/outputs and run inference.
    stream = cuda.Stream()
    return h_input, d_input, h_output, d_output, stream

## host(CPU) 와 device(GPU)  buffer  관리와 추론 진행 
def do_inference(context, h_input, d_input, h_output, d_output, stream):
    ## CPU->GPU로 전송
    # Transfer input data to the GPU.
    cuda.memcpy_htod_async(d_input, h_input, stream)
    ## GPU 전송후 inference 실행 
    # Run inference.
    context.execute_async(bindings=[int(d_input), int(d_output)], stream_handle=stream.handle)
    ## GPU->CPU Memory 결과값을 가져오기
    # Transfer predictions back from the GPU.
    cuda.memcpy_dtoh_async(h_output, d_output, stream)
    # Synchronize the stream
    stream.synchronize()

## TensorRT Engine Build이며, UFF Parser를 이용하여 각 정의 
## UFF File,resnet50-infer-5.uff의 input name/output name, input shape를 등록  
## https://docs.nvidia.com/deeplearning/sdk/tensorrt-archived/tensorrt_500rc/tensorrt-api/python_api/coreConcepts.html
## 상위 링크에서 확인가능 
# The UFF path is used for TensorFlow models. You can convert a frozen TensorFlow graph to UFF using the included convert-to-uff utility.
def build_engine_uff(model_file):
    # You can set the logger severity higher to suppress messages (or lower to display more messages).
    with trt.Builder(TRT_LOGGER) as builder, builder.create_network() as network, trt.UffParser() as parser:
        # Workspace size is the maximum amount of memory available to the builder while building an engine.
        # It should generally be set as high as possible.
        builder.max_workspace_size = common.GiB(1)
        # We need to manually register the input and output nodes for UFF.
        parser.register_input(ModelData.INPUT_NAME, ModelData.INPUT_SHAPE)
        parser.register_output(ModelData.OUTPUT_NAME)
        # Load the UFF model and parse it in order to populate the TensorRT network.
        parser.parse(model_file, network)
        # Build and return an engine.
        return builder.build_cuda_engine(network)

## test_image의 변형이 없이 그대로 리턴하며, pagelocked_buffer(h_input), HOST(CPU) Buffer 에 Image정보를 Resize, antialias ,transpose 후 최종 1D Array 변경 
## CHW (Channel × Height × Width) 상위 INPUT_SHAPE (3, 224, 224)
## 들어온 test_image 그대로 return
def load_normalized_test_case(test_image, pagelocked_buffer):
    # Converts the input image to a CHW Numpy array
    def normalize_image(image):
        # Resize, antialias and transpose the image to CHW.
        c, h, w = ModelData.INPUT_SHAPE
        return np.asarray(image.resize((w, h), Image.ANTIALIAS)).transpose([2, 0, 1]).astype(trt.nptype(ModelData.DTYPE)).ravel()

    # Normalize the image and copy to pagelocked memory.
    np.copyto(pagelocked_buffer, normalize_image(Image.open(test_image)))
    return test_image

def main():
    
    ## commom.py를 이용하여 다음과 같이 설정 
    ## data_path  = /usr/src/tensorrt/data/resnet50 설정 
    ## data_files = "binoculars.jpeg", "reflex_camera.jpeg", "tabby_tiger_cat.jpg","resnet50-infer-5.uff" ,"class_labels.txt"    
    # Set the data path to the directory that contains the trained models and test images for inference.
    data_path, data_files = common.find_sample_data(description="Runs a ResNet50 network with a TensorRT inference engine.", subfolder="resnet50", find_files=["binoculars.jpeg", "reflex_camera.jpeg", "tabby_tiger_cat.jpg", ModelData.MODEL_PATH, "class_labels.txt"])

    ## test_images 에는 data_files의 0~2까지 즉 Image 3개 0>=x && x< 3
    # Get test images, models and labels.
    test_images = data_files[0:3]

    ## 3부터 uff_model_file 과 lables_file 설정  
    uff_model_file, labels_file = data_files[3:]

    ## labels 값을 File을 읽어 줄 순서대로 넣음 
    labels = open(labels_file, 'r').read().split('\n')

    ## 상위함수로 Build TensorRT Engine이며, UFF Parser를 하여 준비  
    # Build a TensorRT engine.
    with build_engine_uff(uff_model_file) as engine:
        # Inference is the same regardless of which parser is used to build the engine, since the model architecture is the same.

        ## 상위함수로 Host(CPU), Device(GPU) 별로 Buffer를 input/output 할당 
        # Allocate buffers and create a CUDA stream.
        h_input, d_input, h_output, d_output, stream = allocate_buffers(engine)

        ## Build된 Engine을 생성하고 inference를 위해 준비 
        # Contexts are used to perform inference.
        with engine.create_execution_context() as context:

            ## Random으로 3개의 test_images들 중 선택하여 하나확정 
            # Load a normalized test case into the host input page-locked buffer.
            test_image = random.choice(test_images)

            ## Host(CPU) Buffer에 test_image를 넣어주고, test_case로 그대로 반환 
            test_case = load_normalized_test_case(test_image, h_input)

            ## Host(CPU) input buffer 기반으로 Device(GPU)로 추론하여 결과를 다시 Host(CPU) output
            # Run the engine. The output will be a 1D tensor of length 1000, where each value represents the
            # probability that the image corresponds to that label
            do_inference(context, h_input, d_input, h_output, d_output, stream)

            ## 추론에서 얻은 Host output중 가장 큰 값의 index를 찾아 반환 (pred)
            # We use the highest probability as our prediction. Its index corresponds to the predicted label.
            pred = labels[np.argmax(h_output)]

            ## Random으로 선택한 Test Case와 추론한 prediction을 비교 
            if "_".join(pred.split()) in os.path.splitext(os.path.basename(test_case))[0]:
                print("Correctly recognized " + test_case + " as " + pred)
            else:
                print("Incorrectly recognized " + test_case + " as " + pred)

if __name__ == '__main__':
    main()


  • 상위 전체소스를  이해하기 위해서 아래 문서 (필수)
UFF Parser를 이용하기 때문에 이곳부터 소스를 보면 쉽게 이해가 쉽다
  https://docs.nvidia.com/deeplearning/sdk/tensorrt-developer-guide/index.html#import_tf_python

TensorRT Python Concept (Build Engine, 상위링크에도 설명)
  https://docs.nvidia.com/deeplearning/sdk/tensorrt-archived/tensorrt_500rc/tensorrt-api/python_api/coreConcepts.html


2.2 uff_resnet50.py  관련 TensorRT API 문서  

초기 Class를 생성시 설정되는 값은 중요하며, 각 모델마다 차이점이 존재한다.
  1. Caffemodel : model-file, proto-file, output-blob-names        
  2. UFF: uff-file, input-dims, uff-input-blob-name, output-blob-names                  
  3. ONNX: onnx-file   

  • Python Parser (Caffe,UFF,ONNX)
trt로 검색후 Model별 확인
  https://docs.nvidia.com/deeplearning/sdk/tensorrt-api/python_api/parsers/Uff/pyUff.html
  https://docs.nvidia.com/deeplearning/sdk/tensorrt-api/python_api/parsers/Caffe/pyCaffe.html
  https://docs.nvidia.com/deeplearning/sdk/tensorrt-api/python_api/parsers/Onnx/pyOnnx.html


  • TensorRT 기본적인 Types / Core  (상위 Logger 와 Datatype 확인)
상위소스에서 trt로 검색하여 관련된 것을 아래에서 확인가능
  https://docs.nvidia.com/deeplearning/sdk/tensorrt-api/python_api/infer/FoundationalTypes/pyFoundationalTypes.html
  https://docs.nvidia.com/deeplearning/sdk/tensorrt-api/python_api/infer/Core/pyCore.html


  • UFF Converter/Operators
  https://docs.nvidia.com/deeplearning/sdk/tensorrt-api/python_api/uff/uff.html#
  https://docs.nvidia.com/deeplearning/sdk/tensorrt-api/python_api/uff/Operators.html#

  • 상위 TensorRT Model의 성능 및 설정되어야하는 부분 확인 
  1.6  각 모델의 성능비교 (TensorRT)  
  https://ahyuo79.blogspot.com/2019/08/deepstream-sdk-40-plugin-gstreamer.html


아쉬운 점이 있다면 별도로 Serialize 와 Deserialize를 진행하지 않아서 Engine을 저장하지 않아 속도가 느리다.


2.3 uff_resnet50.py model engine 생성하여 속도개선 

상위 소스를 매번 실행할때마다 매번 Engine을  새로생성하므로, 시간이 상당히 많이 걸리므로,아래와 같이 처음생성시 Engine을 File로 저장하고,
만들어진 Engine File을 매번 이용하는 방법으로 소스를 수정해보자.

기존에 이용하던 DeepStream의 model-engine 과 동일하다
아래와 같이 소스를 수정하면 매번 엔진 빌드시간이 없어지므로, 실행되는 시간이  빨라진다.


$ sudo vi uff_resnet50.py 
.............
def main():

# Set the data path to the directory that contains the trained models and test images for inference.
    data_path, data_files = common.find_sample_data(description="Runs a ResNet50 network with a TensorRT inference engine.", subfolder="resnet50", find_files=["binoculars.jpeg", "reflex_camera.jpeg", "tabby_tiger_cat.jpg", ModelData.MODEL_PATH, "class_labels.txt"])
    # Get test images, models and labels.
    test_images = data_files[0:3]
    uff_model_file, labels_file = data_files[3:]
    labels = open(labels_file, 'r').read().split('\n')

   
    ##  두번째 부터 이미 저장된 Engine File을 Open하여 Deserialize하여 사용 (Engine Build Time을 없앰)
    ##  아래소스와 거의 동일하지만, build_engine_uff 함수의 역할이 필요 없으므로, 바로 추론가능 
    ##  마지막에 소스 바로종료를 하여 밑에 소스 실행금지 


    with open("sample_uff.engine","rb") as f, trt.Runtime(TRT_LOGGER) as runtime:
        engine = runtime.deserialize_cuda_engine(f.read())
       # Inference is the same regardless of which parser is used to build the engine, since the model architecture is the same.
        # Allocate buffers and create a CUDA stream.
        h_input, d_input, h_output, d_output, stream = allocate_buffers(engine)
        # Contexts are used to perform inference.
        with engine.create_execution_context() as context:
            # Load a normalized test case into the host input page-locked buffer.
            test_image = random.choice(test_images)
            test_case = load_normalized_test_case(test_image, h_input)
            # Run the engine. The output will be a 1D tensor of length 1000, where each value represents the
            # probability that the image corresponds to that label
            do_inference(context, h_input, d_input, h_output, d_output, stream)
            # We use the highest probability as our prediction. Its index corresponds to the predicted label.
            pred = labels[np.argmax(h_output)]
            if "_".join(pred.split()) in os.path.splitext(os.path.basename(test_case))[0]:
                print("2nd Correctly recognized " + test_case + " as " + pred)
            else:
                print("2nd Incorrectly recognized " + test_case + " as " + pred)
            sys.exit()


    ##  첫번째 실행에만 Engine을 Build하고 이를 Serialize 한 후 File로 저장 (TensorRT Manual 참고) 

    # Build a TensorRT engine.
    with build_engine_uff(uff_model_file) as engine:
        with open("sample_uff.engine","wb") as f:
            f.write(engine.serialize())
        # Inference is the same regardless of which parser is used to build the engine, since the model architecture is the same.
        # Allocate buffers and create a CUDA stream.
        h_input, d_input, h_output, d_output, stream = allocate_buffers(engine)
        # Contexts are used to perform inference.
        with engine.create_execution_context() as context:
            # Load a normalized test case into the host input page-locked buffer.
            test_image = random.choice(test_images)
            test_case = load_normalized_test_case(test_image, h_input)
            # Run the engine. The output will be a 1D tensor of length 1000, where each value represents the
            # probability that the image corresponds to that label
            do_inference(context, h_input, d_input, h_output, d_output, stream)
            # We use the highest probability as our prediction. Its index corresponds to the predicted label.
            pred = labels[np.argmax(h_output)]
            if "_".join(pred.split()) in os.path.splitext(os.path.basename(test_case))[0]:
                print("Correctly recognized " + test_case + " as " + pred)
            else:
                print("Incorrectly recognized " + test_case + " as " + pred)

$ sudo python uff_resnet50.py  // wrtie 시 파일 접근권한 
2nd Correctly recognized /usr/src/tensorrt/data/resnet50/reflex_camera.jpeg as reflex camera

개선된 소스로 하면 속도가 이전과는 다르다
  1. 처음동작시 Engine을 serialize 하여 File로  sample_uff.engine 저장 
  2. 두번째 부터는 이미 저장된 Engine File을  sample_uff.engine  읽어 Deserialize 한 후 바로사용

3. TensorRT 직접 Network를 구성 

만약 Model ( UFF/Caffe/ONNX)의 Parser로 Network를 구성하지 않고 직접 TensorRT에서 Network를 구성한다면 어떻게 해야할까?
그렇다면, Training에서 만들어진 결과값, 즉 (weight,bias)은 어디에서는 가져와야 동작 될 것이다

TensorRT 내부구조를 보면 Layer는 대부분 지원되므로 본인이 API를 이해하고 시간만 있다면, 원하는 Network를 구성은 할수 있을 것 같다.
하지만, 문제는 Training에서 얻은 결과값 필요하며, 이를 가져올 방법은 TensorRT로 Training하거나, 이미 Training된 곳에서 가져오는 것일 것이다.

3.1  NVIDIA TensorRT Network 구성예제  

처음 아래 사이트의 예제기반으로 작성한 다음 왜 동작이 안되는 지 몰라 문서를 자세히 읽어보니, pytorch에서 Training한 값을 가져와서 적용하는 구조이다.

  https://docs.nvidia.com/deeplearning/sdk/tensorrt-developer-guide/index.html#create_network_python


$ cd ~ 
$ vi test.py
import tensorrt as trt

INPUT_NAME = "input"
INPUT_SHAPE = (3, 224, 224)
OUTPUT_NAME = "Markoutput"
OUTPUT_SIZE = 400

TRT_LOGGER = trt.Logger(trt.Logger.WARNING)

# Create the builder and network
with trt.Builder(TRT_LOGGER) as builder, builder.create_network() as network:
 # Configure the network layers based on the weights provided. In this case, the weights are imported from a pytorch model. 
 # Add an input layer. The name is a string, dtype is a TensorRT dtype, and the shape can be provided as either a list or tuple.
 input_tensor = network.add_input(name=INPUT_NAME, dtype=trt.float32, shape=INPUT_SHAPE)

 # Add a convolution layer
 conv1_w = weights['conv1.weight'].numpy()
 conv1_b = weights['conv1.bias'].numpy()
 conv1 = network.add_convolution(input=input_tensor, num_output_maps=20, kernel_shape=(5, 5), kernel=conv1_w, bias=conv1_b)
 conv1.stride = (1, 1)

 pool1 = network.add_pooling(input=conv1.get_output(0), type=trt.PoolingType.MAX, window_size=(2, 2))
 pool1.stride = (2, 2)
 conv2_w = weights['conv2.weight'].numpy()
 conv2_b = weights['conv2.bias'].numpy()
 conv2 = network.add_convolution(pool1.get_output(0), 50, (5, 5), conv2_w, conv2_b)
 conv2.stride = (1, 1)

 pool2 = network.add_pooling(conv2.get_output(0), trt.PoolingType.MAX, (2, 2))
 pool2.stride = (2, 2)

 fc1_w = weights['fc1.weight'].numpy()
 fc1_b = weights['fc1.bias'].numpy()
 fc1 = network.add_fully_connected(input=pool2.get_output(0), num_outputs=500, kernel=fc1_w, bias=fc1_b)

 relu1 = network.add_activation(fc1.get_output(0), trt.ActivationType.RELU)

 fc2_w = weights['fc2.weight'].numpy()
 fc2_b = weights['fc2.bias'].numpy()
 fc2 = network.add_fully_connected(relu1.get_output(0), OUTPUT_SIZE, fc2_w, fc2_b)

 fc2.get_output(0).name =OUTPUT_NAME
 network.mark_output(fc2.get_output(0))

$ python test.py
...
    conv1_w = weights['conv1.weight'].numpy()
NameError: name 'weights' is not defined



  • 상위를 이해하기 위해서 TensorRT의 Python 관련된 API 정리 

TensorRT Python Manual-Network (상위소스 network 연결된 method들 )
  https://docs.nvidia.com/deeplearning/sdk/tensorrt-api/python_api/infer/Graph/Network.html

TensorRT Python Manual-Type (상위소스 INPUT_SHAPE ) 
  https://docs.nvidia.com/deeplearning/sdk/tensorrt-api/python_api/infer/FoundationalTypes/Dims.html

TensorRT Python Manual-Type (상위소스 weights)
  https://docs.nvidia.com/deeplearning/sdk/tensorrt-api/python_api/infer/FoundationalTypes/Weights.html?highlight=weights

TensorRT Python Manual-Layer  ( 상위소스 fc1/fc2 )
  https://docs.nvidia.com/deeplearning/sdk/tensorrt-api/python_api/infer/Graph/LayerBase.html
  https://docs.nvidia.com/deeplearning/sdk/tensorrt-api/python_api/infer/Graph/Layers.html?highlight=ifullyconnectedlayer#ifullyconnectedlayer


3.2  TensorRT pytorch MINIST 실행 

TensorRT pytorch Sample
  https://docs.nvidia.com/deeplearning/sdk/tensorrt-sample-support-guide/index.html#network_api_pytorch_mnist


$ cd /usr/src/tensorrt/samples/python/network_api_pytorch_mnist
$ cat requirements.txt  // pytorch가 x86만 지원 
numpy
https://download.pytorch.org/whl/cpu/torch-1.0.0-cp37-cp37m-linux_x86_64.whl  ; python_version=="3.7"
https://download.pytorch.org/whl/cpu/torch-1.0.0-cp36-cp36m-linux_x86_64.whl  ; python_version=="3.6"
https://download.pytorch.org/whl/cpu/torch-1.0.0-cp35-cp35m-linux_x86_64.whl  ; python_version=="3.5"
https://download.pytorch.org/whl/cpu/torch-1.0.0-cp27-cp27mu-linux_x86_64.whl ; python_version=="2.7"
torchvision==0.2.1
Pillow
pycuda

//아래사이트에서 download ,pip를 업그레이드해도 version이 맞지 않아 설치가 안됨 
$ sudo -H pip install torch-1.0.0a0+8601b33-cp27-cp27mu-linux_aarch64.whl

추후에 다시 설치를 진행하도록하며, 직접소스 비교로 진행하기로함

Pytorch ARM Version Install 방법
  https://devtalk.nvidia.com/default/topic/1041716/pytorch-install-problem/
  https://developer.ridgerun.com/wiki/index.php?title=Xavier/Deep_Learning/Deep_Learning_Tutorials/Jetson_Reinforcement

3.3  pytorch model/ TensorRT sample 소스 분석

Pytorch와 TensorRT 소스를 분석해보면 동작하는 방식은  어느정도는 이해가 쉽게간다.
다만 내가 DeepLearning 관련지식과 Pytorch 지식이 거의 전무하기 때문에 Python으로 손쉽게  이해하려고 한다.
역시 내가 생각하기에도 python 코드는 가독성이 좋아 이해하기 쉽고 사용하기도 편한것 같다.

소스의 구성은 MNIST Model(Pytorch)MINIST Sample(TensorRT)로 구성되어있으며, 각각의 두개의 소스를 비교분석해보고 동작원리를 알아보자

  • MNIST Model Source (Pytorch)
$ cat model.py
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
from torchvision import datasets, transforms
from torch.autograd import Variable

import numpy as np
import os

from random import randint


## pytorch로 Network 구성하며 아래의 pytorch 예제를 참고
https://pytorch.org/tutorials/beginner/former_torchies/nnft_tutorial.html

# Network
class Net(nn.Module):

## 각 Layer 이름과 Layer부분을 정의 
    def __init__(self):
        super(Net, self).__init__()
        self.conv1 = nn.Conv2d(1, 20, kernel_size=5)
        self.conv2 = nn.Conv2d(20, 50, kernel_size=5)
        self.conv2_drop = nn.Dropout2d()
        self.fc1 = nn.Linear(800, 500)
        self.fc2 = nn.Linear(500, 10)

## Pytorch를 이용하여 구현된 Network의 구조 
    def forward(self, x):
        x = F.max_pool2d(self.conv1(x), kernel_size=2, stride=2)
        x = F.max_pool2d(self.conv2(x), kernel_size=2, stride=2)
        x = x.view(-1, 800)
        x = F.relu(self.fc1(x))
        x = self.fc2(x)
        return F.log_softmax(x, dim=1)


class MnistModel(object):
    ## 초기값을 설정하고, Traing/Test를 위해 DATASET을 Load 한 후 상위 Network 구성 
    def __init__(self):
        self.batch_size = 64
        self.test_batch_size = 100
        self.learning_rate = 0.01
        self.sgd_momentum = 0.9
        self.log_interval = 100
        # Fetch MNIST data set.
        self.train_loader = torch.utils.data.DataLoader(
            datasets.MNIST('/tmp/mnist/data', train=True, download=True, transform=transforms.Compose([
                transforms.ToTensor(),
                transforms.Normalize((0.1307,), (0.3081,))
                ])),
            batch_size=self.batch_size,
            shuffle=True)
        self.test_loader = torch.utils.data.DataLoader(
            datasets.MNIST('/tmp/mnist/data', train=False, transform=transforms.Compose([
                transforms.ToTensor(),
                transforms.Normalize((0.1307,), (0.3081,))
                ])),
            batch_size=self.test_batch_size,
            shuffle=True)
        self.network = Net()

    ## 총 전체 5번을 상위에서 Load한 DATASET기반으로 Training을 진행과 TEST를 진행 
    # Train the network for several epochs, validating after each epoch.
    def learn(self, num_epochs=5):
        # Train the network for a single epoch
        def train(epoch):
            self.network.train()
            optimizer = optim.SGD(self.network.parameters(), lr=self.learning_rate, momentum=self.sgd_momentum)
            for batch, (data, target) in enumerate(self.train_loader):
                data, target = Variable(data), Variable(target)
                optimizer.zero_grad()
                output = self.network(data)
                loss = F.nll_loss(output, target)
                loss.backward()
                optimizer.step()
                if batch % self.log_interval == 0:
                    print('Train Epoch: {} [{}/{} ({:.0f}%)]\tLoss: {:.6f}'.format(epoch, batch * len(data), len(self.train_loader.dataset), 100. * batch / len(self.train_loader), loss.data.item()))

        # Test the network
        def test(epoch):
            self.network.eval()
            test_loss = 0
            correct = 0
            for data, target in self.test_loader:
                with torch.no_grad():
                    data, target = Variable(data), Variable(target)
                output = self.network(data)
                test_loss += F.nll_loss(output, target).data.item()
                pred = output.data.max(1)[1]
                correct += pred.eq(target.data).cpu().sum()
            test_loss /= len(self.test_loader)
            print('\nTest set: Average loss: {:.4f}, Accuracy: {}/{} ({:.0f}%)\n'.format(test_loss, correct, len(self.test_loader.dataset), 100. * correct / len(self.test_loader.dataset)))


        ## 이곳에서 5번을 실행 
        for e in range(num_epochs):
            train(e + 1)
            test(e + 1)

    ## pytorch에서 아래의 state_dict이 Training 값이라고 한다 
    ## https://pytorch.org/tutorials/beginner/saving_loading_models.html#what-is-a-state-dict
    def get_weights(self):
        return self.network.state_dict()

    ## Random으로 Test Case를 선정하는 것 같다 
    def get_random_testcase(self):
        data, target = next(iter(self.test_loader))
        case_num = randint(0, len(data) - 1)
        test_case = data.numpy()[case_num].ravel().astype(np.float32)
        test_name = target.numpy()[case_num]
        return test_case, test_name

  • Pytorch Model 분석 
class Net  과 class MnistModel 구성되며, 상위소스 class Net의 경우는 아래의소스 populate_network 함수와 반드시 비교분석을 해야
Pytorch 와 TensorRT의 API의 비교하여 차이를 알수 있다.

양쪽소스에서는 각각의 동일한 Network를 구성하고있고, TensorRT의 경우 Pytorch에서 얻은 Training에서 얻은값 기반으로 추론(Inference)하고 있다.

Pythorch에 대해 잘알지 못하고 Training한 경험이 없기때문에, 상위처럼 이해만 하고 넘어가기로하며, 세부 Training 동작방식은 Pytorch 혹은 Tensorflow를 공부하고 다시 봐야할 것 같다


  • MNIST Sample Source (TensorRT)
상위 model을 import하고 동작하고 있으며,이 소스에서도 이전의 Model Source와 마찬가지로 Network를 구성을하고 있지만, TensoRT Python API를 사용하고 있다.


$ cat sample.py 
import model
from PIL import Image
import numpy as np

import pycuda.driver as cuda
import pycuda.autoinit

import tensorrt as trt

import sys, os
sys.path.insert(1, os.path.join(sys.path[0], ".."))

## 상위 common.py 이 부분은 생략 
import common

# You can set the logger severity higher to suppress messages (or lower to display more messages).
TRT_LOGGER = trt.Logger(trt.Logger.WARNING)

class ModelData(object):
    INPUT_NAME = "data"
    INPUT_SHAPE = (1, 28, 28)
    OUTPUT_NAME = "prob"
    OUTPUT_SIZE = 10
    DTYPE = trt.float32

## TensorRT를 이용한 Network 구조 (상위 Pytorch Class Net의 forward 비교, 둘다 동일한 Network)

def populate_network(network, weights):
    # Configure the network layers based on the weights provided.
    input_tensor = network.add_input(name=ModelData.INPUT_NAME, dtype=ModelData.DTYPE, shape=ModelData.INPUT_SHAPE)

## pytorch network 와 tensorRT Network 비교, 아래주석이 pytorch network 이며, 이를 비교 

    ## self.conv1 = nn.Conv2d(1, 20, kernel_size=5)     
    ##  x = F.max_pool2d(self.conv1(x), kernel_size=2, stride=2)  
    conv1_w = weights['conv1.weight'].numpy()
    conv1_b = weights['conv1.bias'].numpy()
    conv1 = network.add_convolution(input=input_tensor, num_output_maps=20, kernel_shape=(5, 5), kernel=conv1_w, bias=conv1_b)
    conv1.stride = (1, 1)
    
    ## 상위 pool의 kernel_size 를 window_size 로 변경 
    pool1 = network.add_pooling(input=conv1.get_output(0), type=trt.PoolingType.MAX, window_size=(2, 2))
    pool1.stride = (2, 2)

    ##  self.conv2 = nn.Conv2d(20, 50, kernel_size=5)
    ## x = F.max_pool2d(self.conv2(x), kernel_size=2, stride=2)
    conv2_w = weights['conv2.weight'].numpy()
    conv2_b = weights['conv2.bias'].numpy()
    conv2 = network.add_convolution(pool1.get_output(0), 50, (5, 5), conv2_w, conv2_b)
    conv2.stride = (1, 1)

    pool2 = network.add_pooling(conv2.get_output(0), trt.PoolingType.MAX, (2, 2))
    pool2.stride = (2, 2)
   
    ## x = x.view(-1, 800)
    ## x = F.relu(self.fc1(x))
    fc1_w = weights['fc1.weight'].numpy()
    fc1_b = weights['fc1.bias'].numpy()
    fc1 = network.add_fully_connected(input=pool2.get_output(0), num_outputs=500, kernel=fc1_w, bias=fc1_b)

    relu1 = network.add_activation(input=fc1.get_output(0), type=trt.ActivationType.RELU)
   
    ## x = self.fc2(x)
    ## F.log_softmax(x, dim=1)
    fc2_w = weights['fc2.weight'].numpy()
    fc2_b = weights['fc2.bias'].numpy()
    fc2 = network.add_fully_connected(relu1.get_output(0), ModelData.OUTPUT_SIZE, fc2_w, fc2_b)

    fc2.get_output(0).name = ModelData.OUTPUT_NAME
    network.mark_output(tensor=fc2.get_output(0))

def build_engine(weights):
    # For more information on TRT basics, refer to the introductory samples.
    with trt.Builder(TRT_LOGGER) as builder, builder.create_network() as network:
        builder.max_workspace_size = common.GiB(1)
        # Populate the network using weights from the PyTorch model.
        populate_network(network, weights)
        # Build and return an engine.
        return builder.build_cuda_engine(network)

# Loads a random test case from pytorch's DataLoader
def load_random_test_case(model, pagelocked_buffer):
    # Select an image at random to be the test case.
    img, expected_output = model.get_random_testcase()
    # Copy to the pagelocked input buffer
    np.copyto(pagelocked_buffer, img)
    return expected_output

def main():
    data_path, _ = common.find_sample_data(description="Runs an MNIST network using a PyTorch model", subfolder="mnist")
    # Train the PyTorch model
    mnist_model = model.MnistModel()
    mnist_model.learn()
    weights = mnist_model.get_weights()
    # Do inference with TensorRT.
    with build_engine(weights) as engine:
        # Build an engine, allocate buffers and create a stream.
        # For more information on buffer allocation, refer to the introductory samples.
        inputs, outputs, bindings, stream = common.allocate_buffers(engine)
        with engine.create_execution_context() as context:
            case_num = load_random_test_case(mnist_model, pagelocked_buffer=inputs[0].host)
            # For more information on performing inference, refer to the introductory samples.
            # The common.do_inference function will return a list of outputs - we only have one in this case.
            [output] = common.do_inference(context, bindings=bindings, inputs=inputs, outputs=outputs, stream=stream)
            pred = np.argmax(output)
            print("Test Case: " + str(case_num))
            print("Prediction: " + str(pred))

if __name__ == '__main__':
    main()


  • MINIST Sample  간단분석 
  1. data_path 정의 /usr/src/tensorrt/data/mnist 로 설정 
  2. model.MnistModel() : minist model 생성 
  3. mnist_model.learn() : minist training 
  4. weights = mnist_model.get_weights()  :  pytorch로 부터 weights 값 얻음 
  5. build_engine(weights) :  weight 기반 TensorRT Engine Build
  6. populate_network(network, weights) : weight기반으로 TensorRT network 구성
  7. builder.build_cuda_engine(network) : TensorRT Engine Build 완료 
  8. common.allocate_buffers(engine) : Memory 할당 (commom.py 참조)
  9. with engine.create_execution_context() as context : TensorRT 엔진실행준비
  10. load_random_test_case :  함수호출하여 CPU buffer 할당 및 test_cast 선정 (model.py 참조)
  11. common.do_inference : CPU에 할당된 Buffer를 GPU에 추론적용하고 CPU다시 가져옴
  12. case_num :  임의로 선정된 값 ,   pred : 추론을 통해 예측값  

TensorRT의 이해는 NVIDIA 영문 Manual로 어느 정도 이해는 하겠지만, 근본적으로 Deep Learning 지식과 각각의 Network 구조를 이해하려면
어쩔수 없이 별도로 이 분야에 대해서 세부적으로 공부 해야할 것 같다.

8/09/2019

NVIDIA GPU 비교 및 Ubuntu Laptop 선정

1. NVIDIA-GPU  와 Laptop 관련정보

최근 Deep Learning 관련된 일을 하고 문서를 보니, NVIDIA-GPU에 관심이 많이 가서 회사일과 별도로 알아보고자 한다.
최근부터 리눅스 랩탑으로 사용을 해도 크게 무리가 없는거 같아,  이 기준으로 사려고 하는데, 관련자료도 별도로 알아봐야겠다.

  • NVIDIA-GPU Version 정보
NVIDIA의 Jetson을 하면서 GPU Version을 Volta 와 Pascal 이였는데, 각각의 아키텍처를 쉽게 설명을 해주고 있어서  좋았다.
  1. Jetson Nano: NVIDIA Maxwell 아키텍처
  2. Jetson TX2:  NVIDIA Pascal 아키텍처
  3. Jetson AGX Xavier : Tensor Core 지원 Volta 아키텍처 (DLA지원)
주의해야 할 것은 Jetson (Tegra version) 과 일반 PC의 GPU는 기본 아키텍처는 같을지 모르지만, 구조가 다르므로 유의해야한다. 

x86기반의 GPU 정보 

ARM기반의 Parallel Interface
  https://ahyuo79.blogspot.com/2016/08/arm-performance-libraries.html

우선 Laptop을 NVIDA GPU 우선으로 선정을 할 것 이므로, 이 NVIDIA-GPU의 객관적인 지료를 찾아보도록하자 

1.1 NVIDIA-GPU의 객관적인 지표 

  • VIDEOCARD BenchMark 
가장쉽게 성능을 알수 있는 지표로 이것을 반드시 참조해야 할 것 같다.
처음 숫자가 높아지면, 성능도 역시 당연히 높을 줄 알았는데, 지표를 보니 다르다.
가격이 안나오는 것은 제외하자

  https://www.videocardbenchmark.net/high_end_gpus.html


  • UserBenchmark
좀 더 다양한 Benchmark를 제공하고 좌측에 Brand에 NVIDIA를 선택하고 시작하자.
  1. Average Benchmark :  이 순위로 성능의 좌표가 될 것 같다. 
  2. Market Share : 시장 점유율이며, 이부분은 아래 가격하고 같이 봐야 가성비를 확인
  3. Price : 가격비로 정렬
  4. User Rating : 유저가 직접 순위를 매긴것 같은데, 별로 신용을 못하겠다. 
  https://gpu.userbenchmark.com/

  • UserBenchmark 직접비교 기능( 별로인것 같음)
GTX-1660 TI 와 GTX-1080을 간단히 비교 (GTX-1660이 저렴한 모델이라는 것에 좀 충격)

  https://gpu.userbenchmark.com/Compare/Nvidia-GTX-1660-Ti-vs-Nvidia-GTX-1080-Ti/4037vs3918


1.2 Deep Learning Training 위한 Benchmark

Deep Learning Training에 가장좋은 것은 클라우드에서 진행하거나, 전용 Server를 사서하면 좋겠지만, 돈이 없으므로 포기하자.

Training은 못해도  Colab에서 할 수 있는 방법도 모색을 해봐야 하며,  시간제약을 극복할 수 있는 방법을 찾아보자.

랩탑가지고 Deep Learning Training (Transfer Learning) 하려고 하는게 무리는 있을 것 같은데, 기왕이면 다홍치마라고, 좋은 것을 찾자.

  • DeepLearning GPU Benchmark 
각각의 성능을 한번 비교해보고, 각 모델에 따라 얼마나 다른 지 확인해보자

  https://timdettmers.com/2019/04/03/which-gpu-for-deep-learning/
  https://towardsdatascience.com/rtx-2060-vs-gtx-1080ti-in-deep-learning-gpu-benchmarks-cheapest-rtx-vs-most-expensive-gtx-card-cd47cd9931d2
  https://lambdalabs.com/blog/2080-ti-deep-learning-benchmarks/


  • NVIDIA Tesla Deep Learning BenchMark
주로 보면, Tesla로 병렬로 연결된 DGX 시리즈 성능 및 각 모델별 성능
DGX를 보면 왠지 Data Center용으로 나온 것일 것 같은데, 아직 정확하게 모르겠음 

  https://developer.nvidia.com/deep-learning-performance-training-inference
  https://www.tensorflow.org/guide/performance/benchmarks


1.3 Laptop에 적용될 GPU 

Laptop에서 지원되는 GPU를 같이 선택하고,  가격은 최대한 낮은 것으로 하고 리눅스가 설치가되는 것으로 찾자

  • 랩탑용 GPU 카드 최종후보  
  1. Geforce RTX-2060  :   $334.99
  2. Geforce GTX-1080  :   $349.99 
  3. Geforce GTX-1660 ti : $269.99 (고려중)

주 관심으로 볼 것이 아래로 한정해서 보기로 했지만 정보부족
  1. Transistor count
  2. Floating-point performance 
  3. Cuda Cores 
  4. Tensor Cores : 정보가 없음 

그림출처
아래사이트 때문에 GTX-1660 ti 까지 고려하게되었으며, 좋은 자료를 제공해주고 있다.
  https://www.brainbox.co.kr/bbs/board.php?bo_table=review&wr_id=7590
  https://technical.city/en/video/GeForce-GTX-1080-vs-GeForce-RTX-2060
  https://www.gpucheck.com/ko-krw/compare/nvidia-geforce-gtx-1660-ti-vs-nvidia-geforce-rtx-2060/intel-core-i7-8700k-3-70ghz-vs-intel-core-i7-8700k-3-70ghz/


1.4 Laptop 비교 검색  및 확정 


  • 랩탑비교 검색 
ASUS는 인텔을 사용안하고, 한성은 부피가 꽤나가서 다 제외하니 남는것이 레노바 뿐 인데, 거의 150만원까지는 가는데, 고민이 많이 된다.
Lenova는 싫은데, 다시한번 생각을 해보지만, 어쩔 수 없이 Lenova 인 것 같다. 

  http://www.enuri.com/list.jsp?cate=0404
  http://www.enuri.com/list.jsp?cate=0404&tabType=1&page=1&order=1
  http://www.enuri.com/detail.jsp?modelno=37324166&cate=0404&IsDeliverySum=N

  • 현재 이 모델로 확정으로 생각 
Lenovo Legion Y540 (RTX-2060)
  1. Intel Core i7-9750H
  2. Intel UHD Graphics 630
  3. NVIDIA GeForce RTX 2060 Mobile 
  4. 16 GB RAM

  https://www.notebookcheck.net/Lenovo-Legion-Y540-with-RTX-2060-laptop-review-Gaming-laptop-with-good-sound-and-144-Hz-panel.428659.0.html


상위모델로 구매결정완료 및 각각의 지속적으로 검색시작


2. Lenovo Laptop 의 Ubunut 설치 가능 


  • Ubuntu Laptop 관련부분 검색 
Laptop 관련 검색도중 나와 같이 Linux로 Laptop을 사용하는 사람들이 꽤 있는 것 같은데, 관련내용을 보다가, 
Ubuntu 가 설치 자체가 안되는 노트북이 있다는 것을 알게되었으며, 조심해야겠다, 
또한 Ubuntu가 Laptop의 전체 BIOS부분을 지원해주지 못하는 것을 알게되었는데, 이부분이 주의할 부분이다. 


Lenovo Y530  관련내용 
이전 모델 관련내용, Ubuntu 설치가 쉽지 않다고 함

Lenovo가 Linux 를 거부한 이유 
Lenovo의 Laptop들은 성능개선을 위해서 RAID를 도입을 해서 Linux 설치가 안된다는 이야기이다.
반드시 ACHI MODE 모드 지원부분 확인 
  http://www.itworld.co.kr/news/101241


2.1  Laptop 의 BIOS 지원문제 


  • BIOS의 ACHI MODE 지원여부 확인필수 
BIOS에서 RAID(인텔 RST) 와 ACHI MODE  두 가지 모드로 동작을 한다고 한다.  
Linux의 경우 ACHI MODE가 지원이 되면 기본 Ubuntu  설치는 되는 것 같지만, Lenovo에서는 쉽지는 않는 것으로 보인다. 
왜냐하면, 다른 BIOS의 호환부분 과 주로 Driver 문제인 것으로 보인다. 

하지만 Laptop 의 가격때문에 포기할 수 없으므로, 관련부분은 해결해 나가면서 설치해보기로 결정. 

  • BIOS의 AHCI (Advanced Host Controller Interface)
SATA Interface 기반으로 기본작동되는 Host 모드로 인텔이 개발이 했기 때문에 거의 표준으로 사용을 한다고 한다. 
 

  • BIOS의 RAID(Redundant Array of Independent Disks)
내가 아는 RAID인 가 했는데, 역시 맞다는 것 같다, Server 혹은 Workstation에서 사용하는 RAID로 병렬로 동시에 연결해서 사용하는 방식이다. 
오래전에 PATA를 사용했을 때도  RAID로 사용했던 것로 기억하는데, SATA로도 RAID를 구성하는 것 같다. 
그런데, 글을 읽다보면, RAID 에서 발전된 iRST (Intel Rapid Storage Technology) 이 기능이 문제인 것으로 보인다. 
상위 기능을 보면, RAID 기능(분산저장)들과 더불어 SSD 를 Cache로 사용으로 Lenovo 역시 이것을 사용하는 것으로 보인다. 



  • How to install Ubuntu 18.04 on Lenovo P1
Lenovo P1에서 설치 한 Ubuntu 18.04 이며,  Laptop을 사기로 결정 
  https://blogg.bekk.no/how-to-install-ubuntu-18-04-on-lenovo-p1-617a4bdea389


  • 11 Places Where You Can Buy Linux Computers
Linux Laptop의 추천 Maker인데  역시 Dell 인 No.1 이지만, Lenovo도 순위가 4위이지만, Think pad이다 
  https://itsfoss.com/get-linux-laptops/


  • Ubuntu on Lenovo Models
Ubunut가  설치되는 Lenovo Model 이지만, 나의 Laptop 이름은 존재하지 않는다. 
아직 최신 노트북이라서 그런지 아직 미지원이며, 일단 AHCI만 지원한다면 구입 
  https://certification.ubuntu.com/certification/make/Lenovo/

8/02/2019

Deepstream SDK 4.0 변화 및 PlugIn 구조 및 생성방법 ( Gstreamer 변화 )

1. DeepStream SDK 4.0 변화 (Gstreamer 변화) 

기존 DeepStream SDK 3.0과 호환되지 않는 부분이 많으며, 우선 빨리 파악하기 위해서 PlugIn Manual 과 소스를 분석하여 어떻게 변경되었는지 알아야겠다.

기존의 DeepStream SDK 3.0에서 동작되었던 , Gstreamer 명령어들이 동작되지 않는 것들이 많다.

  • DeepStream 관련전체문서 (필독)
  https://docs.nvidia.com/metropolis/index.html


상위 전체문서 중에 많이 보게될 문서는 아래 3 문서가 될 것 같다.

  • DeepStream Release Note 
이전버전과 변경사항 및 x86과 Jetson의 차이와 신기능들을 확인하자
  https://docs.nvidia.com/metropolis/deepstream/4.0/DeepStream_4.0_Release_Notes.pdf


  • DeepStream Quick Guide 기본사용법
설치는 sdkmanger로 쉽게 하면될 것이고, 개발 및 관련 설명을 쉽게 정리해서 보기 편하다
  https://docs.nvidia.com/metropolis/deepstream/4.0/dev-guide/index.html


  • DeepStream 개발시 PlugIn Manual 과 DeepStream API
DeepStream 관련부분을 개발할 경우, PlugIn의 정보와 기능을 비롯하여 내부에서 사용하는 API들을 알아야하는데 관련문서들이므로, 필수로 보자
  https://docs.nvidia.com/metropolis/deepstream/4.0/DeepStream_Plugin_Manual.pdf
  https://docs.nvidia.com/metropolis/deepstream/4.0/dev-guide/DeepStream_Development_Guide/baggage/index.html


1.1 Jetson AGX Xavier 의 INT8 특징 

다른 Jetson과 다른게 DLA라는 것이 존재하며, 이는 INT8 Inference기능을 제공을 하고 있다.
이외에도 OpenVX 기능도 존재하지만, 이부분이 OpenCV에도 적용이 되는지는 좀 더 알아봐야할 것 같다.
DeepStream 4.0부터 지원되는 기능은 아니며, 기존부터 존재했다고 하지만, Xavier를 처음 사용하기에 이 를 간단히 정리하며, Jetson Nano , TX2는 이 부분에서 제외

  • INT8 Inferece 관련문서 (Jetson AGX Xavier 지원)
  http://on-demand.gputechconf.com/gtc/2017/presentation/s7310-8-bit-inference-with-tensorrt.pdf

INT8기능이 지원이 되려면 아래와 같이 GPU의 SM 61 Version 이상이어야 하며, 아래와 같은 연산이 지원되어야 가능하다

  • SM61 관련내용 
  https://devtalk.nvidia.com/default/topic/1026069/jetson-tx2/how-to-use-int8-inference-with-jetsontx2-tensorrt-2-1-2/


INT8 Inferece의 목적은 FP32/FP16에 비해 정확성 손실이 크게 없이 빠른계산을 위해 INT8로 변경하여 속도향상이 목적이다

  • Bias의 필요성 ( 절대값이 아닌 상대값으로 보면, 필요가 없어질것으로 추측 )
하지만 아래와 같이 2개의 곱에서 전체의 Bias가 필요가 없어지는데, 이 부분이 좀 혼동이 된다
이 부분은 좀 알아봐야 할 것 같다.


아래의 FP32 Bias가 불필요해서 제거한다고 함



  • 양자화(Quantization, 비율로 INT8에 맞게 양자화 진행 ) 



아래와 같이 양자화할 경우 Threshold를 설정하여 Saturation 을 조절


  • INT8 Inference의 정확성 비교 


상위 INT8의 문서를 간단히 정리하면, Weight는 그대로 두고, Bias를  제거후 일종의 Hash Table 같은 것을 만들어서
FP32를  INT8로 Table를 통해 Mapping하는 방식으로 구현한다 (양자화)
Bias의 불필요성은 값을 절대값이 아닌 상대값으로 보기 때문에 필요가 없어지는 것 같으며, 상위문서를 잘 봐도 크게 데이타 손실은 없을 것 같다.(추측)

재미있는 부분은 양자화할때의 정확성부분이며, 이때 Threshold를 설정하여 Saturation 을 조절도 가능하다는 점이다.
그리고,  불필요하다면, Threshold를 설정하여, 잘라 내어 제거한다
이 부분의 필요성이 언제 필요한지는 추후에 알아봐야할 것 같다


양자화(Quantization) 할때 Mapping시 Hash Table 사용했는지는 모르지만, 예전에 내가 비슷한 것을 구현했던 경험이 있어,
Hash Table을 이용했기때문에, 나라면 Hash Table을 이용했을 것 같다.

INT8의 Inference의 기능도 꽤 재미있는 기능이며, 이 부분에 관심이 많아졌다.
다만 상위문서를 설명을 듣고 싶은데 문서로만 봐서 안타까울 뿐이다.

  • Config File의 IN8 Inference 확인사항 
  1. model-engine-file : TensorRT model-engine (serialized 된 상태의 INT8)
  2. int8-calib-file : CalibrationTable File 이며 각 TensorRT의 Version 정보표시
  3. network-mode :  0=FP32, 1=INT8, 2=FP16 mode , 처음  model-engine이 없을 경우 이 기준으로 생성 

  • Config File의 Example for Jetson AGX Xavier 
  1. model-engine-file=model_b1_int8.engine
  2. int8-calib-file=yolov3-calibration.table.trt5.1
  3. int8-calib-file=../../models/Primary_Detector/cal_trt4.bin


1.2 DeepStream SDK 3.0 과 4.0 비교 

3.0에서 4.0으로 변경되면서 많은 기능이 추가되었지만 호환되지 않는 부분이 많이 생겨, 관련부분을 정리가 필요할 것 같다.
기존 DS3.0에서 이것저것 만들어보고 Porting해보고 했는데, DS4.0에서 많이 지원되는 것 같은데, 관련부분도 다 테스트를 해야한다.

NVIDIA의 문서를 보면 가장 큰 변화사항은 Jetson 과 dGPU Platform 기반의 단일화된 변화라고 하는데, 간단히 정리하면, 최적화를 통한 성능향상이 될 것 같다.
세부사항은 역시 PlugIn Manual로 다 봐야 알겠다.

더불어 이제 x86만 지원가능했던 NGC Docker도 ARM에서도 지원을 해주기 때문에 설치환경이 편하게 될 것 같다.

  • Gst-nvinfer 변화정리  
  1. UFF/ONNX/Caffe 이외의 Custom Model 위한 New Interface제공 (TensorRT IPlugin)
  2. Segmentation/Gray model 지원 
  3. FP16 / INT8 DLA (Jetson Xavier) 지원 (기존 INT8만 지원)
  4. Source Code 제공 (이 부분은 나중에 분석)

  • New PlugIns
  1. Gst-V4L2 기반의 H265+H264 encode 와 decode 지원 ( 기존과 변경됨)
  2. JPEG+MJPEG decoder 지원
  3. gst-nvvideoconver (기존 gst-nvconv 확장)
  4. gst-nvof  ( Optical flow )
  5. nvofvisual / nvsegvisul 지원을 해준다고 하는데, 설정으로 테스트 진행을 해봐야겠다.
  6. dewarper 도 제공해주며, gst-msgbroker도 많이 확장되었다. 
  7. 이외 기존 Plugin들의 이름이 호환되지 않는다. 

자세한 내용 아래의 Release Note를 참고해서 보자
  https://docs.nvidia.com/metropolis/deepstream/4.0/DeepStream_4.0_Release_Notes.pdf


1.3  DeepStream 4.0의 PlugIn 관련사항 


DS4.0 PlugIn Manual
  https://docs.nvidia.com/metropolis/deepstream/4.0/DeepStream_Plugin_Manual.pdf


기존처럼 gst-inspect를 이용하여 PlugIn 기능확인을 할 수 없기 때문에 오직 상위 Manual로 세부사항을 알아야겠다.

  • gst-inspect 명령어로 Element 기능확인 
Terminal에서 확인이 잘되지만, SSH로 연결시 문제가 발생하는 부분이 gst-inspect 부분이다.

$ ssh  nvidia@192.168.55.1  
$ echo $DISPLAY  // 설정이 없음 

$ gst-inspect-1.0 -a   // 모든 PlugIn 확인가능    
.......
$ gst-inspect-1.0 -a |  grep dsexample     
.....
$ gst-inspect-1.0 dsexample    
Factory Details:
  Rank                     primary (256)
  Long-name                DsExample plugin
  Klass                    DsExample Plugin
......
GObject
 +----GInitiallyUnowned
       +----GstObject
             +----GstElement
                   +----GstBaseTransform
                         +----GstDsExample


$ ssh -X  nvidia@192.168.55.1         // X Protocol 지원 

$ echo $DISPLAY   // 상위와 다르게 설정되었으며, 이로 인해 오작동됨  
localhost:10.0

$ export DISPLAY=:1   // 1 or 0 설정  반드시 =:를 사용  

$ gst-inspect-1.0 dsexample    
Factory Details:
  Rank                     primary (256)
  Long-name                DsExample plugin
  Klass                    DsExample Plugin
......
GObject
 +----GInitiallyUnowned
       +----GstObject
             +----GstElement
                   +----GstBaseTransform
                         +----GstDsExample

Gstreamer gst-inspect
  https://gstreamer.freedesktop.org/documentation/tools/gst-inspect.html?gi-language=c#


  • SSH에서 gst-inspect 사용시 주의 사항 
만약  SSH X Protocl 과 같이 접속시 상위의 DISPLAY를 미설정시에는 상위 Command 미동작

미동작원인을 몰랐는데, NVIDIA에서 정확히 알려줘서 해결
  https://devtalk.nvidia.com/default/topic/1058525/deepstream-sdk/gst-inspect-is-not-work-properly-in-ds4-0/post/5368380/#5368380

Ubuntu DISPLAY 관련설정
  https://help.ubuntu.com/community/EnvironmentVariables

  • 만약 문제생길 경우 아래와 같이 Cache 삭제 
$ rm ~/.cache/gstreamer-1.0/registry.aarch64.bin   //문제가 생기면, 아래와 같이 Gstreamer Cache를 지우고 다시 해보자. 
......



1.4 DeepStream PlugIn 구조 및 위치확인 

이전 DS SDK 3.0과 동일하며, 아래의 소스에서 Sample PlugIn을 선택해서 이름을 변경해서 Sample을 만들고 테스트를 진행하자.



  • DeepStream Gst-PlugIn 예제 구성 
아래와 같이 Gstreamer 의 PlugIN 구조를 파악을 하고 예제로 주어진 dsexample을 이름을 변경하여 만들어서 간단히 테스트를 진행하면된다.

$ cd ~/deepstream-4.0/sources/gst-plugins
$ tree .
.
├── gst-dsexample   // 이것 기준으로 동일하게 이름을 변경해서 테스트 진행 
│   ├── dsexample_lib
│   │   ├── dsexample_lib.c
│   │   ├── dsexample_lib.h
│   │   ├── dsexample_lib.o
│   │   ├── libdsexample.a
│   │   └── Makefile
│   ├── gstdsexample.cpp
│   ├── gstdsexample.h
│   ├── gstdsexample.o
│   ├── libnvdsgst_dsexample.so
│   ├── Makefile
│   └── README
├── gst-nvinfer                 // 새로 추가된 nvinfer 
│   ├── gstnvinfer_allocator.cpp
│   ├── gstnvinfer_allocator.h
│   ├── gstnvinfer_allocator.o
│   ├── gstnvinfer.cpp
│   ├── gstnvinfer.h
│   ├── gstnvinfer_meta_utils.cpp
│   ├── gstnvinfer_meta_utils.h
│   ├── gstnvinfer_meta_utils.o
│   ├── gstnvinfer.o
│   ├── gstnvinfer_property_parser.cpp
│   ├── gstnvinfer_property_parser.h
│   ├── gstnvinfer_property_parser.o
│   ├── libnvdsgst_infer.so
│   ├── Makefile
│   └── README
├── gst-nvmsgbroker
│   ├── gstnvmsgbroker.c
│   ├── gstnvmsgbroker.h
│   ├── gstnvmsgbroker.o
│   ├── libnvdsgst_msgbroker.so
│   ├── Makefile
│   └── README
└─── gst-nvmsgconv
      ├── gstnvmsgconv.c
      ├── gstnvmsgconv.h
      ├── gstnvmsgconv.o
      ├── libnvdsgst_msgconv.so
      ├── Makefile
      └── README


  • Gst PlugIn 및 DeepStream PlugIn 위치파악 
DeepStream Plugin 위치 및 Gstreamer PlugIn 위치를 알아보기 위해 아래와 같이 찾아보았다.

$ ls /opt/nvidia/deepstream/deepstream-4.0/lib/gst-plugins/    //DeepStream PlugIn만 설치위치확인  
libnvdsgst_dewarper.so   libnvdsgst_msgbroker.so    libnvdsgst_multistreamtiler.so  libnvdsgst_osd.so        libnvdsgst_tracker.so
libnvdsgst_dsexample.so  libnvdsgst_msgconv.so      libnvdsgst_of.so          libnvdsgst_infer.so      libnvdsgst_multistream.so  libnvdsgst_ofvisual.so       
libnvdsgst_segvisual.so

$ cat /etc/ld.so.conf.d/deepstream.conf              //DeepStream 동적 Library 연결확인 
/opt/nvidia/deepstream/deepstream-4.0/lib

$ echo $PATH   // PATH는 아시다시피, BIN파일을 어느위치에서 실행가능한 환경변수 
/usr/local/cuda-10.0/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin

$ echo $LD_LIBRARY_PATH   // LD의 동적 LIBRARY_PATH로 상위 ld.so.conf 설정도 참조 
/usr/local/cuda-10.0/lib64:

/*
상위 Gstreamer 환경변수 참조하여 관련설정 전부확인했으나 파악실패 
*/
 
$ echo $GST_PLUGIN_PATH  // 설정없음   ,
$ echo $GST_PLUGIN_PATH_1_0 //설정없음 
$ echo $GST_PLUGIN_SYSTEM_PATH  // 설정없음 
$ echo $GST_PLUGIN_SYSTEM_PATH_1_0  // 설정없음
$ ls ~/.local/share/gstreamer-1.0/presets/    // 아무것도 없음 

/*
 Gstreamer PlugIn을 위치를 직접 찾겠다. 
*/

$ find / -name gstreamer-1.0 2> /dev/null    // 관련 Directory 파악완료 
/usr/share/gstreamer-1.0
/usr/include/gstreamer-1.0
/usr/lib/aarch64-linux-gnu/gstreamer1.0/gstreamer-1.0
/usr/lib/aarch64-linux-gnu/gstreamer-1.0
/home/nvidia/.cache/gstreamer-1.0
/home/nvidia/.local/share/gstreamer-1.0

$ cat /home/nvidia/.cache/gstreamer-1.0/registry.aarch64.bin   // 이 안에 파일을 분석하면, directory 구조 파악가능 

$ ls /usr/lib/aarch64-linux-gnu/gstreamer-1.0  // 다른 Gstreamer PlugIn 부분과 DeepStream Plugin 연결 확인완료 (DeepStream로 심볼링크됨)
deepstream                 libgstcurl.so                libgstisomp4.so            libgstomx.so              libgsttaglib.so
include                    libgstcutter.so              libgstivfparse.so          libgstopenal.so           libgsttcp.so
libcluttergst3.so          libgstdashdemux.so           libgstivtc.so              libgstopenexr.so          libgstteletext.so
libgst1394.so              libgstdc1394.so              libgstjack.so              libgstopenglmixers.so     libgsttheora.so
..............


1.5 DeepStream의 PlugIn 개발

상위와 같이 기본동작구성을 알았으니, 기본으로 Gstreamer PlugIn 관련 개발 Manual을 숙지해두고 알아두자

  • Gstreamer PlugIn 개발 (필독)
  https://gstreamer.freedesktop.org/documentation/plugin-development/basics/boiler.html?gi-language=c

  • Gstreamer PlugIn 개발시 Pad 부분 (필독)
  https://gstreamer.freedesktop.org/documentation/plugin-development/basics/pads.html?gi-language=c

  • Gstreamer 의 Properites 설정 (필독)
  https://gstreamer.freedesktop.org/documentation/plugin-development/basics/args.html?gi-language=c

  • 이외 Callback 함수들 연결 
  1. Chain function: chain 함수를 만들어서 Callback 으로 호출하는데, 내부 Data 처리할때 사용
  2. Event Function:  Pad에게 Callback Function 넣고 State에 따라 pad에게 event 생성가능 
  3. Query Function: Query를 받았을 때 Callback 

  • Gstreamer Write Guide
PlugIn 구조를 세부적으로 알기위해서 아래의 Write Guide를 좀 자세히 보자
  https://gstreamer.freedesktop.org/documentation/plugin-development/index.html?gi-language=c


  • SAMPLE의 Gstreamer 구성의 예 
Gstreamer PlugIn의 함수는SAMPLE이라는 이름으로 생성하고자 한다면 아래와 같이 만들면 된다.
함수이름 역시 gst_sample_xxx으로 구성을 하면된다.

 vi sample.h 
G_BEGIN_DECLS
....
#define GST_TYPE_SAMPLE (gst_sample_get_type())
#define GST_SAMPLE(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_ROI,GstSAMPLE))
#define GST_SAMPLE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_ROI,GstSAMPLEClass))
#define GST_SAMPLE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), GST_TYPE_SAMPLE, GstSAMPLEClass))
#define GST_IS_SAMPLE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_SAMPLE))
#define GST_IS_SAMPLE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_SAMPLE))
#define GST_SAMPLE_CAST(obj)  ((GstSAMPLE *)(obj))

..
G_END_DECLS

  • dsexample로  본인이 원하는 PlugIn 생성 
dsexmple을 복사하여 이름만 변경해서 그 구성을 만들어서 일단 테스트를 진행을 해보면 쉽게 동작되는 것을 확인가능하다.
설치가 되면, gst-inspect 로도 쉽게 관련설명을 확인 할수 있다.
자세한 세부설명은 생략 (Gstreamer Manual 참조)

  • dsexample 과 nvmsgbroker 비교분석  
두개의 PlugIn의 구성 동작방식이 다르며, 이는 아래와 같이 간단히 비교가능하다.
gst_xxxxx_class_init 함수에서 사용되는 구조체와 이와 관련된 함수들을 비교 분석할 필요가 있다.

//msgbroker 
//GstBaseSinkClass  , Sink Pad의 중점으로 동작되도록 구성

  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
  GstBaseSinkClass *base_sink_class = GST_BASE_SINK_CLASS (klass);
.....
  base_sink_class->set_caps = GST_DEBUG_FUNCPTR (gst_nvmsgbroker_set_caps);
  base_sink_class->start = GST_DEBUG_FUNCPTR (gst_nvmsgbroker_start);
  base_sink_class->stop = GST_DEBUG_FUNCPTR (gst_nvmsgbroker_stop);
  base_sink_class->render = GST_DEBUG_FUNCPTR (gst_nvmsgbroker_render);

// dsexmple 
//GstBaseTransformClass  , PlugIn 내부에서 Data 변경중심으로 동작 (이때 Data를 어떻게 trasform 시키는지 확인) 

  GObjectClass *gobject_class;
  GstElementClass *gstelement_class;
  GstBaseTransformClass *gstbasetransform_class;

  gobject_class = (GObjectClass *) klass;
  gstelement_class = (GstElementClass *) klass;
  gstbasetransform_class = (GstBaseTransformClass *) klass;

  /* Overide base class functions */
  gobject_class->set_property = GST_DEBUG_FUNCPTR (gst_dsexample_set_property);
  gobject_class->get_property = GST_DEBUG_FUNCPTR (gst_dsexample_get_property);

  gstbasetransform_class->set_caps = GST_DEBUG_FUNCPTR (gst_dsexample_set_caps);
  gstbasetransform_class->start = GST_DEBUG_FUNCPTR (gst_dsexample_start);
  gstbasetransform_class->stop = GST_DEBUG_FUNCPTR (gst_dsexample_stop);

상위와 같이 각각의 Class에 Callback Function을 넣고 동작을 하는데, 언제 호출이되는지를 파악하자.

  • GstBaseSink 와 GstBaseTransform 관련구조 파악 
둘 다 구조를 보면 GstElement 가 부모 Class 이므로 GstElement 하위 클래스 특징을 알아두자
각각의 method들을 파악하자
  https://gstreamer.freedesktop.org/documentation/base/gstbasetransform.html?gi-language=c#GstBaseTransform
  https://gstreamer.freedesktop.org/documentation/base/gstbasesink.html?gi-language=c#GstBaseSink

  • DeepStream Program 과 PlugIn을 작성중에 NVIDIA에게 직접질문사항 
dsexample의 properties 중 full-frame의 설정에따라 openCV 와 crop기능이 동작이 되는데, 이 부분을 내 소스에 적용하여 동작되는 것은 확인했다.
이를 정확하게 이해하고자 하면,  반드시 DeepStream SDK API 문서를 보고 각각의 동작을 이해해야한다.
  https://devtalk.nvidia.com/default/topic/1061422/deepstream-sdk/how-to-crop-the-image-and-save/

ROI를 PlugIn을 이용하여 이미 개발을 했는데, Line으로 가능하다고하는데 추후 테스트진행
  https://devtalk.nvidia.com/default/topic/1061791/deepstream-sdk/about-roi-in-ds4-0-on-xavier-/post/5378191/#5378191


1.6  각 모델의 성능비교 (TensorRT)

이전에 TensorRT를 하면서 trtexec 제대로 사용할 줄을 몰랐는데, 이제 사용법을 제대로 알겠다.
이 Tool은 UFF/Caffe/ONNX Model, TensorRT Engine의 성능측정을 위해서 사용되어진다고 한다.

//각 모델의 성능을 측정을 해보기 위해서 trtexec 사용을 해보자 
$ /usr/src/tensorrt/bin/trtexec --help
&&&& RUNNING TensorRT.trtexec # /usr/src/tensorrt/bin/trtexec --help
[I] help

Mandatory params:
  --deploy=          Caffe deploy file
  OR --uff=          UFF file
  OR --onnx=         ONNX Model file
  OR --loadEngine=   Load a saved engine

Mandatory params for UFF:
  --uffInput=,C,H,W Input blob name and its dimensions for UFF parser (can be specified multiple times)
  --output=      Output blob name (can be specified multiple times)

Mandatory params for Caffe:
  --output=      Output blob name (can be specified multiple times)

Optional params:
  --model=          Caffe model file (default = no model, random weights used)
  --batch=N               Set batch size (default = 1)
  --device=N              Set cuda device to N (default = 0)
  --iterations=N          Run N iterations (default = 10)
  --avgRuns=N             Set avgRuns to N - perf is measured as an average of avgRuns (default=10)
  --percentile=P          For each iteration, report the percentile time at P percentage (0<=P<=100, with 0 representing min, and 100 representing max; default = 99.0%)
  --workspace=N           Set workspace size in megabytes (default = 16)
  --safe                  Only test the functionality available in safety restricted flows.
  --fp16                  Run in fp16 mode (default = false). Permits 16-bit kernels
  --int8                  Run in int8 mode (default = false). Currently no support for ONNX model.
  --verbose               Use verbose logging (default = false)
  --saveEngine=     Save a serialized engine to file.
  --loadEngine=     Load a serialized engine from file.
  --calib=          Read INT8 calibration cache file.  Currently no support for ONNX model.
  --useDLACore=N          Specify a DLA engine for layers that support DLA. Value can range from 0 to n-1, where n is the number of DLA engines on the platform.
  --allowGPUFallback      If --useDLACore flag is present and if a layer can't run on DLA, then run on GPU. 
  --useSpinWait           Actively wait for work completion. This option may decrease multi-process synchronization time at the cost of additional CPU usage. (default = false)
  --dumpOutput            Dump outputs at end of test. 
  -h, --help              Print usage

//trtexec의 정보를 얻기위해서 각 config 파일 파악 
$ cd ~/deepstream-4.0/sources/apps/sample_apps/deepstream-test2
$ cat dstest2_pgie_config.txt

# Following properties are mandatory when engine files are not specified:
#   int8-calib-file(Only in INT8)
#   Caffemodel mandatory properties: model-file, proto-file, output-blob-names            // Caffe Model 은 3가지 정보가 필수 , model-file / proto-file , out-blob-names
#   UFF: uff-file, input-dims, uff-input-blob-name, output-blob-names                            // UFF , uff-file, input-dims, uff-input-blob-name, output-blob-names 
#   ONNX: onnx-file                                                                                                 //  ONNX: onnx-file 

model-file=../../../../samples/models/Primary_Detector/resnet10.caffemodel
proto-file=../../../../samples/models/Primary_Detector/resnet10.prototxt
...
output-blob-names=conv2d_bbox;conv2d_cov/Sigmoid

//TensorRT Engine 
$ /usr/src/tensorrt/bin/trtexec   --loadEngine=/home/nvidia/deepstream-4.0/samples/models/Primary_Detector/resnet10.caffemodel_b4_int8.engine 
&&&& RUNNING TensorRT.trtexec # /usr/src/tensorrt/bin/trtexec --loadEngine=/home/nvidia/deepstream-4.0/samples/models/Primary_Detector/resnet10.caffemodel_b4_int8.engine
[I] loadEngine: /home/nvidia/deepstream-4.0/samples/models/Primary_Detector/resnet10.caffemodel_b4_int8.engine
[I] /home/nvidia/deepstream-4.0/samples/models/Primary_Detector/resnet10.caffemodel_b4_int8.engine has been successfully loaded.
[I] Average over 10 runs is 1.01944 ms (host walltime is 1.10421 ms, 99% percentile time is 1.06387).
[I] Average over 10 runs is 1.00943 ms (host walltime is 1.06669 ms, 99% percentile time is 1.02454).
[I] Average over 10 runs is 1.00519 ms (host walltime is 1.06144 ms, 99% percentile time is 1.01184).
[I] Average over 10 runs is 1.00898 ms (host walltime is 1.07056 ms, 99% percentile time is 1.02982).
[I] Average over 10 runs is 1.00417 ms (host walltime is 1.06018 ms, 99% percentile time is 1.02707).
[I] Average over 10 runs is 1.00541 ms (host walltime is 1.06557 ms, 99% percentile time is 1.02682).
[I] Average over 10 runs is 1.00323 ms (host walltime is 1.0602 ms, 99% percentile time is 1.03834).
[I] Average over 10 runs is 1.00476 ms (host walltime is 1.06061 ms, 99% percentile time is 1.02954).
[I] Average over 10 runs is 1.00358 ms (host walltime is 1.05957 ms, 99% percentile time is 1.00902).
[I] Average over 10 runs is 1.00232 ms (host walltime is 1.05585 ms, 99% percentile time is 1.00704).

//Caffe Model 과 TensorRT 상위비교가능 (소요시간은 알겠지만, percentile time 은 무슨의미인지 퍼센트?)
$ /usr/src/tensorrt/bin/trtexec --deploy=/home/nvidia/deepstream-4.0/samples/models/Primary_Detector/resnet10.prototxt \
  --model=/home/nvidia/deepstream-4.0/samples/models/Primary_Detector/resnet10.caffemodel \
  --output=conv2d_bbox

&&&& RUNNING TensorRT.trtexec # /usr/src/tensorrt/bin/trtexec --deploy=/home/nvidia/deepstream-4.0/samples/models/Primary_Detector/resnet10.prototxt --model=/home/nvidia/deepstream-4.0/samples/models/Primary_Detector/resnet10.caffemodel --output=conv2d_bbox
[I] deploy: /home/nvidia/deepstream-4.0/samples/models/Primary_Detector/resnet10.prototxt
[I] model: /home/nvidia/deepstream-4.0/samples/models/Primary_Detector/resnet10.caffemodel
[I] output: conv2d_bbox
[I] Input "input_1": 3x368x640
[I] Output "conv2d_bbox": 16x23x40
[I] Average over 10 runs is 4.64663 ms (host walltime is 4.72585 ms, 99% percentile time is 4.7319).
[I] Average over 10 runs is 4.62393 ms (host walltime is 4.69601 ms, 99% percentile time is 4.64422).
[I] Average over 10 runs is 4.6295 ms (host walltime is 4.69154 ms, 99% percentile time is 4.64858).
[I] Average over 10 runs is 4.62978 ms (host walltime is 4.68834 ms, 99% percentile time is 4.64538).
[I] Average over 10 runs is 4.62103 ms (host walltime is 4.68236 ms, 99% percentile time is 4.63843).
[I] Average over 10 runs is 4.62193 ms (host walltime is 4.68143 ms, 99% percentile time is 4.64042).
[I] Average over 10 runs is 4.61595 ms (host walltime is 4.67465 ms, 99% percentile time is 4.62768).
[I] Average over 10 runs is 4.61807 ms (host walltime is 4.67505 ms, 99% percentile time is 4.63514).
[I] Average over 10 runs is 4.61827 ms (host walltime is 4.68276 ms, 99% percentile time is 4.62362).
[I] Average over 10 runs is 4.62702 ms (host walltime is 4.69345 ms, 99% percentile time is 4.64864).
&&&& PASSED TensorRT.trtexec # /usr/src/tensorrt/bin/trtexec --deploy=/home/nvidia/deepstream-4.0/samples/models/Primary_Detector/resnet10.prototxt --model=/home/nvidia/deepstream-4.0/samples/models/Primary_Detector/resnet10.caffemodel --output=conv2d_bbox

$ /usr/src/tensorrt/bin/trtexec --deploy=/home/nvidia/deepstream-4.0/samples/models/Primary_Detector/resnet10.prototxt \
  --model=/home/nvidia/deepstream-4.0/samples/models/Primary_Detector/resnet10.caffemodel \
  --output=conv2d_cov/Sigmoid
&&&& RUNNING TensorRT.trtexec # /usr/src/tensorrt/bin/trtexec --deploy=/home/nvidia/deepstream-4.0/samples/models/Primary_Detector/resnet10.prototxt --model=/home/nvidia/deepstream-4.0/samples/models/Primary_Detector/resnet10.caffemodel --output=conv2d_cov/Sigmoid
[I] deploy: /home/nvidia/deepstream-4.0/samples/models/Primary_Detector/resnet10.prototxt
[I] model: /home/nvidia/deepstream-4.0/samples/models/Primary_Detector/resnet10.caffemodel
[I] output: conv2d_cov/Sigmoid
[I] Input "input_1": 3x368x640
[I] Output "conv2d_cov/Sigmoid": 4x23x40
[I] Average over 10 runs is 4.6327 ms (host walltime is 4.70868 ms, 99% percentile time is 4.6904).
[I] Average over 10 runs is 4.62576 ms (host walltime is 4.69326 ms, 99% percentile time is 4.66947).
[I] Average over 10 runs is 4.62769 ms (host walltime is 4.68794 ms, 99% percentile time is 4.65818).
[I] Average over 10 runs is 4.62516 ms (host walltime is 4.69319 ms, 99% percentile time is 4.66173).
[I] Average over 10 runs is 4.62184 ms (host walltime is 4.68396 ms, 99% percentile time is 4.64534).
[I] Average over 10 runs is 4.62518 ms (host walltime is 4.67966 ms, 99% percentile time is 4.64067).
[I] Average over 10 runs is 4.62082 ms (host walltime is 4.68281 ms, 99% percentile time is 4.64358).
[I] Average over 10 runs is 4.62256 ms (host walltime is 4.68476 ms, 99% percentile time is 4.65318).
[I] Average over 10 runs is 4.62129 ms (host walltime is 4.68117 ms, 99% percentile time is 4.64125).
[I] Average over 10 runs is 4.62561 ms (host walltime is 4.6864 ms, 99% percentile time is 4.64435).

//UFF Model 
$ cd ~/deepstream-4.0/sources/objectDetector_SSD
$ cat config_infer_primary_ssd.txt
.......
uff-file=sample_ssd_relu6.uff
uff-input-dims=3;300;300;0
uff-input-blob-name=Input
...
output-blob-names=MarkOutput_0
parse-bbox-func-name=NvDsInferParseCustomSSD
custom-lib-path=nvdsinfer_custom_impl_ssd/libnvdsinfer_custom_impl_ssd.so

//UFF 모델은 Custom Layer를 사용해서 동작이 제대로 안될 것 같다 ( uffinput 부분도 상위 값과 어떤의미를 정확하게 알아야하는데, 모름)
$ /usr/src/tensorrt/bin/trtexec --uff=/home/nvidia/deepstream-4.0/sources/objectDetector_SSD/sample_ssd_relu6.uff \
  --uffInput=Input,3,300,300 \
  --output=MarkOutput_0
[I] uff: /home/nvidia/deepstream-4.0/sources/objectDetector_SSD/sample_ssd_relu6.uff
[I] uffInput: Input,3,300,300
[I] output: MarkOutput_0
[E] [TRT] UffParser: Validator error: concat_box_loc: Unsupported operation _FlattenConcat_TRT
[E] Engine could not be created
[E] Engine could not be created
&&&& FAILED TensorRT.trtexec # /usr/src/tensorrt/bin/trtexec --uff=/home/nvidia/deepstream-4.0/sources/objectDetector_SSD/sample_ssd_relu6.uff --uffInput=Input,3,300,300 --output=MarkOutput_0


Best Practices For TensorRT Performance (trtexec 및 다른 Tool)
  https://docs.nvidia.com/deeplearning/sdk/tensorrt-best-practices/index.html

Caffe Model trtexec 실제사용
  https://devtalk.nvidia.com/default/topic/1061845/deepstream-sdk/resnet50-classification-as-primary-gie/

Model Parser Error 사항
  https://docs.nvidia.com/deeplearning/sdk/tensorrt-developer-guide/index.html#error-messaging

yais Tool 은 추후에 사용해보자
  https://github.com/NVIDIA/tensorrt-laboratory/tree/master/examples/00_TensorRT



2. DeepStream 의 Gstream 테스트 

DeepStream SDK 3.0 및 Gstreamer 관련정리
  https://ahyuo79.blogspot.com/2019/07/deepstream-sdk-30-gstreamer.html


2.1 Gstreamer Debugging 방법 

GST_DEBUG 의 환경변수를 이용하며, 이곳에 원하는 값을 넣고 설정하면 관련 Debug Message 볼수가 있다.
1~9까지 선택이 가능하다
( 1 - ERROR ,2 - WARNING ,3 - FIXME  ,4 - INFO  )
( 5 - DEBUG, 6 - LOG,  7 - TRACE, 9 - MEMDUMP )

Gstreamer 관련 환경변수들 (GST_DEBUG 이외 다양한 환경변수)
  https://gstreamer.freedesktop.org/documentation/gstreamer/running.html?gi-language=c

GST_DEBUG 관련사용방법
  https://gstreamer.freedesktop.org/documentation/tutorials/basic/debugging-tools.html?gi-language=c


  • 전체설정 Debug
Gstreamer 를 전체 Debug 하고자 하면 아래와 같이 하면 되지만 별로 추천하지는 않는다.

$ export GST_DEBUG="*:2" //  전체 WARN,  설정이 가능,  가장 적절    
$ export GST_DEBUG="*:4" //  전체 INFO,설정    
$ export GST_DEBUG=""   // 설정제거


  • 원하는 PlugIn 만 Debug 
모든 PlugIn을 Debug 하지말고 특정 PlugIN만 Debug하여 관련부분의 문제점을 찾아보자.

 // 모든 PlugIN Debug  , 내부 PlugIN을 구조를 모르면 이렇게 확인하고 PlugIN 구조를 파악 
$ GST_DEBUG="*:5" deepstream-app -c deepstream_app_config_yoloV3.txt 

//특정 PlugIn만 Debug 
$ GST_DEBUG="dsexample:5" ./deepstream-rtsp-app rtsp://10.0.0.199:554/h264    // 특정 PlugIN만 세부 Debug 
$ GST_DEBUG="qtdemux:5" deepstream-app -c deepstream_app_config_yoloV3.txt  // qtdemux 만 세부 Debug 

//동시에 여러개 PlugIn Debug 
$ GST_DEBUG="qtdemux:5,dsexample:4 " deepstream-app -c deepstream_app_config_yoloV3.txt  // qtdemux 만 세부 Debug 


  • 이외 PlugIn의 카테고리 설정 Debug
본인이 직접 특정 Category를 선언하고 그에 관련된 부분을 직접 Debug도 가능하다, 상위 PlugIn도 다 들어가보면, Category로 선언하여 사용한다

// 소스에서 직접 원하는 Category 설정 (my_category)
GST_DEBUG_CATEGORY_STATIC (my_category);  // define category (statically)
#define GST_CAT_DEFAULT my_category       // set as default

  if (!my_category) {
   GST_DEBUG_CATEGORY_INIT (my_category, "MY_CAT", 0, NULL);
  }

  GST_CAT_INFO (my_category, "TEST Info %s", "Category TEST");
  GST_CAT_DEBUG (my_category, "TEST Debug %s", "Category TEST");
  GST_CAT_ERROR (my_category, "TEST error %s", "Category TEST");

//실제 테스트 
$ GST_DEBUG="MY_CAT:5" deepstream-app -c deepstream_app_config_yoloV3.txt 
$ GST_DEBUG="MY_CAT:9" deepstream-app -c deepstream_app_config_yoloV3.txt 


  https://gstreamer.freedesktop.org/documentation/gstreamer/gstinfo.html?gi-language=c


2.2  Gstreamer 관련부분 SDK3.0 관련부분 재확인

Gstreamer 관련 Test는 이미 SDK 3.0에서 많이 했기때문에 간단히 서술하며, SDK 4.0과 SDK 3.0은 호환성 Gstreamer 명령어가 동일하게 동작되지 않는 부분이 많다.
PlugIn 이름과 설정 변경이 되었기때문에 상위의 gst-inspect로 확인하고 실행하자

  • DeepStream SDK 3.0의 TEST2 예제 
아래와 같이  decodebin or uridecodebin 사용해서 테스트 진행했으며, 세부내용은 아래참조

$ pwd
/home/nvidia/deepstream_sdk_on_jetson/sources/apps/sample_apps/deepstream-test2

//Sample 영상로 1stGIE,2ndGIE ,nvtracker 사용하여 화면전체 재생 ( X-Window 재생은 상위 참조)

$ gst-launch-1.0 filesrc location=../../../../samples/streams/sample_720p.h264 ! \
        decodebin ! nvinfer config-file-path= dstest2_pgie_config.txt ! \
        nvtracker ! \
        nvinfer config-file-path= dstest2_sgie1_config.txt ! \
        nvinfer config-file-path= dstest2_sgie2_config.txt ! \
        nvinfer config-file-path= dstest2_sgie3_config.txt ! \
        capsfilter caps=video/x-raw(memory:NVMM), format=NV12 ! \
        nvvidconv ! \
        capsfilter caps=video/x-raw(memory:NVMM), format=RGBA ! \
        nvosd font-size=15 ! nvoverlaysink

// RTSP를 이용하여  1stGIE,2ndGIE ,nvtracker 사용하여 화면전체 재생 

$ gst-launch-1.0 uridecodebin uri=rtsp://10.0.0.199:554/h264 ! \
        nvinfer config-file-path= dstest2_pgie_config.txt ! \
        nvtracker ! \
        nvinfer config-file-path= dstest2_sgie1_config.txt ! \
        nvinfer config-file-path= dstest2_sgie2_config.txt ! \
        nvinfer config-file-path= dstest2_sgie3_config.txt ! \
        capsfilter caps=video/x-raw(memory:NVMM), format=NV12 ! \
        nvvideoconvert ! \
        capsfilter caps=video/x-raw(memory:NVMM), format=RGBA ! \
        nvdsosd ! nvoverlaysink

//Sample 영상로 1stGIE,2ndGIE ,nvtracker 사용하여 X-Window 창 재생 

$ gst-launch-1.0 filesrc location=../../../../samples/streams/sample_720p.mp4 ! \
        decodebin ! nvinfer config-file-path= dstest2_pgie_config.txt ! \
        nvtracker ! \
        nvinfer config-file-path= dstest2_sgie1_config.txt ! \
        nvinfer config-file-path= dstest2_sgie2_config.txt ! \
        nvinfer config-file-path= dstest2_sgie3_config.txt ! \
        nvvidconv ! nvosd ! nvegltransform ! nveglglessink

  • DeepStream SDK 3.0 관련문서 
아래 링크와 비교를 해보면, 이전의 DeepStream SDK 3.0과는 많이 변했으며, Gstreamer도 호환이 되지 않는다.
  https://ahyuo79.blogspot.com/2019/07/deepstream-sdk-30-gstreamer.html


2.3 Gstreamer 관련부분 SDK 4.0 관련부분확인 

DeepStream SDK 4.0으로 오면서 우선 가장 큰 차이는 1Channel을 사용해도 streammux는 반드시 사용이 되어야한다.
더불어 nvvidconv 대신 nvvideoconvert을 사용해야하며, 이전 처럼 필터설정은 필요없어 진것 같다.
nvvidconv의 경우 nvosd가 없어져서 동작이 안되는 것로 생각된다.
SDK 4.0으로 오면서 PlugIn(Element)의 Properties가 다양해졌으며, 변경되었기 때문에 조심하자.

  • DeepStream SDK 4.0의 TEST2  기본설정 
우선 아래와 같이 TensorRT (1st, 2nd) 엔진설정을 해두자
상위에서 설명했듯이 Jetson AGX Xavier는 INT8모드 지원하며 이를 설정시 Table도 같이 설정해야함 (상위참조)
- model-engine-file
- int8-calib-file
- network-mode=1     # 0=FP32, 1=INT8, 2=FP16 mode

DeepStream SDK4.0 TEST4 와 iPlugIn 관련사항 (이전부분참조)
  https://ahyuo79.blogspot.com/2019/08/ds-sdk-40-test4-iplugin-sample.html


$cd ~/deepstream-4.0/sources/apps/sample_apps/deepstream-test2
$ pwd
/home/nvidia/deepstream-4.0/sources/apps/sample_apps/deepstream-test2

$ vi dstest2_pgie_config.txt 
model-engine-file=../../../../samples/models/Primary_Detector/resnet10.caffemodel_b1_int8.engine

$ vi dstest2_sgie1_config.txt 
model-engine-file=../../../../samples/models/Secondary_CarColor/resnet18.caffemodel_b16_int8.engine

$ vi dstest2_sgie2_config.txt
model-engine-file=../../../../samples/models/Secondary_CarMake/resnet18.caffemodel_b16_int8.engine

$ vi dstest2_sgie3_config.txt
model-engine-file=../../../../samples/models/Secondary_VehicleTypes/resnet18.caffemodel_b16_int8.engine


  • nvstreammux 기본 사용법 
필수설정이므로 관련 각 기능을 알아두도록하자 (nvinfer 전에 설정)
아래와 같이 m.sink_0 을 두어 앞에 sink를 설정하여 여러채널을 받을 수 있다.
뒤의 설정을 보면 batch-size는 Channel (Frame) 과 Resolution을 설정 할수 있어 Scale도 가능하다
만약 nvinfer를 사용하지 않는다면, 아래의 테스트와 같이 사용을 안해도 상관은 없는것 같다.

m.sink_0 nvstreammux name=m batch-size=1 width=1280 height=720

관련질문사항( 궁금해서 직접 알아봄)
  https://devtalk.nvidia.com/default/topic/1061785/deepstream-sdk/about-streammux-in-ds-on-xavier/

PlugIn Manual ( 아직 미지원사항이 있으므로 주의 , Jetson과 Tesla 와 별도)
  https://docs.nvidia.com/metropolis/deepstream/4.0/DeepStream_Plugin_Manual.pdf

  • DeepStream SDK 4.0의 TEST2의 기본 테스트 진행 
우선  h264parse 와 nvv4l2decoder(nvv4l2decoder 새로 생김)을 이용하여 동작해보고, 출력은 X-Window창으로 출력을 하도록하자 (OpenGL사용)
아래와 같이 nvosd 가 사라져서 두번째 것은 동작이 안된다.

//Sample TEST 2 동작확인 
$ gst-launch-1.0 filesrc location=../../../../samples/streams/sample_720p.h264 ! \
        h264parse !  nvv4l2decoder ! \
        m.sink_0 nvstreammux name=m batch-size=1 width=1280 height=720 ! \
        nvinfer config-file-path= dstest2_pgie_config.txt ! \
        nvtracker tracker-width=640 tracker-height=368  gpu-id=0  \
        ll-lib-file=/opt/nvidia/deepstream/deepstream-4.0/lib/libnvds_nvdcf.so \
        ll-config-file=tracker_config.yml enable-batch-process=1 ! \
        nvinfer config-file-path= dstest2_sgie1_config.txt ! \
        nvinfer config-file-path= dstest2_sgie2_config.txt ! \
        nvinfer config-file-path= dstest2_sgie3_config.txt ! \
        nvvideoconvert ! nvdsosd ! nvegltransform ! nveglglessink

//Sample nvvidconv 와 nvosd 변경 (nvosd 미지원으로 에러발생)   4.0부터는 nvvideoconvert 와 nvdsosd 를 이용권장 
 gst-launch-1.0 filesrc location=../../../../samples/streams/sample_720p.h264 ! \
        h264parse !  nvv4l2decoder ! \
        m.sink_0 nvstreammux name=m batch-size=1 width=1280 height=720 ! \
        nvinfer config-file-path= dstest2_pgie_config.txt ! \
        nvtracker tracker-width=640 tracker-height=368  gpu-id=0  \
        ll-lib-file=/opt/nvidia/deepstream/deepstream-4.0/lib/libnvds_nvdcf.so \
        ll-config-file=tracker_config.yml enable-batch-process=1 ! \
        nvinfer config-file-path= dstest2_sgie1_config.txt ! \
        nvinfer config-file-path= dstest2_sgie2_config.txt ! \
        nvinfer config-file-path= dstest2_sgie3_config.txt ! \
        nvvidconv ! nvosd ! nvegltransform ! nveglglessink


  • DeepStream SDK 4.0 의 TEST2  decode 테스트 실행 
기존처럼 편하게 deepstrem-test2 에 decodebin 과 uridecodebin을 사용이 가능하다.

 
// 주의 해야할 것은 처음 실행시, TensorRT Engine이 없으므로, 생성시간이 많이 걸림
// 각각의 model-engine-file을 설정을 해줘서 이를 해결하지만, Jetson AGX Xavier (INT8 지원가능)

//Sample TEST 2  decodebin 변경 (동작확인) 
$ gst-launch-1.0 filesrc location=../../../../samples/streams/sample_720p.h264 ! \
        decodebin ! \
        m.sink_0 nvstreammux name=m batch-size=1 width=1280 height=720 ! \
        nvinfer config-file-path= dstest2_pgie_config.txt ! \
        nvtracker tracker-width=640 tracker-height=368  gpu-id=0  \
        ll-lib-file=/opt/nvidia/deepstream/deepstream-4.0/lib/libnvds_nvdcf.so \
        ll-config-file=tracker_config.yml enable-batch-process=1 ! \
        nvinfer config-file-path= dstest2_sgie1_config.txt ! \
        nvinfer config-file-path= dstest2_sgie2_config.txt ! \
        nvinfer config-file-path= dstest2_sgie3_config.txt ! \
        nvvideoconvert ! nvdsosd ! nvegltransform ! nveglglessink

//Sample TEST 2  uridecodebin 변경 (RTSP지원) 동작확인 
$ gst-launch-1.0 uridecodebin uri=rtsp://10.0.0.199:554/h264 ! \
        m.sink_0 nvstreammux name=m batch-size=1 width=1280 height=720 ! \
        nvinfer config-file-path= dstest2_pgie_config.txt ! \
        nvtracker tracker-width=640 tracker-height=368  gpu-id=0  \
        ll-lib-file=/opt/nvidia/deepstream/deepstream-4.0/lib/libnvds_nvdcf.so \
        ll-config-file=tracker_config.yml enable-batch-process=1 ! \
        nvinfer config-file-path= dstest2_sgie1_config.txt ! \
        nvinfer config-file-path= dstest2_sgie2_config.txt ! \
        nvinfer config-file-path= dstest2_sgie3_config.txt ! \
        nvvideoconvert ! nvdsosd ! nvegltransform ! nveglglessink

//Sample TEST 2  uridecodebin 변경 (FILE지원) 동작확인 
$ gst-launch-1.0 uridecodebin uri=file:///home/nvidia/deepstream-4.0/samples/streams/sample_720p.h264  ! \
        m.sink_0 nvstreammux name=m batch-size=1 width=1280 height=720 ! \
        nvinfer config-file-path= dstest2_pgie_config.txt ! \
        nvtracker tracker-width=640 tracker-height=368  gpu-id=0  \
        ll-lib-file=/opt/nvidia/deepstream/deepstream-4.0/lib/libnvds_nvdcf.so \
        ll-config-file=tracker_config.yml enable-batch-process=1 ! \
        nvinfer config-file-path= dstest2_sgie1_config.txt ! \
        nvinfer config-file-path= dstest2_sgie2_config.txt ! \
        nvinfer config-file-path= dstest2_sgie3_config.txt ! \
        nvvideoconvert ! nvdsosd ! nvegltransform ! nveglglessink

//Sample TEST 2  uridecodebin 변경 (RTSP지원) 미동작확인 (nvstreamux 삭제) 
$ gst-launch-1.0 uridecodebin uri=rtsp://10.0.0.199:554/h264 ! \
        nvinfer config-file-path= dstest2_pgie_config.txt ! \
        nvtracker tracker-width=640 tracker-height=368  gpu-id=0  \
        ll-lib-file=/opt/nvidia/deepstream/deepstream-4.0/lib/libnvds_nvdcf.so \
        ll-config-file=tracker_config.yml enable-batch-process=1 ! \
        nvinfer config-file-path= dstest2_sgie1_config.txt ! \
        nvinfer config-file-path= dstest2_sgie2_config.txt ! \
        nvinfer config-file-path= dstest2_sgie3_config.txt ! \
        nvvideoconvert ! nvdsosd ! nvegltransform ! nveglglessink

//Sample TEST 2  uridecodebin 변경 (RTSP지원) 동작확인 (nvstreamux 삭제 및 nvinfer/nvtracker 삭제 ) 
$ gst-launch-1.0 uridecodebin uri=rtsp://10.0.0.199:554/h264 ! \
        nvvideoconvert ! nvdsosd ! nvegltransform ! nveglglessink

nvinfer 때문에 nvstreamux 필요한 것 같음 (추측)

  • DeepStream SDK 4.0 의 TEST2  출력부분을 변경하여 각각 테스트
  1. nvegltransform ! nveglglessink 은 X-Window 창으로 출력 
  2. nvoverlaysink 설정하면, 전체 화면출력  

//Sample TEST 2  nvoverlaysink 변경 (전체화면) 동작확인 
$ gst-launch-1.0 uridecodebin uri=file:///home/nvidia/deepstream-4.0/samples/streams/sample_720p.h264  ! \
        m.sink_0 nvstreammux name=m batch-size=1 width=1280 height=720 ! \
        nvinfer config-file-path= dstest2_pgie_config.txt ! \
        nvtracker tracker-width=640 tracker-height=368  gpu-id=0  \
        ll-lib-file=/opt/nvidia/deepstream/deepstream-4.0/lib/libnvds_nvdcf.so \
        ll-config-file=tracker_config.yml enable-batch-process=1 ! \
        nvinfer config-file-path= dstest2_sgie1_config.txt ! \
        nvinfer config-file-path= dstest2_sgie2_config.txt ! \
        nvinfer config-file-path= dstest2_sgie3_config.txt ! \
        nvvideoconvert ! nvdsosd ! nvoverlaysink

  • MPEG4->H.264 Transcoding 테스트 
기존과 동일하게 동작되며, 재생이 아닌이상 streammux가 설정안해도 됨

 $  gst-launch-1.0 filesrc location=../../../../samples/streams/sample_720p.mp4 ! decodebin ! omxh264enc !  h264parse ! qtmux ! filesink location=test.h264 


  • tee를 사용하여 2개의 채널을 분리하여, 재생과 Transcoding 동시진행
기존 SDK 3.0 처럼 tee를 사용하여 재생과 Transcoding을 동시진행하려고 했으나 동작 안되는데, PlugIn(Element) 단위로 보면 미동작 원인이 이해가 안간다.

// 기본영상재생시 동작확인 ( Streammux 권고)
$  gst-launch-1.0 filesrc location=../../../../samples/streams/sample_720p.mp4 ! decodebin ! m.sink_0 nvstreammux name=m batch-size=1 width=1280 height=720 ! nvegltransform ! nveglglessink

// RTSP를 위해 uridecodebin으로 변경 후  nvstreammux 를 제거해서 실행 
$ gst-launch-1.0 uridecodebin uri=rtsp://10.0.0.201:554/h264 ! nvvideoconvert ! nvegltransform ! nveglglessink

// tee를 사용하여 1 Channel 재생  (동작확인) 
$  gst-launch-1.0 filesrc location=../../../../samples/streams/sample_720p.mp4 ! decodebin ! tee name=t ! queue ! m.sink_0 nvstreammux name=m batch-size=1 width=1280 height=720 ! nvegltransform ! nveglglessink 

//tee를 사용하여 2 채널 사용 (문제발생)
$  gst-launch-1.0 filesrc location=../../../../samples/streams/sample_720p.mp4 ! decodebin ! tee name=t ! queue ! m.sink_0 nvstreammux name=m batch-size=1 width=1280 height=720 ! nvegltransform ! nveglglessink t. ! queue ! omxh264enc !  h264parse ! qtmux  ! filesink location=test.mp4 

//tee를 사용하여 2 채널 사용 (NVIDIA 권고사항) 동작은되지만, 가끔 에러발생
$ gst-launch-1.0 filesrc location=../../../../samples/streams/sample_720p.mp4 ! decodebin ! tee name=t ! queue ! m.sink_0 nvstreammux name=m batch-size=1 width=1280 height=720 ! nvegltransform ! nveglglessink t. ! queue ! nvvideoconvert ! nvv4l2h264enc !  h264parse ! qtmux  ! filesink location=test.mp4

//tee를 사용하여 2 채널 사용 fakesink (동작확인)  , 4.0 부터는 NVIDIA에서는 omxh264enc를 권장하는 않는 것 같음 
$  gst-launch-1.0 filesrc location=../../../../samples/streams/sample_720p.mp4 ! decodebin ! tee name=t ! queue ! fakesink t. ! queue ! omxh264enc !  h264parse ! qtmux  ! filesink location=test.mp4 

NVIDIA에게 직접물어보니 omxh264enc 대신 nvv4l2h264enc로 변경
  https://devtalk.nvidia.com/default/topic/1061803/deepstream-sdk/tee-in-ds-4-0-on-xavier-/


  • nvinfer 와 nvosd를 적용 후 화면재생과 H.264 Encoding 동시작업 
DS3.0 과 다르게 상위와 같이 omxh264enc 대신 nvv4l2h264enc을 사용해야 제대로 동작된다

//Sample TEST 2  nvoverlaysink 변경 (전체화면) 동작확인 

$ gst-launch-1.0 uridecodebin uri=file:///home/nvidia/deepstream-4.0/samples/streams/sample_720p.h264  ! \
        m.sink_0 nvstreammux name=m batch-size=1 width=1280 height=720 ! \
        nvinfer config-file-path= dstest2_pgie_config.txt ! \
        nvtracker tracker-width=640 tracker-height=368  gpu-id=0  \
        ll-lib-file=/opt/nvidia/deepstream/deepstream-4.0/lib/libnvds_nvdcf.so \
        ll-config-file=tracker_config.yml enable-batch-process=1 ! \
        nvinfer config-file-path= dstest2_sgie1_config.txt ! \
        nvinfer config-file-path= dstest2_sgie2_config.txt ! \
        nvinfer config-file-path= dstest2_sgie3_config.txt ! \
        nvvideoconvert ! nvdsosd ! nvoverlaysink

// 기본동작은 상위와 동일하지만, 2 Channel로 nvosd를 걸쳐 화면재생과 H.264 Encoding 동시작업
$ gst-launch-1.0 uridecodebin uri=file:///home/nvidia/deepstream-4.0/samples/streams/sample_720p.h264  ! \
        m.sink_0 nvstreammux name=m batch-size=1 width=1280 height=720 ! \
        nvinfer config-file-path= dstest2_pgie_config.txt ! \
        nvtracker tracker-width=640 tracker-height=368  gpu-id=0  \
        ll-lib-file=/opt/nvidia/deepstream/deepstream-4.0/lib/libnvds_nvdcf.so \
        ll-config-file=tracker_config.yml enable-batch-process=1 ! \
        nvinfer config-file-path= dstest2_sgie1_config.txt ! \
        nvinfer config-file-path= dstest2_sgie2_config.txt ! \
        nvinfer config-file-path= dstest2_sgie3_config.txt ! \
        nvvideoconvert ! nvdsosd ! \
        tee name=t ! queue ! nvegltransform ! nveglglessink \
        t. ! queue ! nvvideoconvert ! nvv4l2h264enc !  h264parse ! qtmux  ! filesink location=test.mp4

//NVIDIA가 추후 nvvideoconvert로 변경해보라고 해서 재테스트  
$ gst-launch-1.0 uridecodebin uri=file:///home/nvidia/deepstream-4.0/samples/streams/sample_720p.h264  ! \
        m.sink_0 nvstreammux name=m batch-size=1 width=1280 height=720 ! \
        nvinfer config-file-path= dstest2_pgie_config.txt ! \
        nvtracker tracker-width=640 tracker-height=368  gpu-id=0  \
        ll-lib-file=/opt/nvidia/deepstream/deepstream-4.0/lib/libnvds_nvdcf.so \
        ll-config-file=tracker_config.yml enable-batch-process=1 ! \
        nvinfer config-file-path= dstest2_sgie1_config.txt ! \
        nvinfer config-file-path= dstest2_sgie2_config.txt ! \
        nvinfer config-file-path= dstest2_sgie3_config.txt ! \
        nvvideoconvert ! nvdsosd ! \
        tee name=t ! queue ! nvvideoconvert ! nveglglessink \
        t. ! queue ! nvvideoconvert ! nvv4l2h264enc !  h264parse ! qtmux  ! filesink location=test.mp4