9/20/2019

IOU (Intersection over Union)

1. IOU 관련사항 


  • IOU (Intersection over Union)

아래와 같이 두개의 Box가 겹칠 경우 Intersection의 부분의 비율을 알아보는 것이다.




출처
  https://medium.com/@ageitgey/snagging-parking-spaces-with-mask-r-cnn-and-python-955f2231c400


NVIDIA-DeepStream 
  https://ahyuo79.blogspot.com/search/label/NVIDIA-DeepStream


1.1 IOU 계산 방법 

  • IOU 계산방법 
  https://inspace4u.github.io/dllab/lecture/2017/09/28/IoU.html

상위 방법은 Python으로 사용하면 쉽게 구할 수 있을 것 같은데, 소스의 동작원리를 정확하게 모르겠다.
일반적인 좌표를 사용하게 되면 어떻게 구현이 될까라는 것도 궁금하다

  • 좌표로만 IOU 직접 계산방법
좌표구조로 구성되고 BBOX가 사각형이라고 하면 아래와 같이 구성이 될 것 같다.
Left(X), Top(Y) , Width, Height

이때 사각형의 대각선 좌표값으로,  X, Y 좌표와   (X+Width) , ( Y+Height) 좌표 두개로 쉽게 IOU를 감지하고 이를 계산가능

ex) A-BBOX의 좌표   X(3) , Y (3) , widht (3)  ,height(4) 이라고 하면 아래 두 좌표로 4각형을 인지가능

  1. A-BBOX의  A-MIN 좌표  (3,3)
  2. A-BBOX의  A-MAX 좌표 (6,7) 

  • B-BBOX 의 B-MIN 좌표  X >=3  &&  Y>=3 일 때  
    1. B-MIN 좌표의 X, Y가 아래 두 조건 충족시 IOU 계산 
      1. 3 <  X <  6  (3+width)
      2. 3  < Y  < 7  (3+height)
    2. InterSection Size  =  A-MAX - B-MIN   (각각의 크기나옴)

  • B-BBOX의 B-MIN 좌표   X < 3  || Y  < 3 일 때   
    1. B-MAX 좌표의 X, Y가 아래 두 조건 충족시 IOU 계산
      1. 3 <  X <  6  (3+width)
      2. 3  < Y  < 7  (3+height)
    2. InterSection Size =  B-MAX - A-MIN   (각각의 크기나옴)

상위와 같이 InterSection Size만 알면, 모든 계산이 쉬어진다.
현재 상위 조건은 모눈종이에 직접 그려서 생각해본 것 이며, 오류가 아직 존재할 수 있다.
추후 소스로 작성된 소스 검증을 해봐야겠다.


  • 모눈종이 PDF 문서

머리가 나뻐서 자꾸, 모눈 종이가 필요해서 아래 링크
  http://blog.daum.net/miparang/7058252


  • IOU 관련소스 자료모음 
  https://stackoverflow.com/questions/4549544/total-area-of-intersecting-rectangles/32244956
  https://bskyvision.com/465

아래의 소스는 상위 소스에서 간단히 IOU를 만드는 소스를 만들어 보았는데, 아직 검증을 제대로 해보지를 못했다.


static int max(int v1 , int v2)
{
 if(v1 > v2) return v1;
 else return v2;
}

static int min(int v1 , int v2)
{
 if(v1 > v2) return v2;
 else return v1;
}

static int interSection(int Ax1, int Ay1, int Ax2, int Ay2, int Bx1, int By1, int Bx2, int By2)
{
    int left   = max(Ax1, Bx1);
    int top    = max(Ay1, By1);
    int right  = min(Ax2, Bx2);
    int bottom = min(Ay2, By2);
    int interArea = 0;

    if (left < right && bottom > top)
     interArea = ((right - left) + 1) * ((bottom - top)+1);

   return interArea;
}



1.2  IOU 사용용도 

IOU 사용용도는 다양한  것 같으며, 일단, 아래의 사이트 에서 처럼  실제 BBOX와 예측된 BBOX의 차이를 알아서 교정을 위해서 도 사용이 되어진다.

  1. Ground-Truth bouning Box:  실제 모델의 Box, 손으로 직접 Label
  2. Predicted bouning Box:  Deep Learning을 통해 예측된 BBOX 

IOU 관련내용
  https://www.pyimagesearch.com/2016/11/07/intersection-over-union-iou-for-object-detection/

또한 동일한 Object를 여러개의 Object로 인식을 할 경우, 이때  IOU값에 따라 나머지를 없애는 데에도 사용되어 지는 것 같다.

더불어 Tracking 알고리즘에서도 역시 IOU는 사용이 되어지는데, Object Detection이 되고 이전 Frame 과 이후 Frame IOU값을 가지고 지속적으로
Tracking 하기위해서도 사용되어지는 것 같다.

이외에도 개인적으로 생각을 해보면, ROI(Region of Interest) BBOX 와 Object Detection 된 BBOX의 IOU를 이용도 가능 할 것 같다.
이는 각각의 ROI의 영역이 서로 겹칠 경우,  상위 Smart Parking System일 경우 이용 가능할 것 같다.

간단하지만, 생각해보면 아주 다양하게 이용이 되어지기 때문에  꼭 필요하다

9/16/2019

Jetson Nano 개발환경구축

1. Jetpack 4.2.2 설치 

기존의 SDKmanager를 설치를 해주었다면, Upgrade를 진행을 해주면되고, 아래의 Site에서 Download하자

Jetson Nano의 경우는 eMMC Type이 아닌 SD Card방식이므로 아래의 사이트에서 별도의 SD Card Image를 제공하고 있으므로,
SDKManger 설치가 익숙지 않는다면, 이것으로 Download하여 Image를 Write하여 사용하자

Jetpack Download
  https://developer.nvidia.com/embedded/jetpack

Jetson Nano Board 및 JetPack 설치 Manual
  https://developer.download.nvidia.com/embedded/L4T/r32-2_Release_v1.0/Jetson_Nano_Developer_Kit_User_Guide.pdf?vFL9evtTTiizks2dE50I2tofKlel_baFRnCEFgz-IiCbAttHALUknCurJNUO0-MfoWoONyJRa0QWOHj1GH_3fUJvLFv4elzlagbtYoV6eDmXciTVuQq7mxreo1r4b0fK6SM-NmeQeCU4wUoQRXxgbyp_c3RYdO1WiI_m3YytnaP2Z_CT8BOi


1.1 Jetson Nano의 보드 구성 


Jetson Nano의 구성을 살펴보면,  Jetson TX2와 같이 Carrier Board와 Module로 두개로 분리되어있으며, 추후 Module만 구입가능할지는 다시 봐야 할 것 같다.



  • Jetson Nano의 기본권장사항 
  1. Storage : microSD 지원 권장사양 16GB or 그이상 
  2. Power 선택 : micro B USB Power(J28) or Power Jack(J25) (5V,4A)
    1. micro B USB(J28) 로 USB Device로 연결되므로, 가급적 Power Jack 사용권장
    2. Power Jack으로 사용시 J48 Jumper 필요 
  3. Serial 사용: J44지원 or micro B USB로 연결시 ACM0으로 연결 


