728x90
반응형

 

 

이 포스팅은 Tensorflow 에서 이미 만들어진 ckpt 파일을 가지고 TensorRT로 변환하는 과정에서

마지막 노드를 찾기 위하여 겪게 된 삽질들을 적어두었다 ^^

 

Tensorflow에서 이미 만들어진 ckpt 파일만 가지고 pb 파일을 생성할 수 없기 때문에,

ckpt 파일을 가지고 모델을 테스트 하는 과정에서 iteration 한번으로 pbtxt 를 생성하였다.

 

이 때 중요한 점은 테스트를 한번 돌려서 pb 파일을 생성할 수도 있지만 테스트의 pb와 트레이닝을 끝으로 만들어지는 pb 는 다르다는 것이다. 당연히 내부에 포함되어있는 가중치의 값이 다르니까. 

 

pbtxt 는 모델의 구조를 담은 파일인데 이와 ckpt 파일을 가지고 freeze 하여 pb 파일을 얻었다.

 

이 때 모델의 마지막 노드를 알아야 하는데, 남이 만든 모델이라 알기가 너무 힘들었고, pbtxt 에서 모델의 구조들을 보고서 추론하려면 어마어마한 양의 노드 파일들을 보고 알아야 하는데 그게 불가능 해서 나같은 경우 pbtxt 에서 마지막 노드로 추정되는? 노드들을 몇개 적은 후에 그 노드들을 가지고 pb 파일을 생성하여 tensorboard로 확인하면서 마지막 노드 네임을 얻었다...

 

또한 TensorRT 를 설치하면 포함되어있는 convert-to-uff 모듈을 이용해서 모든 노드들의 네임을 얻는 것도 방법이다.

그러면 알아서 input , output 노드들을 알려주고 나머지 노드 목록들을 알려주는데

이게 input 노드는 맞을 지언정, output 노드는 틀릴 수 있다.

또한 마지막 노드는 여러개가 될 수 있으며, 마지막에 출력된 노드가 항상 마지막 노드인 것은 아니다...

 

아무튼... 얻은 pb를 가지고 TF-TRT를 이용하여 FP16, FP32, INT8 버전의 tensorrt.pb 들을 생성했었다. 

 

 

 

 

 

 

 

그래서 결론적으로 마지막 노드를 찾는 방법은 다음과 같다. 

 

 

1. pbtxt 생성하여 마지막 노드 찾기

 

pbtxt 생성 함수 

tf.train.write_graph(
    graph_or_graph_def,
    logdir,
    name,
    as_text=True
)

사용 예시는 아래와 같다. as_text=True 로 설정할 수도 있지만 (False 는 pb 파일) name을 .pbtxt 로 설정해도 된다.

v = tf.Variable(0, name='my_variable')
sess = tf.Session()
tf.train.write_graph(sess.graph_def, './', 'graph.pbtxt')

이렇게 하여 생성된 pbtxt 를 열어보면 아래와 같은 형식으로 구성되어있다.

(추가예정)

여기서 모델의 코드에 name 이 지정되어있다면 그 name을 찾아서 예상해보는 것도 방법이다. 

그게 아니라면 딥러닝 모델의 마지막 옵션이 무엇인지 예측하여 찾는 것도 방법이다...

예를 들어 FusedBatchNorm 함수가 마지막에 실행된 것 같다면, 실제 마지막 노드의 이름은

tower_0/refine_out/BatchNorm/FusedBatchNorm 이 될 수 있다. 

하지만 pbtxt 를 생성하면 워낙 양이 방대해서... 찾기가 힘들다. 

 

 

 

 

 

 

 

 

2. ckpt + pbtxt = pb 파일을 생성하여 마지막 노드 찾기 

 

필자는 의심되는 마지막 노드를 이용해서 pb 파일을 임의로 생성해보았다. 

import tensorflow as tf
from tensorflow.python.tools import freeze_graph

def main():

	freeze_graph.freeze_graph('/graph.pbtxt', "", False, 
				'/checkpoint.ckpt', 'tower_0/refine_out/BatchNorm/FusedBatchNorm',
				"save/restore_all", "save/Const",
				'frozen.pb', True, "")

	print('done.')
	
