빅데이타 & 머신러닝/머신러닝

텐서플로우에서 모델 export시 그래프를 다시 그리는 이유

Terry Cho 2017. 6. 26. 23:03

텐서플로우에서 checkpoint와 saved model의 차이와


모델을 export할때 그래프를 다시 그리는 이유


조대협 (http://bcho.tistory.com)


Check point vs Saved model


텐서플로우 튜토리얼들을 보면 모델을 저장하고 리스토어 하는데, check point를 사용하도록 가이드하고 있다.

그런데, Tensorflow Serving이나 CloudML등에 학습된 모델을 올려서 inference를 하고자 할때는 check point 파일을 사용하지 않고, 별도로 모델을 Saved model로 export하여 사용한다. 그렇다면 check-point와 saved model의 차이가 무엇일까?


check-point를 학습을 하다가 학습 내용을 중간에 저장하고 나중에 학습을 연달아서 하기 위한 용도로 check point에 의해서 저장되는 값을 모델 그래프의 변수 (Variable)만이 저장된다. 모델의 그래프 자체는 저장되지 않는다. 그래서 check-point를 리스토어하는 예제를 보면, 다시 그래프를 코드로 그 정의한 후에, check-point에 저장된 데이타를 리스토어 하는 것을 볼 수 있다.


Saved model은 inference를 위해서 모델을 저장하는 것으로, check-point와는 다르게 변수뿐만 아니라 모델의 그래프도 같이 저장한다. Tensorflow serving이나 cloud ml 등에서 inference를 위해서는 당연히 변수뿐 아니라 모델의 그래프도 필요하기 때문에 이를 같이 넘기는 것이다.

모델을 Export 할때, 그래프를 다시 그리는 이유는?


다음은 모델을 export 하는 코드의 예제인데, 코드를 보면 모델을 위한 그래프를 다시 정의 하는 것을 볼 수 있다.


 with tf.Session(graph=tf.Graph()) as sess:

   images = tf.placeholder(tf.float32,[None,FLAGS.image_size,FLAGS.image_size,FLAGS.image_color])

   prediction = build_model(images, keep_prob=1.0)


   # Define API inputs/outputs object

   inputs = {'image': images}

   input_signatures = {}

   for key, val in inputs.iteritems():

     predict_input_tensor = meta_graph_pb2.TensorInfo()

     predict_input_tensor.name = val.name

     predict_input_tensor.dtype = val.dtype.as_datatype_enum

     input_signatures[key] = predict_input_tensor


   outputs = {'prediction': prediction}

   output_signatures = {}

   for key, val in outputs.iteritems():

     predict_output_tensor = meta_graph_pb2.TensorInfo()

     predict_output_tensor.name = val.name

     predict_output_tensor.dtype = val.dtype.as_datatype_enum

     output_signatures[key] = predict_output_tensor


   inputs_name, outputs_name = {}, {}

   for key, val in inputs.iteritems():

     inputs_name[key] = val.name

   for key, val in outputs.iteritems():

     outputs_name[key] = val.name

   tf.add_to_collection('inputs', json.dumps(inputs_name))

   tf.add_to_collection('outputs', json.dumps(outputs_name))


   init_op = tf.global_variables_initializer()

   sess.run(init_op)


   # Restore the latest checkpoint and save the model

   saver = tf.train.Saver()

   saver.restore(sess, checkpoint)


   predict_signature_def = signature_def_utils.build_signature_def(

       input_signatures, output_signatures,

       signature_constants.PREDICT_METHOD_NAME)

   build = builder.SavedModelBuilder(model_dir)

   build.add_meta_graph_and_variables(

       sess, [tag_constants.SERVING],

       signature_def_map={

           signature_constants.DEFAULT_SERVING_SIGNATURE_DEF_KEY:

               predict_signature_def

       },

       assets_collection=tf.get_collection(tf.GraphKeys.ASSET_FILEPATHS))

   build.save()


기존의 학습 부분에 그래프가 그려져 있는데도 불구하고, export 할때 그래프를 다시 그려서 저장하는 이유는, training용 그래프와 inference용 그래프가 다르기 때문이다.

training 그래프는, 중간 중간 test (evaluation) 에 사용되는 코드가 들어간다. 즉 test 코드가 들어간다.

또는 training 그래프는 dropout 계층이 있지만, inference에는 dropout 계층이 필요 없기 때문에 그래프가 달라진다.

데이타를 피딩하는 경우에도 training 에서는 속도를 위해서 placeholder를 없애고 바로 Queue runner에서 데이타를 읽어서 모델에 읽도록할 수 도 있지만, inference에는 queue runner를 통해서 데이타를 읽는 것이 아니라 예측을 할 값을 입력 받아야 하기 때문에, 이 경우에는  placeholder가 필요하다.


이런 이유로 training 그래프와 inference 그래프가 달라지는데, 텐서플로우 1.0 버전 이후에서 부터는 model export를 SavedModel을 이용하여 저장하도록 가이드하는데, 구현 복잡도가 다소 높고 아직까지 가이드가 부족하다. 이를 단순화 하기 위해서 Experiment 클래스를 이용하면 자동으로 training 그래프와 inference graph를 분리해주기 때문에, 코드가 단순화 될 수 있기 때문에 모델을 만들때 처음 부터 Experiment 클래스를 사용하기를 권장한다.