TensorFlow 를 이용한 CNN



TensorFlow 와 같은 딥러닝을 위한 프레임워크를 사용하면, 손쉽게 개발을 할 수 있습니다.

하지만 각종 matrix 연산을 편하게 해 줄 뿐 - 물론 편의를 위한 util 들도 있습니다만, - NN 에 해당하는 것들은 직접 개발해 주어야 하죠. 그래서 contrib 에 해당하는 코드들도 많은 것이고요.





나만의 CNN 전용 스크립트




CNN 을 개발하다보면, 하이퍼 파라미터를 바꿔야 하는 일이 빈번합니다.

그리고 Network(Graph) 도 자주 바꿔야 하죠.

그때마다 일일이 스크립트를 수정하는 일은 매우 소모적이고 지루한 일이 아닐 수 없습니다.


작업을 하다가 Rollback 하는것도 쉽지 않고요.

그렇다고 매번 스크립트를 새로 만드는 것도 유지보수 차원에서 힘이 듧니다.

개발자가 가장 싫어하는 일명 '노가다'



제가 만들어 사용하고 있는 스크립트를 공개합니다. :)

배포용으로 만든것이 아니라, 어디까지나 제가 실제로 쓰고있는 것이라 네이밍이라든지, 코드라든지

깔끔하지 않은 부분들이 있다는점 미리 말씀드립니다.



TensorFlow 의 FLAG 를 사용했기에, 정리가 안되서 help 가 좀 복잡한데요.

먼저 help 를 통해 전체 옵션을 보면 아래와 같습니다.

참고로, mandatory 인지 optional 인지 표기는 신경쓰지 마세요.(잘못 된거에요 ㅎ)



$ python ./runner.py --help

usage: runner.py [-h] [--mode MODE] [--data_dir DATA_DIR] [--ckpt CKPT]
                 [--network NETWORK] [--pb PB] [--map MAP]
                 [--out_node OUT_NODE] [--event_dir EVENT_DIR]
                 [--out_dir OUT_DIR] [--model_version MODEL_VERSION]
                 [--image IMAGE] [--num_of_classes NUM_OF_CLASSES]
                 [--title TITLE] [--scaling [SCALING]] [--noscaling]
                 [--image_size IMAGE_SIZE] [--image_crop_size IMAGE_CROP_SIZE]
                 [--log_input [LOG_INPUT]] [--nolog_input]
                 [--grayscale [GRAYSCALE]] [--nograyscale]
                 [--log_feature [LOG_FEATURE]] [--nolog_feature]
                 [--use_fp16 [USE_FP16]] [--nouse_fp16]
                 [--max_steps MAX_STEPS] [--save_steps SAVE_STEPS]
                 [--batch_size BATCH_SIZE] [--continue_train [CONTINUE_TRAIN]]
                 [--nocontinue_train] [--log_frequency LOG_FREQUENCY]
                 [--log_device_placement [LOG_DEVICE_PLACEMENT]]
                 [--nolog_device_placement] [--error_dir ERROR_DIR]
                 [--learning_rate LEARNING_RATE]

optional arguments:
  -h, --help            show this help message and exit
  --mode MODE           Mode {train | eval | export | single | resize | pb |
                        map-export | map-inference} DEFAULT=eval
  --data_dir DATA_DIR   Path to data.
  --ckpt CKPT           The directory where checkpoint file is.
  --network NETWORK     File Path to network.
  --pb PB               pb File.
  --map MAP             Map File Path.
  --out_node OUT_NODE   Name of out node. DEFAULT=softmax_linear/softmax
  --event_dir EVENT_DIR
                        Path to events.
  --out_dir OUT_DIR     Path to data output(for resize mode).
  --model_version MODEL_VERSION
                        Model version for Tensorflow Serving.
  --image IMAGE         Image file for single evaluation.
  --num_of_classes NUM_OF_CLASSES
                        Number of classes.
  --title TITLE         Name of network. DEFAULT=unknown
  --scaling [SCALING]   Scaling Input Images. DEFAULT=True
  --noscaling
  --image_size IMAGE_SIZE
                        Image size(width == height) DEFAULT=100
  --image_crop_size IMAGE_CROP_SIZE
                        Image crop size(width == height) DEFAULT=80
  --log_input [LOG_INPUT]
                        Log input image. DEFAULT=True
  --nolog_input
  --grayscale [GRAYSCALE]
                        Make input images to grayscale. DEFAULT=True
  --nograyscale
  --log_feature [LOG_FEATURE]
                        Log feature maps. DEFAULT=False
  --nolog_feature
  --use_fp16 [USE_FP16]
                        Use float16. DEFAULT=False
  --nouse_fp16
  --max_steps MAX_STEPS
                        Max steps for training. DEFAULT=20000
  --save_steps SAVE_STEPS
                        How often to save steps. DEFAULT=100
  --batch_size BATCH_SIZE
                        Max steps for training. DEFAULT=64
  --continue_train [CONTINUE_TRAIN]
                        Training continued. DEFAULT=True
  --nocontinue_train
  --log_frequency LOG_FREQUENCY
                        How often to log result to the console. DEFAULT=100
  --log_device_placement [LOG_DEVICE_PLACEMENT]
                        Where to log device placement. DEFAULT=False
  --nolog_device_placement
  --error_dir ERROR_DIR
                        Path to copy errors.
  --learning_rate LEARNING_RATE
                        Learning rate. DEFAULT = 0.005




