Skip to content

Commit 7ee15bf

Browse files
authored
Generalization the weight compression algorithm to PyTorch model. (#2400)
cherry pick to release #2333
1 parent b221938 commit 7ee15bf

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

57 files changed

+1871
-1041
lines changed

docs/api/source/conf.py

+4-2
Original file line numberDiff line numberDiff line change
@@ -137,8 +137,10 @@ def collect_api_entities() -> APIInfo:
137137
"keras",
138138
"tensorflow_addons",
139139
# Need add backend implementation functions to avoid endless loops on registered functions by mock module,
140-
"nncf.experimental.tensor.torch_functions",
141-
"nncf.experimental.tensor.numpy_functions",
140+
"nncf.experimental.tensor.functions.numpy_numeric",
141+
"nncf.experimental.tensor.functions.numpy_linalg",
142+
"nncf.experimental.tensor.functions.torch_numeric",
143+
"nncf.experimental.tensor.functions.torch_linalg",
142144
]
143145

144146
with mock(mock_modules):

nncf/common/graph/layer_attributes.py

+4-2
Original file line numberDiff line numberDiff line change
@@ -271,9 +271,11 @@ class ConvertDtypeLayerAttributes(BaseLayerAttributes):
271271

272272

273273
@dataclass
274-
class ParameterLayerAttributes(BaseLayerAttributes):
274+
class ConstantLayerAttributes(BaseLayerAttributes):
275275
"""
276-
:param name: Parameter name.
276+
:param name: Constant name.
277+
:param shape: Constant shape.
277278
"""
278279

279280
name: str
281+
shape: List[int]

nncf/common/graph/transformations/commands.py

-13
Original file line numberDiff line numberDiff line change
@@ -214,16 +214,3 @@ def __init__(self, command_type: TransformationType, target_point: TargetPoint):
214214
@property
215215
def target_point(self) -> TargetPoint:
216216
return self._target_point
217-
218-
def check_command_compatibility(self, command: "TransformationCommand") -> bool:
219-
return (
220-
isinstance(command, TransformationCommand)
221-
and self.type == command.type
222-
and self.target_point == command.target_point
223-
)
224-
225-
def union(self, other: "TransformationCommand") -> "TransformationCommand":
226-
raise NotImplementedError()
227-
228-
def __add__(self, other: "TransformationCommand") -> "TransformationCommand":
229-
return self.union(other)

nncf/common/scopes.py

+9-9
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
# limitations under the License.
1111

1212
import re
13-
from typing import List, Optional, Union
13+
from typing import List, Optional, Set, Union
1414

1515
from nncf.common.graph import NNCFGraph
1616
from nncf.common.graph import NNCFNode
@@ -21,23 +21,23 @@
2121
from nncf.scopes import convert_ignored_scope_to_list
2222

2323

24-
def matches_any(tested_str: str, str_or_list_to_match_to: Union[List[str], str]) -> bool:
24+
def matches_any(tested_str: str, strs_to_match_to: Union[List[str], Set[str], str]) -> bool:
2525
"""
26-
Return True if tested_str matches at least one element in str_or_list_to_match_to.
26+
Return True if tested_str matches at least one element in strs_to_match_to.
2727
2828
:param tested_str: One of the supported entity types to be matched - currently possible to pass either
2929
NNCFNodeName (to refer to the original model operations) or QuantizerId (to refer to specific quantizers).
30-
:param str_or_list_to_match_to: A list of strings specifying for the serializable_id. Entries of the strings
30+
:param strs_to_match_to: A list or set of strings specifying for the serializable_id. Entries of the strings
3131
may be prefixed with `{re}` to enable regex matching.
3232
3333
:return: A boolean value specifying whether a tested_str should matches at least one element
34-
in str_or_list_to_match_to.
34+
in strs_to_match_to.
3535
"""
3636

37-
if str_or_list_to_match_to is None:
37+
if strs_to_match_to is None:
3838
return False
3939

40-
str_list = [str_or_list_to_match_to] if isinstance(str_or_list_to_match_to, str) else str_or_list_to_match_to
40+
str_list = [strs_to_match_to] if isinstance(strs_to_match_to, str) else strs_to_match_to
4141
for item in str_list:
4242
if "{re}" in item:
4343
regex = item.replace("{re}", "")
@@ -51,15 +51,15 @@ def matches_any(tested_str: str, str_or_list_to_match_to: Union[List[str], str])
5151