Jetson Nano Get Started
  https://developer.nvidia.com/embedded/learn/get-started-jetson-nano-devkit
  https://desertbot.io/blog/jetson-nano-usb-login

1.2  Force Recovery Mode 설정 

기존 Jetson 처럼 Force Recovery Mode를 제공하고 있지만, Button 형식으로 제공을 하고 있지 않다


상위 Manual을 읽어보면, 상위 기반은 Adapter 로 Power로 공급하고, J28 micro B USB는 USB Device로 만 사용해야지만, 가능한 이야기이다.
Manual을 읽어보면, 최소 필요사항은 다음과 같다

  1. DC Adapter (5V,4A)
  2. HDMI Cable 과 필요한 모니터 
  3. USB Keyboard와 Mouse 
  4. Jumper 

  • Force Recovery Mode (아직 못해봄 )
  1. J40 의 PIN3,4 연결 (with Jumper)
  2. J48 Pin 의 Power 선택 (USB Power or Adapter Power)
    1. J28 micro USB Power  (without Jumper, Default
    2. J25 Power Jack (with Jumper)
  3. J48 Pin의 선택에 따라 각 Power를 연결 ( USB Power  or Adapter Power)
  4. J40의 PIN3,4 Jumper 제거  

혹시나해서 NVIDIA Site에서 가능한지 물어봤지만, USB Power로만 가능하려면, 우선 PC의 USB 전원 공급이 가능하다면, 가능하다고 한다
  https://devtalk.nvidia.com/default/topic/1063210/jetson-nano/jetson-nano-recovery-mode-/post/5384033/#5384033


  • Force Recovery Mode 설정위치  (J40)
Top View의 상단 좌측의 J40의 구성



2. Jetpack 4.2.2 설치

가능하다면 SDK Manager를 설치해서 Host에도 NVIDIA의 관련 Tools 과 함께 설치진행하자.
만약 Jetson Nano의 상위 Force Recovery Mode 설정이 어렵다면  NVIDIA에서 제공하는 SD Card Image만을 Download하여 dd로 Image를 Write하자 


2.1 SD Card Image Write 방법 

SD card Image Download 후 아래와 같이 SD Card를 삽입하여, SD Card 위치 파악


$ lsusb -t
/:  Bus 02.Port 1: Dev 1, Class=root_hub, Driver=xhci_hcd/6p, 5000M
/:  Bus 01.Port 1: Dev 1, Class=root_hub, Driver=xhci_hcd/12p, 480M
    |__ Port 6: Dev 28, If 0, Class=Mass Storage, Driver=usb-storage, 480M
    |__ Port 9: Dev 3, If 0, Class=Hub, Driver=hub/4p, 480M
    |__ Port 11: Dev 4, If 0, Class=Human Interface Device, Driver=usbhid, 1.5M
    |__ Port 11: Dev 4, If 1, Class=Human Interface Device, Driver=usbhid, 1.5M
    |__ Port 12: Dev 5, If 0, Class=Human Interface Device, Driver=usbhid, 1.5M

$  lsblk
NAME   MAJ:MIN RM   SIZE RO TYPE MOUNTPOINT
loop1    7:1    0 202.9M  1 loop /snap/vlc/1049
sdd      8:48   1  59.7G  0 disk                                  //새로 연결된 SD Card 확인  
└─sdd1   8:49   1  59.7G  0 part 
loop4    7:4    0    89M  1 loop /snap/core/7713
loop2    7:2    0 202.3M  1 loop /snap/vlc/770
loop0    7:0    0 184.8M  1 loop /snap/eclipse/40
sda      8:0    0   2.7T  0 disk 
├─sda4   8:4    0   2.7T  0 part /
├─sda2   8:2    0   513M  0 part /boot/efi
├─sda3   8:3    0   976M  0 part [SWAP]
└─sda1   8:1    0     1M  0 part 
loop5    7:5    0 192.1M  1 loop /snap/eclipse/29
loop3    7:3    0  88.7M  1 loop /snap/core/7396


  • SD Card Image Write (Linux)

##  4M 가 잘 동작이 안된다면, 1M로 설정을 변경해서 하자 
$ unzip -p jetson-nano-sd-r32.2.1.zip | sudo dd of=/dev/sdd bs=4M conv=fsync
or 
$ sudo dd bs=4M if=sd-blob-b01.img of=/dev/sdd status=progress conv=fsync

출처
  https://www.raspberrypi.org/documentation/installation/installing-images/linux.md
Linux에서도 Etcher가 가능(미테스트)
  https://developer.nvidia.com/embedded/learn/get-started-jetson-nano-devkit#write

Write 한 후 Jetson Nano에 연결한 후 HDMI와 USB Keyboard/Mouse로 연결하여 Ubuntu 초기설정을 진행하고, ID/PW를 만들자

이것으로 설정을 하니, DeepStream이 기본으로 설정이 되어있지 않으므로, 가능하다면, SDKmanager로 설치를 해보도록하거나, 별도로 DeepStream을 설치를 진행하자


  • DeepStream 별도설치가능 
  https://developer.nvidia.com/deepstream-download


2.2  SDKmanager 기반 설치 

기존에 SDK Manager를 설치를 했다면, Upgrade를 진행하여, 설치가 쉬우며, 아래와 같이 선택만 하면된다
다만 Jetson Series Board가 동일하게 SDK Manager로 동작할 때는 반드시 상위 Force Recovery Mode를 숙지를 해야한다.



JetPack 4.2.2 부터는 Target SDK에  Tensorflow도 새롭게 추가되어 기본으로 들어가있음



  • Host에서 USB를 통하여 Jetson OS Write
  1. STEP에서 Download를 다 진행된 후 아래의 Message가 확인
  2. Jetson Nano와 Micro B USB로 연결 확인 
  3. Manual로 Setup 설정할 경우 Force Recovery Mode로 진입 (Auto Setup일 경우 필요없음)
  4. Username/Password 설정
  5. Flash 진행 
    Target Components 중 Jetson OS만 Flash 진행 



  • Jetson OS Flash 종료 후 SSH Message 확인
  1. Flash 도중에 SSH에 연결하는 Message 확인 (Ubuntu 초기화해야함)
  2. HDMI로 연결된 화면과 USB Keyboard/Mouse를 이용하여 Ubuntu 초기화설정
  3. Username/Password 설정 후 자동 Reboot 후 SSH Server동작 
  4. 설정된 Username/Password로 SSH 접속 후 Package 설치 진행  

   Target Components 중 Jetson SDK 설치진행 (SSH이용) 


SSH로 연결하여, 나머지 추가적인 Package를 설치하는데 시간이 많이 걸리며, 아래와 같이 완료



3. SD Card 용량 제한사항 

이미 Partition Size가 이미 정해져 있다보니, 용량이 부족한 현상이 발생하며, 아래의 명령으로 늘리는 것이 가능하지만, flash.sh 의 명령의 경우 14G로 제한이 있다고 한다.
(flash명령을 아직 테스트를 해보지 않음)

flash.sh 사용법은 이전의 JetPack을 참조 (Jetson TX2와 거의 동일)
  https://developer.download.nvidia.com/embedded/L4T/r27_Release_v1.0/Docs/Jetson_X2_Developer_Kit_User_Guide.pdf?WG5M_7R8_PzZIb_pcScGrQ3imdfQKUv-NmkuOrUe6RaL5Oz5n0Z7fKm_GZnY1rpumVTduYDvxD3f2QxlUlzcIo3XEjN_nunH-4UGSicgdfo7hAvy3A3KtYivCggScKJr8Ho23QJnhy-VubjOb7D4BGbKL1-Rw5DDs3cIBkKztH91RD9e116atw

용량을 증량하려면, 아래와 같이 직접 SD Card Image를 만들고, 이를 구운다음 DeepStream SDK 별도 설치진행하거나 상위 sdkmanger를 이용하여 설치를 진행하자


  • Host PC에서 직접 본인의 SD Card Image 생성 
$ cd ~/nvidia/nvidia_sdk/JetPack_4.2.2_Linux_GA_P3448/Linux_for_Tegra 

$ ls  // 기존과 다르게 create_jetson_nano_sd-card-image 존재 
create-jetson-nano-sd-card-image.sh   flash.sh  .. 중략

## 16G 설정하면, 최대값으로 설정됨
$ sudo ./create-jetson-nano-sd-card-image.sh -o sd-blob.img -s 16G -r 100
$ sudo dd bs=4M if=sd-blob.img of=/dev/sdd status=progress conv=fsync

상위명령으로 생성된 Image는 기본적인 Jetson OS만 생성하며, Jetson SDK Components를 포함하고 있지 않다.
sdkmanager를 이용하거나 본인 별도로 각각의 Jetson SDK를 설치를 해야한다


  • SDKManager 이용할 경우
상위 Jetson OS Write 부분을 skip명령이 존재하기 때문에, 이부분을 제외하고 SSH로 SDK부분을 설치진행하면된다

출처
  https://devtalk.nvidia.com/default/topic/1050105/jetson-nano/jetson-nano-sd-card-partitions-can-not-extend-/
  https://devtalk.nvidia.com/default/topic/1050105/jetson-nano/jetson-nano-sd-card-partitions-can-not-extend-/


9/10/2019

Anacoda 관련사용법 및 virtual 환경 자료수집

1. Python 의 기본내용

최근 리눅스에서 Machine/Deep Learning하면서 conda를 많이 사용하는데, conda와 pip로 분리되어 각각의 Package를 관리되는 것을 비교해보자.

  • Python 기본설명 및 용어
Python에 관련된 기본설명과 용어들을 알아두자 (Python2는 항상 호환문제로 사용)

  • Python 자료모음 및 관련링크 
Python Programing 할때 문법을 비롯하여, 각 예제 및 사용법에 대한 링크연결 


1.1  Anaconda download 및 설치 

두가지 배포판으로 제공해주고 있으며, 아래의 사이트에서 쉽게 Download 및 설치가 가능하다

  1. Python 3.7 Version : Anaconda3.date.OS.Arch
  2. Python 2.7 Version:  Anaconda2.date.OS.Arch

Anaconda Download
  https://www.anaconda.com/distribution/#download-section
  https://repo.anaconda.com/archive/

Anacoda의 사이트 및 소개
  https://www.anaconda.com/

  • Anaconda 설치방법 (Ubuntu 기반)
아래와 같이 쉽게 설치가 가능하며, 설치된 후에는 쉽게 관리가능
$ cd /tmp

$ curl -O https://repo.anaconda.com/archive/Anaconda3-2019.03-Linux-x86_64.sh        // Python 3.7 

$ sha256sum Anaconda3-2019.03-Linux-x86_64.sh    // checksum 확인 

$ bash Anaconda3-2019.03-Linux-x86_64.sh // 설치진행 

$ conda list // 설치 후 package 확인 

출처: Anaconda 설치 방법
  https://www.digitalocean.com/community/tutorials/how-to-install-anaconda-on-ubuntu-18-04-quickstart


1.2 conda 와 pip 기본비교 

Machine Learning 을 위한 python virtual 환경을 찾다가 대부분 Anaconda로 사용해서 이를 구성을 하는 것 같은데, 이에 대해 좀 더 자세히 알고자 한다.
Anaconda는 일반적으로 conda도 package를 관리하며,  pip인 python의 package 방식과 유사하지만, 다른부분이 많아 이를 비교한다. 


  • Conda 와 pip 의 기본비교 
관련부터 conda는 bin / pip는 wheel or source 이며, conda가 아래를 봐도 다양하게 지원가능하다
virtulenv는 pip는 별도로 설치를 해야하지만, conda는 내부 내장이며, 압도적으로 성능 좋을 것으로 보인다. 

https://www.anaconda.com/blog/understanding-conda-and-pip



  • Tensorflow을 위한 pip 와 anaconda 성능비교 
python package tool인 pip와 conda의 성능비교라고 하며, 성능차이가 나는데 정확한 원인은 anaconda를 좀 더 알아봐야 할 것 같지만,
상위 비교를 보면 대충은 이해는 가는 것 같다. 

https://www.anaconda.com/blog/tensorflow-in-anaconda

  • conda의 기능과 역할 
현재 대체적으로 Mahcine Learning 기반에서 많이 사용되어지며, 내부 Package 역시 Machine Learning에 꼭 필요한 Package 구성이다.
그래서 Tensorflow의 경우 반드시 필요 


1.3  conda 와 pip 사용법 비교 

python의 pip 처럼 conda를 기반으로 설치가 가능하며, 본인이 원하는 환경을 구성하여 각각의 version을 설정도 가능하다고 한다.

  • conda / pip package 기본관리 
python의 pip 와 거의 동일하게 package를 설치/제거/찾기

$ conda install package   // pip install 
$ conda remove package    // pip uninstall 
$ conda list              // pip list 
$ conda search package    // pip search 


  • conda 의 일반 명령어  
conda의 세부명령어들은 아래사이트에서 참조 
  https://docs.conda.io/projects/conda/en/latest/commands.html#conda-general-commands


  • conda 와 pip 의 동작비교 


Task

Conda package and environment manager command

Pip package manager command

Virtualenv environment manager command

Install a package

conda install $PACKAGE_NAME

pip install $PACKAGE_NAME

X

Update a package

conda update --name $ENVIRONMENT_NAME $PACKAGE_NAME

pip install --upgrade $PACKAGE_NAME

X

Update package manager

conda update conda

Linux/macOS: pip install -U pip Win: python -m pip install -U pip

X

Uninstall a package

conda remove --name $ENVIRONMENT_NAME $PACKAGE_NAME

pip uninstall $PACKAGE_NAME

X

Create an environment

conda create --name $ENVIRONMENT_NAME python

X

cd $ENV_BASE_DIR; virtualenv $ENVIRONMENT_NAME

Activate an environment

conda activate $ENVIRONMENT_NAME*

X

source $ENV_BASE_DIR/$ENVIRONMENT_NAME/bin/activate

Deactivate an environment

conda deactivate

X

deactivate

Search available packages

conda search $SEARCH_TERM

pip search $SEARCH_TERM

X

Install package from specific source

conda install --channel $URL $PACKAGE_NAME

pip install --index-url $URL $PACKAGE_NAME

X

List installed packages

conda list --name $ENVIRONMENT_NAME

pip list

X

Create requirements file

conda list --export

pip freeze

X

List all environments

conda info --envs

X

Install virtualenv wrapper, then lsvirtualenv

Install other package manager

conda install pip

pip install conda

X

Install Python

conda install python=x.x

X

X

Update Python

conda update python*

X

X

conda activate only works on conda 4.6 and later versions. For conda versions prior to 4.6, type:

출처:conda의 관련 command 및 pip와 비교 


1.4 conda 설치후 Tensorflow 관련설정

conda 설치 진행후 tensorflow를 사용하기 위해서 관련부분 설치 진행 
  • 나만의 환경구성 
$ conda list | grep python 
ipython                   7.6.1            py37h39e3cac_0  
ipython_genutils          0.2.0                    py37_0  
msgpack-python            0.6.1            py37hfd86e86_1  
python                    3.7.3                h0371630_0  
python-dateutil           2.8.0                    py37_0  
python-libarchive-c       2.8                     py37_11  

$ conda create --name my_env python=3
$ conda activate my_env


$ conda create -n tensorflow_gpuenv tensorflow-gpu    // tensorflow_gpuenv 환경 만들고 필요한 package를 tensorflow-gpu 설정 
$ conda activate tensorflow_gpuenv   // 나만의 환경설정으로 시작 

출처
  https://www.anaconda.com/tensorflow-in-anaconda/

  • TensorBoard 기본사용법
Tensorflow의 Tensorboard 사용법들 관련링크 중요 
  https://medium.com/trackin-datalabs/tensorboard-tensorboard-%EA%B8%B0%EC%B4%88-6163e1642bb0
  https://medium.com/trackin-datalabs/tensorboard-%EA%B0%84%EB%8B%A8%ED%9E%88-%EC%8B%9C%EC%9E%91%ED%95%98%EA%B8%B0-18a4fda2efb1
  https://pinkwink.kr/1086
  https://pythonkim.tistory.com/39


1.5 Pycharm 기반의 Conda 관련내용 링크 


Pycharm 기반의 Conda
  https://www.anaconda.com/pycharm
  https://itsfoss.com/install-pycharm-ubuntu/
  https://itsfoss.com/install-pycharm-ubuntu/



2. Python 의 가상환경 구성 및 관리방법 

Anaconda 를 비롯하여 여러가지 Virtual 환경을 제공을 하고 있으며, Python의 Package들을 독립적으로 Virtual 환경마다 관리가 가능하기때문에 편리하다.
최근에는 주로 사용하는 Vritual 환경은 pip 기반의 virtualenv/venv 와 conda 기반으로 사용하며, 각각의 기능을 알아보자.
venv와 conda 중심으로 알아가면될 것 같다. 

  • Python 다양한 Virtual 환경 비교
pyenv/virtualenv/venv/conda 비교 및 기본이해
  https://wikidocs.net/16402



  • Python Package는 전체관리와 가상환경관리로 구분 
  1. 전체 관련 Package 관리 
  2. 각 독자 가상환경 구성 후 아래의 명령어로 Package 관리 

  • pip 기반의 package 관리(venv 적용후 관리해도됨)
$ pip install -r requirement.txt
$ pip freeze > requirement.txt 

  • conda 기반의 package 관리(가상환경기반으로해도됨)
$ conda install --file packagelist.txt
$ conda list --export > packagelist.txt 


각 가상환경 기본 사용법 (venv/conda)

  • python3의 venv 설정(가상환경) 및 기본 사용법
$ python3 -m venv 설정이름   // create venv  , virtualenv도 거의 동일
$ source 설정이름/bin/activate   // active 가상환경가동 in Linux
>  설정이름/bin/activate.bat   // active in Window
>  설정이름/bin/activate.ps    // active in Window
$ deactivate     // decativate 가상환경에서 빠져나오기 

  • conda 의 가상환경 및 기본사용법
$ conda create -n 설정이름
$ source activate 설정이름
$ source deactivate


Python 의 pip 와 virtualenv 소개 
  https://medium.com/@dan_kim/%ED%8C%8C%EC%9D%B4%EC%8D%AC-%EC%B4%88%EC%8B%AC%EC%9E%90%EB%A5%BC-%EC%9C%84%ED%95%9C-pip-%EA%B7%B8%EB%A6%AC%EA%B3%A0-virtualenv-%EC%86%8C%EA%B0%9C-a53512fab3c2

Python virtualenv(Python2) 와 venv (Python3) 사용법 (조금다르나 거의 동일) 

Python Virtualenv 관련내용 

9/06/2019

TensorRT 5 Python uff_custom_plugin 분석

1. TensorRT Python Custom Layer 분석 

우선 TensorRT Python UFF Custom Layer Source를 이해하기전에 아래의 소스들을 기본적으로 이해를 하고 오는 것이 좋을 것 같다

  • TensorRT Python Code 분석 
TensorRT 5 MNIST 분석
  https://ahyuo79.blogspot.com/2019/09/tensorrt-5-python-tensorflow-mnist.html
TensorRT 5 기본구조
  https://ahyuo79.blogspot.com/2019/08/tensorrt-5-python.html


아래사항은 Tensorflow와 TensorRT 설치가이드이며 관련사항이므로 참고하자
  • Tensorflow 와 TensorRT Package
  https://docs.nvidia.com/deeplearning/frameworks/install-tf-jetson-platform/index.html
  https://docs.nvidia.com/deeplearning/sdk/tensorrt-install-guide/index.html#installing


1.1 TensorRT 소스구조 파악 과 실행 

현재 모든 Manual이 X86기반으로 되어 있으므로, ARM Version이 맞게 이를 변경하고 관련된 Package를 설치하여 아래의 소스를 실행해보자



  • UFF_Custom_PlugIn의 README 정보 
아래의 정보는 어떻게 빌드를 해야할 지 관련된  정보와 실행방법을 알려준다
  https://docs.nvidia.com/deeplearning/sdk/tensorrt-sample-support-guide/index.html#uff_custom_plugin


  • UFF Custom PlugIn 소스구조 및 필요사항 확인 

$ cd /usr/src/tensorrt/samples/python/uff_custom_plugin
$ tree -t
.
├── CMakeLists.txt       // cmake 필요 
├── __init__.py
├── lenet5.py
├── README.md            // 반드시 참조 
├── requirements.txt
├── sample.py
└── plugin
    ├── clipKernel.cu         // CUDA Complier 필요 
    ├── clipKernel.h
    ├── customClipPlugin.cpp  // GCC->G++ Compiler 필요 
    └── customClipPlugin.h

$ cat requirements.txt
Pillow
pycuda
numpy
tensorflow


  • C++ PlugiN 빌드를 위해 cmake 설정 변경 
 cmake의 설정을 아래와 같이 x86->ARM 기반으로 변경하고 필요 Package 설치

$ vi CMakeLists.txt
# We need cmake >= 3.8, since 3.8 introduced CUDA as a first class language
cmake_minimum_required(VERSION 3.8 FATAL_ERROR)
project(ClipPlugin LANGUAGES CXX CUDA)

# Enable all compile warnings
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wno-long-long -pedantic -Werror")

# Sets variable to a value if variable is unset.
macro(set_ifndef var val)
    if (NOT ${var})
        set(${var} ${val})
    endif()
    message(STATUS "Configurable variable ${var} set to ${${var}}")
endmacro()

# -------- CONFIGURATION --------
##set_ifndef(TRT_LIB /usr/lib/x86_64-linux-gnu)
##set_ifndef(TRT_INCLUDE /usr/include/x86_64-linux-gnu)
set_ifndef(TRT_LIB /usr/lib/aarch64-linux-gnu/)
set_ifndef(TRT_INCLUDE /usr/include/aarch64-linux-gnu/ )

# Find dependencies:
message("\nThe following variables are derived from the values of the previous variables unless provided explicitly:\n")

# TensorRT's nvinfer lib
find_library(_NVINFER_LIB nvinfer HINTS ${TRT_LIB} PATH_SUFFIXES lib lib64)
set_ifndef(NVINFER_LIB ${_NVINFER_LIB})

# -------- BUILDING --------

# Add include directories
include_directories(${CUDA_INC_DIR} ${TRT_INCLUDE} ${CMAKE_SOURCE_DIR}/plugin/)

# Define clip plugin library target
add_library(clipplugin MODULE
  ${CMAKE_SOURCE_DIR}/plugin/clipKernel.cu
  ${CMAKE_SOURCE_DIR}/plugin/customClipPlugin.cpp
  ${CMAKE_SOURCE_DIR}/plugin/clipKernel.h
  ${CMAKE_SOURCE_DIR}/plugin/customClipPlugin.h
)

# Use C++11
target_compile_features(clipplugin PUBLIC cxx_std_11)

# Link TensorRT's nvinfer lib
target_link_libraries(clipplugin PRIVATE ${NVINFER_LIB})

# We need to explicitly state that we need all CUDA files
# to be built with -dc as the member functions will be called by
# other libraries and executables (in our case, Python inference scripts)
set_target_properties(clipplugin PROPERTIES
  CUDA_SEPARABLE_COMPILATION ON
)


$ find / -name pybind* 2> /dev/null
$ find / -name cuda* 2> /dev/null
$ find / -name libnvinfer.so 2> /dev/null
$ find / -name NvInfer.h 2> /dev/null  or find / -name NvUffParser.h 2> /dev/null
$ which cmake 

//필요사항 cmake, pybind11 필요 
$ sudo apt install cmake
$ sudo pip3 install pybind11  


  • C++ PlugIn Build 진행 
cmake를 실행하면 CUDACXX, 즉 CUDA Compiler를 못찾기때문에 아래와 같이 추가하고 빌드진행
$ echo $PATH
/home/nvidia/.local/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

// cmake 실행시 CUDACXX 를 /etc/environment 넣거나, CMAKE_CUDA_COMPILER=/usr/local/cuda-10.0/bin/nvcc  설정진행 
$ sudo vi /etc/environment 
PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games"
CUDACXX=/usr/local/cuda-10.0/bin/nvcc 

$ sudo mkdir build && pushd build
$ sudo cmake ..   
or 
//X86기반 설정을 ARM으로 현재사항에 맞게변경 
$ sudo cmake .. \
  -DPYBIND11_DIR=/usr/local/include/python3.6/pybind11/ \
  -DCUDA_ROOT=/usr/local/cuda-10.0/ \
  -DPYTHON3_INC_DIR=/usr/include/python3.6/ \
  -DNVINFER_LIB=/usr/lib/aarch64-linux-gnu/libnvinfer.so \
  -DTRT_INC_DIR=/usr/include/aarch64-linux-gnu/ \
  -DTRT_LIB=/usr/lib/aarch64-linux-gnu/

$ sudo make -j
Scanning dependencies of target clipplugin
[ 50%] Building CXX object CMakeFiles/clipplugin.dir/plugin/customClipPlugin.cpp.o
[ 50%] Building CUDA object CMakeFiles/clipplugin.dir/plugin/clipKernel.cu.o
[ 75%] Linking CUDA device code CMakeFiles/clipplugin.dir/cmake_device_link.o
[100%] Linking CXX shared module libclipplugin.so
[100%] Built target clipplugin

$ popd 

빌드관련 설정사항 (CUDACXX)
  https://github.com/jetsonhacks/buildLibrealsense2TX/issues/13



  • MNIST Training과 TEST 진행 후 모델로 저장 
총 10번의 Training을 실행하며 Progress Bar로 이를 표시 해주고 마지막에 TEST진행한 후 Model로 저장

$ sudo python3 lenet5.py
......
Epoch 1/10
2019-09-06 15:23:04.843040: I tensorflow/stream_executor/platform/default/dso_loader.cc:42] Successfully opened dynamic library libcublas.so.10.0
60000/60000 [==============================] - 8s 139us/sample - loss: 0.2036 - acc: 0.9403
Epoch 2/10
60000/60000 [==============================] - 8s 126us/sample - loss: 0.0816 - acc: 0.9756
Epoch 3/10
60000/60000 [==============================] - 7s 124us/sample - loss: 0.0525 - acc: 0.9839
Epoch 4/10
60000/60000 [==============================] - 7s 124us/sample - loss: 0.0375 - acc: 0.9879
Epoch 5/10
60000/60000 [==============================] - 7s 122us/sample - loss: 0.0277 - acc: 0.9915
Epoch 6/10
60000/60000 [==============================] - 7s 122us/sample - loss: 0.0223 - acc: 0.9928
Epoch 7/10
60000/60000 [==============================] - 7s 122us/sample - loss: 0.0158 - acc: 0.9948
Epoch 8/10
60000/60000 [==============================] - 7s 123us/sample - loss: 0.0152 - acc: 0.9949
Epoch 9/10
60000/60000 [==============================] - 7s 122us/sample - loss: 0.0104 - acc: 0.9969
Epoch 10/10
60000/60000 [==============================] - 7s 123us/sample - loss: 0.0119 - acc: 0.9961
10000/10000 [==============================] - 1s 73us/sample - loss: 0.0810 - acc: 0.9800
Test loss: 0.08098314766188651
Test accuracy: 0.9800000190734863
W0906 15:24:20.296670 547944542224
............................



  • 생성된 모델을 이용하여 예측 테스트 

