CastleJo의 개발일지

Object Detection - Config 만들기

|

강의를 아직 제대로 수강하고 정리하지 않았다.

일단 베이스라인 코드를 구현하려고 노력했고, 그에 대한 정리를 해야겠다.

Object Detection을 위한 High level 프레임워크

detectron2와 mmdetection 두 가지로, pytorch를 상당히 높은 레벨로 포장한 라이브러리가 있다.
멘토님이 말해주신 스위치를 켰다 껏다 하는 행위를 하기에 정말 알맞는… 물론 좋은 라이브러리이고 상당히 좋은 성능을 보이는 것은 맞지만, 낭만이없다 낭만이…

그나마 detectron2가 좀 더 자유롭다고 하여, 그 부분으로 일단 해보자! 라고 생각했다.

하지만 Baseline 코드가 내가 보기엔 hard-coding되어있다고 생각했다. 그래서 내 나름대로 Config 파일을 만들기로 결심하였다.

configparser

json파일로도 설정할 수 있겠지만, Python Runtime 내에서는 이게 더 편한것같다.

간단하게 사용법을 적어놓고 가야겠다.

import configparser

## 새로 만들기  
config = configparser.ConfigParser()  

config['general'] = {}
config['general']['~~~'] = ~~~ 
config['general']['~~~'] = ~~~ 

config['hyperparam'] = {}
config['hyperparam']['~~~'] = ~~~

뭐 이런식으로 일단 Runtime 내에서 설정을 해준다. 개인적으로 이렇게 간단히 만들어주는건 노트북 내에서 설정하는게 제일 좋은것같다.

## 저장
with open('config.ini', 'w', encoding='utf-8') as configfile:
    config.write(configfile)

## 불러오기
config = configparser.ConfigParser().read('config.ini', encoding='utf-8')

이렇게 파일로 만들어서 사용할 수 있다.

## section 확인

for section in config.sections():
	print(section)
	i = iter(config[section])
	while i:
		try:
			key = next(i)
			value = config[section][key]
			print("  ",key,":", value)
		except StopIteration:
			break

이 코드를 적용하면 마치 Json파일의 형태처럼 무슨 값이 들어있는지 세팅할 수 있다.

하지만 주의해야할 점은 모든 값이 문자열 형태로 들어간다는 것이다.

그래서 Config를 불러오는 클래스에서는 따로 정제를 해주어야 한다.

# load_config.py
import configparser
import re
from easydict import EasyDict as edict
import os
from datetime import datetime, timedelta

def _refineVal(x):
	key,val = x
	
	if val.isdigit():
		val = int(val)

	elif re.match(r'^-?\d+(?:\.\d+)$', val):
		val = float(val)

	elif re.match("^\(",val):
		val = tuple(map(int,(val[1:-1].split(","))))
	
	elif key in "classes":
		val = [x for x in val.split("  ")]
	
	elif key in "resume":
		val = True if val in "True" else False

	return key, val

def _refineOutputPath(isTrain, outputDir, customName):
	subPath = "train" if isTrain else "eval"
	nowTime = datetime.now() + timedelta(hours=9)

	cnt = 0
	
	while True:
		addedName = f'{nowTime.strftime("%m-%d")}_{customName}_{cnt:02}'
		path = os.path.join(outputDir,addedName,subPath)
		cnt+=1
		
		if not os.path.exists(path) : 
			return path


def makeDictByConfig():
	parser = configparser.ConfigParser()
	parser.read('config/config.ini', encoding='utf-8')
	
	cfgDict = edict()

	for sect in parser.sections():
		item = [_refineVal(x) for x in parser.items(sect)]
		cfgDict[sect] = dict(item)
	
	cfgDict.path.output_dir = _refineOutputPath(True, cfgDict.path.output_dir, cfgDict.name.custom_model)
	cfgDict.path.output_eval_dir = _refineOutputPath(False, cfgDict.path.output_eval_dir, cfgDict.name.custom_model)
	return cfgDict

나는 이런식으로 중복된 이름일 때 폴더에 자동으로 번호를 매겨주어 모델을 구분하고, 각종 문자열을 정제하는 함수를 만들어서 사용했다.

그렇게 한다면 main문이나, 다른 아무데서나

def main():

	# Call Initialization File 
	customDict = makeDictByConfig()

	# Set Seed
	setSeed(customDict.hyperparam.seed)
	
	# Register Dataset
	registerDataset(
		datasetName = customDict.name.train_dataset,
		jsonPath = customDict.path.train_json,
		imageDirPath = customDict.path.image_root,
		numOfClasses = customDict.general.classes
	)

	registerDataset(
		datasetName = customDict.name.test_dataset,
		jsonPath = customDict.path.test_json,
		imageDirPath = customDict.path.image_root,
		numOfClasses = customDict.general.classes
	)

뭐 이런식으로 직관적으로 사용할 수 있다.

추후 Config파일을 Output 폴더 내에 저장하여, 값을 손쉽게 볼 수 있게 만들 예정이다.