Nvidia Pilotnet Convert 관련 문의

안녕하세요. 국민대학교 avees 연구실에서 학부연구생으로 공부중인 곽승헌이라고 합니다.

제가 end-to-end자율주행 모델에 관심이 있어서 TOPST Ai-G보드에서 돌려볼 만한 모델이 뭐가 있을까 고민하다가 e2e자율주행의 초기 모델인 Nvidia의 Pilotnet이라는 모델을 돌려보기로 했습니다.

실험 진행 과정은 다음과 같습니다.

  1. pilotnet 모델 학습 및 테스트 (로컬 PC) →완료
  2. onnx변환 후 테스트 → 완료
  3. onnx→ enlight변환 (제공해주신 EnlightSDK툴 활용)
  4. quatization (EnlightSDK툴 활용)
  5. 컴파일 및 make
  6. 보드에 복사 후 실행 (tcnnapp -i rtpm -o rtpm -n pilotnet_topst_quantizes/) 명령어를 통해 실행

제가 로컬에서 onnx파일을 돌릴땐 정상주행하는걸 확인했는데 보드에 포팅 후 실행해보니 출력이 항상 0으로 나오는 것을 확인했습니다.

중간에 변환 과정에서 오류가 생긴 것인지 확인하고 싶은데 enlight모델을 로컬PC에서 확인해볼수 있는 방법이 있을까 하여 글 남겨드립니다!

모델 구조는 사진과 같으며, 사용하는 연산자는 텔레칩스 공식 홈페이지에서 확인한 연산자 범주 내에 있는것을 확인하였습니다.

추가로 제가 변환을 진행한 파일들도 첨부해드릴테니 혹시 확인이 가능하시면 확인 해주시면 감사하겠습니다!

안녕하세요 TOPST 매니저입니다.
우선 제공해주신 모델에 대해서는 저희가 검토 후 답변드리도록 하겠습니다.
우선 모델 변환 시 사용한 명령어에 대해서 안내해주시면 조금 더 빠르게 조치를 취할 수 있을 것 같습니다.
또한 enlight 툴킷에서 시뮬레이터 기능을 제공하고 있습니다.

해당 시뮬레이터는 컨버팅과 양자화 이후 모델에 대한 성능 평가를 진행하실 수 있습니다.
모델 변환이 이상하다 생각되시면 컨버팅과 양자화 후에 나온 텐서값들과 onnx 모델 추론 텐서 값을 비교해보시는걸 추천드립니다.

시뮬레이터 사용 방법

  1. 컨버팅 완료 후 모델
  • python3 EnlightSDK/enlight_sim.py {컨버팅 된 모델} --inputs {입력 데이터}
    해당 명령어로 간단하게 실행할 수 있습니다.
    입력 이미지를 넣어 추론을 진행하고 최종적으로 출력된 텐서 값을 내보내게 됩니다.

또한, --dump-output 옵션을 사용하면 이러한 텐서 값들을 담은 .npz 파일을 추출하게
됩니다

  1. 양자화 이후 모델
  • python3 EnlightSDK/enlight_sim.py {양자화 된 모델} --inputs {입력 데이터}

모델에 변경사항이 있어서 재학습 후 onnx변환하였습니다. 파일은 구글드라이브 링크로 첨부해드리겠습니다.
output_data폴더는 제가 carla에서 수집한 이미지와 steer값 데이터입니다.

(topst) avees@avees-MS-7E01:~/tc-nn-toolkit/EnlightSDK$ python converter.py pilotnet.onnx --output pilotnet.enlight --type unknown --dataset Custom --dataset-root ./output_data --num-images 100 --enable-track

이 명령어로 변환해서 아래 결과가 나왔습니다.

[INFO] Find model_config pilotnet.json, but can’t find it
[INFO] so, set DEFAULT config parameter


[v0.9.9] Converting to enlight format. start.