생성된 모델을 읽어서 Custom Layer를 적용후 Test Case와 예측 Case를 비교

$ sudo python3 sample.py
......
UFF Version 0.6.3
=== Automatically deduced input nodes ===
[name: "InputLayer"
op: "Placeholder"
attr {
  key: "dtype"
  value {
    type: DT_FLOAT
  }
}
attr {
  key: "shape"
  value {
    shape {
      dim {
        size: -1
      }
      dim {
        size: 1
      }
      dim {
        size: 28
      }
      dim {
        size: 28
      }
    }
  }
}
]
=========================================

Using output node OutputLayer/Softmax
Converting to UFF graph
Warning: No conversion function registered for layer: CustomClipPlugin yet.
Converting trt_relu6 as custom op: CustomClipPlugin
W0906 15:26:19.200886 548537745424 deprecation_wrapper.py:119] From /usr/lib/python3.6/dist-packages/uff/converters/tensorflow/converter.py:179: The name tf.AttrValue is deprecated. Please use tf.compat.v1.AttrValue instead.

DEBUG: convert reshape to flatten node
No. nodes: 13
UFF Output written to /usr/src/tensorrt/samples/python/uff_custom_plugin/models/trained_lenet5.uff
UFF Text Output written to /usr/src/tensorrt/samples/python/uff_custom_plugin/models/trained_lenet5.pbtxt

