hytos / DTI_PID / WebServer / symbol_training / train.py @ bb5f022f
이력 | 보기 | 이력해설 | 다운로드 (13.9 KB)
1 |
"""
|
---|---|
2 |
Training For Small Object
|
3 |
-> parser에서 data_path와 pre_train_model 경로만 지정해준 후 돌리면 됩니다.
|
4 |
"""
|
5 |
import os |
6 |
import argparse |
7 |
import torch.nn as nn |
8 |
from torch.utils.data import DataLoader |
9 |
from src.doftech_dataset import DoftechDataset, DoftechDatasetTest |
10 |
from src.utils import * |
11 |
from src.loss import YoloLoss |
12 |
from src.yolo_net import Yolo |
13 |
from src.yolo_doftech import YoloD |
14 |
import shutil |
15 |
import visdom |
16 |
import cv2 |
17 |
import pickle |
18 |
import numpy as np |
19 |
from src.vis_utils import array_tool as at |
20 |
from src.vis_utils.vis_tool import visdom_bbox |
21 |
|
22 |
loss_data = {'X': [], 'Y': [], 'legend_U':['total', 'coord', 'conf', 'cls']} |
23 |
#visdom = visdom.Visdom(port='8080')
|
24 |
|
25 |
def get_args(): |
26 |
parser = argparse.ArgumentParser("You Only Look Once: Unified, Real-Time Object Detection")
|
27 |
parser.add_argument("--image_size", type=int, default=448, help="The common width and height for all images") |
28 |
parser.add_argument("--batch_size", type=int, default=10, help="The number of images per batch") |
29 |
|
30 |
# Training 기본 Setting
|
31 |
parser.add_argument("--momentum", type=float, default=0.9) |
32 |
parser.add_argument("--decay", type=float, default=0.0005) |
33 |
parser.add_argument("--dropout", type=float, default=0.5) |
34 |
parser.add_argument("--num_epoches", type=int, default=1000) |
35 |
parser.add_argument("--test_interval", type=int, default=20, help="Number of epoches between testing phases") |
36 |
parser.add_argument("--object_scale", type=float, default=1.0) |
37 |
parser.add_argument("--noobject_scale", type=float, default=0.5) |
38 |
parser.add_argument("--class_scale", type=float, default=1.0) |
39 |
parser.add_argument("--coord_scale", type=float, default=5.0) |
40 |
parser.add_argument("--reduction", type=int, default=32) |
41 |
parser.add_argument("--es_min_delta", type=float, default=0.0, |
42 |
help="Early stopping's parameter: minimum change loss to qualify as an improvement")
|
43 |
parser.add_argument("--es_patience", type=int, default=0, |
44 |
help="Early stopping's parameter: number of epochs with no improvement after which training will be stopped. Set to 0 to disable this technique.")
|
45 |
|
46 |
# 확인해야 하는 PATH
|
47 |
parser.add_argument("--data_path", type=str, default="D:/data/DATA_Doftech/small_symbol/training", help="the root folder of dataset") # 학습 데이터 경로 -> image와 xml의 상위 경로 입력 |
48 |
parser.add_argument("--data_path_test", type=str, default="./data/Test", help="the root folder of dataset") # 테스트 데이터 경로 -> test할 이미지만 넣으면 됨 |
49 |
#parser.add_argument("--pre_trained_model_type", type=str, choices=["model", "params"], default="model")
|
50 |
parser.add_argument("--pre_trained_model_path", type=str, default="trained_models/only_params_trained_yolo_voc") # Pre-training 된 모델 경로 |
51 |
|
52 |
parser.add_argument("--saved_path", type=str, default="./checkpoint") # training 된 모델 저장 경로 |
53 |
parser.add_argument("--conf_threshold", type=float, default=0.35) |
54 |
parser.add_argument("--nms_threshold", type=float, default=0.5) |
55 |
args = parser.parse_args() |
56 |
return args
|
57 |
|
58 |
# 형상 CLASS
|
59 |
DOFTECH_CLASSES= ['gate', 'globe', 'butterfly', 'check', 'ball', 'relief', |
60 |
'3way_solenoid', 'gate_pressure', 'globe_pressure', 'butterfly_pressure', 'ball_shutoff', 'ball_pressure','ball_motor', 'plug_pressure', |
61 |
'circle', 'inst_console', 'inst_console_dcs', 'inst_console_sih', 'logic_dcs', 'utility', 'specialty_items', 'logic', 'logic_local_console_dcs', |
62 |
'reducer', 'blind_spectacle_open', 'blind_insertion_open', 'blind_spectacle_close', 'blind_insertion_close', |
63 |
'strainer_basket', 'strainer_conical', 'fitting_capillary_tubing', 'meter_ultrasonic', 'strainer_y', 'tube_pitot' |
64 |
,'opc']
|
65 |
|
66 |
print(len(DOFTECH_CLASSES))
|
67 |
|
68 |
def train(name=None, classes=None, root_path=None, pre_trained_model_path=None): |
69 |
DOFTECH_CLASSES = classes |
70 |
|
71 |
parser = argparse.ArgumentParser("You Only Look Once: Unified, Real-Time Object Detection")
|
72 |
parser.add_argument("--image_size", type=int, default=448, help="The common width and height for all images") |
73 |
parser.add_argument("--batch_size", type=int, default=10, help="The number of images per batch") |
74 |
|
75 |
# Training 기본 Setting
|
76 |
parser.add_argument("--momentum", type=float, default=0.9) |
77 |
parser.add_argument("--decay", type=float, default=0.0005) |
78 |
parser.add_argument("--dropout", type=float, default=0.5) |
79 |
parser.add_argument("--num_epoches", type=int, default=1000) |
80 |
parser.add_argument("--test_interval", type=int, default=20, help="Number of epoches between testing phases") |
81 |
parser.add_argument("--object_scale", type=float, default=1.0) |
82 |
parser.add_argument("--noobject_scale", type=float, default=0.5) |
83 |
parser.add_argument("--class_scale", type=float, default=1.0) |
84 |
parser.add_argument("--coord_scale", type=float, default=5.0) |
85 |
parser.add_argument("--reduction", type=int, default=32) |
86 |
parser.add_argument("--es_min_delta", type=float, default=0.0, |
87 |
help="Early stopping's parameter: minimum change loss to qualify as an improvement")
|
88 |
parser.add_argument("--es_patience", type=int, default=0, |
89 |
help="Early stopping's parameter: number of epochs with no improvement after which training will be stopped. Set to 0 to disable this technique.")
|
90 |
|
91 |
# 확인해야 하는 PATH
|
92 |
parser.add_argument("--data_path", type=str, default=os.path.join(root_path, 'training'), help="the root folder of dataset") # 학습 데이터 경로 -> image와 xml의 상위 경로 입력 |
93 |
parser.add_argument("--data_path_test", type=str, default=os.path.join(root_path, 'test'), help="the root folder of dataset") # 테스트 데이터 경로 -> test할 이미지만 넣으면 됨 |
94 |
#parser.add_argument("--pre_trained_model_type", type=str, choices=["model", "params"], default="model")
|
95 |
parser.add_argument("--pre_trained_model_path", type=str, default=pre_trained_model_path) # Pre-training 된 모델 경로 |
96 |
|
97 |
parser.add_argument("--saved_path", type=str, default=os.path.join(root_path, 'checkpoint')) # training 된 모델 저장 경로 |
98 |
parser.add_argument("--conf_threshold", type=float, default=0.35) |
99 |
parser.add_argument("--nms_threshold", type=float, default=0.5) |
100 |
opt = parser.parse_args() |
101 |
|
102 |
|
103 |
if torch.cuda.is_available():
|
104 |
torch.cuda.manual_seed(123)
|
105 |
else:
|
106 |
torch.manual_seed(123)
|
107 |
learning_rate_schedule = {"0": 1e-5, "5": 1e-4, |
108 |
"80": 1e-5, "110": 1e-6} |
109 |
|
110 |
training_params = {"batch_size": opt.batch_size,
|
111 |
"shuffle": True, |
112 |
"drop_last": True, |
113 |
"collate_fn": custom_collate_fn}
|
114 |
|
115 |
test_params = {"batch_size": opt.batch_size,
|
116 |
"shuffle": False, |
117 |
"drop_last": False, |
118 |
"collate_fn": custom_collate_fn}
|
119 |
|
120 |
training_set = DoftechDataset(opt.data_path, opt.image_size, is_training=True, classes=DOFTECH_CLASSES)
|
121 |
training_generator = DataLoader(training_set, **training_params) |
122 |
|
123 |
test_set = DoftechDatasetTest(opt.data_path_test, opt.image_size, is_training=False, classes=DOFTECH_CLASSES)
|
124 |
test_generator = DataLoader(test_set, **test_params) |
125 |
|
126 |
pre_model = Yolo(20).cuda()
|
127 |
pre_model.load_state_dict(torch.load(opt.pre_trained_model_path), strict=False)
|
128 |
|
129 |
model = YoloD(pre_model, training_set.num_classes).cuda() |
130 |
|
131 |
nn.init.normal_(list(model.modules())[-1].weight, 0, 0.01) |
132 |
|
133 |
criterion = YoloLoss(training_set.num_classes, model.anchors, opt.reduction) |
134 |
optimizer = torch.optim.SGD(model.parameters(), lr=1e-5, momentum=opt.momentum, weight_decay=opt.decay)
|
135 |
|
136 |
best_loss = 1e10
|
137 |
best_epoch = 0
|
138 |
model.train() |
139 |
num_iter_per_epoch = len(training_generator)
|
140 |
|
141 |
loss_step = 0
|
142 |
|
143 |
for epoch in range(opt.num_epoches): |
144 |
if str(epoch) in learning_rate_schedule.keys(): |
145 |
for param_group in optimizer.param_groups: |
146 |
param_group['lr'] = learning_rate_schedule[str(epoch)] |
147 |
|
148 |
for iter, batch in enumerate(training_generator): |
149 |
image, label = batch |
150 |
if torch.cuda.is_available():
|
151 |
image = Variable(image.cuda(), requires_grad=True)
|
152 |
else:
|
153 |
image = Variable(image, requires_grad=True)
|
154 |
|
155 |
optimizer.zero_grad() |
156 |
logits = model(image) |
157 |
loss, loss_coord, loss_conf, loss_cls = criterion(logits, label) |
158 |
loss.backward() |
159 |
|
160 |
optimizer.step() |
161 |
|
162 |
if iter % opt.test_interval == 0: |
163 |
print("Epoch: {}/{}, Iteration: {}/{}, Lr: {}, Loss:{:.2f} (Coord:{:.2f} Conf:{:.2f} Cls:{:.2f})".format
|
164 |
(epoch + 1, opt.num_epoches, iter + 1, num_iter_per_epoch, optimizer.param_groups[0]['lr'], loss, |
165 |
loss_coord,loss_conf,loss_cls)) |
166 |
|
167 |
predictions = post_processing(logits, opt.image_size, DOFTECH_CLASSES, model.anchors, opt.conf_threshold, |
168 |
opt.nms_threshold) |
169 |
|
170 |
gt_image = at.tonumpy(image[0])
|
171 |
gt_image = visdom_bbox(gt_image, label[0])
|
172 |
#visdom.image(gt_image, opts=dict(title='gt_box_image'), win=3)
|
173 |
|
174 |
if len(predictions) != 0: |
175 |
image = at.tonumpy(image[0])
|
176 |
box_image = visdom_bbox(image, predictions[0])
|
177 |
#visdom.image(box_image, opts=dict(title='box_image'), win=2)
|
178 |
|
179 |
elif len(predictions) == 0: |
180 |
box_image = tensor2im(image) |
181 |
#visdom.image(box_image.transpose([2, 0, 1]), opts=dict(title='box_image'), win=2)
|
182 |
|
183 |
loss_dict = { |
184 |
'total' : loss.item(),
|
185 |
'coord' : loss_coord.item(),
|
186 |
'conf' : loss_conf.item(),
|
187 |
'cls' : loss_cls.item()
|
188 |
} |
189 |
|
190 |
#visdom_loss(visdom, loss_step, loss_dict)
|
191 |
loss_step = loss_step + 1
|
192 |
|
193 |
if epoch % opt.test_interval == 0: |
194 |
model.eval() |
195 |
loss_ls = [] |
196 |
loss_coord_ls = [] |
197 |
loss_conf_ls = [] |
198 |
loss_cls_ls = [] |
199 |
for te_iter, te_batch in enumerate(test_generator): |
200 |
te_image, te_label = te_batch |
201 |
num_sample = len(te_label)
|
202 |
if torch.cuda.is_available():
|
203 |
te_image = te_image.cuda() |
204 |
with torch.no_grad():
|
205 |
te_logits = model(te_image) |
206 |
batch_loss, batch_loss_coord, batch_loss_conf, batch_loss_cls = criterion(te_logits, te_label) |
207 |
loss_ls.append(batch_loss * num_sample) |
208 |
loss_coord_ls.append(batch_loss_coord * num_sample) |
209 |
loss_conf_ls.append(batch_loss_conf * num_sample) |
210 |
loss_cls_ls.append(batch_loss_cls * num_sample) |
211 |
|
212 |
te_loss = sum(loss_ls) / test_set.__len__()
|
213 |
te_coord_loss = sum(loss_coord_ls) / test_set.__len__()
|
214 |
te_conf_loss = sum(loss_conf_ls) / test_set.__len__()
|
215 |
te_cls_loss = sum(loss_cls_ls) / test_set.__len__()
|
216 |
print("Test>> Epoch: {}/{}, Lr: {}, Loss:{:.2f} (Coord:{:.2f} Conf:{:.2f} Cls:{:.2f})".format(
|
217 |
epoch + 1, opt.num_epoches, optimizer.param_groups[0]['lr'], te_loss, te_coord_loss, te_conf_loss, te_cls_loss)) |
218 |
|
219 |
model.train() |
220 |
if te_loss + opt.es_min_delta < best_loss:
|
221 |
best_loss = te_loss |
222 |
best_epoch = epoch |
223 |
print("SAVE MODEL")
|
224 |
torch.save(model.state_dict(), os.path.join(opt.saved_path, name + "_only_params.pth"))
|
225 |
torch.save(model, os.path.join(opt.saved_path, name + "_whole_model.pth"))
|
226 |
|
227 |
# Early stopping
|
228 |
if epoch - best_epoch > opt.es_patience > 0: |
229 |
print("Stop training at epoch {}. The lowest loss achieved is {}".format(epoch, te_loss))
|
230 |
break
|
231 |
|
232 |
def visdom_loss(visdom, loss_step, loss_dict): |
233 |
loss_data['X'].append(loss_step)
|
234 |
loss_data['Y'].append([loss_dict[k] for k in loss_data['legend_U']]) |
235 |
visdom.line( |
236 |
X=np.stack([np.array(loss_data['X'])] * len(loss_data['legend_U']), 1), |
237 |
Y=np.array(loss_data['Y']),
|
238 |
win=30,
|
239 |
opts=dict(xlabel='Step', |
240 |
ylabel='Loss',
|
241 |
title='YOLO_V2',
|
242 |
legend=loss_data['legend_U']),
|
243 |
update='append'
|
244 |
) |
245 |
|
246 |
def tensor2im(image_tensor, imtype=np.uint8): |
247 |
|
248 |
image_numpy = image_tensor[0].detach().cpu().float().numpy()
|
249 |
|
250 |
image_numpy = (np.transpose(image_numpy, (1, 2, 0))) |
251 |
|
252 |
image_numpy = np.clip(image_numpy, 0, 255) |
253 |
|
254 |
return image_numpy.astype(imtype)
|
255 |
|
256 |
def denormalize(tensors): |
257 |
""" Denormalizes image tensors using mean and std """
|
258 |
mean = np.array([0.5, 0.5, 0.5]) |
259 |
std = np.array([0.5, 0.5, 0.5]) |
260 |
|
261 |
# mean = np.array([0.47571, 0.50874, 0.56821])
|
262 |
# std = np.array([0.10341, 0.1062, 0.11548])
|
263 |
|
264 |
denorm = tensors.clone() |
265 |
|
266 |
for c in range(tensors.shape[1]): |
267 |
denorm[:, c] = denorm[:, c].mul_(std[c]).add_(mean[c]) |
268 |
|
269 |
denorm = torch.clamp(denorm, 0, 255) |
270 |
|
271 |
return denorm
|
272 |
|
273 |
if __name__ == "__main__": |
274 |
datas = ['gate', 'globe', 'butterfly', 'check', 'ball', 'relief', |
275 |
'3way_solenoid', 'gate_pressure', 'globe_pressure', 'butterfly_pressure', 'ball_shutoff', 'ball_pressure','ball_motor', 'plug_pressure', |
276 |
'inst', 'func_valve', 'inst_console', 'inst_console_dcs', 'inst_console_sih', 'logic_dcs', 'utility', 'specialty_items', 'logic', 'logic_local_console_dcs', |
277 |
'reducer', 'blind_spectacle_open', 'blind_insertion_open', 'blind_spectacle_close', 'blind_insertion_close', |
278 |
'strainer_basket', 'strainer_conical', 'fitting_capillary_tubing', 'meter_ultrasonic', 'strainer_y', 'tube_pitot', |
279 |
'opc']
|
280 |
data_path = os.path.join(os.path.dirname(os.path.realpath(__file__)) + '\\Data\\', 'VnV') |
281 |
train(name='VnV', classes=datas, root_path=data_path, pre_trained_model_path=os.path.dirname(os.path.realpath(
|
282 |
__file__)) + '\\pre_trained_model\\only_params_trained_yolo_voc')
|