Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
PaddlePaddle
PaddleSlim
提交
f8da03ec
P
PaddleSlim
项目概览
PaddlePaddle
/
PaddleSlim
接近 2 年 前同步成功
通知
51
Star
1434
Fork
344
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
53
列表
看板
标记
里程碑
合并请求
16
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
P
PaddleSlim
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
53
Issue
53
列表
看板
标记
里程碑
合并请求
16
合并请求
16
Pages
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
提交
Issue看板
提交
f8da03ec
编写于
12月 13, 2019
作者:
W
wanghaoshuang
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
Refine sensitive and demo.
上级
47461da0
变更
6
隐藏空白更改
内联
并排
Showing
6 changed file
with
266 addition
and
88 deletion
+266
-88
demo/prune/train.py
demo/prune/train.py
+4
-0
demo/sensitive/run.sh
demo/sensitive/run.sh
+2
-0
demo/sensitive/train.py
demo/sensitive/train.py
+137
-0
paddleslim/prune/__init__.py
paddleslim/prune/__init__.py
+20
-10
paddleslim/prune/sensitive.py
paddleslim/prune/sensitive.py
+81
-33
paddleslim/prune/sensitive_pruner.py
paddleslim/prune/sensitive_pruner.py
+22
-45
未找到文件。
demo/prune/train.py
浏览文件 @
f8da03ec
...
...
@@ -198,6 +198,10 @@ def compress(args):
ratios
=
[
0.33
]
*
len
(
params
),
place
=
place
)
for
param
in
pruned_program
[
0
].
global_block
().
all_parameters
():
if
"weights"
in
param
.
name
:
print
param
.
name
,
param
.
shape
return
_logger
.
info
(
"fops after pruning: {}"
.
format
(
flops
(
pruned_program
)))
for
i
in
range
(
args
.
num_epochs
):
...
...
demo/sensitive/run.sh
0 → 100644
浏览文件 @
f8da03ec
export
CUDA_VISIBLE_DEVICES
=
2
python train.py
demo/sensitive/train.py
0 → 100644
浏览文件 @
f8da03ec
import
os
import
sys
import
logging
import
paddle
import
argparse
import
functools
import
math
import
time
import
numpy
as
np
import
paddle.fluid
as
fluid
from
paddleslim.prune
import
merge_sensitive
,
get_ratios_by_loss
from
paddleslim.prune
import
sensitivity
from
paddleslim.common
import
get_logger
sys
.
path
.
append
(
sys
.
path
[
0
]
+
"/../"
)
import
models
from
utility
import
add_arguments
,
print_arguments
_logger
=
get_logger
(
__name__
,
level
=
logging
.
INFO
)
parser
=
argparse
.
ArgumentParser
(
description
=
__doc__
)
add_arg
=
functools
.
partial
(
add_arguments
,
argparser
=
parser
)
# yapf: disable
add_arg
(
'batch_size'
,
int
,
64
*
4
,
"Minibatch size."
)
add_arg
(
'use_gpu'
,
bool
,
True
,
"Whether to use GPU or not."
)
add_arg
(
'model'
,
str
,
"MobileNet"
,
"The target model."
)
add_arg
(
'pretrained_model'
,
str
,
"../pretrained_model/MobileNetV1_pretained"
,
"Whether to use pretrained model."
)
add_arg
(
'data'
,
str
,
"mnist"
,
"Which data to use. 'mnist' or 'imagenet'"
)
add_arg
(
'log_period'
,
int
,
10
,
"Log period in batches."
)
# yapf: enable
model_list
=
[
m
for
m
in
dir
(
models
)
if
"__"
not
in
m
]
def
compress
(
args
):
test_reader
=
None
if
args
.
data
==
"mnist"
:
import
paddle.dataset.mnist
as
reader
val_reader
=
reader
.
test
()
class_dim
=
10
image_shape
=
"1,28,28"
elif
args
.
data
==
"imagenet"
:
import
imagenet_reader
as
reader
val_reader
=
reader
.
val
()
class_dim
=
1000
image_shape
=
"3,224,224"
else
:
raise
ValueError
(
"{} is not supported."
.
format
(
args
.
data
))
image_shape
=
[
int
(
m
)
for
m
in
image_shape
.
split
(
","
)]
assert
args
.
model
in
model_list
,
"{} is not in lists: {}"
.
format
(
args
.
model
,
model_list
)
image
=
fluid
.
layers
.
data
(
name
=
'image'
,
shape
=
image_shape
,
dtype
=
'float32'
)
label
=
fluid
.
layers
.
data
(
name
=
'label'
,
shape
=
[
1
],
dtype
=
'int64'
)
# model definition
model
=
models
.
__dict__
[
args
.
model
]()
out
=
model
.
net
(
input
=
image
,
class_dim
=
class_dim
)
acc_top1
=
fluid
.
layers
.
accuracy
(
input
=
out
,
label
=
label
,
k
=
1
)
acc_top5
=
fluid
.
layers
.
accuracy
(
input
=
out
,
label
=
label
,
k
=
5
)
val_program
=
fluid
.
default_main_program
().
clone
(
for_test
=
True
)
place
=
fluid
.
CUDAPlace
(
0
)
if
args
.
use_gpu
else
fluid
.
CPUPlace
()
exe
=
fluid
.
Executor
(
place
)
exe
.
run
(
fluid
.
default_startup_program
())
if
args
.
pretrained_model
:
def
if_exist
(
var
):
return
os
.
path
.
exists
(
os
.
path
.
join
(
args
.
pretrained_model
,
var
.
name
))
fluid
.
io
.
load_vars
(
exe
,
args
.
pretrained_model
,
predicate
=
if_exist
)
val_reader
=
paddle
.
batch
(
val_reader
,
batch_size
=
args
.
batch_size
)
val_feeder
=
feeder
=
fluid
.
DataFeeder
(
[
image
,
label
],
place
,
program
=
val_program
)
def
test
(
program
):
batch_id
=
0
acc_top1_ns
=
[]
acc_top5_ns
=
[]
for
data
in
val_reader
():
start_time
=
time
.
time
()
acc_top1_n
,
acc_top5_n
=
exe
.
run
(
program
,
feed
=
val_feeder
.
feed
(
data
),
fetch_list
=
[
acc_top1
.
name
,
acc_top5
.
name
])
end_time
=
time
.
time
()
if
batch_id
%
args
.
log_period
==
0
:
_logger
.
info
(
"Eval batch[{}] - acc_top1: {}; acc_top5: {}; time: {}"
.
format
(
batch_id
,
np
.
mean
(
acc_top1_n
),
np
.
mean
(
acc_top5_n
),
end_time
-
start_time
))
acc_top1_ns
.
append
(
np
.
mean
(
acc_top1_n
))
acc_top5_ns
.
append
(
np
.
mean
(
acc_top5_n
))
batch_id
+=
1
_logger
.
info
(
"Final eva - acc_top1: {}; acc_top5: {}"
.
format
(
np
.
mean
(
np
.
array
(
acc_top1_ns
)),
np
.
mean
(
np
.
array
(
acc_top5_ns
))))
return
np
.
mean
(
np
.
array
(
acc_top1_ns
))
params
=
[]
for
param
in
fluid
.
default_main_program
().
global_block
().
all_parameters
():
if
"_sep_weights"
in
param
.
name
:
params
.
append
(
param
.
name
)
sensitivity
(
val_program
,
place
,
params
,
test
,
sensitivities_file
=
"sensitivities_0.data"
,
pruned_ratios
=
[
0.1
,
0.2
,
0.3
,
0.4
])
sensitivity
(
val_program
,
place
,
params
,
test
,
sensitivities_file
=
"sensitivities_1.data"
,
pruned_ratios
=
[
0.5
,
0.6
,
0.7
])
sens
=
merge_sensitive
(
[
"./sensitivities_0.data"
,
"./sensitivities_1.data"
])
ratios
=
get_ratios_by_loss
(
sens
,
0.01
)
print
ratios
def
main
():
args
=
parser
.
parse_args
()
print_arguments
(
args
)
compress
(
args
)
if
__name__
==
'__main__'
:
main
()
paddleslim/prune/__init__.py
浏览文件 @
f8da03ec
...
...
@@ -11,14 +11,24 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from
.pruner
import
Pruner
from
.auto_pruner
import
AutoPruner
from
.controller_server
import
ControllerServer
from
.controller_client
import
ControllerClient
from
.sensitive_pruner
import
SensitivePruner
from
.sensitive
import
sensitivity
,
flops_sensitivity
from
.pruner
import
*
import
pruner
from
.auto_pruner
import
*
import
auto_pruner
from
.controller_server
import
*
import
controller_server
from
.controller_client
import
*
import
controller_client
from
.sensitive_pruner
import
*
import
sensitive_pruner
from
.sensitive
import
*
import
sensitive
__all__
=
[
'Pruner'
,
'AutoPruner'
,
'ControllerServer'
,
'ControllerClient'
,
'SensitivePruner'
,
'sensitivity'
,
'flops_sensitivity'
]
__all__
=
[]
__all__
+=
pruner
.
__all__
__all__
+=
auto_pruner
.
__all__
__all__
+=
controller_server
.
__all__
__all__
+=
controller_client
.
__all__
__all__
+=
sensitive_pruner
.
__all__
__all__
+=
sensitive
.
__all__
paddleslim/prune/sensitive.py
浏览文件 @
f8da03ec
...
...
@@ -25,7 +25,10 @@ from ..prune import Pruner
_logger
=
get_logger
(
__name__
,
level
=
logging
.
INFO
)
__all__
=
[
"sensitivity"
,
"flops_sensitivity"
]
__all__
=
[
"sensitivity"
,
"flops_sensitivity"
,
"load_sensitivities"
,
"merge_sensitive"
,
"get_ratios_by_loss"
]
def
sensitivity
(
program
,
...
...
@@ -36,23 +39,18 @@ def sensitivity(program,
pruned_ratios
=
None
):
scope
=
fluid
.
global_scope
()
graph
=
GraphWrapper
(
program
)
sensitivities
=
_
load_sensitivities
(
sensitivities_file
)
sensitivities
=
load_sensitivities
(
sensitivities_file
)
if
pruned_ratios
is
None
:
pruned_ratios
=
np
.
arange
(
0.1
,
1
,
step
=
0.1
)
for
name
in
param_names
:
if
name
not
in
sensitivities
:
size
=
graph
.
var
(
name
).
shape
()[
0
]
sensitivities
[
name
]
=
{
'pruned_percent'
:
[],
'loss'
:
[],
'size'
:
size
}
sensitivities
[
name
]
=
{}
baseline
=
None
for
name
in
sensitivities
:
for
ratio
in
pruned_ratios
:
if
ratio
in
sensitivities
[
name
]
[
'pruned_percent'
]
:
if
ratio
in
sensitivities
[
name
]:
_logger
.
debug
(
'{}, {} has computed.'
.
format
(
name
,
ratio
))
continue
if
baseline
is
None
:
...
...
@@ -75,8 +73,7 @@ def sensitivity(program,
_logger
.
info
(
"pruned param: {}; {}; loss={}"
.
format
(
name
,
ratio
,
loss
))
sensitivities
[
name
][
'pruned_percent'
].
append
(
ratio
)
sensitivities
[
name
][
'loss'
].
append
(
loss
)
sensitivities
[
name
][
ratio
]
=
loss
_save_sensitivities
(
sensitivities
,
sensitivities_file
)
...
...
@@ -98,16 +95,11 @@ def flops_sensitivity(program,
scope
=
fluid
.
global_scope
()
graph
=
GraphWrapper
(
program
)
sensitivities
=
_
load_sensitivities
(
sensitivities_file
)
sensitivities
=
load_sensitivities
(
sensitivities_file
)
for
name
in
param_names
:
if
name
not
in
sensitivities
:
size
=
graph
.
var
(
name
).
shape
()[
0
]
sensitivities
[
name
]
=
{
'pruned_percent'
:
[],
'loss'
:
[],
'size'
:
size
}
sensitivities
[
name
]
=
{}
base_flops
=
flops
(
program
)
target_pruned_flops
=
base_flops
*
pruned_flops_rate
...
...
@@ -124,15 +116,16 @@ def flops_sensitivity(program,
lazy
=
False
,
only_graph
=
True
)
param_flops
=
(
base_flops
-
flops
(
pruned_program
))
*
2
channel_size
=
sensitivities
[
name
][
"size"
]
channel_size
=
graph
.
var
(
name
).
shape
()[
0
]
pruned_ratio
=
target_pruned_flops
/
float
(
param_flops
)
pruned_ratio
=
round
(
pruned_ratio
,
3
)
pruned_size
=
round
(
pruned_ratio
*
channel_size
)
pruned_ratio
=
1
if
pruned_size
>=
channel_size
else
pruned_ratio
if
len
(
sensitivities
[
name
]
[
"pruned_percent"
]
)
>
0
:
_logger
.
debug
(
'{} exist; pruned ratio: {}; excepted ratio: {}'
.
format
(
name
,
sensitivities
[
name
][
"pruned_percent"
][
0
]
,
pruned_ratio
))
if
len
(
sensitivities
[
name
]
.
keys
()
)
>
0
:
_logger
.
debug
(
'{} exist; pruned ratio: {}; excepted ratio: {}'
.
format
(
name
,
sensitivities
[
name
].
keys
()
,
pruned_ratio
))
continue
if
baseline
is
None
:
baseline
=
eval_func
(
graph
.
program
)
...
...
@@ -155,8 +148,7 @@ def flops_sensitivity(program,
loss
=
(
baseline
-
pruned_metric
)
/
baseline
_logger
.
info
(
"pruned param: {}; {}; loss={}"
.
format
(
name
,
pruned_ratio
,
loss
))
sensitivities
[
name
][
'pruned_percent'
].
append
(
pruned_ratio
)
sensitivities
[
name
][
'loss'
].
append
(
loss
)
sensitivities
[
name
][
pruned_ratio
]
=
loss
_save_sensitivities
(
sensitivities
,
sensitivities_file
)
# restore pruned parameters
...
...
@@ -166,7 +158,30 @@ def flops_sensitivity(program,
return
sensitivities
def
_load_sensitivities
(
sensitivities_file
):
def
merge_sensitive
(
sensitivities
):
"""
Merge sensitivities.
Args:
sensitivities(list<dict> | list<str>): The sensitivities to be merged. It cann be a list of sensitivities files or dict.
Returns:
sensitivities(dict): A dict with sensitivities.
"""
assert
len
(
sensitivities
)
>
0
if
not
isinstance
(
sensitivities
[
0
],
dict
):
sensitivities
=
[
pickle
.
load
(
open
(
sen
,
'r'
))
for
sen
in
sensitivities
]
new_sensitivities
=
{}
for
sen
in
sensitivities
:
for
param
,
losses
in
sen
.
items
():
if
param
not
in
new_sensitivities
:
new_sensitivities
[
param
]
=
{}
for
percent
,
loss
in
losses
.
items
():
new_sensitivities
[
param
][
percent
]
=
loss
return
new_sensitivities
def
load_sensitivities
(
sensitivities_file
):
"""
Load sensitivities from file.
"""
...
...
@@ -177,17 +192,50 @@ def _load_sensitivities(sensitivities_file):
sensitivities
=
pickle
.
load
(
f
)
else
:
sensitivities
=
pickle
.
load
(
f
,
encoding
=
'bytes'
)
for
param
in
sensitivities
:
sensitivities
[
param
][
'pruned_percent'
]
=
[
round
(
p
,
2
)
for
p
in
sensitivities
[
param
][
'pruned_percent'
]
]
return
sensitivities
def
_save_sensitivities
(
sensitivities
,
sensitivities_file
):
"""
Save sensitivities into file.
"""
Save sensitivities into file.
"""
with
open
(
sensitivities_file
,
'wb'
)
as
f
:
pickle
.
dump
(
sensitivities
,
f
)
def
get_ratios_by_loss
(
sensitivities
,
loss
):
"""
Get the max ratio of each parameter. The loss of accuracy must be less than given `loss`
when the single parameter was pruned by the max ratio.
Args:
sensitivities(dict): The sensitivities used to generate a group of pruning ratios. The key of dict
is name of parameters to be pruned. The value of dict is a list of tuple with
format `(pruned_ratio, accuracy_loss)`.
loss(float): The threshold of accuracy loss.
Returns:
ratios(dict): A group of ratios. The key of dict is name of parameters while the value is the ratio to be pruned.
"""
ratios
=
{}
for
param
,
losses
in
sensitivities
.
items
():
losses
.
sort
()
for
i
in
range
(
len
(
losses
))[::
-
1
]:
if
losses
[
i
][
1
]
<=
loss
:
if
i
==
(
len
(
losses
)
-
1
):
ratios
[
param
]
=
losses
[
i
][
0
]
else
:
r0
,
l0
=
losses
[
i
]
r1
,
l1
=
losses
[
i
+
1
]
d0
=
loss
-
l0
d1
=
l1
-
loss
ratio
=
r0
+
(
loss
-
l0
)
*
(
r1
-
r0
)
/
(
l1
-
l0
)
ratios
[
param
]
=
ratio
if
ratio
>
1
:
print
losses
,
ratio
,
(
r1
-
r0
)
/
(
l1
-
l0
),
i
break
return
ratios
paddleslim/prune/sensitive_pruner.py
浏览文件 @
f8da03ec
...
...
@@ -20,7 +20,7 @@ import numpy as np
import
paddle.fluid
as
fluid
from
..common
import
get_logger
from
.sensitive
import
sensitivity
from
.sensitive
import
flops_sensitivity
from
.sensitive
import
flops_sensitivity
,
get_ratios_by_loss
from
..analysis
import
flops
from
.pruner
import
Pruner
...
...
@@ -153,8 +153,8 @@ class SensitivePruner(object):
sensitivities_file
=
sensitivities_file
,
step_size
=
0.1
)
print
sensitivities
_
,
ratios
=
self
.
_
get_ratios_by_sensitive
(
sensitivities
,
pruned_flops
,
eval_program
)
_
,
ratios
=
self
.
get_ratios_by_sensitive
(
sensitivities
,
pruned_flops
,
eval_program
)
pruned_program
=
self
.
_pruner
.
prune
(
train_program
,
...
...
@@ -185,72 +185,49 @@ class SensitivePruner(object):
topk_percents
=
[
percents
[
param
]
for
param
in
topk_parms
]
return
topk_parms
,
topk_percents
def
_
get_ratios_by_sensitive
(
self
,
sensitivities
,
pruned_flops
,
eval_program
):
def
get_ratios_by_sensitive
(
self
,
sensitivities
,
pruned_flops
,
eval_program
):
"""
Search a group of ratios for pruning target flops.
"""
def
func
(
params
,
x
):
a
,
b
,
c
,
d
=
params
return
a
*
x
*
x
*
x
+
b
*
x
*
x
+
c
*
x
+
d
Args:
def
error
(
params
,
x
,
y
):
return
func
(
params
,
x
)
-
y
sensitivities(dict): The sensitivities used to generate a group of pruning ratios. The key of dict
is name of parameters to be pruned. The value of dict is a list of tuple with
format `(pruned_ratio, accuracy_loss)`.
pruned_flops(float): The percent of FLOPS to be pruned.
eval_program(Program): The program whose FLOPS is considered.
def
slove_coefficient
(
x
,
y
)
:
init_coefficient
=
[
10
,
10
,
10
,
10
]
coefficient
,
loss
=
leastsq
(
error
,
init_coefficient
,
args
=
(
x
,
y
))
return
coefficient
Return
:
ratios(dict): A group of ratios. The key of dict is name of parameters while the value is the ratio to be pruned.
"""
min_loss
=
0.
max_loss
=
0.
# step 1: fit curve by sensitivities
coefficients
=
{}
for
param
in
sensitivities
:
losses
=
np
.
array
([
0
]
*
5
+
sensitivities
[
param
][
'loss'
])
precents
=
np
.
array
([
0
]
*
5
+
sensitivities
[
param
][
'pruned_percent'
])
coefficients
[
param
]
=
slove_coefficient
(
precents
,
losses
)
loss
=
np
.
max
(
losses
)
max_loss
=
np
.
max
([
max_loss
,
loss
])
# step 2: Find a group of ratios by binary searching.
base_flops
=
flops
(
eval_program
)
ratios
=
[]
ratios
=
None
max_times
=
20
while
min_loss
<
max_loss
and
max_times
>
0
:
loss
=
(
max_loss
+
min_loss
)
/
2
_logger
.
info
(
'-----------Try pruned ratios while acc loss={}-----------'
.
format
(
loss
))
ratios
=
[]
# step 2.1: Get ratios according to current loss
for
param
in
sensitivities
:
coefficient
=
copy
.
deepcopy
(
coefficients
[
param
])
coefficient
[
-
1
]
=
coefficient
[
-
1
]
-
loss
roots
=
np
.
roots
(
coefficient
)
for
root
in
roots
:
min_root
=
1
if
np
.
isreal
(
root
)
and
root
>
0
and
root
<
1
:
selected_root
=
min
(
root
.
real
,
min_root
)
ratios
.
append
(
selected_root
)
ratios
=
self
.
get_ratios_by_loss
(
sensitivities
,
loss
)
_logger
.
info
(
'Pruned ratios={}'
.
format
(
[
round
(
ratio
,
3
)
for
ratio
in
ratios
]))
# step 2.2: Pruning by current ratios
param_shape_backup
=
{}
[
round
(
ratio
,
3
)
for
ratio
in
ratios
.
values
()]))
pruned_program
=
self
.
_pruner
.
prune
(
eval_program
,
None
,
# scope
sensitivitie
s
.
keys
(),
ratios
,
ratio
s
.
keys
(),
ratios
.
values
()
,
None
,
# place
only_graph
=
True
)
pruned_ratio
=
1
-
(
float
(
flops
(
pruned_program
))
/
base_flops
)
_logger
.
info
(
'Pruned flops: {:.4f}'
.
format
(
pruned_ratio
))
#
step 2.3:
Check whether current ratios is enough
# Check whether current ratios is enough
if
abs
(
pruned_ratio
-
pruned_flops
)
<
0.015
:
break
if
pruned_ratio
>
pruned_flops
:
...
...
@@ -258,4 +235,4 @@ class SensitivePruner(object):
else
:
min_loss
=
loss
max_times
-=
1
return
sensitivities
.
keys
(),
ratios
return
ratios
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录