=== Testing ===
Loading Test Case: 6
Prediction: 6

$ ls models   // models directory 생성되었으며, pb/uff 파일 생성 
trained_lenet5.pb  trained_lenet5.pbtxt  trained_lenet5.uff


2. TensorRT Python Custom Layer(C++) 소스 

왜 Tensorflow의 Layer 중 TensorRT로 사용시 제약사항있어 Custom Layer를 사용할까?
그리고, 언제 사용되는지에 대해 구체적으로 알고 싶었다.
우선 아래의 소스는 TensorRT가 ReLU6를 미지원해서 이를 Custom Layer를 재정의하여 사용하는 구조이다



  • ReLU/ReLU6 의 차이 
Activation Function 이라고 하며 Tensorflow는 두 개의 함수를 지원을 해주고 있으며, 두개의 함수의 차이설명

  1. Relu      y = max(x ,0)                그래프, x좌표가 0기반으로 y좌표 증가 
  2. Relu6    y = min(max(x, 0), 6)   그래프, x좌표가 0기반으로 y좌표 증가 x좌표의 6까지 한계

  https://woolulu.tistory.com/84
  https://www.tensorflow.org/api_docs/python/tf/nn/relu
  https://www.tensorflow.org/api_docs/python/tf/nn/relu6
  http://www.cs.utoronto.ca/~kriz/conv-cifar10-aug2010.pdf


  • Tensorflow와 TensorRT 제약사항