model : pilotnet.onnx
output : pilotnet.enlight
model_config : auto
type : unknown
yolo_version :
dfl_reg_max : 0
weight : None
mean : (0.486, 0.456, 0.406)
std : (0.229, 0.224, 0.225)
add_detection_post_process : None
num_class : -1
class_labels : None
omit_post_process : False
output_order : auto
variance : (0.125, 0.125)
no_background : False
logistic : softmax
force_output : None
input_shape : None
input_quantization_scale : 128.0
dataset : Custom
dataset_root : ./output_data
image_set : test
download : False
batch_size : 4
num_workers : 0
enable_letterbox : False
enable_track : True
num_images : 100
dump_stats : False
compatibility_log_root : ./log/compatibility_results
compatibility_list : None
disable_checking_compatibility : False
input_ch_pad : None
debug : False
track_per_channel : False
enable_channel_equalization : False

Checking arguments… done
Start ConstantFold optimizer
optimizing … done.
Start DeadCodeElimination optimizer
optimizing … done.
Start FuseLayers optimizer
optimizing … done.
Start DeadCodeElimination optimizer
optimizing … done.
Start ReplaceLayerEnlightFriendly optimizer
optimizing … done.
Start FuseLayers optimizer
optimizing … done.
Start ConstantFuse optimizer
optimizing … done.
Start PostProcessParameterFold optimizer
optimizing … done.
Start InputQNormalizeFold optimizer
optimizing … done.
Start ExposePadLayer optimizer
optimizing … done.
Start DecomposeActivation optimizer
optimizing … done.
Start MakeInputChannelPartition optimizer
optimizing … done.

Checking arguments… done

Checking compatibility with ENLIGHT NPU

╒═══════════════════════════════╤════╕
│ SUMMARY │ │
├───────────────────────────────┼────┤
│ Number of compatible layers │ 11 │
├───────────────────────────────┼────┤
│ Number of incompatible layers │ 0 │
├───────────────────────────────┼────┤
│ Total number of layers │ 11 │
╘═══════════════════════════════╧════╛

Checking compatibility with ENLIGHT NPU … Done
Writing compatibility result to … /home/avees/tc-nn-toolkit/EnlightSDK/log/compatibility_results/pilotnet_compatibility.log

Serializing Graph done.
Writing to File… Done
Writing File path : pilotnet.enlight

Converter. done.

추가로, enlight_sim.py의 결과는 다음과 같았습니다.

(topst) avees@avees-MS-7E01:~/tc-nn-toolkit/EnlightSDK$ python3 enlight_sim.py pilotnet.enlight --inputs 010023.jpg

[INFO] Find model_config pilotnet.json, but can’t find it
[INFO] so, set DEFAULT config parameter


Inference image. start.


model : pilotnet.enlight
inputs : 010023.jpg
model_config : auto
output : None
result_root : ./output_result
th_iou : 0.5
th_conf : 0.5
has_background : False
topk : 5
force_resize : None
crop : None
use_cv2 : False
enable_letterbox : False
image_format : RGB
dump : False
dump_root : ./output_dump
dump_shape : enlight
dump_format : enlight
enable_show : False
enable_customize_post_process : None
enable_blazeface_post_process : None
enable_save_npy_result_for_unknown : False
enable_opts : False

Initializing Network done.
Custom_100

Checking arguments…
Save results (result) path : ./output_result/pilotnet/010023


Show de-quantized output tensor

shape: torch.Size([1, 1, 1])

OutputTensor([[[-0.2038]]], device=‘cuda:0’)

Save results (text) path: ./output_result/pilotnet/010023/010023_result.txt
[1 / 1]
Inference. done.

추가로 quatization을 진행할땐 아래 명령어로 진행하였습니다.

(topst) avees@avees-MS-7E01:~/tc-nn-toolkit/EnlightSDK$ python quantizer.py pilotnet.enlight

[INFO] Find model_config pilotnet.json, but can’t find it
[INFO] so, set DEFAULT config parameter


Quantization. start


model : pilotnet.enlight
output : None
model_config : auto
stats_file : None
scales_file : None
qbits_file : None
dump_scales : False
dump_qbits : False
dump_type : enlight_name
disable_sanity_checker : False
custom_qparam_type : enlight_name
overwrite_concat_qscale : False
m_std_8 : None
m_std_4 : 5
m_std_ratio : None
weight_range_asymmetric : False
disable_clip_min_max : False
force_output_scale : None