5252
def should_consider_scope(
5353
serializable_id: Union[QuantizerId, NNCFNodeName],
54-
ignored_scopes: List[str],
54+
ignored_scopes: Union[List[str], Set[str]],
5555
target_scopes: Optional[List[str]] = None,
5656
) -> bool:
5757
"""
5858
Used when an entity arising during compression has to be compared to an allowlist or a denylist of strings.
5959
6060
:param serializable_id: One of the supported entity types to be matched - currently possible to pass either
6161
NNCFNodeName (to refer to the original model operations) or QuantizerId (to refer to specific quantizers)
62-
:param ignored_scopes: A list of strings specifying a denylist for the serializable_id. Entries of the list
62+
:param ignored_scopes: A list or set of strings specifying a denylist for the serializable_id. Entries of the list
6363
may be prefixed with `{re}` to enable regex matching.
6464
:param target_scopes: A list of strings specifying an allowlist for the serializable_id. Entries of the list
6565
may be prefixed with `{re}` to enable regex matching.

nncf/experimental/common/tensor_statistics/statistical_functions.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
# limitations under the License.
1111

1212
from nncf.experimental.tensor import Tensor
13-
from nncf.experimental.tensor import functions as fns
13+
from nncf.experimental.tensor.functions import numeric as fns
1414

1515

1616
def mean_per_channel(x: Tensor, axis: int) -> Tensor:

nncf/experimental/tensor/README.md

+9-9
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ nncf_tensor.max() # Tensor(2)
5454

5555
### Functions over Tensor
5656

57-
All available functions you can found in [functions.py](functions.py).
57+
All available functions you can found in the functions module.
5858

5959
```python
6060
from nncf.experimental.tensor import functions as fns
@@ -105,7 +105,7 @@ tensor_a[0:2] # Tensor(array([[1],[2]]))
105105
return fns.foo(self, arg1)
106106
```
107107

108-
2. Add function to [function.py](function.py)
108+
2. Add function to functions module
109109

110110
```python
111111
@functools.singledispatch
@@ -133,17 +133,17 @@ tensor_a[0:2] # Tensor(array([[1],[2]]))
133133
raise NotImplementedError(f"Function `foo` is not implemented for {type(x)}")
134134
```
135135

136-
3. Add backend specific implementation of method to:
136+
3. Add backend specific implementation of method to correcponding module:
137137

138-
- [numpy_function.py](numpy_functions.py)
138+
- `functions/numpy_*.py`
139139

140140
```python
141141
@_register_numpy_types(fns.foo)
142142
def _(a: TType, arg1: Type) -> np.ndarray:
143143
return np.foo(a, arg1)
144144
```
145145

146-
- [torch_function.py](torch_functions.py)
146+
- `functions/torch_*.py`
147147

148148
```python
149149
@fns.foo.register(torch.Tensor)
@@ -155,7 +155,7 @@ tensor_a[0:2] # Tensor(array([[1],[2]]))
155155

156156
### Add new backend
157157

158-
1. Add backend specific implementation for all function from [function.py](function.py) in `<NEW_BACKEND>_functions.py` file.
158+
1. Add backend specific implementation for all function from functions module in `functions/<NEW_BACKEND>_*.py` file.
159159

160160
2. Add `test_tensor.py` in backend-specific t directory for tests that inherited from class `TemplateTestNNCFTensorOperators`
161161

@@ -177,8 +177,8 @@ tensor_a[0:2] # Tensor(array([[1],[2]]))
177177
"openvino",
178178
"tensorflow",
179179
"tensorflow_addons",
180-
"nncf.experimental.tensor.torch_functions",
181-
"nncf.experimental.tensor.numpy_functions",
182-
"nncf.experimental.tensor.<NEW_BACKEND>_functions",
180+
"nncf.experimental.tensor.functions.torch_*",
181+
"nncf.experimental.tensor.functions.numpy_*",
182+
"nncf.experimental.tensor.functions.<NEW_BACKEND>_*",
183183
]
184184
```

nncf/experimental/tensor/__init__.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@
99
# See the License for the specific language governing permissions and
1010
# limitations under the License.
1111

12-
from nncf.experimental.tensor.enums import TensorBackendType as TensorBackendType
13-
from nncf.experimental.tensor.enums import TensorDataType as TensorDataType
14-
from nncf.experimental.tensor.enums import TensorDeviceType as TensorDeviceType
12+
from nncf.experimental.tensor.definitions import TensorBackendType as TensorBackendType
13+
from nncf.experimental.tensor.definitions import TensorDataType as TensorDataType
14+
from nncf.experimental.tensor.definitions import TensorDeviceType as TensorDeviceType
1515
from nncf.experimental.tensor.tensor import Tensor as Tensor
1616
from nncf.experimental.tensor.tensor import unwrap_tensor_data as unwrap_tensor_data

nncf/experimental/tensor/enums.py nncf/experimental/tensor/definitions.py

+18
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
# See the License for the specific language governing permissions and
1010
# limitations under the License.
1111

12+
from dataclasses import dataclass
1213
from enum import Enum
1314
from enum import auto
1415

@@ -31,6 +32,8 @@ class TensorDataType(Enum):
3132
float32 = auto()
3233
float64 = auto()
3334
int8 = auto()
35+
int32 = auto()
36+
int64 = auto()
3437
uint8 = auto()
3538

3639

@@ -41,3 +44,18 @@ class TensorDeviceType(Enum):
4144

4245
CPU = auto()
4346
GPU = auto()
47+
48+
49+
@dataclass
50+
class TypeInfo:
51+
"""
52+
The class represents the numerical properties of a floating point types.
53+
54+
:param eps: The smallest representable number such that 1.0 + eps != 1.0.
55+
:param max: The largest representable number.
56+
:param min: The smallest representable number (typically -max).
57+
"""
58+
59+
eps: float
60+
max: float
61+
min: float
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
# Copyright (c) 2024 Intel Corporation
2+
# Licensed under the Apache License, Version 2.0 (the "License");
3+
# you may not use this file except in compliance with the License.
4+
# You may obtain a copy of the License at
5+
# http://www.apache.org/licenses/LICENSE-2.0
6+
# Unless required by applicable law or agreed to in writing, software
7+
# distributed under the License is distributed on an "AS IS" BASIS,
8+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9+
# See the License for the specific language governing permissions and
10+
# limitations under the License.
11+
12+
from nncf.experimental.tensor.functions import linalg as linalg
13+
from nncf.experimental.tensor.functions.numeric import abs as abs
14+
from nncf.experimental.tensor.functions.numeric import all as all
15+
from nncf.experimental.tensor.functions.numeric import allclose as allclose
16+
from nncf.experimental.tensor.functions.numeric import any as any
17+
from nncf.experimental.tensor.functions.numeric import as_tensor_like as as_tensor_like
18+
from nncf.experimental.tensor.functions.numeric import astype as astype
19+
from nncf.experimental.tensor.functions.numeric import clip as clip
20+
from nncf.experimental.tensor.functions.numeric import count_nonzero as count_nonzero
21+
from nncf.experimental.tensor.functions.numeric import device as device
22+
from nncf.experimental.tensor.functions.numeric import dtype as dtype
23+
from nncf.experimental.tensor.functions.numeric import finfo as finfo
24+
from nncf.experimental.tensor.functions.numeric import flatten as flatten
25+
from nncf.experimental.tensor.functions.numeric import isclose as isclose
26+
from nncf.experimental.tensor.functions.numeric import isempty as isempty
27+
from nncf.experimental.tensor.functions.numeric import item as item
28+
from nncf.experimental.tensor.functions.numeric import max as max
29+
from nncf.experimental.tensor.functions.numeric import maximum as maximum
30+
from nncf.experimental.tensor.functions.numeric import mean as mean
31+
from nncf.experimental.tensor.functions.numeric import min as min
32+
from nncf.experimental.tensor.functions.numeric import minimum as minimum
33+
from nncf.experimental.tensor.functions.numeric import moveaxis as moveaxis
34+
from nncf.experimental.tensor.functions.numeric import multiply as multiply
35+
from nncf.experimental.tensor.functions.numeric import ones_like as ones_like
36+
from nncf.experimental.tensor.functions.numeric import reshape as reshape
37+
from nncf.experimental.tensor.functions.numeric import round as round
38+
from nncf.experimental.tensor.functions.numeric import squeeze as squeeze
39+
from nncf.experimental.tensor.functions.numeric import stack as stack
40+
from nncf.experimental.tensor.functions.numeric import sum as sum
41+
from nncf.experimental.tensor.functions.numeric import unstack as unstack
42+
from nncf.experimental.tensor.functions.numeric import var as var
43+
from nncf.experimental.tensor.functions.numeric import where as where
44+
from nncf.experimental.tensor.functions.numeric import zeros_like as zeros_like
45+
46+
47+
def _initialize_backends():
48+
import contextlib
49+
50+
import nncf.experimental.tensor.functions.numpy_linalg
51+
import nncf.experimental.tensor.functions.numpy_numeric
52+
53+
with contextlib.suppress(ImportError):
54+
import nncf.experimental.tensor.functions.torch_linalg
55+
import nncf.experimental.tensor.functions.torch_numeric # noqa: F401
56+
57+
58+
_initialize_backends()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
# Copyright (c) 2024 Intel Corporation
2+
# Licensed under the Apache License, Version 2.0 (the "License");
3+
# you may not use this file except in compliance with the License.
4+
# You may obtain a copy of the License at
5+
# http://www.apache.org/licenses/LICENSE-2.0
6+
# Unless required by applicable law or agreed to in writing, software
7+
# distributed under the License is distributed on an "AS IS" BASIS,
8+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9+
# See the License for the specific language governing permissions and
10+
# limitations under the License.
11+
import functools
12+
from typing import List
13+
14+
import numpy as np
15+
16+
from nncf.experimental.tensor import Tensor
17+
18+
19+
def tensor_guard(func: callable):
20+
"""
21+
A decorator that ensures that the first argument to the decorated function is a Tensor.
22+
"""
23+
24+
@functools.wraps(func)
25+
def wrapper(*args, **kwargs):
26+
if isinstance(args[0], Tensor):
27+
return func(*args, **kwargs)
28+
raise NotImplementedError(f"Function `{func.__name__}` is not implemented for {type(args[0])}")
29+
30+
return wrapper
31+
32+
33+
def dispatch_list(fn: "functools._SingleDispatchCallable", tensor_list: List[Tensor], *args, **kwargs):
34+
"""
35+
Dispatches the function to the type of the wrapped data of the first element in tensor_list.
36+
37+
:param fn: A function wrapped by `functools.singledispatch`.
38+
:param tensor_list: List of Tensors.
39+
:return: The result value of the function call.
40+
"""
41+
unwrapped_list = [i.data for i in tensor_list]
42+
return fn.dispatch(type(unwrapped_list[0]))(unwrapped_list, *args, **kwargs)
43+
44+
45+
def register_numpy_types(singledispatch_fn):
46+
"""
47+
Decorator to register function to singledispatch for numpy classes.
48+
49+
:param singledispatch_fn: singledispatch function.
50+
"""
51+
52+
def inner(func):
53+
singledispatch_fn.register(np.ndarray)(func)
54+
singledispatch_fn.register(np.generic)(func)
55+
return func
56+
57+
return inner
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
# Copyright (c) 2024 Intel Corporation
2+
# Licensed under the Apache License, Version 2.0 (the "License");
3+
# you may not use this file except in compliance with the License.
4+
# You may obtain a copy of the License at
5+
# http://www.apache.org/licenses/LICENSE-2.0
6+
# Unless required by applicable law or agreed to in writing, software
7+
# distributed under the License is distributed on an "AS IS" BASIS,
8+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9+
# See the License for the specific language governing permissions and
10+
# limitations under the License.
11+
12+
import functools
13+
from typing import Optional, Tuple, Union
14+
15+
from nncf.experimental.tensor import Tensor
16+
from nncf.experimental.tensor.functions.dispatcher import tensor_guard
17+
18+
19+
@functools.singledispatch
20+
@tensor_guard
21+
def norm(
22+
a: Tensor,
23+
ord: Optional[Union[str, float, int]] = None,
24+
axis: Optional[Union[int, Tuple[int, ...]]] = None,
25+
keepdims: bool = False,
26+
) -> Tensor:
27+
"""
28+
Computes a vector or matrix norm.
29+
30+
The following norms can be calculated:
31+
32+
===== ============================ ==========================
33+
ord norm for matrices norm for vectors
34+
===== ============================ ==========================
35+
None Frobenius norm 2-norm
36+
'fro' Frobenius norm --
37+
'nuc' nuclear norm --
38+
inf max(sum(abs(x), axis=1)) max(abs(x))
39+
-inf min(sum(abs(x), axis=1)) min(abs(x))
40+
0 -- sum(x != 0)
41+
1 max(sum(abs(x), axis=0)) as below
42+
-1 min(sum(abs(x), axis=0)) as below
43+
2 2-norm (largest sing. value) as below
44+
-2 smallest singular value as below
45+
other -- sum(abs(x)**ord)**(1./ord)
46+
===== ============================ ==========================
47+
48+
The Frobenius norm is given by [1]_:
49+
50+
:math:`||A||_F = [\\sum_{i,j} abs(a_{i,j})^2]^{1/2}`
51+
52+
The nuclear norm is the sum of the singular values.
53+
54+
Both the Frobenius and nuclear norm orders are only defined for
55+
matrices and otherwise raise a ValueError.
56+
57+
:param a: The input tensor.
58+
:param ord: Order of norm. Default: None.
59+
:param axis: Axis over which to compute the vector or matrix norm. Default: None.
60+
:param keepdims: If set to True, the reduced dimensions are retained in the result
61+
as dimensions with size one. Default: False.
62+
:return: Norm of the matrix or vector.
63+
"""
64+
return Tensor(norm(a.data, ord, axis, keepdims))

0 commit comments

Comments
 (0)