if __name__ == '__main__':
	main()

 위와 같이 생성 할 수 있는데, 생성된 pb 를 직관적으로 보려면 tensorboard 를 이용했다. 

tensorboard 를 통해 pb 를 이용하여 모델의 구조를 보는 방법은 아래 포스팅에 적혀있다. 

 

https://eehoeskrap.tistory.com/322

 

[TensorFlow] pb 파일 TensorBoard에 띄우기

import_pb_to_tensorboard.py # Copyright 2017 The TensorFlow Authors. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in co..

eehoeskrap.tistory.com

 

 

텐서보드로 열면 위와 같이 구성되어있는데, 화살표에 feature map 의 사이즈가 적혀있다. 

feature map 의 크기를 보고 마지막 노드를 추정하는 것도 방법이다

 

 

 

 

 

 

 

3. convert-to-uff 이용하기 

convert-to-uff 모듈은 tensorflow에 내장된 tensorrt 를 설치(bazel build 하거나 tensorrt container 설치)하였을 때 포함되어있는 모듈이다. tensorflow 모델을 tensorrt 로 변환하려면 uff parser 를 이용하기 때문에 이 모듈이 있는 것이다. 

이 방법을 사용하려면 pb 파일이 있어야 한다. 대충 마지막 노드를 추정하여 얻은 pb 를 가지고 실행을 한다. 

 

$ convert-to-uff frozen.pb -l

위와 같은 방법으로 그래프 노드들을 모두 확인 할 수 있다. 명령어 실행 시 input, output 노드들의 목록을 보여주고 나서 나머지 모든 노드들을 출력해준다. 

 

 

 

 

 

 

 

4. 그래프의 모든 노드명 출력하여 마지막 노드 찾기

[x.name for x in graphdef_frozen.node]

 

코드에서 세션을 통해 그래프를 실행하고 위와 같은 코드를 실행하여 노드들을 다음과 같이 얻을 수 있다.

주의할 점은 여기서 마지막 노드는 2개 3개도 될 수 있다는 충격적인 사실

그리고 맨 밑에 있는 노드가 항상 마지막 노드를 의미하지는 않는다는 점...

원하는 feature map 의 크기를 가진 마지막 노드를 알아야 한다. 

