[POT] BC update for transformers (#13869)
* Update node_utils * HBC update * Updated HBC & added test * Apply comments
This commit is contained in:
parent
7ab81d0950
commit
8e9a6d3457
@ -88,7 +88,7 @@ class BiasCorrection(Algorithm):
|
||||
logger.update_progress(self._batch_stat_size)
|
||||
|
||||
bias = nu.get_bias_for_node(node)
|
||||
bias_copy = nu.get_node_input(node_copy_bias_add, 1)
|
||||
bias_copy = nu.get_bias_for_node(node_copy)
|
||||
current_bias_value = nu.get_node_value(bias)
|
||||
|
||||
bias_is_updated = False
|
||||
@ -184,6 +184,7 @@ class BiasCorrection(Algorithm):
|
||||
def walk_to_children(node, is_this_branch_node=False):
|
||||
node_parents = self.get_node_parents(node)
|
||||
node_input_0 = nu.get_node_input(node, 0)
|
||||
node_input_0 = nu.get_node_input(node, 1) if node_input_0.type == 'Const' else node_input_0
|
||||
if is_this_branch_node:
|
||||
# Jump over Split nodes
|
||||
if node_input_0.type in self._split_types:
|
||||
@ -496,4 +497,10 @@ class BiasCorrection(Algorithm):
|
||||
|
||||
@staticmethod
|
||||
def get_node_children(node):
|
||||
return [n for n in nu.get_all_node_outputs(node) if n is not None and nu.get_input_data_value(n, 0) is None]
|
||||
child_nodes = []
|
||||
for output_node in nu.get_all_node_outputs(node):
|
||||
for input_port_id, _ in enumerate(nu.get_node_input_ports(output_node)):
|
||||
if nu.get_input_data_value(output_node, input_port_id) is None \
|
||||
and output_node not in child_nodes:
|
||||
child_nodes.append(output_node)
|
||||
return child_nodes
|
||||
|
@ -111,6 +111,7 @@ class FastBiasCorrection(Algorithm):
|
||||
continue
|
||||
|
||||
if bias_shift_magnitude < self._threshold:
|
||||
logger.debug('Setting bias for %s. Magnitude: %f', op_node.fullname, bias_shift_magnitude)
|
||||
op_node['original_bias'] = current_bias_value
|
||||
nu.set_node_value(bias_node, bias_shift)
|
||||
else:
|
||||
|
@ -140,9 +140,9 @@ def get_bias_for_node(node: Node):
|
||||
if len(node_outputs) == 1:
|
||||
potential_bias = node_outputs[0]
|
||||
if potential_bias.type == 'Add' and len(get_node_inputs(potential_bias)) > 1:
|
||||
potential_bias_const = get_node_input(potential_bias, 1)
|
||||
if potential_bias_const.type == 'Const':
|
||||
return potential_bias_const
|
||||
for potential_bias_const in get_node_inputs(potential_bias):
|
||||
if potential_bias_const.type == 'Const':
|
||||
return potential_bias_const
|
||||
return None
|
||||
|
||||
|
||||
@ -191,7 +191,12 @@ def get_quantized_input_key(quantized_node):
|
||||
If input node of quantized node have one output port -> key is name of fq_input node.
|
||||
Otherwise, key is tuple (fq_input name, output port number)
|
||||
"""
|
||||
quantized_input = get_node_input(quantized_node, 0)
|
||||
if quantized_node.type == 'Add':
|
||||
for quantized_node_input in get_node_inputs(quantized_node):
|
||||
if quantized_node_input.type != 'Const':
|
||||
quantized_input = quantized_node_input
|
||||
else:
|
||||
quantized_input = get_node_input(quantized_node, 0)
|
||||
key = quantized_input.fullname
|
||||
if len(quantized_input.out_ports()) > 1:
|
||||
port_number = quantized_node.in_port(0).get_source().out
|
||||
|
@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:6938940f2c47151dc5dd2d139eda106cce2864700d71dcbae8d058dc38e3cd59
|
||||
size 173
|
@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:1333e866390cb28aa46be9a4a1911fd1e0e06a72ecb17f7d257ca264ca9ff6ab
|
||||
size 297505
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -16,6 +16,7 @@ from openvino.tools.pot.algorithms.quantization.fake_quantize_configuration impo
|
||||
from .utils.config import provide_dataset_path
|
||||
from .utils.path import HARDWARE_CONFIG_PATH, HARDWARE_CONFIG_REFERENCE_PATH, \
|
||||
TOOL_CONFIG_PATH, INTERMEDIATE_CONFIG_PATH
|
||||
from .utils.data_helper import load_json
|
||||
|
||||
|
||||
def check_hardware_config(config, config_name):
|
||||
@ -24,8 +25,7 @@ def check_hardware_config(config, config_name):
|
||||
with open(path_to_ref_json.as_posix(), 'w') as f:
|
||||
json.dump(config, f)
|
||||
|
||||
with open(path_to_ref_json.as_posix(), 'r') as f:
|
||||
ref_config = json.load(f)
|
||||
ref_config = load_json(path_to_ref_json.as_posix())
|
||||
|
||||
assert config == ref_config
|
||||
|
||||
@ -86,8 +86,7 @@ def test_load_tool_config(config_name, tmp_path, models):
|
||||
def test_configurations_by_preset(preset):
|
||||
def _load_config(name):
|
||||
path_to_conf = INTERMEDIATE_CONFIG_PATH.joinpath(name).as_posix()
|
||||
with open(path_to_conf, 'r') as f:
|
||||
return json.load(f)
|
||||
return load_json(path_to_conf)
|
||||
|
||||
config = Dict({
|
||||
'preset': preset,
|
||||
|
106
tools/pot/tests/test_model_params.py
Normal file
106
tools/pot/tests/test_model_params.py
Normal file
@ -0,0 +1,106 @@
|
||||
# Copyright (C) 2020-2022 Intel Corporation
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import os
|
||||
|
||||
import numpy as np
|
||||
import pytest
|
||||
from addict import Dict
|
||||
|
||||
from openvino.tools.pot import create_pipeline, load_model
|
||||
from openvino.tools.pot.engines.simplified_engine import SimplifiedEngine
|
||||
from openvino.tools.pot.graph.model_utils import get_nodes_by_type, get_node_by_name
|
||||
from openvino.tools.pot.graph.node_utils import get_bias_for_node, get_node_value
|
||||
from openvino.tools.pot.graph.special_operations import OPERATIONS_WITH_BIAS
|
||||
|
||||
from .utils.config import merge_configs
|
||||
from .utils.data_helper import dump_intermediate_data, load_json
|
||||
from .test_scales import RandomDataLoader
|
||||
|
||||
|
||||
EPS = 1e-6
|
||||
|
||||
|
||||
TEST_TRANSFORMERS_MODELS = [
|
||||
(
|
||||
'transformer_example',
|
||||
'pytorch',
|
||||
'DefaultQuantization',
|
||||
{
|
||||
'preset': 'performance',
|
||||
'stat_subset_size': 10,
|
||||
'threshold': 1000,
|
||||
'target_device': 'CPU'
|
||||
},
|
||||
'transformer_example_fbc'
|
||||
),
|
||||
(
|
||||
'transformer_example',
|
||||
'pytorch',
|
||||
'DefaultQuantization',
|
||||
{
|
||||
'preset': 'performance',
|
||||
'stat_subset_size': 10,
|
||||
'threshold': 1000,
|
||||
'target_device': 'CPU',
|
||||
'use_fast_bias': False
|
||||
},
|
||||
'transformer_example_hbc'
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
@pytest.fixture(scope='module', params=TEST_TRANSFORMERS_MODELS,
|
||||
ids=['{}_{}_{}'.format(*m) for m in TEST_TRANSFORMERS_MODELS])
|
||||
def _params(request):
|
||||
return request.param
|
||||
|
||||
|
||||
def test_transformer_biases_after_correction(_params, tmp_path, models):
|
||||
model_name, framework, algorithm, algorithm_params, test_name = _params
|
||||
|
||||
references_dir = os.path.join(os.path.dirname(os.path.realpath(__file__)),
|
||||
'./data/reference_biases')
|
||||
reference_path = os.path.join(references_dir, f'{test_name}.json')
|
||||
reference_exists = os.path.isfile(reference_path)
|
||||
local_path = os.path.join(tmp_path, f'{test_name}.json')
|
||||
|
||||
model = models.get(model_name, framework, tmp_path)
|
||||
algorithm_config = Dict({
|
||||
'algorithms': [{
|
||||
'name': algorithm,
|
||||
'params': algorithm_params
|
||||
}]
|
||||
})
|
||||
engine_config = {'device': 'CPU'}
|
||||
config = merge_configs(model.model_params, engine_config, algorithm_config)
|
||||
|
||||
data_loader = RandomDataLoader(shapes={'input': (1, 128)}, seed=0)
|
||||
|
||||
engine = SimplifiedEngine(config.engine, data_loader=data_loader)
|
||||
pipeline = create_pipeline(config.compression.algorithms, engine)
|
||||
model = load_model(config.model)
|
||||
compressed_model = pipeline.run(model)
|
||||
|
||||
values = {}
|
||||
|
||||
nodes_with_bias = get_nodes_by_type(
|
||||
compressed_model, [op['type'] for op in OPERATIONS_WITH_BIAS])
|
||||
for node_with_bias in nodes_with_bias:
|
||||
bias = get_bias_for_node(node_with_bias)
|
||||
if bias is not None:
|
||||
values[node_with_bias.fullname] = get_node_value(bias)
|
||||
|
||||
if not reference_exists:
|
||||
dump_intermediate_data(reference_path, values)
|
||||
return
|
||||
dump_intermediate_data(local_path, values)
|
||||
|
||||
references = load_json(reference_path)
|
||||
|
||||
assert len(references) == len(values)
|
||||
for node_name, ref_val in references.items():
|
||||
node = get_node_by_name(compressed_model, node_name)
|
||||
bias = get_bias_for_node(node)
|
||||
cur_val = get_node_value(bias)
|
||||
np.testing.assert_almost_equal(cur_val, ref_val)
|
@ -4,7 +4,6 @@
|
||||
import json
|
||||
import os
|
||||
|
||||
from copy import deepcopy
|
||||
import numpy as np
|
||||
import pytest
|
||||
from addict import Dict
|
||||
@ -17,22 +16,11 @@ from openvino.tools.pot.graph import load_model
|
||||
from openvino.tools.pot.graph.node_utils import get_node_inputs, get_node_input, get_node_value
|
||||
from openvino.tools.pot.graph import model_utils as mu
|
||||
from openvino.tools.pot.statistics.collector import StatisticsCollector
|
||||
from .utils.data_helper import dump_intermediate_data, load_json
|
||||
|
||||
|
||||
EPS = 1e-6
|
||||
|
||||
class NumpyEncoder(json.JSONEncoder):
|
||||
""" Special json encoder for numpy types """
|
||||
# pylint: disable=W0221, E0202
|
||||
def default(self, o):
|
||||
if isinstance(o, np.integer):
|
||||
return int(o)
|
||||
if isinstance(o, np.floating):
|
||||
return float(o)
|
||||
if isinstance(o, np.ndarray):
|
||||
return o.tolist()
|
||||
return json.JSONEncoder.default(self, o)
|
||||
|
||||
|
||||
def get_fq_nodes_stats_algo(model, preset, bits, is_weights, clipping_value=None):
|
||||
test_dir = os.path.join(os.path.dirname(os.path.realpath(__file__)),
|
||||
@ -89,11 +77,6 @@ def get_fq_nodes_stats_algo(model, preset, bits, is_weights, clipping_value=None
|
||||
return out
|
||||
|
||||
|
||||
def get_ref_stats(stats_path):
|
||||
with open(stats_path) as json_file:
|
||||
return json.load(json_file)
|
||||
|
||||
|
||||
CONFIGURATIONS = [('performance', 8,
|
||||
os.path.join(os.path.dirname(os.path.realpath(__file__)),
|
||||
'./data/reference_scale/mobilenet-v2-pytorch_performance_activations.json'), None),
|
||||
@ -127,11 +110,11 @@ def test_activation_scales(tmp_path, models, preset, bits, stats_path, clipping_
|
||||
|
||||
model = models.get('mobilenet-v2-pytorch', 'pytorch', tmp_path)
|
||||
|
||||
ref_nodes = get_ref_stats(stats_path)
|
||||
ref_nodes = load_json(stats_path)
|
||||
nodes = normalize(get_fq_nodes_stats_algo(model, preset, bits, False,
|
||||
clipping_value=clipping_value))
|
||||
local_path = os.path.join(tmp_path, '{}.json'.format(stats_path.split("_")[-2]))
|
||||
dump_intermediate_scales(local_path, nodes)
|
||||
dump_intermediate_data(local_path, nodes)
|
||||
|
||||
assert len(ref_nodes) == len(nodes)
|
||||
processed_nodes = []
|
||||
@ -154,10 +137,10 @@ def test_weights_scales(tmp_path, models):
|
||||
'./data/reference_scale/mobilenet-v2-pytorch_weights.json')
|
||||
|
||||
model = models.get('mobilenet-v2-pytorch', 'pytorch', tmp_path)
|
||||
ref_weights = get_ref_stats(path_to_weights)
|
||||
ref_weights = load_json(path_to_weights)
|
||||
weights = get_fq_nodes_stats_algo(model, False, 8, True)
|
||||
local_path = os.path.join(tmp_path, '{}.json'.format('mv2_weights'))
|
||||
dump_intermediate_scales(local_path, weights)
|
||||
dump_intermediate_data(local_path, weights)
|
||||
|
||||
for fq_name in weights:
|
||||
item_min, item_max = weights[fq_name]['low_level'], weights[fq_name]['high_level']
|
||||
@ -178,11 +161,6 @@ def test_weights_scales(tmp_path, models):
|
||||
assert assert_flag
|
||||
|
||||
|
||||
def load_refs(path_to_refs):
|
||||
with open(path_to_refs) as json_file:
|
||||
return json.load(json_file)
|
||||
|
||||
|
||||
def make_list(x):
|
||||
if isinstance(x, np.ndarray):
|
||||
x = x.tolist()
|
||||
@ -298,7 +276,7 @@ def test_fake_quantize_configurations(tmp_path, models, model_name, model_framew
|
||||
refs_path = os.path.join(REFERENCES_DIR, '{}_{}.json'.format(model_name, algo_mode))
|
||||
local_path = os.path.join(tmp_path, '{}.json'.format(model_name))
|
||||
ref_exists = os.path.isfile(refs_path)
|
||||
refs = load_refs(refs_path) if ref_exists else {}
|
||||
refs = load_json(refs_path) if ref_exists else {}
|
||||
local_file = open(local_path, 'w')
|
||||
|
||||
if not ref_exists:
|
||||
@ -362,7 +340,7 @@ def test_matmul_scale_unification(tmp_path, models, model_name, model_framework,
|
||||
refs_path = os.path.join(REFERENCES_DIR, f'{model_name}.json')
|
||||
local_path = os.path.join(tmp_path, f'{model_name}.json')
|
||||
ref_exists = os.path.isfile(refs_path)
|
||||
refs = load_refs(refs_path) if ref_exists else {}
|
||||
refs = load_json(refs_path) if ref_exists else {}
|
||||
local_file = open(local_path, 'w')
|
||||
|
||||
if not ref_exists:
|
||||
@ -435,9 +413,3 @@ def _get_tf_accuracy_checker_config(path_to_dataset):
|
||||
}]
|
||||
}
|
||||
]}]})
|
||||
|
||||
|
||||
def dump_intermediate_scales(local_path, data):
|
||||
data = json.dumps(deepcopy(data), cls=NumpyEncoder)
|
||||
local_file = open(local_path, 'w')
|
||||
json.dump(data, local_file)
|
||||
|
@ -16,6 +16,7 @@ from openvino.tools.pot.statistics.collector import StatisticsCollector
|
||||
from openvino.tools.pot.algorithms.quantization.minmax.algorithm import MinMaxQuantization
|
||||
from openvino.tools.pot.algorithms.quantization.bias_correction.algorithm import BiasCorrection
|
||||
from .utils.config import PATHS2DATASETS_CONFIG
|
||||
from .utils.data_helper import load_json
|
||||
|
||||
TEST_MODELS = [('mobilenet-v2-pytorch', 'pytorch'), ('lstm_outs_quantization', 'tf')]
|
||||
|
||||
@ -60,8 +61,7 @@ def test_statistics_collector_subsets(tmp_path, models, model_name, model_framew
|
||||
local_path = os.path.join(tmp_path, '{}_{}.json'.format(model_name, 'statistics_data'))
|
||||
local_file = open(local_path, 'w')
|
||||
|
||||
with open(refs_file.as_posix()) as file:
|
||||
refs = json.load(file)
|
||||
refs = load_json(refs_file.as_posix())
|
||||
|
||||
eps = 1e-6
|
||||
local_out = {}
|
||||
|
@ -2,7 +2,6 @@
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import os
|
||||
import json
|
||||
from unittest.mock import patch
|
||||
import pytest
|
||||
try:
|
||||
@ -22,6 +21,7 @@ from openvino.tools.pot.utils.ac_imports import ConfigReader
|
||||
from openvino.tools.pot.configs.config import Config
|
||||
from .utils.config import provide_dataset_path
|
||||
from .utils.path import TELEMETRY_CONFIG_PATH
|
||||
from .utils.data_helper import load_json
|
||||
|
||||
TEST_MODEL = ('mobilenet-v2-pytorch', 'pytorch')
|
||||
TOOL_CONFIG_NAME = ['mobilenet-v2-pytorch.json', 'mobilenet-v2-pytorch_aa.json', 'mobilenet-v2-pytorch_sparsity.json']
|
||||
@ -44,8 +44,7 @@ class TelemetryTest(Telemetry):
|
||||
)
|
||||
def test_telemetry(config_name, tmp_path, models):
|
||||
telemetry = TelemetryTest()
|
||||
with open(os.path.join(TELEMETRY_CONFIG_PATH, 'expected_values.txt')) as file:
|
||||
expected = json.load(file)
|
||||
expected = load_json(os.path.join(TELEMETRY_CONFIG_PATH, 'expected_values.txt'))
|
||||
|
||||
@patch(func, new=telemetry.send_event)
|
||||
def compress_model():
|
||||
|
@ -15,6 +15,7 @@ from openvino.tools.pot.pipeline.initializer import create_pipeline
|
||||
from openvino.tools.pot.engines.ac_engine import ACEngine
|
||||
from .utils.config import get_engine_config, merge_configs
|
||||
from .utils.path import TEST_ROOT
|
||||
from .utils.data_helper import load_json
|
||||
|
||||
TEST_MODELS = [
|
||||
('mobilenet-v2-pytorch', 'pytorch', 'MinMaxQuantization', 'performance', 'VPU'),
|
||||
@ -69,9 +70,8 @@ def test_unify_scales(_params, tmp_path, models):
|
||||
|
||||
ref_path = REFERENCES_PATH.joinpath(model_name + '_to_unify.json')
|
||||
if ref_path.exists():
|
||||
with open(ref_path.as_posix(), 'r') as f:
|
||||
to_unify_ref = json.load(f)
|
||||
assert to_unify == to_unify_ref
|
||||
to_unify_ref = load_json(ref_path.as_posix())
|
||||
assert to_unify == to_unify_ref
|
||||
else:
|
||||
with open(ref_path.as_posix(), 'w+') as f:
|
||||
json.dump(to_unify, f, indent=4)
|
||||
|
32
tools/pot/tests/utils/data_helper.py
Normal file
32
tools/pot/tests/utils/data_helper.py
Normal file
@ -0,0 +1,32 @@
|
||||
# Copyright (C) 2020-2022 Intel Corporation
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import json
|
||||
from copy import deepcopy
|
||||
|
||||
import numpy as np
|
||||
|
||||
|
||||
class NumpyEncoder(json.JSONEncoder):
|
||||
""" Special json encoder for numpy types """
|
||||
# pylint: disable=W0221, E0202
|
||||
|
||||
def default(self, o):
|
||||
if isinstance(o, np.integer):
|
||||
return int(o)
|
||||
if isinstance(o, np.floating):
|
||||
return float(o)
|
||||
if isinstance(o, np.ndarray):
|
||||
return o.tolist()
|
||||
return json.JSONEncoder.default(self, o)
|
||||
|
||||
|
||||
def dump_intermediate_data(local_path, data):
|
||||
data = json.dumps(deepcopy(data), cls=NumpyEncoder)
|
||||
local_file = open(local_path, 'w')
|
||||
json.dump(data, local_file)
|
||||
|
||||
|
||||
def load_json(stats_path):
|
||||
with open(stats_path) as json_file:
|
||||
return json.load(json_file)
|
Loading…
Reference in New Issue
Block a user