(Pytorch) 模拟目标检测回归

'''
    @Author: feizzhang
    Created on: 21.01.2021
'''

import matplotlib.pyplot as plt
import numpy as np
import math
import argparse
import copy

import torch
from torch.autograd import Variable
INF = 100000.

'''
# --------------------------------
#  Loss Function
# --------------------------------
'''
def loss_c(var_list, tgt_list, loss_type):
    '''Compute the loss of N boxes and one GT that N boxes is to optimize

    Args:
        var_list: The variable need to optimize
                  [X, Y, W, H]   X: tensor, N
        tgt_list: The target to optimize
                  [X, Y, W, H]   X: tensor, 1

    Returns:
        The loss value: tensor, N
    '''

    # --------------------------------
    #  Prepare the computation
    # --------------------------------
    X, Y, W, H = var_list
    X0, Y0, W0, H0 = tgt_list

    Xl, Yl, Xr, Yr = X - W / 2., Y - H / 2., X + W / 2., Y + H / 2.
    X0l, Y0l, X0r, Y0r = X0 - W0 / 2., Y0 - H0 / 2., X0 + W0 / 2., Y0 + H0 / 2.

    Xlmax, Xlmin = torch.max(Xl, X0l), torch.min(Xl, X0l)
    Ylmax, Ylmin = torch.max(Yl, Y0l), torch.min(Yl, Y0l)
    Xrmax, Xrmin = torch.max(Xr, X0r), torch.min(Xr, X0r)
    Yrmax, Yrmin = torch.max(Yr, Y0r), torch.min(Yr, Y0r)

    intersect_w = Xrmin - Xlmax
    intersect_h = Yrmin - Ylmax
    intersect_area = intersect_w.clamp(min=0) * intersect_h.clamp(min=0)

    union_area = W * H + W0 * H0 - intersect_area

    external_w = Xrmax - Xlmin
    external_h = Yrmax - Ylmin
    external_area = external_w * external_h

    iou = (intersect_area / union_area).clamp(min=0)

    # --------------------------------
    #  Compute the loss
    # --------------------------------
    if loss_type == "iou":
        return 1 - iou

    elif loss_type == "giou":
        gitem = (external_area - union_area) / external_area
        return 1 - iou + gitem

    elif loss_type[:4] == "diou" or loss_type[:4] == "ciou":
        distance_center = (X - X0) ** 2 + (Y - Y0) ** 2
        distance_diagonal = external_w ** 2 + external_h ** 2 + 1e-16
        ditem = distance_center / distance_diagonal

        if loss_type[:4] == "diou":
            if loss_type[4:] == "plus":
                pass

            else:
                return 1 - iou + ditem

        elif loss_type[:4] == "ciou":
            v = (4 / math.pi ** 2) * torch.pow(torch.atan(W / H) - torch.atan(W0 / H0), 2)

            with torch.no_grad():
                alpha = v / (1 - iou + v)

            citem = alpha * v

            if loss_type[4:] == "plus":
                pass

            else:
                return 1 - iou + ditem + citem

'''
# --------------------------------
#  Define the training
# --------------------------------
'''
def simulation_engine(args):
    # --------------------------------
    #  Initialize all
    # --------------------------------

    X = Variable(torch.FloatTensor([args.X]).cuda(), requires_grad=True)
    Y = Variable(torch.FloatTensor([args.Y]).cuda(), requires_grad=True)
    W = Variable(torch.FloatTensor([args.wh_base]).cuda(), requires_grad=True)
    H = Variable(torch.FloatTensor([args.wh_base * args.ar]).cuda(), requires_grad=True)

    X0 = torch.tensor([args.X0], dtype=torch.float16).cuda()
    Y0 = torch.tensor([args.Y0], dtype=torch.float16).cuda()
    W0 = torch.tensor([args.w0h0_base], dtype=torch.float16).cuda()
    H0 = torch.tensor([args.w0h0_base * args.ar0], dtype=torch.float16).cuda()

    var_list = [X, Y, W, H]
    gt_list = [X0, Y0, W0, H0]
    var_data = []
    loss_data = []

    # --------------------------------
    #  Define optimizer scheduler
    # --------------------------------
    # optimizer = torch.optim.Adam(var_list, lr=args.lr, weight_decay=0, betas=(0.9, 0.99))
    optimizer = torch.optim.SGD(var_list,
                                lr=args.lr,
                                weight_decay=args.weight_decay)
    scheduler = torch.optim.lr_scheduler.MultiStepLR(optimizer, milestones=args.milestones, gamma=args.gamma)

    # --------------------------------
    #  Train for x iter
    # --------------------------------
    for iter in range(args.iter):
        optimizer.zero_grad()

        # --------------------------------
        #  1) Save one iter variable
        # --------------------------------
        if iter % args.save_iter == 0:
            # var_data.update({iter:copy.deepcopy(var_dict)})
            temp = copy.deepcopy(var_list)
            var_data.append({'X':temp[0],'Y':temp[1],'W':temp[2],'H':temp[3]})


        # --------------------------------
        #  2) Set the loss
        # --------------------------------
        loss = loss_c(var_list, gt_list, args.loss_type)
        loss_data.append(loss)

        # --------------------------------
        #  3) Update grad once
        # --------------------------------
        loss.backward()

        # --------------------------------
        #  4) Update pred once
        # --------------------------------
        optimizer.step()

        # --------------------------------
        #  5) Update lr once
        # --------------------------------
        scheduler.step()

    return var_data, loss_data