['net/input',
 'net/Sub/y',
 'net/Sub',
 'net/conv1_1/weights',
 'net/conv1_1/Conv2D',
 'net/conv1_1/BatchNorm/Const',
 'net/conv1_1/BatchNorm/beta',
 'net/conv1_1/BatchNorm/moving_mean',
 'net/conv1_1/BatchNorm/moving_variance',
 'net/conv1_1/BatchNorm/FusedBatchNorm',
 'net/conv1_1/Relu',
 'net/pool1/MaxPool',
 'net/conv2_1/weights',
 'net/conv2_1/Conv2D',
 'net/conv2_1/BatchNorm/Const',
 'net/conv2_1/BatchNorm/beta',
 'net/conv2_1/BatchNorm/moving_mean',
 'net/conv2_1/BatchNorm/moving_variance',
 'net/conv2_1/BatchNorm/FusedBatchNorm',
 'net/conv2_1/Relu',
 'net/pool2/MaxPool',
 'net/conv3_1/weights',
 'net/conv3_1/Conv2D',
 'net/conv3_1/BatchNorm/Const',
 'net/conv3_1/BatchNorm/beta',
 'net/conv3_1/BatchNorm/moving_mean',
 'net/conv3_1/BatchNorm/moving_variance',
 'net/conv3_1/BatchNorm/FusedBatchNorm',
 'net/conv3_1/Relu',
 'net/conv3_2/weights',
 'net/conv3_2/Conv2D',
 'net/conv3_2/BatchNorm/Const',
 'net/conv3_2/BatchNorm/beta',
 'net/conv3_2/BatchNorm/moving_mean',
 'net/conv3_2/BatchNorm/moving_variance',
 'net/conv3_2/BatchNorm/FusedBatchNorm',
 'net/conv3_2/Relu',
 'net/pool3/MaxPool',
 'net/conv4_1/weights',
 'net/conv4_1/Conv2D',
 'net/conv4_1/BatchNorm/Const',
 'net/conv4_1/BatchNorm/beta',
 'net/conv4_1/BatchNorm/moving_mean',
 'net/conv4_1/BatchNorm/moving_variance',
 'net/conv4_1/BatchNorm/FusedBatchNorm',
 'net/conv4_1/Relu',
 'net/conv4_2/weights',
 'net/conv4_2/Conv2D',
 'net/conv4_2/BatchNorm/Const',
 'net/conv4_2/BatchNorm/beta',
 'net/conv4_2/BatchNorm/moving_mean',
 'net/conv4_2/BatchNorm/moving_variance',
 'net/conv4_2/BatchNorm/FusedBatchNorm',
 'net/conv4_2/Relu',
 'net/pool4/MaxPool',
 'net/conv5_1/weights',
 'net/conv5_1/Conv2D',
 'net/conv5_1/BatchNorm/Const',
 'net/conv5_1/BatchNorm/beta',
 'net/conv5_1/BatchNorm/moving_mean',
 'net/conv5_1/BatchNorm/moving_variance',
 'net/conv5_1/BatchNorm/FusedBatchNorm',
 'net/conv5_1/Relu',
 'net/conv5_2/weights',
 'net/conv5_2/Conv2D',
 'net/conv5_2/BatchNorm/Const',
 'net/conv5_2/BatchNorm/beta',
 'net/conv5_2/BatchNorm/moving_mean',
 'net/conv5_2/BatchNorm/moving_variance',
 'net/conv5_2/BatchNorm/FusedBatchNorm',
 'net/conv5_2/Relu',
 'net/pool5/MaxPool',
 'net/Shape',
 'net/strided_slice/stack',
 'net/strided_slice/stack_1',
 'net/strided_slice/stack_2',
 'net/strided_slice',
 'net/strided_slice_1/stack',
 'net/strided_slice_1/stack_1',
 'net/strided_slice_1/stack_2',
 'net/strided_slice_1',
 'net/strided_slice_2/stack',
 'net/strided_slice_2/stack_1',
 'net/strided_slice_2/stack_2',
 'net/strided_slice_2',
 'net/mul',
 'net/strided_slice_3/stack',
 'net/strided_slice_3/stack_1',
 'net/strided_slice_3/stack_2',
 'net/strided_slice_3',
 'net/mul_1',
 'net/Reshape/shape',
 'net/Reshape',
 'net/fc6/weights',
 'net/fc6/biases',
 'net/fc6/MatMul',
 'net/fc6/BiasAdd',
 'net/fc6/Relu',
 'net/fc7/weights',
 'net/fc7/biases',
 'net/fc7/MatMul',
 'net/fc7/BiasAdd',
 'net/fc7/Relu',
 'net/fc8/weights',
 'net/fc8/biases',
 'net/fc8/MatMul',
 'net/fc8/BiasAdd']

 

더 자세한 코드는 아래를 참고하길 바란다.

https://github.com/dkorobchenko-nv/tensorrt-demo/blob/master/Tensorflow_freeze.ipynb

 

dkorobchenko-nv/tensorrt-demo

TensorRT and TensorFlow demo/example (python, jupyter notebook) - dkorobchenko-nv/tensorrt-demo

github.com

 

 

 

 

 

 

 

5. 코드에서 직접 마지막 레이어의 name 이나 scope 를 설정

with tf.Graph().as_default():
      c1 = tf.constant(4, dtype=tf.float64, name='c')
      with tf.name_scope("prefix_name"):
          c2 = tf.constant(4, dtype=tf.int32, name='c')
          c3 = tf.constant(4, dtype=tf.float64, name='c')

위와 같이 코드에서 직접 name 을 설정하거나 scope 를 지정해준다면 훨씬 수월하게 마지막 노드를 찾을 수 있다.

아니면 코드에서 위와 같은 이름이나 스코프의 이름이 존재한다면 그 이름을 찾는 것도 방법이다. 

 

 

 

 

728x90
반응형