image-augmentation.md 6.8 KB
Newer Older
A
Aston Zhang 已提交
1
# 图片增广
M
Mu Li 已提交
2

M
muli 已提交
3 4 5
[“深度卷积神经网络:AlexNet”](../chapter_convolutional-neural-networks/alexnet.md)小节里我们提到过大规模数据集是深度网络能成功的前提条件。在AlexNet当年能取得的成功中,图片增广(image augmentation)功不可没。本小节我们将讨论这个在计算机视觉里被广泛使用的技术。

图片增广是指通过对训练图片做一系列变化来产生相似但又有不同的训练样本,这样来模型训练的时候识别了难以泛化的模式。例如我们可以对图片进行不同的裁剪使得感兴趣的物体出现在不同的位置中,从而使得模型减小对物体出现位置的依赖性。也可以调整亮度色彩等因素来降低模型对色彩的敏感度。
M
Mu Li 已提交
6

A
Aston Zhang 已提交
7
## 常用增广方法
M
Mu Li 已提交
8

M
muli 已提交
9
我们首先读取一张$400\times 500$的图片作为样例解释常用的增广方法。
M
Mu Li 已提交
10 11 12

```{.python .input  n=1}
%matplotlib inline
M
muli 已提交
13 14 15 16 17
import sys
sys.path.insert(0, '..')
import gluonbook as gb
from mxnet import nd, image, gluon, init
from mxnet.gluon.data.vision import transforms
M
Mu Li 已提交
18

M
muli 已提交
19 20
img = image.imread('../img/cat1.jpg')
gb.plt.imshow(img.asnumpy())
M
Mu Li 已提交
21 22
```

M
muli 已提交
23
因为大部分的增广方法都有一定的随机性。接下来我们定义一个辅助函数,它对输入图片`img`运行多次增广方法`aug`并画出结果。
M
Mu Li 已提交
24

M
muli 已提交
25
```{.python .input  n=2}
M
muli 已提交
26 27 28 29 30 31 32 33 34 35 36 37
def plot(imgs, num_rows, num_cols, ratio):
    _, figs = gb.plt.subplots(
        num_rows, num_cols, figsize=(num_cols*ratio, num_rows*ratio))
    for i in range(num_rows):
        for j in range(num_cols):
            figs[i][j].imshow(imgs[i*num_cols+j].asnumpy())
            figs[i][j].axes.get_xaxis().set_visible(False)
            figs[i][j].axes.get_yaxis().set_visible(False)
            
def apply(img, aug, num_rows=2, num_cols=4, ratio=2):
    Y = [aug(img) for _ in range(num_rows*num_cols)]
    plot(Y, num_rows, num_cols, ratio)
M
Mu Li 已提交
38 39 40 41
```

### 变形

M
muli 已提交
42
左右翻转图片通常不影响识别图片,它是最早也是最广泛使用的一种增广。下面我们使用transform模块里的`RandomFlipLeftRight`类来实现按0.5的概率左右翻转图片:
M
Mu Li 已提交
43 44

```{.python .input  n=3}
M
muli 已提交
45
apply(img, transforms.RandomFlipLeftRight())
M
Mu Li 已提交
46 47
```

M
muli 已提交
48
当然有时候我们也使用上下翻转,至少对于我们使用的图片,上下翻转不会造成人的识别障碍。
M
Mu Li 已提交
49 50

```{.python .input  n=4}
M
muli 已提交
51
apply(img, transforms.RandomFlipTopBottom())
M
Mu Li 已提交
52 53
```

M
muli 已提交
54 55 56
我们使用的样例图片里猫在图片正中间,但一般情况下可能不是这样。[“池化层”](../chapter_convolutional-neural-networks/pooling.md)一节里我们解释了池化层能弱化卷积层对目标位置的敏感度,另一方面我们可以通过对图片随机剪裁来是的物体以不同的比例出现在不同位置。

下面代码里我们每次随机裁剪一片面积为原面积10%到100%的区域,其宽和高的比例在0.5和2之间,然后再将高宽缩放到200像素。
M
Mu Li 已提交
57 58

```{.python .input  n=5}
M
muli 已提交
59 60 61
shape_aug = transforms.RandomResizedCrop(
    (200, 200), scale=(.1, 1), ratio=(.5, 2))
apply(img, shape_aug)
M
Mu Li 已提交
62 63 64 65
```

### 颜色变化

M
muli 已提交
66
形状变化外的一个另一大类是变化颜色。颜色一般有四个可以调的参数:亮度、对比、饱和度和色相。下面例子里我们随机将亮度在当前值上增加或减小一个在0到50%之前的量。
M
Mu Li 已提交
67 68

```{.python .input  n=6}
M
muli 已提交
69
apply(img, transforms.RandomLighting(.5))
M
Mu Li 已提交
70 71
```

M
muli 已提交
72 73
同样的修改色相。

M
Mu Li 已提交
74
```{.python .input  n=7}
M
muli 已提交
75
apply(img, transforms.RandomHue(.5))
M
Mu Li 已提交
76 77
```

M
muli 已提交
78
或者用使用`RandomColorJitter`来一起使用。
M
Mu Li 已提交
79

M
muli 已提交
80
```{.python .input  n=8}
M
muli 已提交
81 82 83
color_aug = transforms.RandomColorJitter(
    brightness=.5, contrast=.5, saturation=.5, hue=.5)
apply(img, color_aug)
M
Mu Li 已提交
84 85
```

