diff --git a/contrib/input_to_ops.py b/contrib/input_to_ops.py new file mode 100644 index 00000000000..c75e336eb4e --- /dev/null +++ b/contrib/input_to_ops.py @@ -0,0 +1,64 @@ +# Copyright 2017 The TensorFlow Authors. All Rights Reserved. +# +# 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. +# ============================================================================== +"""Logic to update a Tensorflow model graph with quantization operations.""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import collections + + +# Skip all operations that are backprop related or export summaries. +SKIPPED_PREFIXES = () + + +class InputToOps(object): + """Holds a mapping from tensor's name to ops that take it as input.""" + + def __init__(self, graph): + """Initializes mapping from tensor's name to ops that take it. + + Helps find edges between ops faster and avoids iterating over the whole + graph. The mapping is of type Dict[str, Set[tf.Operation]]. + + Note: while inserting operations into the graph, we do not update the + mapping, assuming that insertion points in the graph are never adjacent. + With that restriction, an out of date mapping still works fine. + + Args: + graph: Graph to process. + """ + self.mapping = collections.defaultdict(set) + for op in (op for op in graph.get_operations()): + if op.name.startswith(SKIPPED_PREFIXES): + continue + for op_input in op.inputs: + self.mapping[op_input.ref()].add(op) + + def ConsumerOperations(self, producer_op): + """Looks through outputs of producer_op, finds ops that take them as input. + + Args: + producer_op: Operation containing outputs to process. + + Returns: + A Set[Operation] containing all operations taking input from producer_op + outputs. + """ + result = set() + for inp in producer_op.outputs: + result.update(self.mapping[inp.ref()]) + return result diff --git a/examples/tensorflow/classification/configs/quantization/mobilenet_v2_imagenet_int8.json b/examples/tensorflow/classification/configs/quantization/mobilenet_v2_imagenet_int8.json index a91c53a6c33..471b5b098b0 100644 --- a/examples/tensorflow/classification/configs/quantization/mobilenet_v2_imagenet_int8.json +++ b/examples/tensorflow/classification/configs/quantization/mobilenet_v2_imagenet_int8.json @@ -7,6 +7,8 @@ "batch_size": 256, "epochs": 15, + "num_classes": 1001, + "dataset_preprocessing_preset": "imagenet2012_slim", "optimizer": { "type": "Adam", @@ -18,20 +20,6 @@ }, "dataset": "imagenet2012", - "dataset_type": "tfds", + "dataset_type": "tfrecords" - "compression": { - "algorithm": "quantization", - "initializer": { - "batchnorm_adaptation": { - "num_bn_adaptation_samples": 2048 - } - }, - "weights": { - "per_channel": false - }, - "activations": { - "per_channel": false - } - } } diff --git a/examples/tensorflow/classification/main.py b/examples/tensorflow/classification/main.py index 3e8834d45aa..3dd22145e8d 100644 --- a/examples/tensorflow/classification/main.py +++ b/examples/tensorflow/classification/main.py @@ -11,13 +11,15 @@ limitations under the License. """ +import os import sys import os.path as osp -from pathlib import Path - import tensorflow as tf import tensorflow_addons as tfa +from pathlib import Path +from tensorflow.python.framework.convert_to_constants import convert_variables_to_constants_v2 + from nncf.config.utils import is_accuracy_aware_training from nncf.tensorflow.helpers.model_creation import create_compressed_model from nncf.tensorflow import create_compression_callbacks @@ -25,14 +27,13 @@ from nncf.tensorflow.initialization import register_default_init_args from nncf.tensorflow.utils.state import TFCompressionState from nncf.tensorflow.utils.state import TFCompressionStateLoader - from examples.tensorflow.classification.datasets.builder import DatasetBuilder from examples.tensorflow.common.argparser import get_common_argument_parser from examples.tensorflow.common.callbacks import get_callbacks from examples.tensorflow.common.callbacks import get_progress_bar from examples.tensorflow.common.distributed import get_distribution_strategy from examples.tensorflow.common.logger import logger -from examples.tensorflow.common.model_loader import get_model +from examples.tensorflow.common.model_loader import get_model as get_model_old from examples.tensorflow.common.optimizer import build_optimizer from examples.tensorflow.common.sample_config import create_sample_config from examples.tensorflow.common.scheduler import build_scheduler @@ -43,6 +44,37 @@ from examples.tensorflow.common.utils import serialize_config from examples.tensorflow.common.utils import serialize_cli_args from examples.tensorflow.common.utils import write_metrics +from examples.tensorflow.classification.test_models import get_KerasLayer_model +from examples.tensorflow.classification.test_models import get_model +from examples.tensorflow.classification.test_models import ModelType + +# KerasLayer with NNCFWrapper 1 epoch +# runs/MobileNetV2_imagenet2012/2021-07-21__14-22-44 +# Keras Layer pure 1 epoch +# runs/MobileNetV2_imagenet2012/2021-07-21__14-53-04 + + +def keras_model_to_frozen_graph(model): + input_signature = [] + for item in model.inputs: + input_signature.append(tf.TensorSpec(item.shape, item.dtype)) + concrete_function = tf.function(model).get_concrete_function(input_signature) + frozen_func = convert_variables_to_constants_v2(concrete_function, lower_control_flow=False) + return frozen_func.graph.as_graph_def(add_shapes=True) + + +def save_model_as_frozen_graph(model, save_path, as_text=False): + frozen_graph = keras_model_to_frozen_graph(model) + save_dir, name = os.path.split(save_path) + tf.io.write_graph(frozen_graph, save_dir, name, as_text=as_text) + + +class DummyContextManager: + def __enter__(self): + pass + + def __exit__(self, *args): + pass def get_argument_parser(): @@ -64,6 +96,11 @@ def get_argument_parser(): help="Use pretrained models from the tf.keras.applications", action="store_true", ) + parser.add_argument( + "--model_type", + choices=[ModelType.KerasLayer, ModelType.FuncModel, ModelType.SubClassModel], + default=ModelType.KerasLayer, + help="Type of mobilenetV2 model which should be quantized.") return parser @@ -152,12 +189,18 @@ def run(config): if config.metrics_dump is not None: write_metrics(0, config.metrics_dump) - model_fn, model_params = get_model(config.model, + model_fn, model_params = get_model_old(config.model, input_shape=config.get('input_info', {}).get('sample_size', None), num_classes=config.get('num_classes', get_num_classes(config.dataset)), pretrained=config.get('pretrained', False), weights=config.get('weights', None)) + if config.model_type == ModelType.KerasLayer: + #args = None + args = get_KerasLayer_model() + else: + args = None + builders = get_dataset_builders(config, strategy.num_replicas_in_sync) datasets = [builder.build() for builder in builders] @@ -188,10 +231,20 @@ def run(config): if resume_training: compression_state = load_compression_state(config.ckpt_path) - with TFOriginalModelManager(model_fn, **model_params) as model: + with DummyContextManager(): with strategy.scope(): - compression_ctrl, compress_model = create_compressed_model(model, nncf_config, compression_state) - compression_callbacks = create_compression_callbacks(compression_ctrl, log_dir=config.log_dir) + if not args: + args = get_model(config.model_type) + + from op_insertion import NNCFWrapperCustom + model = tf.keras.Sequential([ + tf.keras.layers.Input(shape=(224, 224, 3)), + NNCFWrapperCustom(*args, caliblration_dataset=train_dataset), + #args[0]['layer'], + tf.keras.layers.Activation('softmax') + ]) + #compression_ctrl, compress_model = create_compressed_model(model, nncf_config, compression_state) + compress_model = model scheduler = build_scheduler( config=config, @@ -202,13 +255,13 @@ def run(config): loss_obj = tf.keras.losses.CategoricalCrossentropy(label_smoothing=0.1) - compress_model.add_loss(compression_ctrl.loss) + #compress_model.add_loss(compression_ctrl.loss) metrics = [ tf.keras.metrics.CategoricalAccuracy(name='acc@1'), tf.keras.metrics.TopKCategoricalAccuracy(k=5, name='acc@5'), tfa.metrics.MeanMetricWrapper(loss_obj, name='ce_loss'), - tfa.metrics.MeanMetricWrapper(compression_ctrl.loss, name='cr_loss') + #tfa.metrics.MeanMetricWrapper(compression_ctrl.loss, name='cr_loss') ] compress_model.compile(optimizer=optimizer, @@ -218,14 +271,17 @@ def run(config): compress_model.summary() - checkpoint = tf.train.Checkpoint(model=compress_model, - compression_state=TFCompressionState(compression_ctrl)) + checkpoint = tf.train.Checkpoint(model=compress_model) initial_epoch = 0 if resume_training: initial_epoch = resume_from_checkpoint(checkpoint=checkpoint, ckpt_path=config.ckpt_path, steps_per_epoch=train_steps) + weights_path = config.get('weights', None) + if weights_path: + compress_model.load_weights(weights_path) + logger.info(f'Weights from {weights_path} were loaded successfully') callbacks = get_callbacks( include_tensorboard=True, @@ -238,7 +294,7 @@ def run(config): callbacks.append(get_progress_bar( stateful_metrics=['loss'] + [metric.name for metric in metrics])) - callbacks.extend(compression_callbacks) + #callbacks.extend(compression_callbacks) validation_kwargs = { 'validation_data': validation_dataset, @@ -246,34 +302,38 @@ def run(config): 'validation_freq': config.test_every_n_epochs, } + # BN INITIALIZATION + # Set trainable graph for eval + enable_bn = False + if not resume_training and enable_bn: + print(25*'*') + print('Start BN adaptiation') + print(25*'*') + compress_model.layers[0].training_forced = True + # Update BN statistics + compress_model.evaluate(train_dataset, + steps=1000, + callbacks=[get_progress_bar( + stateful_metrics=['loss'] + [metric.name for metric in metrics])], + verbose=1) + # Reset model + compress_model.layers[0].training_forced = None + compress_model.compile(optimizer=optimizer, + loss=loss_obj, + metrics=metrics, + run_eagerly=config.get('eager_mode', False)) + ### if 'train' in config.mode: - if is_accuracy_aware_training(config): - logger.info('starting an accuracy-aware training loop...') - result_dict_to_val_metric_fn = lambda results: 100 * results['acc@1'] - compress_model.accuracy_aware_fit(train_dataset, - compression_ctrl, - nncf_config=config.nncf_config, - callbacks=callbacks, - initial_epoch=initial_epoch, - steps_per_epoch=train_steps, - tensorboard_writer=config.tb, - log_dir=config.log_dir, - uncompressed_model_accuracy=uncompressed_model_accuracy, - result_dict_to_val_metric_fn=result_dict_to_val_metric_fn, - **validation_kwargs) - else: - logger.info('training...') - compress_model.fit( - train_dataset, - epochs=train_epochs, - steps_per_epoch=train_steps, - initial_epoch=initial_epoch, - callbacks=callbacks, - **validation_kwargs) + logger.info('training...') + compress_model.fit( + train_dataset, + epochs=train_epochs, + steps_per_epoch=train_steps, + initial_epoch=initial_epoch, + callbacks=callbacks, + **validation_kwargs) logger.info('evaluation...') - statistics = compression_ctrl.statistics() - logger.info(statistics.to_str()) results = compress_model.evaluate( validation_dataset, steps=validation_steps, @@ -285,9 +345,10 @@ def run(config): write_metrics(results[1], config.metrics_dump) if 'export' in config.mode: - save_path, save_format = get_saving_parameters(config) - compression_ctrl.export_model(save_path, save_format) - logger.info('Saved to {}'.format(save_path)) + save_model_as_frozen_graph(compress_model, config.to_frozen_graph) + #save_path, save_format = get_saving_parameters(config) + #compression_ctrl.export_model(save_path, save_format) + #logger.info('Saved to {}'.format(save_path)) def export(config): @@ -329,6 +390,7 @@ def export(config): def main(argv): parser = get_argument_parser() config = get_config_from_argv(argv, parser) + #config['eager_mode'] = True print_args(config) serialize_config(config.nncf_config, config.log_dir) diff --git a/examples/tensorflow/classification/test_models.py b/examples/tensorflow/classification/test_models.py new file mode 100644 index 00000000000..b8487381d9d --- /dev/null +++ b/examples/tensorflow/classification/test_models.py @@ -0,0 +1,72 @@ +import tensorflow as tf +import tensorflow_hub as hub + +from tensorflow.python.framework.convert_to_constants import _run_inline_graph_optimization + + +class ModelType: + KerasLayer = 'KerasLayerModel' + FuncModel = 'FuncModel' + SubClassModel = 'SubclassModel' + + +def second_dummy_model(): + inputs = tf.keras.Input((1000, )) + x = tf.keras.layers.Dense(10)(inputs) + x = tf.keras.layers.Dense(1001)(x) + return tf.keras.Model(inputs, x) + + +class SubclassModel(tf.keras.Model): + def __init__(self): + super(SubclassModel, self).__init__() + self.first_submodule = tf.keras.applications.mobilenet_v2.MobileNetV2() + self.second_submodule = second_dummy_model() + + def call(self, inputs, training=None, mask=None): + x = self.first_submodule(inputs) + x = self.second_submodule(x) + return x + + +def get_func_model(): + mobilenet = tf.keras.applications.mobilenet_v2.MobileNetV2(input_shape=(224, 224, 3), + include_top=True) + input = tf.keras.Input((224, 224, 3)) + x = mobilenet(input) + return (tf.keras.Model(input, x),) + + +def get_submoduled_model(): + return (SubclassModel(),) + + +def get_KerasLayer_model(): + """ + :return : 2 * (KerasLayer instance, non optimized concrete function, + optimized graph_def) first one is trainable and second one is not + """ + retval = [] + keras_layer = hub.KerasLayer("https://tfhub.dev/google/imagenet/mobilenet_v2_100_224/classification/5", + trainable=True, arguments=dict(batch_norm_momentum=0.997)) + for trainable in [True, False]: + tf_f = tf.function(lambda x: keras_layer.call(x, training=trainable)) + concrete = tf_f.get_concrete_function(*[tf.TensorSpec((None, 224, 224, 3), tf.float32, name='input')]) + optimized_gd = _run_inline_graph_optimization(concrete, False, False) + retval.append({ + 'layer': keras_layer, + 'graph_def': optimized_gd, + 'concrete_function': concrete, + }) + return retval + + +def get_model(model_type: str): + if model_type == 'FuncModel': + return get_func_model() + if model_type == 'SubclassModel': + return get_submoduled_model() + if model_type == 'KerasLayerModel': + return get_KerasLayer_model() + + raise ValueError('Wrong model type') diff --git a/examples/tensorflow/common/object_detection/base_model.py b/examples/tensorflow/common/object_detection/base_model.py index 0ec1bc39655..3bdf080ce26 100644 --- a/examples/tensorflow/common/object_detection/base_model.py +++ b/examples/tensorflow/common/object_detection/base_model.py @@ -49,7 +49,7 @@ def __init__(self, params): # One can use 'RESNET_FROZEN_VAR_PREFIX' to speed up ResNet training when loading from the checkpoint # RESNET_FROZEN_VAR_PREFIX = r'(resnet\d+)\/(conv2d(|_([1-9]|10))|batch_normalization(|_([1-9]|10)))\/' self._frozen_variable_prefix = "" - params_train_regularization_variable_regex = r'.*(kernel|weight):0$' + params_train_regularization_variable_regex = r'.*(kernel|weight|kernel_mirrored|weight_mirrored):0$' self._regularization_var_regex = params_train_regularization_variable_regex self._l2_weight_decay = params.weight_decay diff --git a/examples/tensorflow/object_detection/configs/quantization/retinanet_coco_int8.json b/examples/tensorflow/object_detection/configs/quantization/retinanet_coco_int8.json index 39b3b20d219..580f7ba8913 100644 --- a/examples/tensorflow/object_detection/configs/quantization/retinanet_coco_int8.json +++ b/examples/tensorflow/object_detection/configs/quantization/retinanet_coco_int8.json @@ -23,9 +23,5 @@ 1e-4 ] } - }, - - "compression": { - "algorithm": "quantization" } } diff --git a/examples/tensorflow/object_detection/configs/quantization/retinanet_quantization_layout.json b/examples/tensorflow/object_detection/configs/quantization/retinanet_quantization_layout.json new file mode 100644 index 00000000000..4ca31f4856d --- /dev/null +++ b/examples/tensorflow/object_detection/configs/quantization/retinanet_quantization_layout.json @@ -0,0 +1,2856 @@ +[ + { + "_target_type": { + "_value_": 2, + "_name_": "AFTER_LAYER" + }, + "_layer_name": "input_1", + "_instance_idx": 0, + "_output_port_id": 0, + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": null, + "narrow_range": false, + "half_range": false, + "per_channel": false + }, + { + "_target_type": { + "_value_": 2, + "_name_": "AFTER_LAYER" + }, + "_layer_name": "tf_op_layer_resnet50/Relu_1", + "_instance_idx": 0, + "_output_port_id": 0, + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": null, + "narrow_range": false, + "half_range": false, + "per_channel": false + }, + { + "_target_type": { + "_value_": 2, + "_name_": "AFTER_LAYER" + }, + "_layer_name": "tf_op_layer_resnet50/Relu_2", + "_instance_idx": 0, + "_output_port_id": 0, + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": null, + "narrow_range": false, + "half_range": false, + "per_channel": false + }, + { + "_target_type": { + "_value_": 2, + "_name_": "AFTER_LAYER" + }, + "_layer_name": "batch_normalization_4", + "_instance_idx": 0, + "_output_port_id": 0, + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": null, + "narrow_range": false, + "half_range": false, + "per_channel": false + }, + { + "_target_type": { + "_value_": 2, + "_name_": "AFTER_LAYER" + }, + "_layer_name": "batch_normalization_1", + "_instance_idx": 0, + "_output_port_id": 0, + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": null, + "narrow_range": false, + "half_range": false, + "per_channel": false + }, + { + "_target_type": { + "_value_": 2, + "_name_": "AFTER_LAYER" + }, + "_layer_name": "tf_op_layer_resnet50/Relu_4", + "_instance_idx": 0, + "_output_port_id": 0, + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": null, + "narrow_range": false, + "half_range": false, + "per_channel": false + }, + { + "_target_type": { + "_value_": 2, + "_name_": "AFTER_LAYER" + }, + "_layer_name": "tf_op_layer_resnet50/Relu_5", + "_instance_idx": 0, + "_output_port_id": 0, + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": null, + "narrow_range": false, + "half_range": false, + "per_channel": false + }, + { + "_target_type": { + "_value_": 2, + "_name_": "AFTER_LAYER" + }, + "_layer_name": "tf_op_layer_resnet50/Relu_7", + "_instance_idx": 0, + "_output_port_id": 0, + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": null, + "narrow_range": false, + "half_range": false, + "per_channel": false + }, + { + "_target_type": { + "_value_": 2, + "_name_": "AFTER_LAYER" + }, + "_layer_name": "tf_op_layer_resnet50/Relu_8", + "_instance_idx": 0, + "_output_port_id": 0, + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": null, + "narrow_range": false, + "half_range": false, + "per_channel": false + }, + { + "_target_type": { + "_value_": 2, + "_name_": "AFTER_LAYER" + }, + "_layer_name": "tf_op_layer_resnet50/Relu_10", + "_instance_idx": 0, + "_output_port_id": 0, + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": null, + "narrow_range": false, + "half_range": false, + "per_channel": false + }, + { + "_target_type": { + "_value_": 2, + "_name_": "AFTER_LAYER" + }, + "_layer_name": "tf_op_layer_resnet50/Relu_11", + "_instance_idx": 0, + "_output_port_id": 0, + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": null, + "narrow_range": false, + "half_range": false, + "per_channel": false + }, + { + "_target_type": { + "_value_": 2, + "_name_": "AFTER_LAYER" + }, + "_layer_name": "batch_normalization_14", + "_instance_idx": 0, + "_output_port_id": 0, + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": null, + "narrow_range": false, + "half_range": false, + "per_channel": false + }, + { + "_target_type": { + "_value_": 2, + "_name_": "AFTER_LAYER" + }, + "_layer_name": "batch_normalization_11", + "_instance_idx": 0, + "_output_port_id": 0, + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": null, + "narrow_range": false, + "half_range": false, + "per_channel": false + }, + { + "_target_type": { + "_value_": 2, + "_name_": "AFTER_LAYER" + }, + "_layer_name": "tf_op_layer_resnet50/Relu_13", + "_instance_idx": 0, + "_output_port_id": 0, + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": null, + "narrow_range": false, + "half_range": false, + "per_channel": false + }, + { + "_target_type": { + "_value_": 2, + "_name_": "AFTER_LAYER" + }, + "_layer_name": "tf_op_layer_resnet50/Relu_14", + "_instance_idx": 0, + "_output_port_id": 0, + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": null, + "narrow_range": false, + "half_range": false, + "per_channel": false + }, + { + "_target_type": { + "_value_": 2, + "_name_": "AFTER_LAYER" + }, + "_layer_name": "tf_op_layer_resnet50/Relu_16", + "_instance_idx": 0, + "_output_port_id": 0, + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": null, + "narrow_range": false, + "half_range": false, + "per_channel": false + }, + { + "_target_type": { + "_value_": 2, + "_name_": "AFTER_LAYER" + }, + "_layer_name": "tf_op_layer_resnet50/Relu_17", + "_instance_idx": 0, + "_output_port_id": 0, + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": null, + "narrow_range": false, + "half_range": false, + "per_channel": false + }, + { + "_target_type": { + "_value_": 2, + "_name_": "AFTER_LAYER" + }, + "_layer_name": "tf_op_layer_resnet50/Relu_19", + "_instance_idx": 0, + "_output_port_id": 0, + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": null, + "narrow_range": false, + "half_range": false, + "per_channel": false + }, + { + "_target_type": { + "_value_": 2, + "_name_": "AFTER_LAYER" + }, + "_layer_name": "tf_op_layer_resnet50/Relu_20", + "_instance_idx": 0, + "_output_port_id": 0, + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": null, + "narrow_range": false, + "half_range": false, + "per_channel": false + }, + { + "_target_type": { + "_value_": 2, + "_name_": "AFTER_LAYER" + }, + "_layer_name": "tf_op_layer_resnet50/Relu_22", + "_instance_idx": 0, + "_output_port_id": 0, + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": null, + "narrow_range": false, + "half_range": false, + "per_channel": false + }, + { + "_target_type": { + "_value_": 2, + "_name_": "AFTER_LAYER" + }, + "_layer_name": "tf_op_layer_resnet50/Relu_23", + "_instance_idx": 0, + "_output_port_id": 0, + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": null, + "narrow_range": false, + "half_range": false, + "per_channel": false + }, + { + "_target_type": { + "_value_": 2, + "_name_": "AFTER_LAYER" + }, + "_layer_name": "batch_normalization_27", + "_instance_idx": 0, + "_output_port_id": 0, + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": null, + "narrow_range": false, + "half_range": false, + "per_channel": false + }, + { + "_target_type": { + "_value_": 2, + "_name_": "AFTER_LAYER" + }, + "_layer_name": "batch_normalization_24", + "_instance_idx": 0, + "_output_port_id": 0, + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": null, + "narrow_range": false, + "half_range": false, + "per_channel": false + }, + { + "_target_type": { + "_value_": 2, + "_name_": "AFTER_LAYER" + }, + "_layer_name": "tf_op_layer_resnet50/Relu_25", + "_instance_idx": 0, + "_output_port_id": 0, + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": null, + "narrow_range": false, + "half_range": false, + "per_channel": false + }, + { + "_target_type": { + "_value_": 2, + "_name_": "AFTER_LAYER" + }, + "_layer_name": "tf_op_layer_resnet50/Relu_26", + "_instance_idx": 0, + "_output_port_id": 0, + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": null, + "narrow_range": false, + "half_range": false, + "per_channel": false + }, + { + "_target_type": { + "_value_": 2, + "_name_": "AFTER_LAYER" + }, + "_layer_name": "tf_op_layer_resnet50/Relu_28", + "_instance_idx": 0, + "_output_port_id": 0, + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": null, + "narrow_range": false, + "half_range": false, + "per_channel": false + }, + { + "_target_type": { + "_value_": 2, + "_name_": "AFTER_LAYER" + }, + "_layer_name": "tf_op_layer_resnet50/Relu_29", + "_instance_idx": 0, + "_output_port_id": 0, + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": null, + "narrow_range": false, + "half_range": false, + "per_channel": false + }, + { + "_target_type": { + "_value_": 2, + "_name_": "AFTER_LAYER" + }, + "_layer_name": "tf_op_layer_resnet50/Relu_31", + "_instance_idx": 0, + "_output_port_id": 0, + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": null, + "narrow_range": false, + "half_range": false, + "per_channel": false + }, + { + "_target_type": { + "_value_": 2, + "_name_": "AFTER_LAYER" + }, + "_layer_name": "tf_op_layer_resnet50/Relu_32", + "_instance_idx": 0, + "_output_port_id": 0, + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": null, + "narrow_range": false, + "half_range": false, + "per_channel": false + }, + { + "_target_type": { + "_value_": 2, + "_name_": "AFTER_LAYER" + }, + "_layer_name": "tf_op_layer_resnet50/Relu_34", + "_instance_idx": 0, + "_output_port_id": 0, + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": null, + "narrow_range": false, + "half_range": false, + "per_channel": false + }, + { + "_target_type": { + "_value_": 2, + "_name_": "AFTER_LAYER" + }, + "_layer_name": "tf_op_layer_resnet50/Relu_35", + "_instance_idx": 0, + "_output_port_id": 0, + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": null, + "narrow_range": false, + "half_range": false, + "per_channel": false + }, + { + "_target_type": { + "_value_": 2, + "_name_": "AFTER_LAYER" + }, + "_layer_name": "tf_op_layer_resnet50/Relu_37", + "_instance_idx": 0, + "_output_port_id": 0, + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": null, + "narrow_range": false, + "half_range": false, + "per_channel": false + }, + { + "_target_type": { + "_value_": 2, + "_name_": "AFTER_LAYER" + }, + "_layer_name": "tf_op_layer_resnet50/Relu_38", + "_instance_idx": 0, + "_output_port_id": 0, + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": null, + "narrow_range": false, + "half_range": false, + "per_channel": false + }, + { + "_target_type": { + "_value_": 2, + "_name_": "AFTER_LAYER" + }, + "_layer_name": "tf_op_layer_resnet50/Relu_40", + "_instance_idx": 0, + "_output_port_id": 0, + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": null, + "narrow_range": false, + "half_range": false, + "per_channel": false + }, + { + "_target_type": { + "_value_": 2, + "_name_": "AFTER_LAYER" + }, + "_layer_name": "tf_op_layer_resnet50/Relu_41", + "_instance_idx": 0, + "_output_port_id": 0, + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": null, + "narrow_range": false, + "half_range": false, + "per_channel": false + }, + { + "_target_type": { + "_value_": 2, + "_name_": "AFTER_LAYER" + }, + "_layer_name": "batch_normalization_46", + "_instance_idx": 0, + "_output_port_id": 0, + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": null, + "narrow_range": false, + "half_range": false, + "per_channel": false + }, + { + "_target_type": { + "_value_": 2, + "_name_": "AFTER_LAYER" + }, + "_layer_name": "batch_normalization_43", + "_instance_idx": 0, + "_output_port_id": 0, + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": null, + "narrow_range": false, + "half_range": false, + "per_channel": false + }, + { + "_target_type": { + "_value_": 2, + "_name_": "AFTER_LAYER" + }, + "_layer_name": "tf_op_layer_resnet50/Relu_43", + "_instance_idx": 0, + "_output_port_id": 0, + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": null, + "narrow_range": false, + "half_range": false, + "per_channel": false + }, + { + "_target_type": { + "_value_": 2, + "_name_": "AFTER_LAYER" + }, + "_layer_name": "tf_op_layer_resnet50/Relu_44", + "_instance_idx": 0, + "_output_port_id": 0, + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": null, + "narrow_range": false, + "half_range": false, + "per_channel": false + }, + { + "_target_type": { + "_value_": 2, + "_name_": "AFTER_LAYER" + }, + "_layer_name": "tf_op_layer_resnet50/Relu_46", + "_instance_idx": 0, + "_output_port_id": 0, + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": null, + "narrow_range": false, + "half_range": false, + "per_channel": false + }, + { + "_target_type": { + "_value_": 2, + "_name_": "AFTER_LAYER" + }, + "_layer_name": "tf_op_layer_resnet50/Relu_47", + "_instance_idx": 0, + "_output_port_id": 0, + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": null, + "narrow_range": false, + "half_range": false, + "per_channel": false + }, + { + "_target_type": { + "_value_": 2, + "_name_": "AFTER_LAYER" + }, + "_layer_name": "tf_op_layer_resnet50/Relu_48", + "_instance_idx": 0, + "_output_port_id": 0, + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": null, + "narrow_range": false, + "half_range": false, + "per_channel": false + }, + { + "_target_type": { + "_value_": 2, + "_name_": "AFTER_LAYER" + }, + "_layer_name": "tf_op_layer_box_net/Relu_8", + "_instance_idx": 0, + "_output_port_id": 0, + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": null, + "narrow_range": false, + "half_range": false, + "per_channel": false + }, + { + "_target_type": { + "_value_": 2, + "_name_": "AFTER_LAYER" + }, + "_layer_name": "tf_op_layer_box_net/Relu_12", + "_instance_idx": 0, + "_output_port_id": 0, + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": null, + "narrow_range": false, + "half_range": false, + "per_channel": false + }, + { + "_target_type": { + "_value_": 2, + "_name_": "AFTER_LAYER" + }, + "_layer_name": "tf_op_layer_box_net/Relu_16", + "_instance_idx": 0, + "_output_port_id": 0, + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": null, + "narrow_range": false, + "half_range": false, + "per_channel": false + }, + { + "_target_type": { + "_value_": 2, + "_name_": "AFTER_LAYER" + }, + "_layer_name": "tf_op_layer_class_net/Relu_8", + "_instance_idx": 0, + "_output_port_id": 0, + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": null, + "narrow_range": false, + "half_range": false, + "per_channel": false + }, + { + "_target_type": { + "_value_": 2, + "_name_": "AFTER_LAYER" + }, + "_layer_name": "tf_op_layer_class_net/Relu_12", + "_instance_idx": 0, + "_output_port_id": 0, + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": null, + "narrow_range": false, + "half_range": false, + "per_channel": false + }, + { + "_target_type": { + "_value_": 2, + "_name_": "AFTER_LAYER" + }, + "_layer_name": "tf_op_layer_class_net/Relu_16", + "_instance_idx": 0, + "_output_port_id": 0, + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": null, + "narrow_range": false, + "half_range": false, + "per_channel": false + }, + { + "_target_type": { + "_value_": 2, + "_name_": "AFTER_LAYER" + }, + "_layer_name": "tf_op_layer_box_net/Relu_9", + "_instance_idx": 0, + "_output_port_id": 0, + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": null, + "narrow_range": false, + "half_range": false, + "per_channel": false + }, + { + "_target_type": { + "_value_": 2, + "_name_": "AFTER_LAYER" + }, + "_layer_name": "tf_op_layer_box_net/Relu_13", + "_instance_idx": 0, + "_output_port_id": 0, + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": null, + "narrow_range": false, + "half_range": false, + "per_channel": false + }, + { + "_target_type": { + "_value_": 2, + "_name_": "AFTER_LAYER" + }, + "_layer_name": "tf_op_layer_box_net/Relu_17", + "_instance_idx": 0, + "_output_port_id": 0, + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": null, + "narrow_range": false, + "half_range": false, + "per_channel": false + }, + { + "_target_type": { + "_value_": 2, + "_name_": "AFTER_LAYER" + }, + "_layer_name": "tf_op_layer_class_net/Relu_9", + "_instance_idx": 0, + "_output_port_id": 0, + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": null, + "narrow_range": false, + "half_range": false, + "per_channel": false + }, + { + "_target_type": { + "_value_": 2, + "_name_": "AFTER_LAYER" + }, + "_layer_name": "tf_op_layer_class_net/Relu_13", + "_instance_idx": 0, + "_output_port_id": 0, + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": null, + "narrow_range": false, + "half_range": false, + "per_channel": false + }, + { + "_target_type": { + "_value_": 2, + "_name_": "AFTER_LAYER" + }, + "_layer_name": "tf_op_layer_class_net/Relu_17", + "_instance_idx": 0, + "_output_port_id": 0, + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": null, + "narrow_range": false, + "half_range": false, + "per_channel": false + }, + { + "_target_type": { + "_value_": 2, + "_name_": "AFTER_LAYER" + }, + "_layer_name": "tf_op_layer_box_net/Relu_10", + "_instance_idx": 0, + "_output_port_id": 0, + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": null, + "narrow_range": false, + "half_range": false, + "per_channel": false + }, + { + "_target_type": { + "_value_": 2, + "_name_": "AFTER_LAYER" + }, + "_layer_name": "tf_op_layer_box_net/Relu_14", + "_instance_idx": 0, + "_output_port_id": 0, + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": null, + "narrow_range": false, + "half_range": false, + "per_channel": false + }, + { + "_target_type": { + "_value_": 2, + "_name_": "AFTER_LAYER" + }, + "_layer_name": "tf_op_layer_box_net/Relu_18", + "_instance_idx": 0, + "_output_port_id": 0, + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": null, + "narrow_range": false, + "half_range": false, + "per_channel": false + }, + { + "_target_type": { + "_value_": 2, + "_name_": "AFTER_LAYER" + }, + "_layer_name": "tf_op_layer_class_net/Relu_10", + "_instance_idx": 0, + "_output_port_id": 0, + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": null, + "narrow_range": false, + "half_range": false, + "per_channel": false + }, + { + "_target_type": { + "_value_": 2, + "_name_": "AFTER_LAYER" + }, + "_layer_name": "tf_op_layer_class_net/Relu_14", + "_instance_idx": 0, + "_output_port_id": 0, + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": null, + "narrow_range": false, + "half_range": false, + "per_channel": false + }, + { + "_target_type": { + "_value_": 2, + "_name_": "AFTER_LAYER" + }, + "_layer_name": "tf_op_layer_class_net/Relu_18", + "_instance_idx": 0, + "_output_port_id": 0, + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": null, + "narrow_range": false, + "half_range": false, + "per_channel": false + }, + { + "_target_type": { + "_value_": 2, + "_name_": "AFTER_LAYER" + }, + "_layer_name": "tf_op_layer_box_net/Relu_11", + "_instance_idx": 0, + "_output_port_id": 0, + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": null, + "narrow_range": false, + "half_range": false, + "per_channel": false + }, + { + "_target_type": { + "_value_": 2, + "_name_": "AFTER_LAYER" + }, + "_layer_name": "tf_op_layer_box_net/Relu_15", + "_instance_idx": 0, + "_output_port_id": 0, + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": null, + "narrow_range": false, + "half_range": false, + "per_channel": false + }, + { + "_target_type": { + "_value_": 2, + "_name_": "AFTER_LAYER" + }, + "_layer_name": "tf_op_layer_box_net/Relu_19", + "_instance_idx": 0, + "_output_port_id": 0, + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": null, + "narrow_range": false, + "half_range": false, + "per_channel": false + }, + { + "_target_type": { + "_value_": 2, + "_name_": "AFTER_LAYER" + }, + "_layer_name": "tf_op_layer_class_net/Relu_11", + "_instance_idx": 0, + "_output_port_id": 0, + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": null, + "narrow_range": false, + "half_range": false, + "per_channel": false + }, + { + "_target_type": { + "_value_": 2, + "_name_": "AFTER_LAYER" + }, + "_layer_name": "tf_op_layer_class_net/Relu_15", + "_instance_idx": 0, + "_output_port_id": 0, + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": null, + "narrow_range": false, + "half_range": false, + "per_channel": false + }, + { + "_target_type": { + "_value_": 2, + "_name_": "AFTER_LAYER" + }, + "_layer_name": "tf_op_layer_class_net/Relu_19", + "_instance_idx": 0, + "_output_port_id": 0, + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": null, + "narrow_range": false, + "half_range": false, + "per_channel": false + }, + { + "_target_type": { + "_value_": 2, + "_name_": "AFTER_LAYER" + }, + "_layer_name": "up_sampling2d", + "_instance_idx": 0, + "_output_port_id": 0, + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": null, + "narrow_range": false, + "half_range": false, + "per_channel": false + }, + { + "_target_type": { + "_value_": 2, + "_name_": "AFTER_LAYER" + }, + "_layer_name": "l4", + "_instance_idx": 0, + "_output_port_id": 0, + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": null, + "narrow_range": false, + "half_range": false, + "per_channel": false + }, + { + "_target_type": { + "_value_": 2, + "_name_": "AFTER_LAYER" + }, + "_layer_name": "up_sampling2d_1", + "_instance_idx": 0, + "_output_port_id": 0, + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": null, + "narrow_range": false, + "half_range": false, + "per_channel": false + }, + { + "_target_type": { + "_value_": 2, + "_name_": "AFTER_LAYER" + }, + "_layer_name": "l3", + "_instance_idx": 0, + "_output_port_id": 0, + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": null, + "narrow_range": false, + "half_range": false, + "per_channel": false + }, + { + "_target_type": { + "_value_": 2, + "_name_": "AFTER_LAYER" + }, + "_layer_name": "tf_op_layer_fpn/add_1", + "_instance_idx": 0, + "_output_port_id": 0, + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": null, + "narrow_range": false, + "half_range": false, + "per_channel": false + }, + { + "_target_type": { + "_value_": 2, + "_name_": "AFTER_LAYER" + }, + "_layer_name": "tf_op_layer_box_net/Relu", + "_instance_idx": 0, + "_output_port_id": 0, + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": null, + "narrow_range": false, + "half_range": false, + "per_channel": false + }, + { + "_target_type": { + "_value_": 2, + "_name_": "AFTER_LAYER" + }, + "_layer_name": "tf_op_layer_box_net/Relu_4", + "_instance_idx": 0, + "_output_port_id": 0, + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": null, + "narrow_range": false, + "half_range": false, + "per_channel": false + }, + { + "_target_type": { + "_value_": 2, + "_name_": "AFTER_LAYER" + }, + "_layer_name": "tf_op_layer_class_net/Relu", + "_instance_idx": 0, + "_output_port_id": 0, + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": null, + "narrow_range": false, + "half_range": false, + "per_channel": false + }, + { + "_target_type": { + "_value_": 2, + "_name_": "AFTER_LAYER" + }, + "_layer_name": "tf_op_layer_class_net/Relu_4", + "_instance_idx": 0, + "_output_port_id": 0, + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": null, + "narrow_range": false, + "half_range": false, + "per_channel": false + }, + { + "_target_type": { + "_value_": 2, + "_name_": "AFTER_LAYER" + }, + "_layer_name": "tf_op_layer_box_net/Relu_1", + "_instance_idx": 0, + "_output_port_id": 0, + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": null, + "narrow_range": false, + "half_range": false, + "per_channel": false + }, + { + "_target_type": { + "_value_": 2, + "_name_": "AFTER_LAYER" + }, + "_layer_name": "tf_op_layer_box_net/Relu_5", + "_instance_idx": 0, + "_output_port_id": 0, + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": null, + "narrow_range": false, + "half_range": false, + "per_channel": false + }, + { + "_target_type": { + "_value_": 2, + "_name_": "AFTER_LAYER" + }, + "_layer_name": "tf_op_layer_class_net/Relu_1", + "_instance_idx": 0, + "_output_port_id": 0, + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": null, + "narrow_range": false, + "half_range": false, + "per_channel": false + }, + { + "_target_type": { + "_value_": 2, + "_name_": "AFTER_LAYER" + }, + "_layer_name": "tf_op_layer_class_net/Relu_5", + "_instance_idx": 0, + "_output_port_id": 0, + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": null, + "narrow_range": false, + "half_range": false, + "per_channel": false + }, + { + "_target_type": { + "_value_": 2, + "_name_": "AFTER_LAYER" + }, + "_layer_name": "tf_op_layer_box_net/Relu_2", + "_instance_idx": 0, + "_output_port_id": 0, + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": null, + "narrow_range": false, + "half_range": false, + "per_channel": false + }, + { + "_target_type": { + "_value_": 2, + "_name_": "AFTER_LAYER" + }, + "_layer_name": "tf_op_layer_box_net/Relu_6", + "_instance_idx": 0, + "_output_port_id": 0, + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": null, + "narrow_range": false, + "half_range": false, + "per_channel": false + }, + { + "_target_type": { + "_value_": 2, + "_name_": "AFTER_LAYER" + }, + "_layer_name": "tf_op_layer_class_net/Relu_2", + "_instance_idx": 0, + "_output_port_id": 0, + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": null, + "narrow_range": false, + "half_range": false, + "per_channel": false + }, + { + "_target_type": { + "_value_": 2, + "_name_": "AFTER_LAYER" + }, + "_layer_name": "tf_op_layer_class_net/Relu_6", + "_instance_idx": 0, + "_output_port_id": 0, + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": null, + "narrow_range": false, + "half_range": false, + "per_channel": false + }, + { + "_target_type": { + "_value_": 2, + "_name_": "AFTER_LAYER" + }, + "_layer_name": "tf_op_layer_box_net/Relu_3", + "_instance_idx": 0, + "_output_port_id": 0, + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": null, + "narrow_range": false, + "half_range": false, + "per_channel": false + }, + { + "_target_type": { + "_value_": 2, + "_name_": "AFTER_LAYER" + }, + "_layer_name": "tf_op_layer_box_net/Relu_7", + "_instance_idx": 0, + "_output_port_id": 0, + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": null, + "narrow_range": false, + "half_range": false, + "per_channel": false + }, + { + "_target_type": { + "_value_": 2, + "_name_": "AFTER_LAYER" + }, + "_layer_name": "tf_op_layer_class_net/Relu_3", + "_instance_idx": 0, + "_output_port_id": 0, + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": null, + "narrow_range": false, + "half_range": false, + "per_channel": false + }, + { + "_target_type": { + "_value_": 2, + "_name_": "AFTER_LAYER" + }, + "_layer_name": "tf_op_layer_class_net/Relu_7", + "_instance_idx": 0, + "_output_port_id": 0, + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": null, + "narrow_range": false, + "half_range": false, + "per_channel": false + }, + { + "_target_type": { + "_value_": 2, + "_name_": "AFTER_LAYER" + }, + "_layer_name": "tf_op_layer_resnet50/Relu", + "_instance_idx": 0, + "_output_port_id": 0, + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": null, + "narrow_range": false, + "half_range": false, + "per_channel": false + }, + { + "_target_type": { + "_value_": 2, + "_name_": "AFTER_LAYER" + }, + "_layer_name": "tf_op_layer_resnet50/Relu_3", + "_instance_idx": 0, + "_output_port_id": 0, + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": null, + "narrow_range": false, + "half_range": false, + "per_channel": false + }, + { + "_target_type": { + "_value_": 2, + "_name_": "AFTER_LAYER" + }, + "_layer_name": "batch_normalization_7", + "_instance_idx": 0, + "_output_port_id": 0, + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": null, + "narrow_range": false, + "half_range": false, + "per_channel": false + }, + { + "_target_type": { + "_value_": 2, + "_name_": "AFTER_LAYER" + }, + "_layer_name": "batch_normalization_10", + "_instance_idx": 0, + "_output_port_id": 0, + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": null, + "narrow_range": false, + "half_range": false, + "per_channel": false + }, + { + "_target_type": { + "_value_": 2, + "_name_": "AFTER_LAYER" + }, + "_layer_name": "tf_op_layer_resnet50/Relu_6", + "_instance_idx": 0, + "_output_port_id": 0, + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": null, + "narrow_range": false, + "half_range": false, + "per_channel": false + }, + { + "_target_type": { + "_value_": 2, + "_name_": "AFTER_LAYER" + }, + "_layer_name": "batch_normalization_17", + "_instance_idx": 0, + "_output_port_id": 0, + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": null, + "narrow_range": false, + "half_range": false, + "per_channel": false + }, + { + "_target_type": { + "_value_": 2, + "_name_": "AFTER_LAYER" + }, + "_layer_name": "tf_op_layer_resnet50/Relu_12", + "_instance_idx": 0, + "_output_port_id": 0, + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": null, + "narrow_range": false, + "half_range": false, + "per_channel": false + }, + { + "_target_type": { + "_value_": 2, + "_name_": "AFTER_LAYER" + }, + "_layer_name": "batch_normalization_20", + "_instance_idx": 0, + "_output_port_id": 0, + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": null, + "narrow_range": false, + "half_range": false, + "per_channel": false + }, + { + "_target_type": { + "_value_": 2, + "_name_": "AFTER_LAYER" + }, + "_layer_name": "tf_op_layer_resnet50/Relu_15", + "_instance_idx": 0, + "_output_port_id": 0, + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": null, + "narrow_range": false, + "half_range": false, + "per_channel": false + }, + { + "_target_type": { + "_value_": 2, + "_name_": "AFTER_LAYER" + }, + "_layer_name": "batch_normalization_23", + "_instance_idx": 0, + "_output_port_id": 0, + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": null, + "narrow_range": false, + "half_range": false, + "per_channel": false + }, + { + "_target_type": { + "_value_": 2, + "_name_": "AFTER_LAYER" + }, + "_layer_name": "tf_op_layer_resnet50/Relu_18", + "_instance_idx": 0, + "_output_port_id": 0, + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": null, + "narrow_range": false, + "half_range": false, + "per_channel": false + }, + { + "_target_type": { + "_value_": 2, + "_name_": "AFTER_LAYER" + }, + "_layer_name": "batch_normalization_30", + "_instance_idx": 0, + "_output_port_id": 0, + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": null, + "narrow_range": false, + "half_range": false, + "per_channel": false + }, + { + "_target_type": { + "_value_": 2, + "_name_": "AFTER_LAYER" + }, + "_layer_name": "tf_op_layer_resnet50/Relu_24", + "_instance_idx": 0, + "_output_port_id": 0, + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": null, + "narrow_range": false, + "half_range": false, + "per_channel": false + }, + { + "_target_type": { + "_value_": 2, + "_name_": "AFTER_LAYER" + }, + "_layer_name": "batch_normalization_33", + "_instance_idx": 0, + "_output_port_id": 0, + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": null, + "narrow_range": false, + "half_range": false, + "per_channel": false + }, + { + "_target_type": { + "_value_": 2, + "_name_": "AFTER_LAYER" + }, + "_layer_name": "tf_op_layer_resnet50/Relu_27", + "_instance_idx": 0, + "_output_port_id": 0, + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": null, + "narrow_range": false, + "half_range": false, + "per_channel": false + }, + { + "_target_type": { + "_value_": 2, + "_name_": "AFTER_LAYER" + }, + "_layer_name": "tf_op_layer_resnet50/Relu_30", + "_instance_idx": 0, + "_output_port_id": 0, + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": null, + "narrow_range": false, + "half_range": false, + "per_channel": false + }, + { + "_target_type": { + "_value_": 2, + "_name_": "AFTER_LAYER" + }, + "_layer_name": "batch_normalization_36", + "_instance_idx": 0, + "_output_port_id": 0, + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": null, + "narrow_range": false, + "half_range": false, + "per_channel": false + }, + { + "_target_type": { + "_value_": 2, + "_name_": "AFTER_LAYER" + }, + "_layer_name": "batch_normalization_39", + "_instance_idx": 0, + "_output_port_id": 0, + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": null, + "narrow_range": false, + "half_range": false, + "per_channel": false + }, + { + "_target_type": { + "_value_": 2, + "_name_": "AFTER_LAYER" + }, + "_layer_name": "tf_op_layer_resnet50/Relu_33", + "_instance_idx": 0, + "_output_port_id": 0, + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": null, + "narrow_range": false, + "half_range": false, + "per_channel": false + }, + { + "_target_type": { + "_value_": 2, + "_name_": "AFTER_LAYER" + }, + "_layer_name": "tf_op_layer_resnet50/Relu_36", + "_instance_idx": 0, + "_output_port_id": 0, + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": null, + "narrow_range": false, + "half_range": false, + "per_channel": false + }, + { + "_target_type": { + "_value_": 2, + "_name_": "AFTER_LAYER" + }, + "_layer_name": "batch_normalization_42", + "_instance_idx": 0, + "_output_port_id": 0, + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": null, + "narrow_range": false, + "half_range": false, + "per_channel": false + }, + { + "_target_type": { + "_value_": 2, + "_name_": "AFTER_LAYER" + }, + "_layer_name": "batch_normalization_49", + "_instance_idx": 0, + "_output_port_id": 0, + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": null, + "narrow_range": false, + "half_range": false, + "per_channel": false + }, + { + "_target_type": { + "_value_": 2, + "_name_": "AFTER_LAYER" + }, + "_layer_name": "tf_op_layer_resnet50/Relu_42", + "_instance_idx": 0, + "_output_port_id": 0, + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": null, + "narrow_range": false, + "half_range": false, + "per_channel": false + }, + { + "_target_type": { + "_value_": 2, + "_name_": "AFTER_LAYER" + }, + "_layer_name": "tf_op_layer_resnet50/Relu_45", + "_instance_idx": 0, + "_output_port_id": 0, + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": null, + "narrow_range": false, + "half_range": false, + "per_channel": false + }, + { + "_target_type": { + "_value_": 2, + "_name_": "AFTER_LAYER" + }, + "_layer_name": "batch_normalization_52", + "_instance_idx": 0, + "_output_port_id": 0, + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": null, + "narrow_range": false, + "half_range": false, + "per_channel": false + }, + { + "_target_type": { + "_value_": 2, + "_name_": "AFTER_LAYER" + }, + "_layer_name": "l5", + "_instance_idx": 0, + "_output_port_id": 0, + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": null, + "narrow_range": false, + "half_range": false, + "per_channel": false + }, + { + "_target_type": { + "_value_": 2, + "_name_": "AFTER_LAYER" + }, + "_layer_name": "post_hoc_d5", + "_instance_idx": 0, + "_output_port_id": 0, + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": null, + "narrow_range": false, + "half_range": false, + "per_channel": false + }, + { + "_target_type": { + "_value_": 2, + "_name_": "AFTER_LAYER" + }, + "_layer_name": "p5-bn", + "_instance_idx": 0, + "_output_port_id": 0, + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": null, + "narrow_range": false, + "half_range": false, + "per_channel": false + }, + { + "_target_type": { + "_value_": 2, + "_name_": "AFTER_LAYER" + }, + "_layer_name": "p6-bn", + "_instance_idx": 0, + "_output_port_id": 0, + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": null, + "narrow_range": false, + "half_range": false, + "per_channel": false + }, + { + "_target_type": { + "_value_": 2, + "_name_": "AFTER_LAYER" + }, + "_layer_name": "p7-bn", + "_instance_idx": 0, + "_output_port_id": 0, + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": null, + "narrow_range": false, + "half_range": false, + "per_channel": false + }, + { + "_target_type": { + "_value_": 2, + "_name_": "AFTER_LAYER" + }, + "_layer_name": "tf_op_layer_fpn/add", + "_instance_idx": 0, + "_output_port_id": 0, + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": null, + "narrow_range": false, + "half_range": false, + "per_channel": false + }, + { + "_target_type": { + "_value_": 2, + "_name_": "AFTER_LAYER" + }, + "_layer_name": "p3-bn", + "_instance_idx": 0, + "_output_port_id": 0, + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": null, + "narrow_range": false, + "half_range": false, + "per_channel": false + }, + { + "_target_type": { + "_value_": 2, + "_name_": "AFTER_LAYER" + }, + "_layer_name": "p4-bn", + "_instance_idx": 0, + "_output_port_id": 0, + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": null, + "narrow_range": false, + "half_range": false, + "per_channel": false + }, + { + "_target_type": { + "_value_": 2, + "_name_": "AFTER_LAYER" + }, + "_layer_name": "tf_op_layer_resnet50/Relu_39", + "_instance_idx": 0, + "_output_port_id": 0, + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": null, + "narrow_range": false, + "half_range": false, + "per_channel": false + }, + { + "_target_type": { + "_value_": 2, + "_name_": "AFTER_LAYER" + }, + "_layer_name": "p6", + "_instance_idx": 0, + "_output_port_id": 0, + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": null, + "narrow_range": false, + "half_range": false, + "per_channel": false + }, + { + "_target_type": { + "_value_": 2, + "_name_": "AFTER_LAYER" + }, + "_layer_name": "tf_op_layer_resnet50/Relu_9", + "_instance_idx": 0, + "_output_port_id": 0, + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": null, + "narrow_range": false, + "half_range": false, + "per_channel": false + }, + { + "_target_type": { + "_value_": 2, + "_name_": "AFTER_LAYER" + }, + "_layer_name": "tf_op_layer_resnet50/Relu_21", + "_instance_idx": 0, + "_output_port_id": 0, + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": null, + "narrow_range": false, + "half_range": false, + "per_channel": false + }, + { + "_target_type": { + "_value_": 5, + "_name_": "OPERATION_WITH_WEIGHTS" + }, + "_layer_name": "conv2d", + "_weights_attr_name": "kernel", + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": true, + "narrow_range": false, + "half_range": true, + "per_channel": true + }, + { + "_target_type": { + "_value_": 5, + "_name_": "OPERATION_WITH_WEIGHTS" + }, + "_layer_name": "conv2d_2", + "_weights_attr_name": "kernel", + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": true, + "narrow_range": false, + "half_range": true, + "per_channel": true + }, + { + "_target_type": { + "_value_": 5, + "_name_": "OPERATION_WITH_WEIGHTS" + }, + "_layer_name": "conv2d_3", + "_weights_attr_name": "kernel", + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": true, + "narrow_range": false, + "half_range": true, + "per_channel": true + }, + { + "_target_type": { + "_value_": 5, + "_name_": "OPERATION_WITH_WEIGHTS" + }, + "_layer_name": "conv2d_4", + "_weights_attr_name": "kernel", + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": true, + "narrow_range": false, + "half_range": true, + "per_channel": true + }, + { + "_target_type": { + "_value_": 5, + "_name_": "OPERATION_WITH_WEIGHTS" + }, + "_layer_name": "conv2d_1", + "_weights_attr_name": "kernel", + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": true, + "narrow_range": false, + "half_range": true, + "per_channel": true + }, + { + "_target_type": { + "_value_": 5, + "_name_": "OPERATION_WITH_WEIGHTS" + }, + "_layer_name": "conv2d_5", + "_weights_attr_name": "kernel", + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": true, + "narrow_range": false, + "half_range": true, + "per_channel": true + }, + { + "_target_type": { + "_value_": 5, + "_name_": "OPERATION_WITH_WEIGHTS" + }, + "_layer_name": "conv2d_6", + "_weights_attr_name": "kernel", + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": true, + "narrow_range": false, + "half_range": true, + "per_channel": true + }, + { + "_target_type": { + "_value_": 5, + "_name_": "OPERATION_WITH_WEIGHTS" + }, + "_layer_name": "conv2d_7", + "_weights_attr_name": "kernel", + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": true, + "narrow_range": false, + "half_range": true, + "per_channel": true + }, + { + "_target_type": { + "_value_": 5, + "_name_": "OPERATION_WITH_WEIGHTS" + }, + "_layer_name": "conv2d_8", + "_weights_attr_name": "kernel", + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": true, + "narrow_range": false, + "half_range": true, + "per_channel": true + }, + { + "_target_type": { + "_value_": 5, + "_name_": "OPERATION_WITH_WEIGHTS" + }, + "_layer_name": "conv2d_9", + "_weights_attr_name": "kernel", + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": true, + "narrow_range": false, + "half_range": true, + "per_channel": true + }, + { + "_target_type": { + "_value_": 5, + "_name_": "OPERATION_WITH_WEIGHTS" + }, + "_layer_name": "conv2d_10", + "_weights_attr_name": "kernel", + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": true, + "narrow_range": false, + "half_range": true, + "per_channel": true + }, + { + "_target_type": { + "_value_": 5, + "_name_": "OPERATION_WITH_WEIGHTS" + }, + "_layer_name": "conv2d_12", + "_weights_attr_name": "kernel", + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": true, + "narrow_range": false, + "half_range": true, + "per_channel": true + }, + { + "_target_type": { + "_value_": 5, + "_name_": "OPERATION_WITH_WEIGHTS" + }, + "_layer_name": "conv2d_13", + "_weights_attr_name": "kernel", + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": true, + "narrow_range": false, + "half_range": true, + "per_channel": true + }, + { + "_target_type": { + "_value_": 5, + "_name_": "OPERATION_WITH_WEIGHTS" + }, + "_layer_name": "conv2d_14", + "_weights_attr_name": "kernel", + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": true, + "narrow_range": false, + "half_range": true, + "per_channel": true + }, + { + "_target_type": { + "_value_": 5, + "_name_": "OPERATION_WITH_WEIGHTS" + }, + "_layer_name": "conv2d_11", + "_weights_attr_name": "kernel", + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": true, + "narrow_range": false, + "half_range": true, + "per_channel": true + }, + { + "_target_type": { + "_value_": 5, + "_name_": "OPERATION_WITH_WEIGHTS" + }, + "_layer_name": "conv2d_15", + "_weights_attr_name": "kernel", + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": true, + "narrow_range": false, + "half_range": true, + "per_channel": true + }, + { + "_target_type": { + "_value_": 5, + "_name_": "OPERATION_WITH_WEIGHTS" + }, + "_layer_name": "conv2d_16", + "_weights_attr_name": "kernel", + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": true, + "narrow_range": false, + "half_range": true, + "per_channel": true + }, + { + "_target_type": { + "_value_": 5, + "_name_": "OPERATION_WITH_WEIGHTS" + }, + "_layer_name": "conv2d_17", + "_weights_attr_name": "kernel", + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": true, + "narrow_range": false, + "half_range": true, + "per_channel": true + }, + { + "_target_type": { + "_value_": 5, + "_name_": "OPERATION_WITH_WEIGHTS" + }, + "_layer_name": "conv2d_18", + "_weights_attr_name": "kernel", + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": true, + "narrow_range": false, + "half_range": true, + "per_channel": true + }, + { + "_target_type": { + "_value_": 5, + "_name_": "OPERATION_WITH_WEIGHTS" + }, + "_layer_name": "conv2d_19", + "_weights_attr_name": "kernel", + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": true, + "narrow_range": false, + "half_range": true, + "per_channel": true + }, + { + "_target_type": { + "_value_": 5, + "_name_": "OPERATION_WITH_WEIGHTS" + }, + "_layer_name": "conv2d_20", + "_weights_attr_name": "kernel", + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": true, + "narrow_range": false, + "half_range": true, + "per_channel": true + }, + { + "_target_type": { + "_value_": 5, + "_name_": "OPERATION_WITH_WEIGHTS" + }, + "_layer_name": "conv2d_21", + "_weights_attr_name": "kernel", + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": true, + "narrow_range": false, + "half_range": true, + "per_channel": true + }, + { + "_target_type": { + "_value_": 5, + "_name_": "OPERATION_WITH_WEIGHTS" + }, + "_layer_name": "conv2d_22", + "_weights_attr_name": "kernel", + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": true, + "narrow_range": false, + "half_range": true, + "per_channel": true + }, + { + "_target_type": { + "_value_": 5, + "_name_": "OPERATION_WITH_WEIGHTS" + }, + "_layer_name": "conv2d_23", + "_weights_attr_name": "kernel", + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": true, + "narrow_range": false, + "half_range": true, + "per_channel": true + }, + { + "_target_type": { + "_value_": 5, + "_name_": "OPERATION_WITH_WEIGHTS" + }, + "_layer_name": "conv2d_25", + "_weights_attr_name": "kernel", + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": true, + "narrow_range": false, + "half_range": true, + "per_channel": true + }, + { + "_target_type": { + "_value_": 5, + "_name_": "OPERATION_WITH_WEIGHTS" + }, + "_layer_name": "conv2d_26", + "_weights_attr_name": "kernel", + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": true, + "narrow_range": false, + "half_range": true, + "per_channel": true + }, + { + "_target_type": { + "_value_": 5, + "_name_": "OPERATION_WITH_WEIGHTS" + }, + "_layer_name": "conv2d_27", + "_weights_attr_name": "kernel", + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": true, + "narrow_range": false, + "half_range": true, + "per_channel": true + }, + { + "_target_type": { + "_value_": 5, + "_name_": "OPERATION_WITH_WEIGHTS" + }, + "_layer_name": "conv2d_24", + "_weights_attr_name": "kernel", + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": true, + "narrow_range": false, + "half_range": true, + "per_channel": true + }, + { + "_target_type": { + "_value_": 5, + "_name_": "OPERATION_WITH_WEIGHTS" + }, + "_layer_name": "conv2d_28", + "_weights_attr_name": "kernel", + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": true, + "narrow_range": false, + "half_range": true, + "per_channel": true + }, + { + "_target_type": { + "_value_": 5, + "_name_": "OPERATION_WITH_WEIGHTS" + }, + "_layer_name": "conv2d_29", + "_weights_attr_name": "kernel", + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": true, + "narrow_range": false, + "half_range": true, + "per_channel": true + }, + { + "_target_type": { + "_value_": 5, + "_name_": "OPERATION_WITH_WEIGHTS" + }, + "_layer_name": "conv2d_30", + "_weights_attr_name": "kernel", + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": true, + "narrow_range": false, + "half_range": true, + "per_channel": true + }, + { + "_target_type": { + "_value_": 5, + "_name_": "OPERATION_WITH_WEIGHTS" + }, + "_layer_name": "conv2d_31", + "_weights_attr_name": "kernel", + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": true, + "narrow_range": false, + "half_range": true, + "per_channel": true + }, + { + "_target_type": { + "_value_": 5, + "_name_": "OPERATION_WITH_WEIGHTS" + }, + "_layer_name": "conv2d_32", + "_weights_attr_name": "kernel", + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": true, + "narrow_range": false, + "half_range": true, + "per_channel": true + }, + { + "_target_type": { + "_value_": 5, + "_name_": "OPERATION_WITH_WEIGHTS" + }, + "_layer_name": "conv2d_33", + "_weights_attr_name": "kernel", + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": true, + "narrow_range": false, + "half_range": true, + "per_channel": true + }, + { + "_target_type": { + "_value_": 5, + "_name_": "OPERATION_WITH_WEIGHTS" + }, + "_layer_name": "conv2d_34", + "_weights_attr_name": "kernel", + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": true, + "narrow_range": false, + "half_range": true, + "per_channel": true + }, + { + "_target_type": { + "_value_": 5, + "_name_": "OPERATION_WITH_WEIGHTS" + }, + "_layer_name": "conv2d_35", + "_weights_attr_name": "kernel", + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": true, + "narrow_range": false, + "half_range": true, + "per_channel": true + }, + { + "_target_type": { + "_value_": 5, + "_name_": "OPERATION_WITH_WEIGHTS" + }, + "_layer_name": "conv2d_36", + "_weights_attr_name": "kernel", + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": true, + "narrow_range": false, + "half_range": true, + "per_channel": true + }, + { + "_target_type": { + "_value_": 5, + "_name_": "OPERATION_WITH_WEIGHTS" + }, + "_layer_name": "conv2d_37", + "_weights_attr_name": "kernel", + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": true, + "narrow_range": false, + "half_range": true, + "per_channel": true + }, + { + "_target_type": { + "_value_": 5, + "_name_": "OPERATION_WITH_WEIGHTS" + }, + "_layer_name": "conv2d_38", + "_weights_attr_name": "kernel", + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": true, + "narrow_range": false, + "half_range": true, + "per_channel": true + }, + { + "_target_type": { + "_value_": 5, + "_name_": "OPERATION_WITH_WEIGHTS" + }, + "_layer_name": "conv2d_39", + "_weights_attr_name": "kernel", + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": true, + "narrow_range": false, + "half_range": true, + "per_channel": true + }, + { + "_target_type": { + "_value_": 5, + "_name_": "OPERATION_WITH_WEIGHTS" + }, + "_layer_name": "conv2d_40", + "_weights_attr_name": "kernel", + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": true, + "narrow_range": false, + "half_range": true, + "per_channel": true + }, + { + "_target_type": { + "_value_": 5, + "_name_": "OPERATION_WITH_WEIGHTS" + }, + "_layer_name": "conv2d_41", + "_weights_attr_name": "kernel", + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": true, + "narrow_range": false, + "half_range": true, + "per_channel": true + }, + { + "_target_type": { + "_value_": 5, + "_name_": "OPERATION_WITH_WEIGHTS" + }, + "_layer_name": "conv2d_42", + "_weights_attr_name": "kernel", + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": true, + "narrow_range": false, + "half_range": true, + "per_channel": true + }, + { + "_target_type": { + "_value_": 5, + "_name_": "OPERATION_WITH_WEIGHTS" + }, + "_layer_name": "conv2d_44", + "_weights_attr_name": "kernel", + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": true, + "narrow_range": false, + "half_range": true, + "per_channel": true + }, + { + "_target_type": { + "_value_": 5, + "_name_": "OPERATION_WITH_WEIGHTS" + }, + "_layer_name": "conv2d_45", + "_weights_attr_name": "kernel", + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": true, + "narrow_range": false, + "half_range": true, + "per_channel": true + }, + { + "_target_type": { + "_value_": 5, + "_name_": "OPERATION_WITH_WEIGHTS" + }, + "_layer_name": "conv2d_46", + "_weights_attr_name": "kernel", + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": true, + "narrow_range": false, + "half_range": true, + "per_channel": true + }, + { + "_target_type": { + "_value_": 5, + "_name_": "OPERATION_WITH_WEIGHTS" + }, + "_layer_name": "conv2d_43", + "_weights_attr_name": "kernel", + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": true, + "narrow_range": false, + "half_range": true, + "per_channel": true + }, + { + "_target_type": { + "_value_": 5, + "_name_": "OPERATION_WITH_WEIGHTS" + }, + "_layer_name": "conv2d_47", + "_weights_attr_name": "kernel", + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": true, + "narrow_range": false, + "half_range": true, + "per_channel": true + }, + { + "_target_type": { + "_value_": 5, + "_name_": "OPERATION_WITH_WEIGHTS" + }, + "_layer_name": "conv2d_48", + "_weights_attr_name": "kernel", + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": true, + "narrow_range": false, + "half_range": true, + "per_channel": true + }, + { + "_target_type": { + "_value_": 5, + "_name_": "OPERATION_WITH_WEIGHTS" + }, + "_layer_name": "conv2d_49", + "_weights_attr_name": "kernel", + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": true, + "narrow_range": false, + "half_range": true, + "per_channel": true + }, + { + "_target_type": { + "_value_": 5, + "_name_": "OPERATION_WITH_WEIGHTS" + }, + "_layer_name": "conv2d_50", + "_weights_attr_name": "kernel", + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": true, + "narrow_range": false, + "half_range": true, + "per_channel": true + }, + { + "_target_type": { + "_value_": 5, + "_name_": "OPERATION_WITH_WEIGHTS" + }, + "_layer_name": "conv2d_51", + "_weights_attr_name": "kernel", + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": true, + "narrow_range": false, + "half_range": true, + "per_channel": true + }, + { + "_target_type": { + "_value_": 5, + "_name_": "OPERATION_WITH_WEIGHTS" + }, + "_layer_name": "conv2d_52", + "_weights_attr_name": "kernel", + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": true, + "narrow_range": false, + "half_range": true, + "per_channel": true + }, + { + "_target_type": { + "_value_": 5, + "_name_": "OPERATION_WITH_WEIGHTS" + }, + "_layer_name": "l5", + "_weights_attr_name": "kernel", + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": true, + "narrow_range": false, + "half_range": true, + "per_channel": true + }, + { + "_target_type": { + "_value_": 5, + "_name_": "OPERATION_WITH_WEIGHTS" + }, + "_layer_name": "l4", + "_weights_attr_name": "kernel", + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": true, + "narrow_range": false, + "half_range": true, + "per_channel": true + }, + { + "_target_type": { + "_value_": 5, + "_name_": "OPERATION_WITH_WEIGHTS" + }, + "_layer_name": "post_hoc_d5", + "_weights_attr_name": "kernel", + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": true, + "narrow_range": false, + "half_range": true, + "per_channel": true + }, + { + "_target_type": { + "_value_": 5, + "_name_": "OPERATION_WITH_WEIGHTS" + }, + "_layer_name": "l3", + "_weights_attr_name": "kernel", + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": true, + "narrow_range": false, + "half_range": true, + "per_channel": true + }, + { + "_target_type": { + "_value_": 5, + "_name_": "OPERATION_WITH_WEIGHTS" + }, + "_layer_name": "p6", + "_weights_attr_name": "kernel", + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": true, + "narrow_range": false, + "half_range": true, + "per_channel": true + }, + { + "_target_type": { + "_value_": 5, + "_name_": "OPERATION_WITH_WEIGHTS" + }, + "_layer_name": "post_hoc_d3", + "_weights_attr_name": "kernel", + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": true, + "narrow_range": false, + "half_range": true, + "per_channel": true + }, + { + "_target_type": { + "_value_": 5, + "_name_": "OPERATION_WITH_WEIGHTS" + }, + "_layer_name": "post_hoc_d4", + "_weights_attr_name": "kernel", + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": true, + "narrow_range": false, + "half_range": true, + "per_channel": true + }, + { + "_target_type": { + "_value_": 5, + "_name_": "OPERATION_WITH_WEIGHTS" + }, + "_layer_name": "p7", + "_weights_attr_name": "kernel", + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": true, + "narrow_range": false, + "half_range": true, + "per_channel": true + }, + { + "_target_type": { + "_value_": 5, + "_name_": "OPERATION_WITH_WEIGHTS" + }, + "_layer_name": "box-0", + "_weights_attr_name": "kernel", + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": true, + "narrow_range": false, + "half_range": true, + "per_channel": true + }, + { + "_target_type": { + "_value_": 5, + "_name_": "OPERATION_WITH_WEIGHTS" + }, + "_layer_name": "class-0", + "_weights_attr_name": "kernel", + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": true, + "narrow_range": false, + "half_range": true, + "per_channel": true + }, + { + "_target_type": { + "_value_": 5, + "_name_": "OPERATION_WITH_WEIGHTS" + }, + "_layer_name": "box-1", + "_weights_attr_name": "kernel", + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": true, + "narrow_range": false, + "half_range": true, + "per_channel": true + }, + { + "_target_type": { + "_value_": 5, + "_name_": "OPERATION_WITH_WEIGHTS" + }, + "_layer_name": "class-1", + "_weights_attr_name": "kernel", + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": true, + "narrow_range": false, + "half_range": true, + "per_channel": true + }, + { + "_target_type": { + "_value_": 5, + "_name_": "OPERATION_WITH_WEIGHTS" + }, + "_layer_name": "box-2", + "_weights_attr_name": "kernel", + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": true, + "narrow_range": false, + "half_range": true, + "per_channel": true + }, + { + "_target_type": { + "_value_": 5, + "_name_": "OPERATION_WITH_WEIGHTS" + }, + "_layer_name": "class-2", + "_weights_attr_name": "kernel", + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": true, + "narrow_range": false, + "half_range": true, + "per_channel": true + }, + { + "_target_type": { + "_value_": 5, + "_name_": "OPERATION_WITH_WEIGHTS" + }, + "_layer_name": "box-3", + "_weights_attr_name": "kernel", + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": true, + "narrow_range": false, + "half_range": true, + "per_channel": true + }, + { + "_target_type": { + "_value_": 5, + "_name_": "OPERATION_WITH_WEIGHTS" + }, + "_layer_name": "class-3", + "_weights_attr_name": "kernel", + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": true, + "narrow_range": false, + "half_range": true, + "per_channel": true + }, + { + "_target_type": { + "_value_": 5, + "_name_": "OPERATION_WITH_WEIGHTS" + }, + "_layer_name": "box-predict", + "_weights_attr_name": "kernel", + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": true, + "narrow_range": false, + "half_range": true, + "per_channel": true + }, + { + "_target_type": { + "_value_": 5, + "_name_": "OPERATION_WITH_WEIGHTS" + }, + "_layer_name": "class-predict", + "_weights_attr_name": "kernel", + "num_bits": 8, + "mode": "symmetric", + "signedness_to_force": true, + "narrow_range": false, + "half_range": true, + "per_channel": true + } +] \ No newline at end of file diff --git a/examples/tensorflow/object_detection/main.py b/examples/tensorflow/object_detection/main.py index 69e6ba4ca50..189a8de6487 100644 --- a/examples/tensorflow/object_detection/main.py +++ b/examples/tensorflow/object_detection/main.py @@ -18,6 +18,8 @@ import tensorflow as tf import numpy as np +from tensorflow.python.framework.convert_to_constants import convert_variables_to_constants_v2 + from nncf.tensorflow import AdaptiveCompressionTrainingLoop from nncf.tensorflow import create_compressed_model from nncf.tensorflow.helpers.model_manager import TFOriginalModelManager @@ -47,6 +49,20 @@ from examples.tensorflow.object_detection.models.model_selector import get_predefined_config from examples.tensorflow.object_detection.models.model_selector import get_model_builder +def keras_model_to_frozen_graph(model): + input_signature = [] + for item in model.inputs: + input_signature.append(tf.TensorSpec(item.shape, item.dtype)) + concrete_function = tf.function(model).get_concrete_function(input_signature) + frozen_func = convert_variables_to_constants_v2(concrete_function, lower_control_flow=False) + return frozen_func.graph.as_graph_def(add_shapes=True) + + +def save_model_as_frozen_graph(model, save_path, as_text=False): + frozen_graph = keras_model_to_frozen_graph(model) + save_dir, name = os.path.split(save_path) + tf.io.write_graph(frozen_graph, save_dir, name, as_text=as_text) + def get_argument_parser(): parser = get_common_argument_parser(precision=False, @@ -308,7 +324,14 @@ def model_eval_fn(model): weights=config.get('weights', None)) as model: with strategy.scope(): config.nncf_config.register_extra_structs([ModelEvaluationArgs(eval_fn=model_eval_fn)]) - compression_ctrl, compress_model = create_compressed_model(model, nncf_config, compression_state) + compression_ctrl, model = create_compressed_model(model, nncf_config, compression_state) + from op_insertion import NNCFWrapperCustom + args = [model] + inputs = tf.keras.layers.Input(shape=model.inputs[0].shape[1:], name=model.inputs[0].name.split(':')[0]) + outputs = NNCFWrapperCustom(*args, caliblration_dataset=train_dataset, + enable_mirrored_vars_split=True)(inputs) + compress_model = tf.keras.Model(inputs=inputs, outputs=outputs) + scheduler = build_scheduler( config=config, steps_per_epoch=steps_per_epoch) @@ -374,9 +397,10 @@ def validate_fn(model, **kwargs): write_metrics(metric_result['AP'], config.metrics_dump) if 'export' in config.mode: - save_path, save_format = get_saving_parameters(config) - compression_ctrl.export_model(save_path, save_format) - logger.info("Saved to {}".format(save_path)) + save_model_as_frozen_graph(compress_model, config.to_frozen_graph) + #save_path, save_format = get_saving_parameters(config) + #compression_ctrl.export_model(save_path, save_format) + #logger.info("Saved to {}".format(save_path)) def export(config): @@ -402,6 +426,7 @@ def export(config): def main(argv): parser = get_argument_parser() config = get_config_from_argv(argv, parser) + #config['eager_mode'] = True print_args(config) serialize_config(config.nncf_config, config.log_dir) diff --git a/examples/tensorflow/object_detection/models/retinanet_model.py b/examples/tensorflow/object_detection/models/retinanet_model.py index 02e7756c159..3a98f620840 100644 --- a/examples/tensorflow/object_detection/models/retinanet_model.py +++ b/examples/tensorflow/object_detection/models/retinanet_model.py @@ -68,9 +68,14 @@ def build_outputs(self, inputs, is_training): return model_outputs + @staticmethod + def get_zero_replica_from_mirrored_var(var): + return var._get_replica(0) + def build_loss_fn(self, keras_model, compression_loss_fn): - filter_fn = self.make_filter_trainable_variables_fn() - trainable_variables = filter_fn(keras_model.trainable_variables) + #filter_fn = self.make_filter_trainable_variables_fn() + #trainable_variables = filter_fn(keras_model.trainable_variables) + trainable_variables = [self.get_zero_replica_from_mirrored_var(v) for v in keras_model.layers[1].trainable_model.mirrored_variables if v.trainable] def _total_loss_fn(labels, outputs): cls_loss = self._cls_loss_fn(outputs['cls_outputs'], diff --git a/nncf/tensorflow/api/compression.py b/nncf/tensorflow/api/compression.py index 7bdce4fe907..da79233ff9e 100644 --- a/nncf/tensorflow/api/compression.py +++ b/nncf/tensorflow/api/compression.py @@ -51,11 +51,11 @@ def apply_to(self, model: ModelType) -> ModelType: :return: The model with additional modifications necessary to enable algorithm-specific compression during fine-tuning. """ - transformation_layout = self.get_transformation_layout(model) - transformer = TFModelTransformer(model) - transformed_model = transformer.transform(transformation_layout) + #transformation_layout = self.get_transformation_layout(model) + #transformer = TFModelTransformer(model) + #transformed_model = transformer.transform(transformation_layout) - if self.should_init: - self.initialize(transformed_model) + #if self.should_init: + # self.initialize(transformed_model) - return transformed_model + return model diff --git a/nncf/tensorflow/helpers/model_creation.py b/nncf/tensorflow/helpers/model_creation.py index fe81624e568..8ffc8d1bfd6 100644 --- a/nncf/tensorflow/helpers/model_creation.py +++ b/nncf/tensorflow/helpers/model_creation.py @@ -74,19 +74,19 @@ def create_compressed_model(model: tf.keras.Model, necessary to enable algorithm-specific compression during fine-tuning. """ model = get_built_model(model, config) - original_model_accuracy = None + #original_model_accuracy = None - if is_accuracy_aware_training(config, compression_config_passed=True): - if config.has_extra_struct(ModelEvaluationArgs): - evaluation_args = config.get_extra_struct(ModelEvaluationArgs) - original_model_accuracy = evaluation_args.eval_fn(model) + #if is_accuracy_aware_training(config, compression_config_passed=True): + # if config.has_extra_struct(ModelEvaluationArgs): + # evaluation_args = config.get_extra_struct(ModelEvaluationArgs) + # original_model_accuracy = evaluation_args.eval_fn(model) builder = create_compression_algorithm_builder(config, should_init=not compression_state) if compression_state: builder.load_state(compression_state[BaseController.BUILDER_STATE]) compressed_model = builder.apply_to(model) compression_ctrl = builder.build_controller(compressed_model) - compressed_model.original_model_accuracy = original_model_accuracy - if isinstance(compressed_model, tf.keras.Model): - compressed_model.accuracy_aware_fit = types.MethodType(accuracy_aware_fit, compressed_model) - return compression_ctrl, compressed_model + #compressed_model.original_model_accuracy = original_model_accuracy + #if isinstance(compressed_model, tf.keras.Model): + # compressed_model.accuracy_aware_fit = types.MethodType(accuracy_aware_fit, compressed_model) + return compression_ctrl, model diff --git a/nncf/tensorflow/quantization/algorithm.py b/nncf/tensorflow/quantization/algorithm.py index 717710c1aee..c86680d9cf9 100644 --- a/nncf/tensorflow/quantization/algorithm.py +++ b/nncf/tensorflow/quantization/algorithm.py @@ -317,25 +317,6 @@ def get_transformation_layout(self, model: tf.keras.Model) -> TFTransformationLa transformations.register(command) return transformations - - def _get_quantizable_weighted_layer_nodes(self, nncf_graph: NNCFGraph) -> List[QuantizableWeightedLayerNode]: - retval = [] - w_qconfig = self._get_default_qconfig(self.global_quantizer_constraints[QuantizerGroup.WEIGHTS]) - for node in nncf_graph.get_all_nodes(): - metatype = node.metatype - if metatype in OUTPUT_NOOP_METATYPES: - continue - - if not (metatype in QUANTIZATION_LAYER_METATYPES - and should_consider_scope(node.node_name, - ignored_scopes=self.ignored_scopes_per_group[QuantizerGroup.WEIGHTS], - target_scopes=None)): - continue - - retval.append(QuantizableWeightedLayerNode(node, - [w_qconfig])) - return retval - def _get_custom_layer_node_names(self, nncf_graph: NNCFGraph, converter: TFModelConverter) -> List[NNCFNodeName]: retval = [] for node in nncf_graph.get_all_nodes(): diff --git a/op_insertion.py b/op_insertion.py new file mode 100644 index 00000000000..2ec6e1c14a7 --- /dev/null +++ b/op_insertion.py @@ -0,0 +1,648 @@ +import tensorflow as tf +import numpy as np +import json + +from typing import List +from itertools import islice +from tensorflow.python.framework import importer +from tensorflow.python.eager import wrap_function +from tensorflow.python.distribute.values import MirroredVariable +from tensorflow.python.distribute.values_util import get_current_replica_id_as_int +from tensorflow.python.ops import variable_scope +from tensorflow.python.util import nest +from itertools import zip_longest + +from contrib import input_to_ops +from examples.tensorflow.classification.test_models import ModelType + + +DUMP_GRAPH = False + + +class InsertionPoint(object): + WEIGHTS = 'w' + AFTER_LAYER = 'after' + BEFORE_LAYER = 'before' + + @staticmethod + def from_str(input_str): + if input_str == "AFTER_LAYER": + return InsertionPoint.AFTER_LAYER + if input_str == "BEFORE_LAYER": + return InsertionPoint.BEFORE_LAYER + if input_str == "OPERATION_WITH_WEIGHTS": + return InsertionPoint.WEIGHTS + + raise RuntimeError('Wrong type of insertion point') + + +class QuantizationSetup(object): + def __init__(self, signed=True, + narrow_range=False, + per_channel=False, + symmetric=True, + init_value=6): + self.signed = signed + self.narrow_range = narrow_range + self.per_channel = per_channel + self.symmetric = symmetric + self.init_value = init_value + + +class NNCFCallableGraph(object): + pass + + +class NNCFWrapperCustom(tf.keras.layers.Wrapper): + def __init__(self, + trainable_model, + eval_model=None, + caliblration_dataset=None, + enable_mirrored_vars_split=True, + **kwargs): + super().__init__(tf.keras.layers.Layer(), **kwargs) + self.model_type = ModelType.FuncModel + self.trainable_model = NNCFCallableGraph() + self.eval_model = NNCFCallableGraph() + self.mirrored_vars_created = False + self.ops_vars_created = False + self.initial_model_weights = None + self.calibration_dataset = caliblration_dataset + self.init_steps = 1 + self.training_forced = None + self.enable_mirrored_vars_split = enable_mirrored_vars_split + if isinstance(trainable_model, dict): + self.model_type = ModelType.KerasLayer + + self.trainable_model.graph_def = trainable_model['graph_def'] + self.trainable_model.concrete = trainable_model['concrete_function'] + self.trainable_model.orig_model = eval_model['layer'] + self.eval_model.graph_def = eval_model['graph_def'] + self.eval_model.concrete = eval_model['concrete_function'] + self.eval_model.orig_model = eval_model['layer'] + else: + self.trainable_model.orig_model = trainable_model + self.eval_model.orig_model = trainable_model + # How to get quantization setup from the NNCF + # examples/tensorflow/object_detection/configs/quantization/retinanet_quantization_layout.json + # res = [] + #for q_point in self._quantizer_setup._quantization_points: + # point_dict = {**q_point.target_point.__dict__, **q_point.quantizer_spec.__dict__} + # point_dict['_target_type'] = point_dict['_target_type'].__dict__ + # if '__objclass__' in point_dict['_target_type']: + # point_dict['_target_type'].pop('__objclass__') + # res.append(point_dict) + def get_functional_retinanet_fq_placing_simular_to_nncf2_0(self, g): + path = 'configs/quantization/retinanet_quantization_layout.json' + with open(path, 'r') as inp: + layout = json.load(inp) + for l in layout: + l.update({'ops': [op for op in g.get_operations() if op.name.startswith(l['_layer_name'] +'/')]}) + + transformations = [] + for op_layout in layout: + layout_name = op_layout['_layer_name'] + setup = QuantizationSetup(signed=op_layout['signedness_to_force'] in (True, None), + narrow_range=op_layout['narrow_range'] or op_layout['half_range'], + per_channel=op_layout['per_channel']) + + insertion_point = InsertionPoint.from_str(op_layout['_target_type']['_name_']) + if layout_name.startswith('input'): + op = [g.get_operations()[0]] + elif layout_name.startswith('batch_normalization') or layout_name.endswith('bn'): + op = [op for op in op_layout['ops'] if op.type == 'FusedBatchNormV3'] + elif layout_name.startswith('l') or layout_name.startswith("post_hoc"): + op_type = 'BiasAdd' if insertion_point == InsertionPoint.AFTER_LAYER else 'Conv2D' + op = [op for op in op_layout['ops'] if op.type == op_type] + elif layout_name.startswith('class') or layout_name.startswith('box'): + # Skip shared conv by now + continue + elif (layout_name.startswith('p') and not layout_name.startswith('post_hoc')) \ + or layout_name.startswith('conv2d'): + op = [op for op in op_layout['ops'] if op.type == 'Conv2D'] + elif layout_name.startswith('up_sampling'): + op = [op for op in op_layout['ops'] if op.type == 'ResizeNearestNeighbor'] + elif any(any(layout_name.split('_')[-i].endswith(x) for i in [1, 2]) for x in ['Relu', 'add']): + op = op_layout['ops'] + if 'Relu' in layout_name: + setup.signed = False + else: + raise RuntimeError(f'You forgot about operation {layout_name}') + + assert len(op) == 1 + transformations.append((op[0], insertion_point, setup)) + + return transformations + + def get_keras_layer_mobilenet_v2_fq_placing_simular_to_nncf2_0(self, g): + """Hardcode fq placing for examples.classification.test_models.get_KerasLayer_model""" + #Get blocks fq + add_ops = [op for op in g.get_operations() if 'addv2' in op.type.lower()] + assert len(add_ops) == 10 + depthwise_conv =\ + [op for op in g.get_operations() if 'expanded' in op.name.lower() and 'conv' in op.type.lower() and 'depthwise' in op.name.lower()] + project_ops =\ + [op for op in g.get_operations() if 'expanded' in op.name.lower() and 'conv' in op.type.lower() and 'project' in op.name.lower()] + expand_ops =\ + [op for op in g.get_operations() if 'expanded' in op.name.lower() and 'conv' in op.type.lower() and 'expand/' in op.name.lower()] + assert len(depthwise_conv) == len(project_ops) == len(expand_ops) + 1 + + depthwise_conv_relu = self.get_left_childs(g, depthwise_conv, 2, 'Relu6') + expand_ops_relu = self.get_left_childs(g, expand_ops, 2, 'Relu6') + project_bn = self.get_left_childs(g, project_ops, 1, 'FusedBatchNormV3') + # First conv + first_conv = [op for op in g.get_operations() if 'MobilenetV2/Conv/Conv2D' in op.name and 'conv' in op.type.lower()][0] + first_conv_relu = self.get_left_childs(g, [first_conv], 2, 'Relu6')[0] + # Tail + last_conv = [op for op in g.get_operations() if 'MobilenetV2/Conv_1/Conv2D' in op.name and 'conv' in op.type.lower()][0] + last_conv_relu = self.get_left_childs(g, [last_conv], 2, 'Relu6')[0] + avg_pool = self.get_left_childs(g, [last_conv], 4, 'AvgPool')[0] + prediction_mul = self.get_left_childs(g, [last_conv], 6, ['Conv2D', 'Mul'])[0] + # + # Create transformation + # + transformations = [] + # Transformations for blocks + transformations.extend([(op, InsertionPoint.WEIGHTS, QuantizationSetup(signed=True, narrow_range=False)) for op in depthwise_conv]) + transformations.extend([(op, InsertionPoint.WEIGHTS, QuantizationSetup(signed=True, narrow_range=False)) for op in project_ops]) + transformations.extend([(op, InsertionPoint.WEIGHTS, QuantizationSetup(signed=True, narrow_range=False)) for op in expand_ops]) + + transformations.extend([(op, InsertionPoint.AFTER_LAYER, QuantizationSetup(signed=False)) for op in depthwise_conv_relu]) + transformations.extend([(op, InsertionPoint.AFTER_LAYER, QuantizationSetup(signed=True)) for op in project_bn]) + transformations.extend([(op, InsertionPoint.AFTER_LAYER, QuantizationSetup(signed=False)) for op in expand_ops_relu]) + transformations.extend([(op, InsertionPoint.AFTER_LAYER, QuantizationSetup(signed=True)) for op in add_ops]) + # Transformations for first conv + # FQ on inputs + transformations.append((first_conv, InsertionPoint.BEFORE_LAYER, QuantizationSetup(signed=True))) + # FQ on first conv weights + transformations.append((first_conv, InsertionPoint.WEIGHTS, QuantizationSetup(signed=True, narrow_range=False))) + # FQ after first conv relu + transformations.append((first_conv_relu, InsertionPoint.AFTER_LAYER, QuantizationSetup(signed=False))) + # Transformation for net tail + transformations.append((last_conv, InsertionPoint.WEIGHTS, QuantizationSetup(signed=True, narrow_range=False))) + transformations.append((last_conv_relu, InsertionPoint.AFTER_LAYER, QuantizationSetup(signed=False))) + transformations.append((avg_pool, InsertionPoint.AFTER_LAYER, QuantizationSetup(signed=False))) + transformations.append((prediction_mul, InsertionPoint.WEIGHTS, QuantizationSetup(signed=True, narrow_range=False))) + assert len(transformations) == 117 + + return transformations + + def build(self, input_shape=None): + for training, model in zip([True, False], [self.trainable_model, self.eval_model]): + if self.model_type != ModelType.KerasLayer: + tf_f = tf.function(lambda x: model.orig_model.call(x, training=training)) + input_signature = [] + for item in model.orig_model.inputs: + input_signature.append(tf.TensorSpec(item.shape, item.dtype)) + + concrete = tf_f.get_concrete_function(input_signature) + structured_outputs = concrete.structured_outputs + sorted_vars = get_sorted_on_captured_vars(concrete) + if isinstance(model.orig_model.variables[0], MirroredVariable): + model.mirrored_variables = model.orig_model.variables + else: + # Case when model build before replica context + model.mirrored_variables = self.create_mirrored_variables(sorted_vars) + + else: + concrete = make_new_func(model.graph_def, + model.concrete.graph.captures, + model.concrete.variables, + model.concrete.inputs, + model.concrete.outputs) + + sorted_vars = get_sorted_on_captured_vars(concrete) + model.mirrored_variables = self.create_mirrored_variables(sorted_vars) + structured_outputs = None + + if not self.initial_model_weights: + self.initial_model_weights = self.get_numpy_weights_list(sorted_vars) + + # Save mapping for concrete per replica inputs + model.bn_weights_names = set(['/'.join(v.name.split('/')[:-1]) for v in concrete.variables if 'replica' in v.name.lower()]) + model.sorted_concrete_vars_names = [v.name for v in sorted_vars] + if model.bn_weights_names: + mirrored_vars_extended = [] + for v_concrete_name in model.sorted_concrete_vars_names: + name, _ = name_without_replica_idx(v_concrete_name) + mirrored_vars_extended.extend([v for v in model.mirrored_variables + if name_without_replica_idx(v.name)[0] == name]) + + model.mirrored_variables = mirrored_vars_extended + + # Add new op to layer + if not self.ops_vars_created: + self.op_vars = [] + enable_quantization = True + if enable_quantization: + new_vars = [] + #transformations = self.get_functional_retinanet_fq_placing_simular_to_nncf2_0(concrete.graph) + transformations = self.get_keras_layer_mobilenet_v2_fq_placing_simular_to_nncf2_0(concrete.graph) + if training: + #pass + self.initialize_trainsformations(concrete, transformations) + + with concrete.graph.as_default() as g: + # Insert given transformations + for op, insertion_point, setup in transformations: + def fq_creation(input_tensor, name): + return create_fq_with_weights(input_tensor=input_tensor, + per_channel=setup.per_channel, + name=name, + signed=setup.signed, + init_value=setup.init_value, + narrow_range=setup.narrow_range) + + if insertion_point == InsertionPoint.AFTER_LAYER: + new_vars.append(insert_op_after(g, op, 0, fq_creation, f'{op.name}_after_layer')) + elif insertion_point == InsertionPoint.BEFORE_LAYER: + new_vars.append(insert_op_before(g, op, 0, fq_creation, f'{op.name}_before_layer')) + elif insertion_point == InsertionPoint.WEIGHTS: + new_vars.append(insert_op_before(g, op, 1, fq_creation, f'{op.name}_weights')) + else: + raise RuntimeError('Wrong insertion point in quantization algo') + + if not self.ops_vars_created: + self.op_vars = new_vars + self.ops_vars_created = True + print(f'{len(transformations)} quantizers were added successfully') + + # Make new concrete to update captured_inputs. + # This is needed for correct export. + + # Update captures + if isinstance(tf.distribute.get_strategy(), tf.distribute.MirroredStrategy): + new_ops_vars = get_zero_replica_from_mirrored_vars(self.op_vars) + else: + new_ops_vars = self.op_vars + old_captures = [(data, placeholder) for data, placeholder in concrete.graph.captures] + new_captures = old_captures[:-len(self.op_vars)] + + for new_var, (_, placeholder) in zip(new_ops_vars, old_captures[-len(self.op_vars):]): + new_captures.append((new_var.handle, placeholder)) + new_variables = [v for v in concrete.variables] + new_ops_vars + if len(new_variables) != len(new_captures): + raise RuntimeError('Len of the new vars should be the same as len' + ' of new captures (possible some compression weights missing)') + + concrete = make_new_func(concrete.graph.as_graph_def(), + new_captures, + new_variables, + concrete.inputs, + concrete.outputs) + + if structured_outputs is not None: + # The order should be the same because + # we use concrete.outputs when building new concrete function + #outputs_list = nest.flatten(structured_outputs, expand_composites=True) + concrete._func_graph.structured_outputs = \ + nest.pack_sequence_as(structured_outputs, concrete.outputs, expand_composites=True) + model.output_tensor = concrete.graph.outputs + model.fn_train = concrete + + if DUMP_GRAPH: + tf.io.write_graph(concrete.graph, '/tmp', 'mobilenetv2_sub_with_conv.pb') + + def call(self, inputs, training=None): + if self.training_forced is not None: + training = self.training_forced + print(f'Force training param to {training}') + else: + print(f'Call graph with given trainable={training}') + + model_obj = self.trainable_model if training else self.eval_model + if not self.enable_mirrored_vars_split: + return model_obj.fn_train(inputs) + + if isinstance(tf.distribute.get_strategy(), tf.distribute.MirroredStrategy) or\ + isinstance(model_obj.mirrored_variables[0], MirroredVariable): + print('in context') + replica_context = tf.distribute.get_replica_context() + if replica_context is not None: + print('sort variables') + # Map correspondent replica of MirroredVariable to replica concrete function + replica_id = get_current_replica_id_as_int() + new_variables = [] + new_captured = [] + for concrete_var_name, var, input_tensor in zip_longest( + model_obj.sorted_concrete_vars_names, + model_obj.mirrored_variables + self.op_vars, + model_obj.fn_train.inputs[1:]): + if concrete_var_name: + # Check if some variables from other replicas are needed for + # concrete function call + name, idx = name_without_replica_idx(concrete_var_name) + if name not in model_obj.bn_weights_names: + idx = replica_id + + new_variables.append(var._get_replica(idx)) + new_captured.append((var._get_replica(idx).handle, input_tensor)) + else: + # On compile time don't change vars + new_variables = model_obj.fn_train.graph.variables + new_captured = model_obj.fn_train.graph.captures + else: # not distributed mode + new_variables = [] + new_captured = [] + for var, input_tensor in zip(model_obj.mirrored_variables + self.op_vars, + model_obj.fn_train.inputs[1:]): + new_variables.append(var) + new_captured.append((var.handle, input_tensor)) + + fn_train = make_new_func(model_obj.fn_train.graph.as_graph_def(), + new_captured, + new_variables, + model_obj.fn_train.inputs, + model_obj.output_tensor) + + if model_obj.fn_train.structured_outputs is not None: + # The order should be the same because + # we use concrete.outputs when building new concrete function + #outputs_list = nest.flatten(structured_outputs, expand_composites=True) + fn_train._func_graph.structured_outputs = \ + nest.pack_sequence_as(model_obj.fn_train.structured_outputs, + fn_train.outputs, + expand_composites=True) + return fn_train(inputs) + + def initialize_trainsformations(self, concrete, trainsformations): + """ + Modify init_valuer from QuantizerSetup + for activation from trainsformations + """ + weights_transformations = [t for t in trainsformations if t[1] == InsertionPoint.WEIGHTS] + + for op, _, setup in weights_transformations: + min_val, max_val = self.get_min_max_op_weights(concrete.graph, op, concrete.inputs, + self.initial_model_weights) + setup.init_value = max(abs(min_val), abs(max_val)) + #setup.narrow_range = True + + if self.calibration_dataset is None: + return + return + outputs = [] + activation_transformations = [t for t in trainsformations if t[1] != InsertionPoint.WEIGHTS] + for op, _, _ in activation_transformations: + outputs.append(op.outputs[0]) + + # Create concrete function with outputs from each activation + init_f = make_new_func(concrete.graph.as_graph_def(), + concrete.graph.captures, + concrete.variables, + concrete.inputs, + outputs) + mins = [[] for _ in outputs] + maxs = [[] for _ in outputs] + for x, _ in islice(self.calibration_dataset, self.init_steps): + outs = init_f(x) + for idx, out in enumerate(outs): + mins[idx].append(tf.reduce_min(out).numpy()) + maxs[idx].append(tf.reduce_max(out).numpy()) + + # Update quantization setup + for i, (_, _, setup) in enumerate(activation_transformations): + setup.init_value = max(abs(np.mean(mins[i])), abs(np.mean(maxs[i]))) + #setup.narrow_range = False + + def get_min_max_op_weights(self, graph, op, placeholders, np_vars): + try: + placeholder = self.get_op_weights_placeholder(graph, op) + except IndexError: + print(f'CANT MAP {op.name}') + return -6, 6 + + placeholders_names = [p.name.split(':')[0] for p in placeholders[1:]] + idx = placeholders_names.index(placeholder.name) + weight = np_vars[idx][0] + print(f'map {op.name} -----> {np_vars[idx][1]}') + return np.min(weight), np.max(weight) + + def get_left_childs(self, graph, ops: List, depth: int, op_type: str = None): + """Get child for each op given by ops list in given depth""" + retval = [] + for op in ops: + i = 0 + child = [op] + while i < depth and len(child): + child = OperationUtils.get_children_ops(graph, child[0]) + i += 1 + + child = child[0] + if op_type is not None: + if isinstance(op_type, list): + assert child.type in op_type + else: + assert child.type == op_type + + retval.append(child) + + return retval + + @staticmethod + def get_op_weights_placeholder(graph, op): + placeholder = op + while placeholder.type != 'Placeholder': + placeholder = OperationUtils.get_parent_ops(graph, placeholder)[-1] + + return placeholder + + def create_mirrored_variables(self, vars): + if not self.mirrored_vars_created: + retval = [] + for var in vars: + mirrored_var = tf.Variable(var.numpy(), + trainable=var.trainable, + dtype=var.dtype, + name=var.name.split(':')[0] + '_mirrored') + retval.append(mirrored_var) + self.mirrored_vars_created = True + self.mirrored_vars_cache = retval + else: + retval = self.mirrored_vars_cache + + return retval + + @staticmethod + def get_numpy_weights_list(vars): + #retval = {} + #for var in vars: + # retval[var.name] = var.numpy() + + #return retval + return [(var.numpy(), var.name) for var in vars] + + +def get_zero_replica_from_mirrored_vars(vars): + return [v._get_replica(0) for v in vars] + + +def name_without_replica_idx(name): + name = name.split(':')[0] + if 'replica' in name: + idx = int(name.split('_')[-1]) + name = '/'.join(name.split('/')[:-1]) + else: + idx = 0 + return name, idx + + +def insert_softmax_in_graph(fn_train): + with fn_train.graph.as_default() as g: + softmax = tf.nn.softmax(g.outputs[0]) + + return make_new_func(g.as_graph_def(), + g.captures, + g.variables, + fn_train.inputs, + [softmax]) + + +# Copyed from:tensorflow.contrib.quantize.python.common.DropStringPrefix tags/v1.15.0 +def RerouteTensor(t0, t1, can_modify=None): + """Reroute the end of the tensor t0 to the ends of the tensor t1. + + Args: + t0: a tf.Tensor. + t1: a tf.Tensor. + can_modify: iterable of operations which can be modified. Any operation + outside within_ops will be left untouched by this function. + + Returns: + The number of individual modifications made by the function. + """ + nb_update_inputs = 0 + consumers = t1.consumers() + if can_modify is not None: + consumers = [c for c in consumers if c in can_modify] + consumers_indices = {} + for c in consumers: + consumers_indices[c] = [i for i, t in enumerate(c.inputs) if t is t1] + for c in consumers: + for i in consumers_indices[c]: + c._update_input(i, t0) # pylint: disable=protected-access + nb_update_inputs += 1 + return nb_update_inputs + + +# Copied from pocketflow:learners.uniform_quantization_tf.utils.insert_quant_op +def insert_op_before(graph, target_op, input_idx, node_creation_fn, name): + """Insert quantization operations before node on input_idx. + + Args: + * graph: TensorFlow graph + * node_name: activation node's name + :return: count of fq inserted into model + """ + target_parent = None + output_idx = None + target_op_parents = OperationUtils.get_parent_ops(graph, target_op) + target_parent_output = target_op.inputs[input_idx] + for op in target_op_parents: + for i, outputs in enumerate(op.outputs): + if outputs.name == target_parent_output.name: + target_parent = op + output_idx = i + + if target_parent is None or output_idx is None: + raise RuntimeError(f'Can\'t find node parent, node name: {target_op.name}') + + # re-route the graph to insert quantization operations + return insert_op_after(graph, target_parent, output_idx, node_creation_fn, name) + + +def insert_op_after(graph, target_parent, output_index, node_creation_fn, name): + input_to_ops_map = input_to_ops.InputToOps(graph) + consumer_ops = input_to_ops_map.ConsumerOperations(target_parent) + insert_op_output_tensor, node_weights = node_creation_fn(target_parent.outputs[output_index], name) + RerouteTensor(insert_op_output_tensor, target_parent.outputs[output_index], consumer_ops) + return node_weights + + +def create_fq_with_weights(input_tensor, per_channel, name, signed, init_value, narrow_range): + """Should be called in graph context""" + with variable_scope.variable_scope('new_node'): + # Should check if variable already exist + # if it exist through error + scale = variable_scope.get_variable( + f'scale_{name}', + shape=(), + dtype=tf.float32, + initializer=tf.keras.initializers.Constant(init_value),#init_ops.constant_initializer(1), + trainable=True) + + min = -scale if signed else 0. + if False:#per_channel: + # Per channel not implemented yet + output_tensor = tf.quantization.fake_quant_with_min_max_vars_per_channel(input_tensor, min, scale, + narrow_range=narrow_range) + else: + output_tensor = tf.quantization.fake_quant_with_min_max_vars(input_tensor, min, scale, + narrow_range=narrow_range) + return output_tensor, scale + + +def get_sorted_on_captured_vars(concrete_fun): + sorted_vars = [] + for value_tensor, graph_name in concrete_fun.graph.captures: + for layer_var in concrete_fun.variables: + if layer_var.handle is value_tensor: + sorted_vars.append(layer_var) + return sorted_vars + + +def make_new_func(output_graph_def, captures, variables, inputs, outputs): + new_input_names = [tensor.name for tensor in inputs] + inputs_map = { + tensor.name: tensor for tensor in inputs + } + new_output_names = [tensor.name for tensor in outputs] + new_func = my_function_from_graph_def(output_graph_def, + new_input_names, + new_output_names, + captures,) + for input in new_func.inputs: + input.set_shape(inputs_map[input.name].shape) + break + + new_func.graph.variables = variables + return new_func + + +def my_function_from_graph_def(graph_def, inputs, outputs, ref_captures): + def _imports_graph_def(): + importer.import_graph_def(graph_def, name="") + + wrapped_import = wrap_function.wrap_function(_imports_graph_def, []) + import_graph = wrapped_import.graph + wrapped_import.graph.reset_captures([(tensor, import_graph.get_tensor_by_name(placeholder.name)) + for tensor, placeholder in ref_captures]) + return wrapped_import.prune( + nest.map_structure(import_graph.as_graph_element, inputs), + nest.map_structure(import_graph.as_graph_element, outputs)) + + +class OperationUtils: + @staticmethod + def get_parent_ops(graph, target_op): + retval = {} + target_op_inputs = [x.name for x in target_op.inputs] + for op in graph.get_operations(): + for idx, i in enumerate(target_op_inputs): + if i in [x.name for x in op.outputs]: + retval[idx] = op + if len(retval) == len(target_op.inputs): + break + return [retval[i] for i in range(len(retval))] + + @staticmethod + def get_children_ops(graph, target_op): + retval = {} + target_op_outputs = [x.name for x in target_op.outputs] + for op in graph.get_operations(): + for idx, out in enumerate(target_op_outputs): + if out in [x.name for x in op.inputs]: + retval[idx] = op + if len(retval) == len(target_op.outputs): + break + return [retval[i] for i in range(len(retval))]