blob: 47e206303e996d7dae18c0064e3605821a98c06a [file] [log] [blame]
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, 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.
import argparse
import logging
import os
import time
import numpy as np
import mxnet as mx
from mxnet import nd
from mxnet.contrib.quantization import *
def download_dataset(dataset_url, dataset_dir, logger=None):
if logger is not None:
logger.info('Downloading dataset for inference from %s to %s' % (dataset_url, dataset_dir))
mx.test_utils.download(dataset_url, dataset_dir)
def load_model(symbol_file, param_file, logger=None):
cur_path = os.path.dirname(os.path.realpath(__file__))
symbol_file_path = os.path.join(cur_path, symbol_file)
if logger is not None:
logger.info('Loading symbol from file %s' % symbol_file_path)
symbol = mx.sym.load(symbol_file_path)
param_file_path = os.path.join(cur_path, param_file)
if logger is not None:
logger.info('Loading params from file %s' % param_file_path)
save_dict = nd.load(param_file_path)
arg_params = {}
aux_params = {}
for k, v in save_dict.items():
tp, name = k.split(':', 1)
if tp == 'arg':
arg_params[name] = v
if tp == 'aux':
aux_params[name] = v
return symbol, arg_params, aux_params
def advance_data_iter(data_iter, n):
assert n >= 0
if n == 0:
return data_iter
has_next_batch = True
while has_next_batch:
try:
data_iter.next()
n -= 1
if n == 0:
return data_iter
except StopIteration:
has_next_batch = False
def score(sym, arg_params, aux_params, data, devs, label_name, max_num_examples, logger=None):
metrics = [mx.metric.create('acc'),
mx.metric.create('top_k_accuracy', top_k=5)]
if not isinstance(metrics, list):
metrics = [metrics, ]
mod = mx.mod.Module(symbol=sym, context=devs, label_names=[label_name, ])
mod.bind(for_training=False,
data_shapes=data.provide_data,
label_shapes=data.provide_label)
mod.set_params(arg_params, aux_params)
tic = time.time()
num = 0
for batch in data:
mod.forward(batch, is_train=False)
for m in metrics:
mod.update_metric(m, batch.label)
num += batch_size
if max_num_examples is not None and num >= max_num_examples:
break
speed = num / (time.time() - tic)
if logger is not None:
logger.info('Finished inference with %d images' % num)
logger.info('Finished with %f images per second', speed)
logger.warn('Note: GPU performance is expected to be slower than CPU. Please refer quantization/README.md for details')
for m in metrics:
logger.info(m.get())
def benchmark_score(symbol_file, ctx, batch_size, num_batches, data_layer_type, logger=None):
# get mod
cur_path = os.path.dirname(os.path.realpath(__file__))
symbol_file_path = os.path.join(cur_path, symbol_file)
if logger is not None:
logger.info('Loading symbol from file %s' % symbol_file_path)
sym = mx.sym.load(symbol_file_path)
mod = mx.mod.Module(symbol=sym, context=ctx)
if data_layer_type == "int8":
dshape = mx.io.DataDesc(name='data', shape=(
batch_size,) + data_shape, dtype=np.int8)
elif data_layer_type == 'uint8':
dshape = mx.io.DataDesc(name='data', shape=(
batch_size,) + data_shape, dtype=np.uint8)
else: # float32
dshape = mx.io.DataDesc(name='data', shape=(
batch_size,) + data_shape, dtype=np.float32)
mod.bind(for_training=False,
inputs_need_grad=False,
data_shapes=[dshape])
mod.init_params(initializer=mx.init.Xavier(magnitude=2.))
# get data
if data_layer_type == "float32":
data = [mx.random.uniform(-1.0, 1.0, shape=shape, ctx=ctx, dtype=data_layer_type)
for _, shape in mod.data_shapes]
else:
data = [mx.nd.full(shape=shape, val=127, ctx=ctx, dtype=data_layer_type)
for _, shape in mod.data_shapes]
batch = mx.io.DataBatch(data, []) # empty label
# run
dry_run = 5 # use 5 iterations to warm up
for i in range(dry_run+num_batches):
if i == dry_run:
tic = time.time()
mod.forward(batch, is_train=False)
for output in mod.get_outputs():
output.wait_to_read()
# return num images per second
return num_batches*batch_size/(time.time() - tic)
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='Score a model on a dataset')
parser.add_argument('--ctx', type=str, default='gpu')
parser.add_argument('--benchmark', type=bool, default=False, help='dummy data benchmark')
parser.add_argument('--symbol-file', type=str, required=True, help='symbol file path')
parser.add_argument('--param-file', type=str, required=False, help='param file path')
parser.add_argument('--batch-size', type=int, default=32)
parser.add_argument('--label-name', type=str, default='softmax_label')
parser.add_argument('--dataset', type=str, required=False, help='dataset path')
parser.add_argument('--rgb-mean', type=str, default='0,0,0')
parser.add_argument('--rgb-std', type=str, default='1,1,1')
parser.add_argument('--image-shape', type=str, default='3,224,224')
parser.add_argument('--data-nthreads', type=int, default=60, help='number of threads for data decoding')
parser.add_argument('--num-skipped-batches', type=int, default=0, help='skip the number of batches for inference')
parser.add_argument('--num-inference-batches', type=int, required=True, help='number of images used for inference')
parser.add_argument('--shuffle-dataset', action='store_true', default=True,
help='shuffle the calibration dataset')
parser.add_argument('--shuffle-chunk-seed', type=int, default=3982304,
help='shuffling chunk seed, see'
' https://mxnet.incubator.apache.org/api/python/io/io.html?highlight=imager#mxnet.io.ImageRecordIter'
' for more details')
parser.add_argument('--shuffle-seed', type=int, default=48564309,
help='shuffling seed, see'
' https://mxnet.incubator.apache.org/api/python/io/io.html?highlight=imager#mxnet.io.ImageRecordIter'
' for more details')
parser.add_argument('--data-layer-type', type=str, default="float32",
choices=['float32', 'int8', 'uint8'],
help='data type for data layer')
args = parser.parse_args()
if args.ctx == 'gpu':
ctx = mx.gpu(0)
elif args.ctx == 'cpu':
ctx = mx.cpu(0)
else:
raise ValueError('ctx %s is not supported in this script' % args.ctx)
logging.basicConfig()
logger = logging.getLogger('logger')
logger.setLevel(logging.INFO)
symbol_file = args.symbol_file
param_file = args.param_file
data_nthreads = args.data_nthreads
batch_size = args.batch_size
logger.info('batch size = %d for inference' % batch_size)
rgb_mean = args.rgb_mean
logger.info('rgb_mean = %s' % rgb_mean)
rgb_mean = [float(i) for i in rgb_mean.split(',')]
mean_args = {'mean_r': rgb_mean[0], 'mean_g': rgb_mean[1], 'mean_b': rgb_mean[2]}
rgb_std = args.rgb_std
logger.info('rgb_std = %s' % rgb_std)
rgb_std = [float(i) for i in rgb_std.split(',')]
std_args = {'std_r': rgb_std[0], 'std_g': rgb_std[1], 'std_b': rgb_std[2]}
combine_mean_std = {}
combine_mean_std.update(mean_args)
combine_mean_std.update(std_args)
label_name = args.label_name
logger.info('label_name = %s' % label_name)
image_shape = args.image_shape
data_shape = tuple([int(i) for i in image_shape.split(',')])
logger.info('Input data shape = %s' % str(data_shape))
data_layer_type = args.data_layer_type
if args.benchmark == False:
dataset = args.dataset
download_dataset('http://data.mxnet.io/data/val_256_q90.rec', dataset)
logger.info('Dataset for inference: %s' % dataset)
# creating data iterator
if data_layer_type == 'int8':
data = mx.io.ImageRecordInt8Iter(path_imgrec=dataset,
label_width=1,
preprocess_threads=data_nthreads,
batch_size=batch_size,
data_shape=data_shape,
label_name=label_name,
rand_crop=False,
rand_mirror=False,
shuffle=args.shuffle_dataset,
shuffle_chunk_seed=args.shuffle_chunk_seed,
seed=args.shuffle_seed,
**combine_mean_std)
elif data_layer_type == 'uint8':
data = mx.io.ImageRecordUInt8Iter(path_imgrec=dataset,
label_width=1,
preprocess_threads=data_nthreads,
batch_size=batch_size,
data_shape=data_shape,
label_name=label_name,
rand_crop=False,
rand_mirror=False,
shuffle=args.shuffle_dataset,
shuffle_chunk_seed=args.shuffle_chunk_seed,
seed=args.shuffle_seed,
**combine_mean_std)
else: #float32
data = mx.io.ImageRecordIter(path_imgrec=dataset,
label_width=1,
preprocess_threads=data_nthreads,
batch_size=batch_size,
data_shape=data_shape,
label_name=label_name,
rand_crop=False,
rand_mirror=False,
shuffle=args.shuffle_dataset,
shuffle_chunk_seed=args.shuffle_chunk_seed,
seed=args.shuffle_seed,
**combine_mean_std)
# loading model
sym, arg_params, aux_params = load_model(symbol_file, param_file, logger)
# make sure that fp32 inference works on the same images as calibrated quantized model
logger.info('Skipping the first %d batches' % args.num_skipped_batches)
data = advance_data_iter(data, args.num_skipped_batches)
num_inference_images = args.num_inference_batches * batch_size
logger.info('Running model %s for inference' % symbol_file)
score(sym, arg_params, aux_params, data, [ctx], label_name,
max_num_examples=num_inference_images, logger=logger)
else:
logger.info('Running model %s for inference' % symbol_file)
speed = benchmark_score(symbol_file, ctx, batch_size, args.num_inference_batches, data_layer_type, logger)
logger.info('batch size %2d, image/sec: %f', batch_size, speed)