예제 #1
0
def detect_image(model, image, img_size=416, conf_thres=0.5, nms_thres=0.5):
    """Inferences one image with model.

    :param model: Model for inference
    :type model: models.Darknet
    :param image: Image to inference
    :type image: nd.array
    :param img_size: Size of each image dimension for yolo, defaults to 416
    :type img_size: int, optional
    :param conf_thres: Object confidence threshold, defaults to 0.5
    :type conf_thres: float, optional
    :param nms_thres: IOU threshold for non-maximum suppression, defaults to 0.5
    :type nms_thres: float, optional
    :return: Detections on image with each detection in the format: [x1, y1, x2, y2, confidence, class]
    :rtype: nd.array
    """
    model.eval()  # Set model to evaluation mode

    # Configure input
    input_img = transforms.Compose([DEFAULT_TRANSFORMS,
                                    Resize(img_size)])((image, np.zeros(
                                        (1, 5))))[0].unsqueeze(0)

    if torch.cuda.is_available():
        input_img = input_img.to("cuda")

    # Get detections
    with torch.no_grad():
        detections = model(input_img)
        detections = non_max_suppression(detections, conf_thres, nms_thres)
        detections = rescale_boxes(detections[0], img_size, image.shape[:2])
    return to_cpu(detections).numpy()
예제 #2
0
def run():
    print_environment_info()
    parser = argparse.ArgumentParser(description="Trains the YOLO model.")
    parser.add_argument("-m",
                        "--model",
                        type=str,
                        default="config/yolov3.cfg",
                        help="Path to model definition file (.cfg)")
    parser.add_argument("-d",
                        "--data",
                        type=str,
                        default="config/coco.data",
                        help="Path to data config file (.data)")
    parser.add_argument("-e",
                        "--epochs",
                        type=int,
                        default=300,
                        help="Number of epochs")
    parser.add_argument("-v",
                        "--verbose",
                        action='store_true',
                        help="Makes the training more verbose")
    parser.add_argument(
        "--n_cpu",
        type=int,
        default=8,
        help="Number of cpu threads to use during batch generation")
    parser.add_argument(
        "--pretrained_weights",
        type=str,
        help=
        "Path to checkpoint file (.weights or .pth). Starts training from checkpoint model"
    )
    parser.add_argument("--checkpoint_interval",
                        type=int,
                        default=1,
                        help="Interval of epochs between saving model weights")
    parser.add_argument(
        "--evaluation_interval",
        type=int,
        default=1,
        help="Interval of epochs between evaluations on validation set")
    parser.add_argument("--multiscale_training",
                        action="store_false",
                        help="Allow for multi-scale training")
    parser.add_argument(
        "--iou_thres",
        type=float,
        default=0.5,
        help="Evaluation: IOU threshold required to qualify as detected")
    parser.add_argument("--conf_thres",
                        type=float,
                        default=0.1,
                        help="Evaluation: Object confidence threshold")
    parser.add_argument(
        "--nms_thres",
        type=float,
        default=0.5,
        help="Evaluation: IOU threshold for non-maximum suppression")
    parser.add_argument(
        "--logdir",
        type=str,
        default="logs",
        help="Directory for training log files (e.g. for TensorBoard)")
    parser.add_argument("--seed",
                        type=int,
                        default=-1,
                        help="Makes results reproducable. Set -1 to disable.")
    args = parser.parse_args()
    print(f"Command line arguments: {args}")

    if args.seed != -1:
        provide_determinism(args.seed)

    logger = Logger(args.logdir)  # Tensorboard logger

    # Create output directories if missing
    os.makedirs("output", exist_ok=True)
    os.makedirs("checkpoints", exist_ok=True)

    # Get data configuration
    data_config = parse_data_config(args.data)
    train_path = data_config["train"]
    valid_path = data_config["valid"]
    class_names = load_classes(data_config["names"])
    device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

    # ############
    # Create model
    # ############

    model = load_model(args.model, args.pretrained_weights)

    # Print model
    if args.verbose:
        summary(model,
                input_size=(3, model.hyperparams['height'],
                            model.hyperparams['height']))

    mini_batch_size = model.hyperparams['batch'] // model.hyperparams[
        'subdivisions']

    # #################
    # Create Dataloader
    # #################

    # Load training dataloader
    dataloader = _create_data_loader(train_path, mini_batch_size,
                                     model.hyperparams['height'], args.n_cpu,
                                     args.multiscale_training)

    # Load validation dataloader
    validation_dataloader = _create_validation_data_loader(
        valid_path, mini_batch_size, model.hyperparams['height'], args.n_cpu)

    # ################
    # Create optimizer
    # ################

    params = [p for p in model.parameters() if p.requires_grad]

    if (model.hyperparams['optimizer'] in [None, "adam"]):
        optimizer = optim.Adam(
            params,
            lr=model.hyperparams['learning_rate'],
            weight_decay=model.hyperparams['decay'],
        )
    elif (model.hyperparams['optimizer'] == "sgd"):
        optimizer = optim.SGD(params,
                              lr=model.hyperparams['learning_rate'],
                              weight_decay=model.hyperparams['decay'],
                              momentum=model.hyperparams['momentum'])
    else:
        print("Unknown optimizer. Please choose between (adam, sgd).")

    for epoch in range(args.epochs):

        print("\n---- Training Model ----")

        model.train()  # Set model to training mode

        for batch_i, (_, imgs, targets) in enumerate(
                tqdm.tqdm(dataloader, desc=f"Training Epoch {epoch}")):
            batches_done = len(dataloader) * epoch + batch_i

            imgs = imgs.to(device, non_blocking=True)
            targets = targets.to(device)

            outputs = model(imgs)

            loss, loss_components = compute_loss(outputs, targets, model)

            loss.backward()

            ###############
            # Run optimizer
            ###############

            if batches_done % model.hyperparams['subdivisions'] == 0:
                # Adapt learning rate
                # Get learning rate defined in cfg
                lr = model.hyperparams['learning_rate']
                if batches_done < model.hyperparams['burn_in']:
                    # Burn in
                    lr *= (batches_done / model.hyperparams['burn_in'])
                else:
                    # Set and parse the learning rate to the steps defined in the cfg
                    for threshold, value in model.hyperparams['lr_steps']:
                        if batches_done > threshold:
                            lr *= value
                # Log the learning rate
                logger.scalar_summary("train/learning_rate", lr, batches_done)
                # Set learning rate
                for g in optimizer.param_groups:
                    g['lr'] = lr

                # Run optimizer
                optimizer.step()
                # Reset gradients
                optimizer.zero_grad()

            # ############
            # Log progress
            # ############
            if args.verbose:
                print(
                    AsciiTable([
                        ["Type", "Value"],
                        ["IoU loss", float(loss_components[0])],
                        ["Object loss",
                         float(loss_components[1])],
                        ["Class loss", float(loss_components[2])],
                        ["Loss", float(loss_components[3])],
                        ["Batch loss", to_cpu(loss).item()],
                    ]).table)

            # Tensorboard logging
            tensorboard_log = [("train/iou_loss", float(loss_components[0])),
                               ("train/obj_loss", float(loss_components[1])),
                               ("train/class_loss", float(loss_components[2])),
                               ("train/loss", to_cpu(loss).item())]
            logger.list_of_scalars_summary(tensorboard_log, batches_done)

            model.seen += imgs.size(0)

        # #############
        # Save progress
        # #############

        # Save model to checkpoint file
        if epoch % args.checkpoint_interval == 0:
            checkpoint_path = f"checkpoints/yolov3_ckpt_{epoch}.pth"
            print(f"---- Saving checkpoint to: '{checkpoint_path}' ----")
            torch.save(model.state_dict(), checkpoint_path)

        # ########
        # Evaluate
        # ########

        if epoch % args.evaluation_interval == 0:
            print("\n---- Evaluating Model ----")
            # Evaluate the model on the validation set
            metrics_output = _evaluate(model,
                                       validation_dataloader,
                                       class_names,
                                       img_size=model.hyperparams['height'],
                                       iou_thres=args.iou_thres,
                                       conf_thres=args.conf_thres,
                                       nms_thres=args.nms_thres,
                                       verbose=args.verbose)

            if metrics_output is not None:
                precision, recall, AP, f1, ap_class = metrics_output
                evaluation_metrics = [("validation/precision",
                                       precision.mean()),
                                      ("validation/recall", recall.mean()),
                                      ("validation/mAP", AP.mean()),
                                      ("validation/f1", f1.mean())]
                logger.list_of_scalars_summary(evaluation_metrics, epoch)
