bgawrych commented on a change in pull request #19587:
URL: https://github.com/apache/incubator-mxnet/pull/19587#discussion_r536242416



##########
File path: example/quantization/imagenet_gen_qsym_mkldnn.py
##########
@@ -0,0 +1,274 @@
+# 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 re
+import sys
+from inspect import currentframe, getframeinfo
+
+import mxnet as mx
+from mxnet import gluon
+from mxnet.contrib.quantization import quantize_net
+from mxnet.gluon.data import DataLoader
+from mxnet.gluon.data.vision import transforms
+from mxnet.gluon.model_zoo.vision import get_model
+
+sys.path.append('../..')
+from tools.rec2idx import IndexCreator
+
+
+def download_calib_dataset(dataset_url, calib_dataset, logger=None):
+    if logger is not None:
+        logger.info('Downloading calibration dataset from %s to %s' % 
(dataset_url, calib_dataset))
+    mx.test_utils.download(dataset_url, calib_dataset)
+
+def get_from_gluon(model_name, classes=1000, logger=None):
+    dir_path = os.path.dirname(os.path.realpath(__file__))
+    model_path = os.path.join(dir_path, 'model')
+    if logger is not None:
+        logger.info('Converting model from Gluon-CV ModelZoo %s... into path 
%s' % (model_name, model_path))
+    net = get_model(name=model_name, classes=classes, pretrained=True)
+    prefix = os.path.join(model_path, model_name)
+    return net, prefix
+
+def regex_find_excluded_symbols(patterns_dict, model_name):
+    for key, value in patterns_dict.items():
+        if re.search(key, model_name) is not None:
+            return value
+    return None
+
+def get_exclude_symbols(model_name, exclude_first_conv):
+    # Grouped supported models at the time of commit:
+    # alexnet
+    # densenet121, densenet161
+    # densenet169, densenet201
+    # inceptionv3
+    # mobilenet0.25, mobilenet0.5, mobilenet0.75, mobilenet1.0,
+    # mobilenetv2_0.25, mobilenetv2_0.5, mobilenetv2_0.75, mobilenetv2_1.0
+    # resnet101_v1, resnet152_v1, resnet18_v1, resnet34_v1, resnet50_v1
+    # resnet101_v2, resnet152_v2, resnet18_v2, resnet34_v2, resnet50_v2
+    # squeezenet1.0, squeezenet1.1
+    # vgg11, vgg11_bn, vgg13, vgg13_bn, vgg16, vgg16_bn, vgg19, vgg19_bn
+    exclude_symbol_regex = {
+        'mobilenet[^v]': ['mobilenet_hybridsequential0_flatten0_flatten0', 
'mobilenet_hybridsequential0_globalavgpool2d0_fwd'],
+        'mobilenetv2': ['mobilenetv2_hybridsequential1_flatten0_flatten0'],
+        # 
resnetv2_hybridsequential0_hybridsequential0_bottleneckv20_batchnorm0_fwd is 
excluded for the sake of accuracy
+        'resnet.*v2': ['resnetv2_hybridsequential0_flatten0_flatten0', 
'resnetv2_hybridsequential0_hybridsequential0_bottleneckv20_batchnorm0_fwd'],
+        'squeezenet1': ['squeezenet_hybridsequential1_flatten0_flatten0'],
+    }
+    excluded_sym_names = regex_find_excluded_symbols(exclude_symbol_regex, 
model_name)
+    if excluded_sym_names is None:
+        excluded_sym_names = []
+    if exclude_first_conv:
+        first_conv_regex = {
+            'alexnet': ['alexnet_hybridsequential0_conv2d0_fwd'],
+            'densenet': ['densenet_hybridsequential0_conv2d0_fwd'],
+            'inceptionv3': 
['inception3_hybridsequential0_hybridsequential0_conv2d0_fwd'],
+            'mobilenet[^v]': ['mobilenet_hybridsequential0_conv2d0_fwd'],
+            'mobilenetv2': ['mobilenetv2_hybridsequential0_conv2d0_fwd'],
+            'resnet.*v1': ['resnetv1_hybridsequential0_conv2d0_fwd'],
+            'resnet.*v2': ['resnetv2_hybridsequential0_conv2d0_fwd'],
+            'squeezenet1': ['squeezenet_hybridsequential0_conv2d0_fwd'],
+            'vgg': ['vgg_hybridsequential0_conv2d0_fwd'],
+        }
+        excluded_first_conv_sym_names = 
regex_find_excluded_symbols(first_conv_regex, model_name)
+        if excluded_first_conv_sym_names is None:
+            raise ValueError('Currently, model %s is not supported in this 
script' % model_name)
+        excluded_sym_names += excluded_first_conv_sym_names
+    return excluded_sym_names
+
+
+
+if __name__ == '__main__':
+    parser = argparse.ArgumentParser(description='Generate a calibrated 
quantized model from a FP32 model with Intel MKL-DNN support')
+    parser.add_argument('--model', type=str, default='resnet50_v1',
+                        help='model to be quantized. If no-pretrained is set 
then'
+                             'model must be provided to `model` directory in 
the same path'
+                             'as this python script')
+    parser.add_argument('--epoch', type=int, default=0,
+                        help='number of epochs, default is 0')
+    parser.add_argument('--no-pretrained', action='store_true', default=False,
+                        help='If enabled, will not download pretrained model 
from MXNet or Gluon-CV modelzoo.')
+    parser.add_argument('--batch-size', type=int, default=32)
+    parser.add_argument('--calib-dataset', type=str, 
default='data/val_256_q90.rec',
+                        help='path of the calibration dataset')
+    parser.add_argument('--image-shape', type=str, default='3,224,224',
+                        help='number of channels, height and width of input 
image separated by comma')
+    parser.add_argument('--data-nthreads', type=int, default=0,
+                        help='number of threads for data loading')
+    parser.add_argument('--num-calib-batches', type=int, default=10,
+                        help='number of batches for calibration')
+    parser.add_argument('--exclude-first-conv', action='store_true', 
default=False,
+                        help='excluding quantizing the first conv layer since 
the'
+                             ' input data may have negative value which 
doesn\'t support at moment' )
+    parser.add_argument('--shuffle-dataset', action='store_true',
+                        help='shuffle the calibration dataset')
+    parser.add_argument('--calib-mode', type=str, default='entropy',
+                        help='calibration mode used for generating calibration 
table for the quantized symbol; supports'
+                             ' 1. none: no calibration will be used. The 
thresholds for quantization will be calculated'
+                             ' on the fly. This will result in inference speed 
slowdown and loss of accuracy'
+                             ' in general.'
+                             ' 2. naive: simply take min and max values of 
layer outputs as thresholds for'
+                             ' quantization. In general, the inference 
accuracy worsens with more examples used in'
+                             ' calibration. It is recommended to use `entropy` 
mode as it produces more accurate'
+                             ' inference results.'
+                             ' 3. entropy: calculate KL divergence of the fp32 
output and quantized output for optimal'
+                             ' thresholds. This mode is expected to produce 
the best inference accuracy of all three'
+                             ' kinds of calibration modes if the calibration 
dataset is representative enough of the'
+                             ' inference dataset.')
+    parser.add_argument('--quantized-dtype', type=str, default='auto',
+                        choices=['auto', 'int8', 'uint8'],
+                        help='quantization destination data type for input 
data')
+    parser.add_argument('--quiet', action='store_true', default=False,
+                        help='suppress most of log')
+    args = parser.parse_args()
+    ctx = mx.cpu(0)
+    logger = None
+
+    if not args.quiet:
+        logging.basicConfig()
+        logger = logging.getLogger('logger')
+        logger.setLevel(logging.INFO)
+
+    if logger:
+        logger.info(args)
+        logger.info('shuffle_dataset=%s' % args.shuffle_dataset)
+        logger.info('calibration mode set to %s' % args.calib_mode)
+
+    calib_mode = args.calib_mode
+
+    # download calibration dataset
+    if calib_mode != 'none':
+        idx_file_name = os.path.splitext(args.calib_dataset)[0] + '.idx'
+        if not os.path.isfile(idx_file_name):
+            
download_calib_dataset('http://data.mxnet.io/data/val_256_q90.rec', 
args.calib_dataset)
+            creator = IndexCreator(args.calib_dataset, idx_file_name)
+            creator.create_index()
+            creator.close()
+
+    # get image shape
+    image_shape = args.image_shape
+    data_shape = [(1,) + tuple(int(i) for i in image_shape.split(','))]
+
+    # check if directory for output model exists
+    dir_path = os.path.dirname(os.path.realpath(__file__))
+    dir_path = os.path.join(dir_path, 'model')
+    if not os.path.exists(dir_path):
+        os.mkdir(dir_path) # without try catch block as we expect to finish
+                           # script if it fail
+
+    # download model
+    if not args.no_pretrained:
+        if logger:
+            logger.info('Get pre-trained model from Gluon-CV modelzoo.')
+            logger.info('If you want to use custom model, please set 
--no-pretrained.')
+        net, prefix = get_from_gluon(model_name=args.model, classes=1000, 
logger=logger)
+        rgb_mean = '0.485,0.456,0.406'
+        rgb_std = '0.229,0.224,0.225'

Review comment:
       It's because DataIter, which was used with Module, probably doesn't 
scale input to network to range [0,1]. Now we're using gluon.DataLoader and 
we're using transforms.ToTensor() as one of the transformation. It's scales 
input values to range [0,1] - so mean and std values are recalculated old 
values to range [0,1] instead of [0, 255]




----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
[email protected]


Reply via email to