《动手学深度学习》(PyTorch版)代码注释 - 47 【Image_augmentation】
目录
- 说明
- 配置环境
- 此节说明
- 代码
说明
本博客代码来自开源项目:《动手学深度学习》(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】相关推荐
- 伯禹公益AI《动手学深度学习PyTorch版》Task 04 学习笔记
伯禹公益AI<动手学深度学习PyTorch版>Task 04 学习笔记 Task 04:机器翻译及相关技术:注意力机制与Seq2seq模型:Transformer 微信昵称:WarmIce ...
- 伯禹公益AI《动手学深度学习PyTorch版》Task 07 学习笔记
伯禹公益AI<动手学深度学习PyTorch版>Task 07 学习笔记 Task 07:优化算法进阶:word2vec:词嵌入进阶 微信昵称:WarmIce 优化算法进阶 emmmm,讲实 ...
- 伯禹公益AI《动手学深度学习PyTorch版》Task 03 学习笔记
伯禹公益AI<动手学深度学习PyTorch版>Task 03 学习笔记 Task 03:过拟合.欠拟合及其解决方案:梯度消失.梯度爆炸:循环神经网络进阶 微信昵称:WarmIce 过拟合. ...
- 【动手学深度学习PyTorch版】6 权重衰退
上一篇移步[动手学深度学习PyTorch版]5 模型选择 + 过拟合和欠拟合_水w的博客-CSDN博客 目录 一.权重衰退 1.1 权重衰退 weight decay:处理过拟合的最常见方法(L2_p ...
- 【动手学深度学习PyTorch版】12 卷积层
上一篇移步[动手学深度学习PyTorch版]11 使用GPU_水w的博客-CSDN博客 目录 一.卷积层 1.1从全连接到卷积 ◼ 回顾单隐藏层MLP ◼ Waldo在哪里? ◼ 原则1-平移不变性 ...
- 【动手学深度学习PyTorch版】27 数据增强
上一篇请移步[动手学深度学习PyTorch版]23 深度学习硬件CPU 和 GPU_水w的博客-CSDN博客 目录 一.数据增强 1.1 数据增强(主要是关于图像增强) ◼ CES上的真实的故事 ◼ ...
- 【动手学深度学习PyTorch版】13 卷积层的填充和步幅
上一篇移步[动手学深度学习PyTorch版]12 卷积层_水w的博客-CSDN博客 目录 一.卷积层的填充和步幅 1.1 填充 1.2 步幅 1.3 总结 二.代码实现填充和步幅(使用框架) 一.卷积 ...
- 【动手学深度学习PyTorch版】23 深度学习硬件CPU 和 GPU
上一篇请移步[动手学深度学习PyTorch版]22续 ResNet为什么能训练出1000层的模型_水w的博客-CSDN博客 目录 一.深度学习硬件CPU 和 GPU 1.1 深度学习硬件 ◼ 计算机构 ...
- 【动手学深度学习PyTorch版】15 池化层
上一篇请移步[动手学深度学习PyTorch版]14 卷积层里的多输入多输出通道_水w的博客-CSDN博客 目录 一.池化层 1.1 池化层 ◼池化层原因 ◼ 二维最大池化 1.2 填充.步幅与多个通道 ...
- 伯禹公益AI《动手学深度学习PyTorch版》Task 05 学习笔记
伯禹公益AI<动手学深度学习PyTorch版>Task 05 学习笔记 Task 05:卷积神经网络基础:LeNet:卷积神经网络进阶 微信昵称:WarmIce 昨天打了一天的<大革 ...
最新文章
- 杂谈:选择游戏还是应用?
- eve模拟器华为镜像_EVE-NG简单入门介绍
- android camera 3a,买相机送手机 pixel 3a 上手体验
- 通向架构师的道路(第十二天)之Axis2 Web Service(三)
- 数据湖技术 Iceberg 的探索与实践
- 惠普在笔记本新品的2007新的命名规则
- SAP Cloud SDK for JavaScript 概述
- 人体反应测试仪 c语言,人体反应速度测试仪毕业设计说明
- Docker 容器部署 Consul 集群
- redis setnx 分布式锁_Redis 分布式锁PHP
- 关于Date类型、时间戳格式转换方法
- qfiledialog的取消_QFileDialog类(老九学堂C++会员友情翻译,不喜勿喷)
- CSharpGL(53)漫反射辐照度
- 小雨伞java下载安装教程_TinyUmbrella小雨伞怎么用_TinyUmbrella小雨伞下载及使用教程 - 软件教程 - 格子啦...
- flutter 如何获取屏幕的尺寸
- Mysql碎片恢复_mysql数据库碎片整理
- 微信小程token_微信小程序url与token如何设置
- lnmp 一键安装包
- 进度猫教你如何做出高效可行的项目计划
- jmeter的安装和简单使用
热门文章
- 在树莓派CM4+Ubuntu上使用DSI接口显示屏
- 战略管理学习---第2章 战略目标
- Hack the box: Bastion
- 《你的降落伞是什么颜色》
- golang模拟抢票
- 有赞宣布收费,免费模式在移动电商时代真走不通?
- coffeescript java 执行_新的coffeescript,运行cakefile时产生ENOENT错误
- 零基础微信小程序开发学习笔记(1.初识目录结构)
- linux查看统计数据命令,查看,统计、正则表达式――Linux基本命令(9)
- python 列表推导式 else_列表推导式与表达式生成器在 Python 中的滥用!