Start BatchormalizeFold optimizer
optimizing … done.
Start FuseConstantEltwLayer optimizer
optimizing … done.
Start TreatIntermediateOuput optimizer
optimizing … done.
Start ModifyLayerQuantizationFriendly optimizer
optimizing … done.
quantization… done.
End sanity check for custom quantization
Start MakeCompilerFriendly optimizer
optimizing … done.
Start MakeLayerAligned optimizer
optimizing … done.
Serializing Graph done.
Writing to File… Done
Writing File path : pilotnet_quantized.enlight

Quantizer done.

양자화가 진행된 파일에 대해 enlight_sim.py를 돌린 결과는 아래와 같습니다.

(topst) avees@avees-MS-7E01:~/tc-nn-toolkit/EnlightSDK$ python3 enlight_sim.py pilotnet_quantized.enlight --inputs 010023.jpg

[INFO] Find model_config pilotnet.json, but can’t find it
[INFO] so, set DEFAULT config parameter


Inference image. start.


model : pilotnet_quantized.enlight
inputs : 010023.jpg
model_config : auto
output : None
result_root : ./output_result
th_iou : 0.5
th_conf : 0.5
has_background : False
topk : 5
force_resize : None
crop : None
use_cv2 : False
enable_letterbox : False
image_format : RGB
dump : False
dump_root : ./output_dump
dump_shape : enlight
dump_format : enlight
enable_show : False
enable_customize_post_process : None
enable_blazeface_post_process : None
enable_save_npy_result_for_unknown : False
enable_opts : False

Initializing Network done.
Custom_100

Checking arguments…
Save results (result) path : ./output_result/pilotnet_quantized/010023


Show de-quantized output tensor

shape: torch.Size([1, 1, 1])

OutputTensor([[[-0.2023]]], device=‘cuda:0’)

Save results (text) path: ./output_result/pilotnet_quantized/010023/010023_result.txt
[1 / 1]
Inference. done.

이 과정까지는 큰 문제가 없는것으로 보여지는데 맞는지 확인 부탁드립니다!

혹시 이후의 컴파일이나 AI-G보드 적용과정에 대해 github에 있는 파일보다 자세한 과정이 담겨있는 파일이 있으면 전달해 주시면 감사하겠습니다!!

컴파일은 아래 과정을 통해 진행하였습니다.

(topst) avees@avees-MS-7E01:~/tc-nn-toolkit$ python EnlightSDK/compiler.py EnlightSDK/pilotnet_quantized.enlight

[INFO] Find model_config pilotnet.json, but can’t find it
[INFO] so, set DEFAULT config parameter


Compiling network. start.


model : EnlightSDK/pilotnet_quantized.enlight
dump_root : ./output_code
model_config : auto
th_conf : 0.5
th_iou : 0.5
max_detection : -1
batch_mode : False
batch_size : 2
cmd_queue_enable : False
save_parameter : False
max_work_buf_size : 100
decomposition_enable : False
debug_group : None
max_group_size : 32

output_dir : ./output_code/pilotnet_quantized
Start ComposeActivation optimizer
optimizing … done.
Start ComposeAny_Trial optimizer
optimizing … done.
Start ComposeSinglePattern optimizer
optimizing … done.
Start FuseLayers optimizer
optimizing … done.
Start CheckHwCompatibility optimizer
optimizing … done.
Start MakeGroupPatternInverseY optimizer
optimizing … done.
Start ComposeGroupPattern optimizer
optimizing … done.
Start MakeGroupPatternSerial optimizer
optimizing … done.
Start PartitionCostOptimizer optimizer
optimizing … done.
Start DecomposeGroupPattern optimizer
optimizing … done.
Start UpdateGroupPatternSerial optimizer
optimizing … done.
Start MakeGroupPatternResidual optimizer
optimizing … done.
Start MakeGroupPatternSingle optimizer
optimizing … done.
Start MakeGroupPatternMisc optimizer
optimizing … done.
Start MakeGroupPatternNpuFriendly optimizer
optimizing … done.
Start MakeGroupPatternRemain optimizer
optimizing … done.
Start MakeInputRowGroupPartitionInverseY optimizer
optimizing … done.
Start MakeInputRowGroupPartition optimizer
optimizing … done.
Start MakeOutputChannelGroupPartition optimizer
optimizing … done.
Start MakeOutputChannelGroupPartitionMisc optimizer
optimizing … done.
Start MakeInputRowGroupPartitionMisc optimizer
optimizing … done.
Start CheckPartitionValid optimizer
optimizing … done.
Start DecomposeSinglePattern optimizer
optimizing … done.
high level validating
high level validation … done.
Start Preprocess(version=NPUV20) code-emitting
code-emitting … done.
Start MakeGroupLayerOperator(version=NPUV20) code-emitting
Enable work buffer optimizer …
Start AllocMem(version=NPUV20) code-emitting
work-buffer size : 0.0438232421875 (MiB)
code-emitting … done.
Start LinkIact(version=NPUV20) code-emitting
code-emitting … done.
work-buffer allocation sanity check … done.
Start MakeCode(version=NPUV20) code-emitting
code-emitting … done.
./output_code/pilotnet_quantized/npu_cmd.h created
./output_code/pilotnet_quantized/npu_cmd.bin created
./output_code/pilotnet_quantized/npu_cmd.txt created
Writing [322976]/[322976]
./output_code/pilotnet_quantized/quantized_network.bin created
./output_code/pilotnet_quantized/oact_entry_table.py created
./output_code/pilotnet_quantized/network.h created
./output_code/pilotnet_quantized/post_process.c created
./output_code/pilotnet_quantized/oe_network.bin created
./output_code/pilotnet_quantized/oe_network.h created

            NPU compiler: done sucessfully.

