# 图像分类网络结构搜索-快速开始

该教程以图像分类模型MobileNetV2为例，说明如何在CIFAR10数据集上快速使用[网络结构搜索接口](../api/nas_api.md)基于模拟退火（Simulated Annealing，简称SA）的方式进行网络结构搜索。

模拟退火算法来源于固体退火原理，将固体加温至充分高，再让其徐徐冷却，加温时，固体内部粒子随温升变为无序状，内能增大，而徐徐冷却时粒子渐趋有序，在每个温度都达到平衡态，最后在常温时达到基态，内能减为最小。详细算法可以参考[模拟退火](https://github.com/PaddlePaddle/PaddleSlim/blob/develop/docs/zh_cn/algo/algo.md#411-%E6%A8%A1%E6%8B%9F%E9%80%80%E7%81%AB)

Tips: 运行该示例前请确认已正确安装Paddle和PaddleSlim。

该示例包含以下步骤：

1. 加载示例数据
2. 构建模型
3. 开始训练当前网络结构
4. 开始评估当前网络结构
5. 回传当前网络结构的得分


以下章节依次介绍每个步骤的内容。

# 1. 加载示例数据
使用的示例数据集为CIFAR10，CIFAR10数据集共有60000张彩色图片，图像大小是32\*32，分为10个类，每类6000张图片。训练集包含50000张图片，共有10个类别，每类从6000张>图片中随机选取5000张图片，剩下的图片组成测试集，测试集包含10000张图片。paddle框架中`paddle.dataset.cifar`包括了cifar数据集的下载和读取，代码如下：
```python
import paddle
import paddle.fluid as fluid
train_reader = paddle.batch(paddle.reader.shuffle(paddle.dataset.cifar.train10(cycle=False), buf_size=1024),batch_size=256)
train_feeder = fluid.DataFeeder(inputs, fluid.CPUPlace())
eval_reader = paddle.batch(paddle.dataset.cifar.test10(cycle=False), batch_size=256)
eval_feeder = fluid.DataFeeder(inputs, fluid.CPUPlace())
```
其他数据集读取请参考[自定义数据集]()


# 2. 构建模型
本示例是基于MobileNetV2搜索空间进行网络结构搜索的，MobileNetV2是一个基于inverted residual的轻量级网络，详细信息可以查看[MobileNetV2: Inverted Residuals and Linear Bottlenecks](https://arxiv.org/abs/1801.04381)
```python
### 初始化SANAS实例，指定搜索空间为MobileNetV2。
sanas = slim.nas.SANAS(configs=[('MobileNetV2Space')], server_addr=("", 8337), save_checkpoint=None)
### 获取当前搜索到的模型结构
archs = sanas.next_archs()[0]
### 根据模型结构构造训练program和测试program
train_program = fluid.Program()
startup_program = fluid.Program()
with fluid.program_guard(train_program, startup_program):
    data = fluid.data(name='data', shape=[None, 3, 32, 32], dtype='float32')
    label = fluid.data(name='label', shape=[None, 1], dtype='int64')
    output = archs(data)
    output = fluid.layers.fc(input=output, size=10)

    ### 定义优化器和损失函数
    softmax_out = fluid.layers.softmax(input=output, use_cudnn=False)
    cost = fluid.layers.cross_entropy(input=softmax_out, label=label)
    avg_cost = fluid.layers.mean(cost)
    acc_top1 = fluid.layers.accuracy(input=softmax_out, label=label, k=1)
    acc_top5 = fluid.layers.accuracy(input=softmax_out, label=label, k=5)
    ### 克隆训练program得到测试program，这一步必须放在定义优化器之前
    eval_program = fluid.default_main_program().clone(for_test=True)

    optimizer = fluid.optimizer.Adam(learning_rate=0.1)
    optimizer.minimize(avg_cost)

    place = fluid.CPUPlace()
    exe = fluid.Executor(place)
    exe.run(startup_program)
```

# 3. 开始训练当前网络结构
根据上面一步拿到的模型结构构造的训练program开始训练。
```python
outputs = [avg_cost.name, acc_top1.name, acc_top5.name]
for data in train_reader():
    batch_reward = exe.run(train_program, feed=train_feeder.feed(data), fetch_list = outputs)
    print("TRAIN: loss: {}, acc1: {}, acc5:{}".format(batch_reward[0], batch_reward[1], batch_reward[2]))
```

# 4. 开始评估当前网络结构
评估第3步中训练的模型结构。
```python
reward = []
for data in eval_reader():
    batch_reward = exe.run(eval_program, feed=eval_feeder.feed(data), fetch_list = outputs)
    reward_avg = np.mean(np.array(batch_reward), axis=1)
    reward.append(reward_avg)
    print("TEST: loss: {}, acc1: {}, acc5:{}".format(batch_reward[0], batch_reward[1], batch_reward[2]))
finally_reward = np.mean(np.array(reward), axis=0)
print("FINAL TEST: avg_cost: {}, acc1: {}, acc5: {}".format(finally_reward[0], finally_reward[1], finally_reward[2]))
```

# 5. 回传当前网络结构的得分
在当前模型结构训练和评估完成后回传最终的评估结果，SANAS会根据这个结果更新下一轮的模型结构。
```python
sanas.reward(float(finally_reward[1]))
```