아래의 사이트를 여러번 봤지만 세부적으로 좀 더 자세히 분석해보자
우선 TensorRT의 제약사항을 아래에서 확인가능하다
그리고, 두번째 Site에서 Tensorflow의 UFF Parser 지원사항을 보면  ReLU6 지원가능을 볼수 있다
이렇게 보면 TensorRT도 지원가능할 것 같지만, 이것은 UFF Parser까지만 인식가능가능하다는 말이다

  https://docs.nvidia.com/deeplearning/sdk/tensorrt-support-matrix/index.html
  https://docs.nvidia.com/deeplearning/sdk/tensorrt-support-matrix/index.html#supported-ops
  UFF에서 인식가능
  https://docs.nvidia.com/deeplearning/sdk/tensorrt-api/python_api/uff/Operators.html?highlight=relu#activation

  • TensorRT API C++/Python 확인 (Activition Layer에서 ReLu만 지원)
이제 실제 TensorRT의 Layer에서 직접 찾아보면, ReLu만 지원가능하고 ReLu6는 찾을 수 없다
Python의 API를 보면 더 쉽다

  https://docs.nvidia.com/deeplearning/sdk/tensorrt-developer-guide/index.html#layers
  https://docs.nvidia.com/deeplearning/sdk/tensorrt-api/c_api/classnvinfer1_1_1_i_activation_layer.html
  https://docs.nvidia.com/deeplearning/sdk/tensorrt-api/c_api/namespacenvinfer1.html#acbd177748000d30ae0277ee980757eb6
  https://docs.nvidia.com/deeplearning/sdk/tensorrt-api/python_api/infer/Graph/Layers.html#iactivationlayer


  • TensorRT로 Network 직접 구성할때 Relu 예제참조 (C++/Python) 