Compiler. done.

그 후 아래 과정을 거쳐 make 진행 후 보드에 붙여넣었습니다.
(보드 IP는 제가 원활한 통신을 위해 임의로 변경했습니다)

(topst) avees@avees-MS-7E01:~/tc-nn-toolkit$ cd build_network/
(topst) avees@avees-MS-7E01:~/tc-nn-toolkit/build_network$ cp ../output_code/pilotnet_quantized/ ./ -ar
(topst) avees@avees-MS-7E01:~/tc-nn-toolkit/build_network$ cp ../output_code/pilotnet_quantized/ ./ -ar
(topst) avees@avees-MS-7E01:~/tc-nn-toolkit/build_network$ rm -rf network.h post_process.c
(topst) avees@avees-MS-7E01:~/tc-nn-toolkit/build_network$ ln -s ./pilotnet_quantized/network.h
(topst) avees@avees-MS-7E01:~/tc-nn-toolkit/build_network$ ln -s ./pilotnet_quantized/post_process.c
(topst) avees@avees-MS-7E01:~/tc-nn-toolkit/build_network$ make
aarch64-none-linux-gnu-gcc -fPIC -Wall -D__NO_INLINE__ -std=gnu99 -lpthread -pthread -O2 -g -D__i386__ -pg -Wno-unused -Wno-attributes -fno-delete-null-pointer-checks -fPIC -no-pie -Ienlight -Inetwork -c post_process.c classifier/classifier.c detector/detector.c detector/yolo_detector.c detector/ssd_detector.c custom_postproc/custom_postproc.c enlight/enlight_network.c network.c
detector/yolo_detector.c: In function ‘detect_object_float’:
detector/yolo_detector.c:810:15: warning: ‘obj.dims[2]’ may be used uninitialized in this function [-Wmaybe-uninitialized]
810 | const int len_obj = obj[0].dims[2];
| ^~~~~~~
aarch64-none-linux-gnu-gcc -shared -Wl,-soname,net.so -o net.so post_process.o detector.o classifier.o yolo_detector.o ssd_detector.o custom_postproc.o enlight_network.o network.o
rm -rf ./.o network/.o enlight/.o /.o
(topst) avees@avees-MS-7E01:~/tc-nn-toolkit/build_network$ ls -l pilotnet_quantized/
total 1856
-rw-rw-r-- 1 avees avees 61 Jan 13 05:48 compile_debug.log
-rw-rw-r-- 1 avees avees 80 Jan 13 05:48 network_buf.txt
-rw-rw-r-- 1 avees avees 637 Jan 13 05:48 network_group_info.csv
-rw-rw-r-- 1 avees avees 2387 Jan 13 05:48 network.h
-rw-rw-r-- 1 avees avees 1536 Jan 13 05:48 npu_cmd.bin
-rw-rw-r-- 1 avees avees 6293 Jan 13 05:48 npu_cmd.h
-rw-rw-r-- 1 avees avees 5526 Jan 13 05:48 npu_cmd.txt
-rw-rw-r-- 1 avees avees 94 Jan 13 05:48 oact_entry_table.py
-rw-rw-r-- 1 avees avees 336900 Jan 13 05:48 oe_network.bin
-rw-rw-r-- 1 avees avees 1187311 Jan 13 05:48 oe_network.h
-rw-rw-r-- 1 avees avees 3608 Jan 13 05:59 post_process.c
-rw-rw-r-- 1 avees avees 322976 Jan 13 05:48 quantized_network.bin
-rw-rw-r-- 1 avees avees 20 Jan 13 05:48 trap_point.py
(topst) avees@avees-MS-7E01:~/tc-nn-toolkit/build_network$ scp -r pilotnet_quantized/ root@192.168.24.200:/home/root
The authenticity of host ‘192.168.24.200 (192.168.24.200)’ can’t be established.
ED25519 key fingerprint is SHA256:Q784kP52aM3KOsv5OA/nYXwrSZjAsKoIB+hkTM/uRco.
This host key is known by the following other names/addresses:
~/.ssh/known_hosts:11: [hashed name]
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added ‘192.168.24.200’ (ED25519) to the list of known hosts.
root@192.168.24.200’s password:
oact_entry_table.py 100% 94 110.9KB/s 00:00
oe_network.bin 100% 329KB 20.1MB/s 00:00
network.h 100% 2387 3.7MB/s 00:00
compile_debug.log 100% 61 127.5KB/s 00:00
npu_cmd.bin 100% 1536 2.7MB/s 00:00
quantized_network.bin 100% 315KB 55.9MB/s 00:00
trap_point.py 100% 20 40.2KB/s 00:00
post_process.c 100% 3608 6.2MB/s 00:00
oe_network.h 100% 1159KB 21.4MB/s 00:00
npu_cmd.txt 100% 5526 7.0MB/s 00:00
network_group_info.csv 100% 637 1.0MB/s 00:00
network_buf.txt 100% 80 139.5KB/s 00:00
npu_cmd.h 100% 6293 903.3KB/s 00:00