'''
# --------------------------------
#  Simulate the boxes regression
# --------------------------------
'''
def main(args):

    gt_ar_list = args.gt_ar_list
    gt_wh_base_list = args.gt_wh_base_list

    pred_ar_list = args.pred_ar_list
    pred_wh_base_list = args.pred_wh_base_list

    r = args.raduis * np.sqrt(np.random.rand(args.num_boxes))
    thelta = 2 * np.pi * np.random.rand(args.num_boxes)

    X = args.X0 + r * np.cos(thelta)
    Y = args.Y0 + r * np.sin(thelta)

    for iou_type in args.loss_type_list:
        args.loss_type = iou_type
        print("[^] {} begins!".format(iou_type))
        num = 0

        x_error = np.zeros(args.iter)
        y_error = np.zeros(args.iter)
        w_error = np.zeros(args.iter)
        h_error = np.zeros(args.iter)

        for ar, base in zip(gt_ar_list, gt_wh_base_list):
            args.ar0 = ar
            args.w0h0_base = base

            for i in range(X.shape[0]):
                args.X, args.Y = X[i], Y[i]

                for ar, base in zip(pred_ar_list, pred_wh_base_list):
                    args.ar = ar
                    args.wh_base = base
                    num += 1

                    data, _ = simulation_engine(args)

                    for i, box in enumerate(data):
                        Xi = box['X'].detach().cpu().numpy()
                        Yi = box['Y'].detach().cpu().numpy()
                        Wi = box['W'].detach().cpu().numpy()
                        Hi = box['H'].detach().cpu().numpy()

                        x_error[i] += np.abs(Xi - args.X0)
                        y_error[i] += np.abs(Yi - args.Y0)
                        w_error[i] += np.abs(Wi - args.w0h0_base)
                        h_error[i] += np.abs(Hi - args.w0h0_base * args.ar0)

                    print(num, (x_error + y_error + w_error + h_error)[390:])

        np2str = ''
        error = x_error + y_error + w_error + h_error
        for i in range(error.shape[0]):

            if i == 0:
                np2str = str(error[i])

            if i > 0:
                np2str = np2str + '_' + str(error[i])

                if i == error.shape[0] - 1:
                    np2str = np2str + '\n'

        with open(args.error_save_path, mode="a+") as f:
            f.write(args.loss_type + '\n')
            f.write(np2str)

    print("[@] OVER sayolala!")


