/
sim_caffenet.py
222 lines (190 loc) · 10.3 KB
/
sim_caffenet.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
#################################################
# This module exports functions for running the
# simulation of CaffeNet on the specfied dataset
#################################################
import sys
# point to the caffe root, modify the following path if not
caffe_root = '../../'
sys.path.insert(0, caffe_root+'python')
import caffe
from caffe import layers as L, params as P
from caffenet_prototxt import caffenet, quantized_caffenet, convert_to_quantization_param
import tempfile, os
import numpy as np
import math
from fixed_point import FixedPoint
import collections
# global variable: caffenet pretrained weights
caffenet_weights = caffe_root + '/models/bvlc_reference_caffenet/bvlc_reference_caffenet.caffemodel'
def sim_floating_point_caffenet(LMDB_filename, batch_size=10, iterations=10, verbose=False):
'''Simlulate the floating point CaffeNet on the specified LMDB dataset,
where the batch size of running CaffeNet and the total iterations are
specified by the arguments.
'''
# step 1. create the data & label layer
# create input data layer for caffenet accuracy
mean_file = caffe_root + 'data/ilsvrc12/imagenet_mean.binaryproto'
ilsvrc_data, ilsvrc_label = L.Data(batch_size=batch_size, backend=P.Data.LMDB,
source=LMDB_filename, ntop=2, transform_param={'crop_size': 227,
'mirror': False, 'mean_file': mean_file})
# step 2. generate the caffenet prototxt definition to a temporary file
f = tempfile.NamedTemporaryFile(delete=False)
#print 'Generate Prototxt File to %s' % (f.name,)
f.close()
# generate the prototxt
caffenet(ilsvrc_data, label=ilsvrc_label, train=False, num_classes=1000,
classifier_name='fc8', learn_all=False, filename=f.name)
# step 3. create the net
floating_point_caffenet = None
floating_point_caffenet = caffe.Net(f.name, caffenet_weights, caffe.TEST)
# step 4. do feedforward and record the range & accuracy
accuracy = np.zeros((2, iterations)) # 2: top1 & top5
blobs_range, blobs_range_ = {}, {}
for blob_name in floating_point_caffenet.blobs:
blobs_range[blob_name] = np.zeros(2)
blobs_range_[blob_name] = np.zeros((2, iterations))
print 'Floating point CaffNet starts inference...'
for i in range(iterations):
floating_point_caffenet.forward()
if verbose:
print 'Batch %d/%d: Top-1 Acc: %.2f%%, Top-5 Acc: %.2f%%' % (i, iterations,
floating_point_caffenet.blobs['acc_top1'].data*100.,
floating_point_caffenet.blobs['acc_top5'].data*100.)
elif i % 10 == 0:
print 'Batch %d/%d: Top-1 Acc: %.2f%%, Top-5 Acc: %.2f%%' % (i, iterations,
floating_point_caffenet.blobs['acc_top1'].data*100.,
floating_point_caffenet.blobs['acc_top5'].data*100.)
accuracy[0, i] = floating_point_caffenet.blobs['acc_top1'].data
accuracy[1, i] = floating_point_caffenet.blobs['acc_top5'].data
for blob_name in floating_point_caffenet.blobs:
blobs_range_[blob_name][0, i] = floating_point_caffenet.blobs[blob_name].\
data.min()
blobs_range_[blob_name][1, i] = floating_point_caffenet.blobs[blob_name].\
data.max()
# calculate the final blobs range (max & min values)
for blob_name in blobs_range:
blobs_range[blob_name] = [blobs_range_[blob_name][0].min(), blobs_range_[blob_name][1].max()]
# step 5. record the weights & biases range
weights_range, biases_range = {}, {}
for param_name, param in floating_point_caffenet.params.items():
weights_range[param_name] = np.zeros(2) # 2: min & max
biases_range[param_name] = np.zeros(2) # 2: min & max
weights_range[param_name][0] = param[0].data.min()
weights_range[param_name][1] = param[0].data.max()
biases_range[param_name][0] = param[1].data.min()
biases_range[param_name][1] = param[1].data.max()
# obtain the kernel name, used for further evaluation of floating on different kernels
kernels_name = floating_point_caffenet.params.keys()
# step 6. clean the network and file
del floating_point_caffenet
os.unlink(f.name)
return accuracy, blobs_range, weights_range, biases_range, kernels_name
def sim_fixed_point_caffenet(LMDB_filename, bit_width, blobs_range,
weights_range, biases_range, batch_size=10, iterations=10, round_method='FLOOR',
round_strategy='CONSERVATIVE', verbose=False):
'''simluate the fixed point caffenet. It will automatically determine
the fraction bit width based on the range of activations, weights & biases.
The integer bit width is determined by the specified bit width.
- bit_width: a dictionary contains the bitwidth of `weights`, `biases`, and
`blobs` respectively.
'''
# step 1. create data layer & label layer
mean_file = caffe_root + 'data/ilsvrc12/imagenet_mean.binaryproto'
ilsvrc_data, ilsvrc_label = L.Data(batch_size=batch_size, backend=P.Data.LMDB,
source=LMDB_filename, ntop=2, transform_param={'crop_size': 227,
'mirror': False, 'mean_file': mean_file})
# step 2. generates the quantization parameters
blobs_quantization_params = quantization_param_wrapper(bit_width['blobs'],
blobs_range, round_method=round_method, round_strategy=round_strategy)
weights_quantization_params = quantization_param_wrapper(bit_width['weights'],
weights_range, round_method=round_method, round_strategy=round_strategy)
biases_quantization_params = quantization_param_wrapper(bit_width['biases'],
biases_range, round_method=round_method, round_strategy=round_strategy)
# step 3. generate the caffenet prototxt definition to a temporary file
f = tempfile.NamedTemporaryFile(delete=False)
#print 'Generate Prototxt File to %s' % (f.name,)
f.close()
# generate the prototxt
quantized_caffenet(ilsvrc_data, blobs_quantization_params, label=ilsvrc_label,
train=False, num_classes=1000, classifier_name='fc8',
learn_all=False, filename=f.name)
# step 4. create the net
fixed_point_caffenet = None
fixed_point_caffenet = caffe.Net(f.name, caffenet_weights, caffe.TEST)
# step 5. quantize the weights & biases based on the quantization parameters
for k, v in fixed_point_caffenet.params.items():
# 1. quantize the weights
WFixedPoint = FixedPoint(weights_quantization_params[k]['range'],
weights_quantization_params[k]['bit_width'], round_method=round_method,
round_strategy=round_strategy)
#print 'Before kernel[%s]: %d' % (k, len(np.unique(v[0].data)))
v[0].data[...] = WFixedPoint.quantize(v[0].data)
#print 'After kernel[%s]: %d' % (k, len(np.unique(v[0].data)))
# 2. quantize the biases
BFixedPoint = FixedPoint(biases_quantization_params[k]['range'],
biases_quantization_params[k]['bit_width'], round_method=round_method,
round_strategy=round_strategy)
v[1].data[...] = BFixedPoint.quantize(v[1].data)
# step 6. feedforward the neural network
accuracy = np.zeros((2, iterations)) # 2: top1 & top5
print 'Fixed point CaffeNet starts inference...'
for i in range(iterations):
fixed_point_caffenet.forward()
if verbose:
print 'Batch %d/%d: Top-1 Acc: %.2f%%, Top-5 Acc: %.2f%%' % (i, iterations,
fixed_point_caffenet.blobs['acc_top1'].data*100.,
fixed_point_caffenet.blobs['acc_top5'].data*100.)
elif i % 10 == 0:
print 'Batch %d/%d: Top-1 Acc: %.2f%%, Top-5 Acc: %.2f%%' % (i, iterations,
fixed_point_caffenet.blobs['acc_top1'].data*100.,
fixed_point_caffenet.blobs['acc_top5'].data*100.)
accuracy[0, i] = fixed_point_caffenet.blobs['acc_top1'].data
accuracy[1, i] = fixed_point_caffenet.blobs['acc_top5'].data
# step 7. clean the network and file
del fixed_point_caffenet
os.unlink(f.name)
return accuracy
def quantization_param_wrapper(bit_width, data_range, round_method='FLOOR',
round_strategy='CONSERVATIVE'):
'''wrapper function to get the quantization parameters. Mainly supports for
singleton bit width.
'''
dict_bit_width = {}
if type(bit_width) is int:
for k in data_range:
dict_bit_width[k] = bit_width
elif type(bit_width) is dict or type(bit_width) is collections.OrderedDict:
dict_bit_width = bit_width
else:
raise TypeError('unexpected data type of bitwidth')
return convert_to_quantization_param(dict_bit_width, data_range,
round_method=round_method, round_strategy=round_strategy)
################################################################################
# deprecated function, never use
################################################################################
def determine_quantization_params(bit_width, data_range, round_method='FLOOR'):
'''helper function to determine the quantization parameters: integer, fraction, and
rounding method
- bit_width: integer or dictionary to support a fine granularity bit width
- data_range: dictionary for each layer's range
'''
data_quantization = {}
for k, v in data_range.items():
data_quantization[k] = np.zeros(2, dtype=np.int) # 2 quantization: I + F
max_abs_range = np.max(np.abs(v))
assert max_abs_range > 0, 'unexpected constant 0 range'
# integer: determined by the max absolute range @ index 0
data_quantization[k][0] = math.ceil(math.log(max_abs_range, 2))
# fraction: determined by the bit width & fraction width @ index 1
if type(bit_width) is int:
data_quantization[k][1] = bit_width - 1 - (data_quantization[k][0] > 0) * \
data_quantization[k][0]
elif type(bit_width) is dict:
data_quantization[k][1] = bit_width[k] - 1 - (data_quantization[k][0] > 0) * \
data_quantization[k][0]
#print 'set BW of %s to be %d' % (k, bit_width[k])
else:
raise TypeError("unexpected data type of bitwidth")
assert data_quantization[k][1] >= 0, 'not enough bit width'
# normalized to the quantization parameter format
return convert_to_quantization_param(data_quantization, round_method=round_method)