Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
MindSpore
docs
提交
844a3fce
D
docs
项目概览
MindSpore
/
docs
通知
4
Star
2
Fork
2
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
D
docs
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
提交
Issue看板
提交
844a3fce
编写于
8月 19, 2020
作者:
W
wangmin
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
second order optimizer for resnet50 application
上级
2996be7d
变更
2
隐藏空白更改
内联
并排
Showing
2 changed file
with
449 addition
and
0 deletion
+449
-0
tutorials/source_zh_cn/advanced_use/second_order_optimizer_for_resnet50_application.md
...ed_use/second_order_optimizer_for_resnet50_application.md
+448
-0
tutorials/source_zh_cn/index.rst
tutorials/source_zh_cn/index.rst
+1
-0
未找到文件。
tutorials/source_zh_cn/advanced_use/second_order_optimizer_for_resnet50_application.md
0 → 100644
浏览文件 @
844a3fce
# ResNet-50二阶优化实践
<!-- TOC -->
-
[
ResNet-50二阶优化实践
](
#resnet-50二阶优化实践
)
-
[
概述
](
#概述
)
-
[
准备环节
](
#准备环节
)
-
[
准备数据集
](
#准备数据集
)
-
[
配置分布式环境变量
](
#配置分布式环境变量
)
-
[
加载处理数据集
](
#加载处理数据集
)
-
[
定义网络
](
#定义网络
)
-
[
定义损失函数及THOR优化器
](
#定义损失函数及thor优化器
)
-
[
定义损失函数
](
#定义损失函数
)
-
[
定义优化器
](
#定义优化器
)
-
[
训练网络
](
#训练网络
)
-
[
配置模型保存
](
#配置模型保存
)
-
[
配置训练网络
](
#配置训练网络
)
-
[
运行脚本
](
#运行脚本
)
-
[
模型推理
](
#模型推理
)
<!-- /TOC -->
<a
href=
"https://gitee.com/mindspore/docs/blob/master/tutorials/source_zh_cn/advanced_use/second_order_optimizer_for_resnet50_application.md"
target=
"_blank"
><img
src=
"../_static/logo_source.png"
></a>
## 概述
常见的优化算法可分为一阶优化算法和二阶优化算法。经典的一阶优化算法如SGD等,计算量小、计算速度快,但是收敛的速度慢,所需的迭代次数多。而二阶优化算法使用目标函数的二阶导数来加速收敛,能更快地收敛到模型最优值,所需要的迭代次数少,但由于二阶优化算法过高的计算成本,导致其总体执行时间仍然慢于一阶,故目前在深度神经网络训练中二阶优化算法的应用并不普遍。二阶优化算法的主要计算成本在于二阶信息矩阵(Hessian矩阵、
[
FIM矩阵
](
https://arxiv.org/pdf/1808.07172.pdf
)
等)的求逆运算,时间复杂度约为$O(n^3)$。
MindSpore开发团队在现有的自然梯度算法的基础上,对FIM矩阵采用近似、切分等优化加速手段,极大的降低了逆矩阵的计算复杂度,开发出了可用的二阶优化器THOR。使用8块Ascend 910 AI处理器,THOR可以在72min内完成ResNet50-v1.5网络和ImageNet数据集的训练,相比于SGD+Momentum速度提升了近一倍。
本篇教程将主要介绍如何在Ascend 910 以及GPU上,使用MindSpore提供的二阶优化器THOR训练ResNet50-v1.5网络和ImageNet数据集。
> 你可以在这里下载完整的示例代码:
<https://gitee.com/mindspore/mindspore/tree/master/model_zoo/official/cv/resnet_thor>
。
### 示例代码目录结构
```
shell
├── resnet_thor
├── README.md
├── scripts
├── run_distribute_train.sh
# launch distributed training for Ascend 910
└── run_eval.sh
# launch inference for Ascend 910
├── run_distribute_train_gpu.sh
# launch distributed training for GPU
└── run_eval_gpu.sh
# launch inference for GPU
├── src
├── crossentropy.py
# CrossEntropy loss function
├── config.py
# parameter configuration
├── dataset_helper.py
# dataset helper for minddata dataset
├── grad_reducer_thor.py
# grad reduce for thor
├── model_thor.py
# model for train
├── resnet_thor.py
# resnet50_thor backone
├── thor.py
# thor optimizer
├── thor_layer.py
# thor layer
└── dataset.py
# data preprocessing
├── eval.py
# infer script
└── train.py
# train script
```
整体执行流程如下:
1.
准备ImagNet数据集,处理需要的数据集;
2.
定义ResNet50网络;
3.
定义损失函数和THOR优化器;
4.
加载数据集并进行训练,训练完成后,查看结果及保存模型文件;
5.
加载保存的模型,进行推理。
## 准备环节
在动手进行实践之前,确保你已经正确安装了MindSpore。如果没有,可以通过
[
MindSpore安装页面
](
https://www.mindspore.cn/install
)
安装MindSpore。
### 准备数据集
下载完整的ImageNet2012数据集,将数据集解压分别存放到本地工作区的
`ImageNet2012/ilsvrc`
、
`ImageNet2012/ilsvrc_eval`
路径下。
目录结构如下:
```
└─ImageNet2012
├─ilsvrc
│ n03676483/
│ n04067472/
│ n01622779/
│ ......
└─ilsvrc_eval
│ n03018349/
│ n02504013
│ n07871810
│ ......
```
### 配置分布式环境变量
#### Ascend 910
Ascend 910 AI处理器的分布式环境变量配置参考
[
分布式并行训练 (Ascend)
](
https://www.mindspore.cn/tutorial/zh-CN/master/advanced_use/distributed_training_ascend.html#id4
)
。
#### GPU
GPU的分布式环境配置参考
[
分布式并行训练 (GPU)
](
https://www.mindspore.cn/tutorial/zh-CN/master/advanced_use/distributed_training_gpu.html#id4
)
。
## 加载处理数据集
分布式训练时,通过并行的方式加载数据集,同时通过MindSpore提供的数据增强接口对数据集进行处理。加载处理数据集的脚本在源码的
`src/dataset.py`
脚本中。
```
python
import
os
import
mindspore.common.dtype
as
mstype
import
mindspore.dataset.engine
as
de
import
mindspore.dataset.transforms.vision.c_transforms
as
C
import
mindspore.dataset.transforms.c_transforms
as
C2
from
mindspore.communication.management
import
init
,
get_rank
,
get_group_size
def
create_dataset
(
dataset_path
,
do_train
,
repeat_num
=
1
,
batch_size
=
32
,
target
=
"Ascend"
):
if
target
==
"Ascend"
:
device_num
,
rank_id
=
_get_rank_info
()
else
:
init
(
"nccl"
)
rank_id
=
get_rank
()
device_num
=
get_group_size
()
if
device_num
==
1
:
ds
=
de
.
ImageFolderDatasetV2
(
dataset_path
,
num_parallel_workers
=
8
,
shuffle
=
True
)
else
:
ds
=
de
.
ImageFolderDatasetV2
(
dataset_path
,
num_parallel_workers
=
8
,
shuffle
=
True
,
num_shards
=
device_num
,
shard_id
=
rank_id
)
image_size
=
224
mean
=
[
0.485
*
255
,
0.456
*
255
,
0.406
*
255
]
std
=
[
0.229
*
255
,
0.224
*
255
,
0.225
*
255
]
# define map operations
if
do_train
:
trans
=
[
C
.
RandomCropDecodeResize
(
image_size
,
scale
=
(
0.08
,
1.0
),
ratio
=
(
0.75
,
1.333
)),
C
.
RandomHorizontalFlip
(
prob
=
0.5
),
C
.
Normalize
(
mean
=
mean
,
std
=
std
),
C
.
HWC2CHW
()
]
else
:
trans
=
[
C
.
Decode
(),
C
.
Resize
(
256
),
C
.
CenterCrop
(
image_size
),
C
.
Normalize
(
mean
=
mean
,
std
=
std
),
C
.
HWC2CHW
()
]
type_cast_op
=
C2
.
TypeCast
(
mstype
.
int32
)
ds
=
ds
.
map
(
input_columns
=
"image"
,
num_parallel_workers
=
8
,
operations
=
trans
)
ds
=
ds
.
map
(
input_columns
=
"label"
,
num_parallel_workers
=
8
,
operations
=
type_cast_op
)
# apply batch operations
ds
=
ds
.
batch
(
batch_size
,
drop_remainder
=
True
)
# apply dataset repeat operation
ds
=
ds
.
repeat
(
repeat_num
)
return
ds
```
> MindSpore支持进行多种数据处理和增强的操作,各种操作往往组合使用,具体可以参考[数据处理与数据增强](https://www.mindspore.cn/tutorial/zh-CN/master/use/data_preparation/data_processing_and_augmentation.html)章节。
## 定义网络
本示例中使用的网络模型为ResNet50-v1.5,先定义
[
ResNet50网络
](
https://gitee.com/mindspore/mindspore/blob/master/model_zoo/official/cv/resnet/src/resnet.py
)
,然后使用二阶优化器自定义的算子替换
`Cov2d`
和
和
`Dense`
算子。定义好的网络模型在在源码
`src/resnet_thor.py`
脚本中,自定义的算子
`Conv2d_thor`
和
`Dense_thor`
在
`src/thor_layer.py`
脚本中。
-
使用
`Conv2d_thor`
替换原网络模型中的
`Conv2d`
-
使用
`Dense_thor`
替换原网络模型中的
`Dense`
> 使用THOR自定义的算子`Conv2d_thor`和`Dense_thor`是为了保存模型训练中的二阶矩阵信息,新定义的网络与原网络模型的backbone一致。
网络构建完成以后,在
`__main__`
函数中调用定义好的ResNet50:
```
python
...
from
src.resnet_thor
import
resnet50
...
f
__name__
==
"__main__"
:
...
# define the net
net
=
resnet50
(
class_num
=
config
.
class_num
,
damping
=
damping
,
loss_scale
=
config
.
loss_scale
,
frequency
=
config
.
frequency
,
batch_size
=
config
.
batch_size
)
...
```
## 定义损失函数及THOR优化器
### 定义损失函数
MindSpore支持的损失函数有
`SoftmaxCrossEntropyWithLogits`
、
`L1Loss`
、
`MSELoss`
等。THOR优化器需要使用
`SoftmaxCrossEntropyWithLogits`
损失函数。
损失函数的实现步骤在
`src/crossentropy.py`
脚本中。这里使用了深度网络模型训练中的一个常用trick:label smoothing,通过对真实标签做平滑处理,提高模型对分类错误标签的容忍度,从而可以增加模型的泛化能力。
```
python
class
CrossEntropy
(
_Loss
):
"""CrossEntropy"""
def
__init__
(
self
,
smooth_factor
=
0.
,
num_classes
=
1000
):
super
(
CrossEntropy
,
self
).
__init__
()
self
.
onehot
=
P
.
OneHot
()
self
.
on_value
=
Tensor
(
1.0
-
smooth_factor
,
mstype
.
float32
)
self
.
off_value
=
Tensor
(
1.0
*
smooth_factor
/
(
num_classes
-
1
),
mstype
.
float32
)
self
.
ce
=
nn
.
SoftmaxCrossEntropyWithLogits
()
self
.
mean
=
P
.
ReduceMean
(
False
)
def
construct
(
self
,
logit
,
label
):
one_hot_label
=
self
.
onehot
(
label
,
F
.
shape
(
logit
)[
1
],
self
.
on_value
,
self
.
off_value
)
loss
=
self
.
ce
(
logit
,
one_hot_label
)
loss
=
self
.
mean
(
loss
,
0
)
return
loss
```
在
`__main__`
函数中调用定义好的损失函数:
```
python
...
from
src.crossentropy
import
CrossEntropy
...
if
__name__
==
"__main__"
:
...
# define the loss function
if
not
config
.
use_label_smooth
:
config
.
label_smooth_factor
=
0.0
loss
=
CrossEntropy
(
smooth_factor
=
config
.
label_smooth_factor
,
num_classes
=
config
.
class_num
)
...
```
### 定义优化器
THOR优化器的参数更新公式如下:
$$
\t
heta^{t+1} =
\t
heta^t +
\a
lpha F^{-1}
\n
abla E$$
参数更新公式中各参数的含义如下:
-
$
\t
heta$:网络中的可训参数;
-
$t$:迭代次数;
-
$
\a
lpha$:学习率值,参数的更新步长;
-
$F^{-1}$:FIM矩阵,在网络中计算获得;
-
$
\n
abla E$:一阶梯度值。
从参数更新公式中可以看出,THOR优化器需要额外计算的是每一层的FIM矩阵,每一层的FIM矩阵就是之前在自定义的网络模型中计算获得的。FIM矩阵可以对每一层参数更新的步长和方向进行自适应的调整,加速收敛的同时可以降低调参的复杂度。
```
python
...
if
args_opt
.
device_target
==
"Ascend"
:
from
src.thor
import
THOR
else
:
from
src.thor
import
THOR_GPU
as
THOR
...
if
__name__
==
"__main__"
:
...
# learning rate setting
lr
=
get_model_lr
(
0
,
config
.
lr_init
,
config
.
lr_decay
,
config
.
lr_end_epoch
,
step_size
,
decay_epochs
=
39
)
# define the optimizer
net_opt
=
opt
=
THOR
(
filter
(
lambda
x
:
x
.
requires_grad
,
net
.
get_parameters
()),
Tensor
(
lr
),
config
.
momentum
,
filter
(
lambda
x
:
'matrix_A'
in
x
.
name
,
net
.
get_parameters
()),
filter
(
lambda
x
:
'matrix_G'
in
x
.
name
,
net
.
get_parameters
()),
filter
(
lambda
x
:
'A_inv_max'
in
x
.
name
,
net
.
get_parameters
()),
filter
(
lambda
x
:
'G_inv_max'
in
x
.
name
,
net
.
get_parameters
()),
config
.
weight_decay
,
config
.
loss_scale
)
...
```
## 训练网络
### 配置模型保存
MindSpore提供了callback机制,可以在训练过程中执行自定义逻辑,这里使用框架提供的
`ModelCheckpoint`
函数。
`ModelCheckpoint`
可以保存网络模型和参数,以便进行后续的fine-tuning操作。
`TimeMonitor`
、
`LossMonitor`
是MindSpore官方提供的callback函数,可以分别用于监控训练过程中单步迭代时间和
`loss`
值的变化。
```
python
...
from
mindspore.train.callback
import
ModelCheckpoint
,
CheckpointConfig
,
TimeMonitor
,
LossMonitor
...
if
__name__
==
"__main__"
:
...
# define callbacks
time_cb
=
TimeMonitor
(
data_size
=
step_size
)
loss_cb
=
LossMonitor
()
cb
=
[
time_cb
,
loss_cb
]
if
config
.
save_checkpoint
:
config_ck
=
CheckpointConfig
(
save_checkpoint_steps
=
config
.
save_checkpoint_epochs
*
step_size
,
keep_checkpoint_max
=
config
.
keep_checkpoint_max
)
ckpt_cb
=
ModelCheckpoint
(
prefix
=
"resnet"
,
directory
=
ckpt_save_dir
,
config
=
config_ck
)
cb
+=
[
ckpt_cb
]
...
```
### 配置训练网络
通过MindSpore提供的
`model.train`
接口可以方便地进行网络的训练。THOR优化器通过降低二阶矩阵更新频率,来减少计算量,提升计算速度,故重新定义一个Model_thor类,继承MindSpore提供的Model类。在Model_thor类中增加二阶矩阵更新频率控制参数,用户可以通过调整该参数,优化整体的性能。
```
python
...
from
mindspore.train.loss_scale_manager
import
FixedLossScaleManager
from
src.model_thor
import
Model_Thor
as
Model
...
if
__name__
==
"__main__"
:
...
loss_scale
=
FixedLossScaleManager
(
config
.
loss_scale
,
drop_overflow_update
=
False
)
if
target
==
"Ascend"
:
model
=
Model
(
net
,
loss_fn
=
loss
,
optimizer
=
opt
,
amp_level
=
'O2'
,
loss_scale_manager
=
loss_scale
,
keep_batchnorm_fp32
=
False
,
metrics
=
{
'acc'
},
frequency
=
config
.
frequency
)
else
:
model
=
Model
(
net
,
loss_fn
=
loss
,
optimizer
=
opt
,
loss_scale_manager
=
loss_scale
,
metrics
=
{
'acc'
},
amp_level
=
"O2"
,
keep_batchnorm_fp32
=
True
,
frequency
=
config
.
frequency
)
...
```
### 运行脚本
训练脚本定义完成之后,调
`scripts`
目录下的shell脚本,启动分布式训练进程。
#### Ascend 910
目前MindSpore分布式在Ascend上执行采用单卡单进程运行方式,即每张卡上运行1个进程,进程数量与使用的卡的数量一致。其中,0卡在前台执行,其他卡放在后台执行。每个进程创建1个目录,用来保存日志信息以及算子编译信息。下面以使用8张卡的分布式训练脚本为例,演示如何运行脚本:
使用以下命令运行脚本:
```
sh run_distribute_train.sh [RANK_TABLE_FILE] [DATASET_PATH] [DEVICE_NUM]
```
脚本需要传入变量
`RANK_TABLE_FILE`
、
`DATASET_PATH`
和
`DEVICE_NUM`
,其中:
-
`RANK_TABLE_FILE`
:组网信息文件的路径。
-
`DATASET_PATH`
:训练数据集路径。
-
`DEVICE_NUM`
: 实际的运行卡数。
其余环境变量请参考安装教程中的配置项。
训练过程中loss打印示例如下:
```
bash
...
epoch: 1 step: 5004, loss is 4.4182425
epoch: 2 step: 5004, loss is 3.740064
epoch: 3 step: 5004, loss is 4.0546017
epoch: 4 step: 5004, loss is 3.7598825
epoch: 5 step: 5004, loss is 3.3744206
......
epoch: 40 step: 5004, loss is 1.6907625
epoch: 41 step: 5004, loss is 1.8217756
epoch: 42 step: 5004, loss is 1.6453942
...
```
训练完后,即保存的模型文件,示例如下:
```
bash
resnet-42_5004.ckpt
```
其中,
`resnet-42_5004.ckpt`
:指保存的模型参数文件。名称具体含义checkpoint_
*网络名称*
-
*第几个epoch*
_
*第几个step*
.ckpt。
##### GPU
在GPU硬件平台上,MindSpore采用OpenMPI的
`mpirun`
进行分布式训练。下面以使用8张卡的分布式训练脚本为例,演示如何运行脚本:
```
sh run_distribute_train_gpu.sh [DATASET_PATH] [DEVICE_NUM]
```
脚本需要传入变量
`DATASET_PATH`
和
`DEVICE_NUM`
,其中:
-
`DATASET_PATH`
:训练数据集路径。
-
`DEVICE_NUM`
: 实际的运行卡数。
在GPU训练时,无需设置
`DEVICE_ID`
环境变量,因此在主训练脚本中不需要调用
`int(os.getenv('DEVICE_ID'))`
来获取卡的物理序号,同时
`context`
中也无需传入
`device_id`
。我们需要将device_target设置为GPU,并需要调用
`init("nccl")`
来使能NCCL。
训练过程中loss打印示例如下:
```
bash
...
epoch: 1 step: 5004, loss is 4.3069
epoch: 2 step: 5004, loss is 3.5695
epoch: 3 step: 5004, loss is 3.5893
epoch: 4 step: 5004, loss is 3.1987
epoch: 5 step: 5004, loss is 3.3526
......
epoch: 40 step: 5004, loss is 1.9482
epoch: 41 step: 5004, loss is 1.8950
epoch: 42 step: 5004, loss is 1.9023
...
```
训练完后,即保存的模型文件,示例如下:
```
bash
resnet-42_5004.ckpt
```
## 模型推理
使用训练过程中保存的checkpoint文件进行推理,验证模型的泛化能力。首先通过
`load_checkpoint`
接口加载模型文件,然后调用
`Model`
的
`eval`
接口对输入图片类别作出预测,再与输入图片的真实类别做比较,得出最终的预测精度值。
### 定义推理网络
1.
使用
`load_checkpoint`
接口加载模型文件。
2.
使用
`model.eval`
接口读入测试数据集,进行推理。
3.
计算得出预测精度值。
```
python
...
from
mindspore.train.serialization
import
load_checkpoint
,
load_param_into_net
...
if
__name__
==
"__main__"
:
...
# define net
net
=
resnet
(
class_num
=
config
.
class_num
)
net
.
add_flags_recursive
(
thor
=
False
)
# load checkpoint
param_dict
=
load_checkpoint
(
args_opt
.
checkpoint_path
)
keys
=
list
(
param_dict
.
keys
())
for
key
in
keys
:
if
"damping"
in
key
:
param_dict
.
pop
(
key
)
load_param_into_net
(
net
,
param_dict
)
net
.
set_train
(
False
)
# define model
model
=
Model
(
net
,
loss_fn
=
loss
,
metrics
=
{
'top_1_accuracy'
,
'top_5_accuracy'
})
# eval model
res
=
model
.
eval
(
dataset
)
print
(
"result:"
,
res
,
"ckpt="
,
args_opt
.
checkpoint_path
)
```
### 执行推理
推理网络定义完成之后,调用/scripts目录下的shell脚本,进行推理。
#### Ascend 910
在Ascend 910硬件平台上,推理的执行命令如下:
```
sh run_eval.sh [DATASET_PATH] [CHECKPOINT_PATH]
```
脚本需要传入变量
`DATASET_PATH`
和
`CHECKPOINT_PATH`
,其中:
-
`DATASET_PATH`
:推理数据集路径。
-
`CHECKPOINT_PATH`
: 保存的checkpoint路径。
目前推理使用的是单卡(默认device 0)进行推理,推理的结果如下:
```
result: {'top_5_accuracy': 0.9295574583866837, 'top_1_accuracy': 0.761443661971831} ckpt=train_parallel0/resnet-42_5004.ckpt
```
-
`top_5_accuracy`
:对于一个输入图片,如果预测概率排名前五的标签中包含真实标签,即认为分类正确;
-
`top_1_accuracy`
:对于一个输入图片,如果预测概率最大的标签与真实标签相同,即认为分类正确。
#### GPU
在GPU硬件平台上,推理的执行命令如下:
```
sh run_eval_gpu.sh [DATASET_PATH] [CHECKPOINT_PATH]
```
脚本需要传入变量
`DATASET_PATH`
和
`CHECKPOINT_PATH`
,其中:
-
`DATASET_PATH`
:推理数据集路径。
-
`CHECKPOINT_PATH`
: 保存的checkpoint路径。
推理的结果如下:
```
result: {'top_5_accuracy': 0.9281169974391805, 'top_1_accuracy': 0.7593830025608195} ckpt=train_parallel0/resnet-42_5004.ckpt
```
tutorials/source_zh_cn/index.rst
浏览文件 @
844a3fce
...
...
@@ -32,6 +32,7 @@ MindSpore教程
advanced_use/computer_vision_application
advanced_use/nlp_application
advanced_use/second_order_optimizer_for_resnet50_application
.. toctree::
:glob:
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录