| |
| |
| |
| |
| import torch, torchvision, time, random |
| import numpy as np |
| import matplotlib.pyplot as plt |
| import matplotlib.patches as patches |
| import os |
| from datetime import datetime |
| from collections import Counter |
| import torchvision.ops as ops |
| from pycocotools.cocoeval import COCOeval |
| import json |
| from tqdm import tqdm |
|
|
|
|
| import qat_core |
|
|
| def ssd_postprocess_person_cls(pred): |
| """ |
| Take the models prediction outputs (pred) and make the post processing operations to |
| classification head outputs since the output is not directly class probabilities. |
| Assuming square input image so H=W. |
| Assuming binary classes (0/1). |
| |
| Input: |
| - pred: predicted outputs of the model as list of length 4 as [output1_reg, output1_class, output2_reg, output2_class] |
| and shape of [(NR1, CR1, HR1, WR1), (NC1, CC1, HC1, WC1), (NR2, CR2, HR2, WR2), (NC2, CC2, HC2, WC2)]. |
| |
| Returns: |
| - person_cls: person class probabilities (torch.FloatTensor) shape of [CC1/2*HC1*WC1 + CC2/2*HC2*WC2]. |
| """ |
| head_regression_hires = pred[0] |
| head_classification_hires = pred[1] |
| head_regression_lores = pred[2] |
| head_classification_lores = pred[3] |
|
|
| |
| head_classification_hires_background = head_classification_hires[0,1::2,:,:] |
| head_classification_hires_person = head_classification_hires[0,0::2,:,:] |
| head_classification_lores_background = head_classification_lores[0,1::2,:,:] |
| head_classification_lores_person = head_classification_lores[0,0::2,:,:] |
|
|
| |
| |
| hires_rowscols = head_regression_hires.shape[3] |
| lores_rowscols = head_regression_lores.shape[3] |
| hires_numanchors = int(head_regression_hires.shape[1]/4) |
| lores_numanchors = int(head_regression_lores.shape[1]/4) |
|
|
| background_hires_flat = explicit_flatten(head_classification_hires_background, 'hires', hires_rowscols, hires_numanchors, lores_rowscols, lores_numanchors) |
| background_lores_flat = explicit_flatten(head_classification_lores_background, 'lores', hires_rowscols, hires_numanchors, lores_rowscols, lores_numanchors) |
| person_hires_flat = explicit_flatten(head_classification_hires_person, 'hires', hires_rowscols, hires_numanchors, lores_rowscols, lores_numanchors) |
| person_lores_flat = explicit_flatten(head_classification_lores_person, 'lores', hires_rowscols, hires_numanchors, lores_rowscols, lores_numanchors) |
| |
| person_flat = torch.cat((person_hires_flat, person_lores_flat)) |
| background_flat = torch.cat((background_hires_flat, background_lores_flat)) |
|
|
| total_cat = torch.cat( ( torch.unsqueeze(person_flat,0) , torch.unsqueeze(background_flat,0) ) ) |
| softmax_fcn = torch.nn.Softmax(dim=0) |
| softmax_result = softmax_fcn(total_cat) |
|
|
| person_hires_flat_sft = softmax_result[0,:][0:background_hires_flat.shape[0]] |
| person_lores_flat_sft = softmax_result[0,:][background_hires_flat.shape[0]:] |
|
|
| person_hires_classification_scores = explicit_unflatten(person_hires_flat_sft, 'hires', hires_rowscols, hires_numanchors, lores_rowscols, lores_numanchors) |
| person_lores_classification_scores = explicit_unflatten(person_lores_flat_sft, 'lores', hires_rowscols, hires_numanchors, lores_rowscols, lores_numanchors) |
|
|
| person_cls = torch.cat(( person_hires_flat_sft, person_lores_flat_sft )) |
|
|
| return person_cls |
|
|
|
|
| def ssd_postprocess_person_bboxes(pred, image_width, image_height, anchors_head1, anchors_head2): |
| """ |
| Take the models prediction output (pred) and make the post processing operations to |
| show bboxes. |
| Assuming square input image so H=W. |
| Assuming binary classes (0/1). |
| |
| Input: |
| - pred: predicted outputs of the model as list of length 4[output1_reg, output1_class, output2_reg, output2_class] |
| shape of [(NR1, CR1, HR1, WR1), (NC1, CC1, HC1, WC1), (NR2, CR2, HR2, WR2), (NC2, CC2, HC2, WC2)]. |
| - image_width: Integer. |
| - image_height: Integer. |
| - anchors_head1: list of length 4, contains image_width/image_height*anchor_ratios as tuples. |
| shape [(W*A1, H*B1), (W*A2, H*B2), (W*A3, H*B3), (W*A4, H*B4)] where A#num and B#num are |
| corresponding different aspect ratios. |
| - anchors_head2: list of length 4, contains image_width/image_height*anchor_ratios as tuples. |
| shape [(W*C1, H*D1), (W*C2, H*D2), (W*C3, H*D3), (W*C4, H*D4)] where C#num and D#num are |
| corresponding different aspect ratios. |
| Returns: |
| - absolute_boxes: absolute value of bounding boxes (torch.FloatTensor) shape of [CR1/4*HR1*WR1 + CR2/4*HR2*WR2, 4]. |
| """ |
| head_regression_hires = pred[0] |
| head_classification_hires = pred[1] |
| head_regression_lores = pred[2] |
| head_classification_lores = pred[3] |
|
|
| |
| |
| hires_rowscols = head_regression_hires.shape[3] |
| lores_rowscols = head_regression_lores.shape[3] |
| hires_numanchors = int(head_regression_hires.shape[1]/4) |
| lores_numanchors = int(head_regression_lores.shape[1]/4) |
|
|
| |
| delta_x_hires = head_regression_hires[0, 0::4, :, :] |
| delta_y_hires = head_regression_hires[0, 1::4, :, :] |
| delta_w_hires = head_regression_hires[0, 2::4, :, :] |
| delta_h_hires = head_regression_hires[0, 3::4, :, :] |
|
|
| delta_x_lores = head_regression_lores[0, 0::4, :, :] |
| delta_y_lores = head_regression_lores[0, 1::4, :, :] |
| delta_w_lores = head_regression_lores[0, 2::4, :, :] |
| delta_h_lores = head_regression_lores[0, 3::4, :, :] |
|
|
| |
| |
| |
| |
| |
| |
| var_x = 0.1 |
| var_y = 0.1 |
| var_w = 0.2 |
| var_h = 0.2 |
|
|
| w_anchors_hires = torch.tensor(anchors_head1)[:,0] |
| h_anchors_hires = torch.tensor(anchors_head1)[:,1] |
| w_anchors_lores = torch.tensor(anchors_head2)[:,0] |
| h_anchors_lores = torch.tensor(anchors_head2)[:,1] |
|
|
| x_anchors_hires = populate_xy_anchors(delta_x_hires, 'x') |
| y_anchors_hires = populate_xy_anchors(delta_y_hires, 'y') |
| x_anchors_lores = populate_xy_anchors(delta_x_lores, 'x') |
| y_anchors_lores = populate_xy_anchors(delta_y_lores, 'y') |
|
|
| w_anchors_hires_rpt = populate_wh_anchors(delta_w_hires, w_anchors_hires) |
| h_anchors_hires_rpt = populate_wh_anchors(delta_h_hires, h_anchors_hires) |
| w_anchors_lores_rpt = populate_wh_anchors(delta_w_lores, w_anchors_lores) |
| h_anchors_lores_rpt = populate_wh_anchors(delta_h_lores, h_anchors_lores) |
|
|
| absolute_x_hires = delta_x_hires * w_anchors_hires_rpt * var_x + x_anchors_hires |
| absolute_y_hires = delta_y_hires * h_anchors_hires_rpt * var_y + y_anchors_hires |
| absolute_x_lores = delta_x_lores * w_anchors_lores_rpt * var_x + x_anchors_lores |
| absolute_y_lores = delta_y_lores * h_anchors_lores_rpt * var_y + y_anchors_lores |
|
|
| absolute_w_hires = (delta_w_hires * var_w).exp() * w_anchors_hires_rpt |
| absolute_h_hires = (delta_h_hires * var_h).exp() * h_anchors_hires_rpt |
| absolute_w_lores = (delta_w_lores * var_w).exp() * w_anchors_lores_rpt |
| absolute_h_lores = (delta_h_lores * var_h).exp() * h_anchors_lores_rpt |
|
|
| absolute_hires_xleft = absolute_x_hires - absolute_w_hires/2 |
| absolute_hires_xright = absolute_x_hires + absolute_w_hires/2 |
| absolute_hires_ytop = absolute_y_hires - absolute_h_hires/2 |
| absolute_hires_ybottom = absolute_y_hires + absolute_h_hires/2 |
|
|
| absolute_lores_xleft = absolute_x_lores - absolute_w_lores/2 |
| absolute_lores_xright = absolute_x_lores + absolute_w_lores/2 |
| absolute_lores_ytop = absolute_y_lores - absolute_h_lores/2 |
| absolute_lores_ybottom = absolute_y_lores + absolute_h_lores/2 |
|
|
| absolute_hires_xleft_flat = explicit_flatten(absolute_hires_xleft, 'hires', hires_rowscols, hires_numanchors, lores_rowscols, lores_numanchors) |
| absolute_hires_xright_flat = explicit_flatten(absolute_hires_xright, 'hires', hires_rowscols, hires_numanchors, lores_rowscols, lores_numanchors) |
| absolute_hires_ytop_flat = explicit_flatten(absolute_hires_ytop, 'hires', hires_rowscols, hires_numanchors, lores_rowscols, lores_numanchors) |
| absolute_hires_ybottom_flat = explicit_flatten(absolute_hires_ybottom, 'hires', hires_rowscols, hires_numanchors, lores_rowscols, lores_numanchors) |
|
|
| absolute_lores_xleft_flat = explicit_flatten(absolute_lores_xleft, 'lores', hires_rowscols, hires_numanchors, lores_rowscols, lores_numanchors) |
| absolute_lores_xright_flat = explicit_flatten(absolute_lores_xright, 'lores', hires_rowscols, hires_numanchors, lores_rowscols, lores_numanchors) |
| absolute_lores_ytop_flat = explicit_flatten(absolute_lores_ytop, 'lores', hires_rowscols, hires_numanchors, lores_rowscols, lores_numanchors) |
| absolute_lores_ybottom_flat = explicit_flatten(absolute_lores_ybottom, 'lores', hires_rowscols, hires_numanchors, lores_rowscols, lores_numanchors) |
|
|
| absolute_xleft = torch.unsqueeze(torch.cat((absolute_hires_xleft_flat, absolute_lores_xleft_flat)) ,1) |
| absolute_xright = torch.unsqueeze(torch.cat((absolute_hires_xright_flat, absolute_lores_xright_flat)) ,1) |
| absolute_ytop = torch.unsqueeze(torch.cat((absolute_hires_ytop_flat, absolute_lores_ytop_flat)) ,1) |
| absolute_ybottom = torch.unsqueeze(torch.cat((absolute_hires_ybottom_flat, absolute_lores_ybottom_flat)),1) |
|
|
| absolute_boxes = torch.cat((absolute_xleft, absolute_ytop, absolute_xright, absolute_ybottom), dim=1) |
| return absolute_boxes |
|
|
|
|
|
|
| |
| def explicit_flatten(tensor, hires_or_lores, hires_rowscols, hires_numanchors, lores_rowscols, lores_numanchors): |
| flattened_tensor = torch.zeros_like(tensor.flatten()) |
| |
| if(hires_or_lores=='hires'): |
| rc = hires_rowscols |
| na = hires_numanchors |
| elif(hires_or_lores=='lores'): |
| rc = lores_rowscols |
| na = lores_numanchors |
| else: |
| print("somethings wrong") |
| return |
| |
| for row in range(0, rc): |
| for col in range(0, rc): |
| for anc in range(0, na): |
| flattened_tensor[anc*rc*rc + row*rc + col] = tensor[anc,row,col]; |
| |
| return flattened_tensor |
|
|
| |
| def explicit_unflatten(flattened_tensor, hires_or_lores, hires_rowscols, hires_numanchors, lores_rowscols, lores_numanchors): |
| if(hires_or_lores=='hires'): |
| tensor = torch.zeros((hires_numanchors, hires_rowscols, hires_rowscols)) |
| rc = hires_rowscols |
| na = hires_numanchors |
| elif(hires_or_lores=='lores'): |
| tensor = torch.zeros((lores_numanchors, lores_rowscols, lores_rowscols)) |
| rc = lores_rowscols |
| na = lores_numanchors |
| else: |
| print("somethings wrong") |
| return |
| |
| for row in range(0, rc): |
| for col in range(0, rc): |
| for anc in range(0, na): |
| tensor[anc,row,col] = flattened_tensor[anc*rc*rc + row*rc + col]; |
| |
| return tensor |
|
|
|
|
| def plot_softmax_confidence_scores(person_hires_flat_sft, person_lores_flat_sft): |
| fig, ax = plt.subplots(figsize=(10,6)) |
| ax.plot(person_hires_flat_sft.detach().cpu().numpy()) |
| ax.plot(person_lores_flat_sft.detach().cpu().numpy()) |
| ax.grid() |
| ax.legend(['hires confidences', 'lores confidences']) |
| plt.title('softmax-processed confidence scores for the two heads') |
| plt.show() |
|
|
|
|
| def populate_wh_anchors(delta_ref, wh_anchors_hilores): |
| wh_anchors_hilores_rpt = torch.ones_like(delta_ref) |
| for i in range(0, wh_anchors_hilores_rpt.shape[0]): |
| wh_anchors_hilores_rpt[i] = wh_anchors_hilores_rpt[i]*wh_anchors_hilores[i] |
|
|
| return wh_anchors_hilores_rpt |
|
|
| def populate_xy_anchors(delta_ref, x_or_y): |
| xy_anchors_hilores = torch.zeros_like(delta_ref) |
| scale = 512 / delta_ref.shape[2] |
| for i in range(0, xy_anchors_hilores.shape[0]): |
| for j in range(0, xy_anchors_hilores.shape[1]): |
| for k in range(0, xy_anchors_hilores.shape[2]): |
| if(x_or_y == 'x'): |
| xy_anchors_hilores[i,j,k] = scale * k + (scale +1) / 2 |
| if(x_or_y == 'y'): |
| xy_anchors_hilores[i,j,k] = scale * j + (scale +1) / 2 |
| return xy_anchors_hilores |
|
|
| def plot_image_mnv2_2xSSDlite(image, pred_person_cls = None, pred_absolute_boxes = None, color = 'r' |
| ,nmsIoUTreshold = 0.45, predConfPlotTreshold = 0.6,target=None, figsize=(16,16), |
| saveFig=False, imageID=None, folderName='UnconstFPT'): |
| """ Plots original image, ground truths, and predictions if available. |
| Does non-maximum-suppression and plots perdiction boxes, saves figure under "Training Outputs" folder in specified folderName |
| Args: |
| image : (Tensor) Shape[Channel,width, height] |
| pred_person_cls : (Tensor) person class confidences for predicted boxes Shape[numPred,1] |
| pred_absolute_boxes : (Tensor) predicted boxes [xmin,ymin,xmax,ymax] Shape[numPred,4] |
| color: Color of drawn predicted boxes |
| nmsIoUTreshold : non max suppression IoU treshold |
| predConfPlotTreshold : Confidence treshold to draw predicted boxes |
| target : (Tensor) Ground truth boxes [pictureID, xmin, ymin, w, h] Shape[numGt, 5] |
| folderName : Foldername under ./Model Outputs diectory to save figure. |
| |
| Return: none |
| |
| """ |
| |
| if (image.min()<0): |
| image[0,:,:] = (image[0,:,:]/2)+0.5 |
| image[1,:,:] = (image[1,:,:]/2)+0.5 |
| image[2,:,:] = (image[2,:,:]/2)+0.5 |
|
|
| image = image.permute(1, 2, 0).to("cpu") |
| fig, ax = plt.subplots(figsize=figsize); |
| |
| if (saveFig): |
| plt.ioff() |
| else: |
| plt.ion() |
| |
| ax.imshow(image,aspect='equal') |
| |
| |
| if (target != None): |
| absolute_box_label = target.clone() |
| if (absolute_box_label.shape[0] != 0): |
| absolute_box_label = absolute_box_label[:,1:] |
| absolute_box_label[:,2] = absolute_box_label[:,2] + absolute_box_label[:,0] |
| absolute_box_label[:,3] = absolute_box_label[:,3] + absolute_box_label[:,1] |
|
|
| for ii, box in enumerate(absolute_box_label): |
| upper_left_x = box[0] |
| upper_left_y = box[1] |
| ww = box[2] - box[0] |
| hh = box[3] - box[1] |
| rect = patches.Rectangle( |
| (upper_left_x, upper_left_y), |
| ww, hh, |
| linewidth=5, |
| edgecolor='g', |
| facecolor="none", |
| ) |
| ax.add_patch(rect); |
|
|
| |
| if (pred_absolute_boxes != None): |
| confidences = pred_person_cls |
| boxes = pred_absolute_boxes |
| nms_picks = torchvision.ops.nms(boxes, confidences, nmsIoUTreshold) |
|
|
| boxes_to_draw = boxes[nms_picks].detach().cpu().numpy() |
| confs_to_draw = confidences[nms_picks].detach().cpu().numpy() |
|
|
| for ii, box in enumerate(boxes_to_draw): |
| if(confs_to_draw[ii] > predConfPlotTreshold): |
| upper_left_x = box[0]; |
| upper_left_y = box[1]; |
| ww = box[2] - box[0] |
| hh = box[3] - box[1] |
| |
| conf = "{:.3f}".format(confs_to_draw[ii]) |
| |
| if not saveFig: |
| print(f'Conf{ii} : {confs_to_draw[ii]}') |
| |
| plt.text(upper_left_x,upper_left_y-5, conf, fontsize = 12,color= color) |
| rect = patches.Rectangle( |
| (upper_left_x, upper_left_y), |
| ww, hh, |
| linewidth=2, |
| edgecolor=color, |
| facecolor="none", |
| ) |
| ax.add_patch(rect); |
| |
| |
| if saveFig: |
| trainingOutpDir = os.path.join(".","Training Outputs") |
| saveDir = os.path.join(trainingOutpDir,folderName) |
|
|
| if not (os.path.isdir(trainingOutpDir)): |
| os.mkdir(trainingOutpDir) |
|
|
| if not (os.path.isdir(saveDir)): |
| os.mkdir(saveDir) |
| |
| if (imageID == None): |
| imageID = 'NA' |
| else: |
| imageID = str(int(imageID)) |
| |
|
|
| imageName = folderName+"_ImgId_"+imageID+".png" |
| imageDir = os.path.join(saveDir, imageName) |
| plt.savefig(imageDir) |
| plt.close('all') |
| plt.cla() |
|
|
| else: |
| plt.show() |
| plt.close('all') |
| |
| |
| def generateAnchorsInOrigImage(anchors,headgridSize,originalPicSize=512): |
| ''' |
| Prepares anchor tensors in original image. |
| |
| E.g. If there are 4 anchors for the prediction head, |
| 4 anchor positions in original image are calculated for (x=0, y=0),(x=1, y=0)... feature grid, and written |
| one under the other to anchorsInOrig |
| |
| Args: |
| anchors : (tuple) Tuple of anchor boxes in Tensor w,h form Tuple(Shape[numAnchors,2]) |
| headgridSize : Prediction head grid size, 16 or 32 for mobilenet |
| originalPicSize : original image size |
| |
| Return: |
| anchorsInOrig : Tensor shape[#ofboxes*head width size*head height size,4], anchors are written in (cx, cy, w, h) form |
| ''' |
| scale = originalPicSize/headgridSize |
| anchorsInOrig = torch.zeros([len(anchors)*headgridSize*headgridSize,4]) |
| numOfAnchorBox = len(anchors) |
| for i in range(headgridSize): |
| for j in range(headgridSize): |
| for k in range(len(anchors)): |
| cx = j*scale + (scale+1)/2 |
| cy = i*scale + (scale+1)/2 |
| w, h = anchors[k] |
| tempAnch = torch.tensor([cx,cy,w,h]) |
| anchorsInOrig[i*headgridSize*numOfAnchorBox + j*numOfAnchorBox + k,:]=tempAnch |
| |
| |
| return anchorsInOrig |
|
|
|
|
| def prepareHeadDataforLoss(HeadBB,HeadConf): |
| ''' |
| Prepares prediction head tensors for loss calculation |
| |
| E.g. If there are 4 BBs for the prediction head, |
| 4 BB positions in delta form are written one under the other, for (x=0, y=0),(x=1, y=0)... of feature grid and returned |
| |
| Args: |
| HeadBB : (tensor) Location head of the layer Shape[numofAncBoxesperCell * 4, head width, head height ] |
| Boxes -> [dcx, dcy, dw, dh ] |
| HeadConf : (tensor) Confidence head of the layer Shape[numofAncBoxesperCell * 2, head width, head height ] |
| Confidences -> (p(person), p(background)) |
| |
| Return: |
| BBs : (tensor) Predicted bounding boxes are written in delta form (dcx, dcy, dw, dh) |
| shape[numofAncBoxesperCell * head width * head height ,4] -> shape[4096,4] for 32x32 head |
| |
| CFs : (tensor) Class confidences are written in (p(person), p(background)) |
| shape[#ofPredperFeatureCell * head width * head height ,2] -> shape[4096,2] for 32x32 head |
| ''' |
| width = HeadBB.shape[1] |
| height = HeadBB.shape[2] |
| |
| numOfAnchorBox = int(HeadBB.shape[0]/4) |
| BBs = torch.zeros([width*height*numOfAnchorBox,4]).to(device) |
| CFs = torch.zeros([width*height*numOfAnchorBox,2]).to(device) |
| for i in range(width): |
| for j in range(height): |
| for k in range(numOfAnchorBox): |
| BBs[i*height*numOfAnchorBox + j*numOfAnchorBox + k,:] = HeadBB[k*4:k*4+4,i,j] |
| CFs[i*height*numOfAnchorBox + j*numOfAnchorBox + k,:] = HeadConf[k*2:k*2+2,i,j] |
| |
| return BBs, CFs |
|
|
|
|
| def prepareHeadDataforLoss_fast(HeadBB,HeadConf): |
| ''' |
| Same function with prepareHeadDataforLoss(), but blackbox faster implementation. |
| See details in prepareHeadDataforLoss() |
| ''' |
|
|
| BBs = HeadBB.squeeze(0) |
| BBs = BBs.permute((1,2,0)) |
| BBs = BBs.contiguous().view(-1,4) |
|
|
| CFs = HeadConf.squeeze(0) |
| CFs = CFs.permute((1,2,0)) |
| CFs = CFs.contiguous().view(-1,2) |
| return BBs, CFs |
|
|
|
|
| |
| def point_form(boxes): |
| """ Convert box in form (cx, cy, w, h) to (xmin, ymin, xmax, ymax) |
| representation for comparison to point form ground truth data. |
| Args: |
| boxes: (tensor) boxes in (cx, cy, w, h) form |
| Return: |
| boxes: (tensor) Converted xmin, ymin, xmax, ymax form of boxes. |
| """ |
| return torch.cat((boxes[:, :2] - boxes[:, 2:]/2, |
| boxes[:, :2] + boxes[:, 2:]/2), 1) |
|
|
| |
| def intersect(box_a, box_b): |
| """ We resize both tensors to [A,B,2] without new malloc: |
| [A,2] -> [A,1,2] -> [A,B,2] |
| [B,2] -> [1,B,2] -> [A,B,2] |
| Then we compute the area of intersect between box_a and box_b. |
| Args: |
| box_a: (tensor) bounding boxes, Shape: [A,4]. xmin, ymin, xmax, ymax form |
| box_b: (tensor) bounding boxes, Shape: [B,4]. |
| Return: |
| (tensor) intersection area, Shape: [A,B]. |
| """ |
| device = torch.device("cuda" if torch.cuda.is_available() else "cpu") |
| A = box_a.size(0) |
| B = box_b.size(0) |
| box_a = box_a.to(device) |
| box_b = box_b.to(device) |
| max_xy = torch.min(box_a[:, 2:].unsqueeze(1).expand(A, B, 2), |
| box_b[:, 2:].unsqueeze(0).expand(A, B, 2)) |
| min_xy = torch.max(box_a[:, :2].unsqueeze(1).expand(A, B, 2), |
| box_b[:, :2].unsqueeze(0).expand(A, B, 2)) |
| inter = torch.clamp((max_xy - min_xy), min=0) |
| return inter[:, :, 0] * inter[:, :, 1] |
|
|
| def jaccard(box_a, box_b): |
| """Compute the jaccard overlap of two sets of boxes. The jaccard overlap |
| is simply the intersection over union of two boxes. Here we operate on |
| ground truth boxes and default boxes. |
| E.g.: |
| A ∩ B / A ∪ B = A ∩ B / (area(A) + area(B) - A ∩ B) |
| Args: |
| box_a: (tensor) Ground truth bounding boxes, Shape: [num_objects,4] |
| box_b: (tensor) Prior boxes from priorbox layers, Shape: [num_priors,4] |
| Return: |
| jaccard overlap: (tensor) Shape: [box_a.size(0), box_b.size(0)] |
| """ |
| device = torch.device("cuda" if torch.cuda.is_available() else "cpu") |
| |
| inter = intersect(box_a, box_b) |
| area_a = ((box_a[:, 2]-box_a[:, 0]) * |
| (box_a[:, 3]-box_a[:, 1])).unsqueeze(1).expand_as(inter) |
| area_b = ((box_b[:, 2]-box_b[:, 0]) * |
| (box_b[:, 3]-box_b[:, 1])).unsqueeze(0).expand_as(inter) |
| |
| area_a = area_a.to(device) |
| area_b = area_b.to(device) |
| union = area_a + area_b - inter |
| return inter / union |
|
|
| def collate_fn(batch): |
| """ |
| Custom collate function. |
| Need to create own collate_fn Function for COCO. |
| Merges a list of samples to form a mini-batch of Tensor(s). |
| Used when using batched loading from a map-style dataset. |
| """ |
| return zip(*batch) |
|
|
| def sampleRandomPicsFromCOCO_old(train_loader, numtoPlot = 10, PictureSize = 512): |
| ''' |
| This function is used to sample random pictures from COCO dataset |
| |
| Args: |
| numtoPlot : number of random pictures to plot from dataset |
| |
| Return: |
| SelectedPics : (tensor) size[numtoPlot, 3, PictureSize, PictureSize] |
| SelectedTargets: list[(tensor)] list of bounding boxes in COCO format for each picture |
| |
| ''' |
| import random |
| device = torch.device("cuda" if torch.cuda.is_available() else "cpu") |
|
|
| numofbatches = len(train_loader) |
| batchsize = train_loader.batch_size |
| randomBatches = random.sample(range(0, numofbatches), numtoPlot) |
|
|
| selectedTargets = [] |
| selectedPics = torch.zeros((numtoPlot,3,PictureSize,PictureSize)).to(device) |
| dataloader_iterator = iter(train_loader) |
|
|
| |
| i = 0 |
| batchnum = 0 |
| while batchnum < numofbatches: |
| |
| if batchnum in randomBatches: |
| data = next(dataloader_iterator) |
| picnum = random.randrange(0, batchsize, 1) |
| randomBatches.remove(batchnum) |
|
|
| imageBatch, targetBatch, picNum = data |
| image = imageBatch[picnum].unsqueeze(0).clone().to(device) |
| target = targetBatch[picnum].clone().to(device) |
|
|
| selectedPics[i,:,:,:] = image |
| selectedTargets.append(target) |
| i += 1 |
| else: |
| next(dataloader_iterator) |
| |
| batchnum += 1 |
|
|
| if not randomBatches: |
| break |
| |
| return selectedPics, selectedTargets |
|
|
|
|
| def sampleRandomPicsFromCOCO(dataset, numtoPick = 10, pickSame = False): |
| ''' |
| This function is used to sample random pictures from a COCO type dataset |
| |
| Args: |
| dataset: dataset to be sampled |
| numtoPick : number of random pictures to pick from dataset |
| pickSame: if it is set to true, |
| |
| Return: |
| SelectedPics : (tensor) size[numtoPlot, 3, PictureSize, PictureSize] |
| SelectedTargets: list[(tensor)] list of bounding boxes in COCO format for each picture |
| ''' |
| |
| if pickSame: |
| random.seed(1234) |
| else: |
| pass |
| |
| random_indices = random.sample(range(len(dataset)), numtoPick) |
| |
| rand_sampler = torch.utils.data.SubsetRandomSampler(random_indices) |
| loader = torch.utils.data.DataLoader(dataset, |
| sampler=rand_sampler, |
| batch_size=1, |
| collate_fn=collate_fn, |
| drop_last=False) |
| |
| device = torch.device("cuda" if torch.cuda.is_available() else "cpu") |
| |
| selectedTargets = [] |
| selectedPics = torch.zeros((numtoPick, 3, 512, 512)).to(device) |
| picIds = [] |
| |
| for i, data in enumerate(loader): |
| imageBatch, targetBatch, picNum = data |
| image = imageBatch[0].unsqueeze(0).to(device) |
| target = targetBatch[0].to(device) |
|
|
| selectedPics[i,:,:,:] = image |
| selectedTargets.append(target) |
| picIds.append(picNum[0]) |
| |
| return selectedPics, selectedTargets, picIds |
|
|
|
|
| def saveOutputs(pictures, picIds, targets, preds, anchors_head1, anchors_head2, |
| savefolderName='UnconstFPT', |
| nmsIoUTreshold = 0.45, predConfPlotTreshold = 0.6, figsize=(8,8)): |
| ''' |
| Saves pictures,ground truths and model predictions under specified folder |
| ''' |
| predsPostProcess = PredsPostProcess(512, anchors_head1, anchors_head2) |
| |
| image_width = pictures.shape[2] |
| image_height = pictures.shape[3] |
| |
| BBs1 = preds[0].clone() |
| CFs1 = preds[1].clone() |
| BBs2 = preds[2].clone() |
| CFs2 = preds[3].clone() |
| |
| for imgNum in tqdm(range(0,pictures.shape[0])): |
| |
| img = pictures[imgNum,:,:,:].clone() |
| target = targets[imgNum].clone() |
| pred = (BBs1[imgNum,:,:,:].unsqueeze(0), CFs1[imgNum,:,:,:].unsqueeze(0), |
| BBs2[imgNum,:,:,:].unsqueeze(0), CFs2[imgNum,:,:,:].unsqueeze(0)) |
| id = picIds[imgNum] |
|
|
| absolute_boxes,person_cls = predsPostProcess.getPredsInOriginal(pred) |
| |
| plot_image_mnv2_2xSSDlite(img, pred_person_cls = person_cls, pred_absolute_boxes = absolute_boxes, color = 'r' |
| ,nmsIoUTreshold = nmsIoUTreshold, predConfPlotTreshold = predConfPlotTreshold, |
| target=target, figsize=figsize, |
| saveFig=True, imageID= id, folderName = savefolderName) |
|
|
| class PredsPostProcess: |
| ''' |
| Class to convert mobilenet SSD heads to real image coordinates in form [xmin, ymin, xmax, ymax] |
| |
| ''' |
| def __init__(self, image_width, anchors_head1, anchors_head2): |
| Head1AnchorsForLoss = generateAnchorsInOrigImage(anchors_head1,headgridSize=32,originalPicSize=image_width) |
| Head2AnchorsForLoss = generateAnchorsInOrigImage(anchors_head2,headgridSize=16,originalPicSize=image_width) |
| AnchorsFlatten_wh = torch.cat((Head1AnchorsForLoss,Head2AnchorsForLoss),0) |
| |
| |
| device = torch.device("cuda" if torch.cuda.is_available() else "cpu") |
| |
| AnchorsFlatten_wh = AnchorsFlatten_wh.to(device) |
| self.AnchorsFlatten_wh = AnchorsFlatten_wh |
| self.softmax_fcn = torch.nn.Softmax(dim=1).to(device) |
| self.var_x = 0.1 |
| self.var_y = 0.1 |
| self.var_w = 0.2 |
| self.var_h = 0.2 |
| |
| def getPredsInOriginal(self,preds): |
| ''' |
| Args: |
| preds: Prediction heads, i.e output of mobilenet model() |
| |
| Return: |
| absolute_boxes: 32 * 32 *4 + 16 * 16 * 5 = 5376 pred BB's in form [imagenum, xmin, ymin, xmax, ymax] |
| (tensor) [5376, 5] |
| person cls: Person classification heads, (tensor) [5376,1] |
| |
| ''' |
| AnchorsFlatten_wh = self.AnchorsFlatten_wh |
| BBhires, CFhires = prepareHeadDataforLoss_fast(preds[0].data,preds[1].data) |
| BBlores, CFlores = prepareHeadDataforLoss_fast(preds[2].data,preds[3].data) |
|
|
| |
| cls = torch.cat(( CFhires, CFlores)) |
| cls = self.softmax_fcn(cls) |
| person_cls =cls[:,0] |
|
|
| delta_boxes_wh = torch.cat(( BBhires, BBlores)) |
|
|
| pred_cx = delta_boxes_wh[:,0]*self.var_x*self.AnchorsFlatten_wh[:,2] + self.AnchorsFlatten_wh[:,0] |
| pred_cy = delta_boxes_wh[:,1]*self.var_y*self.AnchorsFlatten_wh[:,3] + self.AnchorsFlatten_wh[:,1] |
| pred_w = (delta_boxes_wh[:,2]*self.var_w).exp()*self.AnchorsFlatten_wh[:,2] |
| pred_h = (delta_boxes_wh[:,3]*self.var_h).exp()*self.AnchorsFlatten_wh[:,3] |
|
|
| absolute_xleft = pred_cx - pred_w/2 |
| absolute_ytop = pred_cy - pred_h/2 |
| absolute_xright = pred_cx + pred_w/2 |
| absolute_ybottom = pred_cy + pred_h/2 |
|
|
| absolute_boxes = torch.cat((absolute_xleft.view(-1,1), absolute_ytop.view(-1,1), absolute_xright.view(-1,1), absolute_ybottom.view(-1,1)), dim=1) |
|
|
| return absolute_boxes, person_cls |
| |
| |
| def mAP(cocoGT, cocoDT, imgIDS, catIDS=1, annType="bbox"): |
| """ |
| Explanation: This function calculate the mean average precision for given |
| ground truths and detection results. Default category and |
| annotation format is set to 'person' and 'bbox' respectively. |
| This function is based on popular benchmark function "pycocotools" |
| that is forked 3.3k. Please re-check the iou threshold (parameter iouThrs) |
| ,which is default '.5:.05:.95', before you run the code. |
| Arguments: |
| cocoGT(Json File): Annotated orginal valset of COCO. |
| cocoDT(Json File): Model Results as format ===> [{"image_id":42, "category_id":18, "bbox":[258.15,41.29,348.26,243.78],"score":0.236}, |
| {"image_id":73, "category_id":11, "bbox":[61,22.75,504,609.67], "score":0.318}, |
| ...] |
| imgIDS(list): list of image IDs. |
| catIDS(list): list of category ids. Default=1 as person. |
| annType(String): Annotation type, Default=bbox. Can be ['segm','bbox','keypoints']. |
| Returns: |
| None: just results as strings in terminal. |
| ######################## More Detailed Guideline ######################## |
| The usage for CocoEval is as follows: # |
| cocoGt=..., cocoDt=... # load dataset and results # |
| E = CocoEval(cocoGt,cocoDt); # initialize CocoEval object # |
| E.params.recThrs = ...; # set parameters as desired # |
| E.evaluate(); # run per image evaluation # |
| E.accumulate(); # accumulate per image results # |
| E.summarize(); # display summary metrics of results # |
| ######################################################################### |
| The evaluation parameters are as follows (defaults in brackets): # |
| imgIds - [all] N img ids to use for evaluation # |
| catIds - [all] K cat ids to use for evaluation # |
| iouThrs - [.5:.05:.95] T=10 IoU thresholds for evaluation # |
| recThrs - [0:.01:1] R=101 recall thresholds for evaluation # |
| areaRng - [...] A=4 object area ranges for evaluation # |
| maxDets - [1 10 100] M=3 thresholds on max detections per image # |
| iouType - ['segm'] set iouType to 'segm', 'bbox' or 'keypoints' # |
| iouType replaced the now DEPRECATED useSegm parameter. # |
| useCats - [1] if true use category labels for evaluation # |
| Note: if useCats=0 category labels are ignored as in proposal scoring. # |
| Note: multiple areaRngs [Ax2] and maxDets [Mx1] can be specified. # |
| ######################################################################### |
| evaluate(): evaluates detections on every image and every category and # |
| concats the results into the "evalImgs" with fields: # |
| dtIds - [1xD] id for each of the D detections (dt) # |
| gtIds - [1xG] id for each of the G ground truths (gt) # |
| dtMatches - [TxD] matching gt id at each IoU or 0 # |
| gtMatches - [TxG] matching dt id at each IoU or 0 # |
| dtScores - [1xD] confidence of each dt # |
| gtIgnore - [1xG] ignore flag for each gt # |
| dtIgnore - [TxD] ignore flag for each dt at each IoU # |
| ######################################################################### |
| accumulate(): accumulates the per-image, per-category evaluation # |
| results in "evalImgs" into the dictionary "eval" with fields: # |
| params - parameters used for evaluation # |
| date - date evaluation was performed # |
| counts - [T,R,K,A,M] parameter dimensions (see above) # |
| precision - [TxRxKxAxM] precision for every evaluation setting # |
| recall - [TxKxAxM] max recall for every evaluation setting # |
| Note: precision and recall==-1 for settings with no gt objects. # |
| ######################################################################### |
| ***For more details of COCOeval please check: https://github.com/cocodataset/cocoapi/blob/master/PythonAPI/pycocotools/cocoeval.py |
| ***If you need an orginal example from API please check: https://github.com/cocodataset/cocoapi/blob/master/PythonAPI/pycocoEvalDemo.ipynb |
| """ |
| |
| cocoEval = COCOeval(cocoGT,cocoDT,annType) |
| cocoEval.params.imgIds = imgIDS |
| cocoEval.params.catIds = catIDS |
| cocoEval.evaluate() |
| cocoEval.accumulate() |
| cocoEval.summarize() |
| |
| |
| def round_floats(o): |
| ''' |
| Used to round floats before writing to json form |
| ''' |
| if isinstance(o, float): |
| return round(o, 3) |
| if isinstance(o, dict): |
| return {k: round_floats(v) for k, v in o.items()} |
| if isinstance(o, (list, tuple)): |
| return [round_floats(x) for x in o] |
| return o |
|
|
|
|
| def get_FPnum_per_image(bbox, GT_bbox, min_IoU = 0.5): |
| ''' Founds the number of False Positives by assocating detection BB's to GT BBs |
| |
| Arguments: |
| ------------- |
| bbox : list |
| N x 4 list of detection bounding boxes in xmin, ymin, w, h form |
| GT_bbox : list |
| N x 4 list of ground truth bounding boxes in xmin, ymin, w, h form |
| min_IoU : float [0,1] |
| Treshold of intersection of union to evaluate detection and GT to be matched, if IoU of Det and GT is below |
| this value they are automatically marked as unmatched |
| ''' |
| |
| bbox = torch.tensor(bbox) |
|
|
| |
| bbox[:,2] = bbox[:,0] + bbox[:,2] |
| bbox[:,3] = bbox[:,1] + bbox[:,3] |
|
|
| GT_bbox[:,2] = GT_bbox[:,0] + GT_bbox[:,2] |
| GT_bbox[:,3] = GT_bbox[:,1] + GT_bbox[:,3] |
|
|
| IoUscore = jaccard(GT_bbox, bbox) |
|
|
| num_det = IoUscore.shape[1] |
| num_TP = 0 |
| GT_indexes = [x for x in range(IoUscore.shape[0])] |
|
|
| |
| for det_idx in range(IoUscore.shape[1]): |
| |
| max_IoU = min_IoU |
| max_IoU_gt_id = None |
|
|
| |
| for i, gt_idx in enumerate(GT_indexes): |
| currentIoU = IoUscore[gt_idx, det_idx] |
| if currentIoU > max_IoU: |
| max_IoU = currentIoU |
| max_IoU_gt_id = i |
|
|
| if max_IoU_gt_id is not None: |
| del GT_indexes[max_IoU_gt_id] |
| num_TP += 1 |
|
|
| if len(GT_indexes) == 0: |
| break |
|
|
| FP_count_image = num_det - num_TP |
| return FP_count_image |
|
|
|
|
| def calculatemAP(model, test_loader,cocoGT, ANCHORS_HEAD1, ANCHORS_HEAD2 , PredMinConfTreshold=0.7 , |
| nmsIoUTreshold = 0.5, mAPOnlyFirstBatch= False, calculate_FP_ratio=False, hardware_mode = False): |
| device = torch.device("cuda" if torch.cuda.is_available() else "cpu") |
| t1 = time.time() |
| print('mAP calculation started...') |
| predsPostProcess = PredsPostProcess(512, ANCHORS_HEAD1, ANCHORS_HEAD2) |
|
|
| dataDictList =[] |
| imgIDS = [] |
| model.eval() |
| |
| total_GT_count = 0 |
| total_FP_count = 0 |
| |
| with torch.no_grad(): |
| for i, data in enumerate(tqdm(test_loader)): |
|
|
| imageBatch, targetBatch , idxBatch = data |
|
|
| imageStack = torch.stack(imageBatch).detach().to(device) |
| predBatch = model(imageStack) |
| |
| |
| if hardware_mode: |
| BBs1 = predBatch[0].detach() / 128.0 |
| CFs1 = predBatch[1].detach() / 128.0 |
| BBs2 = predBatch[2].detach() / 128.0 |
| CFs2 = predBatch[3].detach() / 128.0 |
| else: |
| BBs1 = predBatch[0].detach() |
| CFs1 = predBatch[1].detach() |
| BBs2 = predBatch[2].detach() |
| CFs2 = predBatch[3].detach() |
|
|
| for imgNum in range(imageStack.shape[0]): |
|
|
| img = imageStack[imgNum,:,:,:] |
| target = targetBatch[imgNum] |
| image_id = int(idxBatch[imgNum]) |
| imgIDS.append(image_id) |
|
|
| pred = (BBs1[imgNum,:,:,:].unsqueeze(0), CFs1[imgNum,:,:,:].unsqueeze(0), |
| BBs2[imgNum,:,:,:].unsqueeze(0), CFs2[imgNum,:,:,:].unsqueeze(0)) |
|
|
| absolute_boxes, person_cls = predsPostProcess.getPredsInOriginal(pred) |
|
|
| confidences = person_cls |
| boxes = absolute_boxes |
| nms_picks = torchvision.ops.nms(boxes, confidences, nmsIoUTreshold) |
| boxes_to_draw = boxes[nms_picks] |
| confs_to_draw = confidences[nms_picks] |
|
|
| |
| confMask = (confs_to_draw > PredMinConfTreshold) |
| |
| |
| if calculate_FP_ratio and (target.shape[0] != 0): |
| GT_bbox = target[:,1:] |
| total_GT_count += GT_bbox.shape[0] |
| |
| |
| if (confMask.any()): |
|
|
| |
| bbox = boxes_to_draw[confMask] |
| |
| bbox[:,2] = bbox[:,2] - bbox[:,0] |
| bbox[:,3] = bbox[:,3] - bbox[:,1] |
|
|
| bbox = bbox.tolist() |
| score = confs_to_draw[confMask].tolist() |
| category_id = np.ones_like(score,dtype=int).tolist() |
|
|
| for j in range(len(bbox)): |
| box = {"image_id":image_id, "category_id":category_id[j], "bbox":bbox[j],"score":score[j]} |
| dataDictList.append(round_floats(box)) |
| |
| |
| if calculate_FP_ratio: |
| |
| |
| if GT_bbox.shape[0] == 0: |
| total_FP_count += len(score) |
|
|
| |
| else: |
| FP_count_image = get_FPnum_per_image(bbox, GT_bbox, min_IoU=0.5) |
| total_FP_count += FP_count_image |
|
|
| if mAPOnlyFirstBatch: |
| break |
| |
| if (len(dataDictList)): |
| |
| cocoDT = json.dumps(dataDictList) |
|
|
| |
| with open('cocoDT.json', 'w') as outfile: |
| outfile.write(cocoDT) |
|
|
| |
| cocoDT=cocoGT.loadRes('cocoDT.json') |
|
|
| |
| annType = 'bbox' |
| cocoEval = COCOeval(cocoGT,cocoDT,annType) |
| cocoEval.params.catIds = 1 |
| cocoEval.params.imgIds = imgIDS |
| cocoEval.evaluate() |
| cocoEval.accumulate() |
| cocoEval.summarize() |
| |
| |
| if calculate_FP_ratio: |
| print() |
| print('********** False Positive Statistics **********') |
| print(f'Total GT Boxes: {total_GT_count}, Total FPs Boxes: {total_FP_count}, FP% : {total_FP_count/total_GT_count*100}') |
| print() |
| |
|
|
| mean_ap = cocoEval.stats[0].item() |
| mean_recall = cocoEval.stats[8].item() |
|
|
| |
| os.remove("cocoDT.json") |
| else: |
| mean_ap = 0 |
| mean_recall = 0 |
| t2 = time.time() |
| print(f'mAP done in : {t2-t1} secs') |
| return mean_ap, mean_recall |
|
|
|
|
| def batchNormAdaptation(model, train_loader,numSamples = 100): |
| ''' |
| BN parameters of intel model is spoiled intentionally/or unintentionaly before publishing. |
| Batch norm adaptation routine is proposed before any training based on this model. |
| https://github.com/openvinotoolkit/nncf/blob/develop/docs/compression_algorithms/Quantization.md#batch-norm-statistics-adaptation |
| #numSamples predictions are made and running mean variance are recalculated for the layers. |
| ''' |
| device = torch.device("cuda" if torch.cuda.is_available() else "cpu") |
| |
| print('') |
| print('Batch norm adaptation before training started.') |
|
|
|
|
| for i, data in enumerate(train_loader): |
|
|
| imageBatch, targetBatch, imgIDs = data |
|
|
| imageStack = torch.stack(imageBatch) |
| imageStack = imageStack.detach() |
| imageStack.requires_grad_(False) |
| imageStack = imageStack.to(device) |
|
|
| predBatch = model(imageStack) |
| if (i*len(imgIDs) >= numSamples): |
| return model |
|
|
| |
| def conv_model_fptunc2fpt(model): |
| layer_str_arr = [attr_name for attr_name in dir(model) if |
| isinstance(getattr(model, attr_name), qat_core.layers.shallow_base_layer)] |
| |
| for layer in layer_str_arr: |
| layer_attribute = getattr(model, layer) |
| layer_attribute.mode_fptunconstrained2fpt('fpt') |
| setattr(model, layer, layer_attribute) |
| |
| |
| add_res_attribute = getattr(model, 'add_residual') |
| add_res_attribute.mode_fptunconstrained2fpt('fpt') |
| setattr(model, 'add_residual', add_res_attribute) |
| |
| return model |
|
|
|
|
| def conv_model_fpt2qat(model, weight_dictionary, shift_quantile=0.985): |
| print('Folding BNs and converting to qat mode') |
| layer_attributes = [] |
| for layer_string in dir(model): |
| if(layer_string in weight_dictionary): |
| layer_attribute = getattr(model, layer_string) |
| |
| if layer_attribute.mode == 'fpt': |
| print('Folding BN for:', layer_string) |
| weight_bits=weight_dictionary[layer_string] |
| print(f'Layer bit is : {weight_bits}') |
| |
| |
| if weight_bits == 1: |
| print('layer is converted in to qat_ap mode') |
| layer_attribute.configure_layer_base(weight_bits=2 , bias_bits=8, shift_quantile=shift_quantile) |
| layer_attribute.mode_fpt2qat('qat_ap'); |
| |
| else: |
| print('layer is converted in to qat mode') |
| layer_attribute.configure_layer_base(weight_bits=weight_bits , bias_bits=8, shift_quantile=shift_quantile) |
| layer_attribute.mode_fpt2qat('qat'); |
| |
| setattr(model, layer_string, layer_attribute) |
| print('') |
| |
| else: |
| print('To convert model to QAT mode, all layers must be in fpt mode but, ' + layer_string + 'is in' + layer_attribute.mode +' mode. Exiting...') |
| sys.exit() |
|
|
| add_res_attribute = getattr(model, 'add_residual') |
| if add_res_attribute.mode == 'fpt': |
| add_res_attribute.mode_fpt2qat('qat') |
| setattr(model, 'add_residual', add_res_attribute) |
| else: |
| print('To convert model to QAT mode, add_residual modüle must be in fpt mode but, it is in ' + add_res_attribute.mode + ' mode. Exiting...') |
| sys.exit() |
| |
| print('********* Converting to qat mode finished *********') |
| print('') |
| return model |
|
|
| def conv_model_qat2hw(model): |
| print('Converting model to eval/hw mode for testing') |
| |
| layer_str_arr = [attr_name for attr_name in dir(model) if |
| isinstance(getattr(model, attr_name), qat_core.layers.shallow_base_layer)] |
| |
| for layer in layer_str_arr: |
| layer_attribute = getattr(model, layer) |
|
|
| if layer_attribute.mode == 'qat': |
| layer_attribute.mode_qat2hw('eval') |
| setattr(model, layer, layer_attribute) |
| |
| elif layer_attribute.mode == 'qat_ap': |
| layer_attribute.mode_qat_ap2hw('eval') |
| setattr(model, layer, layer_attribute) |
| |
| else: |
| print('To convert model to hw mode, all layers must be in qat or qat_ap mode but, ' + layer_string + 'is in' + layer_attribute.mode +' mode. Exiting...') |
| sys.exit() |
| |
| model = model.to(model.conv1.op.weight.device.type) |
| |
| |
| add_res_attribute = getattr(model, 'add_residual') |
| if add_res_attribute.mode == 'qat': |
| add_res_attribute.mode_qat2hw('eval') |
| setattr(model, 'add_residual', add_res_attribute) |
| else: |
| print('To convert model to QAT mode, add_residual modüle must be in qat mode but, it is in ' + add_res_attribute.mode + ' mode. Exiting...') |
| sys.exit() |
| |
| print('********* Converting model to eval/hw mode for testing finished *********') |
| print('') |
| return model |