Skip to content

Commit 5e2460f

Browse files
8.0b2 Release (#2308)
1 parent 8b7048e commit 5e2460f

File tree

431 files changed

+23130
-30075
lines changed

Some content is hidden

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

431 files changed

+23130
-30075
lines changed

NOTICE.txt

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,3 +46,36 @@ distributed under the License is distributed on an "AS IS" BASIS,
4646
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
4747
See the License for the specific language governing permissions and
4848
limitations under the License.
49+
50+
This project contains content in the files coremltools/optimize/torch/quantization/modules/conv_transpose.py and coremltools/optimize/torch/quantization/modules/conv_transpose_fused.py which are adapted from pytorch (https://github.com/pytorch/). The license for these follows:
51+
52+
Copyright (c) 2016 Facebook, Inc (Adam Paszke)
53+
54+
All rights reserved.
55+
56+
Redistribution and use in source and binary forms, with or without
57+
modification, are permitted provided that the following conditions are met:
58+
59+
1. Redistributions of source code must retain the above copyright
60+
notice, this list of conditions and the following disclaimer.
61+
62+
2. Redistributions in binary form must reproduce the above copyright
63+
notice, this list of conditions and the following disclaimer in the
64+
documentation and/or other materials provided with the distribution.
65+
66+
3. Neither the names of Facebook, Deepmind Technologies, NYU, NEC Laboratories America
67+
and IDIAP Research Institute nor the names of its contributors may be
68+
used to endorse or promote products derived from this software without
69+
specific prior written permission.
70+
71+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
72+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
73+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
74+
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
75+
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
76+
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
77+
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
78+
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
79+
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
80+
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
81+
POSSIBILITY OF SUCH DAMAGE.

README.md

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,9 @@ With coremltools, you can:
2626
After conversion, you can integrate the Core ML models with your app using Xcode.
2727

2828
## Install 8.0 Beta
29-
The [coremltools version 8 beta 1](https://github.com/apple/coremltools/releases/tag/8.0b1) is now out. To install, run the following command in your terminal:
29+
The [coremltools version 8 beta 2](https://github.com/apple/coremltools/releases/tag/8.0b2) is now out. To install, run the following command in your terminal:
3030
```shell
31-
pip install coremltools==8.0b1
31+
pip install coremltools==8.0b2
3232
```
3333

3434

@@ -46,11 +46,9 @@ pip install -U coremltools
4646

4747
To install coremltools, see [Installing Core ML Tools](https://apple.github.io/coremltools/docs-guides/source/installing-coremltools.html). For more information, see the following:
4848

49-
* [Release Notes](https://github.com/apple/coremltools/releases/)
50-
* [Guide and examples](https://apple.github.io/coremltools/docs-guides/index.html)
49+
* [Release Notes](https://github.com/apple/coremltools/releases/)
50+
* [Guide and examples](https://apple.github.io/coremltools/docs-guides/index.html)
5151
* [API Reference](https://apple.github.io/coremltools/index.html)
5252
* [Core ML Specification](https://apple.github.io/coremltools/mlmodel/index.html)
5353
* [Building from Source](BUILDING.md)
54-
* [Contribution Guidelines](CONTRIBUTING.md)
55-
56-
54+
* [Contribution Guidelines](CONTRIBUTING.md)

coremltools/_deps/__init__.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ def _warn_if_above_max_supported_version(package_name, package_version, max_supp
5454
_HAS_SKLEARN = True
5555
_SKLEARN_VERSION = None
5656
_SKLEARN_MIN_VERSION = "0.17"
57-
_SKLEARN_MAX_VERSION = "1.1.2"
57+
_SKLEARN_MAX_VERSION = "1.5.1"
5858

5959

6060
def __get_sklearn_version(version):
@@ -197,6 +197,14 @@ def __get_sklearn_version(version):
197197
else:
198198
_HAS_SCIPY = True
199199

200+
# ---------------------------------------------------------------------------------------
201+
try:
202+
import transformers
203+
except:
204+
_HAS_HF = False
205+
else:
206+
_HAS_HF = True
207+
200208
# General utils
201209
def version_ge(module, target_version):
202210
"""

coremltools/converters/mil/backend/mil/load.py

Lines changed: 34 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import os
77
import warnings
88
from collections import OrderedDict
9-
from typing import Any, Dict, List, Optional, Tuple
9+
from typing import Any, Dict, List, Optional, Tuple, Union
1010

1111
import numpy as np
1212

@@ -62,12 +62,19 @@
6262
BlobWriter = None
6363

6464

65-
def should_use_weight_file(val):
65+
def should_use_weight_file(
66+
val: Union[np.ndarray, np.generic],
67+
specification_version: Optional[int] = _SPECIFICATION_VERSION_IOS_15,
68+
) -> bool:
69+
# additional dtype are supported >= iOS18
70+
supported_dtypes = ["float16", "float32", "uint8", "int8"]
71+
if specification_version >= _SPECIFICATION_VERSION_IOS_18:
72+
supported_dtypes += ["uint16", "int16", "int32", "uint32"]
6673
return (
6774
val is not None
6875
and isinstance(val, (np.ndarray, np.generic))
6976
and val.size >= 10
70-
and val.dtype in ['float16', 'float32', 'uint8', 'int8']
77+
and val.dtype in supported_dtypes
7178
)
7279

7380

@@ -80,13 +87,26 @@ def __init__(
8087
self,
8188
prog: Program,
8289
weights_dir: str,
90+
specification_version: int,
8391
):
8492
self.prog = prog
8593
self.weights_dir = weights_dir
94+
self.specification_version = specification_version
8695
self.blob_writers = {}
8796
self.weight_id_to_file_value = {} # mapping from weight_id to file value
8897
self.prog.validate(check_essential_scope=True)
8998

99+
@staticmethod
100+
def _get_valid_kwargs(kwargs: Dict[str, Any]) -> Dict[str, Any]:
101+
"""
102+
Get a valid kwargs to initialize a MILProtoExporter object.
103+
"""
104+
return {
105+
"prog": kwargs["prog"],
106+
"weights_dir": kwargs["weights_dir"],
107+
"specification_version": kwargs["specification_version"],
108+
}
109+
90110
def translate_program_attributes(self) -> Dict[str, Any]:
91111
"""
92112
Get the program attributes which need to be exported to mil proto.
@@ -157,7 +177,7 @@ def get_milproto_value(self, var: Var) -> proto.MIL_pb2.Value:
157177
"""
158178
Translate a pymil Var into milproto value.
159179
"""
160-
if should_use_weight_file(var.val):
180+
if should_use_weight_file(var.val, self.specification_version):
161181
return self.create_file_value(var)
162182
else:
163183
return create_immediate_value(var)
@@ -475,9 +495,7 @@ def convert_function(self, function: Function, opset: str) -> proto.MIL_pb2.Func
475495
inputs=inputs, opset=opset, block_specializations={opset: block}
476496
)
477497

478-
def export(
479-
self, specification_version: Optional[str] = _SPECIFICATION_VERSION_IOS_15
480-
) -> proto.MIL_pb2.Program:
498+
def export(self) -> proto.MIL_pb2.Program:
481499
"""
482500
Export a pymil program into mil proto with the given specification version.
483501
"""
@@ -486,7 +504,9 @@ def export(
486504

487505
function_protos = {}
488506
for func_name, func in self.prog.functions.items():
489-
function_protos[func_name] = self.convert_function(func, _OPSET[specification_version])
507+
function_protos[func_name] = self.convert_function(
508+
func, _OPSET[self.specification_version]
509+
)
490510

491511
kwargs = {
492512
"version": 1,
@@ -1040,11 +1060,14 @@ def load(
10401060
)
10411061

10421062
# convert pymil program into mil proto
1063+
kwargs["prog"] = prog
1064+
kwargs["weights_dir"] = weights_dir
1065+
kwargs["specification_version"] = specification_version
1066+
exporter_kwargs = MILProtoExporter._get_valid_kwargs(kwargs)
10431067
mil_proto_exporter = MILProtoExporter(
1044-
prog,
1045-
weights_dir,
1068+
**exporter_kwargs,
10461069
)
1047-
mil_proto = mil_proto_exporter.export(specification_version)
1070+
mil_proto = mil_proto_exporter.export()
10481071

10491072
# return the model provided by users
10501073
desc = kwargs.get("model_description", None)

coremltools/converters/mil/backend/mil/test_load.py

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@
33
# Use of this source code is governed by a BSD-3-clause license that can be
44
# found in the LICENSE.txt file or at https://opensource.org/licenses/BSD-3-Clause
55

6+
import itertools
67
import math
8+
import os
79
import platform
810
import shutil
911
import tempfile
@@ -22,9 +24,68 @@
2224
TestConstexprLut as _TestConstexprLut,
2325
)
2426
from coremltools.converters.mil.mil.program import Symbol
27+
from coremltools.converters.mil.mil.types.type_mapping import string_to_nptype
2528
from coremltools.models.utils import _macos_version
2629

2730

31+
class TestWeightFileSerialization:
32+
@staticmethod
33+
@pytest.mark.parametrize(
34+
"dtype, opset_version",
35+
itertools.product(
36+
["fp16", "fp32", "uint8", "int8", "uint16", "int16", "int32", "uint32"],
37+
[ct.target.iOS16, ct.target.iOS18],
38+
),
39+
)
40+
def test_weight_serialization(dtype, opset_version):
41+
if dtype == "uint32":
42+
# There is a pass that casts the output to CoreML supported dtype.
43+
# uint32 will fail because `cast` op doesn't accept such input type.
44+
pytest.skip("uint32 is not supported in `cast` op.")
45+
if dtype in ["uint8", "int8", "uint16", "int16"] and opset_version == ct.target.iOS16:
46+
# iOS16 doesn't support the above dtype either
47+
pytest.skip("dtype not support in iOS16")
48+
49+
if dtype in ["fp16", "fp32", "uint8", "int8"]:
50+
should_serialize_weight = True
51+
else:
52+
should_serialize_weight = opset_version >= ct.target.iOS18
53+
54+
@mb.program(input_specs=[mb.TensorSpec((1,))], opset_version=opset_version)
55+
def prog(x):
56+
val = np.random.rand(1000).astype(string_to_nptype(dtype))
57+
return mb.const(val=val), mb.add(x=x, y=1.0)
58+
59+
# we don't want the const to be constant folding after casting
60+
pipeline = ct.PassPipeline()
61+
pipeline.set_options("common::const_elimination", {"skip_const_by_size": "-1"})
62+
mlmodel = ct.convert(
63+
prog,
64+
minimum_deployment_target=opset_version,
65+
pass_pipeline=pipeline,
66+
)
67+
saved_package_path = tempfile.mkdtemp(suffix=".mlpackage")
68+
mlmodel.save(saved_package_path)
69+
70+
# check the weights are serialized as file value
71+
if ct.utils._macos_version() >= (15, 0):
72+
with tempfile.TemporaryDirectory() as serialize_dir:
73+
os.system(f"coremlcompiler compile {saved_package_path} {serialize_dir}")
74+
model_name_with_extension = os.path.basename(saved_package_path)
75+
model_name_wo_extension, _ = os.path.splitext(model_name_with_extension)
76+
mil_file = open(
77+
os.path.join(serialize_dir, f"{model_name_wo_extension}.mlmodelc", "model.mil")
78+
)
79+
mil_txt = mil_file.read()
80+
if should_serialize_weight:
81+
assert f"tensor<{dtype}, [1000]>(BLOBFILE" in mil_txt
82+
else:
83+
assert f"tensor<{dtype}, [1000]>(BLOBFILE" not in mil_txt
84+
85+
# cleanup
86+
shutil.rmtree(saved_package_path)
87+
88+
2889
class TestMILFlexibleShapes:
2990
@mb.program(input_specs=[mb.TensorSpec(shape=[1, 3, Symbol("H"), Symbol("W")])])
3091
def basic_network(x):
@@ -991,9 +1052,11 @@ def prog(state, x, y):
9911052
prog,
9921053
convert_to="mlprogram",
9931054
minimum_deployment_target=ct.target.iOS18,
1055+
skip_model_load=True,
9941056
)
9951057

9961058
mil = mlmodel.get_spec().mlProgram
1059+
9971060
for function in mil.functions.values():
9981061
for block in function.block_specializations.values():
9991062
ops = list(block.operations)
@@ -1005,6 +1068,50 @@ def prog(state, x, y):
10051068
]
10061069
assert [val.type for val in ops] == expected_ops
10071070

1071+
@staticmethod
1072+
def test_coreml_update_state_lowering_with_prefer_state_in_downstream():
1073+
@mb.program(
1074+
input_specs=[
1075+
mb.StateTensorSpec((1,), dtype=types.fp16),
1076+
mb.TensorSpec((1,), dtype=types.fp16),
1077+
mb.TensorSpec((1,), dtype=types.fp16),
1078+
mb.TensorSpec((1,), dtype=types.fp16),
1079+
],
1080+
opset_version=ct.target.iOS18,
1081+
)
1082+
def prog(state, x, y, z):
1083+
# Although seemingly not used, graph pass prefer_state_in_downstream will
1084+
# make its output as identiy.x
1085+
mb.coreml_update_state(state=state, value=x)
1086+
# If value only feeds into coreml_update_state,
1087+
# the prefer_state_in_downstream has no affects
1088+
mb.coreml_update_state(state=state, value=y)
1089+
# This is the one that really is not used
1090+
mb.coreml_update_state(state=state, value=z)
1091+
return mb.identity(x=x), mb.coreml_update_state(state=state, value=y)
1092+
1093+
mlmodel = ct.convert(
1094+
prog,
1095+
convert_to="mlprogram",
1096+
minimum_deployment_target=ct.target.iOS18,
1097+
skip_model_load=True,
1098+
)
1099+
1100+
mil = mlmodel.get_spec().mlProgram
1101+
for function in mil.functions.values():
1102+
for block in function.block_specializations.values():
1103+
ops = list(block.operations)
1104+
expected_ops = [
1105+
"write_state",
1106+
"read_state",
1107+
"write_state",
1108+
"write_state",
1109+
"identity",
1110+
"write_state",
1111+
"read_state",
1112+
]
1113+
assert [val.type for val in ops] == expected_ops
1114+
10081115
@staticmethod
10091116
@pytest.mark.skipif(ct.utils._macos_version() < (15, 0),
10101117
reason="State only supported on macOS 15+")

coremltools/converters/mil/converter.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -278,7 +278,7 @@ def mil_convert_to_proto(
278278
# If the client calls `mil_convert` directly, the `pass_pipeline` is None. To keep the
279279
# behaviour same as before, the quantization pass is removed in this situation.
280280
# TODO: rdar://106111553 ([Infra] Quantization Pass is skipped when `mil_convert` is called directly.)
281-
main_pipeline = PassPipeline()
281+
main_pipeline = kwargs.get("pass_pipeline", PassPipeline())
282282
main_pipeline.remove_passes({"common::add_fp16_cast", "common::add_int16_cast"})
283283
frontend_pipeline, backend_pipeline = _construct_other_pipelines(
284284
main_pipeline, convert_from, convert_to

coremltools/converters/mil/experimental/passes/generic_conv_scale_fusion.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ def var_constraints(pattern):
115115
if not is_scalar:
116116
conv_weight = pattern.conv.weight.val
117117
passed = passed and (
118-
np.product(scale.shape) == Cout
118+
np.prod(scale.shape) == Cout
119119
or (len(scale.shape) == len(conv_weight.shape) and scale.shape[1] == Cout)
120120
or (len(scale.shape) == len(conv_weight.shape) - 1 and scale.shape[0] == Cout)
121121
)

0 commit comments

Comments
 (0)