보드 터미널에서 확인해보니 전송받은거까진 확인했습니다

root@ai-g-topst:~# ls -l pilotnet_quantized/
total 1856
-rw-r–r-- 1 root root 61 Apr 30 01:56 compile_debug.log
-rw-r–r-- 1 root root 80 Apr 30 01:56 network_buf.txt
-rw-r–r-- 1 root root 637 Apr 30 01:56 network_group_info.csv
-rw-r–r-- 1 root root 2387 Apr 30 01:56 network.h
-rw-r–r-- 1 root root 1536 Apr 30 01:56 npu_cmd.bin
-rw-r–r-- 1 root root 6293 Apr 30 01:56 npu_cmd.h
-rw-r–r-- 1 root root 5526 Apr 30 01:56 npu_cmd.txt
-rw-r–r-- 1 root root 94 Apr 30 01:56 oact_entry_table.py
-rw-r–r-- 1 root root 336900 Apr 30 01:56 oe_network.bin
-rw-r–r-- 1 root root 1187311 Apr 30 01:56 oe_network.h
-rw-r–r-- 1 root root 3608 Apr 30 01:56 post_process.c
-rw-r–r-- 1 root root 322976 Apr 30 01:56 quantized_network.bin
-rw-r–r-- 1 root root 20 Apr 30 01:56 trap_point.py

현재 위 사진과 같이 실험을 진행해 보았습니다.

왼쪽 아래는 원본 사진이며, 왼쪽 위 터미널 결과는 enlight_sim.py를 진행한 결과입니다. 양자화까지 진행시킨 enlight모델을 넣었을때 0.3723으로 준수한 값이 나왔지만, AI-G보드에 올려서 돌린 결과 0.00000으로 나왔습니다.

tcnnapp -i rtpm -o rtpm -n pilotnet_quantized -N pilotnet_quantized로 실행하였습니다.

아래는 post_process.c 코드입니다.
혹시 다른 곳에서 문제가 생겼을지 의심이 되는 과정이나 파일이 있다면 알려주시면 감사하겠습니다!

/*
Openedges Enlight Network Compiler
Classification main
*/

