def train(self): """Train an FCN""" optimizer = Adam(lr=1e-3) loss = 'categorical_crossentropy' self.fcn.compile(optimizer=optimizer, loss=loss) log = "# of classes %d" % self.n_classes print_log(log, self.args.verbose) log = "Batch size: %d" % self.args.batch_size print_log(log, self.args.verbose) # prepare callbacks for saving model weights # and learning rate scheduler # model weights are saved when test iou is highest # learning rate decreases by 50% every 20 epochs # after 40th epoch accuracy = AccuracyCallback(self) scheduler = LearningRateScheduler(lr_scheduler) callbacks = [accuracy, scheduler] # train the fcn network self.fcn.fit(x=self.train_generator, use_multiprocessing=False, callbacks=callbacks, epochs=self.args.epochs)
def restore_weights(self): """Load previously trained model weights""" if self.args.restore_weights: save_dir = os.path.join(os.getcwd(), self.args.save_dir) filename = os.path.join(save_dir, self.args.restore_weights) log = "Loading weights: %s" % filename print_log(log, self.args.verbose) self.fcn.load_weights(filename)
def preload_test(self): """Pre-load test dataset to save time """ path = os.path.join(self.args.data_path, self.args.test_labels) # ground truth data is stored in an npy file self.test_dictionary = np.load(path, allow_pickle=True).flat[0] self.test_keys = np.array(list(self.test_dictionary.keys())) print_log("Loaded %s" % path, self.args.verbose)
def eval_init(self): # model weights are saved for future validation # prepare model model saving directory. save_dir = os.path.join(os.getcwd(), self.args.save_dir) if not os.path.isdir(save_dir): os.makedirs(save_dir) model_name = self.backbone.name model_name += '-' + str(self.args.layers) + "layer-" model_name += self.args.dataset model_name += '-best-iou.h5' log = "Weights filename: %s" % model_name print_log(log, self.args.verbose) self.weights_path = os.path.join(save_dir, model_name) self.preload_test() self.miou = 0 self.miou_history = [] self.mpla_history = []
def eval(self): """Evaluate a trained FCN model using mean IoU metric. """ s_iou = 0 s_pla = 0 # evaluate iou per test image eps = np.finfo(float).eps for key in self.test_keys: # load a test image image_path = os.path.join(self.args.data_path, key) image = skimage.img_as_float(imread(image_path)) segmentation = self.segment_objects(image) # load test image ground truth labels gt = self.test_dictionary[key] i_pla = 100 * (gt == segmentation).all(axis=(2)).mean() s_pla += i_pla i_iou = 0 n_masks = 0 # compute mask for each object in the test image # including background for i in range(self.n_classes): if np.sum(gt[..., i]) < eps: continue mask = segmentation[..., i] intersection = mask * gt[..., i] union = np.ceil((mask + gt[..., i]) / 2.0) intersection = np.sum(intersection) union = np.sum(union) if union > eps: iou = intersection / union i_iou += iou n_masks += 1 # average iou per image i_iou /= n_masks if not self.args.train: log = "%s: %d objs, miou=%0.4f ,pla=%0.2f%%"\ % (key, n_masks, i_iou, i_pla) print_log(log, self.args.verbose) # accumulate all image ious s_iou += i_iou if self.args.plot: self.evaluate(key, image) n_test = len(self.test_keys) m_iou = s_iou / n_test self.miou_history.append(m_iou) np.save("miou_history.npy", self.miou_history) m_pla = s_pla / n_test self.mpla_history.append(m_pla) np.save("mpla_history.npy", self.mpla_history) if m_iou > self.miou and self.args.train: log = "\nOld best mIoU=%0.4f, New best mIoU=%0.4f, Pixel level accuracy=%0.2f%%"\ % (self.miou, m_iou, m_pla) print_log(log, self.args.verbose) self.miou = m_iou print_log("Saving weights... %s"\ % self.weights_path,\ self.args.verbose) self.fcn.save_weights(self.weights_path) else: log = "\nCurrent mIoU=%0.4f, Pixel level accuracy=%0.2f%%"\ % (m_iou, m_pla) print_log(log, self.args.verbose)
def evaluate_test(self): # test labels csv path path = os.path.join(self.args.data_path, self.args.test_labels) # test dictionary dictionary, _ = build_label_dictionary(path) keys = np.array(list(dictionary.keys())) # sum of precision s_precision = 0 # sum of recall s_recall = 0 # sum of IoUs s_iou = 0 # evaluate per image for key in keys: # grounnd truth labels labels = np.array(dictionary[key]) # 4 boxes coords are 1st four items of labels gt_boxes = labels[:, 0:-1] # last one is class gt_class_ids = labels[:, -1] # load image id by key image_file = os.path.join(self.args.data_path, key) image = skimage.img_as_float(imread(image_file)) image, classes, offsets = self.detect_objects(image) # perform nms _, _, class_ids, boxes = show_boxes(args, image, classes, offsets, self.feature_shapes, show=False) boxes = np.reshape(np.array(boxes), (-1,4)) # compute IoUs iou = layer_utils.iou(gt_boxes, boxes) # skip empty IoUs if iou.size ==0: continue # the class of predicted box w/ max iou maxiou_class = np.argmax(iou, axis=1) # true positive tp = 0 # false positiove fp = 0 # sum of objects iou per image s_image_iou = [] for n in range(iou.shape[0]): # ground truth bbox has a label if iou[n, maxiou_class[n]] > 0: s_image_iou.append(iou[n, maxiou_class[n]]) # true positive has the same class and gt if gt_class_ids[n] == class_ids[maxiou_class[n]]: tp += 1 else: fp += 1 # objects that we missed (false negative) fn = abs(len(gt_class_ids) - tp) s_iou += (np.sum(s_image_iou) / iou.shape[0]) s_precision += (tp/(tp + fp)) s_recall += (tp/(tp + fn)) n_test = len(keys) print_log("mIoU: %f" % (s_iou/n_test), self.args.verbose) print_log("Precision: %f" % (s_precision/n_test), self.args.verbose) print_log("Recall : %f" % (s_recall/n_test), self.args.verbose)
def train(self): """Train an ssd network.""" # build the train data generator if self.train_generator is None: self.build_generator() optimizer = Adam(lr=1e-3) # choice of loss functions via args if self.args.improved_loss: print_log("Focal loss and smooth L1", self.args.verbose) loss = [focal_loss_categorical, smooth_l1_loss] elif self.args.smooth_l1: print_log("Smooth L1", self.args.verbose) loss = ['categorical_crossentropy', smooth_l1_loss] else: print_log("Cross-entropy and L1", self.args.verbose) loss = ['categorical_crossentropy', l1_loss] self.ssd.compile(optimizer=optimizer, loss=loss) # model weights are saved for future validation # prepare model model saving directory. save_dir = os.path.join(os.getcwd(), self.args.save_dir) model_name = self.backbone.name model_name += '-' + str(self.args.layers) + "layer" if self.args.normalize: model_name += "-norm" if self.args.improved_loss: model_name += "-improved_loss" elif self.args.smooth_l1: model_name += "-smooth_l1" if self.args.threshold < 1.0: model_name += "-extra_anchors" model_name += "-" model_name += self.args.dataset model_name += '-{epoch:03d}.h5' log = "# of classes %d" % self.n_classes print_log(log, self.args.verbose) log = "Batch size: %d" % self.args.batch_size print_log(log, self.args.verbose) log = "Weights filename: %s" % model_name print_log(log, self.args.verbose) if not os.path.isdir(save_dir): os.makedirs(save_dir) filepath = os.path.join(save_dir, model_name) # prepare callbacks for saving model weights # and learning rate scheduler # learning rate decreases by 50% every 20 epochs # after 60th epoch checkpoint = ModelCheckpoint(filepath=filepath, verbose=1, save_weights_only=True) scheduler = LearningRateScheduler(lr_scheduler) callbacks = [checkpoint, scheduler] # train the ssd network self.ssd.fit_generator(generator=self.train_generator, use_multiprocessing=True, callbacks=callbacks, epochs=self.args.epochs, workers=self.args.workers)