0. 사전에 알아둘 것



해당 스크립트는 Classification 을 위한 것입니다.

그리고 데이터는 별도 라벨링을 하지 않습니다.

폴더 명으로 자동 라벨링 됩니다.

그리고 폴더명은 0부터 시작하여 숫자로 되어 있어야 합니다.


예를들어 사탕, 사과, 딸기 라는 세개의 Class 가 있다면, 폴더의 구조는



이런형태가 되어야 합니다.

0 이 사탕, 1 이 사과, 2 가 딸기라는 정보는 따로 받지 않습니다.

index 와 string map 을 만들면 되긴 하는데, 저는 그냥 쓰고 있습니다. :)


Eval 과 Train 으로 폴더 구분한건, 필수사항이 아닙니다.

저는 그냥 저렇게 나눠놓는게 편하더라고요.







1. 학습



학습을 위한 커맨드는 다음과 같습니다.



python ./runner.py --mode=train --ckpt=/tmp/MyClassifier --data_dir=/DataSet/Object
/Train

이것이 기본 커맨드 입니다.
ckpt 는 TensorFlow 의 Saver 에 의해서 그래프, 메타정보, 모델 등이 저장될 위치입니다.
data_dir 은 아시겠지만, 학습할 데이터가 위치한 경로고요. 저 안에는 0, 1, 2, 3.... 형태로 이미지 데이터가 Class 에 맞게 나뉘어 들어가 있겠죠.



사용 가능한 모든 파라메타는 다음과 같습니다.



python ./runner.py --mode=train --ckpt=/tmp/MyClassifier --data_dir=/DataSet/Object
/Train --network=nn_default --max_steps=10000 --image_size=100 --image_crop_size=80 --continue_train=False --grayscale=False --scaling=True --log_feature=True --use_fp16=True --batch_size=64 --learning_rate=0.01

대충 파라미터를 보면 아실거라고 생각되니, 모든것에 대해서 자세한 설명은 따로 하지 않겠습니다.


알아둬야 할 점 몇 가지를 말씀 드립니다.


기본적으로 scaling 값은 True 입니다. 

내부적으로 학습에 정사각형 형태의 이미지를 사용하는데요.

직사각형을 넣게 되더라도 내부적으로 정사각형 이미지로 바꿔 줍니다.

그래서 학습하거나 검증할때, 이미지를 사전에 정사각형으로 만들어서 넣어 줄 필요가 없죠.


이때 True 값이면, 입력한 이미지를 정사각형 형태로 Scaling(크기는 image_size 파라미터 값) 해 줍니다.

만약 False 값이면, 입력한 이미지를 크기 조절없이 image_crop 값으로 Cropping 해 버립니다.


더불어 True 더라도, 이미지를 정사각형 형태로 먼저 scaling 하고, 이후에 image_crop 사이즈로 cropping 하는것은 마찬가지 입니다.



image_size 와 image_crop_size 가 동일하고, scaling 이 True 라면, cropping 은 무시되는 셈이죠. :)



그리고 network=nn_default 라는 부분이 있는데요.

매번 CNN 을 개발하다보면, 수시로 graph 구성을 바꾸게 됩니다. 이게 워낙 귀찮은 일이 아닐 수 없지요.


그래서 저는 network 부분만 독립된 파일로 구성하게 하였습니다.

소스를 열어보시면 아시겠지만, 


def crop_image(images):

def inference(images, keep_prob, batch_size, image_crop_size, input_channels, num_of_classes, variable_with_weight_decay, variable_on_cpu, activation_summary, log_input, log_feature):


이렇게 2개의 메소드만 들어있는 python 파일입니다.


crop_image 는 학습/검증 이전에 input 으로 들어가는 이미지를 수정할때 사용됩니다.