#include “stdint.h”
#include “enlight_network.h”

#ifdef i386

include <stdio.h>

define post_process_log(…) do {printf(VA_ARGS);} while(0)

#else

define post_process_log(…) do {_printf(VA_ARGS);} while(0)

#endif

extern void custom_postproc_init();

extern int custom_postproc_run(
int num_output,
enlight_act_tensor_t** outputs /**< output conf@brie@brieftensor number */
);

/** @brief Run custom post-processing
*

  •  Unknonw@para@parampost-processing
    
  • @param net_inst in@para@paramtance Network instance.

  • @param output_base o@paramtput @paramensor buffe@retur@return base

  • @param reserved

  • @return Return number of output
    */
    int run_post_process(
    void *net_inst,
    void *output_base,
    int num_input,
    void *reserved)
    {
    enlight_network_t inst;
    enlight_custom_postproc_t
    custom_param;

    int result;
    int i, num_output;
    enlight_act_tensor_t* output_tensors[MAX_NUM_OUTPUT];

    inst = (enlight_network_t )net_inst;
    custom_param = (enlight_custom_postproc_t
    )inst->post_proc_extension;

    num_output = enlight_custom_get_output_tensors(custom_param, output_tensors);

    for (i = 0; i < num_output; i++)
    output_tensors[i]->base = output_base;

    custom_postproc_init();

    result = custom_postproc_run(num_output, output_tensors);
    post_process_log(“custom process result: %d\n”, result);

    return num_output;
    }

컴파일 과정에서 문제가 생긴거라고 예상이 되는데 혹시 제가 제공해드리면 도움이 될 파일이 있으실까요?

안녕하세요 TOPST매니저입니다.
우선 전달주신 파일들을 확인해본 결과 후처리 빌드과정에서 나온 결과물인 net.so파일이 누락된 것 같습니다.
make하신 이후에 net.so를 cp명령어를 통해 네트워크 디렉터리로 이동하신 후 모델 추론을 시도해보시길 바랍니다.

감사합니다.

답변 감사합니다. 해당 과정 완료 후 보드에 복사해서 값이 나오게 하는것까진 성공했습니다.

제가 얻은 pilotnet.enlight 모델과 pilotnet_quantized.enlight모델에 같은 이미지를 인풋으로 주고 enlight_sim.py 진행했을때 이미지를 바꿔가면서 실험해도 모두 비슷한 결과가 나왔습니다. 그런데 컴파일 후 보드에 같은 이미지를 넣고 돌리면 같은 전혀 다른 결과가 나옵니다. 혹시 post processing과정의 문제인가요?

추가로 이미지 해상도는 200x66사이즈를 인풋으로 주고 있는데, 이게 문제가 될 수 있을지도 궁금합니다!

안녕하세요 TOPST 입니다.
우선 외부 일정으로 답변이 늦어진 점 사과드립니다.
입력 데이터의 경우에는 크게 문제가 없어보이는 상황입니다.
모델 후처리 과정보다는 전처리 과정에서 문제가 발생하여 결과가 다르게 나오는 것이라고 생각됩니다.

우선 저희 측에서도 관련해서 테스트 진행 후에 관련해서 답변드리도록 하겠습니다.

감사합니다.

넵 답변 감사합니다!

보드에서 tcnnapp을 이용하여 이미지를 주고받을때 어떤 식으로 주고받는지를 확인해 보고 싶은데, github에 올라와 있는 자료는 커맨드정도뿐인거 같아서 자세히 뜯어보기가 어렵습니다. 혹시 관련 자료가 있으면 공유해주실 수 있나요?

tcnnapp의 내부 소스는 github에는 공개되어 있지 않습니다.
AI-G Yocto 빌드 후
topst_sdk/build/ai-g-topst/tmp/work/cortexa53-telechips-linux/tc-nn-app/1.0.0-r0/git에서 내부 소스에 대해 확인해보실 수 있습니다.
감사합니다.

넵 답변 감사합니다!

혹시 compiler.py를 통해 나온 파일들은 로컬pc에서 테스트해볼수 있는 방법은 따로 없는걸까요?

넵 컴파일 이후 나온 네트워크는 npu api를 통해서만 추론 가능하도록 되어있습니다.

감사합니다.