알 수 없는 알고리즘으로 해당 사이트로 접근했다면 이전 글들을 읽고 오시기 바랍니다.
developer-youn.tistory.com/35
이번 글은 크게 어려움이 없었습니다. 그래서 전체적으로 어떻게 진행했는지 작성하는 글입니다.
여기까지 따라오신 분들을 위해 현재 과정의 대략적 구조를 보여드리겠습니다.
8. 데이터 레이블링과 포맷 변환
데이터 레이블링은 labelImg 를 이용해서 진행합니다.
학습 데이터는 xml파일로 저장이 되며 아래 글은 labelImg를 통해 각 이미지 별 xml파일이 생성되었다는 가정하에 작성을 하겠습니다.
xml파일을 csv파일을 거쳐 record파일로 만들기 위해 아래 코드를 clone합니다.
git clone <https://github.com/datitran/raccoon_dataset.git>
이미지 파일과 xml파일들을 하나의 csv파일로 묶어야 합니다.
아래 코드를 통해 이미지와 xml파일이 같이 있는 디렉토리에서 xml파일의 정보만을 추출해 하나의 csv파일에 저장할 수 있습니다. clone 후 생성된 디렉토리에서 xml_to_csv.py를 일부 수정하였습니다.
import os
import glob
import pandas as pd
import xml.etree.ElementTree as ET
def xml_to_csv(path):
xml_list = []
for xml_file in glob.glob(path + '/*.xml'):
tree = ET.parse(xml_file)
root = tree.getroot()
for member in root.findall('object'):
value = (root.find('filename').text,
int(root.find('size')[0].text),
int(root.find('size')[1].text),
member[0].text,
int(member[4][0].text),
int(member[4][1].text),
int(member[4][2].text),
int(member[4][3].text)
)
xml_list.append(value)
column_name = ['filename', 'width', 'height', 'class', 'xmin', 'ymin', 'xmax', 'ymax']
xml_df = pd.DataFrame(xml_list, columns=column_name)
return xml_df
def main():
image_path = "PATH/TO/IMAGE/DIR"
print(image_path)
xml_df = xml_to_csv(image_path)
xml_df.to_csv('train.csv', index=None)
print('Successfully converted xml to csv.')
main()
label map파일을 작성, 수정해주어야 합니다. 내부 item들은 레이블링 했던 클래스들을 넣어주면 됩니다.
확장자는 .pbtxt이며 key, value 구조를 가지고 있습니다.
예시는 다음과 같습니다.
item {
id: 1
display_name: "dog"
}
item {
id: 2
display_name: "bike"
}
item {
id: 3
display_name: "truck"
}
item {
id: 4
display_name: "person"
}
item {
id: 5
display_name: "cat"
}
item {
id: 6
display_name: "zebra"
}
위 과정을 통해 csv파일을 만들고 label map을 만든 후 .record파일을 만들어야 합니다.
제공되는 generate_tfrecord.py를 수정하였습니다.
주의할 점은 class_text_to_int 함수의 내용을 label map에서 정의한 것과 유사하게 수정해야 합니다.
# -*- coding: utf-8 -*-
from __future__ import division
from __future__ import print_function
from __future__ import absolute_import
import os
import io
import pandas as pd
import tensorflow.compat.v1 as tf
from tensorflow import __version__
print(__version__)
from PIL import Image
from object_detection.utils import dataset_util
from collections import namedtuple, OrderedDict
flags = tf.app.flags
flags.DEFINE_string('csv_input', '', 'Path to the CSV input')
flags.DEFINE_string('output_path', '', 'Path to output TFRecord')
flags.DEFINE_string('image_dir', '', 'Path to images')
FLAGS = flags.FLAGS
# TO-DO replace this with label map
def class_text_to_int(row_label):
if row_label == 'dog':
return 1
elif row_label == 'bike':
return 2
elif row_label == 'truck':
return 3
elif row_label == 'person':
return 4
elif row_label == 'cat':
return 5
elif row_label == 'zebra':
return 6
else:
return 0
def split(df, group):
data = namedtuple('data', ['filename', 'object'])
gb = df.groupby(group)
return [data(filename, gb.get_group(x)) for filename, x in zip(gb.groups.keys(), gb.groups)]
def create_tf_example(group, path):
with tf.gfile.GFile(os.path.join(path, '{}'.format(group.filename)), 'rb') as fid:
encoded_jpg = fid.read()
encoded_jpg_io = io.BytesIO(encoded_jpg)
image = Image.open(encoded_jpg_io)
width, height = image.size
filename = group.filename.encode('utf8')
image_format = b'jpg'
xmins = []
xmaxs = []
ymins = []
ymaxs = []
classes_text = []
classes = []
for index, row in group.object.iterrows():
xmins.append(row['xmin'] / width)
xmaxs.append(row['xmax'] / width)
ymins.append(row['ymin'] / height)
ymaxs.append(row['ymax'] / height)
classes_text.append(row['class'].encode('utf8'))
classes.append(class_text_to_int(row['class']))
tf_example = tf.train.Example(features=tf.train.Features(feature={
'image/height': dataset_util.int64_feature(height),
'image/width': dataset_util.int64_feature(width),
'image/filename': dataset_util.bytes_feature(filename),
'image/source_id': dataset_util.bytes_feature(filename),
'image/encoded': dataset_util.bytes_feature(encoded_jpg),
'image/format': dataset_util.bytes_feature(image_format),
'image/object/bbox/xmin': dataset_util.float_list_feature(xmins),
'image/object/bbox/xmax': dataset_util.float_list_feature(xmaxs),
'image/object/bbox/ymin': dataset_util.float_list_feature(ymins),
'image/object/bbox/ymax': dataset_util.float_list_feature(ymaxs),
'image/object/class/text': dataset_util.bytes_list_feature(classes_text),
'image/object/class/label': dataset_util.int64_list_feature(classes),
}))
return tf_example
def main(_):
writer = tf.python_io.TFRecordWriter(FLAGS.output_path)
path = os.path.join(FLAGS.image_dir)
examples = pd.read_csv(FLAGS.csv_input)
grouped = split(examples, 'filename')
for group in grouped:
tf_example = create_tf_example(group, path)
writer.write(tf_example.SerializeToString())
writer.close()
output_path = os.path.join(os.getcwd(), FLAGS.output_path)
print('Successfully created the TFRecords: {}'.format(output_path))
if __name__ == '__main__':
tf.app.run()
위 코드의 사용법은 다음과 같습니다.
python generate_tfrecord.py --csv_input=PATH/TO/.CSV_FILE --output_path=PATH/FOR/CREATE/.RECORD_FILE--image_dir=PATH/TO/IMAGE_DATA/DIRECOTRY
example)
python generate_tfrecord.py --csv_input=./data/train.csv --output_path=./data/train.record --image_dir=./data/imageDir
9. 데이터 학습 및 모델 추출
Tensorflow2 object detection API Model Zoo에서 성능 지표를 본 후 적합한 모델을 선택하여 진행합니다.(https://github.com/tensorflow/models/blob/master/research/object_detection/g3doc/tf2_detection_zoo.md)
Google Tensorflow팀에서 추천하는 디렉토리 구조는 다음과 같습니다.
Object_detection
├── data/
│ ├── eval-00000-of-00001.tfrecord
│ ├── label_map.txt
│ ├── train-00000-of-00002.tfrecord
│ └── train-00001-of-00002.tfrecord
└── models/
└── my_model_dir/
├── eval/ # Created by evaluation job.
├── my_model.config
└── model_ckpt-100-data@1 #
└── model_ckpt-100-index # Created by training job.
└── checkpoint #
이후 해야 할 일은 다음과 같습니다.
- object_detection 디렉토리 밑에 있는 data디렉토리에 label map과 tfrecord파일들을 넣어줍니다.
- 위에서 선정한 딥러닝 모델을 압축 해제하여 models밑에 새로 디렉토리를 생성한 후 그 밑에 압축 해제합니다.
- 압축을 해제하면 해당 디렉토리에 checkpoint, saved_model디렉토리와 pipeline.config 파일이 존재합니다.
- pipeline.config파일을 수정해야 합니다.
pipeline 수정
- num_classes에 대해서는 label map에 있는 총 클래스 개수를 적어줍니다.
- batch_size는 1로 설정한 후 조금씩 올려가며 해당 gpu 메모리 상황에 맞게 값을 정해줍니다.
- total_steps와 num_steps도 적당히 설정해주어야 합니다. 적당히라고 하는 이유는 과도하게 설정하는 경우 역전파 과정에서 loss함수에서 오류가 발생해 loss율이 nan이 나올 수 있기 때문입니다.(learning rate도 이 부분에 대해 관련이 있어 전반적으로 수정을 해나갈 필요가 있습니다.)
- fine_tune_checkpoint는 위에서 압축 해제한 모델의 checkpoint경로를 상대 경로로 잡아주어야 합니다.
- label_map_path에는 data밑에 있는 label map파일을 설정해줍니다. 데이터셋 생성과정에서 사용한 label map의 경로를 잡아주면 됩니다.
- input_path에는 각 reader의 타입에 맞게 train.record와 test.record파일의 경로를 잡아줍니다.
- 저는 이 부분에서 모든 경로를 상대 경로로 작성했습니다.
- 절대 경로로 하면 dead lock걸려서 상대 경로로 고치니까 잘되어서 이 부분은 차이가 있을 수 있습니다.
학습
이제 명령어를 입력해서 정말 학습을 진행합니다.
그전에 위에 개인 model폴더에 temp_ckpt나 다른 이름으로 자유롭게 checkpoint 가 저장될 디렉토리를 하나 생성한다.
이후 research 폴더에서 명령어를 통해 model_main_tf2.py를 실행합니다. 예시는 다음과 같습니다.
python model_main_tf2.py --pipeline_config_path=./models/yhj_ssd_efficientdet/efficientdet_d2_coco17_tpu-32/pipeline.config --model_dir=./models/yhj_ssd_efficientdet/efficientdet_d2_coco17_tpu-32/ckpt_temp/
pipeline_config_path에는 조금 전 수정했던 pipeline이 존재하는 path를 입력하고 --model_dir에는 조금 전 생성했던 디렉토리의 경로를 잡아줍니다.
학습 후 모델 export
학습된 모델을 사용할 수 있게 export 하는 과정이 필요합니다. exporter_main_v2.py를 수행합니다. 예시는 다음과 같습니다.
python ./object_detection/exporter_main_v2.py --input_type image_tensor --pipeline_config_path ./object_detection/models/yhj_ssd_resnet/pipeline.config --trained_checkpoint_dir ./object_detection/models/yhj_ssd_resnet/ckpt_temp/ --output_directory ./object_detection/models/yhj_ssd_resnet/exported_model/
학습에서 입력한 옵션에 추가적으로 --output_directory는 외부로 export 될 파일이 저장될 경로를 입력해줍니다.
다음 글에서는 학습된 모델을 테스트할 수 있는 예제 코드와 제가 겪었던 에러, 성능 튜닝 경험에 대해 적도록 하겠습니다.
'Python > 딥러닝 (Deep-Learning)' 카테고리의 다른 글
우리 EasyOCR로 한번 가자(1) (2) | 2021.04.13 |
---|---|
Tensorlfow Object Detection API 사용 중 발생한 에러 정리 (0) | 2021.03.25 |
눈물없이는 볼 수 없는 회사 딥러닝 프레임워크 업데이트 모험기(2) (0) | 2021.02.21 |
눈물없이는 볼 수 없는 회사 딥러닝 프레임워크 업데이트 모험기(1) (0) | 2021.01.30 |
Mask R cnn native 설치하기 (0) | 2019.08.28 |