M
muli 已提交
86 87 88
### 使用多个增广

实际应用中我们会将多个增广叠加使用。我们可以使用Compose类来将多个增广串联起来。
M
Mu Li 已提交
89

M
muli 已提交
90
```{.python .input  n=9}
M
muli 已提交
91 92 93
augs = transforms.Compose([
    transforms.RandomFlipLeftRight(), color_aug, shape_aug])
apply(img, augs)
M
Mu Li 已提交
94 95
```

M
muli 已提交
96 97 98
## 使用图片增广来训练

接下来我们来看一个将图片增广应用在实际训练的例子,并比较其与不使用时的区别。这里我们使用CIFAR-10数据集,而不是之前我们一直使用的FashionMNIST。原因在于FashionMNIST中物体位置和尺寸都已经统一化了,而CIFAR-10中物体颜色和大小区别更加显著。下面我们展示CIFAR-10中的前32张训练图片。
M
Mu Li 已提交
99

M
muli 已提交
100
```{.python .input  n=10}
M
muli 已提交
101
plot(gluon.data.vision.CIFAR10(train=True)[0:32][0], 4, 8, 1)
M
Mu Li 已提交
102 103
```

M
muli 已提交
104
在训练时,我们通常将图片增广作用在训练图片上,使得模型能识别出各种变化过后的版本。这里我们仅仅使用最简单的随机水平翻转。此外我们使用`ToTensor`变换来图片转成MXNet需要的格式,即格式为(批量,通道,高,宽)以及类型为32位浮点数。
M
Mu Li 已提交
105

M
muli 已提交
106
```{.python .input  n=11}
M
muli 已提交
107 108 109 110 111 112 113 114
train_augs = transforms.Compose([
    transforms.RandomFlipLeftRight(),
    transforms.ToTensor(),
])

test_augs = transforms.Compose([
    transforms.ToTensor(),
])
M
Mu Li 已提交
115 116
```

M
muli 已提交
117 118
接下来我们定义一个辅助函数来方便读取图片并应用增广。Gluon的数据集提供`transform_first`函数来对数据里面的第一项图片(标签为第二项)来应用增广。另外图片增广将增加计算复杂度,我们使用两个额外CPU进程加来加速计算。

M
muli 已提交
119
```{.python .input  n=12}
M
muli 已提交
120 121 122 123
def load_cifar10(is_train, augs, batch_size):
    return gluon.data.DataLoader(gluon.data.vision.CIFAR10(
        train=is_train).transform_first(augs),
        batch_size=batch_size, shuffle=is_train, num_workers=2)
M
muli 已提交
124 125
```

M
muli 已提交
126
### 模型训练
M
Mu Li 已提交
127

M
muli 已提交
128
我们使用ResNet 18来训练CIFAR-10。训练的的代码跟[“残差网络:ResNet”](..//chapter_convolutional-neural-networks/resnet.md)一致,除了使用所有可用的GPU和不同的学习率外。
M
Mu Li 已提交
129

M
muli 已提交
130 131 132
```{.python .input  n=14}
def train(train_augs, test_augs, lr=.1):
    batch_size = 256
A
Aston Zhang 已提交
133
    ctx = gb.try_all_gpus()
M
muli 已提交
134
    net = gb.resnet18(10)
M
Mu Li 已提交
135
    net.initialize(ctx=ctx, init=init.Xavier())
M
muli 已提交
136 137 138 139 140
    trainer = gluon.Trainer(net.collect_params(), 'sgd', {'learning_rate':lr})
    loss = gluon.loss.SoftmaxCrossEntropyLoss()
    train_data = load_cifar10(True, train_augs, batch_size)
    test_data = load_cifar10(False, test_augs, batch_size)
    gb.train(train_data, test_data, net, loss, trainer, ctx, num_epochs=8)
M
Mu Li 已提交
141 142
```

M
muli 已提交
143
首先我们看使用了图片增广的情况。
M
Mu Li 已提交
144

M
muli 已提交
145
```{.python .input  n=15}
M
Mu Li 已提交
146 147 148
train(train_augs, test_augs)
```

M
muli 已提交
149
作为对比,我们只对训练数据做中间剪裁。
M
Mu Li 已提交
150

M
muli 已提交
151
```{.python .input  n=16}
M
Mu Li 已提交
152 153 154
train(test_augs, test_augs)
```

M
muli 已提交
155
可以看到,即使是简单的随机翻转也会有明显效果。使用增广类似于增加了正则项话,它使得训练精度变低,但对提升测试精度有帮助。
M
Mu Li 已提交
156

A
Aston Zhang 已提交
157
## 小结
M
Mu Li 已提交
158

M
muli 已提交
159
图片增广对现有训练数据生成大量随机图片来有效避免过拟合。
M
Mu Li 已提交
160 161 162

## 练习

M
muli 已提交
163
尝试在CIFAR-10训练中增加不同的增广方法。
S
Sheng Zha 已提交
164

A
Aston Zhang 已提交
165
## 扫码直达[讨论区](https://discuss.gluon.ai/t/topic/1666)
S
Sheng Zha 已提交
166

A
Aston Zhang 已提交
167
![](../img/qr_image-augmentation.svg)