目录

  • 说明
  • 配置环境
  • 此节说明
  • 代码

说明

本博客代码来自开源项目:《动手学深度学习》(PyTorch版)
并且在博主学习的理解上对代码进行了大量注释,方便理解各个函数的原理和用途

配置环境

使用环境:python3.8
平台:Windows10
IDE:PyCharm

此节说明

此节对应书本上9.4节
此节功能为:锚框
由于此节相对复杂,代码注释量较多

代码

# 本书链接https://tangshusen.me/Dive-into-DL-PyTorch/#/
# 锚框
# 注释:黄文俊
# E-mail:hurri_cane@qq.comfrom matplotlib import pyplot as plt
from PIL import Image
import numpy as np
import math
import torchimport sys
sys.path.append("..")
import d2lzh_pytorch as d2l
print(torch.__version__) # 1.2.0d2l.set_figsize()
img = Image.open('F:/PyCharm/Learning_pytorch/data/img/catdog.jpg')
w, h = img.size
print("w = %d, h = %d" % (w, h)) # w = 728, h = 561# 本函数已保存在d2lzh_pytorch包中方便以后使用
def MultiBoxPrior(feature_map, sizes=[0.75, 0.5, 0.25], ratios=[1, 2, 0.5]):"""feature_map: torch tensor, Shape: [N, C, H, W].sizes: List of sizes (0~1) of generated MultiBoxPriores.ratios: List of aspect ratios (non-negative) of generated MultiBoxPriores.Returns:anchors of shape (1, num_anchors, 4). 由于batch里每个都一样, 所以第一维为1"""pairs = []  # pair of (size, sqrt(ration))for r in ratios:pairs.append([sizes[0], math.sqrt(r)])for s in sizes[1:]:pairs.append([s, math.sqrt(ratios[0])])pairs = np.array(pairs)ss1 = pairs[:, 0] * pairs[:, 1]     # size * sqrt(ration)ss2 = pairs[:, 0] / pairs[:, 1]     # size / sqrt(ration)base_anchors = np.stack([-ss1, -ss2, ss1, ss2], axis=1) / 2# 从中心点出发的半边长度h, w = feature_map.shape[-2:]shifts_x = np.arange(0, w) / wshifts_y = np.arange(0, h) / hshift_x, shift_y = np.meshgrid(shifts_x, shifts_y)# 生成网格shift_x = shift_x.reshape(-1)shift_y = shift_y.reshape(-1)shifts = np.stack((shift_x, shift_y, shift_x, shift_y), axis=1)anchors = shifts.reshape((-1, 1, 4)) + base_anchors.reshape((1, -1, 4))return torch.tensor(anchors, dtype=torch.float32).view(1, -1, 4)X = torch.Tensor(1, 3, h, w)  # 构造输入数据
Y = MultiBoxPrior(X, sizes=[0.75, 0.5, 0.25], ratios=[1, 2, 0.5])
print(Y.shape)  # torch.Size([1, 2042040, 4])boxes = Y.reshape((h, w, 5, 4))
print(boxes[250, 250, 0, :])    # * torch.tensor([w, h, w, h], dtype=torch.float32)# 在图像上画出多个边界框
# 本函数已保存在d2lzh_pytorch包中方便以后使用
def show_bboxes(axes, bboxes, labels=None, colors=None):def _make_list(obj, default_values=None):if obj is None:obj = default_valueselif not isinstance(obj, (list, tuple)):obj = [obj]return objlabels = _make_list(labels)colors = _make_list(colors, ['b', 'g', 'r', 'm', 'c'])for i, bbox in enumerate(bboxes):color = colors[i % len(colors)] # 取余rect = d2l.bbox_to_rect(bbox.detach().cpu().numpy(), color)axes.add_patch(rect)if labels and len(labels) > i:text_color = 'k' if color == 'w' else 'w'axes.text(rect.xy[0], rect.xy[1], labels[i],va='center', ha='center', fontsize=6, color=text_color,bbox=dict(facecolor=color, lw=0))d2l.set_figsize()
fig = d2l.plt.imshow(img)
bbox_scale = torch.tensor([[w, h, w, h]], dtype=torch.float32)
show_bboxes(fig.axes, boxes[250, 250, :, :] * bbox_scale,['s=0.75, r=1', 's=0.75, r=2', 's=0.55, r=0.5', 's=0.5, r=1', 's=0.25, r=1'])plt.show()
print("*"*50)# 9.4.2 交并比(IOU)# 以下函数已保存在d2lzh_pytorch包中方便以后使用
# 参考https://github.com/sgrvinod/a-PyTorch-Tutorial-to-Object-Detection/blob/master/utils.py#L356
def compute_intersection(set_1, set_2):"""计算anchor之间的交集Args:set_1: a tensor of dimensions (n1, 4), anchor表示成(xmin, ymin, xmax, ymax)set_2: a tensor of dimensions (n2, 4), anchor表示成(xmin, ymin, xmax, ymax)Returns:intersection of each of the boxes in set 1 with respect to each of the boxes in set 2, shape: (n1, n2)"""# PyTorch auto-broadcasts singleton dimensionslower_bounds = torch.max(set_1[:, :2].unsqueeze(1), set_2[:, :2].unsqueeze(0))  # (n1, n2, 2)upper_bounds = torch.min(set_1[:, 2:].unsqueeze(1), set_2[:, 2:].unsqueeze(0))  # (n1, n2, 2)# 在指定维度上增加一个维度intersection_dims = torch.clamp(upper_bounds - lower_bounds, min=0)  # (n1, n2, 2)# torch.clamp(input, min, max, out=None) → Tensor# 将输入input张量每个元素的夹紧到区间 [min,max][min,max],并返回结果到一个新张量。return intersection_dims[:, :, 0] * intersection_dims[:, :, 1]  # (n1, n2)def compute_jaccard(set_1, set_2):"""计算anchor之间的Jaccard系数(IoU)Args:set_1: a tensor of dimensions (n1, 4), anchor表示成(xmin, ymin, xmax, ymax)set_2: a tensor of dimensions (n2, 4), anchor表示成(xmin, ymin, xmax, ymax)Returns:Jaccard Overlap of each of the boxes in set 1 with respect to each of the boxes in set 2, shape: (n1, n2)"""# Find intersectionsintersection = compute_intersection(set_1, set_2)  # (n1, n2)# Find areas of each box in both setsareas_set_1 = (set_1[:, 2] - set_1[:, 0]) * (set_1[:, 3] - set_1[:, 1])  # (n1)areas_set_2 = (set_2[:, 2] - set_2[:, 0]) * (set_2[:, 3] - set_2[:, 1])  # (n2)# Find the union# PyTorch auto-broadcasts singleton dimensionsunion = areas_set_1.unsqueeze(1) + areas_set_2.unsqueeze(0) - intersection  # (n1, n2)return intersection / union  # (n1, n2)bbox_scale = torch.tensor((w, h, w, h), dtype=torch.float32)
ground_truth = torch.tensor([[0, 0.1, 0.08, 0.52, 0.92],[1, 0.55, 0.2, 0.9, 0.88]])
anchors = torch.tensor([[0, 0.1, 0.2, 0.3], [0.15, 0.2, 0.4, 0.4],[0.63, 0.05, 0.88, 0.98], [0.66, 0.45, 0.8, 0.8],[0.57, 0.3, 0.92, 0.9]])fig = d2l.plt.imshow(img)
show_bboxes(fig.axes, ground_truth[:, 1:] * bbox_scale, ['dog', 'cat'], 'k')
show_bboxes(fig.axes, anchors * bbox_scale, ['0', '1', '2', '3', '4']);
plt.show()# 为锚框标注类别和偏移量
# 以下函数已保存在d2lzh_pytorch包中方便以后使用
def assign_anchor(bb, anchor, jaccard_threshold=0.5):"""Args:bb: 真实边界框(bounding box), shape:(nb, 4)anchor: 待分配的anchor, shape:(na, 4)jaccard_threshold: 预先设定的阈值Returns:assigned_idx: shape: (na, ), 每个anchor分配的真实bb对应的索引, 若未分配任何bb则为-1"""na = anchor.shape[0]nb = bb.shape[0]jaccard = compute_jaccard(anchor, bb).detach().cpu().numpy()    # shape: (na, nb)assigned_idx = np.ones(na) * -1  # 初始全为-1# 先为每个bb分配一个anchor(不要求满足jaccard_threshold)jaccard_cp = jaccard.copy()for j in range(nb):i = np.argmax(jaccard_cp[:, j])assigned_idx[i] = jjaccard_cp[i, :] = float("-inf")    # 赋值为负无穷, 相当于去掉这一行# 处理还未被分配的anchor, 要求满足jaccard_thresholdfor i in range(na):if assigned_idx[i] == -1:j = np.argmax(jaccard[i, :])if jaccard[i, j] >= jaccard_threshold:assigned_idx[i] = jreturn torch.tensor(assigned_idx, dtype=torch.long)def xy_to_cxcy(xy):"""Args:xy: bounding boxes in boundary coordinates, a tensor of size (n_boxes, 4)Returns:bounding boxes in center-size coordinates, a tensor of size (n_boxes, 4)"""return torch.cat([(xy[:, 2:] + xy[:, :2]) / 2,  # c_x, c_yxy[:, 2:] - xy[:, :2]], 1)  # w, hdef MultiBoxTarget(anchor, label):"""Args:anchor: torch tensor, 输入的锚框, 一般是通过MultiBoxPrior生成, shape:(1,锚框总数,4)label: 真实标签, shape为(bn, 每张图片最多的真实锚框数, 5)第二维中,如果给定图片没有这么多锚框, 可以先用-1填充空白, 最后一维中的元素为[类别标签, 四个坐标值]Returns:列表, [bbox_offset, bbox_mask, cls_labels]bbox_offset: 每个锚框的标注偏移量,形状为(bn,锚框总数*4)bbox_mask: 形状同bbox_offset, 每个锚框的掩码, 一一对应上面的偏移量, 负类锚框(背景)对应的掩码均为0, 正类锚框的掩码均为1cls_labels: 每个锚框的标注类别, 其中0表示为背景, 形状为(bn,锚框总数)"""assert len(anchor.shape) == 3 and len(label.shape) == 3bn = label.shape[0]def MultiBoxTarget_one(anc, lab, eps=1e-6):"""MultiBoxTarget函数的辅助函数, 处理batch中的一个Args:anc: shape of (锚框总数, 4)lab: shape of (真实锚框数, 5), 5代表[类别标签, 四个坐标值]eps: 一个极小值, 防止log0Returns:offset: (锚框总数*4, )bbox_mask: (锚框总数*4, ), 0代表背景, 1代表非背景cls_labels: (锚框总数, 4), 0代表背景"""an = anc.shape[0]assigned_idx = assign_anchor(lab[:, 1:], anc)   # (锚框总数, )# 将真实锚框的位置代入assign_anchor函数,而不代入真实锚框的编号bbox_mask = ((assigned_idx >= 0).float().unsqueeze(-1)).repeat(1, 4)    # (锚框总数, 4)# .repeat(1, 4)第一列元素重复4次cls_labels = torch.zeros(an, dtype=torch.long)  # 0表示背景assigned_bb = torch.zeros((an, 4), dtype=torch.float32) # 所有anchor对应的bb坐标for i in range(an):bb_idx = assigned_idx[i]if bb_idx >= 0: # 即非背景cls_labels[i] = lab[bb_idx, 0].long().item() + 1 # 注意要加一assigned_bb[i, :] = lab[bb_idx, 1:]center_anc = xy_to_cxcy(anc) # (center_x, center_y, w, h)center_assigned_bb = xy_to_cxcy(assigned_bb)offset_xy = 10.0 * (center_assigned_bb[:, :2] - center_anc[:, :2]) / center_anc[:, 2:]offset_wh = 5.0 * torch.log(eps + center_assigned_bb[:, 2:] / center_anc[:, 2:])offset = torch.cat([offset_xy, offset_wh], dim = 1) * bbox_mask # (锚框总数, 4)return offset.view(-1), bbox_mask.view(-1), cls_labelsbatch_offset = []batch_mask = []batch_cls_labels = []for b in range(bn):offset, bbox_mask, cls_labels = MultiBoxTarget_one(anchor[0, :, :], label[b, :, :])batch_offset.append(offset)batch_mask.append(bbox_mask)batch_cls_labels.append(cls_labels)bbox_offset = torch.stack(batch_offset)bbox_mask = torch.stack(batch_mask)cls_labels = torch.stack(batch_cls_labels)return [bbox_offset, bbox_mask, cls_labels]# 我们通过unsqueeze函数为锚框和真实边界框添加样本维。
labels = MultiBoxTarget(anchors.unsqueeze(dim=0),ground_truth.unsqueeze(dim=0))print(labels)
print("*"*50)# 9.4.4 输出预测边界框# 非极大值抑制
# 以下函数已保存在d2lzh_pytorch包中方便以后使用
from collections import namedtuple
Pred_BB_Info = namedtuple("Pred_BB_Info", ["index", "class_id", "confidence", "xyxy"])def non_max_suppression(bb_info_list, nms_threshold = 0.5):"""非极大抑制处理预测的边界框Args:bb_info_list: Pred_BB_Info的列表, 包含预测类别、置信度等信息nms_threshold: 阈值Returns:output: Pred_BB_Info的列表, 只保留过滤后的边界框信息"""output = []# 先根据置信度从高到低排序sorted_bb_info_list = sorted(bb_info_list, key = lambda x: x.confidence, reverse=True)while len(sorted_bb_info_list) != 0:best = sorted_bb_info_list.pop(0)output.append(best)if len(sorted_bb_info_list) == 0:breakbb_xyxy = []for bb in sorted_bb_info_list:bb_xyxy.append(bb.xyxy)iou = compute_jaccard(torch.tensor([best.xyxy]),torch.tensor(bb_xyxy))[0] # shape: (len(sorted_bb_info_list), )n = len(sorted_bb_info_list)sorted_bb_info_list = [sorted_bb_info_list[i] for i in range(n) if iou[i] <= nms_threshold]return outputdef MultiBoxDetection(cls_prob, loc_pred, anchor, nms_threshold = 0.5):"""Args:cls_prob: 经过softmax后得到的各个锚框的预测概率, shape:(bn, 预测总类别数+1, 锚框个数)loc_pred: 预测的各个锚框的偏移量, shape:(bn, 锚框个数*4)anchor: MultiBoxPrior输出的默认锚框, shape: (1, 锚框个数, 4)nms_threshold: 非极大抑制中的阈值Returns:所有锚框的信息, shape: (bn, 锚框个数, 6)每个锚框信息由[class_id, confidence, xmin, ymin, xmax, ymax]表示class_id=-1 表示背景或在非极大值抑制中被移除了"""assert len(cls_prob.shape) == 3 and len(loc_pred.shape) == 2 and len(anchor.shape) == 3bn = cls_prob.shape[0]def MultiBoxDetection_one(c_p, l_p, anc, nms_threshold = 0.5):"""MultiBoxDetection的辅助函数, 处理batch中的一个Args:c_p: (预测总类别数+1, 锚框个数)l_p: (锚框个数*4, )anc: (锚框个数, 4)nms_threshold: 非极大抑制中的阈值Return:output: (锚框个数, 6)"""pred_bb_num = c_p.shape[1]anc = (anc + l_p.view(pred_bb_num, 4)).detach().cpu().numpy()   # 加上偏移量confidence, class_id = torch.max(c_p, 0)confidence = confidence.detach().cpu().numpy()class_id = class_id.detach().cpu().numpy()pred_bb_info = [Pred_BB_Info(index = i,class_id = class_id[i] - 1, # 正类label从0开始confidence = confidence[i],xyxy=[*anc[i]]) # xyxy是个列表for i in range(pred_bb_num)]# 正类的indexobj_bb_idx = [bb.index for bb in non_max_suppression(pred_bb_info, nms_threshold)]output = []for bb in pred_bb_info:output.append([(bb.class_id if bb.index in obj_bb_idx else -1.0),bb.confidence,*bb.xyxy])return torch.tensor(output) # shape: (锚框个数, 6)batch_output = []for b in range(bn):batch_output.append(MultiBoxDetection_one(cls_prob[b], loc_pred[b], anchor[0], nms_threshold))return torch.stack(batch_output)anchors = torch.tensor([[0.1, 0.08, 0.52, 0.92], [0.08, 0.2, 0.56, 0.95],[0.15, 0.3, 0.62, 0.91], [0.55, 0.2, 0.9, 0.88]])
offset_preds = torch.tensor([0.0] * (4 * len(anchors)))
cls_probs = torch.tensor([[0., 0., 0., 0.,],  # 背景的预测概率[0.9, 0.8, 0.7, 0.1],  # 狗的预测概率[0.1, 0.2, 0.3, 0.9]])  # 猫的预测概率fig = d2l.plt.imshow(img)
show_bboxes(fig.axes, anchors * bbox_scale,['dog=0.9', 'dog=0.8', 'dog=0.7', 'cat=0.9'])
plt.show()output = MultiBoxDetection(cls_probs.unsqueeze(dim=0), offset_preds.unsqueeze(dim=0),anchors.unsqueeze(dim=0), nms_threshold=0.5)
print(output)fig = d2l.plt.imshow(img)
for i in output[0].detach().cpu().numpy():if i[0] == -1:continuelabel = ('dog=', 'cat=')[int(i[0])] + str(i[1])show_bboxes(fig.axes, [torch.tensor(i[2:]) * bbox_scale], label)
plt.show()print("*"*50)

《动手学深度学习》(PyTorch版)代码注释 - 47 【Image_augmentation】相关推荐

  1. 伯禹公益AI《动手学深度学习PyTorch版》Task 04 学习笔记

    伯禹公益AI<动手学深度学习PyTorch版>Task 04 学习笔记 Task 04:机器翻译及相关技术:注意力机制与Seq2seq模型:Transformer 微信昵称:WarmIce ...

  2. 伯禹公益AI《动手学深度学习PyTorch版》Task 07 学习笔记

    伯禹公益AI<动手学深度学习PyTorch版>Task 07 学习笔记 Task 07:优化算法进阶:word2vec:词嵌入进阶 微信昵称:WarmIce 优化算法进阶 emmmm,讲实 ...

  3. 伯禹公益AI《动手学深度学习PyTorch版》Task 03 学习笔记

    伯禹公益AI<动手学深度学习PyTorch版>Task 03 学习笔记 Task 03:过拟合.欠拟合及其解决方案:梯度消失.梯度爆炸:循环神经网络进阶 微信昵称:WarmIce 过拟合. ...

  4. 【动手学深度学习PyTorch版】6 权重衰退

    上一篇移步[动手学深度学习PyTorch版]5 模型选择 + 过拟合和欠拟合_水w的博客-CSDN博客 目录 一.权重衰退 1.1 权重衰退 weight decay:处理过拟合的最常见方法(L2_p ...

  5. 【动手学深度学习PyTorch版】12 卷积层

    上一篇移步[动手学深度学习PyTorch版]11 使用GPU_水w的博客-CSDN博客 目录 一.卷积层 1.1从全连接到卷积 ◼ 回顾单隐藏层MLP ◼ Waldo在哪里? ◼ 原则1-平移不变性 ...

  6. 【动手学深度学习PyTorch版】27 数据增强

    上一篇请移步[动手学深度学习PyTorch版]23 深度学习硬件CPU 和 GPU_水w的博客-CSDN博客 目录 一.数据增强 1.1 数据增强(主要是关于图像增强) ◼ CES上的真实的故事 ◼ ...

  7. 【动手学深度学习PyTorch版】13 卷积层的填充和步幅

    上一篇移步[动手学深度学习PyTorch版]12 卷积层_水w的博客-CSDN博客 目录 一.卷积层的填充和步幅 1.1 填充 1.2 步幅 1.3 总结 二.代码实现填充和步幅(使用框架) 一.卷积 ...

  8. 【动手学深度学习PyTorch版】23 深度学习硬件CPU 和 GPU

    上一篇请移步[动手学深度学习PyTorch版]22续 ResNet为什么能训练出1000层的模型_水w的博客-CSDN博客 目录 一.深度学习硬件CPU 和 GPU 1.1 深度学习硬件 ◼ 计算机构 ...

  9. 【动手学深度学习PyTorch版】15 池化层

    上一篇请移步[动手学深度学习PyTorch版]14 卷积层里的多输入多输出通道_水w的博客-CSDN博客 目录 一.池化层 1.1 池化层 ◼池化层原因 ◼ 二维最大池化 1.2 填充.步幅与多个通道 ...

  10. 伯禹公益AI《动手学深度学习PyTorch版》Task 05 学习笔记

    伯禹公益AI<动手学深度学习PyTorch版>Task 05 学习笔记 Task 05:卷积神经网络基础:LeNet:卷积神经网络进阶 微信昵称:WarmIce 昨天打了一天的<大革 ...

最新文章

  1. 杂谈:选择游戏还是应用?
  2. eve模拟器华为镜像_EVE-NG简单入门介绍
  3. android camera 3a,买相机送手机 pixel 3a 上手体验
  4. 通向架构师的道路(第十二天)之Axis2 Web Service(三)
  5. 数据湖技术 Iceberg 的探索与实践
  6. 惠普在笔记本新品的2007新的命名规则
  7. SAP Cloud SDK for JavaScript 概述
  8. 人体反应测试仪 c语言,人体反应速度测试仪毕业设计说明
  9. Docker 容器部署 Consul 集群
  10. redis setnx 分布式锁_Redis 分布式锁PHP
  11. 关于Date类型、时间戳格式转换方法
  12. qfiledialog的取消_QFileDialog类(老九学堂C++会员友情翻译,不喜勿喷)
  13. CSharpGL(53)漫反射辐照度
  14. 小雨伞java下载安装教程_TinyUmbrella小雨伞怎么用_TinyUmbrella小雨伞下载及使用教程 - 软件教程 - 格子啦...
  15. flutter 如何获取屏幕的尺寸
  16. Mysql碎片恢复_mysql数据库碎片整理
  17. 微信小程token_微信小程序url与token如何设置
  18. lnmp 一键安装包
  19. 进度猫教你如何做出高效可行的项目计划
  20. jmeter的安装和简单使用

热门文章

  1. 在树莓派CM4+Ubuntu上使用DSI接口显示屏
  2. 战略管理学习---第2章 战略目标
  3. Hack the box: Bastion
  4. 《你的降落伞是什么颜色》
  5. golang模拟抢票
  6. 有赞宣布收费,免费模式在移动电商时代真走不通?
  7. coffeescript java 执行_新的coffeescript,运行cakefile时产生ENOENT错误
  8. 零基础微信小程序开发学习笔记(1.初识目录结构)
  9. linux查看统计数据命令,查看,统计、正则表达式――Linux基本命令(9)
  10. python 列表推导式 else_列表推导式与表达式生成器在 Python 中的滥用!