release commit
authorManu Mathew <a0393608@ti.com>
Fri, 24 Jan 2020 02:39:06 +0000 (08:09 +0530)
committerManu Mathew <a0393608@ti.com>
Fri, 24 Jan 2020 02:39:06 +0000 (08:09 +0530)
21 files changed:
README.md
docs/Calibration.md
docs/Quantization.md
examples/quantization_example.py
modules/pytorch_jacinto_ai/vision/models/pixel2pixel/deeplabv3lite.py
modules/pytorch_jacinto_ai/vision/models/pixel2pixel/fpn_pixel2pixel.py
modules/pytorch_jacinto_ai/vision/models/resnet.py
modules/pytorch_jacinto_ai/xnn/layers/conv_blocks.py
modules/pytorch_jacinto_ai/xnn/layers/deconv_blocks.py
modules/pytorch_jacinto_ai/xnn/layers/layer_config.py
modules/pytorch_jacinto_ai/xnn/quantize/quant_base_module.py
modules/pytorch_jacinto_ai/xnn/quantize/quant_test_module.py
modules/pytorch_jacinto_ai/xnn/quantize/quant_train_module.py
modules/pytorch_jacinto_ai/xnn/quantize/quant_train_utils.py
modules/pytorch_jacinto_ai/xnn/quantize/quant_utils.py
modules/pytorch_jacinto_ai/xnn/utils/__init__.py
modules/pytorch_jacinto_ai/xnn/utils/attr_dict.py
modules/pytorch_jacinto_ai/xnn/utils/weights_utils.py [new file with mode: 0644]
run_quantization.sh
run_quantization_example.sh
run_segmentation.sh

index 97bd27e8c141528df2e7587d307fed119a2d9353..b8dc37999d75cc125356fa7ffc44573571f2c97a 100644 (file)
--- a/README.md
+++ b/README.md
@@ -38,15 +38,15 @@ The following examples are currently available. Click on each of the links below
 * Object Detection<br>
     * Object Detection - coming soon..<br>
     * Object Keypoint Estimation - coming soon..<br>
-* Quantization<br>
-    * [**Quantization Aware Training**](docs/Quantization.md)<br>
+* [**Quantization**](docs/Quantization.md)<br>
+
 
 Some of the common training and validation commands are provided in shell scripts (.sh files) in the root folder.
 
 ## Additional Information
 For information on other similar devkits, please visit:<br> 