'''
# --------------------------------
#  Plot the error
# --------------------------------
'''
def plot_error(args):
    class results_tf(object):
        def __init__(self):
            super(results_tf, self).__init__()

    storage = results_tf()
    iou_type_list = []

    with open(args.error_save_path, "r") as f:

        lines = f.readlines()
        for i in range(len(lines)//2):
            iou_type = lines[2*i].strip()
            iou_type_list.append(iou_type)
            error = np.array(lines[2*i+1].strip().split('_')).astype(np.float32)

            setattr(storage, iou_type, error.astype(int))

    fig = plt.figure()
    error1 = getattr(storage, iou_type_list[0])
    x = np.arange(error1.shape[0])

    for iou_i in iou_type_list:
        errori = getattr(storage, iou_i)
        plt.plot(x, errori, label=iou_i)

    plt.legend(loc='upper right')
    plt.ylim(0, 50000)
    plt.show()


'''
# --------------------------------
#  Visualize single box regression
# --------------------------------
'''
def visualize_box(args):
    data, loss_rec = simulation_engine(args)

    fig = plt.figure()
    ax1 = fig.add_subplot(211)

    for i, box in enumerate(data):
        X = box['X'].cpu().detach().numpy()
        Y = box['Y'].cpu().detach().numpy()
        W = box['W'].cpu().detach().numpy()
        H = box['H'].cpu().detach().numpy()

        xl, yl, w, h = X - W / 2., Y - H / 2., W, H

        color = "red"
        alpha = 0.7

        if i == len(data) - 1:
            color = "yellow"
            alpha = 1

        ax1.add_patch(plt.Rectangle(xy=(xl, yl),
                                    width=w,
                                    height=h,
                                    fill=False,
                                    color=color,
                                    alpha=alpha,
                                    linewidth=1))
        ax1.scatter(X, Y, s=15, alpha=alpha, color=color)

    ax1.add_patch(plt.Rectangle(xy=(args.X0 - args.w0h0_base / 2., args.Y0 - args.w0h0_base * args.ar0 / 2.),
                                width=args.w0h0_base,
                                height=args.w0h0_base * args.ar0,
                                fill=False,
                                color="black",
                                alpha=0.5,
                                linewidth=2))


    x_major_locator = plt.MultipleLocator(10)
    y_major_locator = plt.MultipleLocator(10)

    ax = plt.gca()

    ax1.xaxis.set_major_locator(x_major_locator)
    ax1.yaxis.set_major_locator(y_major_locator)

    ax1.set_xlim(0, 10)
    ax1.set_ylim(0, 10)

    ax1.set_title(args.loss_type + " *** " + "config")

    ax2 = fig.add_subplot(212)
    losses = [loss.squeeze().cpu().detach().numpy() for loss in loss_rec]

    x_ax = np.arange(0, len(losses), 1)

    ax2.set_xlim(0, 400)
    ax2.set_ylim(0, 2)

    ax2.set_xlabel("epoch")
    ax2.set_ylabel("losses")

    ax2.plot(x_ax, losses)
    plt.show()


'''
# --------------------------------
#  Config
# --------------------------------
'''
parser = argparse.ArgumentParser(description="Set your own simulation hyperparameter")
# --------------------------------
#  Initialize the training
# --------------------------------
parser.add_argument('--iter', type=int, default=400)
parser.add_argument('--save_iter', type=int, default=1, help="how iteration to save the data")

# --------------------------------
#  Initialize the single prediction
# --------------------------------
parser.add_argument('--X', type=float, default=32.)
parser.add_argument('--Y', type=float, default=4.)
parser.add_argument('--ar', type=float, default=1.)
parser.add_argument('--wh_base', type=float, default=8.)

# --------------------------------
#  Set the single GT
# --------------------------------
parser.add_argument('--X0', type=float, default=8.)
parser.add_argument('--Y0', type=float, default=8.)
parser.add_argument('--ar0', type=float, default=1.)
parser.add_argument('--w0h0_base', type=float, default=4.)

# --------------------------------
#  Set the all the prediction
#  and GT
# --------------------------------
parser.add_argument('--raduis', type=int, default=6)
parser.add_argument('--num_boxes', type=int, default=5000)
parser.add_argument('--error_save_path', type=str, default="./output.txt")
parser.add_argument('--gt_ar_list', type=list, default=[1., 4., 1 / 4, 2, 1 / 2, 3, 1 / 3])
parser.add_argument('--gt_wh_base_list', type=list, default=[2., 1, 4, 1.4, 2.8, 1.15, 3.46])
parser.add_argument('--pred_ar_list', type=list, default=[1., 3, 1 / 3, 2, 1 / 2, 4, 1 / 4])
parser.add_argument('--pred_wh_base_list', type=list, default=[3., 1.7, 5.1, 2.1, 4.2, 1.5, 6])
parser.add_argument('--loss_type_list', type=list, default=["diou", "ciou", "diouplus", "ciouplus"])

# --------------------------------
#  Set the loss
# --------------------------------
parser.add_argument('--loss_type', type=str, default="ciouplus")

# --------------------------------
#  Set optimizer & scheduler
# --------------------------------
parser.add_argument('--lr', type=float, default=5)
parser.add_argument('--gamma', type=float, default=0.3)
parser.add_argument('--weight_decay', type=float, default=0)
parser.add_argument('--milestones', type=list, default=[5, 60, 120, 200, 300])
# [5, 60, 120, 200, 300]
# [100, 180, 260, 320, 350] giou
# [40, 80, 260, 320, 350] diou [20, 80, 120, 240]
# [40, 80, 120, 180, 240, 300] ciou [20, 80, 120, 200]
if __name__ == '__main__':
    args = parser.parse_args()
    main(args)

posted @ 2021-01-22 09:40  FromL77  阅读(121)  评论(0编辑  收藏  举报