README.md 9.6 KB
Newer Older
W
wwhu 已提交
1 2 3
图像分类
=======================

4
这里将介绍如何在PaddlePaddle下使用AlexNet、VGG、GoogLeNet、ResNet、Inception-v4、Inception-ResNet-v2和Xception模型进行图像分类。图像分类问题的描述和这些模型的介绍可以参考[PaddlePaddle book](https://github.com/PaddlePaddle/book/tree/develop/03.image_classification)
W
wwhu 已提交
5 6 7 8 9 10 11 12 13

## 训练模型

### 初始化

在初始化阶段需要导入所用的包,并对PaddlePaddle进行初始化。

```python
import gzip
G
guosheng 已提交
14 15
import argparse

W
wwhu 已提交
16
import paddle.v2.dataset.flowers as flowers
W
wwhu 已提交
17 18 19 20 21 22
import paddle.v2 as paddle
import reader
import vgg
import resnet
import alexnet
import googlenet
23
import inception_v4
G
guosheng 已提交
24
import inception_resnet_v2
25
import xception
W
wwhu 已提交
26

W
wwhu 已提交
27 28 29 30 31 32 33 34

# PaddlePaddle init
paddle.init(use_gpu=False, trainer_count=1)
```

### 定义参数和输入

设置算法参数(如数据维度、类别数目和batch size等参数),定义数据输入层`image`和类别标签`lbl`
C
cnn 已提交
35
所用数据集是[flowers](http://www.robots.ox.ac.uk/~vgg/data/flowers/102/)。花的种类是102,因此,CLASS_DIM=102。
W
wwhu 已提交
36 37

```python
G
guosheng 已提交
38
# Use 3 * 331 * 331 or 3 * 299 * 299 for DATA_DIM in Inception-ResNet-v2.
W
wwhu 已提交
39
DATA_DIM = 3 * 224 * 224
W
wwhu 已提交
40
CLASS_DIM = 102
W
wwhu 已提交
41 42 43 44 45 46 47 48 49 50
BATCH_SIZE = 128

image = paddle.layer.data(
    name="image", type=paddle.data_type.dense_vector(DATA_DIM))
lbl = paddle.layer.data(
    name="label", type=paddle.data_type.integer_value(CLASS_DIM))
```

### 获得所用模型

51
这里可以选择使用AlexNet、VGG、GoogLeNet、ResNet、Inception-v4、Inception-ResNet-v2和Xception模型中的一个模型进行图像分类。通过调用相应的方法可以获得网络最后的Softmax层。
W
wwhu 已提交
52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95

1. 使用AlexNet模型

指定输入层`image`和类别数目`CLASS_DIM`后,可以通过下面的代码得到AlexNet的Softmax层。

```python
out = alexnet.alexnet(image, class_dim=CLASS_DIM)
```

2. 使用VGG模型

根据层数的不同,VGG分为VGG13、VGG16和VGG19。使用VGG16模型的代码如下:

```python
out = vgg.vgg16(image, class_dim=CLASS_DIM)
```

类似地,VGG13和VGG19可以分别通过`vgg.vgg13``vgg.vgg19`方法获得。

3. 使用GoogLeNet模型

GoogLeNet在训练阶段使用两个辅助的分类器强化梯度信息并进行额外的正则化。因此`googlenet.googlenet`共返回三个Softmax层,如下面的代码所示:

```python
out, out1, out2 = googlenet.googlenet(image, class_dim=CLASS_DIM)
loss1 = paddle.layer.cross_entropy_cost(
    input=out1, label=lbl, coeff=0.3)
paddle.evaluator.classification_error(input=out1, label=lbl)
loss2 = paddle.layer.cross_entropy_cost(
    input=out2, label=lbl, coeff=0.3)
paddle.evaluator.classification_error(input=out2, label=lbl)
extra_layers = [loss1, loss2]
```

对于两个辅助的输出,这里分别对其计算损失函数并评价错误率,然后将损失作为后文SGD的extra_layers。

4. 使用ResNet模型

ResNet模型可以通过下面的代码获取:

```python
out = resnet.resnet_imagenet(image, class_dim=CLASS_DIM)
```

96 97
5. 使用Inception-v4模型

W
wangmeng28 已提交
98
Inception-v4模型可以通过下面的代码获取, 本例中使用的模型输入大小为`3 * 224 * 224` (原文献中使用的输入大小为`3 * 299 * 299`):
99 100 101 102 103

```python
out = inception_v4.inception_v4(image, class_dim=CLASS_DIM)
```

104 105

6. 使用Inception-ResNet-v2模型
G
guosheng 已提交
106 107 108 109

提供的Inception-ResNet-v2模型支持`3 * 331 * 331``3 * 299 * 299`两种大小的输入,同时可以自行设置dropout概率,可以通过如下的代码使用:

```python
G
guosheng 已提交
110 111
out = inception_resnet_v2.inception_resnet_v2(
    image, class_dim=CLASS_DIM, dropout_rate=0.5, size=DATA_DIM)
G
guosheng 已提交
112 113 114 115
```

注意,由于和其他几种模型输入大小不同,若配合提供的`reader.py`使用Inception-ResNet-v2时请先将`reader.py``paddle.image.simple_transform`中的参数为修改为相应大小。

116
7. 使用Xception模型
W
wangmeng28 已提交
117 118 119 120 121 122 123

Xception模型可以通过下面的代码获取:

```python
out = xception.xception(image, class_dim=CLASS_DIM)
```

W
wwhu 已提交
124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146
### 定义损失函数

```python
cost = paddle.layer.classification_cost(input=out, label=lbl)
```

### 创建参数和优化方法

```python
# Create parameters
parameters = paddle.parameters.create(cost)

# Create optimizer
optimizer = paddle.optimizer.Momentum(
    momentum=0.9,
    regularization=paddle.optimizer.L2Regularization(rate=0.0005 *
                                                     BATCH_SIZE),
    learning_rate=0.001 / BATCH_SIZE,
    learning_rate_decay_a=0.1,
    learning_rate_decay_b=128000 * 35,
    learning_rate_schedule="discexp", )
```

W
wwhu 已提交
147 148 149 150 151
通过 `learning_rate_decay_a` (简写$a$) 、`learning_rate_decay_b` (简写$b$) 和 `learning_rate_schedule` 指定学习率调整策略,这里采用离散指数的方式调节学习率,计算公式如下, $n$ 代表已经处理过的累计总样本数,$lr_{0}$ 即为参数里设置的 `learning_rate`

$$  lr = lr_{0} * a^ {\lfloor \frac{n}{ b}\rfloor} $$


W
wwhu 已提交
152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169
### 定义数据读取

首先以[花卉数据](http://www.robots.ox.ac.uk/~vgg/data/flowers/102/index.html)为例说明如何定义输入。下面的代码定义了花卉数据训练集和验证集的输入:

```python
train_reader = paddle.batch(
    paddle.reader.shuffle(
        flowers.train(),
        buf_size=1000),
    batch_size=BATCH_SIZE)
test_reader = paddle.batch(
    flowers.valid(),
    batch_size=BATCH_SIZE)
```

若需要使用其他数据,则需要先建立图像列表文件。`reader.py`定义了这种文件的读取方式,它从图像列表文件中解析出图像路径和类别标签。

图像列表文件是一个文本文件,其中每一行由一个图像路径和类别标签构成,二者以跳格符(Tab)隔开。类别标签用整数表示,其最小值为0。下面给出一个图像列表文件的片段示例:
W
wwhu 已提交
170

W
wwhu 已提交
171 172 173 174 175 176 177 178 179 180
```
dataset_100/train_images/n03982430_23191.jpeg    1
dataset_100/train_images/n04461696_23653.jpeg    7
dataset_100/train_images/n02441942_3170.jpeg 8
dataset_100/train_images/n03733281_31716.jpeg    2
dataset_100/train_images/n03424325_240.jpeg  0
dataset_100/train_images/n02643566_75.jpeg   8
```

训练时需要分别指定训练集和验证集的图像列表文件。这里假设这两个文件分别为`train.list``val.list`,数据读取方式如下:
W
wwhu 已提交
181 182 183 184

```python
train_reader = paddle.batch(
    paddle.reader.shuffle(
185
        reader.train_reader('train.list'),
W
wwhu 已提交
186 187 188
        buf_size=1000),
    batch_size=BATCH_SIZE)
test_reader = paddle.batch(
189
    reader.test_reader('val.list'),
W
wwhu 已提交
190
    batch_size=BATCH_SIZE)
W
wwhu 已提交
191
```
W
wwhu 已提交
192

W
wwhu 已提交
193 194
### 定义事件处理程序
```python
W
wwhu 已提交
195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210
# End batch and end pass event handler
def event_handler(event):
    if isinstance(event, paddle.event.EndIteration):
        if event.batch_id % 1 == 0:
            print "\nPass %d, Batch %d, Cost %f, %s" % (
                event.pass_id, event.batch_id, event.cost, event.metrics)
    if isinstance(event, paddle.event.EndPass):
        with gzip.open('params_pass_%d.tar.gz' % event.pass_id, 'w') as f:
            parameters.to_tar(f)

        result = trainer.test(reader=test_reader)
        print "\nTest with Pass %d, %s" % (event.pass_id, result.metrics)
```

### 定义训练方法

211
对于AlexNet、VGG、ResNet、Inception-v4、Inception-ResNet-v2和Xception,可以按下面的代码定义训练方法:
W
wwhu 已提交
212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237

```python
# Create trainer
trainer = paddle.trainer.SGD(
    cost=cost,
    parameters=parameters,
    update_equation=optimizer)
```

GoogLeNet有两个额外的输出层,因此需要指定`extra_layers`,如下所示:

```python
# Create trainer
trainer = paddle.trainer.SGD(
    cost=cost,
    parameters=parameters,
    update_equation=optimizer,
    extra_layers=extra_layers)
```

### 开始训练

```python
trainer.train(
    reader=train_reader, num_passes=200, event_handler=event_handler)
```
W
wwhu 已提交
238 239 240 241 242 243 244 245 246 247

## 应用模型
模型训练好后,可以使用下面的代码预测给定图片的类别。

```python
# load parameters
with gzip.open('params_pass_10.tar.gz', 'r') as f:
    parameters = paddle.parameters.Parameters.from_tar(f)

file_list = [line.strip() for line in open(image_list_file)]
248 249 250
test_data = [(paddle.image.load_and_transform(image_file, 256, 224, False)
              .flatten().astype('float32'), )
             for image_file in file_list]
W
wwhu 已提交
251 252 253 254 255 256 257
probs = paddle.infer(
    output_layer=out, parameters=parameters, input=test_data)
lab = np.argsort(-probs)
for file_name, result in zip(file_list, lab):
    print "Label of %s is: %d" % (file_name, result[0])
```

258
首先从文件中加载训练好的模型(代码里以第10轮迭代的结果为例),然后读取`image_list_file`中的图像。`image_list_file`是一个文本文件,每一行为一个图像路径。代码使用`paddle.infer`判断`image_list_file`中每个图像的类别,并进行输出。
259 260

## 使用预训练模型
G
guosheng 已提交
261
为方便进行测试和fine-tuning,我们提供了一些对应于示例中模型配置的预训练模型,目前包括在ImageNet 1000类上训练的ResNet50、ResNet101和Vgg16,请使用`models`目录下的脚本`model_download.sh`进行模型下载,如下载ResNet50可进入`models`目录并执行"`sh model_download.sh ResNet50`",完成后同目录下的`Paddle_ResNet50.tar.gz`即是训练好的模型,可以在代码中使用如下两种方式进行加载模:
262

263 264 265 266 267 268 269 270 271 272
```python
parameters = paddle.parameters.Parameters.from_tar(gzip.open('Paddle_ResNet50.tar.gz', 'r'))
```

```python
parameters = paddle.parameters.create(cost)
parameters.init_from_tar(gzip.open('Paddle_ResNet50.tar.gz', 'r'))
```

### 注意事项
P
peterzhang2029 已提交
273
模型压缩包中所含各文件的文件名和模型配置中的参数名一一对应,是加载模型参数的依据。我们提供的预训练模型均使用了示例代码中的配置,如需修改网络配置,请多加注意,需要保证网络配置中的参数名和压缩包中的文件名能够正确对应。