-- [https://github.com/TexasInstruments/jacinto-ai-devkit](https://github.com/TexasInstruments/jacinto-ai-devkit)<br> AND
-- [https://git.ti.com/jacinto-ai-devkit](https://git.ti.com/jacinto-ai-devkit)<br>
+- Landing Page: [https://github.com/TexasInstruments/jacinto-ai-devkit](https://github.com/TexasInstruments/jacinto-ai-devkit)<br>
+- Actual Git Repositories: [https://git.ti.com/jacinto-ai-devkit](https://git.ti.com/jacinto-ai-devkit)<br>
 
 ## Acknowledgements
 
index bbd339ba3aee1104691fa9335324d087e1f421ff..3aae54ecb36bdbe4766da26d089c99c13cbf8fe5 100644 (file)
@@ -1,6 +1,7 @@
 
-## Post Training Calibration (a.k.a. Calibration) For Quantization (this is not our recommended now)
-Note: Although Calibration methods for improving the Quantization accuracy are described in detail here, this is not our recommended method as this does not yield the best results in some cases. There are specific scenarios where Quantization Aware is not possible (eg. to import a floating point model for embedded inference) and Calibration is mainly meant for such scenarios. However, Calibration methods are improving at a rapid pace and it is possible that Calibration methods will be good enough for all cases in the future - but for now, you can go to the section on Quantization Aware Training as that is the recommended method.
+## Post Training Calibration For Quantization (a.k.a. Calibration)
+**Note: this is not our recommended method in PyTorch.**<br>
+Note: Post Training Calibration or simply Calibration is a method to reduce the accuracy loss with quantization. This is an approximate method and does not require ground truth or back-propagation - hence it is suitable for implementation in an Import/Calibration tool. We have simulated this Calibration scheme in PyTorch and can be used as fast method to improve the accuracy of Quantization. However, in a training frame work such as PyTorch, it is possible to get better accuracy with Quantization Aware Training - hence we suggest to use [Quantization Aware Training](./Quantization.md).
 
 Post Training Calibration & Quantization can take a model trained in floating point and with a few steps convert it to a model that is friendly for quantized inference. Compared to the alternative (Trained Quantization), the advantages of this method are:
 - Calibration is fast - a typical calibration finishes in a few minutes.
index d138d550eb37cddbed56a0bbfc6f8045b287a27c..c8b152f7c2256fd6d2c9778dd19275eb8ec6d82e 100644 (file)
@@ -1,14 +1,26 @@
 # Quantization
 
+## Introduction
 Quantization of a CNN model is the process of converting floating point data & operations to fixed point (integer). This includes quantization of weights, feature maps and all operations (including convolution). The quantization style used in this code is **power-of-2, symmetric quantization for both weights and activations**.
 
-Accuracy of inference can degrade if the CNN model is quantized to 8bits using simple methods and steps have to be taken to minimize this accuracy loss. One such method of reducing accuracy loss is "Quantization Aware Training". When Quantization Aware Training is incorporated into the training flow, the parameters of the model are adjusted to suit quantization. This includes adjusting of weights, biases and activation ranges.
+Accuracy of inference can degrade if the CNN model is quantized to 8bits using simple methods and steps have to be taken to minimize this accuracy loss. The parameters of the model need to be adjusted to suit quantization. This includes adjusting of weights, biases and activation ranges. This adjustment can be done as part of the Calibration or as part of Training.
 
-In order to make the activations quantization friendly, it is important to clip them during Quantization Aware Training. PACT2 activation function has been developed to clip the activations to a power-of-two value. PACT2 can be used in the place of commonly used activation functions such as ReLU or ReLU6.
+## Overview
+TI Deep Learning Library (TIDL) is a highly optimized runtime for Deep Learning Models on TI’s Jacinto7 TDA4x Devices (eg. TDA4VM). TIDL supports two kinds of Quantization schemes: (1) Post Training Calibration & Quantization, (2) Quantization Aware Training.<br>
+- Post Training Calibration & Quantization (Calibration): TIDL can accept a floating point model and Calibrate it with a few sample images. The Calibration is done during the import of the model. The current Calibration scheme is fairly simple  but there are plans to improve it substantially.<br>
+- Quantization Aware Training (QAT): This is needed if accuracy obtained with Calibration is not satisfactory (eg. Quantization Accuracy Drop >2%). QAT operates as a second phase after the initial training in floating point is done. We have provide this PyTorch Jacinto AI DevKit to enable QAT with PyTorch. There also a plan to make TensorFlow Jacinto AI DevKit available. Further Details are available at: [https://github.com/TexasInstruments/jacinto-ai-devkit](https://github.com/TexasInstruments/jacinto-ai-devkit)<br>
+
+## PACT2 activation function
+In order to make the activations quantization friendly, it is important to clip them during Quantization Aware Training. PACT2 activation function has been developed to clip the activations to a power-of-two value. PACT2 can be used in the place of commonly used activation functions such as ReLU or ReLU6. Our Quantization Aware Training method will automatically insert PACT2 activation functions wherver necessary to constraint the ranges of activations. The following is a block diagram of the PACT2:
 <p float="left"> <img src="quantization/pact2_activation.png" width="640" hspace="5"/> </p>
 We use statistical range clipping in PACT2 to improve the Quantized Accuracy (compared to simple min-max range clipping).
 
-## Quantization Aware Training (a.k.a. Trained Quantization)
+## Post Training Calibration For Quantization (a.k.a. Calibration)
+**Note: this is not our recommended method in PyTorch.**<br>
+Post Training Calibration or simply Calibration is a method to reduce the accuracy loss with quantization. This is an approximate method and does not require ground truth or back-propagation - hence it is suitable for implementation in an Import/Calibration tool. We have simulated that in PyTorch and can be used as fast method to improve the accuracy of Quantization. If you are interested, you can take a look at the [documentation of Calibration here](Calibration.md).<br>
+However, in a training frame work such as PyTorch, it is possible to get better accuracy with Quantization Aware Training and we recommend to use that (next section).
+
+## Quantization Aware Training (QAT)
 Quantization Aware Training (QAT) is easy to incorporate into an existing PyTorch training code. We provide a wrapper module called QuantTrainModule to automate all the tasks required for QAT. The user simply needs to wrap his model in QuantTrainModule and do the training.
 
 The overall flow of training is as follows:
@@ -61,9 +73,9 @@ torch.onnx.export(model.module, dummy_input, os.path.join(save_path,'model.onnx'
 
 As can be seen, it is easy to incorporate QuantTrainModule in your existing training code as the only thing required is to wrap your original model in QuantTrainModule. Careful attention needs to be given to how the parameters of the pretrained model is loaded and trained model is saved as shown in the above code snippet.
 
-Optional: We have provided a utility function called xnn.utils.load_weights() that prints which parameters are loaded correctly and which are not - you can use this load function if needed to ensure that your parameters are loaded correctly.
+Optional: We have provided a utility function called pytorch_jacinto_ai.xnn.utils.load_weights() that prints which parameters are loaded correctly and which are not - you can use this load function if needed to ensure that your parameters are loaded correctly.
 
-Example commands for trained quantization: 
+Example commands for QAT:
 ```
 python ./scripts/train_classification_main.py --dataset_name image_folder_classification --model_name mobilenetv2_tv_x1 --data_path ./data/datasets/image_folder_classification --pretrained https://download.pytorch.org/models/mobilenet_v2-b0353104.pth --batch_size 64 --quantize True --epochs 150 --epoch_size 1000 --lr 5e-5 --evaluate_start False
 ```
@@ -72,9 +84,6 @@ python ./scripts/train_classification_main.py --dataset_name image_folder_classi
 python ./scripts/train_segmentation_main.py --dataset_name cityscapes_segmentation --model_name deeplabv3lite_mobilenetv2_tv --data_path ./data/datasets/cityscapes/data --img_resize 384 768 --output_size 1024 2048 --gpus 0 1 --pretrained ./data/modelzoo/semantic_segmentation/cityscapes/deeplabv3lite-mobilenetv2/cityscapes_segmentation_deeplabv3lite-mobilenetv2_2019-06-26-08-59-32.pth --batch_size 8 --quantize True --epochs 150 --lr 5e-5 --evaluate_start False
 ```
 
-## Post Training Quantization (a.k.a. Calibration)
-We also have another method called Post Training Calibration or simply Calibration to reduce the accuracy loss with quantization. If you are interested, you can take a look at the [documentation of Calibration here](Calibration.md) - but that is not our recommend method now.
-
 
 ## Important Notes - read carefully
 - **Multi-GPU training/calibration/validation with DataParallel is not yet working with our quantization modules** QuantTrainModule/QuantCalibrateModule/QuantTestModule. We recommend not to wrap the modules in DataParallel if you are training/calibrating/testing with quantization - i.e. if your model is wrapped in QuantTrainModule/QuantCalibrateModule/QuantTestModule.<br>
@@ -86,15 +95,15 @@ We also have another method called Post Training Calibration or simply Calibrati
 
 ## Results
 
-The table below shows the Quantized Accuracy with various Calibration and methods and also Trained Quantization. Some of the commands used to generate these results are summarized in the file **run_quantization.sh** for convenience.
+The table below shows the Quantized Accuracy with various Calibration and methods and also QAT. Some of the commands used to generate these results are summarized in the file **run_quantization.sh** for convenience.
 
 ###### Dataset: ImageNet Classification (Image Classification)
 
-|Mode Name               |Backbone   |Stride|Resolution|Float Acc%|Simple Calib Acc%|Advanced Calib Acc%|Advanced DW Calib Acc%|Trained Quant Acc%|
-|----------              |-----------|------|----------|--------- |---              |---                |---                   |---               |
-|ResNet50(TorchVision)   |ResNet50   |32    |224x224   |**76.15** |75.56            |**75.56**          |75.56                 |                  |
-|MobileNetV2(TorchVision)|MobileNetV2|32    |224x224   |**71.89** |67.77            |**68.39**          |69.34                 |**70.55**         |
-|MobileNetV2(Shicai)     |MobileNetV2|32    |224x224   |**71.44** |45.60            |**68.81**          |70.65                 |                  |
+|Mode Name               |Backbone   |Stride|Resolution|Float Acc%|Simple Calib Acc%|Adv Calib Acc%|Adv DW Calib Acc%|QAT Acc%    |Acc Drop - Adv Calib|Acc Drop - QAT|
+|----------              |-----------|------|----------|--------- |---              |---           |---              |---         |---                 |---           |
+|ResNet50(TorchVision)   |ResNet50   |32    |224x224   |**76.15** |75.56            |**75.56**     |75.56            |**76.05**   |-0.59               |-0.10         |
+|MobileNetV2(TorchVision)|MobileNetV2|32    |224x224   |**71.89** |67.77            |**68.39**     |69.34            |**70.74**   |-4.26               |-1.34         |
+|MobileNetV2(Shicai)     |MobileNetV2|32    |224x224   |**71.44** |0.0              |**68.81**     |70.65            |**70.54**   |-2.63               |-0.9          |
 
 Notes:
 - For Image Classification, the accuracy measure used is % Top-1 Classification Accuracy. 'Top-1 Classification Accuracy' is abbreviated by Acc in the above table.<br>
@@ -102,13 +111,21 @@ Notes:
 
 ###### Dataset: Cityscapes Segmentation (Semantic Segmentation)
 
-|Mode Name               |Backbone   |Stride|Resolution|Float Acc%|Simple Calib Acc%|Advanced Calib Acc%|Advanced DW Calib Acc%|Trained Quant Acc%|
-|----------              |-----------|------|----------|----------|---              |---                |---                   |---               |
-|DeepLabV3Lite           |MobileNetV2|16    |768x384   |**69.13** |61.71            |**67.95**          |68.47                 |**68.26**         |
+|Mode Name               |Backbone   |Stride|Resolution|Float Acc%|Simple Calib Acc%|Adv Calib Acc%|Adv DW Calib Acc%|QAT Acc%|Acc Drop - Advanced Calib|Acc Drop - QAT|
+|----------              |-----------|------|----------|----------|---              |---           |---              |---               |---            |---           |
+|DeepLabV3Lite           |MobileNetV2|16    |768x384   |**69.13** |61.71            |**67.95**     |68.47            |**68.44**         |-1.18          |-0.69         |
+
+Note: For Semantic Segmentation, the accuracy measure used in MeanIoU Accuracy. 'MeanIoU Accuracy' is abbreviated by Acc in the above table.
+
+**Terminology:**
+- Simple Calib: Calibration based on min/max ranges
+- Adv Calib: Includes histogram based ranges, calibration of weight/bias parameters to compensate for quantization accuracy loss.
+- Adv DW Calib: Also include Per-Channel Weight Quantization for Depthwise layers
+- QAT: Quantization Aware Training with PyTorch Jacinto AI DevKit
 
-Notes: 
- - For Semantic Segmentation, the accuracy measure used in MeanIoU Accuracy. 'MeanIoU Accuracy' is abbreviated by Acc in the above table.
-<br>
+**Conclusion based on Simulation Results:**<br>
+- Advanced Calibration Methods may have >2% Accuracy Drop in some cases.
+- Quantization Aware Training (QAT) is consistently able to produce <2% Accuracy drop.
 
 
 ## References 
index 7d5e5488da26f9c9e6e43d429311ae56a1e38b88..91dac7325bffe7b29844ef547c23af6b05a0eb17 100644 (file)
@@ -1,4 +1,9 @@
-# this code is modified from: https://github.com/pytorch/examples/blob/master/imagenet/main.py
+# ----------------------------------
+# Quantization Aware Training (QAT) Example
+# Texas Instruments (C) 2018-2020
+# All Rights Reserved
+# ----------------------------------
+# this original code is from: https://github.com/pytorch/examples/blob/master/imagenet/main.py
 # the changes required for quantizing the model are under the flag args.quantize
 import argparse
 import os
@@ -43,23 +48,27 @@ parser.add_argument('-j', '--workers', default=4, type=int, metavar='N',
                     help='number of data loading workers (default: 4)')
 parser.add_argument('--epochs', default=90, type=int, metavar='N',
                     help='number of total epochs to run')
-parser.add_argument('--epoch-size', default=0, type=int, metavar='N',
+parser.add_argument('--epoch_size', default=0, type=int, metavar='N',
                     help='number of iterations in one training epoch. 0 (default) means full training epoch')
-parser.add_argument('--start-epoch', default=0, type=int, metavar='N',
+parser.add_argument('--epoch_size_val', default=0, type=int, metavar='N',
+                    help='number of iterations in one validation epoch. 0 (default) means full validation epoch')
+parser.add_argument('--start_epoch', default=0, type=int, metavar='N',
                     help='manual epoch number (useful on restarts)')
-parser.add_argument('-b', '--batch-size', default=256, type=int,
+parser.add_argument('-b', '--batch_size', default=256, type=int,
                     metavar='N',
                     help='mini-batch size (default: 256), this is the total '
                          'batch size of all GPUs on the current node when '
                          'using Data Parallel or Distributed Data Parallel')
-parser.add_argument('--lr', '--learning-rate', default=0.1, type=float,
+parser.add_argument('--lr', '--learning_rate', default=0.1, type=float,
                     metavar='LR', help='initial learning rate', dest='lr')
+parser.add_argument('--lr_step_size', default=30, type=int,
+                    metavar='N', help='number of steps before learning rate is reduced')
 parser.add_argument('--momentum', default=0.9, type=float, metavar='M',
                     help='momentum')
-parser.add_argument('--wd', '--weight-decay', default=1e-4, type=float,
+parser.add_argument('--wd', '--weight_decay', default=1e-4, type=float,
                     metavar='W', help='weight decay (default: 1e-4)',
                     dest='weight_decay')
-parser.add_argument('-p', '--print-freq', default=100, type=int,
+parser.add_argument('-p', '--print_freq', default=100, type=int,
                     metavar='N', help='print frequency (default: 10)')
 parser.add_argument('--resume', default='', type=str, metavar='PATH',
                     help='path to latest checkpoint (default: none)')
@@ -67,23 +76,25 @@ parser.add_argument('-e', '--evaluate', dest='evaluate', action='store_true',
                     help='evaluate model on validation set')
 parser.add_argument('--pretrained', type=str, default=None,
                     help='use pre-trained model')
-parser.add_argument('--world-size', default=-1, type=int,
+parser.add_argument('--world_size', default=-1, type=int,
                     help='number of nodes for distributed training')
 parser.add_argument('--rank', default=-1, type=int,
                     help='node rank for distributed training')
-parser.add_argument('--dist-url', default='tcp://224.66.41.62:23456', type=str,
+parser.add_argument('--dist_url', default='tcp://224.66.41.62:23456', type=str,
                     help='url used to set up distributed training')
-parser.add_argument('--dist-backend', default='nccl', type=str,
+parser.add_argument('--dist_backend', default='nccl', type=str,
                     help='distributed backend')
 parser.add_argument('--seed', default=None, type=int,
                     help='seed for initializing training. ')
 parser.add_argument('--gpu', default=None, type=int,
                     help='GPU id to use.')
-parser.add_argument('--multiprocessing-distributed', action='store_true',
+parser.add_argument('--multiprocessing_distributed', action='store_true',
                     help='Use multi-processing distributed training to launch '
                          'N processes per node, which has N GPUs. This is the '
                          'fastest way to use PyTorch for either single node or '
                          'multi node data parallel training')
+parser.add_argument('--save_path', type=str, default='./data/checkpoints/quantization',
+                    help='path to save the logs and models')
 parser.add_argument('--quantize', action='store_true',
                     help='Enable Quantization')
 best_acc1 = 0
@@ -92,6 +103,8 @@ best_acc1 = 0
 def main():
     args = parser.parse_args()
 
+    args.cur_lr = args.lr
+
     if args.seed is not None:
         random.seed(args.seed)
         torch.manual_seed(args.seed)
@@ -235,7 +248,7 @@ def main_worker(gpu, ngpus_per_node, args):
         transforms.Compose([
             transforms.RandomResizedCrop(224),
             transforms.RandomHorizontalFlip(),
-            xvision.transforms.ToFloat(),
+            xvision.transforms.ToFloat(),   # converting to float avoids the division by 255 in ToTensor()
             transforms.ToTensor(),
             normalize,
         ]))
@@ -253,7 +266,7 @@ def main_worker(gpu, ngpus_per_node, args):
         datasets.ImageFolder(valdir, transforms.Compose([
             transforms.Resize(256),
             transforms.CenterCrop(224),
-            xvision.transforms.ToFloat(),
+            xvision.transforms.ToFloat(),   # converting to float avoids the division by 255 in ToTensor()
             transforms.ToTensor(),
             normalize,
         ])),
@@ -284,16 +297,18 @@ def main_worker(gpu, ngpus_per_node, args):
         model_orig = model_orig.module if args.quantize else model_orig
         if not args.multiprocessing_distributed or (args.multiprocessing_distributed
                 and args.rank % ngpus_per_node == 0):
-            out_basename = args.arch
-            out_basename += ('_quantized_checkpoint.pth.tar' if args.quantize else '_checkpoint.pth.tar')
-            save_filename = os.path.join('./data/checkpoints/quantization', out_basename)
-            save_checkpoint({
+            out_basename = args.arch + ('_checkpoint_quantized.pth' if args.quantize else '_checkpoint.pth')
+            save_filename = os.path.join(args.save_path, out_basename)
+            checkpoint_dict = {
                 'epoch': epoch + 1,
                 'arch': args.arch,
                 'state_dict': model_orig.state_dict(),
                 'best_acc1': best_acc1,
                 'optimizer' : optimizer.state_dict(),
-            }, is_best, filename=save_filename)
+            }
+            save_checkpoint(checkpoint_dict, is_best, filename=save_filename)
+            save_onnxname = os.path.splitext(save_filename)[0]+'.onnx'
+            write_onnx_model(model, is_best, filename=save_onnxname)
 
 
 def train(train_loader, model, criterion, optimizer, epoch, args):
@@ -341,7 +356,7 @@ def train(train_loader, model, criterion, optimizer, epoch, args):
         end = time.time()
 
         if i % args.print_freq == 0:
-            progress.display(i)
+            progress.display(i, args.cur_lr)
 
 
 def validate(val_loader, model, criterion, args):
@@ -360,6 +375,9 @@ def validate(val_loader, model, criterion, args):
     with torch.no_grad():
         end = time.time()
         for i, (images, target) in enumerate(val_loader):
+            # break the epoch at at the iteration epoch_size_val
+            if args.epoch_size_val != 0 and i >= args.epoch_size_val:
+                break
             images = images.cuda(args.gpu, non_blocking=True)
             target = target.cuda(args.gpu, non_blocking=True)
 
@@ -378,7 +396,7 @@ def validate(val_loader, model, criterion, args):
             end = time.time()
 
             if i % args.print_freq == 0:
-                progress.display(i)
+                progress.display(i, args.cur_lr)
 
         # TODO: this should also be done with the ProgressMeter
         print(' * Acc@1 {top1.avg:.3f} Acc@5 {top5.avg:.3f}'
@@ -387,12 +405,27 @@ def validate(val_loader, model, criterion, args):
     return top1.avg
 
 
-def save_checkpoint(state, is_best, filename='checkpoint.pth.tar'):
+def save_checkpoint(state, is_best, filename='checkpoint.pth'):
     dirname = os.path.dirname(filename)
     xnn.utils.makedir_exist_ok(dirname)
     torch.save(state, filename)
     if is_best:
-        shutil.copyfile(filename, os.path.splitext(filename)[0]+'_best.pth.tar')
+        shutil.copyfile(filename, os.path.splitext(filename)[0]+'_best.pth')
+
+
+def create_rand_inputs(is_cuda):
+    dummy_input = torch.rand((1, 3, 224, 224))
+    dummy_input = dummy_input.cuda() if is_cuda else dummy_input
+    return dummy_input
+
+
+def write_onnx_model(model, is_best, filename='checkpoint.onnx'):
+    model.eval()
+    is_cuda = next(model.parameters()).is_cuda
+    dummy_input = create_rand_inputs(is_cuda)
+    torch.onnx.export(model, dummy_input, filename, export_params=True, verbose=False)
+    if is_best:
+        shutil.copyfile(filename, os.path.splitext(filename)[0]+'_best.onnx')
 
 
 class AverageMeter(object):
@@ -422,11 +455,12 @@ class AverageMeter(object):
 class ProgressMeter(object):
     def __init__(self, num_batches, meters, prefix=""):
         self.batch_fmtstr = self._get_batch_fmtstr(num_batches)
+        self.lr_fmtstr = self._get_lr_fmtstr()
         self.meters = meters
         self.prefix = prefix
 
-    def display(self, batch):
-        entries = [self.prefix + self.batch_fmtstr.format(batch)]
+    def display(self, batch, cur_lr):
+        entries = [self.prefix + self.batch_fmtstr.format(batch), self.lr_fmtstr.format(cur_lr)]
         entries += [str(meter) for meter in self.meters]
         print('\t'.join(entries))
 
@@ -435,10 +469,14 @@ class ProgressMeter(object):
         fmt = '{:' + str(num_digits) + 'd}'
         return '[' + fmt + '/' + fmt.format(num_batches) + ']'
 
+    def _get_lr_fmtstr(self):
+        fmt = 'LR {:g}'
+        return fmt
 
 def adjust_learning_rate(optimizer, epoch, args):
     """Sets the learning rate to the initial LR decayed by 10 every 30 epochs"""
-    lr = args.lr * (0.1 ** (epoch // 30))
+    lr = args.lr * (0.1 ** (epoch // args.lr_step_size))
+    args.cur_lr = lr
     for param_group in optimizer.param_groups:
         param_group['lr'] = lr
 
index 2252e385ade719a7c839b8a3d3db75dbc2cb62f1..ea43dfc2f1696101c24caeff799c0ae67a37f82d 100644 (file)
@@ -11,8 +11,9 @@ from ..multi_input_net import MobileNetV2TVMI4, MobileNetV2EricsunMI4, ResNet50M
 
 ###########################################
 __all__ = ['DeepLabV3Lite', 'DeepLabV3LiteDecoder',
-           'deeplabv3lite_mobilenetv2_tv', 'deeplabv3lite_mobilenetv2_ericsun',
-           'deeplabv3lite_resnet50']
+           'deeplabv3lite_mobilenetv2_tv', 'deeplabv3lite_mobilenetv2_tv_fd',
+           'deeplabv3lite_mobilenetv2_ericsun',
+           'deeplabv3lite_resnet50', 'deeplabv3lite_resnet50_p5', 'deeplabv3lite_resnet50_p5_fd']
 
 
 ###########################################
@@ -40,20 +41,20 @@ class DeepLabV3LiteDecoder(torch.nn.Module):
 
         self.decoder_channels = merged_channels = (current_channels+model_config.shortcut_out)
 
-        # prediction
-        if self.model_config.final_prediction:
-            ConvXWSepBlock = xnn.layers.ConvGWSepNormAct2d if model_config.groupwise_sep else xnn.layers.ConvDWSepNormAct2d
-            final_activation = xnn.layers.get_fixed_pact2(output_range = model_config.output_range) \
-                    if (model_config.output_range is not None) else False
-            self.pred = ConvXWSepBlock(merged_channels, model_config.output_channels, kernel_size=3, normalization=((not model_config.linear_dw),False),
-                activation=(False,final_activation), groups = 1)
+        upstride1 = model_config.shortcut_strides[-1]//model_config.shortcut_strides[0]
+        self.upsample1 = xnn.layers.UpsampleGenericTo(decoder_channels, decoder_channels, upstride1, model_config.interpolation_type, model_config.interpolation_mode)
 
         self.cat = xnn.layers.CatBlock()
 
-        upstride1 = model_config.shortcut_strides[-1]//model_config.shortcut_strides[0]
-        upstride2 = model_config.shortcut_strides[0]
-        self.upsample1 = xnn.layers.UpsampleTo(decoder_channels, decoder_channels, upstride1, model_config.interpolation_type, model_config.interpolation_mode)
-        self.upsample2 = xnn.layers.UpsampleTo(model_config.output_channels, model_config.output_channels, upstride2, model_config.interpolation_type, model_config.interpolation_mode)
+        # prediction
+        if self.model_config.final_prediction:
+            ConvXWSepBlock = xnn.layers.ConvGWSepNormAct2d if model_config.groupwise_sep else xnn.layers.ConvDWSepNormAct2d
+            final_activation = xnn.layers.get_fixed_pact2(output_range = model_config.output_range) if (model_config.output_range is not None) else False
+            self.pred = ConvXWSepBlock(merged_channels, model_config.output_channels, kernel_size=3, normalization=((not model_config.linear_dw),False), activation=(False,final_activation), groups=1)
+            if self.model_config.final_upsample:
+                upstride2 = model_config.shortcut_strides[0]
+                self.upsample2 = xnn.layers.UpsampleGenericTo(model_config.output_channels, model_config.output_channels, upstride2, model_config.interpolation_type, model_config.interpolation_mode)
+            #
 
     # the upsampling is using functional form to support size based upsampling for odd sizes
     # that are not a perfect ratio (eg. 257x513), which seem to be popular for segmentation
@@ -120,7 +121,10 @@ def get_config_deeplav3lite_mnv2():
     model_config.normalize_input = False
     model_config.split_outputs = False
     model_config.use_aspp = True
+    model_config.fastdown = False
+
     model_config.strides = (2,2,2,2,1)
+    model_config.fastdown = False
     model_config.groupwise_sep = False
     encoder_stride = np.prod(model_config.strides)
     model_config.shortcut_strides = (4,encoder_stride)
@@ -171,6 +175,17 @@ def deeplabv3lite_mobilenetv2_tv(model_config, pretrained=None):
     return model, change_names_dict
 
 
+def deeplabv3lite_mobilenetv2_tv_fd(model_config, pretrained=None):
+    model_config = get_config_deeplav3lite_mnv2().merge_from(model_config)
+    model_config.fastdown = True
+    model_config.strides = (2,2,2,2,1)
+    model_config.shortcut_strides = (8,32)
+    model_config.shortcut_channels = (24,320)
+    model_config.decoder_chan = 256
+    model_config.aspp_chan = 256
+    return deeplabv3lite_mobilenetv2_tv(model_config, pretrained=pretrained)
+
+
 def deeplabv3lite_mobilenetv2_ericsun(model_config, pretrained=None):
     model_config = get_config_deeplav3lite_mnv2().merge_from(model_config)
     # encoder setup
@@ -243,3 +258,17 @@ def deeplabv3lite_resnet50(model_config, pretrained=None):
         model = xnn.utils.load_weights(model, pretrained, change_names_dict, ignore_size=True, verbose=True)
 
     return model, change_names_dict
+
+
+def deeplabv3lite_resnet50_p5(model_config, pretrained=None):
+    model_config.width_mult = 0.5
+    model_config.shortcut_channels = (128,1024)
+    return deeplabv3lite_resnet50(model_config, pretrained=pretrained)
+
+
+def deeplabv3lite_resnet50_p5_fd(model_config, pretrained=None):
+    model_config.width_mult = 0.5
+    model_config.fastdown = True
+    model_config.shortcut_channels = (128,1024)
+    model_config.shortcut_strides = (8,64)
+    return deeplabv3lite_resnet50(model_config, pretrained=pretrained)
\ No newline at end of file
index b24a204e07700fec2b093520b2fc2b4a32245f9b..4b13855dcc470719f149a5121b3ec9d6618837f9 100644 (file)
@@ -41,6 +41,7 @@ def get_config_fpnp2p_mnv2():
 
     model_config.final_prediction = True
     model_config.final_upsample = True
+    model_config.output_range = None
 
     model_config.normalize_input = False
     model_config.split_outputs = False
@@ -132,9 +133,6 @@ class FPNPixel2PixelDecoder(torch.nn.Module):
         self.output_type = model_config.output_type
         self.decoder_channels = decoder_channels = round(self.model_config.decoder_chan*self.model_config.decoder_factor)
 
-        upstride_final = self.model_config.shortcut_strides[0]
-        self.upsample = xnn.layers.UpsampleTo(model_config.output_channels, model_config.output_channels, upstride_final, model_config.interpolation_type, model_config.interpolation_mode)
-
         self.rfblock = None
         if self.model_config.use_aspp:
             current_channels = self.model_config.shortcut_channels[-1]
@@ -161,7 +159,13 @@ class FPNPixel2PixelDecoder(torch.nn.Module):
 
         # prediction
         if self.model_config.final_prediction:
-            self.pred = xnn.layers.ConvDWSepNormAct2d(current_channels, self.model_config.output_channels, kernel_size=3, normalization=(True,False), activation=(False,False))
+            final_activation = xnn.layers.get_fixed_pact2(output_range = model_config.output_range) if (model_config.output_range is not None) else False
+            self.pred = xnn.layers.ConvDWSepNormAct2d(current_channels, self.model_config.output_channels, kernel_size=3, normalization=(True,False), activation=(False,final_activation))
+
+            if self.model_config.final_upsample:
+                upstride_final = self.model_config.shortcut_strides[0]
+                self.upsample = xnn.layers.UpsampleTo(model_config.output_channels, model_config.output_channels, upstride_final, model_config.interpolation_type, model_config.interpolation_mode)
+            #
         #
 
     def forward(self, x_input, x, x_list):
index 18d0f8edd729098bc01b3c32333b760782b079a8..788fd8b46a51a47ea97cbd38734feb78f68e5457 100644 (file)
@@ -382,6 +382,6 @@ def resnet50_with_model_config(model_config, pretrained=None):
     model_config = get_config().merge_from(model_config)
     model = resnet50(input_channels=model_config.input_channels, strides=model_config.strides,
                      num_classes=model_config.num_classes, pretrained=pretrained,
-                     fastdown=model_config.fastdown)
+                     width_mult=model_config.width_mult, fastdown=model_config.fastdown)
     return model
 
index f5fdac90119458d01c8e0d8354c0877ade7ccbe6..1f9ce2728a595d5b680cfe1306067c6dbebda88e 100644 (file)
@@ -2,11 +2,12 @@ import torch
 from .layer_config import *
 from . import functional
 
+
 ############################################################### 
 def ConvLayer2d(in_planes, out_planes, kernel_size, stride=1, groups=1, dilation=1, padding=None, bias=False):
     """convolution with padding"""
     padding = padding if padding else ((kernel_size-1)//2)*dilation
-    return torch.nn.Conv2d(in_planes, out_planes, kernel_size=kernel_size, stride=stride, dilation=dilation, padding=padding, bias=bias, groups=groups)
+    return DefaultConv2d(in_planes, out_planes, kernel_size=kernel_size, stride=stride, dilation=dilation, padding=padding, bias=bias, groups=groups)
 
 
 def ConvDWLayer2d(in_planes, out_planes, stride=1, dilation=1, kernel_size=None, bias=False):
@@ -30,7 +31,7 @@ def ConvNormAct2d(in_planes, out_planes, kernel_size=None, stride=1, groups=1, d
     if normalization is True:
         normalization = DefaultNorm2d
 
-    layers = [torch.nn.Conv2d(in_planes, out_planes, kernel_size=kernel_size, stride=stride, dilation=dilation, padding=padding, bias=bias, groups=groups)]
+    layers = [DefaultConv2d(in_planes, out_planes, kernel_size=kernel_size, stride=stride, dilation=dilation, padding=padding, bias=bias, groups=groups)]
     if normalization:
         layers.append(normalization(out_planes))
 
index acfc4131024801227cf9e6df9bd64fcd850c5953..e91c95884d0fea47951795c32bf8f8cfaccb3821 100644 (file)
@@ -19,6 +19,31 @@ def UpsampleTo(input_channels, output_channels, upstride, interpolation_type, in
     return upsample
 
 
+class UpsampleGenericTo(torch.nn.Module):
+    def __init__(self, input_channels, output_channels, upstride, interpolation_type, interpolation_mode):
+        super().__init__()
+        self.upsample_list = torch.nn.ModuleList()
+        self.upstride_list = []
+        while upstride >= 2:
+            upstride_layer = 4 if upstride > 4 else upstride
+            upsample = UpsampleTo(input_channels, output_channels, upstride_layer, interpolation_type, interpolation_mode)
+            self.upsample_list.append(upsample)
+            self.upstride_list.append(upstride_layer)
+            upstride = upstride//4
+
+    def forward(self, x):
+        assert isinstance(x, (list,tuple)) and len(x)==2, 'input must be a tuple/list of size 2'
+        x, x_target = x
+        xt_shape = x.shape
+        for idx, (upsample, upstride) in enumerate(zip(self.upsample_list,self.upstride_list)):
+            xt_shape = (xt_shape[0], xt_shape[1], xt_shape[2]*upstride, xt_shape[3]*upstride)
+            xt = torch.zeros(xt_shape).to(x.device)
+            x = upsample((x, xt))
+            xt_shape = x.shape
+        #
+        return x
+
+
 ############################################################### 
 def DeConvLayer2d(in_planes, out_planes, kernel_size, stride=1, groups=1, dilation=1, padding=None, output_padding=None, bias=False):
     """convolution with padding"""
index ad6fccd04a6ce1f694d8093f972063c4f780f770..e1ced5c7df02f337bd30294834afd899eeccb9f8 100644 (file)
@@ -1,6 +1,10 @@
 import torch
 from .normalization import *
 
+# optional/experimental
+try: from .conv_ws_internal import *
+except: pass
+
 
 ###############################################################
 #class NoTrackBatchNorm2d(torch.nn.BatchNorm2d):
@@ -22,3 +26,7 @@ DefaultNorm2d = torch.nn.BatchNorm2d #SlowBatchNorm2d #Group8Norm
 ###############################################################
 DefaultAct2d = torch.nn.ReLU #torch.nn.ReLU6 #torch.nn.HardTanh ##pytorch_jacinto_ai.xnn.layers.ReLU8
 
+###############################################################
+# Default Convolution: torch.nn.Conv2d or ConvWS2d
+###############################################################
+DefaultConv2d = torch.nn.Conv2d
\ No newline at end of file
index e92e4f1d7f277a6d7fdff9c9834a3f074453a210..48a6fca4fa5673d93ad6e834e844356c67c84a29 100644 (file)
@@ -10,9 +10,6 @@ class QuantEstimationType:
 
 # base module to be use for all quantization modules
 class QuantBaseModule(QuantGraphModule):
-    WEIGHTS_CLAMP_VALUE = 31.0
-    WEIGHTS_CLAMP_RATIO = 32.0
-
     def __init__(self, module):
         super().__init__(module)
 
index abb58fca779d4f6da494ad0b1cc2126e78258a5c..5814f025cb1e7836b0930616b86a707d224e2dfe 100644 (file)
@@ -155,7 +155,7 @@ class QuantTestModule(QuantBaseModule):
 
 
     # implement this in a derived class to clamp weights
-    def clamp_weights(self, module):
+    def constrain_weights(self, module):
         pass
 
 
@@ -330,7 +330,7 @@ class QuantTestModule(QuantBaseModule):
 
 
     def quantize_weights(self, module, tensor_in, qrange):
-        self.clamp_weights(module)
+        self.constrain_weights(module)
 
         bitwidth_weights = self.get_bitwidth_weights(module)
         with torch.no_grad():
@@ -534,7 +534,7 @@ class QuantTestModule(QuantBaseModule):
 #         # bias correction update factor
 #         self.max_calibration_repeats = max(30//self.range_calibration_offline_iters,1)
 #         self.bias_calibration_factor = 1.0/(self.range_calibration_offline_iters*self.max_calibration_repeats)
-#         self.clamp_weights_enabled = bias_calibration #and (not per_channel_q)
+#         self.constrain_weights_enabled = bias_calibration #and (not per_channel_q)
 #         self.adjust_bias_now = False
 #         # TODO: it may be better to start bias adjustment after the activation range update has stabilized
 #         self.bias_calibration_start = 0
@@ -568,22 +568,12 @@ class QuantTestModule(QuantBaseModule):
 #     #
 #
 #
-#     def clamp_weights(self, module):
-#         if not self.clamp_weights_enabled:
+#     def constrain_weights(self, module):
+#         if not self.constrain_weights_enabled:
 #             return
 #         #
-#         # bias correction cannot correct for very large errors. clamp the weights.
-#         weights_clamp_value = QuantBaseModule.WEIGHTS_CLAMP_VALUE
-#         if weights_clamp_value is None:
-#             mn, mx = utils.extrema_fast(module.weight.data, percentile_range_shrink=25.0); clip_val = max(abs(float(mn)), abs(float(mx))); clip_thr = max(clip_val, weights_clamp_value)
-#             # mn, mx = self.wt_mse_based_clip(module.weight.data); clip_thr = max(abs(float(mn)), abs(float(mx)))
-#         else:
-#             clip_thr = weights_clamp_value
-#         #
-#         # clip the weights using the threshold.
-#         clampled_weights = torch.clamp(module.weight.data, -clip_thr, clip_thr)
-#         module.weight.data.copy_(clampled_weights)
-#         #
+#         constrained_weight = constrain_weight(module.weight.data)
+#         module.weight.data.copy_(constrained_weight.data)
 #     #
 #
 #
index 2bbdf5308456841513d68dca370da5f539e8c094..fc94ca777ff6572acfb60f6dd33e0829f3105b21 100644 (file)
@@ -18,13 +18,13 @@ warnings.filterwarnings('ignore', category=torch.jit.TracerWarning)
 ###########################################################
 class QuantTrainModule(QuantBaseModule):
     def __init__(self, module, bitwidth_weights=8, bitwidth_activations=8, per_channel_q=False, histogram_range=True,
-                 clamp_weights=True, bias_calibration=False, dummy_input=None):
+                 constrain_weights=True, bias_calibration=False, dummy_input=None):
         super().__init__(module)
         assert dummy_input is not None, 'dummy input is needed by quantized models to analyze graph'
         self.bitwidth_weights = bitwidth_weights
         self.bitwidth_activations = bitwidth_activations
         self.per_channel_q = per_channel_q
-        self.clamp_weights = clamp_weights #and (not bool(self.per_channel_q))
+        self.constrain_weights = constrain_weights #and (not bool(self.per_channel_q))
         self.bias_calibration = bias_calibration
 
         # for help in debug/print
@@ -44,7 +44,7 @@ class QuantTrainModule(QuantBaseModule):
         # set attributes to all modules - can control the behaviour from here
         utils.apply_setattr(self, bitwidth_weights=bitwidth_weights, bitwidth_activations=bitwidth_activations, per_channel_q=per_channel_q, bias_calibration=bias_calibration,
                            quantize_enable=True, quantize_weights=True, quantize_bias=True, quantize_activations=True,
-                           percentile_range_shrink=percentile_range_shrink, clamp_weights=self.clamp_weights)
+                           percentile_range_shrink=percentile_range_shrink, constrain_weights=self.constrain_weights)
 
         # for help in debug/print
         utils.add_module_names(self)
@@ -139,7 +139,7 @@ class QuantTrainModule(QuantBaseModule):
 ###########################################################
 class QuantCalibrateModule(QuantTrainModule):
     def __init__(self, module, bitwidth_weights=8, bitwidth_activations=8, per_channel_q=False, bias_calibration=True,
-                 histogram_range=True, clamp_weights=True, lr_calib=0.1, dummy_input=None):
+                 histogram_range=True, constrain_weights=True, lr_calib=0.1, dummy_input=None):
         self.bias_calibration = bias_calibration
         self.lr_calib = lr_calib
         self.bias_calibration_factor = lr_calib
@@ -150,7 +150,7 @@ class QuantCalibrateModule(QuantTrainModule):
         # BNs can be adjusted based on the input provided - however this is not really required
         self.calibrate_bn = False
         super().__init__(module, bitwidth_weights=bitwidth_weights, bitwidth_activations=bitwidth_activations, per_channel_q=per_channel_q,
-                         histogram_range=histogram_range, clamp_weights=clamp_weights, bias_calibration=bias_calibration, dummy_input=dummy_input)
+                         histogram_range=histogram_range, constrain_weights=constrain_weights, bias_calibration=bias_calibration, dummy_input=dummy_input)
     #
 
 
index cb3a714df87002fed98060c5e2d72ed9e33117cb..5db807215903ac5b14ea21573d6b7af62a4695a3 100644 (file)
@@ -4,6 +4,7 @@ import torch
 from .. import utils
 from .. import layers
 from .quant_base_module import *
+from .quant_utils import *
 
 ###########################################################
 class QuantTrainParams:
@@ -99,7 +100,7 @@ class QuantTrainPAct2(layers.PAct2):
         self.quantize_weights = True
         self.quantize_bias = True
         self.quantize_activations = True
-        self.clamp_weights = True
+        self.constrain_weights = True
         self.bias_calibration = False
         # save quantized weight/bias once in a while into the params - not needed
         self.params_save_frequency = None #(10 if self.bias_calibration else None)
@@ -178,30 +179,8 @@ class QuantTrainPAct2(layers.PAct2):
         return y
     #
 
-    def apply_clamp_weights(self, merged_weight):
-        weights_clamp_value = QuantBaseModule.WEIGHTS_CLAMP_VALUE
-        weights_clamp_ratio = QuantBaseModule.WEIGHTS_CLAMP_RATIO
-
-        # a simple clamp - this may not be suitable for all models
-        # clamped_weight = merged_weight.clamp(-weights_clamp_value, weights_clamp_value)
-
-        # a better clamp - look at the statistics and then clamp
-        weight_max = merged_weight.abs().max()
-        # weight_min = merged_weight.abs().min()
-        weight_median = merged_weight.abs().median()
-        if (weight_max > weights_clamp_value) and (weight_max > (weight_median*weights_clamp_ratio)):
-            weight_max = torch.min(weight_max, weight_median*weights_clamp_ratio)
-            weight_max2 = layers.ceil2_g(weight_max)
-            scale_max2 = 128.0 / weight_max2
-            # minimum 1 - using slightly higher margin to ensure quantization aware training
-            # will not immediately cause the weights to move to the next scale range
-            clamp_margin = 8
-            clamp_max2 = (weight_max2 * scale_max2 - clamp_margin) / scale_max2
-            clamped_weight = merged_weight.clamp(-clamp_max2, clamp_max2)
-        else:
-            clamped_weight = merged_weight
-        #
-        return clamped_weight
+    def apply_constrain_weights(self, merged_weight):
+        return constrain_weight(merged_weight)
 
 
     def merge_quantize_weights(self, qparams, is_merged):
@@ -247,11 +226,11 @@ class QuantTrainPAct2(layers.PAct2):
         # quantize weight and bias
         if (conv is not None):
             if (self.quantize_weights):
-                if self.clamp_weights and first_training_iter:
+                if self.constrain_weights and first_training_iter:
                     with torch.no_grad():
                         # clamp merged weights, invert the bn and copy to conv weight
-                        clampled_weight = self.apply_clamp_weights(merged_weight.data)
-                        merged_weight.data.copy_(clampled_weight.data)
+                        constrained_weight = self.apply_constrain_weights(merged_weight.data)
+                        merged_weight.data.copy_(constrained_weight.data)
                         # store clipped weight after inverting bn
                         conv.weight.data.copy_(merged_weight.data * merged_scale_inv.view(-1, 1, 1, 1))
                     #
index b75bba81cc35226ca6e01a2fca8b6e237198ae53..a2c437e8e5ca9af63a4b1d58addc6667c34c41f0 100644 (file)
@@ -1,4 +1,6 @@
 import math
+import torch
+from .. import layers
 
 def even_round_tensor(tensor_in):
     #rounding towards even. banker's rounding (Unbiased rounding)
@@ -45,4 +47,51 @@ def compute_tensor_scale(tensor, mn, mx, bitwidth, power2_scaling):
     #    tensor_scale = (num / den)
     #    tensor_scale = min(tensor_scale, 65536)
     #
-    return tensor_scale, clamp_limits
\ No newline at end of file
+    return tensor_scale, clamp_limits
+
+
+# constants --------------------
+weights_clamp_value = 15.0 #31
+weights_clamp_ratio = 16.0 #32
+# -----------------------------
+
+def clamp_weight_simple(merged_weight):
+    # a simple clamp - this may not be suitable for all models
+    clamped_weight = merged_weight.clamp(-weights_clamp_value, weights_clamp_value)
+    return clamped_weight
+
+
+def clamp_weight_ratio(merged_weight):
+    # an intlligent clamp - look at the statistics and then clamp
+    weight_max = merged_weight.abs().max()
+    weight_median = merged_weight.abs().median()
+    if (weight_max > weights_clamp_value) and (weight_max > (weight_median*weights_clamp_ratio)):
+        weight_max = torch.min(weight_max, weight_median*weights_clamp_ratio)
+        weight_max2 = layers.ceil2_g(weight_max)
+        scale_max2 = 128.0 / weight_max2
+        # minimum 1 - using slightly higher margin to ensure quantization aware training
+        # will not immediately cause the weights to move to the next scale range
+        clamp_margin = 8
+        clamp_max2 = (weight_max2 * scale_max2 - clamp_margin) / scale_max2
+        clamped_weight = merged_weight.clamp(-clamp_max2, clamp_max2)
+    else:
+        clamped_weight = merged_weight
+    #
+    return clamped_weight
+
+
+def clamp_weight_soft(weight):
+    weight_max = weight.abs().max()
+    weight_median = weight.abs().median()
+    if (weight_max > weights_clamp_value) and (weight_max > (weight_median*weights_clamp_ratio)):
+        weight = torch.tanh(weight/weights_clamp_value)*(weights_clamp_value)
+    #
+    return weight
+
+
+def constrain_weight(weight, per_channel=True):
+    weight = clamp_weight_ratio(weight)
+    # weight = layers.standardize_weight(weight, per_channel)
+    # weight = clamp_weight_soft(weight)
+    return weight
+
index f3a0197c88b0aac25c1ebab27fdd84f74b067132..d327afed6f92ed9f55c67593001daac7c020b8ee 100644 (file)
@@ -6,7 +6,7 @@ from .tensor_utils import *
 from .logger import *
 from .utils_hist import *
 from .attr_dict import *
-from .weights_init import *
+from .weights_utils import *
 from .image_utils import *
 from .module_utils import *
 from .count_flops import forward_count_flops
index 55aee2b4f9877d728f843bd3b4f14134127165aa..d1a186ba1536077adfa639c3c4933e311edf2ec7 100644 (file)
@@ -8,8 +8,10 @@ class AttrDict(dict):
 
 
     def merge_from(self, src_cfg):
-        for src_key, src_val in src_cfg.items():
-            self[src_key] = src_val
+        if src_cfg is not None:
+            for src_key, src_val in src_cfg.items():
+                self[src_key] = src_val
+            #
         #
         return self
 
diff --git a/modules/pytorch_jacinto_ai/xnn/utils/weights_utils.py b/modules/pytorch_jacinto_ai/xnn/utils/weights_utils.py
new file mode 100644 (file)
index 0000000..9bf0f6e
--- /dev/null
@@ -0,0 +1,21 @@
+import torch
+from ..layers.functional import ceil2_g
+
+def module_weights_init(module):
+    # weight initialization
+    for m in module.modules():
+        if isinstance(m, torch.nn.Conv2d):
+            torch.nn.init.kaiming_normal_(m.weight, mode='fan_out', nonlinearity='relu')
+            if m.bias is not None:
+                torch.nn.init.zeros_(m.bias)
+        elif isinstance(m, (torch.nn.BatchNorm2d, torch.nn.GroupNorm)):
+            torch.nn.init.ones_(m.weight)
+            if m.bias is not None:
+                torch.nn.init.zeros_(m.bias)
+        elif isinstance(m, torch.nn.Linear):
+            torch.nn.init.normal_(m.weight, 0, 0.01)
+            torch.nn.init.zeros_(m.bias)
+
+
+
+
index a867cc9a43b0715f047b72fafccff03f6bf77c2e..63ea151c5740ccd34443779744b50e745e90214a 100755 (executable)
@@ -7,19 +7,19 @@
 #### Image Classification - Trained Quantization - MobileNetV2
 #python ./scripts/train_classification_main.py --dataset_name image_folder_classification --model_name mobilenetv2_tv_x1 --data_path ./data/datasets/image_folder_classification \
 #--pretrained https://download.pytorch.org/models/mobilenet_v2-b0353104.pth \
-#--batch_size 64 --quantize True --epochs 25 --epoch_size 1000 --lr 5e-5 --evaluate_start False
+#--batch_size 64 --quantize True --epochs 25 --epoch_size 1000 --lr 1e-5 --evaluate_start False
 #
 #
 #### Image Classification - Trained Quantization - MobileNetV2(Shicai) - a TOUGH MobileNetV2 pretrained model
 #python ./scripts/train_classification_main.py --dataset_name image_folder_classification --model_name mobilenetv2_shicai_x1 --data_path ./data/datasets/image_folder_classification \
 #--pretrained ./data/modelzoo/pretrained/pytorch/others/shicai/MobileNet-Caffe/mobilenetv2_shicai_rgb.tar \
-#--batch_size 64 --quantize True --epochs 25 --epoch_size 1000 --lr 5e-5 --evaluate_start False
+#--batch_size 64 --quantize True --epochs 25 --epoch_size 1000 --lr 1e-5 --evaluate_start False
 #
 #
 #### Semantic Segmentation - Trained Quantization for MobileNetV2+DeeplabV3Lite
 #python ./scripts/train_segmentation_main.py --dataset_name cityscapes_segmentation --model_name deeplabv3lite_mobilenetv2_tv --data_path ./data/datasets/cityscapes/data --img_resize 384 768 --output_size 1024 2048 --gpus 0 1 \
 #--pretrained ./data/modelzoo/semantic_segmentation/cityscapes/deeplabv3lite-mobilenetv2/cityscapes_segmentation_deeplabv3lite-mobilenetv2_2019-06-26-08-59-32.pth \
-#--batch_size 12 --quantize True --epochs 150 --lr 5e-5 --evaluate_start False
+#--batch_size 12 --quantize True --epochs 150 --lr 1e-5 --evaluate_start False
 
 
 ## =====================================================================================
index bc61f04a9db920ae958bf574f24a2c5b12992551..5380576f4bce406d29e65db10321e9013334a4cb 100755 (executable)
@@ -1,39 +1,57 @@
 #!/bin/bash
 
 # ----------------------------------
-base_dir="./data/checkpoints/quantization"
+# Quantization Aware Training (QAT) Example
+# Texas Instruments (C) 2018-2020
+# All Rights Reserved
+# ----------------------------------
+
+# ----------------------------------
 date_var=`date '+%Y-%m-%d_%H-%M-%S'`
-logdir=$base_dir/"$date_var"_quantization
-logfile=$logdir/run.log
-echo Logging the output to: $logfile
+base_dir="./data/checkpoints/quantization_example"
+save_path="$base_dir"/"$date_var"_quantization_example
+log_file=$save_path/run.log
+echo Logging the output to: $log_file
 
-mkdir $base_dir
-mkdir $logdir
-exec &> >(tee -a "$logfile")
 # ----------------------------------
+mkdir -p $save_path
+exec &> >(tee -a "$log_file")
 
+# ----------------------------------
 # model names and pretrained paths from torchvision - add more as required
 declare -A model_pretrained=(
   [mobilenet_v2]=https://download.pytorch.org/models/mobilenet_v2-b0353104.pth
-  [shufflenetv2_x1.0]=https://download.pytorch.org/models/shufflenetv2_x1-5666bf0f80.pth
   [resnet50]=https://download.pytorch.org/models/resnet50-19c8e357.pth
+  [mobilenetv2_shicai]='./data/modelzoo/pretrained/pytorch/others/shicai/MobileNet-Caffe/mobilenetv2_shicai_rgb.tar'
+  [shufflenetv2_x1.0]=https://download.pytorch.org/models/shufflenetv2_x1-5666bf0f80.pth
 )
 
+# ----------------------------------
+# parameters for quantization aware training
+# ----------------------------------
+lr=1e-5             # initial learning rate for quantization aware training - recommend to use 1e-5 (or at max 5e-5)
+batch_size=64       # use a relatively smaller batch size as quantization aware training does not use multi-gpu
+epochs=10           # numerb of epochs to train
+epoch_size=1000     # artificially limit one training epoch to this many iterations - this argument is only used to limit the training time and may hurt acuracy - set to 0 to use the full training epoch
+epoch_size_val=0    # validation epoch size - set to 0 for full validation epoch
+
+
+# ----------------------------------
 for model in "${!model_pretrained[@]}"; do
   echo ==========================================================
   pretrained="${model_pretrained[$model]}"
 
-  echo ----------------------------------------------------------
-  echo Estimating evaluation accuracy without quantization for $model
-  python -u ./examples/quantization_example.py ./data/datasets/image_folder_classification --arch $model --batch-size 64 --evaluate --pretrained $pretrained
-
-  echo ----------------------------------------------------------
-  echo Estimating evaluation accuracy with quantization for $model
-  python -u ./examples/quantization_example.py ./data/datasets/image_folder_classification --arch $model --batch-size 64 --evaluate --quantize --pretrained $pretrained
+#  echo ----------------------------------------------------------
+#  echo Estimating evaluation accuracy without quantization for $model
+#  python -u ./examples/quantization_example.py ./data/datasets/image_folder_classification --arch $model --batch_size 64 --evaluate --pretrained $pretrained --save_path $save_path
+#
+#  echo ----------------------------------------------------------
+#  echo Estimating evaluation accuracy with quantization for $model
+#  python -u ./examples/quantization_example.py ./data/datasets/image_folder_classification --arch $model --batch_size 64 --evaluate --quantize --pretrained $pretrained --save_path $save_path
 
   echo ----------------------------------------------------------
   echo Quantization Aware Training for $model
   # note: this example uses only a part of the training epoch and only 10 such (partial) epochs during quantized training to save time,
   # but it may necessary to use the full training epoch if the accuracy is not satisfactory.
-  python -u ./examples/quantization_example.py ./data/datasets/image_folder_classification --arch $model --batch-size 64 --lr 5e-5 --epoch-size 1000 --epochs 10 --quantize --pretrained $pretrained
+  python -u ./examples/quantization_example.py ./data/datasets/image_folder_classification --arch $model --batch_size $batch_size --lr $lr --epoch_size $epoch_size --epoch_size_val $epoch_size_val --epochs $epochs --quantize --pretrained $pretrained --save_path $save_path
 done
\ No newline at end of file
index 9a30e760b8a82fe7ee2116d8e623465e26b02229..8f7f2cf15c074fabcde05f1c3aee3149d29b440f 100755 (executable)
@@ -8,11 +8,11 @@
 #python ./scripts/train_segmentation_main.py --dataset_name cityscapes_segmentation --model_name deeplabv3lite_mobilenetv2_tv --data_path ./data/datasets/cityscapes/data --img_resize 384 768 --output_size 1024 2048 --gpus 0 1 \
 #--pretrained https://download.pytorch.org/models/mobilenet_v2-b0353104.pth
 
-#### Cityscapes Semantic Segmentation - Training with MobileNetV2+DeeplabV3Lite
+#### Cityscapes Semantic Segmentation - Training with MobileNetV2+FPN
 #python ./scripts/train_segmentation_main.py --dataset_name cityscapes_segmentation --model_name fpn_pixel2pixel_aspp_mobilenetv2_tv --data_path ./data/datasets/cityscapes/data --img_resize 384 768 --output_size 1024 2048 --gpus 0 1 \
 #--pretrained https://download.pytorch.org/models/mobilenet_v2-b0353104.pth
 
-#### Cityscapes Semantic Segmentation - original fpn - no aspp model, stride 64 model - Low Complexity Model
+#### Cityscapes Semantic Segmentation - MobileNetV2+FPN - no aspp model, stride 64 model - Low Complexity Model
 #python ./scripts/train_segmentation_main.py --dataset_name cityscapes_segmentation --model_name fpn_pixel2pixel_aspp_mobilenetv2_tv_fd --data_path ./data/datasets/cityscapes/data --img_resize 384 768 --output_size 1024 2048 --gpus 0 1 \
 #--pretrained https://download.pytorch.org/models/mobilenet_v2-b0353104.pth
 
 #python ./scripts/train_segmentation_main.py --dataset_name cityscapes_segmentation --model_name fpn_pixel2pixel_aspp_mobilenetv2_tv_fd --data_path ./data/datasets/cityscapes/data --img_resize 768 1536 --rand_crop 512 1024 --output_size 1024 2048 --gpus 0 1 \
 #--pretrained https://download.pytorch.org/models/mobilenet_v2-b0353104.pth
 
-#### Cityscapes Semantic Segmentation - fpn with 128 decoder channels - no aspp model, stride 64 model, Higher Resolution - Low Complexity Model
-#python ./scripts/train_segmentation_main.py --dataset_name cityscapes_segmentation --model_name fpn128_pixel2pixel_aspp_mobilenetv2_tv_fd --data_path ./data/datasets/cityscapes/data --img_resize 768 1536 --rand_crop 512 1024 --output_size 1024 2048 --gpus 0 1 \
-#--pretrained https://download.pytorch.org/models/mobilenet_v2-b0353104.pth
+
+
+#### Cityscapes Semantic Segmentation - Training with ResNet50+DeeplabV3Lite
+#python ./scripts/train_segmentation_main.py --dataset_name cityscapes_segmentation --model_name deeplabv3lite_resnet50 --data_path ./data/datasets/cityscapes/data --img_resize 384 768 --output_size 1024 2048 --gpus 0 1 \
+#--pretrained https://download.pytorch.org/models/resnet50-19c8e357.pth
 
 #### Cityscapes Semantic Segmentation - Training with ResNet50+FPN
+#python ./scripts/train_segmentation_main.py --dataset_name cityscapes_segmentation --model_name fpn_pixel2pixel_aspp_resnet50 --data_path ./data/datasets/cityscapes/data --img_resize 384 768 --rand_crop 512 1024 --output_size 1024 2048 --gpus 0 1 \
+#--pretrained https://download.pytorch.org/models/resnet50-19c8e357.pth
+
+#### Cityscapes Semantic Segmentation - Training with FD-ResNet50+FPN - High Resolution
 #python ./scripts/train_segmentation_main.py --dataset_name cityscapes_segmentation --model_name fpn_pixel2pixel_aspp_resnet50 --data_path ./data/datasets/cityscapes/data --img_resize 768 1536 --rand_crop 512 1024 --output_size 1024 2048 --gpus 0 1 \
 #--pretrained https://download.pytorch.org/models/resnet50-19c8e357.pth
 
-#### Cityscapes Semantic Segmentation - Training with FD-ResNet50+FPN - Low Complexity Model
+#### Cityscapes Semantic Segmentation - Training with ResNet50_p5+DeeplabV3Lite (ResNet50 encoder with half the channels): deeplabv3lite_resnet50_p5 & deeplabv3lite_resnet50_p5_fd
+#python ./scripts/train_segmentation_main.py --dataset_name cityscapes_segmentation --model_name deeplabv3lite_resnet50_p5 --data_path ./data/datasets/cityscapes/data --img_resize 384 768 --output_size 1024 2048 --gpus 0 1 \
+#--pretrained "./data/modelzoo/pretrained/pytorch/imagenet_classification/resnet50-0.5_b256_lr0.1_step30_inception-aug(0.08-1.0)_epoch(92of100)_1gmac_(72.05%)/model_best.pth.tar"
+
+#### Cityscapes Semantic Segmentation - Training with FD-ResNet50+FPN - High Resolution - Low Complexity Model
 #python ./scripts/train_segmentation_main.py --dataset_name cityscapes_segmentation --model_name fpn_pixel2pixel_aspp_resnet50_fd --data_path ./data/datasets/cityscapes/data --img_resize 768 1536 --rand_crop 512 1024 --output_size 1024 2048 --gpus 0 1 \
 #--pretrained https://download.pytorch.org/models/resnet50-19c8e357.pth
 
 
+
 #### VOC Segmentation - Training with MobileNetV2+DeeplabV3Lite
 #python ./scripts/train_segmentation_main.py --dataset_name voc_segmentation --model_name deeplabv3lite_mobilenetv2_tv --data_path ./data/datasets/voc --img_resize 512 512 --output_size 512 512 --gpus 0 1 \
 #--pretrained https://download.pytorch.org/models/mobilenet_v2-b0353104.pth