Skip to content

Commit e18ab78

Browse files
AIFollowersqingqing01
authored andcommitted
add model_stat.py (#16512)
* Add a tool to summary model's PARAMS, FLOPs in paddle/fluid/contrib.
1 parent d4f63d8 commit e18ab78

File tree

1 file changed

+194
-0
lines changed

1 file changed

+194
-0
lines changed
Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
# Copyright (c) 2019 PaddlePaddle Authors. All Rights Reserved
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
'''
15+
Example:
16+
>>from paddle.fluid.contrib.model_stat import summary
17+
>>main_program = ...
18+
>>summary(main_program)
19+
+-----+------------+----------------+----------------+---------+------------+
20+
| No. | TYPE | INPUT | OUTPUT | PARAMs | FLOPs |
21+
+-----+------------+----------------+----------------+---------+------------+
22+
| 0 | conv2d | (3, 200, 200) | (64, 100, 100) | 9408 | 188160000 |
23+
| 1 | batch_norm | (64, 100, 100) | (64, 100, 100) | 256 | 640000 |
24+
| 2 | relu | (64, 100, 100) | (64, 100, 100) | 0 | 640000 |
25+
| 3 | pool2d | (64, 100, 100) | (64, 50, 50) | 0 | 1440000 |
26+
...
27+
| 176 | conv2d | (512, 7, 7) | (512, 7, 7) | 2359296 | 231211008 |
28+
| 177 | relu | (512, 7, 7) | (512, 7, 7) | 0 | 25088 |
29+
| 178 | conv2d | (512, 7, 7) | (2048, 7, 7) | 1048576 | 102760448 |
30+
| 179 | relu | (2048, 7, 7) | (2048, 7, 7) | 0 | 100352 |
31+
| 180 | pool2d | (2048, 7, 7) | (2048, 1, 1) | 0 | 100352 |
32+
+-----+------------+----------------+----------------+---------+------------+
33+
Total PARAMs: 48017344(0.0480G)
34+
Total FLOPs: 11692747751(11.69G)
35+
'''
36+
from collections import OrderedDict
37+
from prettytable import PrettyTable
38+
39+
40+
def summary(main_prog):
41+
'''
42+
It can summary model's PARAMS, FLOPs until now.
43+
It support common operator like conv, fc, pool, relu, sigmoid, bn etc.
44+
Args:
45+
main_prog: main program
46+
Returns:
47+
print summary on terminal
48+
'''
49+
collected_ops_list = []
50+
for one_b in main_prog.blocks:
51+
block_vars = one_b.vars
52+
for one_op in one_b.ops:
53+
op_info = OrderedDict()
54+
spf_res = _summary_model(block_vars, one_op)
55+
if spf_res is None:
56+
continue
57+
# TODO: get the operator name
58+
op_info['type'] = one_op.type
59+
op_info['input_shape'] = spf_res[0][1:]
60+
op_info['out_shape'] = spf_res[1][1:]
61+
op_info['PARAMs'] = spf_res[2]
62+
op_info['FLOPs'] = spf_res[3]
63+
collected_ops_list.append(op_info)
64+
65+
summary_table, total = _format_summary(collected_ops_list)
66+
_print_summary(summary_table, total)
67+
68+
69+
def _summary_model(block_vars, one_op):
70+
'''
71+
Compute operator's params and flops.
72+
Args:
73+
block_vars: all vars of one block
74+
one_op: one operator to count
75+
Returns:
76+
in_data_shape: one operator's input data shape
77+
out_data_shape: one operator's output data shape
78+
params: one operator's PARAMs
79+
flops: : one operator's FLOPs
80+
'''
81+
if one_op.type in ['conv2d', 'depthwise_conv2d']:
82+
k_arg_shape = block_vars[one_op.input("Filter")[0]].shape
83+
in_data_shape = block_vars[one_op.input("Input")[0]].shape
84+
out_data_shape = block_vars[one_op.output("Output")[0]].shape
85+
c_out, c_in, k_h, k_w = k_arg_shape
86+
_, c_out_, h_out, w_out = out_data_shape
87+
assert c_out == c_out_, 'shape error!'
88+
k_groups = one_op.attr("groups")
89+
kernel_ops = k_h * k_w * (c_in / k_groups)
90+
bias_ops = 0 if one_op.input("Bias") == [] else 1
91+
params = c_out * (kernel_ops + bias_ops)
92+
flops = h_out * w_out * c_out * (kernel_ops + bias_ops)
93+
# base nvidia paper, include mul and add
94+
flops = 2 * flops
95+
96+
elif one_op.type == 'pool2d':
97+
in_data_shape = block_vars[one_op.input("X")[0]].shape
98+
out_data_shape = block_vars[one_op.output("Out")[0]].shape
99+
_, c_out, h_out, w_out = out_data_shape
100+
k_size = one_op.attr("ksize")
101+
params = 0
102+
flops = h_out * w_out * c_out * (k_size[0] * k_size[1])
103+
104+
elif one_op.type == 'mul':
105+
k_arg_shape = block_vars[one_op.input("Y")[0]].shape
106+
in_data_shape = block_vars[one_op.input("X")[0]].shape
107+
out_data_shape = block_vars[one_op.output("Out")[0]].shape
108+
# TODO: fc has mul ops
109+
# add attr to mul op, tell us whether it belongs to 'fc'
110+
# this's not the best way
111+
if 'fc' not in one_op.output("Out")[0]:
112+
return None
113+
k_in, k_out = k_arg_shape
114+
# bias in sum op
115+
params = k_in * k_out + 1
116+
flops = k_in * k_out
117+
118+
elif one_op.type in ['sigmoid', 'tanh', 'relu', 'leaky_relu', 'prelu']:
119+
in_data_shape = block_vars[one_op.input("X")[0]].shape
120+
out_data_shape = block_vars[one_op.output("Out")[0]].shape
121+
params = 0
122+
if one_op.type == 'prelu':
123+
params = 1
124+
flops = 1
125+
for one_dim in in_data_shape:
126+
flops *= one_dim
127+
128+
elif one_op.type == 'batch_norm':
129+
in_data_shape = block_vars[one_op.input("X")[0]].shape
130+
out_data_shape = block_vars[one_op.output("Y")[0]].shape
131+
_, c_in, h_out, w_out = in_data_shape
132+
# gamma, beta
133+
params = c_in * 2
134+
# compute mean and std
135+
flops = h_out * w_out * c_in * 2
136+
137+
else:
138+
return None
139+
140+
return in_data_shape, out_data_shape, params, flops
141+
142+
143+
def _format_summary(collected_ops_list):
144+
'''
145+
Format summary report.
146+
Args:
147+
collected_ops_list: the collected operator with summary
148+
Returns:
149+
summary_table: summary report format
150+
total: sum param and flops
151+
'''
152+
summary_table = PrettyTable(
153+
["No.", "TYPE", "INPUT", "OUTPUT", "PARAMs", "FLOPs"])
154+
summary_table.align = 'r'
155+
156+
total = {}
157+
total_params = []
158+
total_flops = []
159+
for i, one_op in enumerate(collected_ops_list):
160+
# notice the order
161+
table_row = [
162+
i,
163+
one_op['type'],
164+
one_op['input_shape'],
165+
one_op['out_shape'],
166+
int(one_op['PARAMs']),
167+
int(one_op['FLOPs']),
168+
]
169+
summary_table.add_row(table_row)
170+
total_params.append(int(one_op['PARAMs']))
171+
total_flops.append(int(one_op['FLOPs']))
172+
173+
total['params'] = total_params
174+
total['flops'] = total_flops
175+
176+
return summary_table, total
177+
178+
179+
def _print_summary(summary_table, total):
180+
'''
181+
Print all the summary on terminal.
182+
Args:
183+
summary_table: summary report format
184+
total: sum param and flops
185+
'''
186+
parmas = total['params']
187+
flops = total['flops']
188+
print(summary_table)
189+
print('Total PARAMs: {}({:.4f}M)'.format(
190+
sum(parmas), sum(parmas) / (10**6)))
191+
print('Total FLOPs: {}({:.2f}G)'.format(sum(flops), sum(flops) / 10**9))
192+
print(
193+
"Notice: \n now supported ops include [Conv, DepthwiseConv, FC(mul), BatchNorm, Pool, Activation(sigmoid, tanh, relu, leaky_relu, prelu)]"
194+
)

0 commit comments

Comments
 (0)