예제 #3
0
def _evaluate(model, dataloader, class_names, img_size, iou_thres, conf_thres,
              nms_thres, verbose):
    """Evaluate model on validation dataset.

    :param model: Model to evaluate
    :type model: models.Darknet
    :param dataloader: Dataloader provides the batches of images with targets
    :type dataloader: DataLoader
    :param class_names: List of class names
    :type class_names: [str]
    :param img_size: Size of each image dimension for yolo
    :type img_size: int
    :param iou_thres: IOU threshold required to qualify as detected
    :type iou_thres: float
    :param conf_thres: Object confidence threshold
    :type conf_thres: float
    :param nms_thres: IOU threshold for non-maximum suppression
    :type nms_thres: float
    :param verbose: If True, prints stats of model
    :type verbose: bool
    :return: Returns precision, recall, AP, f1, ap_class
    """
    model.eval()  # Set model to evaluation mode

    Tensor = torch.cuda.FloatTensor if torch.cuda.is_available(
    ) else torch.FloatTensor

    labels = []
    sample_metrics = []  # List of tuples (TP, confs, pred)
    for _, imgs, targets in tqdm.tqdm(dataloader, desc="Validating"):
        # Extract labels
        labels += targets[:, 1].tolist()
        # Rescale target
        targets[:, 2:] = xywh2xyxy(targets[:, 2:])
        targets[:, 2:] *= img_size

        imgs = Variable(imgs.type(Tensor), requires_grad=False)

        with torch.no_grad():
            outputs = to_cpu(model(imgs))
            outputs = non_max_suppression(outputs,
                                          conf_thres=conf_thres,
                                          iou_thres=nms_thres)

        sample_metrics += get_batch_statistics(outputs,
                                               targets,
                                               iou_threshold=iou_thres)

    if len(sample_metrics) == 0:  # No detections over whole validation set.
        print("---- No detections over whole validation set ----")
        return None

    # Concatenate sample statistics
    true_positives, pred_scores, pred_labels = [
        np.concatenate(x, 0) for x in list(zip(*sample_metrics))
    ]
    metrics_output = ap_per_class(true_positives, pred_scores, pred_labels,
                                  labels)

    print_eval_stats(metrics_output, class_names, verbose)

    return metrics_output