참, mode 값이 train 일때는 자동으로 입력된 이미지에 대해서 distortion 을 합니다.

밝기라든지, 색조라든지, 랜덤 크로핑이라든지..(랜던 크로핑은 image_size 와 image_crop_size 의 차이)..



이 이외의 작업을 하고 싶을때 crop_image() 메소드 부분에서 처리할 수 있습니다.

참고로 이미지를 입력 받고, 가장 먼저 호출되는 부분입니다.



grayscale 은 input 이미지를 1채널 흑백으로 할거냐, 3채널 그대로 쓸거냐의 옵션입니다.

기본값은 True 입니다. 그러니 3 채널 이미지를 그대로 쓰고 싶다면 옵션을 False 로 넣어주어야 합니다.


log_feature 는 graph 에서 각 conv 맵을 tensorboard 에서 확인 할 수 있도록, summary 에 넣을지 말지에 대한 옵션입니다.

기본값은 False 입니다.







2. 검증



검증을 위한 커맨드는 다음과 같습니다.



python ./runner.py --mode=eval --ckpt=/tmp/MyClassifier --data_dir=/DataSet/Object/Eval --event_dir=/tmp/MyClassifier/Eval


ckpt 는 앞서 학습할때 넣었던 경로 그대로 넣어줍니다.

data_dir 은 검증에 사용할 파일들이 있는 디렉토리고요. 마찬가지로 0, 1, 2, 3... 형태의 폴더들이 있습니다.

event_dir 은 evalutation 의 summary 를 저장할 경로입니다. TensorBoard 를 위한 것이죠.


이때, image_size 라든지 grayscale 이라든지 Train 할때 사용했던 하이퍼 파라미터는 내부적으로 불러와서 사용됩니다.

그러니 직접 입력할 필요가 없습니다.(물론 다르게 입력해도 안 됩니다.)






3. 단일파일 검증



폴더 통째로 Evaluation 하는것 이외에, 한 개의 이미지 파일을 넣어 어떤 Class 로 분류되었는지 확인할 수 있습니다.



python ./runner.py --mode=single --ckpt=/tmp/MyClassifier --image=/DataSet/Object/Apple.jpg





4. *.pb 파일로 export 하기



학습된 Graph, 와 Weight 를 포함한 모델파일을 export 할 수 있습니다.




python ./runner.py --mode=export --ckpt=/tmp/MyClassifier

이렇게 하고나면, frozon.pb 와 optimized.pb 파일 두 개가 나옵니다.

optimized.pb 를 사용하시면 됩니다.





5. *.pb 파일로 단일파일 검증



TensorFlow 의 ckpt 의 파일이 아닌, export 한 *.pb 파일로 검증할 수도 있습니다.



python ./runner.py --mode=pb --pb=/tmp/MyClassifier/optimized.pb --image=/DataSet/Object/Eval/0/Apple.jpg





7. Weight Map 추출하기



CNN 의 특정 Layer 의 Weights 들을 추출해서 파일로 저장할 수 있습니다.

이 weights 들은 다양한 곳에 쓰일 수 있습니다. :)



python ./runner.py --mode=map-export --pb=/tmp/MyClassifier/optimized.pb --data_dir=/DataSet/Object/Eval --map=/tmp/map.txt

이렇게 하면, Eval 안에 있는 모든 이미지 파일들을 찾아서, 지정한 layer 의 weight 값을 모두 map.txt 파일에 저장해 줍니다.

layer 이름은 --out_node 로 지정해 줄 수 있습니다. 기본값은 softmax_linear/softmax_linear:0 입니다.







8. Weight Map 을 가지고 단일파일의 weight distance 구하기



자세한 설명은 생략합니다. 설명하기가 복잡하네요.

단일 파일을 넣었을때, map 을 export 했던 Layer 의 weight 값이 distance 가 얼마나 차이가 나냐를 구하는 것입니다. -_-;

설명을 잘 못하겠네요.


아무튼 사용방법은 다음과 같습니다



python ./runner.py --mode=map-inference --pb=/tmp/MyClassifier/optimized.pb --map=/tmp/map.txt --image=/DataSet/mypic.jpg




이외에도 내부적으로 사용하는 하이퍼 파라메터들이 있는데, 옵션으로 빼도 좋을 것 같습니다.



새로운 graph(network)를 테스트 하려면, nn_default.py 를 복사해서 자신에게 맞게 수정해서 사용하시면 됩니다.

당연히 --network= 파라미터의 값으로 넣어주어야 하고요.



파일: CNN.zip





Git 로 올려두면, 관리하기가 귀찮고 배포용도 아니므로 그냥 파일첨부 합니다 :)