Skip to content

Commit

Permalink
update nas api and search space doc (PaddlePaddle#16)
Browse files Browse the repository at this point in the history
  • Loading branch information
ceci3 authored and wanghaoshuang committed Jan 8, 2020
1 parent 95edffb commit fad4901
Show file tree
Hide file tree
Showing 4 changed files with 88 additions and 170 deletions.
179 changes: 47 additions & 132 deletions docs/docs/api/nas_api.md
Original file line number Diff line number Diff line change
@@ -1,25 +1,35 @@
# paddleslim.nas API文档
## 搜索空间参数的配置
通过参数配置搜索空间。更多搜索空间的使用可以参考[search_space](../search_space.md)

**参数:**

- **input_size(int|None)**:- `input_size`表示输入feature map的大小。
- **output_size(int|None)**:- `output_size`表示输出feature map的大小。
- **block_num(int|None)**:- `block_num`表示搜索空间中block的数量。
- **block_mask(list|None)**:- `block_mask`是一组由0、1组成的列表,0表示当前block是normal block,1表示当前block是reduction block。如果设置了`block_mask`,则主要以`block_mask`为主要配置,`input_size``output_size``block_num`三种配置是无效的。

## SANAS API文档

## class SANAS
SANAS(Simulated Annealing Neural Architecture Search)是基于模拟退火算法进行模型结构搜索的算法,一般用于离散搜索任务。
Note:<br>
1. reduction block表示经过这个block之后的feature map大小下降为之前的一半,normal block表示经过这个block之后feature map大小不变。<br>
2. `input_size``output_size`用来计算整个模型结构中reduction block数量。

---
## SANAS

>paddleslim.nas.SANAS(configs, server_addr, init_temperature, reduce_rate, search_steps, save_checkpoint, load_checkpoint, is_server)
paddleslim.nas.SANAS(configs, server_addr=("", 8881), init_temperature=100, reduce_rate=0.85, search_steps=300, save_checkpoint='./nas_checkpoint', load_checkpoint=None, is_server=True)[源代码](https://github.com/PaddlePaddle/PaddleSlim/blob/develop/paddleslim/nas/sa_nas.py#L36)
: SANAS(Simulated Annealing Neural Architecture Search)是基于模拟退火算法进行模型结构搜索的算法,一般用于离散搜索任务。

**参数:**
- **configs(list<tuple>):** 搜索空间配置列表,格式是`[(key, {input_size, output_size, block_num, block_mask})]`或者`[(key)]`(MobileNetV2、MobilenetV1和ResNet的搜索空间使用和原本网络结构相同的搜索空间,所以仅需指定`key`即可), `input_size``output_size`表示输入和输出的特征图的大小,`block_num`是指搜索网络中的block数量,`block_mask`是一组由0和1组成的列表,0代表不进行下采样的block,1代表下采样的block。 更多paddleslim提供的搜索空间配置可以参考。
- **server_addr(tuple):** SANAS的地址,包括server的ip地址和端口号,如果ip地址为None或者为""的话则默认使用本机ip。默认:("", 8881)。
- **init_temperature(float):** 基于模拟退火进行搜索的初始温度。默认:100。
- **reduce_rate(float):** 基于模拟退火进行搜索的衰减率。默认:0.85。
- **search_steps(int):** 搜索过程迭代的次数。默认:300。
- **save_checkpoint(str|None):** 保存checkpoint的文件目录,如果设置为None的话则不保存checkpoint。默认:`./nas_checkpoint`
- **load_checkpoint(str|None):** 加载checkpoint的文件目录,如果设置为None的话则不加载checkpoint。默认:None。
- **is_server(bool):** 当前实例是否要启动一个server。默认:True。

**返回:**

- **configs(list<tuple>)** - 搜索空间配置列表,格式是`[(key, {input_size, output_size, block_num, block_mask})]`或者`[(key)]`(MobileNetV2、MobilenetV1和ResNet的搜索空间使用和原本网络结构相同的搜索空间,所以仅需指定`key`即可), `input_size``output_size`表示输入和输出的特征图的大小,`block_num`是指搜索网络中的block数量,`block_mask`是一组由0和1组成的列表,0代表不进行下采样的block,1代表下采样的block。 更多paddleslim提供的搜索空间配置可以参考。
- **server_addr(tuple)** - SANAS的地址,包括server的ip地址和端口号,如果ip地址为None或者为""的话则默认使用本机ip。默认:("", 8881)。
- **init_temperature(float)** - 基于模拟退火进行搜索的初始温度。默认:100。
- **reduce_rate(float)** - 基于模拟退火进行搜索的衰减率。默认:0.85。
- **search_steps(int)** - 搜索过程迭代的次数。默认:300。
- **save_checkpoint(str|None)** - 保存checkpoint的文件目录,如果设置为None的话则不保存checkpoint。默认:`./nas_checkpoint`
- **load_checkpoint(str|None)** - 加载checkpoint的文件目录,如果设置为None的话则不加载checkpoint。默认:None。
- **is_server(bool)** - 当前实例是否要启动一个server。默认:True。

**返回:**
一个SANAS类的实例

**示例代码:**
Expand All @@ -29,16 +39,19 @@ config = [('MobileNetV2Space')]
sanas = SANAS(config=config)
```

---

>tokens2arch(tokens)
通过一组token得到实际的模型结构,一般用来把搜索到最优的token转换为模型结构用来做最后的训练。
paddlesim.nas.SANAS.tokens2arch(tokens)
: 通过一组token得到实际的模型结构,一般用来把搜索到最优的token转换为模型结构用来做最后的训练。

Note:<br>
tokens是一个列表,token映射到搜索空间转换成相应的网络结构,一组token对应唯一的一个网络结构。

**参数:**
- **tokens(list):** 一组token。

**返回**
返回一个模型结构实例。
- **tokens(list):** - 一组token。

**返回:**
根据传入的token得到一个模型结构实例。

**示例代码:**
```
Expand All @@ -49,12 +62,11 @@ for arch in archs:
output = arch(input)
input = output
```
---

>next_archs():
获取下一组模型结构。
paddleslim.nas.SANAS.next_archs()
: 获取下一组模型结构。

**返回**
**返回**
返回模型结构实例的列表,形式为list。

**示例代码:**
Expand All @@ -67,116 +79,19 @@ for arch in archs:
input = output
```

---

>reward(score):
把当前模型结构的得分情况回传。
paddleslim.nas.SANAS.reward(score)
: 把当前模型结构的得分情况回传。

**参数:**
**score<float>:** 当前模型的得分,分数越大越好。

**返回**
模型结构更新成功或者失败,成功则返回`True`,失败则返回`False`
- **score<float>:** - 当前模型的得分,分数越大越好。

**返回:**
模型结构更新成功或者失败,成功则返回`True`,失败则返回`False`

**代码示例**
```python
import numpy as np
import paddle
import paddle.fluid as fluid
from paddleslim.nas import SANAS
from paddleslim.analysis import flops

max_flops = 321208544
batch_size = 256

# 搜索空间配置
config=[('MobileNetV2Space')]

# 实例化SANAS
sa_nas = SANAS(config, server_addr=("", 8887), init_temperature=10.24, reduce_rate=0.85, search_steps=100, is_server=True)

for step in range(100):
archs = sa_nas.next_archs()
train_program = fluid.Program()
test_program = fluid.Program()
startup_program = fluid.Program()
### 构造训练program
with fluid.program_guard(train_program, startup_program):
image = fluid.data(name='image', shape=[None, 3, 32, 32], dtype='float32')
label = fluid.data(name='label', shape=[None, 1], dtype='int64')

for arch in archs:
output = arch(image)
out = fluid.layers.fc(output, size=10, act="softmax")
softmax_out = fluid.layers.softmax(input=out, 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)

### 构造测试program
test_program = train_program.clone(for_test=True)
### 定义优化器
sgd = fluid.optimizer.SGD(learning_rate=1e-3)
sgd.minimize(avg_cost)


### 增加限制条件,如果没有则进行无限制搜索
if flops(train_program) > max_flops:
continue

### 定义代码是在cpu上运行
place = fluid.CPUPlace()
exe = fluid.Executor(place)
exe.run(startup_program)

### 定义训练输入数据
train_reader = paddle.batch(
paddle.reader.shuffle(
paddle.dataset.cifar.train10(cycle=False), buf_size=1024),
batch_size=batch_size,
drop_last=True)

### 定义预测输入数据
test_reader = paddle.batch(
paddle.dataset.cifar.test10(cycle=False),
batch_size=batch_size,
drop_last=False)
train_feeder = fluid.DataFeeder([image, label], place, program=train_program)
test_feeder = fluid.DataFeeder([image, label], place, program=test_program)


### 开始训练,每个搜索结果训练5个epoch
for epoch_id in range(5):
for batch_id, data in enumerate(train_reader()):
fetches = [avg_cost.name]
outs = exe.run(train_program,
feed=train_feeder.feed(data),
fetch_list=fetches)[0]
if batch_id % 10 == 0:
print('TRAIN: steps: {}, epoch: {}, batch: {}, cost: {}'.format(step, epoch_id, batch_id, outs[0]))

### 开始预测,得到最终的测试结果作为score回传给sa_nas
reward = []
for batch_id, data in enumerate(test_reader()):
test_fetches = [
avg_cost.name, acc_top1.name
]
batch_reward = exe.run(test_program,
feed=test_feeder.feed(data),
fetch_list=test_fetches)
reward_avg = np.mean(np.array(batch_reward), axis=1)
reward.append(reward_avg)

print('TEST: step: {}, batch: {}, avg_cost: {}, acc_top1: {}'.
format(step, batch_id, batch_reward[0],batch_reward[1]))

finally_reward = np.mean(np.array(reward), axis=0)
print(
'FINAL TEST: avg_cost: {}, acc_top1: {}'.format(
finally_reward[0], finally_reward[1]))

### 回传score
sa_nas.reward(float(finally_reward[1]))
paddleslim.nas.SANAS.current_info()
: 返回当前token和搜索过程中最好的token和reward。

```
**返回:**
搜索过程中最好的token,reward和当前训练的token,形式为dict。
72 changes: 35 additions & 37 deletions docs/docs/api/search_space.md → docs/docs/search_space.md
Original file line number Diff line number Diff line change
@@ -1,53 +1,50 @@
# paddleslim.nas 提供的搜索空间:
## 搜索空间简介
: 搜索空间是神经网络搜索中的一个概念。搜索空间是一系列模型结构的汇集, SANAS主要是利用模拟退火的思想在搜索空间中搜索到一个比较小的模型结构或者一个精度比较高的模型结构。

1. 根据原本模型结构构造搜索空间:
## paddleslim.nas 提供的搜索空间

1.1 MobileNetV2Space

1.2 MobileNetV1Space

1.3 ResNetSpace
##### 根据初始模型结构构造搜索空间
1. MobileNetV2Space<br>
&emsp; MobileNetV2的网络结构可以参考:[代码](https://github.com/PaddlePaddle/models/blob/develop/PaddleCV/image_classification/models/mobilenet_v2.py#L29)[论文](https://arxiv.org/abs/1801.04381)

2. MobileNetV1Space<br>
&emsp; MobilNetV1的网络结构可以参考:[代码](https://github.com/PaddlePaddle/models/blob/develop/PaddleCV/image_classification/models/mobilenet_v1.py#L29)[论文](https://arxiv.org/abs/1704.04861)

2. 根据相应模型的block构造搜索空间
3. ResNetSpace<br>
&emsp; ResNetSpace的网络结构可以参考:[代码](https://github.com/PaddlePaddle/models/blob/develop/PaddleCV/image_classification/models/resnet.py#L30)[论文](https://arxiv.org/pdf/1512.03385.pdf)

2.1 MobileNetV1BlockSpace

2.2 MobileNetV2BlockSpace

2.3 ResNetBlockSpace

2.4 InceptionABlockSpace

2.5 InceptionCBlockSpace

##### 根据相应模型的block构造搜索空间
1. MobileNetV1BlockSpace<br>
&emsp; MobileNetV1Block的结构可以参考:[代码](https://github.com/PaddlePaddle/models/blob/develop/PaddleCV/image_classification/models/mobilenet_v1.py#L173)

##搜索空间的配置介绍:
2. MobileNetV2BlockSpace<br>
&emsp; MobileNetV2Block的结构可以参考:[代码](https://github.com/PaddlePaddle/models/blob/develop/PaddleCV/image_classification/models/mobilenet_v2.py#L174)

**input_size(int|None)**`input_size`表示输入feature map的大小。
**output_size(int|None)**`output_size`表示输出feature map的大小。
**block_num(int|None)**`block_num`表示搜索空间中block的数量。
**block_mask(list|None)**`block_mask`表示当前的block是一个reduction block还是一个normal block,是一组由0、1组成的列表,0表示当前block是normal block,1表示当前block是reduction block。如果设置了`block_mask`,则主要以`block_mask`为主要配置,`input_size``output_size``block_num`三种配置是无效的。
3. ResNetBlockSpace<br>
&emsp; ResNetBlock的结构可以参考:[代码](https://github.com/PaddlePaddle/models/blob/develop/PaddleCV/image_classification/models/resnet.py#L148)

**Note:**
1. reduction block表示经过这个block之后的feature map大小下降为之前的一半,normal block表示经过这个block之后feature map大小不变。
2. `input_size``output_size`用来计算整个模型结构中reduction block数量。
4. InceptionABlockSpace<br>
&emsp; InceptionABlock的结构可以参考:[代码](https://github.com/PaddlePaddle/models/blob/develop/PaddleCV/image_classification/models/inception_v4.py#L140)

5. InceptionCBlockSpace<br>
&emsp; InceptionCBlock结构可以参考:[代码](https://github.com/PaddlePaddle/models/blob/develop/PaddleCV/image_classification/models/inception_v4.py#L291)

##搜索空间示例:

1. 使用paddleslim中提供用原本的模型结构来构造搜索空间的话,仅需要指定搜索空间名字即可。例如:如果使用原本的MobileNetV2的搜索空间进行搜索的话,传入SANAS中的config直接指定为[('MobileNetV2Space')]
2. 使用paddleslim中提供的block搜索空间构造搜索空间:
2.1 使用`input_size`, `output_size``block_num`来构造搜索空间。例如:传入SANAS的config可以指定为[('MobileNetV2BlockSpace', {'input_size': 224, 'output_size': 32, 'block_num': 10})]
## 搜索空间示例

1. 使用paddleslim中提供用初始的模型结构来构造搜索空间的话,仅需要指定搜索空间名字即可。例如:如果使用原本的MobileNetV2的搜索空间进行搜索的话,传入SANAS中的config直接指定为[('MobileNetV2Space')]
2. 使用paddleslim中提供的block搜索空间构造搜索空间:<br>
2.1 使用`input_size`, `output_size``block_num`来构造搜索空间。例如:传入SANAS的config可以指定为[('MobileNetV2BlockSpace', {'input_size': 224, 'output_size': 32, 'block_num': 10})]。<br>
2.2 使用`block_mask`构造搜索空间。例如:传入SANAS的config可以指定为[('MobileNetV2BlockSpace', {'block_mask': [0, 1, 1, 1, 1, 0, 1, 0]})]


# 自定义搜索空间(search space)
## 自定义搜索空间(search space)

自定义搜索空间类需要继承搜索空间基类并重写以下几部分:
1. 初始化的tokens(`init_tokens`函数),可以设置为自己想要的tokens列表, tokens列表中的每个数字指的是当前数字在相应的搜索列表中的索引。例如本示例中若tokens=[0, 3, 5],则代表当前模型结构搜索到的通道数为[8, 40, 128]
2. token中每个数字的搜索列表长度(`range_table`函数),tokens中每个token的索引范围。
3. 根据token产生模型结构(`token2arch`函数),根据搜索到的tokens列表产生模型结构。
自定义搜索空间类需要继承搜索空间基类并重写以下几部分:<br>
&emsp; 1. 初始化的tokens(`init_tokens`函数),可以设置为自己想要的tokens列表, tokens列表中的每个数字指的是当前数字在相应的搜索列表中的索引。例如本示例中若tokens=[0, 3, 5],则代表当前模型结构搜索到的通道数为[8, 40, 128]<br>
&emsp; 2. token中每个数字的搜索列表长度(`range_table`函数),tokens中每个token的索引范围。<br>
&emsp; 3. 根据token产生模型结构(`token2arch`函数),根据搜索到的tokens列表产生模型结构。 <br>

以新增reset block为例说明如何构造自己的search space。自定义的search space不能和已有的search space同名。

Expand All @@ -70,17 +67,18 @@ class ResNetBlockSpace2(SearchSpaceBase):
def init_tokens(self):
return [0] * 3 * len(self.block_mask)

### 定义
### 定义token的index的取值范围
def range_table(self):
return [len(self.filter_num)] * 3 * len(self.block_mask)

### 把token转换成模型结构
def token2arch(self, tokens=None):
if tokens == None:
tokens = self.init_tokens()

self.bottleneck_params_list = []
for i in range(len(self.block_mask)):
self.bottleneck_params_list.append(self.filter_num[tokens[i * 3 + 0]],
self.bottleneck_params_list.append(self.filter_num[tokens[i * 3 + 0]],
self.filter_num[tokens[i * 3 + 1]],
self.filter_num[tokens[i * 3 + 2]],
2 if self.block_mask[i] == 1 else 1)
Expand Down Expand Up @@ -113,4 +111,4 @@ class ResNetBlockSpace2(SearchSpaceBase):
conv = fluid.layers.conv2d(input, num_filters, filter_size, stride, name=name+'_conv')
bn = fluid.layers.batch_norm(conv, act=act, name=name+'_bn')
return bn
```
```
2 changes: 1 addition & 1 deletion docs/mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ nav:
- 模型分析: api/analysis_api.md
- 知识蒸馏: api/single_distiller_api.md
- SA搜索: api/nas_api.md
- 搜索空间: api/search_space.md
- 搜索空间: search_space.md
- 硬件延时评估表: table_latency.md
- 算法原理: algo/algo.md

Expand Down
5 changes: 5 additions & 0 deletions paddleslim/nas/sa_nas.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,11 @@ def _get_host_ip(self):
return socket.gethostbyname(socket.gethostname())

def tokens2arch(self, tokens):
"""
Convert tokens to network architectures.
Returns:
list<function>: A list of functions that define networks.
"""
return self._search_space.token2arch(tokens)

def current_info(self):
Expand Down

0 comments on commit fad4901

Please sign in to comment.