이제 실제 TensorRT로 ReLu을 구성할때를 살펴보자

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


  • DLA의 ReLu 제약사항 (참고사항, Xavier만 해당)
  https://docs.nvidia.com/deeplearning/sdk/tensorrt-developer-guide/index.html#dla_layers


2.1 lenet5.py 분석 
기본처럼 MNIST DataSet를 Download하여 Training과 TEST를 진행한 후 결과를 Model 생성하여, 즉 PB파일로 저장하는 구조이다
이전 소스와 거의 유사하지만 아래의 build_model시 network 구조가 다르다고 보면된다

$ cat lenet5.py 
import tensorflow as tf
import numpy as np
import os

WORKING_DIR = os.environ.get("TRT_WORKING_DIR") or os.path.dirname(os.path.realpath(__file__))

## 생성될 Model의 저장장소 
MODEL_DIR = os.path.join(
    WORKING_DIR,
    'models'
)

## Google mnist data를 download하여 ReShape 진행 
def load_data():
    mnist = tf.keras.datasets.mnist
    (x_train, y_train),(x_test, y_test) = mnist.load_data()
    x_train, x_test = x_train / 255.0, x_test / 255.0

    ## 기존과 다르게 reshape를 하여, 1차원을 추가하는데, -1 값이 60000 과 10000로 정의되는 것 같음 
    ## https://docs.scipy.org/doc/numpy/reference/generated/numpy.reshape.html#numpy.reshape
    x_train = np.reshape(x_train, (-1, 1, 28, 28))
    x_test = np.reshape(x_test, (-1, 1, 28, 28))
    return x_train, y_train, x_test, y_test

## Tensorflow Keras를 이용하여 Model의 Network 정의 
def build_model():
    # Create the keras model
    model = tf.keras.models.Sequential()
    model.add(tf.keras.layers.InputLayer(input_shape=[1, 28, 28], name="InputLayer"))
    model.add(tf.keras.layers.Flatten())
    model.add(tf.keras.layers.Dense(512))
    ## ReLU6 부분이 TensorRT로 Custom Layer로 재정의 될 부분이다 
    ## TensorRT는 ReLU6을 지원하지 못하므로, 이 부분을 C++로 PlugIn으로 재정의하고 Custom Layer로 구현한다
    ## https://devtalk.nvidia.com/default/topic/1037149/tensorrt/relu6-not-supported-in-tensorrt-for-mobilenetv2/   
    model.add(tf.keras.layers.Activation(activation=tf.nn.relu6, name="ReLU6"))
    model.add(tf.keras.layers.Dense(10, activation=tf.nn.softmax, name="OutputLayer"))
    return model

