动手学深度学习(八):计算机视觉
[个人博客]# (一) 图像增广
对图像进行随机变化(翻转和裁剪)能够扩大训练集的规模也能减少模型对某些属性的依赖,提高泛化能力.
`torchvision.transforms`模块提供了翻转和裁剪的方法
- `RandomHorizontalFlip()`50%几率使得图像向左或向右翻转
- `RandomVerticalFlip()`50%几率使得图像向上或向下翻转
- `RandomResizedCrop((200, 200), scale=(0.1, 1), ratio=(0.5, 2))` 随机裁剪原始面积0.1到1的区域,宽高比在0.5-2,宽度和高度缩放到200像素
- `ColorJitter(brightness=0.5, contrast=0, saturation=0, hue=0)` 调整亮度、对比度、饱和度和色调. 0.5就是0-0.5
- `Compose()` 提供了将多个增广方法数组合并为一个方法的方法
# (二) 微调
有些模型需要大量的数据集进行训练(比如Fashion-MNIST图像数据集),如果实例数据不够,那么很可能过拟合.解决办法除了收集更多的数据,还可以"迁移学习"将源数据集的知识迁移到目标数据集.
步骤:
- 在源数据集上预训练神经网络模型(源模型)
- 创建一个新的神经网络模型(目标模型),复制源模型上的所有模型设计及其参数(输出层除外)
- 向目标模型添加输出层,其输出数是目标数据集中的类别数.然后随机初始化该层的模型参数.
- 训练目标模型.输出层从头训练但是其他层都是只是微调.
# (三) 目标检测和边界框
目标检测就是图像中有多个目标我们既想知道他们的类别又想知道他们在图像中的具体位置.而边界框就是确定目标位置的一种表示方法.
边界框顾名思义就是一个矩形,可以由左上角和右下角两点确定,也可以由中心点和宽高决定.如果要将边界框在图中画出来,可以将边界框表示为`matplotlib`的边界框格式然后再`axes.add_patch()`即可画出.
# (四) 锚框
目标检测算法会在输入图像中采样大量区域然后判断.以每个像素为中心生成多个缩放比和宽高比不同的边界框,这些框就是锚框.
在实践中,我们用的是缩放比$s$和宽高比$r$.选择一些缩放比$s_1,...,s_n$和宽高比$r_1,...,s_m$,由于复杂度我们可以只考虑这些锚框(共n+m-1个):
$$
(s_1,r_1),(s_1,r_2),...,(s_1,r_2),(s_1,r_m),(s_2,r_1),...,(s_n,r_1)
$$
生成锚框坐标张量的实例代码:
```python
#@save
def multibox_prior(data, sizes, ratios): # data是输入数据,sizes是锚框的大小列表,ratios是锚框的宽高比列表。
"""生成以每个像素为中心具有不同形状的锚框"""
in_height, in_width = data.shape[-2:]
device, num_sizes, num_ratios = data.device, len(sizes), len(ratios)
boxes_per_pixel = (num_sizes + num_ratios - 1)
size_tensor = torch.tensor(sizes, device=device)
ratio_tensor = torch.tensor(ratios, device=device)
# 为了将锚点移动到像素的中心,需要设置偏移量。
# 因为一个像素的高为1且宽为1,我们选择偏移我们的中心0.5
offset_h, offset_w = 0.5, 0.5
steps_h = 1.0 / in_height# 在y轴上缩放步长
steps_w = 1.0 / in_width# 在x轴上缩放步长
# 生成锚框的所有中心点
center_h = (torch.arange(in_height, device=device) + offset_h) * steps_h
center_w = (torch.arange(in_width, device=device) + offset_w) * steps_w
shift_y, shift_x = torch.meshgrid(center_h, center_w, indexing='ij')
shift_y, shift_x = shift_y.reshape(-1), shift_x.reshape(-1)
# 生成“boxes_per_pixel”个高和宽,
# 之后用于创建锚框的四角坐标(xmin,xmax,ymin,ymax)
w = torch.cat((size_tensor * torch.sqrt(ratio_tensor),
sizes * torch.sqrt(ratio_tensor)))\
* in_height / in_width# 处理矩形输入
h = torch.cat((size_tensor / torch.sqrt(ratio_tensor),
sizes / torch.sqrt(ratio_tensor)))
# 除以2来获得半高和半宽
anchor_manipulations = torch.stack((-w, -h, w, h)).T.repeat(
in_height * in_width, 1) / 2
# 每个中心点都将有“boxes_per_pixel”个锚框,
# 所以生成含所有锚框中心的网格,重复了“boxes_per_pixel”次
out_grid = torch.stack(,
dim=1).repeat_interleave(boxes_per_pixel, dim=0)
output = out_grid + anchor_manipulations
return output.unsqueeze(0)
```
只是做一个参考,我们完全可以自己实现.
**交并比(intersection over union,loU)**
如果需要描述锚框和真实边界框之间的相似性,可以用杰卡德系数表示(两个边界框的杰卡德系数就叫**交并比**),一张图就很显然了:
![交并比](https://zh-v2.d2l.ai/_images/iou.svg)
**锚框与真实边界框匹配**
原文书上很长一段话,其实算法很简单,列一个交并比矩阵(其实就是列表):
| | $B_1$ | $B_2$ | ... | $B_{n_b}$ |
| --------- | ---------- | ---------- | --- | ------------ |
| $A_1$ | $J_{11}$ | $J_{12}$ | | $J_{1n_b}$ |
| $A_2$ | $J_{21}$ | $J_{22}$ | | $J_{2n_b}$ |
| ... | | | | |
| $A_{n_a}$ | $J_{n_a1}$ | $J_{n_a2}$ | | $J_{n_an_b}$ |
我们要做的就是对于每一个锚框分配一个最接近的真实边界框,那么显然我们可以先找这个矩阵的所有元素中大的那个元素(也就是匹配的最好的),然后找到之后就把这一列和这一行划掉(代表这个A已经和这个B匹配了),当所有B都被分配完了还剩下一些A没有分配怎么办呢?那就只能将剩下的这些A(也就是这些行中)找到最佳匹配的B,而且需要大于预定义的阈值的时候才把B分配给这些剩下的A.
> 不过我有一个疑问:为什么不直接每个A(也就是每一行)直接找最匹配的B?是为了尽可能避免重复吗?
**偏移量**
当一个锚框分配一个真实边界框的时候也就是这个锚框类别被标记和这个真实边界框的类别相同了.如果锚框没有被分配真实边界框(因为可能低于预定义的阈值),将其标记为background(背景、负类锚框)
中心坐标分别为$(x_a,y_a)(x_b,y_b)$两个框的偏移(相差)可以用以下式子表示:
$$
\left(\frac{\frac{x_b-x_a}{w_a}-\mu_x}{\sigma_x},\frac{\frac{y_b-y_a}{h_a}-\mu_y}{\sigma_y},\frac{\log\frac{w_b}{w_a}-\mu_w}{\sigma_w},\frac{\log\frac{h_b}{h_a}-\mu_h}{\sigma_h}\right)
$$
常量的默认值为$\mu_x=\mu_y=\mu_w=\mu_h=0,\sigma_x=\sigma_y=0.1 , \sigma_w=\sigma_h=0.2$.负类锚框的偏移量被标记为0.
> 偏移量的四个维度有什么含义?
**预测**
将锚框和预测偏移量作为输入,用逆偏移变换返回预测的边界框坐标.当有许多锚框时,可能会输出许多相似的具有明显重叠的预测边界框,都围绕着同一目标.为了简化输出,我们可以使用非极大值抑制(non-maximum suppression,NMS)合并属于同一目标的类似的预测边界框.非极大值抑制就是不断将置信度最高的预测边界框$B_1$作为基准,然后将所有与$B_1$的IoU超过预定阈值ϵ的非基准预测边界框移除.
代码暂时没有贴... <p>目标检测就是图像中有多个目标我们既想知道他们的类别又想知道他们在图像中的具体位置.而边界框就是确定目标位置的一种表示方法.这个是如何实现?</p>
页:
[1]