diff --git a/.azure-pipelines/scripts/codeScan/pydocstyle/scan_path.txt b/.azure-pipelines/scripts/codeScan/pydocstyle/scan_path.txt index 820d22d87bb..10da807b570 100644 --- a/.azure-pipelines/scripts/codeScan/pydocstyle/scan_path.txt +++ b/.azure-pipelines/scripts/codeScan/pydocstyle/scan_path.txt @@ -1,4 +1,3 @@ -/neural-compressor/neural_compressor/adaptor/mxnet_utils /neural-compressor/neural_compressor/adaptor/ox_utils /neural-compressor/neural_compressor/adaptor/tensorflow.py /neural-compressor/neural_compressor/adaptor/tf_utils diff --git a/.azure-pipelines/scripts/fwk_version.sh b/.azure-pipelines/scripts/fwk_version.sh index f475e1f77b3..2b5e8cb4520 100644 --- a/.azure-pipelines/scripts/fwk_version.sh +++ b/.azure-pipelines/scripts/fwk_version.sh @@ -7,4 +7,3 @@ export torchvision_version='0.20.1' export ipex_version='2.5.0+cpu' export onnx_version='1.17.0' export onnxruntime_version='1.20.0' -export mxnet_version='1.9.1' diff --git a/.azure-pipelines/scripts/ut/env_setup.sh b/.azure-pipelines/scripts/ut/env_setup.sh index 0905d6efbb2..01c575cf545 100644 --- a/.azure-pipelines/scripts/ut/env_setup.sh +++ b/.azure-pipelines/scripts/ut/env_setup.sh @@ -16,7 +16,6 @@ echo "torchvision version is $torchvision_version" echo "ipex version is $ipex_version" echo "onnx version is $onnx_version" echo "onnxruntime version is $onnxruntime_version" -echo "mxnet version is $mxnet_version" test_case=$1 echo -e "##[group]test case is ${test_case}" @@ -66,14 +65,6 @@ if [[ "${onnxruntime_version}" != "" ]]; then pip install optimum==1.24.0 fi -if [ "${mxnet_version}" != '' ]; then - pip install numpy==1.23.5 - echo "re-install pycocotools resolve the issue with numpy..." - pip uninstall pycocotools -y - pip install --no-cache-dir pycocotools - pip install mxnet==${mxnet_version} -fi - # install special test env requirements # common deps pip install cmake diff --git a/docs/source/adaptor.md b/docs/source/adaptor.md index b8af7a934fb..b2fa9b2e357 100644 --- a/docs/source/adaptor.md +++ b/docs/source/adaptor.md @@ -17,7 +17,7 @@ Adaptor Intel® Neural Compressor builds the low-precision inference solution on popular deep learning frameworks such as TensorFlow, PyTorch, -MXNet, and ONNX Runtime. The adaptor layer is the bridge between the +and ONNX Runtime. The adaptor layer is the bridge between the tuning strategy and vanilla framework quantization APIs. ## Adaptor Support Matrix @@ -27,7 +27,6 @@ tuning strategy and vanilla framework quantization APIs. |TensorFlow |✔ | |PyTorch |✔ | |ONNX |✔ | -|MXNet |✔ | ## Working Flow diff --git a/docs/source/add_new_adaptor.md b/docs/source/add_new_adaptor.md index 2027261c759..73e5941a49b 100644 --- a/docs/source/add_new_adaptor.md +++ b/docs/source/add_new_adaptor.md @@ -11,7 +11,7 @@ How to Add An Adaptor - [Add quantize API according to tune cfg](#add-quantize-api-according-to-tune-cfg) ## Introduction -Intel® Neural Compressor builds the low-precision inference solution on popular deep learning frameworks such as TensorFlow, PyTorch, MXNet, Keras and ONNX Runtime. The adaptor layer is the bridge between the tuning strategy and vanilla framework quantization APIs, each framework has own adaptor. The users can add new adaptor to set strategy capabilities. +Intel® Neural Compressor builds the low-precision inference solution on popular deep learning frameworks such as TensorFlow, PyTorch, Keras and ONNX Runtime. The adaptor layer is the bridge between the tuning strategy and vanilla framework quantization APIs, each framework has own adaptor. The users can add new adaptor to set strategy capabilities. The document outlines the process of adding support for a new adaptor, in Intel® Neural Compressor with minimal changes. It provides instructions and code examples for implementation of a new adaptor. By following the steps outlined in the document, users can extend Intel® Neural Compressor's functionality to accommodate new adaptor and incorporate it into quantization workflows. diff --git a/docs/source/calibration.md b/docs/source/calibration.md index 719fe2edc68..e8ed73167d4 100644 --- a/docs/source/calibration.md +++ b/docs/source/calibration.md @@ -42,11 +42,6 @@ Currently, Intel® Neural Compressor supports three popular calibration algorith minmax minmax, kl - - MXNet - minmax - minmax, kl - OnnxRuntime minmax diff --git a/docs/source/dataloader.md b/docs/source/dataloader.md index 78e73acbb5a..dcf168cc613 100644 --- a/docs/source/dataloader.md +++ b/docs/source/dataloader.md @@ -33,7 +33,6 @@ Of cause, users can also use frameworks own dataloader in Neural Compressor. |---------------|:----------:| | TensorFlow | ✔ | | Keras | ✔ | -| MXNet | ✔ | | PyTorch | ✔ | | ONNX Runtime | ✔ | @@ -45,7 +44,7 @@ Acceptable parameters for `DataLoader` API including: | Parameter | Description | |---------------|:----------:| -|framework (str)| different frameworks, such as `tensorflow`, `tensorflow_itex`, `keras`, `mxnet`, `pytorch` and `onnxruntime`.| +|framework (str)| different frameworks, such as `tensorflow`, `tensorflow_itex`, `keras`, `pytorch` and `onnxruntime`.| |dataset (object)| A dataset object from which to get data. Dataset must implement `__iter__` or `__getitem__` method.| |batch_size (int, optional)| How many samples per batch to load. Defaults to 1.| |collate_fn (Callable, optional)| Callable function that processes the batch you want to return from your dataloader. Defaults to None.| diff --git a/docs/source/examples_readme.md b/docs/source/examples_readme.md index b3fc71ebd08..a63b7e683c1 100644 --- a/docs/source/examples_readme.md +++ b/docs/source/examples_readme.md @@ -4,7 +4,7 @@ 2. [Release Data](#release-data) ## Example List -A wide variety of examples are provided to demonstrate the usage of Intel® Neural Compressor in different frameworks: TensorFlow, PyTorch, MXNet, and ONNX Runtime. +A wide variety of examples are provided to demonstrate the usage of Intel® Neural Compressor in different frameworks: TensorFlow, PyTorch, and ONNX Runtime. View the [examples in Neural Compressor GitHub Repo](https://github.com/intel/neural-compressor/tree/master/examples). ## Release Data diff --git a/docs/source/framework_yaml.md b/docs/source/framework_yaml.md index 7e8f6136a4d..d6769cca47d 100644 --- a/docs/source/framework_yaml.md +++ b/docs/source/framework_yaml.md @@ -16,7 +16,7 @@ running user cases and setting up framework capabilities, respectively. Here, we introduce the framework YAML file, which describes the behavior of a specific framework. There is a corresponding framework YAML file for each framework supported by Intel® Neural Compressor - TensorFlow -, Intel® Extension for TensorFlow*, PyTorch, Intel® Extension for PyTorch*, ONNX Runtime, and MXNet. +, Intel® Extension for TensorFlow*, PyTorch, Intel® Extension for PyTorch* and ONNX Runtime. >**Note**: Before diving to the details, we recommend that the end users do NOT make modifications unless they have clear requirements that can only be met by modifying the attributes. @@ -28,7 +28,6 @@ unless they have clear requirements that can only be met by modifying the attrib | TensorFlow | ✔ | | PyTorch | ✔ | | ONNX | ✔ | -| MXNet | ✔ | ## Get started with Framework YAML Files diff --git a/docs/source/infrastructure.md b/docs/source/infrastructure.md index cfa1912c571..e1a66064d62 100644 --- a/docs/source/infrastructure.md +++ b/docs/source/infrastructure.md @@ -188,4 +188,3 @@ Intel® Neural Compressor has unified interfaces which dispatch tasks to differe |TensorFlow |✔ | |PyTorch |✔ | |ONNX |plan to support in the future | -|MXNet |✔ | diff --git a/docs/source/metric.md b/docs/source/metric.md index dc0844c8341..641799fd40b 100644 --- a/docs/source/metric.md +++ b/docs/source/metric.md @@ -7,9 +7,7 @@ Metrics 2.2. [PyTorch](#pytorch) - 2.3. [MxNet](#mxnet) - - 2.4. [ONNXRT](#onnxrt) + 2.3. [ONNXRT](#onnxrt) 3. [Get Started with Metric](#get-started-with-metric) @@ -56,18 +54,6 @@ Neural Compressor supports some built-in metrics that are popularly used in indu | MSE(compare_label) | **compare_label** (bool, default=True): Whether to compare label. False if there are no labels and will use FP32 preds as labels. | preds, labels | Computes Mean Squared Error (MSE) loss. | | F1() | None | preds, labels | Computes the F1 score of a binary classification problem. | -### MXNet - -| Metric | Parameters | Inputs | Comments | -| :------ | :------ | :------ | :------ | -| topk(k) | **k** (int, default=1): Number of top elements to look at for computing accuracy | preds, labels | Computes top k predictions accuracy. | -| Accuracy() | None | preds, labels | Computes accuracy classification score.
Please refer to [MXNet docs](https://mxnet.apache.org/versions/1.7.0/api/python/docs/api/metric/index.html#mxnet.metric.Accuracy) for details. | -| Loss() | None | preds, labels | A dummy metric for directly printing loss, it calculates the average of predictions.
Please refer to [MXNet docs](https://mxnet.apache.org/versions/1.7.0/api/python/docs/_modules/mxnet/metric.html#Loss) for details. | -| MAE() | None | preds, labels | Computes Mean Absolute Error (MAE) loss.
Please refer to [MXNet docs](https://mxnet.apache.org/versions/1.7.0/api/python/docs/api/metric/index.html#mxnet.metric.MAE) for details. | -| RMSE(compare_label) | **compare_label** (bool, default=True): Whether to compare label. False if there are no labels and will use FP32 preds as labels. | preds, labels | Computes Root Mean Squared Error (RMSE) loss. | -| MSE() | None | preds, labels | Computes Mean Squared Error (MSE) loss.
Please refer to [MXNet docs](https://mxnet.apache.org/versions/1.7.0/api/python/docs/api/metric/index.html#mxnet.metric.MSE) for details. | -| F1() | None | preds, labels | Computes the F1 score of a binary classification problem.
Please refer to [MXNet docs](https://mxnet.apache.org/versions/1.7.0/api/python/docs/api/metric/index.html#mxnet.metric.F1) for details. | - ### ONNXRT diff --git a/docs/source/migration.md b/docs/source/migration.md index b4d087c890f..5744307304b 100644 --- a/docs/source/migration.md +++ b/docs/source/migration.md @@ -69,7 +69,7 @@ from neural_compressor.config import PostTrainingQuantConfig, TuningCriterion, A PostTrainingQuantConfig( ## model: this parameter does not need to specially be defined; - backend="default", # framework: set as "default" when framework was tensorflow, pytorch, pytorch_fx, onnxrt_integer and onnxrt_qlinear. Set as "ipex" when framework was pytorch_ipex, mxnet is currently unsupported; + backend="default", # framework: set as "default" when framework was tensorflow, pytorch, pytorch_fx, onnxrt_integer and onnxrt_qlinear. Set as "ipex" when framework was pytorch_ipex; inputs="image_tensor", # input: same as in the conf.yaml; outputs="num_detections,detection_boxes,detection_scores,detection_classes", # output: same as in the conf.yaml; device="cpu", # device: same as in the conf.yaml; @@ -178,7 +178,7 @@ from neural_compressor.config import QuantizationAwareTrainingConfig QuantizationAwareTrainingConfig( ## model: this parameter does not need to specially be defined; - backend="default", # framework: set as "default" when framework was tensorflow, pytorch, pytorch_fx, onnxrt_integer and onnxrt_qlinear. Set as "ipex" when framework was pytorch_ipex, mxnet is currently unsupported; + backend="default", # framework: set as "default" when framework was tensorflow, pytorch, pytorch_fx, onnxrt_integer and onnxrt_qlinear. Set as "ipex" when framework was pytorch_ipex; inputs="image_tensor", # input: same as in the conf.yaml; outputs="num_detections,detection_boxes,detection_scores,detection_classes", # output: same as in the conf.yaml; device="cpu", # device: same as in the conf.yaml; @@ -570,7 +570,7 @@ from neural_compressor.config import MixedPrecisionConfig, TuningCriterion, Accu MixedPrecisionConfig( ## model: this parameter does not need to specially be defined; - backend="default", # framework: set as "default" when framework was tensorflow, pytorch, pytorch_fx, onnxrt_integer and onnxrt_qlinear. Set as "ipex" when framework was pytorch_ipex, mxnet is currently unsupported; + backend="default", # framework: set as "default" when framework was tensorflow, pytorch, pytorch_fx, onnxrt_integer and onnxrt_qlinear. Set as "ipex" when framework was pytorch_ipex; inputs="image_tensor", # input: same as in the conf.yaml; outputs="num_detections,detection_boxes,detection_scores,detection_classes", # output: same as in the conf.yaml; device="cpu", # device: same as in the conf.yaml; @@ -667,7 +667,7 @@ version: 1.0 model: # mandatory. used to specify model specific information. name: ssd_mobilenet_v1 # mandatory. the model name. - framework: tensorflow # mandatory. supported values are tensorflow, pytorch, pytorch_fx, pytorch_ipex, onnxrt_integer, onnxrt_qlinear or mxnet; allow new framework backend extension. + framework: tensorflow # mandatory. supported values are tensorflow, pytorch, pytorch_fx, pytorch_ipex, onnxrt_integer or onnxrt_qlinear; allow new framework backend extension. inputs: image_tensor # optional. inputs and outputs fields are only required in tensorflow. outputs: num_detections,detection_boxes,detection_scores,detection_classes @@ -711,7 +711,7 @@ from neural_compressor.config import BenchmarkConfig BenchmarkConfig( ## model: this parameter does not need to specially be defined; - backend="default", # framework: set as "default" when framework was tensorflow, pytorch, pytorch_fx, onnxrt_integer and onnxrt_qlinear. Set as "ipex" when framework was pytorch_ipex, mxnet is currently unsupported; + backend="default", # framework: set as "default" when framework was tensorflow, pytorch, pytorch_fx, onnxrt_integer and onnxrt_qlinear. Set as "ipex" when framework was pytorch_ipex; inputs="image_tensor", # input: same as in the conf.yaml; outputs="num_detections,detection_boxes,detection_scores,detection_classes", # output: same as in the conf.yaml; device="cpu", # device: same as in the conf.yaml; diff --git a/docs/source/mixed_precision.md b/docs/source/mixed_precision.md index 91bdcf27318..2ddcec4f4f6 100644 --- a/docs/source/mixed_precision.md +++ b/docs/source/mixed_precision.md @@ -98,15 +98,6 @@ The recently launched 3rd Gen Intel® Xeon® Scalable processor (codenamed Coope ✔ :x: - - MXNet - OneDNN - OneDNN - "default" - cpu - ✔ - :x: - diff --git a/docs/source/model.md b/docs/source/model.md index b9eb3bfd9c3..330a180154f 100644 --- a/docs/source/model.md +++ b/docs/source/model.md @@ -92,15 +92,6 @@ The Neural Compressor Model feature is used to encapsulate the behavior of model onnx.onnx_ml_pb2.ModelProto frozen onnx - - MXNet - mxnet.gluon.HybridBlock - save_path.json - - - mxnet.symbol.Symbol - save_path-symbol.json and save_path-0000.params - diff --git a/docs/source/quantization.md b/docs/source/quantization.md index 69d8d71c022..58ac7e19ba9 100644 --- a/docs/source/quantization.md +++ b/docs/source/quantization.md @@ -60,7 +60,6 @@ Sometimes the reduce_range feature, that's using 7 bit width (1 sign bit + 6 dat | TensorFlow | [oneDNN](https://github.com/oneapi-src/oneDNN) | Activation (int8/uint8), Weight (int8) | - | | PyTorch | [FBGEMM](https://github.com/pytorch/FBGEMM) | Activation (uint8), Weight (int8) | Activation (uint8) | | PyTorch(IPEX) | [oneDNN](https://github.com/oneapi-src/oneDNN) | Activation (int8/uint8), Weight (int8) | - | -| MXNet | [oneDNN](https://github.com/oneapi-src/oneDNN) | Activation (int8/uint8), Weight (int8) | - | | ONNX Runtime | [MLAS](https://github.com/microsoft/onnxruntime/tree/master/onnxruntime/core/mlas) | Weight (int8) | Activation (uint8) | #### Quantization Scheme in TensorFlow @@ -80,11 +79,6 @@ Sometimes the reduce_range feature, that's using 7 bit width (1 sign bit + 6 dat + int8: scale = 2 * max(abs(rmin), abs(rmax)) / (max(int8) - min(int8) - 1) + uint8: scale = max(rmin, rmax) / (max(uint8) - min(uint8)) -#### Quantization Scheme in MXNet -+ Symmetric Quantization - + int8: scale = 2 * max(abs(rmin), abs(rmax)) / (max(int8) - min(int8) - 1) - + uint8: scale = max(rmin, rmax) / (max(uint8) - min(uint8)) - #### Quantization Scheme in ONNX Runtime + Symmetric Quantization + int8: scale = 2 * max(abs(rmin), abs(rmax)) / (max(int8) - min(int8) - 1) @@ -441,7 +435,7 @@ conf = PostTrainingQuantConfig(recipes=recipes) ``` ### Specify Quantization Backend and Device -Intel(R) Neural Compressor support multi-framework: PyTorch, Tensorflow, ONNX Runtime and MXNet. The neural compressor will automatically determine which framework to use based on the model type, but for backend and device, users need to set it themselves in configure object. +Intel(R) Neural Compressor support multi-framework: PyTorch, Tensorflow and ONNX Runtime. The neural compressor will automatically determine which framework to use based on the model type, but for backend and device, users need to set it themselves in configure object. @@ -511,13 +505,6 @@ Intel(R) Neural Compressor support multi-framework: PyTorch, Tensorflow, ONNX Ru - - - - - - -
"itex" cpu | gpu
MXNetOneDNNOneDNN"default"cpu

diff --git a/docs/source/transform.md b/docs/source/transform.md index ce1fa55bb43..741db669365 100644 --- a/docs/source/transform.md +++ b/docs/source/transform.md @@ -9,9 +9,7 @@ Transform 2.2 [Pytorch](#pytorch) - 2.3 [MXNet](#mxnet) - - 2.4 [ONNXRT](#onnxrt) + 2.3 [ONNXRT](#onnxrt) ## Introduction @@ -73,28 +71,6 @@ Neural Compressor supports built-in preprocessing methods on different framework | ResizeWithRatio(min_dim, max_dim, padding) | **min_dim** (int, default=800): Resizes the image such that its smaller dimension == min_dim
**max_dim** (int, default=1365): Ensures that the image longest side does not exceed this value
**padding** (bool, default=False): If true, pads image with zeros so its size is max_dim x max_dim | Resize image with aspect ratio and pad it to max shape(optional). If the image is padded, the label will be processed at the same time. The input image should be np.array. | ResizeWithRatio:
   min_dim: 800
   max_dim: 1365
   padding: True | | LabelShift(label_shift) | **label_shift**(int, default=0): number of label shift | Convert label to label - label_shift | LabelShift:
   label_shift: 0 | -### MXNet - -| Transform | Parameters | Comments | Usage(In yaml file) | -| :------ | :------ | :------ | :------ | -| Resize(size, interpolation) | **size** (list or int): Size of the result
**interpolation** (str, default='bilinear'):Desired interpolation type, support 'bilinear', 'nearest', 'bicubic' | Resize the input image to the given size | Resize:
   size: 256
   interpolation: bilinear | -| CenterCrop(size) | **size** (list or int): Size of the result | Crop the given image at the center to the given size | CenterCrop:
   size: [10, 10] # or size: 10 | -| RandomResizedCrop(size, scale, ratio, interpolation) | **size** (list or int): Size of the result
**scale** (tuple or list, default=(0.08, 1.0)):range of size of the origin size cropped
**ratio** (tuple or list, default=(3. / 4., 4. / 3.)): range of aspect ratio of the origin aspect ratio cropped
**interpolation** (str, default='bilinear'):Desired interpolation type, support 'bilinear', 'nearest', 'bicubic' | Crop the given image to random size and aspect ratio | RandomResizedCrop:
   size: [10, 10] # or size: 10
   scale: [0.08, 1.0]
   ratio: [3. / 4., 4. / 3.]
   interpolation: bilinear | -| Normalize(mean, std) | **mean** (list, default=[0.0]):means for each channel, if len(mean)=1, mean will be broadcasted to each channel, otherwise its length should be same with the length of image shape
**std** (list, default=[1.0]):stds for each channel, if len(std)=1, std will be broadcasted to each channel, otherwise its length should be same with the length of image shape | Normalize a image with mean and standard deviation | Normalize:
   mean: [0.0, 0.0, 0.0]
   std: [1.0, 1.0, 1.0] | -| RandomCrop(size) | **size** (list or int): Size of the result | Crop the image at a random location to the given size | RandomCrop:
   size: [10, 10] # size: 10 | -| Compose(transform_list) | **transform_list** (list of Transform objects): list of transforms to compose | Compose several transforms together | If user uses yaml file to configure transforms, Neural Compressor will automatic call Compose to group other transforms.
**In user code:**
from neural_compressor.experimental.data import TRANSFORMS
preprocess = TRANSFORMS(framework, 'preprocess')
resize = preprocess["Resize"] (\**args)
normalize = preprocess["Normalize"] (\**args)
compose = preprocess["Compose"] ([resize, normalize])
sample = compose(sample)
# sample: image, label | -| CropResize(x, y, width, height, size, interpolation) | **x** (int): Left boundary of the cropping area
**y** (int): Top boundary of the cropping area
**width** (int): Width of the cropping area
**height** (int): Height of the cropping area
**size** (list or int): resize to new size after cropping
**interpolation** (str, default='bilinear'): Desired interpolation type, support 'bilinear', 'nearest', 'bicubic' | Crop the input image with given location and resize it | CropResize:
   x: 0
   y: 5
   width: 224
   height: 224
   size: [100, 100] # or size: 100
   interpolation: bilinear | -| RandomHorizontalFlip() | None | Horizontally flip the given image randomly | RandomHorizontalFlip: {} | -| RandomVerticalFlip() | None | Vertically flip the given image randomly | RandomVerticalFlip: {} | -| CropToBoundingBox(offset_height, offset_width, target_height, target_width) | **offset_height** (int): Vertical coordinate of the top-left corner of the result in the input
**offset_width** (int): Horizontal coordinate of the top-left corner of the result in the input
**target_height** (int): Height of the result
**target_width** (int): Width of the result | Crop an image to a specified bounding box | CropToBoundingBox:
   offset_height: 10
   offset_width: 10
   target_height: 224
   224 | -| ToArray() | None | Convert NDArray to numpy array | ToArray: {} | -| ToTensor() | None | Convert an image NDArray or batch of image NDArray to a tensor NDArray | ToTensor: {} | -| Cast(dtype) | **dtype** (str, default ='float32'): The target data type | Convert image to given dtype | Cast:
   dtype: float32 | -| Transpose(perm) | **perm** (list): A permutation of the dimensions of input image | Transpose image according perm | Transpose:
   perm: [1, 2, 0] | -| AlignImageChannel(dim) | **dim** (int): The channel number of result image | Align image channel, now just support [H,W]->[H,W,dim], [H,W,4]->[H,W,3] and [H,W,3]->[H,W].
This transform is going to be deprecated. | AlignImageChannel:
   dim: 3 | -| ToNDArray() | None | Convert np.array to NDArray | ToNDArray: {} | -| ResizeWithRatio(min_dim, max_dim, padding) | **min_dim** (int, default=800): Resizes the image such that its smaller dimension == min_dim
**max_dim** (int, default=1365): Ensures that the image longest side does not exceed this value
**padding** (bool, default=False): If true, pads image with zeros so its size is max_dim x max_dim | Resize image with aspect ratio and pad it to max shape(optional). If the image is padded, the label will be processed at the same time. The input image should be np.array. | ResizeWithRatio:
   min_dim: 800
   max_dim: 1365
   padding: True | - ### ONNXRT | Type | Parameters | Comments | Usage(In yaml file) | diff --git a/docs/source/tuning_strategies.md b/docs/source/tuning_strategies.md index ad1bf00ab9b..cf2cc9e3980 100644 --- a/docs/source/tuning_strategies.md +++ b/docs/source/tuning_strategies.md @@ -44,7 +44,7 @@ Tuning Strategies ## Introduction Intel® Neural Compressor aims to help users quickly deploy -the low-precision inference solution on popular Deep Learning frameworks such as TensorFlow, PyTorch, ONNX, and MXNet. With built-in strategies, it automatically optimizes low-precision recipes for deep learning models to achieve optimal product objectives, such as inference performance and memory usage, with expected accuracy criteria. Currently, several tuning strategies, including `auto`, `O0`, `O1`, `Basic`, `MSE`, `MSE_V2`, `HAWQ_V2`, `Bayesian`, `Exhaustive`, `Random`, `SigOpt`, `TPE`, etc are supported. By default, the [`quant_level="auto"`](./tuning_strategies.md#auto) is used for tuning. +the low-precision inference solution on popular Deep Learning frameworks such as TensorFlow, PyTorch and ONNX. With built-in strategies, it automatically optimizes low-precision recipes for deep learning models to achieve optimal product objectives, such as inference performance and memory usage, with expected accuracy criteria. Currently, several tuning strategies, including `auto`, `O0`, `O1`, `Basic`, `MSE`, `MSE_V2`, `HAWQ_V2`, `Bayesian`, `Exhaustive`, `Random`, `SigOpt`, `TPE`, etc are supported. By default, the [`quant_level="auto"`](./tuning_strategies.md#auto) is used for tuning. ## Strategy Design Before tuning, the `tuning space` was constructed according to the framework capability and user configuration. Then the selected strategy generates the next quantization configuration according to its traverse process and the previous tuning record. The tuning process stops when meeting the exit policy. The function of strategies is shown diff --git a/examples/.config/model_params_mxnet.json b/examples/.config/model_params_mxnet.json deleted file mode 100644 index b249d5293c4..00000000000 --- a/examples/.config/model_params_mxnet.json +++ /dev/null @@ -1,86 +0,0 @@ -{ - "mxnet": { - "resnet50v1": { - "model_src_dir": "image_recognition/cnn_models/quantization/ptq", - "dataset_location": "/tf_dataset/mxnet/val_256_q90.rec", - "input_model": "/tf_dataset/mxnet/resnet50_v1", - "yaml": "cnn.yaml", - "strategy": "basic", - "batch_size": 64, - "new_benchmark": false - }, - "inceptionv3": { - "model_src_dir": "image_recognition/cnn_models/quantization/ptq", - "dataset_location": "/tf_dataset/mxnet/val_256_q90.rec", - "input_model": "/tf_dataset/mxnet/inceptionv3", - "yaml": "cnn.yaml", - "strategy": "basic", - "batch_size": 64, - "new_benchmark": false - }, - "mobilenet1.0": { - "model_src_dir": "image_recognition/cnn_models/quantization/ptq", - "dataset_location": "/tf_dataset/mxnet/val_256_q90.rec", - "input_model": "/tf_dataset/mxnet/mobilenet1.0", - "yaml": "cnn.yaml", - "strategy": "basic", - "batch_size": 64, - "new_benchmark": false - }, - "mobilenetv2_1.0": { - "model_src_dir": "image_recognition/cnn_models/quantization/ptq", - "dataset_location": "/tf_dataset/mxnet/val_256_q90.rec", - "input_model": "/tf_dataset/mxnet/mobilenetv2_1.0", - "yaml": "cnn.yaml", - "strategy": "basic", - "batch_size": 64, - "new_benchmark": false - }, - "resnet18_v1": { - "model_src_dir": "image_recognition/cnn_models/quantization/ptq", - "dataset_location": "/tf_dataset/mxnet/val_256_q90.rec", - "input_model": "/tf_dataset/mxnet/resnet18_v1", - "yaml": "cnn.yaml", - "strategy": "basic", - "batch_size": 64, - "new_benchmark": false - }, - "squeezenet1.0": { - "model_src_dir": "image_recognition/cnn_models/quantization/ptq", - "dataset_location": "/tf_dataset/mxnet/val_256_q90.rec", - "input_model": "/tf_dataset/mxnet/squeezenet1.0", - "yaml": "cnn.yaml", - "strategy": "basic", - "batch_size": 64, - "new_benchmark": false - }, - "resnet152_v1": { - "model_src_dir": "image_recognition/cnn_models/quantization/ptq", - "dataset_location": "/tf_dataset/mxnet/val_256_q90.rec", - "input_model": "/tf_dataset/mxnet/resnet152_v1", - "yaml": "cnn.yaml", - "strategy": "basic", - "batch_size": 64, - "new_benchmark": false - }, - "ssd-resnet50_v1": { - "model_src_dir": "object_detection/ssd_models/quantization/ptq", - "dataset_location": "/tf_dataset/dataset/VOCdevkit", - "input_model": "/tf_dataset/mxnet/ssd-resnet50_v1/ssd_512_resnet50_v1_voc", - "yaml": "ssd.yaml", - "strategy": "basic", - "batch_size": 256, - "new_benchmark": false - }, - "ssd-mobilenet1.0": { - "model_src_dir": "object_detection/ssd_models/quantization/ptq", - "dataset_location": "/tf_dataset/dataset/VOCdevkit", - "input_model": "/tf_dataset/mxnet/ssd-mobilenet1.0/ssd_512_mobilenet1.0_voc", - "yaml": "ssd.yaml", - "strategy": "basic", - "batch_size": 256, - "new_benchmark": false - } - } -} - diff --git a/examples/pytorch/recommendation/dlrm/quantization/ptq/fx/conf.yaml b/examples/pytorch/recommendation/dlrm/quantization/ptq/fx/conf.yaml index 757eb044581..c7d1e7a2c49 100644 --- a/examples/pytorch/recommendation/dlrm/quantization/ptq/fx/conf.yaml +++ b/examples/pytorch/recommendation/dlrm/quantization/ptq/fx/conf.yaml @@ -15,7 +15,7 @@ model: # mandatory. used to specify model specific information. name: dlrm - framework: pytorch_fx # mandatory. possible values are tensorflow, mxnet, pytorch, pytorch_ipex, onnxrt_integerops and onnxrt_qlinearops. + framework: pytorch_fx # mandatory. possible values are tensorflow, pytorch, pytorch_ipex, onnxrt_integerops and onnxrt_qlinearops. tuning: accuracy_criterion: diff --git a/examples/pytorch/recommendation/dlrm/quantization/ptq/ipex/conf_ipex.yaml b/examples/pytorch/recommendation/dlrm/quantization/ptq/ipex/conf_ipex.yaml index e0181e5aa88..1ec6fbf27a2 100644 --- a/examples/pytorch/recommendation/dlrm/quantization/ptq/ipex/conf_ipex.yaml +++ b/examples/pytorch/recommendation/dlrm/quantization/ptq/ipex/conf_ipex.yaml @@ -17,7 +17,7 @@ version: 1.0 model: # mandatory. used to specify model specific information. name: dlrm - framework: pytorch_ipex # mandatory. supported values are tensorflow, pytorch, pytorch_ipex, onnxrt_integer, onnxrt_qlinear or mxnet; allow new framework backend extension. + framework: pytorch_ipex # mandatory. supported values are tensorflow, pytorch, pytorch_ipex, onnxrt_integer or onnxrt_qlinear; allow new framework backend extension. quantization: # optional. tuning constraints on model-wise for advance user to reduce tuning space. calibration: diff --git a/neural_compressor/adaptor/adaptor.py b/neural_compressor/adaptor/adaptor.py index 7f621a42b7c..c12fb501188 100644 --- a/neural_compressor/adaptor/adaptor.py +++ b/neural_compressor/adaptor/adaptor.py @@ -17,7 +17,7 @@ from abc import abstractmethod -"""The framework backends supported by neural_compressor, including tensorflow, mxnet and pytorch. +"""The framework backends supported by neural_compressor, including tensorflow and pytorch. User could add new backend support by implementing new Adaptor subclass under this directory. The naming convention of new Adaptor subclass should be something like ABCAdaptor, user diff --git a/neural_compressor/adaptor/mxnet.py b/neural_compressor/adaptor/mxnet.py deleted file mode 100644 index b28364180ea..00000000000 --- a/neural_compressor/adaptor/mxnet.py +++ /dev/null @@ -1,505 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# Copyright (c) 2021 Intel Corporation -# -# Licensed 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 logging -import math -import os -from collections import OrderedDict -from copy import deepcopy - -import yaml - -from neural_compressor.adaptor.adaptor import Adaptor, adaptor_registry -from neural_compressor.adaptor.mxnet_utils.util import * -from neural_compressor.adaptor.query import QueryBackendCapability -from neural_compressor.data.dataloaders.base_dataloader import BaseDataLoader -from neural_compressor.utils.utility import GLOBAL_STATE, MODE, CpuInfo, LazyImport, dump_elapsed_time, singleton - -mx = LazyImport("mxnet") -logger = logging.getLogger("neural_compressor") - - -@adaptor_registry -class MxNetAdaptor(Adaptor): - """The MXNet adaptor layer, do MXNet quantization, calibration, inspect layer tensors. - - Args: - framework_specific_info (dict): framework specific configuration for quantization. - """ - - def __init__(self, framework_specific_info): - super(MxNetAdaptor, self).__init__(framework_specific_info) - assert check_mx_version("1.6.0"), "Need MXNet version >= 1.6.0, but got version: %s" % (mx.__version__) - - self.pre_optimized_model = None - self.quantizable_nodes = [] - self._qtensor_to_tensor = {} - self._tensor_to_node = {} - self.qdataloader = framework_specific_info.get("q_dataloader") - self.query_handler = MXNetQuery(local_config_file=os.path.join(os.path.dirname(__file__), "mxnet.yaml")) - - self.ctx = mx.cpu() if framework_specific_info["device"] == "cpu" else None - self.benchmark = GLOBAL_STATE.STATE == MODE.BENCHMARK - self.optype_statistics = None - assert self.ctx is not None, "Unsupported device" - - @dump_elapsed_time("Pass quantize model") - def quantize(self, tune_cfg, nc_model, dataloader, q_func=None): - """The function is used to do MXNet calibration and quantization in post-training - quantization. - - Args: - tune_cfg (dict): quantization config. - nc_model (object): neural_compressor fp32 model to be quantized. - dataloader (object): calibration dataset. - q_func (optional): training function for quantization aware training mode, - unimplement yet for MXNet. - - Returns: - (MXNetModel): quantized model - """ - assert q_func is None, "quantization aware training mode is not supported on mxnet" - - calib_cache = nc_model.calib_cache - - def calib_func(tmp_tune_cfg, dataloader): - quant_cfg, calib_cfg, amp_cfg = parse_tune_config(tmp_tune_cfg, self.quantizable_nodes) - logger.debug("Dump quantization configurations:") - logger.debug(quant_cfg) - - sym_model, dataloader = prepare_model_data(nc_model, self.ctx, dataloader) - qsym_model, calib_tensors = quantize_sym_model(sym_model, self.ctx, quant_cfg) - calib_data = self._collect_thresholds(sym_model, dataloader, calib_tensors, calib_cfg, calib_cache) - qsym_model = calib_model(qsym_model, calib_data, calib_cfg) - qsym_model = fuse(qsym_model, self.ctx) # post-quantization fusion - - if len(amp_cfg["excluded_sym_names"]) < len(self.quantizable_nodes): - qsym_model = amp_convert(qsym_model, dataloader.input_desc, amp_cfg) - - q_nc_model = make_nc_model(nc_model, qsym_model, self.ctx, dataloader.input_desc) - q_nc_model.calib_cache["last"] = calib_data.th_dict - q_nc_model.q_config = { - "mxnet_version": mx.__version__, - "amp_cfg": amp_cfg, - "quant_cfg": quant_cfg, - "calib_cfg": calib_cfg, - "th_dict": calib_data.th_dict, - "input_desc": dataloader.input_desc, - "framework_specific_info": {"device": self.ctx.device_type}, - } - return q_nc_model - - calib_sampling_size = tune_cfg.get("calib_sampling_size", 1) - if isinstance(dataloader, BaseDataLoader): - batch_size = dataloader.batch_size - try: - for i in range(batch_size): - if calib_sampling_size % (batch_size - i) == 0: - calib_batch_size = batch_size - i - if i != 0: - logger.warning( - "Reset `calibration.dataloader.batch_size` field " - "to {}".format(calib_batch_size) + " to make sure the sampling_size is " - "divisible exactly by batch size" - ) - break - tmp_iterations = int(math.ceil(calib_sampling_size / calib_batch_size)) - tmp_tune_cfg = deepcopy(tune_cfg) - tmp_tune_cfg["calib_iteration"] = tmp_iterations - dataloader.batch(calib_batch_size) - return calib_func(tmp_tune_cfg, dataloader) - except Exception: # pragma: no cover - logger.warning("Fail to forward with batch size={}, set to {} now.".format(batch_size, 1)) - tmp_tune_cfg = deepcopy(tune_cfg) - tmp_tune_cfg["calib_iteration"] = calib_sampling_size - dataloader.batch(1) - return calib_func(tmp_tune_cfg, dataloader) - else: - if hasattr(dataloader, "batch_size") and calib_sampling_size % dataloader.batch_size != 0: - iter = tune_cfg["calib_iteration"] - logger.warning( - "Please note that calibration sampling size {} " - "isn't divisible exactly by batch size {}. " - "So the real sampling size is {}.".format( - calib_sampling_size, dataloader.batch_size, dataloader.batch_size * iter - ) - ) - return calib_func(tune_cfg, dataloader) - - def _collect_thresholds(self, sym_model, calib_data, calib_tensors, calib_cfg, calib_cache): - """Calculate thresholds for each tensor. The calibration method can be min/max - or KL on different tensors. - - Args: - sym_model (tuple): symbol model (symnet, args, auxs). - calib_data (DataLoaderWrap): dataset to do calibration on. - calib_tensors (list): tensors to calibrate - calib_cfg (dict): calibration config. - calib_cache (dict): cached calibration results - - Returns: - (CalibResult): The results of calibration (pair of thresholds for each tensor). - """ - assert ( - calib_cfg["calib_mode"] == "naive" - ), "`calib_mode` must be set to `naive`, for `collector.min_max_dict` to be used" - - if calib_cache.get("batches", -1) != calib_cfg["batches"]: - calib_cache["batches"] = calib_cfg["batches"] - calib_cache["kl"] = {} - calib_cache["minmax"] = {} - - cache_kl = calib_cache["kl"] - cache_minmax = calib_cache["minmax"] - tensors_kl, tensors_minmax = distribute_calib_tensors(calib_tensors, calib_cfg, self._tensor_to_node) - to_collect_kl = tensors_kl - set(cache_kl.keys()) - to_collect_minmax = tensors_minmax - set(cache_minmax.keys()) - collector = CalibCollector(to_collect_kl, to_collect_minmax) - - if len(to_collect_kl) + len(to_collect_minmax) > 0: - - def b_filter(): - for _ in range(calib_cfg["batches"]): - yield True - - logger.info("Start to collect tensors of the FP32 model.") - batches = run_forward( - sym_model, self.ctx, calib_data, b_filter(), collector, collector.pre_batch, collector.post_batch - ) - logger.info("Get collected tensors of the FP32 model from {} batches.".format(batches)) - - if len(collector.include_tensors_kl) > 0: - cache_kl.update(collector.calc_kl_th_dict(calib_cfg["quantized_dtype"])) - cache_minmax.update(collector.min_max_dict) - - return CalibData(cache_kl, cache_minmax, tensors_kl, tensors_minmax) - - def evaluate( - self, - nc_model, - data_x, - postprocess=None, - metrics=None, - measurer=None, - iteration=-1, - tensorboard=False, - fp32_baseline=False, - ): - """The function is used to run evaluation on validation dataset. - - Args: - nc_model (object): model to evaluate. - data_x (object): data iterator/loader. - postprocess (object, optional): process the result from the model - metrics (list): list of evaluate metric. - measurer (object, optional): for precise benchmark measurement. - iteration(int, optional): control steps of mini-batch - tensorboard (boolean, optional): for tensorboard inspect tensor. - fp32_baseline (boolean, optional): only for compare_label=False pipeline - - Returns: - acc: evaluate result. - """ - - def b_filter(): - if iteration == -1: - while True: - yield True - for _ in range(iteration): - yield True - - def pre_batch(net, batch): - if measurer is not None: - measurer.start() - - def post_batch(net, batch, outs): - if measurer is not None: - measurer.end() - _, labels = batch - outs = ensure_list(outs) - labels = ensure_list(labels) - assert len(labels) == len(outs) == 1 - - out = outs[0].asnumpy() - label = labels[0].asnumpy() - if postprocess is not None: - out, label = postprocess((out, label)) - if metrics is not None: - for metric in metrics: - metric.update(out, label) - - if isinstance(data_x, BaseDataLoader) and not self.benchmark: - try: - sym_model, dataloader = prepare_model_data(nc_model, self.ctx, data_x) - run_forward(sym_model, self.ctx, dataloader, b_filter(), pre_batch=pre_batch, post_batch=post_batch) - except Exception: # pragma: no cover - logger.warning("Fail to forward with batch size={}, set to {} now.".format(data_x.batch_size, 1)) - data_x.batch(1) - sym_model, dataloader = prepare_model_data(nc_model, self.ctx, data_x) - run_forward(sym_model, self.ctx, dataloader, b_filter(), pre_batch=pre_batch, post_batch=post_batch) - else: - sym_model, dataloader = prepare_model_data(nc_model, self.ctx, data_x) - run_forward(sym_model, self.ctx, dataloader, b_filter(), pre_batch=pre_batch, post_batch=post_batch) - acc = [metric.result() for metric in metrics] if metrics is not None else 0 - return acc if not isinstance(acc, list) or len(acc) > 1 else acc[0] - - @dump_elapsed_time("Query quantizable operators") - def query_fw_capability(self, nc_model): - """Query MXNet quantization capability on the model/op level with the specific model. - - Args: - nc_model (object): model to query. - - Returns: - dict: modelwise and opwise config. - """ - sym_model, self.qdataloader = prepare_model_data(nc_model, self.ctx, self.qdataloader) - # (TODO) to align with other fw, set pre_optimized_model here - self.pre_optimized_model = sym_model - - self.quantizable_nodes, self._tensor_to_node, all_op_nodes = query_quantizable_nodes( - sym_model, self.ctx, self.qdataloader - ) - - config = self.query_handler.get_quantization_capability()["int8"] - bf16_config = self.query_handler.get_quantization_capability().get("bf16", {}) - valid_precisions = self.query_handler.get_mixed_precision_combination() - use_bf16 = ("bf16" in valid_precisions and CpuInfo().bf16) or os.getenv("FORCE_BF16") == "1" - if use_bf16: - config = combine_capabilities(config, bf16_config) - - op_type_wise = OrderedDict() - op_wise = OrderedDict() - for node in self.quantizable_nodes: - op_capability = config.get(node["type"], config["default"]) - op_type_wise.setdefault(node["type"], op_capability) - op_wise.setdefault((node["name"], node["type"]), op_capability) - - if use_bf16: - for node_name, op_name in all_op_nodes.items(): - if (node_name, op_name) not in op_wise and op_name in bf16_config: - op_type_wise.setdefault(op_name, bf16_config[op_name]) - op_wise.setdefault((node_name, op_name), bf16_config[op_name]) - - # make sure configurations are independent - for op, cfg in op_type_wise.items(): - op_type_wise[op] = deepcopy(cfg) - for key, cfg in op_wise.items(): - op_wise[key] = deepcopy(cfg) - - return {"optypewise": op_type_wise, "opwise": op_wise} - - def _inspect_tensor(self, nc_model, data_x, node_list=[], iteration_list=[]): - def b_filter(): - iteration_set = set(iteration_list) - if len(iteration_set) == 0: - while True: - yield True - i = 1 - while len(iteration_set) > 0: - run = i in iteration_list - iteration_set -= {i} - i += 1 - yield run - - sym_model, dataloader = prepare_model_data(nc_model, self.ctx, data_x) - collector = TensorCollector(node_list, self._qtensor_to_tensor, self._tensor_to_node) - num_batches = run_forward(sym_model, self.ctx, dataloader, b_filter(), collector, pre_batch=collector.pre_batch) - logger.debug("Inspect batches at {}.".format(num_batches)) - self._qtensor_to_tensor = collector.qtensor_to_tensor - return collector.tensors_dicts - - def inspect_tensor( - self, - nc_model, - data_x, - op_list=[], - iteration_list=[], - inspect_type="activation", - save_to_disk=False, - save_path=None, - quantization_cfg=None, - ): - """The function is used by tune strategy class for dumping tensor info. - - Args: - nc_model (object): The model to do calibration. - data_x (object): Data iterator/loader. - op_list (list): list of inspect tensors. - iteration_list (list): list of inspect iterations. - - Returns: - dict: includes tensor dicts - """ - if inspect_type not in ["all", "activation"]: - raise NotImplementedError() - - tensor_dict_list = self._inspect_tensor(nc_model, data_x, op_list, iteration_list) - for tensor_dict in tensor_dict_list: - for key, tensors in tensor_dict.items(): - for tensor_name, (is_quantized, tensor) in tensors.items(): - tensor_dict[key][tensor_name] = tensor # discard is_quantized - if is_quantized: - assert tensor.dtype in QUANTIZATION_DTYPES - assert "last" in nc_model.calib_cache - min_th, max_th = nc_model.calib_cache["last"][tensor_name] - tensor_dict[key][tensor_name] = mx.nd.contrib.dequantize( - tensor, - min_range=mx.nd.array([min_th]).squeeze(), - max_range=mx.nd.array([max_th]).squeeze(), - out_type="float32", - ) - tensor_dict[key][tensor_name] = tensor_dict[key][tensor_name].asnumpy() - - # transform to format expected by neural_compressor (assume only 1 tensor for now) - node = key - assert len(tensors) == 1, "Multiple tensors from a single node are not supported" - tensor = list(tensor_dict[key].values())[0] - tensor_dict[key] = {node: tensor} - - return {"activation": tensor_dict_list} - - def recover_tuned_model(self, nc_model, q_config): - """Execute the recover process on the specified model. - - Args: - tune_cfg (dict): quantization configuration - nc_model (object): fp32 model - q_config (dict): recover configuration - - Returns: - MXNetModel: the quantized model - """ - if q_config["mxnet_version"] != mx.__version__: # pragma: no cover - logger.warning( - "Attempting to recover a model generated with a different " - "version of MXNet ({})".format(q_config["mxnet_version"]) - ) - - sym_model = prepare_model(nc_model, self.ctx, q_config["input_desc"]) - qsym_model, calib_tensors = quantize_sym_model(sym_model, self.ctx, q_config["quant_cfg"]) - - calib_data = CalibData() - calib_data.th_dict = q_config["th_dict"] - assert set(calib_tensors).issubset(calib_data.th_dict.keys()) - - qsym_model = calib_model(qsym_model, calib_data, q_config["calib_cfg"]) - qsym_model = fuse(qsym_model, self.ctx) # post-quantization fusion - - q_nc_model = make_nc_model(nc_model, qsym_model, self.ctx, q_config["input_desc"]) - q_nc_model.calib_cache["last"] = q_config["th_dict"] - q_nc_model.q_config = q_config - return q_nc_model - - def set_tensor(self, model, tensor_dict): - """The function is used by tune strategy class for setting tensor back to model. - - Args: - model (object): The model to set tensor. Usually it is quantized model. - tensor_dict (dict): The tensor dict to set. Note the numpy array contains float - value, adaptor layer has the responsibility to quantize to - int8 or int32 to set into the quantized model if needed. - The dict format is something like: - { - 'weight0_name': numpy.array, - 'bias0_name': numpy.array, - ... - } - """ - raise NotImplementedError - - def save(self, model, path): - model.save(path) - - -@singleton -class MXNetQuery(QueryBackendCapability): - def __init__(self, local_config_file): - super().__init__() - self.version = mx.__version__ - self.cfg = local_config_file - self.cur_config = None - self._one_shot_query() - - def _one_shot_query(self): - with open(self.cfg) as f: - content = yaml.safe_load(f) - try: - self.cur_config = self._get_specified_version_cfg(content) - except Exception as e: # pragma: no cover - logger.info("Fail to parse {} due to {}.".format(self.cfg, str(e))) - self.cur_config = None - raise ValueError( - "Please check if the format of {} follows Neural Compressor yaml schema.".format(self.cfg) - ) - - def _get_specified_version_cfg(self, data): - """Get the configuration for the current runtime. - If there's no matched configuration in the input yaml, we'll - use the `default` field of yaml. - - Args: - data (Yaml content): input yaml file. - - Returns: - [dictionary]: the content for specific version. - """ - default_config = None - for sub_data in data: - if sub_data["version"]["name"] == self.version: - return sub_data - - if sub_data["version"]["name"] == "default": - default_config = sub_data - - return default_config - - def get_version(self): - """Get the current backend's version string.""" - return deepcopy(self.cur_config["version"]["name"]) - - def get_precisions(self): - """Get the supported low precisions, e.g ['int8', 'bf16']""" - return deepcopy(self.cur_config["precisions"]["names"]) - - def get_op_types(self): - """Get the op types for specific backend per low precision. - - e.g {'1.6.0': {'int8': ['Conv2D', 'fully_connected']}} - """ - return deepcopy(self.cur_config["ops"]) - - def get_fuse_patterns(self): - """Get the fusion patterns for specified op type for every specific precision.""" - return deepcopy(self.cur_config["patterns"]) - - def get_quantization_capability(self): - """Get the quantization capability of low precision op types. - - e.g, granularity, scheme and etc. - """ - return deepcopy(self.cur_config["capabilities"]) - - def get_mixed_precision_combination(self): - """Get the valid precision combination base on hardware and user' config. - - e.g['fp32', 'bf16', 'int8'] - """ - if self.cur_config["precisions"]["valid_mixed_precisions"]: - return [i.strip() for i in self.cur_config["precisions"]["valid_mixed_precisions"]] - - return [i.strip() for i in self.get_precisions().split(",")] diff --git a/neural_compressor/adaptor/mxnet.yaml b/neural_compressor/adaptor/mxnet.yaml deleted file mode 100644 index 3d1f7ad5b69..00000000000 --- a/neural_compressor/adaptor/mxnet.yaml +++ /dev/null @@ -1,355 +0,0 @@ -# -# Copyright (c) 2021 Intel Corporation -# -# Licensed 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. - ---- -- - version: - name: 'default' - # (MXNet-specific): - # 'int8' for this version specifies quantization in general (both int8 and uint8), as - # quantization dtype is selected automatically for each quantized node according to the - # calibration results: min_value < 0 => int8; min_value >= 0 => uint8. - # 'int8' here means 'auto' in MXNet - - precisions: &common_precisions - names: int8, fp32 - valid_mixed_precisions: [] - - ops: &common_ops - int8: [ 'quantize_output', - 'Flatten', 'Pooling', 'Convolution', 'FullyConnected', - '_sg_mkldnn_conv', '_sg_mkldnn_fully_connected', - '_sg_mkldnn_selfatt_qk', '_sg_mkldnn_selfatt_valatt', - '_sg_onednn_conv', '_sg_onednn_fully_connected', - '_sg_onednn_selfatt_qk', '_sg_onednn_selfatt_valatt' ] - fp32: ['*'] - - capabilities: &common_capabilities - int8: { - 'default': &capability_default [ - {'activation': { - 'dtype': ['int8'], - 'quant_mode': 'static', - 'scheme': ['sym'], - 'granularity': ['per_tensor'], - 'algorithm': ['minmax', 'kl']}, - 'weight': { - 'dtype': ['int8'], - 'scheme': ['sym'], - 'granularity': ['per_tensor'], - 'algorithm': ['minmax']} - }, - {'activation': { - 'dtype': 'fp32'}, - 'weight': { - 'dtype': 'fp32'} - }, - {'activation': { - 'dtype': ['int8'], - 'quant_mode': 'dynamic', - 'scheme': ['sym'], - 'granularity': ['per_tensor'], - 'algorithm': ['minmax', 'kl']}, - 'weight': { - 'dtype': ['int8'], - 'scheme': ['sym'], - 'granularity': ['per_tensor'], - 'algorithm': ['minmax']} - } - ], - 'quantize_output': &capability_quantize [ - {'activation': { - 'dtype': ['int8'], - 'quant_mode': 'static', - 'scheme': ['sym'], - 'granularity': ['per_tensor'], - 'algorithm': ['minmax', 'kl']} - }, - {'activation': { - 'dtype': ['int8'], - 'quant_mode': 'dynamic', - 'scheme': ['sym'], - 'granularity': ['per_tensor'], - 'algorithm': ['minmax', 'kl']} - } - ], - 'Flatten': &capability_flatten [ - {'activation': { - 'dtype': ['int8'], - 'quant_mode': 'static', - 'scheme': ['sym'], - 'granularity': ['per_tensor'], - 'algorithm': ['minmax', 'kl']} - }, - {'activation': { - 'dtype': ['int8'], - 'quant_mode': 'dynamic', - 'scheme': ['sym'], - 'granularity': ['per_tensor'], - 'algorithm': ['minmax', 'kl']} - }, - {'activation': { - 'dtype': 'fp32'} - } - ], - 'Pooling': &capability_pooling [ - {'activation': { - 'dtype': ['int8'], - 'quant_mode': 'static', - 'scheme': ['sym'], - 'granularity': ['per_tensor'], - 'algorithm': ['minmax', 'kl']} - }, - {'activation': { - 'dtype': ['int8'], - 'quant_mode': 'dynamic', - 'scheme': ['sym'], - 'granularity': ['per_tensor'], - 'algorithm': ['minmax', 'kl']} - }, - {'activation': { - 'dtype': 'fp32'} - } - ], - 'Convolution': &capability_conv [ - {'activation': { - 'dtype': ['int8'], - 'quant_mode': 'static', - 'scheme': ['sym'], - 'granularity': ['per_tensor'], - 'algorithm': ['minmax', 'kl']}, - 'weight': { - 'dtype': ['int8'], - 'scheme': ['sym'], - 'granularity': ['per_channel'], - 'algorithm': ['minmax']} - }, - {'activation': { - 'dtype': 'fp32'}, - 'weight': { - 'dtype': 'fp32'} - }, - {'activation': { - 'dtype': ['int8'], - 'quant_mode': 'dynamic', - 'scheme': ['sym'], - 'granularity': ['per_tensor'], - 'algorithm': ['minmax', 'kl']}, - 'weight': { - 'dtype': ['int8'], - 'scheme': ['sym'], - 'granularity': ['per_channel'], - 'algorithm': ['minmax']} - } - ], - 'FullyConnected': &capability_fc [ - {'activation': { - 'dtype': ['int8'], - 'quant_mode': 'static', - 'scheme': ['sym'], - 'granularity': ['per_tensor'], - 'algorithm': ['minmax', 'kl']}, - 'weight': { - 'dtype': ['int8'], - 'scheme': ['sym'], - 'granularity': ['per_tensor'], - 'algorithm': ['minmax']} - }, - {'activation': { - 'dtype': 'fp32'}, - 'weight': { - 'dtype': 'fp32'} - }, - {'activation': { - 'dtype': ['int8'], - 'quant_mode': 'dynamic', - 'scheme': ['sym'], - 'granularity': ['per_tensor'], - 'algorithm': ['minmax', 'kl']}, - 'weight': { - 'dtype': ['int8'], - 'scheme': ['sym'], - 'granularity': ['per_tensor'], - 'algorithm': ['minmax']} - } - ], - '_sg_mkldnn_conv': *capability_conv, - '_sg_mkldnn_fully_connected': *capability_fc, - '_sg_mkldnn_selfatt_qk': &capability_mkldnn_qk [ - {'activation': { - 'dtype': ['int8'], - 'quant_mode': 'static', - 'scheme': ['sym'], - 'granularity': ['per_tensor'], - 'algorithm': ['minmax', 'kl']} - }, - {'activation': { - 'dtype': ['int8'], - 'quant_mode': 'dynamic', - 'scheme': ['sym'], - 'granularity': ['per_tensor'], - 'algorithm': ['minmax', 'kl']} - }, - {'activation': { - 'dtype': 'fp32'} - } - ], - '_sg_mkldnn_selfatt_valatt': &capability_mkldnn_valatt [ - {'activation': { - 'dtype': ['int8'], - 'quant_mode': 'static', - 'scheme': ['sym'], - 'granularity': ['per_tensor'], - 'algorithm': ['minmax', 'kl']} - }, - {'activation': { - 'dtype': ['int8'], - 'quant_mode': 'dynamic', - 'scheme': ['sym'], - 'granularity': ['per_tensor'], - 'algorithm': ['minmax', 'kl']} - }, - {'activation': { - 'dtype': 'fp32'} - } - ], - '_sg_onednn_conv': *capability_conv, - '_sg_onednn_fully_connected': *capability_fc, - '_sg_onednn_selfatt_qk': *capability_mkldnn_qk, - '_sg_onednn_selfatt_valatt': *capability_mkldnn_valatt - } - - patterns: &common_patterns - # (MXNet-specific): - # fusion patterns are hardcoded in the framework - fp32: [ - # conv + bn - # conv + act + sum - # conv + add - # conv + bn + act - # conv + bn + add + act - # conv + bn + sum + act - # fc + relu - ] - int8: [] - -- - version: - name: '1.6.0' - - precisions: - <<: *common_precisions - - ops: - <<: *common_ops - - capabilities: - << : *common_capabilities - - patterns: - << : *common_patterns - -- - version: - name: '1.7.0' - - precisions: - <<: *common_precisions - - ops: - <<: *common_ops - - capabilities: - << : *common_capabilities - - patterns: - << : *common_patterns - -- - version: - name: '1.8.0' - - precisions: - <<: *common_precisions - - ops: - <<: *common_ops - - capabilities: - << : *common_capabilities - - patterns: - << : *common_patterns - -- - version: - name: '1.9.0' - - precisions: - <<: *common_precisions - - ops: - <<: *common_ops - - capabilities: - << : *common_capabilities - - patterns: - << : *common_patterns - -- - version: - name: '2.0.0' - - precisions: - names: int8, fp32, bf16 - valid_mixed_precisions: [] - - ops: - <<: *common_ops - bf16: [ 'Convolution', 'Deconvolution', 'FullyConnected', - '_sg_mkldnn_conv', '_sg_mkldnn_fully_connected', - '_sg_mkldnn_selfatt_qk', '_sg_mkldnn_selfatt_valatt', - '_sg_onednn_conv', '_sg_onednn_fully_connected', - '_sg_onednn_selfatt_qk', '_sg_onednn_selfatt_valatt' ] - - capabilities: - << : *common_capabilities - bf16: { - 'Convolution': &bf16_capability_conv { - 'activation': {'dtype': ['bf16', 'fp32']}, - 'weight': {'dtype': ['bf16', 'fp32']}, - }, - 'FullyConnected': &bf16_capability_fc { - 'activation': {'dtype': ['bf16', 'fp32']}, - 'weight': {'dtype': ['bf16', 'fp32']}, - }, - '_sg_mkldnn_conv': *bf16_capability_conv, - '_sg_mkldnn_fully_connected': *bf16_capability_fc, - '_sg_mkldnn_selfatt_qk': &bf16_capability_mkldnn_qk { - 'activation': {'dtype': ['bf16', 'fp32']}, - }, - '_sg_mkldnn_selfatt_valatt': &bf16_capability_mkldnn_valatt { - 'activation': {'dtype': ['bf16', 'fp32']}, - }, - '_sg_onednn_conv': *bf16_capability_conv, - '_sg_onednn_fully_connected': *bf16_capability_fc, - '_sg_onednn_selfatt_qk': *bf16_capability_mkldnn_qk, - '_sg_onednn_selfatt_valatt': *bf16_capability_mkldnn_valatt - } - - - patterns: - << : *common_patterns diff --git a/neural_compressor/adaptor/mxnet_utils/__init__.py b/neural_compressor/adaptor/mxnet_utils/__init__.py deleted file mode 100644 index 73ea24d7f04..00000000000 --- a/neural_compressor/adaptor/mxnet_utils/__init__.py +++ /dev/null @@ -1,18 +0,0 @@ -"""Mxnet util init.""" - -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# Copyright (c) 2021 Intel Corporation -# -# Licensed 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. diff --git a/neural_compressor/adaptor/mxnet_utils/util.py b/neural_compressor/adaptor/mxnet_utils/util.py deleted file mode 100644 index 73fa74b4735..00000000000 --- a/neural_compressor/adaptor/mxnet_utils/util.py +++ /dev/null @@ -1,925 +0,0 @@ -"""Mxnet util module.""" - -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# Copyright (c) 2021 Intel Corporation -# -# Licensed 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 ctypes -import json -import os -import re -from enum import Enum -from tempfile import TemporaryDirectory - -import numpy as np - -from neural_compressor.model.mxnet_model import MXNetModel as NCModel -from neural_compressor.utils.utility import LazyImport - -mx = LazyImport("mxnet") - - -QUANTIZE_OP_NAME = "quantize_output" -QUANTIZE_OP_NAMES = ["_contrib_quantize_v2"] -QUANTIZE_DEFAULT_ALGORITHM = "minmax" -QUANTIZE_NODE_POSTFIX = "_quantize" -QUANTIZED_NODE_PREFIX = "quantized_" -QUANTIZATION_DTYPES = [np.int8, np.uint8] -NULL_OP_NAMES = ["null"] - - -class OpType(Enum): - """Enum op types.""" - - NORMAL = 0 - QUANTIZE = 1 - QUANTIZED = 2 - - -def isiterable(obj) -> bool: - """Checks whether object is iterable. - - Args: - obj : object to check. - - Returns: - boolean: True if object is iterable, else False. - """ - try: - iter(obj) - return True - except TypeError: - return False - - -def ensure_list(x): - """Ensures that object is a list. - - Args: - x : input. - - Returns: - list: x if x is list, else [x]. - """ - return x if isinstance(x, (tuple, list)) else [x] - - -def check_mx_version(version): - """Checks MXNet version. - - Args: - version (str): version to check. - - Returns: - boolean: True if mx.__version__ >= version, else False. - """ - d1 = re.split(r"\.", mx.__version__) - d2 = re.split(r"\.", version) - d1 = [int(d1[i]) for i in range(len(d1))] - d2 = [int(d2[i]) for i in range(len(d2))] - return d1 >= d2 - - -def combine_capabilities(current, new): - """Combine capabilities. - - Args: - current (dict): current capabilities. - new (dict): new capabilities. - - Returns: - dict: contains all capabilities. - """ - if isinstance(current, list): - assert isinstance(new, list) - return list(set(current) | set(new)) - assert isinstance(current, dict) and isinstance(new, dict) - current = current.copy() - new = new.copy() - for k, v in current.items(): - current[k] = combine_capabilities(v, new.get(k, type(v)())) - new.pop(k, None) - current.update(new) - return current - - -def make_nc_model(target, sym_model, ctx, input_desc): - """Converts a symbolic model to an Neural Compressor model. - - Args: - target (object): target model type to return. - sym_model (tuple): symbol model (symnet, args, auxs). - input_desc (list): model input data description. - - Returns: - NCModel: converted neural_compressor model - """ - assert isinstance(sym_model, tuple) and isinstance(sym_model[0], mx.symbol.Symbol) - - if isinstance(target.model, mx.gluon.HybridBlock): - return NCModel(make_symbol_block(sym_model, ctx, input_desc)) - return NCModel(sym_model) - - -def fuse(sym_model, ctx): - """Fuse the supplied model. - - Args: - sym_model (tuple): symbol model (symnet, args, auxs). - - Returns: - tuple: fused symbol model (symnet, args, auxs). - """ - assert isinstance(sym_model, tuple) and isinstance(sym_model[0], mx.symbol.Symbol) - - symnet, args, auxs = sym_model - framework = get_framework_name(ctx) - if framework is not None: - if check_mx_version("2.0.0"): - symnet = symnet.optimize_for(framework) - else: - symnet = symnet.get_backend_symbol(framework) - return (symnet, args, auxs) - - -def get_framework_name(ctx): - """Get the framework name by version. - - Args: - ctx (object): mxnet context object. - - Returns: - str: framework name. - """ - if "cpu" in ctx.device_type: - if check_mx_version("2.0.0"): - return "ONEDNN_QUANTIZE" - else: - return "MKLDNN_QUANTIZE" - return None - - -def prepare_model_data(nc_model, ctx, data_x): - """Prepares sym_model and dataloader needed for quantization, calibration or running. - - Args: - nc_model (object): model to prepare. - data_x (object): data iterator/loader to prepare. - - Returns: - tuple: symbol model (symnet, args, auxs) and DataLoaderWrap. - """ - dataloader = prepare_dataloader(nc_model, ctx, data_x) - sym_model = prepare_model(nc_model, ctx, dataloader.input_desc) - return sym_model, dataloader - - -def prepare_model(nc_model, ctx, input_desc): - """Prepare model. - - Args: - nc_model (object): model to prepare. - ctx (object): mxnet context object. - input_desc (list): input list of mxnet data types. - - Returns: - object: mxnet model (symnet, args, auxs). - """ - assert isinstance(nc_model, NCModel) - - model_x = nc_model.model - if isinstance(model_x, mx.gluon.HybridBlock): - if not model_x._active: - model_x.hybridize(static_alloc=False, static_shape=False) - model_x(*create_data_example(ctx, input_desc)) - with TemporaryDirectory() as tmpdirname: - prefix = os.path.join(tmpdirname, "tmp") - model_x.export(prefix, epoch=0, remove_amp_cast=False) - sym_model = mx.model.load_checkpoint(prefix, 0) - elif isinstance(model_x, tuple) and isinstance(model_x[0], mx.symbol.Symbol): - sym_model = model_x - else: - raise TypeError("Wrong model type") - if not is_model_quantized(sym_model): - sym_model = fuse(sym_model, ctx) - return _match_array_semantics(sym_model) - - -def create_data_example(ctx, input_desc): - """Create data example by mxnet input description and ctx. - - Args: - ctx (object): mxnet context object. - input_desc (list): input list of mxnet data types. - - Returns: - list: data example. - """ - if mx.is_np_array(): - return [mx.np.zeros(d.shape, ctx=ctx, dtype=d.dtype) for d in input_desc] - else: - return [mx.nd.zeros(d.shape, ctx=ctx, dtype=d.dtype) for d in input_desc] - - -def _match_array_semantics(sym_model): - """Match array semantics. - - Args: - sym_model (tuple): symbol model (symnet, args, auxs). - - Returns: - tuple: symbol model (symnet, args, auxs). - """ - if check_mx_version("2.0.0") and mx.util.is_np_array(): - symnet, args, auxs = sym_model - symnet = symnet.as_np_ndarray() - for k, v in args.items(): - args[k] = v.as_np_ndarray() - for k, v in auxs.items(): - auxs[k] = v.as_np_ndarray() - sym_model = (symnet, args, auxs) - return sym_model - - -def prepare_dataloader(nc_model, ctx, data_x): - """Prepare dataloader. - - Args: - nc_model (object): model to prepare. - ctx (object): mxnet context object. - data_x (object): mxnet io iterable object or dataloader object. - - Returns: - object: dataloader. - """ - assert isinstance(nc_model, NCModel) - - if isinstance(data_x, DataLoaderWrap): - return data_x - - dataloader = data_x - if isinstance(dataloader, mx.io.DataIter): - dataloader = DataIterLoader(dataloader) - assert isiterable(dataloader), "Dataloader must be iterable (mx.gluon.data.DataLoader-like)" - - model_x = nc_model.model - if isinstance(model_x, mx.gluon.HybridBlock): - data = ensure_list(next(iter(dataloader))) # data example - data = [ndarray_to_device(d, ctx) for d in data] - if not model_x._active: - model_x.hybridize(static_alloc=False, static_shape=False) - while True: - try: - model_x(*data) - except (ValueError, TypeError): - del data[-1] # remove label - else: - break - inputs, _ = model_x._cached_graph - input_desc = [mx.io.DataDesc(name=i.name, shape=d.shape, dtype=d.dtype) for i, d in zip(inputs, data)] - elif isinstance(model_x, tuple) and isinstance(model_x[0], mx.symbol.Symbol): - assert hasattr( - data_x, "provide_data" - ), "Dataloader must provide data information (mx.io.DataDesc for each input)" - input_desc = data_x.provide_data - else: - raise TypeError("Wrong model type") - return DataLoaderWrap(dataloader, input_desc) - - -def ndarray_to_device(ndarray, device): - """Ndarray to device. - - Args: - ndarray (ndarray): model to prepare. - device (object): mxnet device object. - - Returns: - ndarray: ndarray on the device. - """ - try: - return ndarray.to_device(device) # 2.x version - except AttributeError: - return ndarray.as_in_context(device) # 1.x version - - -def is_model_quantized(sym_model): - """Checks whether the model is quantized. - - Args: - sym_model (tuple): symbol model (symnet, args, auxs). - - Returns: - boolean: True if model is quantized, else False. - """ - assert isinstance(sym_model, tuple) and isinstance(sym_model[0], mx.symbol.Symbol) - - for sym in sym_model[0].get_internals(): - _, op_type = _dequantize_sym_name(sym.name) - if op_type != OpType.NORMAL: - return True - return False - - -def _dequantize_sym_name(sym_name, check_list=None): - """Revise sym name by the node prefix or postfix. - - Args: - sym_name (str): sym name. - - Returns: - tuple: (name, op_type). - """ - name = sym_name - op_type = OpType.NORMAL - if sym_name.endswith(QUANTIZE_NODE_POSTFIX): - op_type = OpType.QUANTIZE - name = sym_name[: -len(QUANTIZE_NODE_POSTFIX)] - elif sym_name.startswith(QUANTIZED_NODE_PREFIX): - op_type = OpType.QUANTIZED - name = sym_name[len(QUANTIZED_NODE_PREFIX) :] - assert ( - check_list is None or name in check_list - ), "name of the quantized symbol must be in the following format: " '"{}_". Symbol: {}'.format( - QUANTIZED_NODE_PREFIX, name - ) - return (name, op_type) - - -def query_quantizable_nodes(sym_model, ctx, dataloader): - """Query quantizable nodes of the given model. - - Args: - sym_model (tuple): symbol model (symnet, args, auxs) to query. - - Returns: - list: quantizable nodes of the given model. - dict: tensor to node mapping. - """ - assert isinstance(sym_model, tuple) and isinstance(sym_model[0], mx.symbol.Symbol) - assert isinstance(dataloader, DataLoaderWrap) - - symnet = sym_model[0] - nodes_ops = {n["name"]: n["op"].lower() for n in json.loads(symnet.tojson())["nodes"]} - - qmodel, calib_tensors = quantize_sym_model(sym_model, ctx, {"quantized_dtype": "auto", "quantize_mode": "smart"}) - qsymnet = qmodel[0] - qnodes_ops = {n["name"]: n["op"].lower() for n in json.loads(qsymnet.tojson())["nodes"]} - - # collect fp32 tensors - collector = NameCollector() - run_forward(sym_model, ctx, dataloader, [True], collector) - tensors = set(collector.names) - - # map tensors to nodes - tensor_to_node = {} - nodes = set(nodes_ops.keys()) - for tensor in tensors: - node = _tensor_to_node(tensor, nodes) - if node != "": - tensor_to_node[tensor] = node - elif tensor in calib_tensors: - tensor_to_node[tensor] = tensor - assert set(calib_tensors).issubset(set(tensor_to_node.keys())) - calib_nodes = [tensor_to_node[ct] for ct in calib_tensors] - - quantizable = {} - for qsym in qsymnet.get_internals(): - if qnodes_ops[qsym.name] in NULL_OP_NAMES: - continue - sym_name, op_type = _dequantize_sym_name(qsym.name, nodes_ops.keys()) - node_name = _tensor_to_node(sym_name, nodes_ops.keys()) - node_name = sym_name if node_name == "" else node_name - assert qnodes_ops[qsym.name] not in QUANTIZE_OP_NAMES or ( - op_type == OpType.QUANTIZE - ), 'Quantize node was not recognised properly. Node name: "{}"'.format(node_name) - if node_name in calib_nodes: - if op_type == OpType.QUANTIZE: - quantizable[node_name] = QUANTIZE_OP_NAME - elif op_type == OpType.QUANTIZED: - quantizable[node_name] = nodes_ops[node_name] - - quantizable_nodes = [{"name": name, "type": op} for (name, op) in quantizable.items()] - op_nodes = {k: v for k, v in nodes_ops.items() if v not in NULL_OP_NAMES} - return quantizable_nodes, tensor_to_node, op_nodes - - -def quantize_sym_model(sym_model, ctx, qconfig): - """Quantizes the symbolic model according to the configuration. - - Args: - sym_model (tuple): symbol model (symnet, args, auxs). - qconfig (dict): quantization configuration. - - Returns: - tuple: Symbol model (symnet, args, auxs) and list of tensors for calibration. - """ - assert isinstance(sym_model, tuple) and isinstance(sym_model[0], mx.symbol.Symbol) - - symnet, args, auxs = sym_model - if not check_mx_version("1.7.0"): - qconfig.pop("quantize_granularity", None) - - arguments = {"sym": symnet, "offline_params": list(args.keys())} - arguments.update(qconfig) - if check_mx_version("2.0.0"): - arguments["device"] = ctx - else: - arguments["ctx"] = ctx - qsymnet, calib_tensors = mx.contrib.quantization._quantize_symbol(**arguments) - # args = mx.contrib.quantization._quantize_params(qsymnet, args, {}) - return ((qsymnet, args, auxs), calib_tensors) - - -def _tensor_to_node(tensor, nodes): - """Map tensor to one of the nodes. - - This function assumes, that node tensors (weights, outputs, etc) contain node name in their names. - """ - assert len(nodes) > 0, "`nodes` cannot be empty" - - PATTERNS = {"", "_output[0-9]*$", "_[0-9]+$"} - mapping = [] - for pattern in PATTERNS: - node = re.sub(pattern, "", tensor) - if node in nodes and node not in mapping: - mapping.append(node) - assert len(mapping) == 1, "Tensor matched to more than one node. " "Tensor: {}, matched: {}".format( - tensor, mapping - ) - return mapping[0] if len(mapping) > 0 else "" - - -def _qtensor_to_tensor(qtensor, tensors): - """Map quantized tensor to its fp32 equivalent. - - New ops may require updating the patterns. - Tensors of quantize nodes (which are not present in fp32 models) will be mapped to their input - nodes. - """ - assert len(tensors) > 0, "`tensors` cannot be empty" - - PATTERNS = { - "quantize": "", - "_quantize_output0": "", - "_quantize_0": "", - "_0_quantize_output0": "_output", - "_0_quantize_0": "_output", - "_([0-9]+)_quantize_output0": "_output\g<1>", - "_([0-9]+)_quantize_0": "_output\g<1>", - "quantized_": "", - } - mapping = [] - for pattern, repl in PATTERNS.items(): - tensor = re.sub(pattern, repl, qtensor) - if tensor in tensors and tensor not in mapping: - mapping.append(tensor) - assert ( - len(mapping) == 1 - ), "Quantized tensor matched more than one fp32 tensor. " "Quantized tensor: {}, matched: {}".format( - qtensor, mapping - ) - return mapping[0] if len(mapping) > 0 else "" - - -def run_forward(sym_model, ctx, dataloader, b_filter, collector=None, pre_batch=None, post_batch=None): - """Run forward propagation on the model. - - Args: - sym_model (tuple): symbol model (symnet, args, auxs). - dataloader (DataLoaderWrap): data loader. - b_filter (generator): filter on which batches to run inference on. - collector (object): collects information during inference. - pre_batch: function to call prior to batch inference. - post_batch: function to call after batch inference. - - Returns: - int: batch count. - """ - assert isinstance(dataloader, DataLoaderWrap) - assert collector is None or (hasattr(collector, "collect_gluon") and hasattr(collector, "collect_module")) - - if check_mx_version("2.0.0"): - sym_block = make_symbol_block(sym_model, ctx, dataloader.input_desc) - if collector is not None: - sym_block.register_op_hook(collector.collect_gluon, monitor_all=True) - return _gluon_forward(sym_block, ctx, dataloader, b_filter, pre_batch, post_batch) - else: - mod = make_module(sym_model, ctx, dataloader.input_desc) - if collector is not None: - mod._exec_group.execs[0].set_monitor_callback(collector.collect_module, monitor_all=True) - return _module_forward(mod, dataloader, b_filter, pre_batch, post_batch) - - -def make_symbol_block(sym_model, ctx, input_desc): - """Convert a symbol model to gluon SymbolBlock. - - Args: - sym_model (tuple): symbol model (symnet, args, auxs). - input_desc (list): model input data description. - - Returns: - mx.gluon.SymbolBlock: SymbolBlock model. - """ - assert isinstance(sym_model, tuple) and isinstance(sym_model[0], mx.symbol.Symbol) - - symnet, args, auxs = sym_model - inputs = [mx.sym.var(d.name) for d in input_desc] - sym_block = mx.gluon.SymbolBlock(symnet, inputs) - param_dict = args - param_dict.update(auxs) - if check_mx_version("2.0.0"): - sym_block.load_dict(param_dict, cast_dtype=True, dtype_source="saved", allow_missing=True) - else: - # params = {'arg:' + name: param for name, param in args.items()} - # params.update({'aux:' + name: param for name, param in auxs.items()}) - sym_block.collect_params().load_dict(param_dict, ctx=ctx, cast_dtype=True, dtype_source="saved") - return sym_block - - -def _gluon_forward(net, ctx, dataloader, b_filter, pre_batch=None, post_batch=None): - """Gluon forward func.""" - batch_num = 0 - for run, batch in zip(b_filter, dataloader): - if not run: - continue - batch_num += 1 - batch = ensure_list(batch) - batch = [ndarray_to_device(d, ctx) for d in batch] - data = batch[: len(dataloader.input_desc)] - label = batch[len(dataloader.input_desc) :] - - if pre_batch is not None: - pre_batch(net, (data, label)) - - out = net(*data) - - if post_batch is not None: - post_batch(net, (data, label), out) - return batch_num - - -def make_module(sym_model, ctx, input_desc): - """Convert a symbol model to Module. - - Args: - sym_model (tuple): symbol model (symnet, args, auxs). - input_desc (list): model input data description. - - Returns: - mx.module.Module: Module model. - """ - assert isinstance(sym_model, tuple) and isinstance(sym_model[0], mx.symbol.Symbol) - - symnet, args, auxs = sym_model - mod = mx.module.module.Module(symbol=symnet, data_names=[d.name for d in input_desc], label_names=None, context=ctx) - mod.bind(input_desc, for_training=False) - mod.set_params(args, auxs, allow_missing=True) - return mod - - -def _module_forward(module, dataloader, b_filter, pre_batch=None, post_batch=None): - """Module forward func.""" - batch_num = 0 - for run, batch in zip(b_filter, dataloader): - if not run: - continue - batch_num += 1 - data = batch[: len(dataloader.input_desc)] - label = batch[len(dataloader.input_desc) :] - - if pre_batch is not None: - pre_batch(module, (data, label)) - - module.forward(mx.io.DataBatch(data=data), is_train=False) - - if post_batch is not None: - post_batch(module, (data, label), module.get_outputs()) - return batch_num - - -def parse_tune_config(tune_cfg, quantizable_nodes): - """Convert the strategy config to MXNet quantization config. - - Args: - tune_cfg (dict): tune config from neural_compressor strategy. - quantizable_nodes (list): quantizable nodes in the model. - - Returns: - dict: quantization configuration. - dict: calibration configuration. - """ - excluded_symbols = [] - calib_minmax_nodes = set() - calib_kl_nodes = set() - amp_excluded_nodes = set() - - for op in quantizable_nodes: - cfg = tune_cfg["op"][(op["name"], op["type"])]["activation"] - if cfg["dtype"] not in ["bf16"]: - amp_excluded_nodes.add(op["name"]) - if cfg["dtype"] not in ["int8"]: - excluded_symbols.append(op["name"]) - # config for quantize node, that might be added after this node - # (to quantize its output) - cfg["algorithm"] = QUANTIZE_DEFAULT_ALGORITHM - - if cfg["algorithm"] == "kl": - calib_kl_nodes.add(op["name"]) - else: - calib_minmax_nodes.add(op["name"]) - assert len(calib_kl_nodes & calib_minmax_nodes) == 0 - - quant_cfg = {"excluded_symbols": excluded_symbols, "quantized_dtype": "auto", "quantize_mode": "smart"} - if check_mx_version("1.7.0"): - quant_cfg["quantize_granularity"] = "tensor-wise" - - calib_cfg = { - "quantized_dtype": quant_cfg["quantized_dtype"], - "batches": tune_cfg["calib_iteration"], - "calib_mode": "naive", - "calib_kl_nodes": calib_kl_nodes, - "calib_minmax_nodes": calib_minmax_nodes, - } - - amp_cfg = {"target_dtype": "bfloat16", "excluded_sym_names": amp_excluded_nodes} - - return quant_cfg, calib_cfg, amp_cfg - - -def distribute_calib_tensors(calib_tensors, calib_cfg, tensor_to_node): - """Distributes the tensors for calibration, depending on the algorithm set in the configuration of their nodes. - - Args: - calib_tensors: tensors to distribute. - calib_cfg (dict): calibration configuration. - tensor_to_node (dict): tensor to node mapping. - - Returns: - tuple: kl tensors and minmax tensors. - """ - calib_tensors = set(calib_tensors) - kl_tensors = {} - minmax_tensors = {} - for cl in calib_tensors: - assert cl in tensor_to_node, "`calib_tensors` entry matched no node. Entry: {}".format(cl) - node = tensor_to_node[cl] - if node in calib_cfg["calib_kl_nodes"]: - kl_tensors[cl] = node - if node in calib_cfg["calib_minmax_nodes"]: - minmax_tensors[cl] = node - - kl_tensors = set(kl_tensors.keys()) - minmax_tensors = set(minmax_tensors.keys()) - assert ( - len(kl_tensors & minmax_tensors) == 0 - ), "same `calib_tensors` entries matched both kl " "and minmax nodes. Entries: {}".format( - kl_tensors & minmax_tensors - ) - - # `rest` are the nodes that require calibration because of some node being excluded - # for example: input -> quantize -> conv_1 -> pooling -> conv_2 - # when conv_1 is quantized, pooling output does not require calibration - # when conv_1 is excluded, pooling output requires calibration (as it is input of a quantized - # node): input -> conv_1 -> pooling -> quantize -> conv_2 - rest = calib_tensors - (kl_tensors | minmax_tensors) - minmax_tensors |= rest # assign them to the minmax algorithm by default - - return (kl_tensors, minmax_tensors) - - -def calib_model(qsym_model, calib_data, calib_cfg): - """Calibrate the quantized symbol model using data gathered by the collector. - - Args: - qsym_model (tuple): quantized symbol model (symnet, args, auxs). - calib_data (CalibData): data needed for calibration (thresholds). - calib_cfg (dict): calibration configuration. - - Returns: - tuple: quantized calibrated symbol model (symnet, args, auxs). - """ - assert isinstance(qsym_model, tuple) and isinstance(qsym_model[0], mx.symbol.Symbol) - - qsymnet, qargs, auxs = qsym_model - if check_mx_version("2.0.0"): - return mx.contrib.quantization.calib_graph(qsymnet, qargs, auxs, calib_data, calib_cfg["calib_mode"]) - else: - return mx.contrib.quantization.calib_graph( - qsymnet, qargs, auxs, calib_data, calib_cfg["calib_mode"], quantized_dtype=calib_cfg["quantized_dtype"] - ) - - -def amp_convert(sym_model, input_desc, amp_cfg): - """Convert model to support amp.""" - assert check_mx_version("2.0.0"), ( - "AMP is supported since MXNet 2.0. This error is due to " "an error in the configuration file." - ) - from mxnet import amp - - input_dtypes = {i.name: i.dtype for i in input_desc} - return amp.convert_model(*sym_model, input_dtypes, **amp_cfg, cast_params_offline=True) - - -class DataLoaderWrap: - """DataLoader Wrap.""" - - def __init__(self, dataloader, input_desc): - """Initialize.""" - self.dataloader = dataloader - self.input_desc = input_desc - self._iter = None - - def __iter__(self): - """Iter.""" - self._iter = iter(self.dataloader) - return self - - def __next__(self): - """Next.""" - return next(self._iter) - - -class DataIterLoader: - """DataIterLoader.""" - - def __init__(self, data_iter): - """Initialize.""" - self.data_iter = data_iter - - def __iter__(self): - """Iter.""" - self.data_iter.reset() - return self - - def __next__(self): - """Next.""" - batch = self.data_iter.__next__() - return batch.data + (batch.label if batch.label is not None else []) - - -class CollectorBase: - """Collector Base class.""" - - def collect_gluon(self, name, _, arr): - """Collect by gluon api.""" - raise NotImplementedError() - - def collect_module(self, name, arr): - """Collect by module name.""" - name = mx.base.py_str(name) - handle = ctypes.cast(arr, mx.base.NDArrayHandle) - arr = mx.nd.NDArray(handle, writable=False) - self.collect_gluon(name, "", arr) - - def pre_batch(self, m, b): - """Function to call prior to batch inference.""" - pass - - def post_batch(self, m, b, o): - """Function to call after batch inference.""" - pass - - -class CalibCollector(CollectorBase): - """Collect the calibration thresholds depending on the algorithm set.""" - - def __init__(self, include_tensors_kl, include_tensors_minmax, num_bins=8001): - """Initialize.""" - self.min_max_dict = {} - self.hist_dict = {} - self.num_bins = num_bins - self.include_tensors_minmax = include_tensors_minmax - self.include_tensors_kl = include_tensors_kl - - def collect_gluon(self, name, _, arr): - """Collect by gluon api.""" - if name in self.include_tensors_kl: - alg = "kl" - elif name in self.include_tensors_minmax: - alg = "minmax" - else: - return - - min_range = arr.min().asscalar() - max_range = arr.max().asscalar() - th = max(abs(min_range), abs(max_range)) - # minmax (always) - if name in self.min_max_dict: - cur_min_max = self.min_max_dict[name] - self.min_max_dict[name] = (min(cur_min_max[0], min_range), max(cur_min_max[1], max_range)) - else: - self.min_max_dict[name] = (min_range, max_range) - - if alg == "kl": # histogram only when kl is specified - arr = arr.asnumpy() - if name in self.hist_dict: - self.hist_dict[name] = self._combine_histogram(self.hist_dict[name], arr, min_range, max_range, th) - else: - hist, hist_edges = np.histogram(arr, bins=self.num_bins, range=(-th, th)) - self.hist_dict[name] = (hist, hist_edges, min_range, max_range, th) - - @staticmethod - def _combine_histogram(old_hist, arr, new_min, new_max, new_th): - """Combine histogram.""" - if check_mx_version("2.0.0"): - return mx.contrib.quantization._LayerHistogramCollector.combine_histogram( - old_hist, arr, new_min, new_max, new_th - ) - else: - return mx.contrib.quantization.combine_histogram(old_hist, arr, new_min, new_max, new_th) - - def calc_kl_th_dict(self, quantized_dtype): - """Calculation kl thresholds.""" - if len(self.hist_dict) > 0: - if check_mx_version("2.0.0"): - return mx.contrib.quantization._LayerHistogramCollector.get_optimal_thresholds( - self.hist_dict, quantized_dtype - ) - else: - return mx.contrib.quantization._get_optimal_thresholds(self.hist_dict, quantized_dtype) - return {} - - -class TensorCollector(CollectorBase): - """Tensors collector. - - Builds up qtensor_to_tensor mapping. - """ - - def __init__(self, include_nodes, qtensor_to_tensor, tensor_to_node): - """Initialize.""" - self.tensors_dicts = [] - self.include_nodes = include_nodes - self.qtensor_to_tensor = qtensor_to_tensor - self.tensor_to_node = tensor_to_node - - rest = set(self.include_nodes) - set(self.tensor_to_node.values()) - assert len(rest) == 0, "Unexpected tensors set to be collected: {}".format(rest) - - def collect_gluon(self, name, _, arr): - """Collect by gluon api.""" - is_quantized = False - if name not in self.tensor_to_node: - if name in self.qtensor_to_tensor: - name = self.qtensor_to_tensor[name] - else: - qname, name = name, _qtensor_to_tensor(name, self.tensor_to_node) - self.qtensor_to_tensor[qname] = name - if name == "": - return - is_quantized = arr.dtype in QUANTIZATION_DTYPES - - node = self.tensor_to_node[name] - if node in self.include_nodes: - self.tensors_dicts[-1].setdefault(node, {})[name] = (is_quantized, arr.copy()) - - def pre_batch(self, m, b): - """Preprocess.""" - self.tensors_dicts.append({}) - - -class NameCollector(CollectorBase): - """Name collector.""" - - def __init__(self): - """Initialize.""" - self.names = [] - - def collect_gluon(self, name, _, arr): - """Collect by gluon api.""" - self.names.append(name) - - -class CalibData: - """Calibration data class.""" - - def __init__(self, cache_kl={}, cache_minmax={}, tensors_kl=[], tensors_minmax=[]): - """Initialize.""" - self.th_dict = {} - self.th_dict.update({t: cache_kl[t] for t in tensors_kl}) - self.th_dict.update({t: cache_minmax[t] for t in tensors_minmax}) - - # `min_max_dict` is used as a thresholds dictionary when `calib_mode` == 'naive' - @property - def min_max_dict(self): - """Return mix-max dict.""" - return self.th_dict - - # for mxnet version >= 2.0.0 - def post_collect(self): - """Return mix-max dict for mxnet version >= 2.0.0.""" - return self.th_dict diff --git a/neural_compressor/benchmark.py b/neural_compressor/benchmark.py index 56d6afcf339..9cdbde59401 100644 --- a/neural_compressor/benchmark.py +++ b/neural_compressor/benchmark.py @@ -166,8 +166,6 @@ def run_instance(model, conf, b_dataloader=None, b_func=None): ) if framework == "keras": framework_specific_info.update({"workspace_path": options.workspace}) - if framework == "mxnet": - framework_specific_info.update({"b_dataloader": b_dataloader}) if "onnx" in framework: framework_specific_info.update( {"workspace_path": options.workspace, "graph_optimization": OPTIONS[framework].graph_optimization} diff --git a/neural_compressor/config.py b/neural_compressor/config.py index 8189e3aa766..95e97f8f974 100644 --- a/neural_compressor/config.py +++ b/neural_compressor/config.py @@ -2361,11 +2361,11 @@ def search(self, search): self._search = search -class MXNet: - """Base config class for MXNet.""" +class PyTorch: + """Base config class for PyTorch.""" def __init__(self, precisions=None): - """Init an MXNet object.""" + """Init an PyTorch object.""" self._precisions = precisions @property @@ -2383,7 +2383,7 @@ def precisions(self, precisions): self._precisions = precisions -class ONNX(MXNet): +class ONNX(PyTorch): """Config class for ONNX.""" def __init__(self, graph_optimization_level=None, precisions=None): @@ -2408,7 +2408,7 @@ def graph_optimization_level(self, graph_optimization_level): self._graph_optimization_level = graph_optimization_level -class TensorFlow(MXNet): +class TensorFlow(PyTorch): """Config class for TensorFlow.""" def __init__(self, precisions=None): @@ -2416,7 +2416,7 @@ def __init__(self, precisions=None): super().__init__(precisions) -class Keras(MXNet): +class Keras(PyTorch): """Config class for Keras.""" def __init__(self, precisions=None): @@ -2424,14 +2424,6 @@ def __init__(self, precisions=None): super().__init__(precisions) -class PyTorch(MXNet): - """Config class for PyTorch.""" - - def __init__(self, precisions=None): - """Init a PyTorch object.""" - super().__init__(precisions) - - quantization = PostTrainingQuantConfig() benchmark = BenchmarkConfig() options = Options() @@ -2443,7 +2435,6 @@ def __init__(self, precisions=None): tensorflow_config = TensorFlow() keras_config = Keras() pytorch_config = PyTorch() -mxnet_config = MXNet() class _Config: @@ -2460,7 +2451,6 @@ def __init__( onnxruntime=onnxruntime_config, tensorflow=tensorflow_config, pytorch=pytorch_config, - mxnet=mxnet_config, keras=keras_config, ): """Init a config object.""" @@ -2473,7 +2463,6 @@ def __init__( self._nas = nas self._tensorflow = tensorflow self._pytorch = pytorch - self._mxnet = mxnet self._keras = keras @property @@ -2501,11 +2490,6 @@ def pytorch(self): """Get the pytorch object.""" return self._pytorch - @property - def mxnet(self): - """Get the mxnet object.""" - return self._mxnet - @property def pruning(self): """Get the pruning object.""" diff --git a/neural_compressor/data/dataloaders/dataloader.py b/neural_compressor/data/dataloaders/dataloader.py index 2b8c3909ae9..360707ec347 100644 --- a/neural_compressor/data/dataloaders/dataloader.py +++ b/neural_compressor/data/dataloaders/dataloader.py @@ -15,7 +15,6 @@ # See the License for the specific language governing permissions and # limitations under the License. """Built-in dataloaders for multiple framework backends.""" -from .mxnet_dataloader import MXNetDataLoader from .onnxrt_dataloader import ONNXRTDataLoader from .pytorch_dataloader import PyTorchDataLoader from .tensorflow_dataloader import TensorflowDataLoader @@ -24,7 +23,6 @@ "tensorflow": TensorflowDataLoader, "tensorflow_itex": TensorflowDataLoader, "keras": TensorflowDataLoader, - "mxnet": MXNetDataLoader, "pytorch": PyTorchDataLoader, "pytorch_ipex": PyTorchDataLoader, "pytorch_fx": PyTorchDataLoader, @@ -89,8 +87,7 @@ def __new__( "onnxrt_qdqops", "onnxrt_qlinearops", "onnxrt_integerops", - "mxnet", - ), "framework support tensorflow pytorch mxnet onnxruntime" + ), "framework support tensorflow pytorch onnxruntime" return DATALOADERS[framework]( dataset=dataset, batch_size=batch_size, diff --git a/neural_compressor/data/dataloaders/mxnet_dataloader.py b/neural_compressor/data/dataloaders/mxnet_dataloader.py deleted file mode 100644 index 17772c7a4a3..00000000000 --- a/neural_compressor/data/dataloaders/mxnet_dataloader.py +++ /dev/null @@ -1,57 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# Copyright (c) 2021 Intel Corporation -# -# Licensed 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. -# ============================================================================== -"""MXNet Dataloader implementation.""" - -import logging - -from neural_compressor.utils.utility import LazyImport - -from .base_dataloader import BaseDataLoader - -mx = LazyImport("mxnet") - - -class MXNetDataLoader(BaseDataLoader): # pragma: no cover - """Subclass of BaseDataLoader.""" - - def _generate_dataloader( - self, - dataset, - batch_size, - last_batch, - collate_fn, - sampler, - batch_sampler, - num_workers, - pin_memory, - shuffle, - distributed, - ): - """Overwrite _generate_dataloader function.""" - if shuffle: - logging.warning("Shuffle is not supported yet in MXNetDataLoader, " "ignoring shuffle keyword.") - return mx.gluon.data.DataLoader( - dataset, - batch_size=batch_size, - batchify_fn=collate_fn, - last_batch=last_batch, - num_workers=num_workers, - pin_memory=pin_memory, - sampler=sampler, - batch_sampler=batch_sampler, - ) diff --git a/neural_compressor/data/dataloaders/tensorflow_dataloader.py b/neural_compressor/data/dataloaders/tensorflow_dataloader.py index 51db0ce8bbc..b526a763f9f 100644 --- a/neural_compressor/data/dataloaders/tensorflow_dataloader.py +++ b/neural_compressor/data/dataloaders/tensorflow_dataloader.py @@ -41,7 +41,7 @@ class TFDataDataLoader(BaseDataLoader): # pragma: no cover In tensorflow1.x dataloader is coupled with the graph, but it also support feed_dict method to do session run, this dataloader is designed to satisfy the usage of feed dict - in tf1.x. Although it's a general dataloader and can be used in MXNet and PyTorch. + in tf1.x. Although it's a general dataloader and can be used in PyTorch. Args: dataset: obj. wrapper of needed data. diff --git a/neural_compressor/data/datasets/coco_dataset.py b/neural_compressor/data/datasets/coco_dataset.py index b90e881ce2d..a501f54b1f4 100644 --- a/neural_compressor/data/datasets/coco_dataset.py +++ b/neural_compressor/data/datasets/coco_dataset.py @@ -38,7 +38,6 @@ from .dataset import Dataset, IterableDataset, dataset_registry tf = LazyImport("tensorflow") -mx = LazyImport("mxnet") torch = LazyImport("torch") @@ -160,7 +159,7 @@ def __new__(cls, root, num_cores=28, transform=None, filter=filter): @dataset_registry( dataset_type="COCORaw", framework="onnxrt_qlinearops, \ - onnxrt_integerops, pytorch, mxnet, tensorflow, \ + onnxrt_integerops, pytorch, tensorflow, \ tensorflow_itex", dataset_format="", ) @@ -263,7 +262,7 @@ def __getitem__(self, index): @dataset_registry( dataset_type="COCONpy", framework="onnxrt_qlinearops, \ - onnxrt_integerops, pytorch, mxnet, tensorflow, \ + onnxrt_integerops, pytorch, tensorflow, \ tensorflow_itex", dataset_format="", ) diff --git a/neural_compressor/data/datasets/dataset.py b/neural_compressor/data/datasets/dataset.py index 46cec9ba36a..4f97340c8ea 100644 --- a/neural_compressor/data/datasets/dataset.py +++ b/neural_compressor/data/datasets/dataset.py @@ -26,7 +26,6 @@ torch = LazyImport("torch") torchvision = LazyImport("torchvision") tf = LazyImport("tensorflow") -mx = LazyImport("mxnet") np = LazyImport("numpy") hashlib = LazyImport("hashlib") gzip = LazyImport("gzip") @@ -53,21 +52,11 @@ class PyTorchDatasets(object): # pragma: no cover def __init__(self): """Initialize the attributes of class.""" self.datasets = { - "ImageFolder": PytorchMxnetWrapDataset(torchvision.datasets.ImageFolder), + "ImageFolder": PytorchWrapDataset(torchvision.datasets.ImageFolder), } self.datasets.update(PYTORCH_DATASETS) -@singleton -class MXNetDatasets(object): # pragma: no cover - """The base class of MXNet datasets class.""" - - def __init__(self): - """Initialize the attributes of class.""" - self.datasets = {} - self.datasets.update(MXNET_DATASETS) - - @singleton class ONNXRTQLDatasets(object): # pragma: no cover """The base class of ONNXRT QLinear datasets class.""" @@ -88,11 +77,11 @@ def __init__(self): self.datasets.update(ONNXRTIT_DATASETS) -class PytorchMxnetWrapDataset: # pragma: no cover - """The base class for PyTorch and MXNet frameworks. +class PytorchWrapDataset: # pragma: no cover + """The base class for PyTorch framework. Args: - datafunc: The datasets class of PyTorch or MXNet. + datafunc: The datasets class of PyTorch. """ def __init__(self, datafunc): @@ -100,15 +89,15 @@ def __init__(self, datafunc): self.datafunc = datafunc def __call__(self, transform=None, filter=None, *args, **kwargs): - """Wrap the dataset for PyTorch and MXNet framework.""" - return PytorchMxnetWrapFunction(self.datafunc, transform=transform, filter=filter, *args, **kwargs) + """Wrap the dataset for PyTorch framework.""" + return PytorchWrapFunction(self.datafunc, transform=transform, filter=filter, *args, **kwargs) -class PytorchMxnetWrapFunction: # pragma: no cover - """The Helper class for PytorchMxnetWrapDataset. +class PytorchWrapFunction: # pragma: no cover + """The Helper class for PytorchWrapDataset. Args: - dataset (datasets class): The datasets class of PyTorch or MXNet. + dataset (datasets class): The datasets class of PyTorch. transform (transform object): transform to process input data. filter (Filter objects): filter out examples according to specific conditions. @@ -138,7 +127,6 @@ def __getitem__(self, index): framework_datasets = { "tensorflow": TensorflowDatasets, "tensorflow_itex": TensorflowDatasets, - "mxnet": MXNetDatasets, "pytorch": PyTorchDatasets, "pytorch_ipex": PyTorchDatasets, "pytorch_fx": PyTorchDatasets, @@ -163,7 +151,7 @@ class Datasets(object): # pragma: no cover Args: framework (str): framework name, like:"tensorflow", "tensorflow_itex", "keras", - "mxnet", "onnxrt_qdq", "onnxrt_qlinearops", "onnxrt_integerops", + "onnxrt_qdq", "onnxrt_qlinearops", "onnxrt_integerops", "pytorch", "pytorch_ipex", "pytorch_fx", "onnxruntime". """ @@ -173,7 +161,6 @@ def __init__(self, framework): "tensorflow", "tensorflow_itex", "keras", - "mxnet", "onnxrt_qdq", "onnxrt_qlinearops", "onnxrt_integerops", @@ -181,7 +168,7 @@ def __init__(self, framework): "pytorch_ipex", "pytorch_fx", "onnxruntime", - ], "framework support tensorflow pytorch mxnet onnxrt" + ], "framework support tensorflow pytorch onnxrt" self.datasets = framework_datasets[framework]().datasets def __getitem__(self, dataset_type): @@ -196,7 +183,6 @@ def __getitem__(self, dataset_type): # user/model specific datasets will be registered here TENSORFLOW_DATASETS = {} TENSORFLOWITEX_DATASETS = {} -MXNET_DATASETS = {} PYTORCH_DATASETS = {} PYTORCHIPEX_DATASETS = {} PYTORCHFX_DATASETS = {} @@ -206,7 +192,6 @@ def __getitem__(self, dataset_type): registry_datasets = { "tensorflow": TENSORFLOW_DATASETS, "tensorflow_itex": TENSORFLOWITEX_DATASETS, - "mxnet": MXNET_DATASETS, "pytorch": PYTORCH_DATASETS, "pytorch_ipex": PYTORCHIPEX_DATASETS, "pytorch_fx": PYTORCHFX_DATASETS, @@ -223,7 +208,7 @@ def dataset_registry(dataset_type, framework, dataset_format=""): # pragma: no Args: cls (class): The class of register. dataset_type (str): The dataset registration name - framework (str): support 3 framework including 'tensorflow', 'pytorch', 'mxnet' + framework (str): support 3 framework including 'tensorflow', 'pytorch', data_format (str): The format dataset saved, eg 'raw_image', 'tfrecord' Returns: @@ -235,7 +220,6 @@ def decorator_dataset(cls): assert single_framework in [ "tensorflow", "tensorflow_itex", - "mxnet", "pytorch", "pytorch_ipex", "pytorch_fx", @@ -243,7 +227,7 @@ def decorator_dataset(cls): "onnxrt_integerops", "onnxrt_qdq", "onnxruntime", - ], "The framework support tensorflow mxnet pytorch onnxrt" + ], "The framework support tensorflow pytorch onnxrt" dataset_name = dataset_type + dataset_format if dataset_name in registry_datasets[single_framework].keys(): raise ValueError("Cannot have two datasets with the same name") @@ -509,22 +493,6 @@ def __getitem__(self, index): # pragma: no cover return (image, label) -@dataset_registry(dataset_type="CIFAR10", framework="mxnet", dataset_format="") -class MXNetCIFAR10(CIFAR10): - """The MXNet datasets for CIFAR10.""" - - def __getitem__(self, index): # pragma: no cover - """Magic method. - - x[i] is roughly equivalent to type(x).__getitem__(x, index) - """ - image, label = self.data[index], self.targets[index] - image = mx.nd.array(image) - if self.transform is not None: - image, label = self.transform((image, label)) - return (image, label) - - @dataset_registry(dataset_type="CIFAR10", framework="tensorflow, tensorflow_itex", dataset_format="") class TensorflowCIFAR10(CIFAR10): """The Tensorflow datasets for CIFAR10.""" @@ -604,22 +572,6 @@ def __getitem__(self, index): # pragma: no cover return (image, label) -@dataset_registry(dataset_type="CIFAR100", framework="mxnet", dataset_format="") -class MXNetCIFAR100(CIFAR100): - """The MXNet datasets for CIFAR100.""" - - def __getitem__(self, index): # pragma: no cover - """Magic method. - - x[i] is roughly equivalent to type(x).__getitem__(x, index) - """ - image, label = self.data[index], self.targets[index] - image = mx.nd.array(image) - if self.transform is not None: - image, label = self.transform((image, label)) - return (image, label) - - @dataset_registry(dataset_type="CIFAR100", framework="tensorflow, tensorflow_itex", dataset_format="") class TensorflowCIFAR100(CIFAR100): """The Tensorflow datasets for CIFAR100.""" @@ -753,23 +705,6 @@ def __getitem__(self, index): return (image, label) -@dataset_registry(dataset_type="MNIST", framework="mxnet", dataset_format="") -class MXNetMNIST(MNIST): # pragma: no cover - """The MXNet datasets for MNIST.""" - - def __getitem__(self, index): - """Magic method. - - x[i] is roughly equivalent to type(x).__getitem__(x, index) - """ - image, label = self.data[index], int(self.targets[index]) - image = mx.nd.array(image) - image = image.reshape((image.shape[0], image.shape[1], 1)) - if self.transform is not None: - image, label = self.transform((image, label)) - return (image, label) - - @dataset_registry(dataset_type="MNIST", framework="tensorflow, tensorflow_itex", dataset_format="") class TensorflowMNIST(MNIST): # pragma: no cover """The Tensorflow datasets for MNIST.""" @@ -865,23 +800,6 @@ def __getitem__(self, index): return (image, label) -@dataset_registry(dataset_type="FashionMNIST", framework="mxnet", dataset_format="") -class MXNetFashionMNIST(FashionMNIST): # pragma: no cover - """The MXNet Dataset for FashionMNIST.""" - - def __getitem__(self, index): - """Magic method. - - x[i] is roughly equivalent to type(x).__getitem__(x, index) - """ - image, label = self.data[index], int(self.targets[index]) - image = mx.nd.array(image) - image = image.reshape((image.shape[0], image.shape[1], 1)) - if self.transform is not None: - image, label = self.transform((image, label)) - return (image, label) - - @dataset_registry(dataset_type="FashionMNIST", framework="tensorflow, tensorflow_itex", dataset_format="") class TensorflowFashionMNIST(FashionMNIST): # pragma: no cover """The Tensorflow Dataset for FashionMNIST.""" @@ -964,42 +882,6 @@ def __getitem__(self, index): return (image, label) -@dataset_registry(dataset_type="ImageFolder", framework="mxnet", dataset_format="") -class MXNetImageFolder(ImageFolder): # pragma: no cover - """The MXNet Dataset for image folder. - - Expects the data folder to contain subfolders representing the classes to which - its images belong. - - Please arrange data in this way: - root/class_1/xxx.png - root/class_1/xxy.png - root/class_1/xxz.png - ... - root/class_n/123.png - root/class_n/nsdf3.png - root/class_n/asd932_.png - Please put images of different categories into different folders. - - Args: root (str): Root directory of dataset. - transform (transform object, default=None): transform to process input data. - filter (Filter objects, default=None): filter out examples according to specific - conditions. - """ - - def __getitem__(self, index): - """Magic method. - - x[i] is roughly equivalent to type(x).__getitem__(x, index) - """ - sample = self.image_list[index] - label = sample[1] - image = mx.image.imread(sample[0]) - if self.transform is not None: - image, label = self.transform((image, label)) - return (image, label) - - @dataset_registry(dataset_type="ImageFolder", framework="tensorflow, tensorflow_itex", dataset_format="") class Tensorflow(ImageFolder): # pragma: no cover """The Tensorflow Dataset for image folder. diff --git a/neural_compressor/data/datasets/dummy_dataset.py b/neural_compressor/data/datasets/dummy_dataset.py index 946ffdef688..d2260c46d45 100644 --- a/neural_compressor/data/datasets/dummy_dataset.py +++ b/neural_compressor/data/datasets/dummy_dataset.py @@ -25,7 +25,6 @@ from .dataset import Dataset, dataset_registry -mx = LazyImport("mxnet") torch = LazyImport("torch") logger = logging.getLogger("neural_compressor") @@ -35,8 +34,7 @@ dataset_type="dummy", framework="tensorflow, tensorflow_itex, \ onnxrt_qlinearops, onnxrt_integerops, \ - pytorch, pytorch_ipex, pytorch_fx, \ - mxnet", + pytorch, pytorch_ipex, pytorch_fx", dataset_format="", ) class DummyDataset(Dataset): # pragma: no cover diff --git a/neural_compressor/data/datasets/dummy_dataset_v2.py b/neural_compressor/data/datasets/dummy_dataset_v2.py index 67b75ba906d..a9d12511b71 100644 --- a/neural_compressor/data/datasets/dummy_dataset_v2.py +++ b/neural_compressor/data/datasets/dummy_dataset_v2.py @@ -26,7 +26,6 @@ from .dataset import IterableDataset, dataset_registry -mx = LazyImport("mxnet") torch = LazyImport("torch") @@ -34,7 +33,7 @@ dataset_type="dummy_v2", framework="tensorflow, tensorflow_itex, \ onnxrt_qlinearops, onnxrt_integerops, \ - pytorch, pytorch_ipex, pytorch_fx, mxnet", + pytorch, pytorch_ipex, pytorch_fx", dataset_format="", ) class DummyDataset(IterableDataset): # pragma: no cover @@ -162,7 +161,7 @@ def __len__(self): dataset_type="sparse_dummy_v2", framework="tensorflow, tensorflow_itex, \ onnxrt_qlinearops, onnxrt_integerops, \ - pytorch, pytorch_ipex, pytorch_fx, mxnet", + pytorch, pytorch_ipex, pytorch_fx", dataset_format="", ) class SparseDummyDataset(IterableDataset): # pragma: no cover diff --git a/neural_compressor/data/datasets/imagenet_dataset.py b/neural_compressor/data/datasets/imagenet_dataset.py index b05a8420fd7..e76ae491f8e 100644 --- a/neural_compressor/data/datasets/imagenet_dataset.py +++ b/neural_compressor/data/datasets/imagenet_dataset.py @@ -43,7 +43,6 @@ from .dataset import Dataset, IterableDataset, dataset_registry tf = LazyImport("tensorflow") -mx = LazyImport("mxnet") torch = LazyImport("torch") @@ -128,19 +127,6 @@ def __getitem__(self, index): return (image, label) -@dataset_registry(dataset_type="ImagenetRaw", framework="mxnet", dataset_format="") -class MXNetImagenetRaw(ImagenetRaw): # pragma: no cover - """Dataset for ImageNet data generation on mxnet backend.""" - - def __getitem__(self, index): - """Return the item of dataset according to the given index.""" - image_path, label = self.image_list[index], self.label_list[index] - image = mx.image.imread(image_path) - if self.transform is not None: - image, label = self.transform((image, label)) - return (image, label) - - @dataset_registry( dataset_type="ImagenetRaw", framework="tensorflow, \ diff --git a/neural_compressor/data/filters/coco_filter.py b/neural_compressor/data/filters/coco_filter.py index c1455f07dbf..f12778811ed 100644 --- a/neural_compressor/data/filters/coco_filter.py +++ b/neural_compressor/data/filters/coco_filter.py @@ -44,7 +44,7 @@ def __call__(self, image, label): @filter_registry( filter_type="LabelBalanceCOCORaw", framework="tensorflow, \ - tensorflow_itex, pytorch, mxnet, onnxrt_qlinearops, onnxrt_integerops", + tensorflow_itex, pytorch, onnxrt_qlinearops, onnxrt_integerops", ) class LabelBalanceCOCORawFilter(Filter): # pragma: no cover """The label balance filter for COCO raw data.""" diff --git a/neural_compressor/data/filters/filter.py b/neural_compressor/data/filters/filter.py index 7744a915131..f34188d883a 100644 --- a/neural_compressor/data/filters/filter.py +++ b/neural_compressor/data/filters/filter.py @@ -61,22 +61,11 @@ def __init__(self): self.filters.update(PYTORCH_FILTERS) -@singleton -class MXNetFilters(object): # pragma: no cover - """The base filter class for MXNet framework.""" - - def __init__(self): - """Initialize the attribute of the class.""" - self.filters = {} - self.filters.update(MXNET_FILTERS) - - TENSORFLOW_FILTERS = {} TENSORFLOW_ITEX_FILTERS = {} ONNXRT_IT_FILTERS = {} ONNXRT_QL_FILTERS = {} PYTORCH_FILTERS = {} -MXNET_FILTERS = {} framework_filters = { "tensorflow": TensorflowFilters, @@ -84,7 +73,6 @@ def __init__(self): "pytorch": PyTorchFilters, "pytorch_ipex": PyTorchFilters, "pytorch_fx": PyTorchFilters, - "mxnet": MXNetFilters, "onnxrt_qlinearops": ONNXRTQLFilters, "onnxrt_qdq": ONNXRTQLFilters, "onnxruntime": ONNXRTQLFilters, @@ -97,7 +85,6 @@ def __init__(self): "pytorch": PYTORCH_FILTERS, "pytorch_ipex": PYTORCH_FILTERS, "pytorch_fx": PYTORCH_FILTERS, - "mxnet": MXNET_FILTERS, "onnxrt_integerops": ONNXRT_IT_FILTERS, "onnxrt_qdq": ONNXRT_QL_FILTERS, "onnxruntime": ONNXRT_QL_FILTERS, @@ -109,7 +96,7 @@ class FILTERS(object): # pragma: no cover """The filter register for all frameworks. Args: - framework (str): frameworks in ["tensorflow", "tensorflow_itex", "mxnet", + framework (str): frameworks in ["tensorflow", "tensorflow_itex", "onnxrt_qdq", "pytorch", "pytorch_ipex", "pytorch_fx", "onnxrt_integerops", "keras", "onnxrt_qlinearops", "onnxruntime"]. @@ -121,7 +108,6 @@ def __init__(self, framework): "tensorflow", "tensorflow_itex", "keras", - "mxnet", "onnxrt_qdq", "pytorch", "pytorch_ipex", @@ -129,7 +115,7 @@ def __init__(self, framework): "onnxrt_integerops", "onnxrt_qlinearops", "onnxruntime", - ], "framework support tensorflow pytorch mxnet onnxrt" + ], "framework support tensorflow pytorch onnxrt" self.filters = framework_filters[framework]().filters self.framework = framework @@ -147,7 +133,7 @@ def filter_registry(filter_type, framework): # pragma: no cover Args: filter_type (str): fILTER registration name. - framework (str): support 4 framework including 'tensorflow', 'pytorch', 'mxnet', 'onnxrt'. + framework (str): support 4 framework including 'tensorflow', 'pytorch', 'onnxrt'. cls (class): The class of register. Returns: @@ -163,12 +149,11 @@ def decorator_transform(cls): "pytorch", "pytorch_ipex", "pytorch_fx", - "mxnet", "onnxrt_integerops", "onnxrt_qdq", "onnxrt_qlinearops", "onnxruntime", - ], "The framework support tensorflow mxnet pytorch onnxrt" + ], "The framework support tensorflow pytorch onnxrt" if filter_type in registry_filters[single_framework].keys(): raise ValueError("Cannot have two transforms with the same name") registry_filters[single_framework][filter_type] = cls diff --git a/neural_compressor/data/transforms/transform.py b/neural_compressor/data/transforms/transform.py index 6333988e7f9..de535df5f8b 100644 --- a/neural_compressor/data/transforms/transform.py +++ b/neural_compressor/data/transforms/transform.py @@ -28,7 +28,6 @@ torchvision = LazyImport("torchvision") torch = LazyImport("torch") tf = LazyImport("tensorflow") -mx = LazyImport("mxnet") cv2 = LazyImport("cv2") @@ -110,48 +109,6 @@ def _get_general(self): return general -class MXNetTransforms(Transforms): - """Mxnet Transforms subclass.""" - - def _get_preprocess(self): - """Mxnet get preprocess method. - - Returns: - preprocess: a dict including all the registered preprocess methods - """ - preprocess = { - "ToTensor": PytorchMxnetWrapFunction(mx.gluon.data.vision.transforms.ToTensor), - "CenterCrop": PytorchMxnetWrapFunction(mx.gluon.data.vision.transforms.CenterCrop), - "RandomHorizontalFlip": PytorchMxnetWrapFunction(mx.gluon.data.vision.transforms.RandomFlipLeftRight), - "RandomVerticalFlip": PytorchMxnetWrapFunction(mx.gluon.data.vision.transforms.RandomFlipTopBottom), - } - preprocess.update(MXNET_TRANSFORMS["preprocess"]) - return preprocess - - def _get_postprocess(self): - """Mxnet get postprocess method. - - Returns: - postprocess: a dict including all the registered postprocess methods - """ - postprocess = {} - postprocess.update(MXNET_TRANSFORMS["postprocess"]) - return postprocess - - def _get_general(self): - """Mxnet get general method. - - Returns: - general: a dict including all the registered general methods - """ - general = { - "Compose": mx.gluon.data.vision.transforms.Compose, - "Cast": PytorchMxnetWrapFunction(mx.gluon.data.vision.transforms.Cast), - } - general.update(MXNET_TRANSFORMS["general"]) - return general - - class PyTorchTransforms(Transforms): """Pytorch Transforms subclass.""" @@ -162,14 +119,14 @@ def _get_preprocess(self): preprocess: a dict including all the registered preprocess methods """ preprocess = { - "ToTensor": PytorchMxnetWrapFunction(torchvision.transforms.ToTensor), - "ToPILImage": PytorchMxnetWrapFunction(torchvision.transforms.ToPILImage), - "CenterCrop": PytorchMxnetWrapFunction(torchvision.transforms.CenterCrop), - "RandomCrop": PytorchMxnetWrapFunction(torchvision.transforms.RandomCrop), - "RandomHorizontalFlip": PytorchMxnetWrapFunction(torchvision.transforms.RandomHorizontalFlip), - "RandomVerticalFlip": PytorchMxnetWrapFunction(torchvision.transforms.RandomVerticalFlip), - "Pad": PytorchMxnetWrapFunction(torchvision.transforms.Pad), - "ColorJitter": PytorchMxnetWrapFunction(torchvision.transforms.ColorJitter), + "ToTensor": PytorchWrapFunction(torchvision.transforms.ToTensor), + "ToPILImage": PytorchWrapFunction(torchvision.transforms.ToPILImage), + "CenterCrop": PytorchWrapFunction(torchvision.transforms.CenterCrop), + "RandomCrop": PytorchWrapFunction(torchvision.transforms.RandomCrop), + "RandomHorizontalFlip": PytorchWrapFunction(torchvision.transforms.RandomHorizontalFlip), + "RandomVerticalFlip": PytorchWrapFunction(torchvision.transforms.RandomVerticalFlip), + "Pad": PytorchWrapFunction(torchvision.transforms.Pad), + "ColorJitter": PytorchWrapFunction(torchvision.transforms.ColorJitter), } preprocess.update(PYTORCH_TRANSFORMS["preprocess"]) return preprocess @@ -268,7 +225,6 @@ def _get_general(self): framework_transforms = { "tensorflow": TensorflowTransforms, "tensorflow_itex": TensorflowTransforms, - "mxnet": MXNetTransforms, "pytorch": PyTorchTransforms, "pytorch_ipex": PyTorchTransforms, "pytorch_fx": PyTorchTransforms, @@ -281,7 +237,6 @@ def _get_general(self): # transform registry will register transforms into these dicts TENSORFLOW_TRANSFORMS = {"preprocess": {}, "postprocess": {}, "general": {}} TENSORFLOW_ITEX_TRANSFORMS = {"preprocess": {}, "postprocess": {}, "general": {}} -MXNET_TRANSFORMS = {"preprocess": {}, "postprocess": {}, "general": {}} PYTORCH_TRANSFORMS = {"preprocess": {}, "postprocess": {}, "general": {}} ONNXRT_QL_TRANSFORMS = {"preprocess": {}, "postprocess": {}, "general": {}} ONNXRT_IT_TRANSFORMS = {"preprocess": {}, "postprocess": {}, "general": {}} @@ -289,7 +244,6 @@ def _get_general(self): registry_transforms = { "tensorflow": TENSORFLOW_TRANSFORMS, "tensorflow_itex": TENSORFLOW_ITEX_TRANSFORMS, - "mxnet": MXNET_TRANSFORMS, "pytorch": PYTORCH_TRANSFORMS, "pytorch_ipex": PYTORCH_TRANSFORMS, "pytorch_fx": PYTORCH_TRANSFORMS, @@ -325,8 +279,7 @@ def __init__(self, framework, process): "onnxrt_qdq", "onnxrt_qlinearops", "onnxrt_integerops", - "mxnet", - ), "framework support tensorflow pytorch mxnet onnxrt" + ), "framework support tensorflow pytorch onnxrt" assert process in ("preprocess", "postprocess", "general"), "process support preprocess postprocess, general" self.transforms = framework_transforms[framework](process).transforms self.framework = framework @@ -363,7 +316,7 @@ def transform_registry(transform_type, process, framework): Args: transform_type (str): Transform registration name process (str): support 3 process including 'preprocess', 'postprocess', 'general' - framework (str): support 4 framework including 'tensorflow', 'pytorch', 'mxnet', 'onnxrt' + framework (str): support 4 framework including 'tensorflow', 'pytorch', 'onnxrt' cls (class): The class of register. Returns: @@ -375,7 +328,6 @@ def decorator_transform(cls): assert single_framework in [ "tensorflow", "tensorflow_itex", - "mxnet", "pytorch", "pytorch_ipex", "pytorch_fx", @@ -383,7 +335,7 @@ def decorator_transform(cls): "onnxrt_qdq", "onnxrt_integerops", "onnxruntime", - ], "The framework support tensorflow mxnet pytorch onnxrt" + ], "The framework support tensorflow pytorch onnxrt" if transform_type in registry_transforms[single_framework][process].keys(): raise ValueError("Cannot have two transforms with the same name") registry_transforms[single_framework][process][transform_type] = cls @@ -444,14 +396,14 @@ def __call__(self, sample): return (image, label) -class PytorchMxnetWrapFunction(object): - """Pytorch and MXNet wrapper function class.""" +class PytorchWrapFunction(object): + """Pytorch wrapper function class.""" def __init__(self, transform_func): - """Initialize `PytorchMxnetWrapFunction` class. + """Initialize `PytorchWrapFunction` class. Args: - transform_func (function): pytorch or mxnet transform function + transform_func (function): pytorch transform function """ self.transform_func = transform_func @@ -459,19 +411,19 @@ def __call__(self, **args): """__call__ method. Returns: - PytorchMxnetTransform class + PytorchTransform class """ - return PytorchMxnetTransform(self.transform_func(**args)) + return PytorchTransform(self.transform_func(**args)) -class PytorchMxnetTransform(BaseTransform): - """Pytorch and Mxnet transform class, the subclass of BaseTransform.""" +class PytorchTransform(BaseTransform): + """Pytorch transform class, the subclass of BaseTransform.""" def __init__(self, transform_func): - """Initialize `PytorchMxnetTransform` class. + """Initialize `PytorchTransform` class. Args: - transform_func (function): pytorch or mxnet transform function + transform_func (function): pytorch transform function """ self.transform_func = transform_func @@ -479,7 +431,7 @@ def __call__(self, sample): """__call__ method. Returns: - a tuple of image and label which get from pytorch or mxnet transform processing + a tuple of image and label which get from pytorch transform processing """ image, label = sample image = self.transform_func(image) @@ -498,12 +450,6 @@ def __call__(self, sample): "bicubic": 3, } -interpolation_mxnet_map = { - "nearest": 0, - "bilinear": 1, - "bicubic": 2, -} - def get_torchvision_map(interpolation): """Get torchvision interpolation map.""" @@ -577,27 +523,6 @@ def __call__(self, sample): return (image, label) -@transform_registry(transform_type="CropToBoundingBox", process="preprocess", framework="mxnet") -class MXNetCropToBoundingBox(CropToBoundingBox): - """Crops an image to a specified bounding box. - - Args: - offset_height (int): Vertical coordinate of the top-left corner of the result in the input - offset_width (int): Horizontal coordinate of the top-left corner of the result in the input - target_height (int): Height of the result - target_width (int): Width of the result - - Returns: - tuple of processed image and label - """ - - def __call__(self, sample): - """Call mx.image.fixed_crop.""" - image, label = sample - image = mx.image.fixed_crop(image, self.offset_height, self.offset_width, self.target_height, self.target_width) - return (image, label) - - @transform_registry( transform_type="CropToBoundingBox", process="preprocess", framework="onnxrt_qlinearops, onnxrt_integerops" ) @@ -658,7 +583,7 @@ def __call__(self, sample): @transform_registry( transform_type="ResizeWithRatio", process="preprocess", - framework="onnxrt_qlinearops, onnxrt_integerops, pytorch, mxnet", + framework="onnxrt_qlinearops, onnxrt_integerops, pytorch", ) class ResizeWithRatio(BaseTransform): """Resize image with aspect ratio and pad it to max shape(optional). @@ -837,25 +762,6 @@ def __call__(self, sample): return (image, label) -@transform_registry(transform_type="Transpose", process="preprocess", framework="mxnet") -class MXNetTranspose(Transpose): - """Transpose image according to perm. - - Args: - perm (list): A permutation of the dimensions of input image - - Returns: - tuple of processed image and label - """ - - def __call__(self, sample): - """Transpose the image according to perm in sample.""" - image, label = sample - assert len(image.shape) == len(self.perm), "Image rank doesn't match Perm rank" - image = mx.ndarray.transpose(image, self.perm) - return (image, label) - - @transform_registry(transform_type="Transpose", process="preprocess", framework="pytorch") class PyTorchTranspose(Transpose): """Transpose image according to perm. @@ -955,10 +861,10 @@ def __call__(self, sample): transform_type="ToArray", process="preprocess", framework="onnxrt_qlinearops, onnxrt_integerops, tensorflow, \ - tensorflow_itex, pytorch, mxnet", + tensorflow_itex, pytorch", ) class ToArray(BaseTransform): - """Convert PIL Image or NDArray to numpy array. + """Convert PIL Image to numpy array. Returns: tuple of processed image and label @@ -971,8 +877,6 @@ def __call__(self, sample): image, label = sample if isinstance(image, Image.Image): image = np.array(image) - elif isinstance(image, mx.ndarray.NDArray): # pylint: disable=no-member - image = image.asnumpy() else: raise ValueError("Unknown image type!") return (image, label) @@ -1337,53 +1241,6 @@ def __call__(self, sample): return (transformer(image), label) -@transform_registry(transform_type="RandomResizedCrop", process="preprocess", framework="mxnet") -class RandomResizedCropMXNetTransform(BaseTransform): - """Crop the given image to random size and aspect ratio. - - Args: - size (list or int): - Size of the result - scale (tuple or list, default=(0.08, 1.0)): - range of size of the origin size cropped - ratio (tuple or list, default=(3. / 4., 4. / 3.)): - range of aspect ratio of the origin aspect ratio cropped - interpolation (str, default='bilinear'): - Desired interpolation type, support 'bilinear', 'nearest', 'bicubic' - - Returns: - tuple of processed image and label - """ - - def __init__(self, size, scale=(0.08, 1.0), ratio=(3.0 / 4.0, 4.0 / 3.0), interpolation="bilinear"): - """Initialize `RandomResizedCropMXNetTransform` class.""" - if isinstance(size, int): - self.size = size, size - elif isinstance(size, list): - if len(size) == 1: - self.size = size[0], size[0] - elif len(size) == 2: - self.size = size[1], size[0] - self.scale = scale - self.ratio = ratio - - if interpolation in interpolation_mxnet_map.keys(): - self.interpolation = interpolation_mxnet_map[interpolation] - else: - raise ValueError("Undefined interpolation type") - - if scale[0] > scale[1] or ratio[0] > ratio[1]: - raise ValueError("Scale and ratio should be of kind (min, max)") - - def __call__(self, sample): - """Crop the image in sample to the random size.""" - image, label = sample - transformer = mx.gluon.data.vision.transforms.RandomResizedCrop( - size=self.size, scale=self.scale, ratio=self.ratio, interpolation=self.interpolation - ) - return (transformer(image), label) - - @transform_registry(transform_type="RandomResizedCrop", process="preprocess", framework="tensorflow, tensorflow_itex") class RandomResizedCropTFTransform(BaseTransform): """Crop the given image to random size and aspect ratio. @@ -1593,7 +1450,7 @@ def __call__(self, sample): transform_type="AlignImageChannel", process="preprocess", framework="tensorflow, tensorflow_itex, \ - onnxrt_qlinearops, onnxrt_integerops, mxnet", + onnxrt_qlinearops, onnxrt_integerops", ) class AlignImageChannelTransform(BaseTransform): """Align image channel, now just support [H,W]->[H,W,dim], [H,W,4]->[H,W,3] and [H,W,3]->[H,W]. @@ -1659,56 +1516,6 @@ def __call__(self, sample): return (image, label) -@transform_registry(transform_type="ToNDArray", process="preprocess", framework="mxnet") -class ToNDArrayTransform(BaseTransform): - """Convert np.array to NDArray. - - Returns: - tuple of processed image and label - """ - - def __call__(self, sample): - """Convert np.array of the image in sample.""" - image, label = sample - image = mx.nd.array(image) - return image, label - - -@transform_registry(transform_type="Resize", process="preprocess", framework="mxnet") -class ResizeMXNetTransform(BaseTransform): - """Resize the input image to the given size. - - Args: - size (list or int): Size of the result - interpolation (str, default='bilinear'):Desired interpolation type, - support 'bilinear', 'nearest', 'bicubic' - - Returns: - tuple of processed image and label - """ - - def __init__(self, size, interpolation="bilinear"): - """Initialize `ResizeMXNetTransform` class.""" - if isinstance(size, int): - self.size = size, size - elif isinstance(size, list): - if len(size) == 1: - self.size = size[0], size[0] - elif len(size) == 2: - self.size = size[1], size[0] - - if interpolation in interpolation_mxnet_map.keys(): - self.interpolation = interpolation_mxnet_map[interpolation] - else: - raise ValueError("Undefined interpolation type") - - def __call__(self, sample): - """Resize the input image in sample to the given size.""" - image, label = sample - transformer = mx.gluon.data.vision.transforms.Resize(size=self.size, interpolation=self.interpolation) - return (transformer(image), label) - - @transform_registry(transform_type="Resize", process="preprocess", framework="onnxrt_qlinearops, onnxrt_integerops") class ResizeTransform(BaseTransform): """Resize the input image to the given size. @@ -1829,44 +1636,6 @@ def __call__(self, sample): return (transformer(image), label) -@transform_registry(transform_type="CropResize", process="preprocess", framework="mxnet") -class MXNetCropResizeTransform(BaseTransform): - """Crop the input image with given location and resize it. - - Args: - x (int):Left boundary of the cropping area - y (int):Top boundary of the cropping area - width (int):Width of the cropping area - height (int):Height of the cropping area - size (list or int): resize to new size after cropping - interpolation (str, default='bilinear'):Desired interpolation type, - support 'bilinear', 'nearest', 'bicubic' - - Returns: - tuple of processed image and label - """ - - def __init__(self, x, y, width, height, size, interpolation="bilinear"): - """Initialize `MXNetCropResizeTransform` class.""" - if interpolation in interpolation_mxnet_map.keys(): - self.interpolation = interpolation_mxnet_map[interpolation] - else: - raise ValueError("Undefined interpolation type") - self.x = x - self.y = y - self.width = width - self.height = height - self.size = size - - def __call__(self, sample): - """Resize the input image in sample with given location.""" - image, label = sample - transformer = mx.gluon.data.vision.transforms.CropResize( - self.x, self.y, self.width, self.height, self.size, self.interpolation - ) - return (transformer(image), label) - - @transform_registry(transform_type="CropResize", process="preprocess", framework="onnxrt_qlinearops, onnxrt_integerops") class CropResizeTransform(BaseTransform): """Crop the input image with given location and resize it. @@ -1949,8 +1718,8 @@ def __call__(self, sample): return (image, label) -@transform_registry(transform_type="Normalize", process="preprocess", framework="mxnet") -class MXNetNormalizeTransform(BaseTransform): +@transform_registry(transform_type="Normalize", process="preprocess", framework="pytorch") +class PyTorchNormalizeTransform(BaseTransform): """Normalize a image with mean and standard deviation. Args: @@ -1966,44 +1735,13 @@ class MXNetNormalizeTransform(BaseTransform): """ def __init__(self, mean=[0.0], std=[1.0]): - """Initialize `MXNetNormalizeTransform` class.""" + """Initialize `PyTorchNormalizeTransform` class.""" self.mean = mean self.std = std for item in self.std: if item < 10**-6: raise ValueError("Std should be greater than 0") - def __call__(self, sample): - """Normalize the image in sample.""" - image, label = sample - axes = [len(image.shape) - 1] - axes.extend(list(np.arange(len(image.shape) - 1))) - image = mx.ndarray.transpose(image, axes) - assert len(self.mean) == image.shape[0], "Mean channel must match image channel" - transformer = mx.gluon.data.vision.transforms.Normalize(self.mean, self.std) - image = transformer(image) - axes = list(np.arange(1, len(image.shape))) - axes.extend([0]) - image = mx.ndarray.transpose(image, axes) - return (image, label) - - -@transform_registry(transform_type="Normalize", process="preprocess", framework="pytorch") -class PyTorchNormalizeTransform(MXNetNormalizeTransform): - """Normalize a image with mean and standard deviation. - - Args: - mean (list, default=[0.0]): - means for each channel, if len(mean)=1, mean will be broadcasted to each channel, - otherwise its length should be same with the length of image shape - std (list, default=[1.0]): - stds for each channel, if len(std)=1, std will be broadcasted to each channel, - otherwise its length should be same with the length of image shape - - Returns: - tuple of processed image and label - """ - def __call__(self, sample): """Normalize the image in sample.""" image, label = sample @@ -2044,9 +1782,7 @@ def __call__(self, sample): return (image, label) -@transform_registry( - transform_type="RandomCrop", process="preprocess", framework="mxnet, onnxrt_qlinearops, onnxrt_integerops" -) +@transform_registry(transform_type="RandomCrop", process="preprocess", framework="onnxrt_qlinearops, onnxrt_integerops") class RandomCropTransform(BaseTransform): """Crop the image at a random location to the given size. diff --git a/neural_compressor/metric/metric.py b/neural_compressor/metric/metric.py index 06eb5ed0d73..b7377501155 100644 --- a/neural_compressor/metric/metric.py +++ b/neural_compressor/metric/metric.py @@ -28,7 +28,6 @@ torch = LazyImport("torch") tf = LazyImport("tensorflow") -mx = LazyImport("mxnet") transformers = LazyImport("transformers") @@ -80,31 +79,6 @@ def __init__(self) -> None: self.metrics.update(PYTORCH_METRICS) -@singleton -class MXNetMetrics(object): - """MXNet metrics collection. - - Attributes: - metrics: A dict to maintain all metrics for MXNet model. - """ - - def __init__(self) -> None: - """Initialize the metrics collection.""" - from neural_compressor.adaptor.mxnet_utils.util import check_mx_version - - if check_mx_version("2.0.0"): - import mxnet.gluon.metric as mx_metrics - else: - import mxnet.metric as mx_metrics - self.metrics = { - "Accuracy": WrapMXNetMetric(mx_metrics.Accuracy), - "MAE": WrapMXNetMetric(mx_metrics.MAE), - "MSE": WrapMXNetMetric(mx_metrics.MSE), - "Loss": WrapMXNetMetric(mx_metrics.Loss), - } - self.metrics.update(MXNET_METRICS) - - @singleton class ONNXRTQLMetrics(object): """ONNXRT QLinear metrics collection. @@ -137,7 +111,6 @@ def __init__(self) -> None: "tensorflow": TensorflowMetrics, "tensorflow_itex": TensorflowMetrics, "keras": TensorflowMetrics, - "mxnet": MXNetMetrics, "pytorch": PyTorchMetrics, "pytorch_ipex": PyTorchMetrics, "pytorch_fx": PyTorchMetrics, @@ -151,7 +124,6 @@ def __init__(self) -> None: TENSORFLOW_METRICS = {} TENSORFLOW_ITEX_METRICS = {} KERAS_METRICS = {} -MXNET_METRICS = {} PYTORCH_METRICS = {} ONNXRT_QL_METRICS = {} ONNXRT_IT_METRICS = {} @@ -160,7 +132,6 @@ def __init__(self) -> None: "tensorflow": TENSORFLOW_METRICS, "tensorflow_itex": TENSORFLOW_ITEX_METRICS, "keras": KERAS_METRICS, - "mxnet": MXNET_METRICS, "pytorch": PYTORCH_METRICS, "pytorch_ipex": PYTORCH_METRICS, "pytorch_fx": PYTORCH_METRICS, @@ -194,9 +165,8 @@ def __init__(self, framework: str): "onnxrt_qdq", "onnxrt_qlinearops", "onnxrt_integerops", - "mxnet", "onnxruntime", - ), "framework support tensorflow pytorch mxnet onnxrt" + ), "framework support tensorflow pytorch onnxrt" self.metrics = framework_metrics[framework]().metrics def __getitem__(self, metric_type: str): @@ -227,7 +197,7 @@ def metric_registry(metric_type: str, framework: str): """Decorate for registering all Metric subclasses. The cross-framework metric is supported by specifying the framework param - as one of tensorflow, pytorch, mxnet, onnxrt. + as one of tensorflow, pytorch, onnxrt. Args: metric_type: The metric type. @@ -243,7 +213,6 @@ def decorator_metric(cls): "tensorflow", "tensorflow_itex", "keras", - "mxnet", "onnxrt_qlinearops", "onnxrt_integerops", "onnxrt_qdq", @@ -251,7 +220,7 @@ def decorator_metric(cls): "pytorch", "pytorch_ipex", "pytorch_fx", - ], "The framework support tensorflow mxnet pytorch onnxrt" + ], "The framework support tensorflow pytorch onnxrt" if metric_type in registry_metrics[single_framework].keys(): raise ValueError("Cannot have two metrics with the same name") @@ -371,35 +340,6 @@ def result(self): return self._metric.compute() -class WrapMXNetMetric(BaseMetric): - """The wrapper of Metric class for MXNet.""" - - def update(self, preds, labels=None, sample_weight=None): - """Convert the prediction to MXNet array. - - Args: - preds: The prediction result. - labels: The reference. Defaults to None. - sample_weight: The sampling weight. Defaults to None. - """ - preds = mx.nd.array(preds) - labels = mx.nd.array(labels) - self._metric.update(labels=labels, preds=preds) - - def reset(self): - """Clear the predictions and labels.""" - self._metric.reset() - - def result(self): - """Evaluate the difference between predictions and labels. - - Returns: - acc: The evaluated result. - """ - acc_name, acc = self._metric.get() - return acc - - class WrapONNXRTMetric(BaseMetric): """The wrapper of Metric class for ONNXRT.""" @@ -501,7 +441,7 @@ def _shape_validate(preds, labels): return preds, labels -@metric_registry("F1", "tensorflow, tensorflow_itex, pytorch, mxnet, onnxrt_qlinearops, onnxrt_integerops") +@metric_registry("F1", "tensorflow, tensorflow_itex, pytorch, onnxrt_qlinearops, onnxrt_integerops") class F1(BaseMetric): """F1 score of a binary classification problem. @@ -813,7 +753,7 @@ def result(self): return aes_sum / aes_size -@metric_registry("RMSE", "tensorflow, tensorflow_itex, pytorch, mxnet, onnxrt_qlinearops, onnxrt_integerops") +@metric_registry("RMSE", "tensorflow, tensorflow_itex, pytorch, onnxrt_qlinearops, onnxrt_integerops") class RMSE(BaseMetric): """Computes Root Mean Squared Error (RMSE) loss. @@ -990,7 +930,7 @@ def result(self): return self.num_correct / self.num_sample -@metric_registry("topk", "pytorch, mxnet, onnxrt_qlinearops, onnxrt_integerops") +@metric_registry("topk", "pytorch, onnxrt_qlinearops, onnxrt_integerops") class GeneralTopK(BaseMetric): """Compute Top-k Accuracy classification score. diff --git a/neural_compressor/mix_precision.py b/neural_compressor/mix_precision.py index 53c7c6ecaf8..c9d411291d0 100644 --- a/neural_compressor/mix_precision.py +++ b/neural_compressor/mix_precision.py @@ -43,8 +43,6 @@ def fit(model, conf, eval_func=None, eval_dataloader=None, eval_metric=None, **k For PyTorch model, it's torch.nn.model instance. For onnx model, it should be a path to .onnx file or onnx.onnx_ml_pb2.ModelProto. - For MXNet model, it's mxnet.symbol.Symbol - or gluon.HybirdBlock instance. conf (MixedPrecisionConfig): The MixedPrecisionConfig class containing accuracy goal, tuning objective and mixed_precision tuning space etc. eval_func (function, optional): The evaluation function provided by user. diff --git a/neural_compressor/model/base_model.py b/neural_compressor/model/base_model.py index 2d6a03fdaea..61d7d073617 100644 --- a/neural_compressor/model/base_model.py +++ b/neural_compressor/model/base_model.py @@ -28,9 +28,8 @@ def __init__(self, model, **kwargs): Args: model (object): raw model format. For Tensorflow model, could be path to frozen pb file, path to ckpt or savedmodel folder, loaded estimator/graph_def/graph/keras model object. - For PyTorch model, it's torch.nn.model instance. For MXNet model, it's mxnet.symbol.Symbol - or gluon.HybirdBlock instance. For ONNX model, it's path to onnx model or loaded ModelProto - model object. + For PyTorch model, it's torch.nn.model instance. For ONNX model, it's path to onnx model + or loaded ModelProto model object. """ self.component = None diff --git a/neural_compressor/model/model.py b/neural_compressor/model/model.py index 06362d2d0d8..52a07962bbf 100644 --- a/neural_compressor/model/model.py +++ b/neural_compressor/model/model.py @@ -24,7 +24,6 @@ from neural_compressor.config import options from neural_compressor.model.base_model import BaseModel from neural_compressor.model.keras_model import KerasModel -from neural_compressor.model.mxnet_model import MXNetModel from neural_compressor.model.onnx_model import ONNXModel from neural_compressor.model.tensorflow_model import ( TensorflowBaseModel, @@ -43,7 +42,6 @@ torch = LazyImport("torch") tf = LazyImport("tensorflow") -mx = LazyImport("mxnet") onnx = LazyImport("onnx") ort = LazyImport("onnxruntime") yaml = LazyImport("yaml") @@ -55,7 +53,6 @@ "tensorflow_itex": TensorflowModel, "tensorflow_qat": TensorflowQATModel, "keras": KerasModel, - "mxnet": MXNetModel, "pytorch": PyTorchModel if TORCH else None, "pytorch_ipex": IPEXModel if TORCH else None, "pytorch_fx": PyTorchFXModel if TORCH else None, @@ -124,16 +121,6 @@ def _is_tensorflow(model): else: return "tensorflow" - def _is_mxnet(model): - try: - is_mxnet = isinstance(model, mx.gluon.HybridBlock) or ( - hasattr(model, "__len__") and len(model) > 1 and isinstance(model[0], mx.symbol.Symbol) - ) - except: - return "NA" - else: - return "mxnet" if is_mxnet else "NA" - if isinstance(model, str): absmodel = os.path.abspath(os.path.expanduser(model)) assert os.path.exists(absmodel) or os.path.exists( @@ -147,7 +134,7 @@ def _is_mxnet(model): if isinstance(model, TensorflowBaseModel): return "tensorflow" - checker = [_is_tensorflow, _is_pytorch, _is_onnxruntime, _is_mxnet] + checker = [_is_tensorflow, _is_pytorch, _is_onnxruntime] for handler in checker: fwk_name = handler(model) if fwk_name != "NA": @@ -169,9 +156,8 @@ def __new__(cls, root, **kwargs): Args: root (object): raw model format. For Tensorflow model, could be path to frozen pb file, path to ckpt or savedmodel folder, loaded estimator/graph_def/graph/keras model object. - For PyTorch model, it's torch.nn.model instance. For MXNet model, it's mxnet.symbol.Symbol - or gluon.HybirdBlock instance. For ONNX model, it's path to onnx model or loaded ModelProto - model object. + For PyTorch model, it's torch.nn.model instance. For ONNX model, it's path to onnx model + or loaded ModelProto model object. Returns: BaseModel: neural_compressor built-in model diff --git a/neural_compressor/model/mxnet_model.py b/neural_compressor/model/mxnet_model.py deleted file mode 100644 index 4324ea7c1ab..00000000000 --- a/neural_compressor/model/mxnet_model.py +++ /dev/null @@ -1,76 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# Copyright (c) 2021 Intel Corporation -# -# Licensed 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. - -"""Class for MXNet model.""" - -import os - -from neural_compressor.utils import logger -from neural_compressor.utils.utility import LazyImport - -from .base_model import BaseModel - -mx = LazyImport("mxnet") - - -class MXNetModel(BaseModel): - """Build MXNet model.""" - - def __init__(self, model, **kwargs): - """Initialize a MXNet model. - - Args: - model (mxnet model): model path - """ - # (TODO) MXNet does not support recover model from tuning history currently - self.q_config = None - self._model = model - self.calib_cache = {} - - def framework(self): - """Return framework.""" - return "mxnet" - - @property - def model(self): - """Return model itself.""" - return self._model - - @model.setter - def model(self, model): - """Set model.""" - self._model = model - - def save(self, root=None): - """Save MXNet model.""" - if root is None: - from neural_compressor import config as cfg - - root = cfg.default_workspace - root = os.path.abspath(os.path.expanduser(root)) - os.makedirs(os.path.dirname(root), exist_ok=True) - - if isinstance(self._model, mx.gluon.HybridBlock): - self._model.export(root, remove_amp_cast=False) - logger.info("Save quantized hybrid block model to {}.".format(root)) - else: - symnet, args, auxs = self._model - symnet = symnet.as_nd_ndarray() - args = {k: v.as_nd_ndarray() for k, v in args.items()} - auxs = {k: v.as_nd_ndarray() for k, v in auxs.items()} - mx.model.save_checkpoint(root, 0, symnet, args, auxs, remove_amp_cast=False) - logger.info("Save quantized symbol model to {}.".format(root)) diff --git a/neural_compressor/quantization.py b/neural_compressor/quantization.py index 138dce19913..31cf9829f23 100644 --- a/neural_compressor/quantization.py +++ b/neural_compressor/quantization.py @@ -48,8 +48,6 @@ def fit( a path to ckpt/savedmodel folder. For PyTorch model, it's torch.nn.model instance. - For MXNet model, it's mxnet.symbol.Symbol - or gluon.HybirdBlock instance. conf (PostTrainingQuantConfig): The class of PostTrainingQuantConfig containing accuracy goal, tuning objective and preferred calibration & quantization tuning space etc. diff --git a/neural_compressor/strategy/strategy.py b/neural_compressor/strategy/strategy.py index 7acd7fb581b..384bb7c58af 100644 --- a/neural_compressor/strategy/strategy.py +++ b/neural_compressor/strategy/strategy.py @@ -1480,8 +1480,6 @@ def _set_framework_info(self, q_dataloader, q_func=None): "workspace_path": options.workspace, } ) - if framework == "mxnet": - framework_specific_info.update({"q_dataloader": q_dataloader}) if "onnx" in framework.lower(): if self.mixed_precision_mode: framework_specific_info.update({"approach": "post_training_dynamic_quant"}) diff --git a/neural_compressor/template/graph_optimization.yaml b/neural_compressor/template/graph_optimization.yaml index a84712bdbb2..6253bc1b16a 100644 --- a/neural_compressor/template/graph_optimization.yaml +++ b/neural_compressor/template/graph_optimization.yaml @@ -17,7 +17,7 @@ version: 1.0 # optional. reserved for fu model: # mandatory. used to specify model specific information. name: ssd_mobilenet_v1 - framework: tensorflow # mandatory. supported values are tensorflow, pytorch, pytorch_fx, pytorch_ipex, onnxrt_integer, onnxrt_qlinear or mxnet; allow new framework backend extension. + framework: tensorflow # mandatory. supported values are tensorflow, pytorch, pytorch_fx, pytorch_ipex, onnxrt_integer or onnxrt_qlinear; allow new framework backend extension. inputs: image_tensor # optional. inputs and outputs fields are only required in tensorflow. outputs: num_detections,detection_boxes,detection_scores,detection_classes diff --git a/neural_compressor/template/pruning.yaml b/neural_compressor/template/pruning.yaml index 6a1adb7d3c2..cb7f6e80ef9 100644 --- a/neural_compressor/template/pruning.yaml +++ b/neural_compressor/template/pruning.yaml @@ -17,7 +17,7 @@ version: 1.0 # optional. reserved for fu model: # mandatory. neural_compressor uses this module name and framework name to decide where to save tuning history and deploy yaml. name: resnet50v1.5 - framework: pytorch # mandatory. supported values are tensorflow, pytorch, pytorch_fx, pytorch_ipex, onnxrt_integer, onnxrt_qlinear or mxnet; allow new framework backend extension. + framework: pytorch # mandatory. supported values are tensorflow, pytorch, pytorch_fx, pytorch_ipex, onnxrt_integer or onnxrt_qlinear; allow new framework backend extension. device: cpu # optional. default value is cpu. other value is gpu. diff --git a/neural_compressor/template/ptq.yaml b/neural_compressor/template/ptq.yaml index bc192d4fb5b..9a4331c09cd 100644 --- a/neural_compressor/template/ptq.yaml +++ b/neural_compressor/template/ptq.yaml @@ -17,7 +17,7 @@ version: 1.0 # optional. reserved for fu model: # mandatory. used to specify model specific information. name: ssd_mobilenet_v1 # mandatory. the model name. - framework: tensorflow # mandatory. supported values are tensorflow, pytorch, pytorch_fx, pytorch_ipex, onnxrt_integer, onnxrt_qlinear or mxnet; allow new framework backend extension. + framework: tensorflow # mandatory. supported values are tensorflow, pytorch, pytorch_fx, pytorch_ipex, onnxrt_integer or onnxrt_qlinear; allow new framework backend extension. inputs: image_tensor # optional. inputs and outputs fields are only required in tensorflow. outputs: num_detections,detection_boxes,detection_scores,detection_classes diff --git a/neural_compressor/template/qat.yaml b/neural_compressor/template/qat.yaml index 96200e30af6..de3830e3a7c 100644 --- a/neural_compressor/template/qat.yaml +++ b/neural_compressor/template/qat.yaml @@ -17,7 +17,7 @@ version: 1.0 # optional. reserved for fu model: # mandatory. used to specify model specific information. name: resnet50v1.5 - framework: pytorch # mandatory. supported values are tensorflow, pytorch, pytorch_fx, pytorch_ipex, onnxrt_integer, onnxrt_qlinear or mxnet; allow new framework backend extension. + framework: pytorch # mandatory. supported values are tensorflow, pytorch, pytorch_fx, pytorch_ipex, onnxrt_integer or onnxrt_qlinear; allow new framework backend extension. device: cpu # optional. default value is cpu. other value is gpu. diff --git a/neural_compressor/utils/options.py b/neural_compressor/utils/options.py index 0746f7650a8..edf138da124 100644 --- a/neural_compressor/utils/options.py +++ b/neural_compressor/utils/options.py @@ -34,7 +34,6 @@ class onnxrt: "pytorch": None, "pytorch_fx": None, "pytorch_ipex": None, - "mxnet": None, "onnxrt_integerops": onnxrt, "onnxrt_qlinearops": onnxrt, "onnxrt_qdq": onnxrt, diff --git a/neural_compressor/utils/utility.py b/neural_compressor/utils/utility.py index 34d15105fbd..ed12488205e 100644 --- a/neural_compressor/utils/utility.py +++ b/neural_compressor/utils/utility.py @@ -55,7 +55,6 @@ "onnxrt_qlinearops": ["onnx", "onnxruntime"], "onnxrt_integerops": ["onnx", "onnxruntime"], "onnxruntime": ["onnx", "onnxruntime"], - "mxnet": ["mxnet"], } @@ -440,12 +439,6 @@ def recover(fp32_model, tuning_history_path, num, **kwargs): tf_fp32_model = Model(fp32_model) tune_index_qmodel = adaptor.recover_tuned_model(tf_fp32_model, q_config) return tune_index_qmodel - elif "mxnet" in framework: - from neural_compressor.model import Model - - mx_fp32_model = Model(fp32_model) - tune_index_qmodel = adaptor.recover_tuned_model(mx_fp32_model, q_config) - return tune_index_qmodel def str2array(s): diff --git a/test/adaptor/mxnet_adaptor/test_mxnet_query_fwk.py b/test/adaptor/mxnet_adaptor/test_mxnet_query_fwk.py deleted file mode 100644 index 977274d6d4a..00000000000 --- a/test/adaptor/mxnet_adaptor/test_mxnet_query_fwk.py +++ /dev/null @@ -1,69 +0,0 @@ -# -# -*- coding: utf-8 -*- -# -import os -import platform -import sys -import unittest - -import yaml - -sys.path.append("..") - -import mxnet as mx - -import neural_compressor -import neural_compressor.adaptor - - -class TestMXNetQuery(unittest.TestCase): - @classmethod - def setUpClass(self): - if platform.system().lower() == "windows": - self.skipTest(self, "not support mxnet on windows yet") - import importlib - - nc_path = os.path.dirname(importlib.util.find_spec("neural_compressor").origin) - self.yaml_path = os.path.join(nc_path, "adaptor/mxnet.yaml") - self.Queryhandler = neural_compressor.adaptor.mxnet.MXNetQuery(self.yaml_path) - self.version = mx.__version__ - - def test_get_specified_version_cfg(self): - with open(self.yaml_path) as f: - content = yaml.safe_load(f) - default_config = self.Queryhandler._get_specified_version_cfg(content) - self.assertIsNotNone(default_config) - - def test_one_shot_query(self): - self.Queryhandler._one_shot_query() - self.assertIsNotNone(self.Queryhandler.cur_config) - - def test_get_version(self): - Query_version = self.Queryhandler.get_version() - # if the mxnet version not in cfgs, the default maybe be ok. - self.assertNotIn([mx.__version__, "default"], [Query_version]) - - def test_get_precisions(self): - Query_precisions = self.Queryhandler.get_precisions() - res = Query_precisions.split(",") - self.assertEqual(len(res), len(set(res))) - - def test_get_op_types(self): - Query_op_types = self.Queryhandler.get_op_types() - self.assertEqual(len(Query_op_types), len(set(Query_op_types))) - - def test_get_fuse_patterns(self): - Query_fusion_pattern = self.Queryhandler.get_fuse_patterns() - self.assertEqual(len(Query_fusion_pattern), len(set(Query_fusion_pattern))) - - def test_get_quantization_capability(self): - Query_quantization_capability = self.Queryhandler.get_quantization_capability() - self.assertIsNotNone(Query_quantization_capability) - - def test_get_mixed_precision_combination(self): - Query_mixed_precision = self.Queryhandler.get_mixed_precision_combination() - self.assertNotIn(["int8", "bf16"], Query_mixed_precision) - - -if __name__ == "__main__": - unittest.main() diff --git a/test/config/test_config_2x.py b/test/config/test_config_2x.py index 69c999ba794..86f3af44fcf 100644 --- a/test/config/test_config_2x.py +++ b/test/config/test_config_2x.py @@ -3,7 +3,7 @@ import unittest from neural_compressor import set_random_seed, set_resume_from, set_tensorboard, set_workspace -from neural_compressor.config import BenchmarkConfig, MixedPrecisionConfig, MXNet, PostTrainingQuantConfig +from neural_compressor.config import BenchmarkConfig, MixedPrecisionConfig, PostTrainingQuantConfig from neural_compressor.config import _Config as conf from neural_compressor.config import options from neural_compressor.utils.constant import * @@ -46,11 +46,6 @@ def test_config(self): a = conf(mixed_precision=cfg) self.assertEqual(a.mixed_precision.precisions, ["bf16"]) - cfg = MXNet() - cfg.precisions = "bf16" - a = conf(mxnet=cfg) - self.assertEqual(a.mxnet.precisions, ["bf16"]) - set_workspace("workspace_path") self.assertEqual(options.workspace, "workspace_path") diff --git a/test/data/test_dataloader.py b/test/data/test_dataloader.py index 253edd5a03a..3071de87a80 100644 --- a/test/data/test_dataloader.py +++ b/test/data/test_dataloader.py @@ -97,70 +97,6 @@ def test_pytorch_dataset(self): self.assertEqual(data[0][0].shape, (24, 24)) break - @unittest.skipIf(platform.system().lower() == "windows", "not support mxnet on windows yet") - def test_mxnet_dataset(self): - dataloader_args = { - "batch_size": 2, - "dataset": {"CIFAR10": {"root": "./", "train": False, "download": False}}, - "transform": {"Resize": {"size": 24}}, - "filter": None, - } - self.assertRaises(RuntimeError, create_dataloader, "mxnet", dataloader_args) - - dataloader_args = { - "batch_size": 2, - "dataset": {"CIFAR100": {"root": "./", "train": False, "download": False}}, - "transform": {"Resize": {"size": 24}}, - "filter": None, - } - self.assertRaises(RuntimeError, create_dataloader, "mxnet", dataloader_args) - - dataloader_args = { - "dataset": {"MNIST": {"root": "./test", "train": False, "download": False}}, - "transform": {"Resize": {"size": 24}}, - "filter": None, - } - self.assertRaises(RuntimeError, create_dataloader, "mxnet", dataloader_args) - - dataloader_args = { - "batch_size": 2, - "dataset": {"MNIST": {"root": "./", "train": True, "download": True}}, - "transform": {"Resize": {"size": 24}}, - "filter": None, - } - dataloader = create_dataloader("mxnet", dataloader_args) - - for data in dataloader: - self.assertEqual(len(data[0]), 2) - self.assertEqual(data[0][0].shape, (24, 24, 1)) - break - - dataloader_args = { - "batch_size": 2, - "dataset": {"FashionMNIST": {"root": "./", "train": False, "download": True}}, - "transform": {"Resize": {"size": 24}}, - "filter": None, - } - dataloader = create_dataloader("mxnet", dataloader_args) - - for data in dataloader: - self.assertEqual(len(data[0]), 2) - self.assertEqual(data[0][0].shape, (24, 24, 1)) - break - - dataloader_args = { - "batch_size": 2, - "shuffle": True, - "dataset": {"MNIST": {"root": "./", "train": True, "download": True}}, - "transform": {"Resize": {"size": 24}}, - "filter": None, - } - with self.assertLogs() as cm: - dataloader = create_dataloader("mxnet", dataloader_args) - self.assertEqual( - cm.output, ["WARNING:root:Shuffle is not supported yet in" " MXNetDataLoader, ignoring shuffle keyword."] - ) - def test_tf_dataset(self): dataloader_args = { "batch_size": 2, @@ -348,30 +284,6 @@ def test_pytorch(self): self.assertEqual(data[0][0].shape, (24, 24, 3)) break - @unittest.skipIf(platform.system().lower() == "windows", "not support mxnet on windows yet") - def test_mxnet(self): - import mxnet as mx - - dataloader_args = { - "dataset": {"ImagenetRaw": {"data_path": "val", "image_list": None}}, - "transform": {"Resize": {"size": 24}}, - "filter": None, - } - dataloader = create_dataloader("mxnet", dataloader_args) - for data in dataloader: - self.assertEqual(data[0][0].shape, (24, 24, 3)) - break - - dataloader_args = { - "dataset": {"ImagenetRaw": {"data_path": "val", "image_list": "val/val.txt"}}, - "transform": {"Resize": {"size": 24}}, - "filter": None, - } - dataloader = create_dataloader("mxnet", dataloader_args) - for data in dataloader: - self.assertEqual(data[0][0].shape, (24, 24, 3)) - break - def test_onnx(self): dataloader_args = { "dataset": {"ImagenetRaw": {"data_path": "val", "image_list": None}}, @@ -480,19 +392,6 @@ def test_pytorch(self): self.assertEqual(data[0][0].shape, (3, 24, 24)) break - @unittest.skipIf(platform.system().lower() == "windows", "not support mxnet on windows yet") - def test_mxnet(self): - dataloader_args = { - "dataset": {"ImageFolder": {"root": "./val"}}, - "transform": {"Resize": {"size": 24}}, - "filter": None, - } - dataloader = create_dataloader("mxnet", dataloader_args) - - for data in dataloader: - self.assertEqual(data[0][0].shape, (24, 24, 3)) - break - def test_onnx(self): dataloader_args = { "dataset": {"ImageFolder": {"root": "./val"}}, @@ -821,13 +720,10 @@ def test_coco_record_disable_eager(self): tf.compat.v1.disable_eager_execution() self.test_coco_record() - @unittest.skipIf(platform.system().lower() == "windows", "not support mxnet on windows yet") def test_coco_raw(self): import collections import json - import mxnet as mx - from neural_compressor.data import TRANSFORMS random_array = np.random.random_sample([100, 100, 3]) * 255 @@ -897,27 +793,6 @@ def test_coco_raw(self): for image, label in dataloader: self.assertEqual(image[0].shape, (100, 100, 3)) - args = {"COCORaw": {"root": "./", "img_dir": "", "anno_dir": "anno.json"}} - ds = create_dataset("mxnet", args, None, None) - - def collate(batch): - elem = batch[0] - if isinstance(elem, mx.ndarray.NDArray): - return mx.nd.stack(*batch) - elif isinstance(elem, collections.abc.Sequence): - batch = zip(*batch) - return [collate(samples) for samples in batch] - elif isinstance(elem, collections.abc.Mapping): - return {key: collate([d[key] for d in batch]) for key in elem} - elif isinstance(elem, np.ndarray): - return np.stack(batch) - else: - return batch - - dataloader = DATALOADERS["mxnet"](ds, collate_fn=collate) - for image, label in dataloader: - self.assertEqual(image[0].shape, (100, 100, 3)) - args = {"COCORaw": {"root": "./", "img_dir": "", "anno_dir": "anno.json"}} ds = create_dataset("pytorch", args, None, None) @@ -941,13 +816,11 @@ def collate(batch): os.remove("test_1.jpg") os.remove("anno.json") - @unittest.skipIf(platform.system().lower() == "windows", "not support mxnet on windows yet") def test_coco_npy(self): import collections import json import cv2 - import mxnet as mx import numpy as np from neural_compressor.data import TRANSFORMS @@ -1042,27 +915,6 @@ def convert_npy(src): for image, label in dataloader: self.assertEqual(image[0].shape, (100, 100, 3)) - args = {"COCONpy": {"root": "./", "npy_dir": "", "anno_dir": "anno.json"}} - ds = create_dataset("mxnet", args, None, None) - - def collate(batch): - elem = batch[0] - if isinstance(elem, mx.ndarray.NDArray): - return mx.nd.stack(*batch) - elif isinstance(elem, collections.abc.Sequence): - batch = zip(*batch) - return [collate(samples) for samples in batch] - elif isinstance(elem, collections.abc.Mapping): - return {key: collate([d[key] for d in batch]) for key in elem} - elif isinstance(elem, np.ndarray): - return np.stack(batch) - else: - return batch - - dataloader = DATALOADERS["mxnet"](ds, collate_fn=collate) - for image, label in dataloader: - self.assertEqual(image[0].shape, (100, 100, 3)) - args = {"COCONpy": {"root": "./", "npy_dir": "", "anno_dir": "anno.json"}} ds = create_dataset("pytorch", args, None, None) @@ -1328,25 +1180,6 @@ def test_pytorch_dummy(self): data, label = next(iterator) self.assertEqual(data[0].shape, (2, 256, 256, 3)) - @unittest.skipIf(platform.system().lower() == "windows", "not support mxnet on windows yet") - def test_mxnet_dummy(self): - datasets = Datasets("mxnet") - transform = TRANSFORMS("mxnet", "preprocess")["Resize"](**{"size": 100}) - dataset = datasets["dummy"](shape=(4, 256, 256, 3), transform=transform) - - data_loader = DATALOADERS["mxnet"](dataset) - iterator = iter(data_loader) - data = next(iterator) - self.assertEqual(data[0].shape, (1, 256, 256, 3)) - # dynamic batching - data_loader.batch(batch_size=2, last_batch="rollover") - iterator = iter(data_loader) - data = next(iterator) - self.assertEqual(data[0].shape, (2, 256, 256, 3)) - - dataset = datasets["dummy"](shape=(4, 256, 256, 3), label=True) - self.assertEqual(dataset[0][1], 0) - def test_onnxrt_qlinear_dummy(self): datasets = Datasets("onnxrt_qlinearops") transform = TRANSFORMS("onnxrt_qlinearops", "preprocess")["Resize"](**{"size": 100}) diff --git a/test/data/test_transform.py b/test/data/test_transform.py index 02798127008..8ecea759e35 100644 --- a/test/data/test_transform.py +++ b/test/data/test_transform.py @@ -12,7 +12,6 @@ from neural_compressor.utils.create_obj_from_config import create_dataset, get_postprocess from neural_compressor.utils.utility import LazyImport -mx = LazyImport("mxnet") tf = LazyImport("tensorflow") torch = LazyImport("torch") torchvision = LazyImport("torchvision") @@ -199,10 +198,7 @@ def testQuantizedInput(self): class TestDataConversion(unittest.TestCase): @classmethod def setUpClass(cls): - if platform.system().lower() == "windows": - cls.skipTest(cls, "not support mxnet on windows yet") cls.img = np.random.random_sample([10, 10, 3]) * 255 - cls.mx_trans = TRANSFORMS("mxnet", "preprocess") cls.pt_trans = TRANSFORMS("pytorch", "preprocess") def testToPILImage(self): @@ -215,32 +211,18 @@ def testToTensor(self): image, _ = trans((TestDataConversion.img.astype(np.uint8), None)) self.assertTrue(isinstance(image, torch.Tensor)) - trans = TestDataConversion.mx_trans["ToTensor"]() - image, _ = trans((mx.nd.array(TestDataConversion.img), None)) - self.assertTrue(isinstance(image, mx.ndarray.NDArray)) # pylint: disable=no-member - - def testToNDArray(self): - trans = TestDataConversion.mx_trans["ToNDArray"]() - image, _ = trans((TestDataConversion.img.astype(np.uint8), None)) - self.assertTrue(isinstance(image, mx.ndarray.NDArray)) - class TestSameTransfoms(unittest.TestCase): @classmethod def setUpClass(cls): - if platform.system().lower() == "windows": - cls.skipTest(cls, "not support mxnet on windows yet") cls.img = np.random.random_sample([10, 10, 3]) * 255 cls.tf_trans = TRANSFORMS("tensorflow", "preprocess") cls.pt_trans = TRANSFORMS("pytorch", "preprocess") - cls.mx_trans = TRANSFORMS("mxnet", "preprocess") cls.ox_trans = TRANSFORMS("onnxrt_qlinearops", "preprocess") - cls.mx_img = mx.nd.array(cls.img.astype(np.uint8)) cls.pt_img = Image.fromarray(cls.img.astype(np.uint8)) cls.tf_img = tf.constant(cls.img) _ = TRANSFORMS("tensorflow", "postprocess") _ = TRANSFORMS("pytorch", "postprocess") - _ = TRANSFORMS("mxnet", "postprocess") _ = TRANSFORMS("onnxrt_qlinearops", "postprocess") _ = TRANSFORMS("onnxrt_integerops", "postprocess") @@ -835,30 +817,8 @@ def testPyTorch(self): with self.assertRaises(ValueError): transforms["AlignImageChannel"](**{"dim": 5}) - @unittest.skipIf(platform.system().lower() == "windows", "not support mxnet on windows yet") - def testMXNet(self): - transforms = TRANSFORMS("mxnet", "preprocess") - align = transforms["AlignImageChannel"](**{"dim": 1}) - image, _ = align((TestAlignImageChannel.img1.astype(np.uint8), None)) - self.assertEqual(image.shape[-1], 1) - - align = transforms["AlignImageChannel"](**{"dim": 1}) - image, _ = align((TestAlignImageChannel.img2.astype(np.uint8), None)) - self.assertEqual(image.shape[-1], 1) - - align = transforms["AlignImageChannel"](**{"dim": 3}) - image, _ = align((TestAlignImageChannel.img3.astype(np.uint8), None)) - self.assertEqual(image.shape[-1], 3) - - align = transforms["AlignImageChannel"](**{"dim": 2}) - self.assertRaises(ValueError, align, (TestAlignImageChannel.img1.astype(np.uint8), None)) - - with self.assertRaises(ValueError): - transforms["AlignImageChannel"](**{"dim": 5}) - class TestToArray(unittest.TestCase): - @unittest.skipIf(platform.system().lower() == "windows", "not support mxnet on windows yet") def testParse(self): random_array = np.random.random_sample([10, 10, 3]) * 255 random_array = random_array.astype(np.uint8) @@ -868,37 +828,6 @@ def testParse(self): img, _ = onnx_parse((img1, None)) self.assertTrue(isinstance(img, np.ndarray)) - mxnet_transforms = TRANSFORMS("mxnet", "preprocess") - mxnet_parse = mxnet_transforms["ToArray"]() - img, _ = mxnet_parse((mx.nd.array(random_array), None)) - self.assertTrue(isinstance(img, np.ndarray)) - self.assertRaises(ValueError, mxnet_parse, ([1, 2], None)) - - -class TestMXNetTransform(unittest.TestCase): - @classmethod - def setUpClass(cls): - if platform.system().lower() == "windows": - cls.skipTest(cls, "not support mxnet on windows yet") - array = np.random.random_sample([100, 100, 3]) * 255 - cls.img = mx.nd.array(array) - cls.transforms = TRANSFORMS("mxnet", "preprocess") - - def testRandomCrop(self): - args = {"size": [50]} - randomcrop = TestMXNetTransform.transforms["RandomCrop"](**args) - compose = TestMXNetTransform.transforms["Compose"]([randomcrop]) - image_result = compose((TestMXNetTransform.img, None)) - self.assertEqual(image_result[0].shape, (50, 50, 3)) - - def testNormalize(self): - args = {"mean": [0.0, 0.0, 0.0], "std": [0.29, 0.24, 0.25]} - normalize = TestMXNetTransform.transforms["Normalize"](**args) - image_result = normalize((TestMXNetTransform.img, None)) - self.assertAlmostEqual( - image_result[0].asnumpy()[0][0][0], (TestMXNetTransform.img.asnumpy() / [0.29])[0][0][0], places=3 - ) - class TestONNXTransfrom(unittest.TestCase): @classmethod diff --git a/test/metric/test_metrics.py b/test/metric/test_metrics.py index e91928b782f..826df6f7ae0 100644 --- a/test/metric/test_metrics.py +++ b/test/metric/test_metrics.py @@ -169,16 +169,6 @@ def test_pytorch_F1(self): F1.update(preds, labels) self.assertEqual(F1.result(), 0.8) - @unittest.skipIf(platform.system().lower() == "windows", "not support mxnet on windows yet") - def test_mxnet_F1(self): - metrics = METRICS("mxnet") - F1 = metrics["F1"]() - preds = [0, 1, 1, 1, 1, 0] - labels = [0, 1, 1, 1] - - F1.update(preds, labels) - self.assertEqual(F1.result(), 0.8) - def test_onnx_topk(self): metrics = METRICS("onnxrt_qlinearops") top1 = metrics["topk"]() @@ -219,46 +209,6 @@ def test_onnx_topk(self): self.assertEqual(top2.result(), 0.8) self.assertEqual(top3.result(), 1) - @unittest.skipIf(platform.system().lower() == "windows", "not support mxnet on windows yet") - def test_mxnet_topk(self): - metrics = METRICS("mxnet") - top1 = metrics["topk"]() - top1.reset() - self.assertEqual(top1.result(), 0) - top2 = metrics["topk"](k=2) - top3 = metrics["topk"](k=3) - - predicts = [[0, 0.2, 0.9, 0.3], [0, 0.9, 0.8, 0]] - single_predict = [0, 0.2, 0.9, 0.3] - - labels = [[0, 1, 0, 0], [0, 0, 1, 0]] - sparse_labels = [2, 2] - single_label = 2 - - # test functionality of one-hot label - top1.update(predicts, labels) - top2.update(predicts, labels) - top3.update(predicts, labels) - self.assertEqual(top1.result(), 0.0) - self.assertEqual(top2.result(), 0.5) - self.assertEqual(top3.result(), 1) - - # test functionality of sparse label - top1.update(predicts, sparse_labels) - top2.update(predicts, sparse_labels) - top3.update(predicts, sparse_labels) - self.assertEqual(top1.result(), 0.25) - self.assertEqual(top2.result(), 0.75) - self.assertEqual(top3.result(), 1) - - # test functionality of single label - top1.update(single_predict, single_label) - top2.update(single_predict, single_label) - top3.update(single_predict, single_label) - self.assertEqual(top1.result(), 0.4) - self.assertEqual(top2.result(), 0.8) - self.assertEqual(top3.result(), 1) - def test_tensorflow_topk(self): metrics = METRICS("tensorflow") top1 = metrics["topk"]() @@ -884,7 +834,6 @@ def test_tensorflow_COCOmAP(self): self.assertRaises(ValueError, mAP.update, detection_2, ground_truth_2) self.assertRaises(ValueError, mAP2.update, detection_2, ground_truth_2) - @unittest.skipIf(platform.system().lower() == "windows", "not support mxnet on windows now") def test__accuracy(self): predicts1 = [1, 0, 1, 1] labels1 = [0, 1, 1, 1] @@ -913,21 +862,6 @@ def test__accuracy(self): acc.update(predicts4, labels4) self.assertEqual(acc.result(), 0.25) - metrics = METRICS("mxnet") - acc = metrics["Accuracy"]() - acc.update(predicts1, labels1) - acc_result = acc.result() - self.assertEqual(acc_result, 0.5) - acc.reset() - acc.update(predicts2, labels2) - self.assertEqual(acc.result(), 0.25) - acc.reset() - acc.update(predicts3, labels3) - self.assertEqual(acc.result(), 0.25) - acc.reset() - acc.update(predicts4, labels4) - self.assertEqual(acc.result(), 0.25) - metrics = METRICS("onnxrt_qlinearops") acc = metrics["Accuracy"]() acc.update(predicts1, labels1) @@ -951,17 +885,6 @@ def test__accuracy(self): wrong_labels = [[0, 1, 1]] self.assertRaises(ValueError, acc.update, wrong_predictions, wrong_labels) - @unittest.skipIf(platform.system().lower() == "windows", "not support mxnet on windows yet") - def test_mxnet_accuracy(self): - metrics = METRICS("mxnet") - acc = metrics["Accuracy"]() - predicts = [1, 0, 1, 1] - labels = [0, 1, 1, 1] - acc.update(predicts, labels) - acc_result = acc.result() - self.assertEqual(acc_result, 0.5) - - @unittest.skipIf(platform.system().lower() == "windows", "not support mxnet on windows now") def test_mse(self): predicts1 = [1, 0, 0, 1] labels1 = [0, 1, 0, 0] @@ -986,15 +909,6 @@ def test_mse(self): mse_result = mse.result() self.assertEqual(mse_result, 0.625) - metrics = METRICS("mxnet") - mse = metrics["MSE"]() - mse.update(predicts1, labels1) - mse_result = mse.result() - self.assertEqual(mse_result, 0.75) - mse.update(predicts2, labels2) - mse_result = mse.result() - self.assertEqual(mse_result, 0.625) - metrics = METRICS("pytorch") mse = metrics["MSE"]() mse.update(predicts1, labels1) @@ -1004,7 +918,6 @@ def test_mse(self): mse_result = mse.result() self.assertEqual(mse_result, 0.625) - @unittest.skipIf(platform.system().lower() == "windows", "not support mxnet on windows now") def test_mae(self): predicts1 = [1, 0, 0, 1] labels1 = [0, 1, 0, 0] @@ -1033,15 +946,6 @@ def test_mae(self): mae_result = mae.result() self.assertEqual(mae_result, 0.5) - metrics = METRICS("mxnet") - mae = metrics["MAE"]() - mae.update(predicts1, labels1) - mae_result = mae.result() - self.assertEqual(mae_result, 0.75) - mae.update(predicts2, labels2) - mae_result = mae.result() - self.assertEqual(mae_result, 0.5) - metrics = METRICS("onnxrt_qlinearops") mae = metrics["MAE"]() mae.update(predicts1, labels1) @@ -1056,7 +960,6 @@ def test_mae(self): self.assertRaises(AssertionError, mae.update, [1, 2], [1]) self.assertRaises(AssertionError, mae.update, 1, np.array([1, 2])) - @unittest.skipIf(platform.system().lower() == "windows", "not support mxnet on windows now") def test_rmse(self): predicts1 = [1, 0, 0, 1] labels1 = [1, 0, 0, 0] @@ -1082,15 +985,6 @@ def test_rmse(self): rmse_result = rmse.result() self.assertAlmostEqual(rmse_result, np.sqrt(0.5)) - metrics = METRICS("mxnet") - rmse = metrics["RMSE"]() - rmse.update(predicts1, labels1) - rmse_result = rmse.result() - self.assertEqual(rmse_result, 0.5) - rmse.update(predicts2, labels2) - rmse_result = rmse.result() - self.assertAlmostEqual(rmse_result, np.sqrt(0.5)) - metrics = METRICS("onnxrt_qlinearops") rmse = metrics["RMSE"]() rmse.update(predicts1, labels1) diff --git a/test/metric/test_metrics_2x.py b/test/metric/test_metrics_2x.py index fe326b34dce..e013548a43d 100644 --- a/test/metric/test_metrics_2x.py +++ b/test/metric/test_metrics_2x.py @@ -168,16 +168,6 @@ def test_pytorch_F1(self): F1.update(preds, labels) self.assertEqual(F1.result(), 0.8) - @unittest.skipIf(platform.system().lower() == "windows", "not support mxnet on windows yet") - def test_mxnet_F1(self): - metrics = METRICS("mxnet") - F1 = metrics["F1"]() - preds = [0, 1, 1, 1, 1, 0] - labels = [0, 1, 1, 1] - - F1.update(preds, labels) - self.assertEqual(F1.result(), 0.8) - def test_onnx_topk(self): metrics = METRICS("onnxrt_qlinearops") top1 = metrics["topk"]() @@ -218,46 +208,6 @@ def test_onnx_topk(self): self.assertEqual(top2.result(), 0.8) self.assertEqual(top3.result(), 1) - @unittest.skipIf(platform.system().lower() == "windows", "not support mxnet on windows yet") - def test_mxnet_topk(self): - metrics = METRICS("mxnet") - top1 = metrics["topk"]() - top1.reset() - self.assertEqual(top1.result(), 0) - top2 = metrics["topk"](k=2) - top3 = metrics["topk"](k=3) - - predicts = [[0, 0.2, 0.9, 0.3], [0, 0.9, 0.8, 0]] - single_predict = [0, 0.2, 0.9, 0.3] - - labels = [[0, 1, 0, 0], [0, 0, 1, 0]] - sparse_labels = [2, 2] - single_label = 2 - - # test functionality of one-hot label - top1.update(predicts, labels) - top2.update(predicts, labels) - top3.update(predicts, labels) - self.assertEqual(top1.result(), 0.0) - self.assertEqual(top2.result(), 0.5) - self.assertEqual(top3.result(), 1) - - # test functionality of sparse label - top1.update(predicts, sparse_labels) - top2.update(predicts, sparse_labels) - top3.update(predicts, sparse_labels) - self.assertEqual(top1.result(), 0.25) - self.assertEqual(top2.result(), 0.75) - self.assertEqual(top3.result(), 1) - - # test functionality of single label - top1.update(single_predict, single_label) - top2.update(single_predict, single_label) - top3.update(single_predict, single_label) - self.assertEqual(top1.result(), 0.4) - self.assertEqual(top2.result(), 0.8) - self.assertEqual(top3.result(), 1) - def test_tensorflow_topk(self): metrics = METRICS("tensorflow") top1 = metrics["topk"]() @@ -882,7 +832,6 @@ def test_tensorflow_COCOmAP(self): self.assertRaises(ValueError, mAP.update, detection_2, ground_truth_2) self.assertRaises(ValueError, mAP2.update, detection_2, ground_truth_2) - @unittest.skipIf(platform.system().lower() == "windows", "not support mxnet on windows now") def test__accuracy(self): predicts1 = [1, 0, 1, 1] labels1 = [0, 1, 1, 1] @@ -911,21 +860,6 @@ def test__accuracy(self): acc.update(predicts4, labels4) self.assertEqual(acc.result(), 0.25) - metrics = METRICS("mxnet") - acc = metrics["Accuracy"]() - acc.update(predicts1, labels1) - acc_result = acc.result() - self.assertEqual(acc_result, 0.5) - acc.reset() - acc.update(predicts2, labels2) - self.assertEqual(acc.result(), 0.25) - acc.reset() - acc.update(predicts3, labels3) - self.assertEqual(acc.result(), 0.25) - acc.reset() - acc.update(predicts4, labels4) - self.assertEqual(acc.result(), 0.25) - metrics = METRICS("onnxrt_qlinearops") acc = metrics["Accuracy"]() acc.update(predicts1, labels1) @@ -949,17 +883,6 @@ def test__accuracy(self): wrong_labels = [[0, 1, 1]] self.assertRaises(ValueError, acc.update, wrong_predictions, wrong_labels) - @unittest.skipIf(platform.system().lower() == "windows", "not support mxnet on windows yet") - def test_mxnet_accuracy(self): - metrics = METRICS("mxnet") - acc = metrics["Accuracy"]() - predicts = [1, 0, 1, 1] - labels = [0, 1, 1, 1] - acc.update(predicts, labels) - acc_result = acc.result() - self.assertEqual(acc_result, 0.5) - - @unittest.skipIf(platform.system().lower() == "windows", "not support mxnet on windows now") def test_mse(self): predicts1 = [1, 0, 0, 1] labels1 = [0, 1, 0, 0] @@ -984,15 +907,6 @@ def test_mse(self): mse_result = mse.result() self.assertEqual(mse_result, 0.625) - metrics = METRICS("mxnet") - mse = metrics["MSE"]() - mse.update(predicts1, labels1) - mse_result = mse.result() - self.assertEqual(mse_result, 0.75) - mse.update(predicts2, labels2) - mse_result = mse.result() - self.assertEqual(mse_result, 0.625) - metrics = METRICS("pytorch") mse = metrics["MSE"]() mse.update(predicts1, labels1) @@ -1002,7 +916,6 @@ def test_mse(self): mse_result = mse.result() self.assertEqual(mse_result, 0.625) - @unittest.skipIf(platform.system().lower() == "windows", "not support mxnet on windows now") def test_mae(self): predicts1 = [1, 0, 0, 1] labels1 = [0, 1, 0, 0] @@ -1031,15 +944,6 @@ def test_mae(self): mae_result = mae.result() self.assertEqual(mae_result, 0.5) - metrics = METRICS("mxnet") - mae = metrics["MAE"]() - mae.update(predicts1, labels1) - mae_result = mae.result() - self.assertEqual(mae_result, 0.75) - mae.update(predicts2, labels2) - mae_result = mae.result() - self.assertEqual(mae_result, 0.5) - metrics = METRICS("onnxrt_qlinearops") mae = metrics["MAE"]() mae.update(predicts1, labels1) @@ -1054,7 +958,6 @@ def test_mae(self): self.assertRaises(AssertionError, mae.update, [1, 2], [1]) self.assertRaises(AssertionError, mae.update, 1, np.array([1, 2])) - @unittest.skipIf(platform.system().lower() == "windows", "not support mxnet on windows now") def test_rmse(self): predicts1 = [1, 0, 0, 1] labels1 = [1, 0, 0, 0] @@ -1080,15 +983,6 @@ def test_rmse(self): rmse_result = rmse.result() self.assertAlmostEqual(rmse_result, np.sqrt(0.5)) - metrics = METRICS("mxnet") - rmse = metrics["RMSE"]() - rmse.update(predicts1, labels1) - rmse_result = rmse.result() - self.assertEqual(rmse_result, 0.5) - rmse.update(predicts2, labels2) - rmse_result = rmse.result() - self.assertAlmostEqual(rmse_result, np.sqrt(0.5)) - metrics = METRICS("onnxrt_qlinearops") rmse = metrics["RMSE"]() rmse.update(predicts1, labels1) diff --git a/test/model/test_model.py b/test/model/test_model.py index b22d5a025e2..600a52a5282 100644 --- a/test/model/test_model.py +++ b/test/model/test_model.py @@ -13,7 +13,6 @@ from neural_compressor.model import MODELS, Model from neural_compressor.model.model import get_model_fwk_name -from neural_compressor.model.mxnet_model import MXNetModel from neural_compressor.model.onnx_model import ONNXModel @@ -461,64 +460,5 @@ def testPyTorch(self): self.assertEqual("pytorch", get_model_fwk_name(PyTorchFXModel(ori_model))) -def load_mxnet_model(symbol_file, param_file): - import mxnet as mx - - symbol = mx.sym.load(symbol_file) - save_dict = mx.nd.load(param_file) - arg_params = {} - aux_params = {} - for k, v in save_dict.items(): - tp, name = k.split(":", 1) - if tp == "arg": - arg_params[name] = v - return symbol, arg_params, aux_params - - -class TestMXNetModel(unittest.TestCase): - @classmethod - def setUpClass(self): - if platform.system().lower() == "windows": - self.skipTest(self, "not support mxnet on windows yet") - import mxnet as mx - import mxnet.gluon.nn as nn - - net = nn.HybridSequential() - net.add(nn.Dense(128, activation="relu")) - net.add(nn.Dense(64, activation="relu")) - net.add(nn.Dense(10)) - net.initialize() - net.hybridize() - fake_data = mx.random.uniform(shape=(1, 128, 128)) - net(fake_data) - self.net = net - - @classmethod - def tearDownClass(self): - os.remove("test-symbol.json") - os.remove("test-0000.params") - os.remove("test2-symbol.json") - os.remove("test2-0000.params") - - def test_model(self): - import mxnet as mx - - self.assertEqual("mxnet", get_model_fwk_name(self.net)) - model = MODELS["mxnet"](self.net) - self.assertEqual(True, isinstance(model, MXNetModel)) - self.assertEqual(True, isinstance(model.model, mx.gluon.HybridBlock)) - - model.save("./test") - self.assertEqual(True, os.path.exists("test-symbol.json")) - self.assertEqual(True, os.path.exists("test-0000.params")) - - net = load_mxnet_model("test-symbol.json", "test-0000.params") - model.model = net - self.assertEqual(True, isinstance(model.model[0], mx.symbol.Symbol)) - model.save("./test2") - self.assertEqual(True, os.path.exists("test2-symbol.json")) - self.assertEqual(True, os.path.exists("test2-0000.params")) - - if __name__ == "__main__": unittest.main() diff --git a/test/requirements.txt b/test/requirements.txt index 831f391e35a..0c117db3d86 100644 --- a/test/requirements.txt +++ b/test/requirements.txt @@ -5,7 +5,6 @@ dynast==1.6.0rc1 horovod intel-extension-for-pytorch intel-tensorflow>=2.12.0 -mxnet-mkl neural-compressor onnx onnxruntime