## Model의 Build 부터 설정 부터 Training 
def train_model():

    ## 기본 Network 정의 
    # Build and compile model
    model = build_model()
    model.compile(optimizer='adam',
                  loss='sparse_categorical_crossentropy',
                  metrics=['accuracy'])

    ## Model의 TESTSET의 Download와 설정변경 
    # Load data
    x_train, y_train, x_test, y_test = load_data()

    ## 실제 Training 할 숫자 정의, epochs  , verbose는 1은 Prograss bar 설정  
    # Train the model on the data
    model.fit(
        x_train, y_train,
        epochs = 10,
        verbose = 1
    )

    ## 실제 Training 과 TEST 진행 
    # Evaluate the model on test data
    test_loss, test_acc = model.evaluate(x_test, y_test)
    print("Test loss: {}\nTest accuracy: {}".format(test_loss, test_acc))

    return model

## Save Model를 위해서 Mkdir 
def maybe_mkdir(dir_path):
    if not os.path.exists(dir_path):
        os.makedirs(dir_path)


## Training/Test가 된 model을 freezing 하여 저장 
def save_model(model):
    output_names = model.output.op.name
    sess = tf.keras.backend.get_session()

    graphdef = sess.graph.as_graph_def()
    frozen_graph = tf.graph_util.convert_variables_to_constants(sess, graphdef, [output_names])
    frozen_graph = tf.graph_util.remove_training_nodes(frozen_graph)

    # Make directory to save model in if it doesn't exist already
    maybe_mkdir(MODEL_DIR)

    model_path = os.path.join(MODEL_DIR, "trained_lenet5.pb")
    with open(model_path, "wb") as ofile:
        ofile.write(frozen_graph.SerializeToString())


if __name__ == "__main__":
    ## Model 생성부터 Training/Test 관련 설정 과 Training/Test 진행 
    model = train_model()

    ## 생성된 Model 저장 
    save_model(model)



  • Tensorflow Keras model 관련함수
Tensorflow의 keras 관련 함수는 아래에서 찾자
  https://www.tensorflow.org/api_docs/python/tf/keras/Sequential


2.2 sample.py 분석 
기존소스 Sample Source와 거의 유사하지만 C++의 IPlugInv2와 사용하여 TensorRT의 Layer를 재정의하고, Custom Layer를 C++로 구현된것을 연결한다.


$ cat sample.py 
import sys
import os
import ctypes
from random import randint

from PIL import Image
import numpy as np
import tensorflow as tf

import pycuda.driver as cuda
import pycuda.autoinit

import tensorrt as trt
import graphsurgeon as gs
import uff

# ../common.py
sys.path.insert(1,
    os.path.join(
        os.path.dirname(os.path.realpath(__file__)),
        os.pardir
    )
)
import common

# lenet5.py
import lenet5


# MNIST dataset metadata
MNIST_IMAGE_SIZE = 28
MNIST_CHANNELS = 1
MNIST_CLASSES = 10

WORKING_DIR = os.environ.get("TRT_WORKING_DIR") or os.path.dirname(os.path.realpath(__file__))

# Path where clip plugin library will be built (check README.md)
CLIP_PLUGIN_LIBRARY = os.path.join(
    WORKING_DIR,
    'build/libclipplugin.so'
)

# Path to which trained model will be saved (check README.md)
MODEL_PATH = os.path.join(
    WORKING_DIR,
    'models/trained_lenet5.pb'
)

# Define global logger object (it should be a singleton,
# available for TensorRT from anywhere in code).
# You can set the logger severity higher to suppress messages
# (or lower to display more messages)
TRT_LOGGER = trt.Logger(trt.Logger.WARNING)

## 기본설정된 정의는 이곳에서 모두 확인 
# Define some global constants about the model.
class ModelData(object):
    INPUT_NAME = "InputLayer"
    INPUT_SHAPE = (MNIST_CHANNELS, MNIST_IMAGE_SIZE, MNIST_IMAGE_SIZE)
    RELU6_NAME = "ReLU6"
    OUTPUT_NAME = "OutputLayer/Softmax"
    OUTPUT_SHAPE = (MNIST_IMAGE_SIZE, )
    DATA_TYPE = trt.float32

## TensorRT의 Custom Layer 연결부분 (핵심)
# Generates mappings from unsupported TensorFlow operations to TensorRT plugins
def prepare_namespace_plugin_map():
    # In this sample, the only operation that is not supported by TensorRT
    # is tf.nn.relu6, so we create a new node which will tell UffParser which
    # plugin to run and with which arguments in place of tf.nn.relu6.


    ## Model의 ReLU6 -> trt_relu6 Layer 재정의하고 , 이 부분을  C++에서 Custom Layer에 연결구현 
    ## customClipPlugin.cpp의 CustomClipPlugin 연결하며,  clipMin ,clipMax Argument로 설정 Plugin의 Arg
    ## IPluginV2 C++ Class 의 Override로 구현(customClipPlugin.cpp)하고 있으며, 아래의 TensorRT C++ 참조 
    ## https://docs.nvidia.com/deeplearning/sdk/tensorrt-api/c_api/classnvinfer1_1_1_i_plugin_v2.html
    ## python API- create_plugin_node
    ## https://docs.nvidia.com/deeplearning/sdk/tensorrt-api/python_api/graphsurgeon/graphsurgeon.html
    ## https://docs.nvidia.com/deeplearning/sdk/tensorrt-developer-guide/index.html#graphsurgeon
    ## 상위 C++ 소스는 iPlugInv2를 사용하며, 이로 재정의한다 
    ## iPlugIn도 Python API가 있는데, C++ 예제만 있으니, 이소스의 한계는 여기까지이다. 
    ## clipMax/clipMin 값은 상위 Relu/Relu6의 차이를 보면될 것 같다  
    # The "clipMin" and "clipMax" fields of this TensorFlow node will be parsed by createPlugin,
    # and used to create a CustomClipPlugin with the appropriate parameters.
    trt_relu6 = gs.create_plugin_node(name="trt_relu6", op="CustomClipPlugin", clipMin=0.0, clipMax=6.0)
    namespace_plugin_map = {
        ModelData.RELU6_NAME: trt_relu6
    }
    return namespace_plugin_map

## PB파일이름을 UFF파일으로 변경 
# Transforms model path to uff path (e.g. /a/b/c/d.pb -> /a/b/c/d.uff)
def model_path_to_uff_path(model_path):
    uff_path = os.path.splitext(model_path)[0] + ".uff"
    return uff_path

## PB파일을 UFF로 변경하는 것으로 PB파일과 PlugIN 정보
# Converts the TensorFlow frozen graphdef to UFF format using the UFF converter
def model_to_uff(model_path):
     
    ## Model의 Layer 중 ReLU6 이 부분을 C++ PlugIn으로 연결하고 재정의 
    # Transform graph using graphsurgeon to map unsupported TensorFlow
    # operations to appropriate TensorRT custom layer plugins
    dynamic_graph = gs.DynamicGraph(model_path)
    dynamic_graph.collapse_namespaces(prepare_namespace_plugin_map())

    ## UFF File로 저장 
    # Save resulting graph to UFF file
    output_uff_path = model_path_to_uff_path(model_path)
    uff.from_tensorflow(
        dynamic_graph.as_graph_def(),
        [ModelData.OUTPUT_NAME],
        output_filename=output_uff_path,
        text=True
    )
    return output_uff_path

## Build TensorRT Engine이며, Pb파일을 UFF로 변경하고 이를 기준으로 정의 
# Builds TensorRT Engine
def build_engine(model_path):
    with trt.Builder(TRT_LOGGER) as builder, builder.create_network() as network, trt.UffParser() as parser:
        builder.max_workspace_size = common.GiB(1)
      
        ## PB를 UFF로 변경 상위 함수 호출 
        uff_path = model_to_uff(model_path)

        ## INPUT_SHAPE 1,28,28로 설정 
        parser.register_input(ModelData.INPUT_NAME, ModelData.INPUT_SHAPE)
        parser.register_output(ModelData.OUTPUT_NAME)
        parser.parse(uff_path, network)
        
        ## TensorRT Engine 생성 
        return builder.build_cuda_engine(network)

## pagelocked_buffer는 Host(CPU) input buffer에 Data 저장 하고 random으로 test case를 설정하고 이 결과를 반환 
# Loads a test case into the provided pagelocked_buffer. Returns loaded test case label.
def load_normalized_test_case(pagelocked_buffer):
    _, _, x_test, y_test = lenet5.load_data()
    num_test = len(x_test)
    case_num = randint(0, num_test-1)
    img = x_test[case_num].ravel()
    np.copyto(pagelocked_buffer, img)
    return y_test[case_num]

def main():

    ## PlugIn File 없으면, 에러처리
    # Load the shared object file containing the Clip plugin implementation.
    # By doing this, you will also register the Clip plugin with the TensorRT
    # PluginRegistry through use of the macro REGISTER_TENSORRT_PLUGIN present
    # in the plugin implementation. Refer to plugin/clipPlugin.cpp for more details.
    if not os.path.isfile(CLIP_PLUGIN_LIBRARY):
        raise IOError("\n{}\n{}\n{}\n".format(
            "Failed to load library ({}).".format(CLIP_PLUGIN_LIBRARY),
            "Please build the Clip sample plugin.",
            "For more information, see the included README.md"
        ))

    ## PlugIn을 동적 라이브러리 연결 
    ctypes.CDLL(CLIP_PLUGIN_LIBRARY)

    ## 학습된 Model이 없으면 에러 발생 
    # Load pretrained model
    if not os.path.isfile(MODEL_PATH):
        raise IOError("\n{}\n{}\n{}\n".format(
            "Failed to load model file ({}).".format(MODEL_PATH),
            "Please use 'python lenet5.py' to train and save the model.",
            "For more information, see the included README.md"
        ))

    ##  build_engine 함수 호출  
    # Build an engine and retrieve the image mean from the model.
    with build_engine(MODEL_PATH) as engine:

        ## 기존의 common.py 참조하면, host(CPU) /device(GPU) buffer 할당  
        inputs, outputs, bindings, stream = common.allocate_buffers(engine)
 
        ## Engine 생성
        with engine.create_execution_context() as context:
             
            ## 기존과 동일하게 host(CPU) 정보를 넣고, Random으로 설정한 test_case  표시 
            print("\n=== Testing ===")
            test_case = load_normalized_test_case(inputs[0].host)
            print("Loading Test Case: " + str(test_case))

            ## common.py의 inference해서 얻은 결과 표시하여 상위 값과 비교 
            # The common do_inference function will return a list of outputs - we only have one in this case.
            [pred] = common.do_inference(context, bindings=bindings, inputs=inputs, outputs=outputs, stream=stream)
            print("Prediction: " + str(np.argmax(pred)))


if __name__ == "__main__":
    main()


3. TensorRT Python Custom Layer(Python) 소스

상위 구현된 IPluginV2와 관련된 함수를 Python으로 구현을 한번 해보자
아래와 같이 Python으로도 IPlugInV2를 지원을 해주고 있다

Python의 iPlugInv2 부분 아래 주소
  https://docs.nvidia.com/deeplearning/sdk/tensorrt-api/python_api/infer/Plugin/pyPlugin.html
  https://docs.nvidia.com/deeplearning/sdk/tensorrt-api/python_api/infer/Plugin/IPluginV2.html

다음은 Custom Layer의 예제 C++과 Python의 예제를 다루고 있다

Custom Layer C++ Example
  https://docs.nvidia.com/deeplearning/sdk/tensorrt-developer-guide/index.html#add_custom_layer


3.1 Python Custom Layer  Example

Python Custom Layer Example은 이곳을 보면 되고 구성은 두개로 나뉘어 지면, 연결하는 Interface인 IPlugin에 관련되어서만 서술하고 있다.
Custom Layer의 구현에 대해서 별다른 이야기가 없다
TensorRT Manual이 계속 변경되니, 추후에 예제도 추가될지도 모른다

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


  • Layer를 새롭게 정의하여 Network에 추가방식 (IPluginCreator)
IPluginCreator로 사용하여 새롭게 Layer정의하고 이 Plugin을 network 추가 (상위구성과 다름)
Layer의 구체적인 동작방식은 정의하지 않았다 (동작방식은 C++로 구현?)

  https://docs.nvidia.com/deeplearning/sdk/tensorrt-developer-guide/index.html#example1_add_custom_layer_python
  https://docs.nvidia.com/deeplearning/sdk/tensorrt-api/python_api/infer/Plugin/IPluginCreator.html
  https://docs.nvidia.com/deeplearning/sdk/tensorrt-api/python_api/infer/Graph/Network.html?highlight=add_plugin_v2#tensorrt.INetworkDefinition.add_plugin_v2


  • UFF Parser에 존재하는 Layer를 재정의해서 구현하는 방식  (IPluginV2)
이 예제가 상위 구성 IPluginV2의 비슷하지만 아래참조사이트의 op="LReLU_TRT"의 부분이 별도의 구현예제는 없다 (개인생각으로는 C++로 구현)

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


만약 상위 소스를 Python으로만 수정한다면 PlugIn의 Custom Layer부분이 Serialize가 되어야 할 것인데, 이 부분이 어떻게 될까 한번 생각해보면, 일단
C++은 Compiler라서 이미 Binary이기때문에 Serialize가 쉽지만, Python은 Interpreter 이므로 PlugIn을 Python으로 구현하면 성능뿐만 아니라, 동작구현에도 어려움이 있을 것 같다

내 개인생각으로는 PlugIn은 Python으로 해도, 동작방식(Custom Layer)은 C++으로 구현을 해야할 것 같다
이 부분은 생각해볼만한 문제이며, 만약 순수 Python 구현된 Custom Layer 예제가 나오면 어떻게 나올지 궁금하다