Skip to content

Commit

Permalink
Sync for 4.0 release (#961)
Browse files Browse the repository at this point in the history
* sync for 4.0 release

* fix py2 issue in conversion of torch std op
  • Loading branch information
aseemw committed Oct 10, 2020
1 parent fc77bc3 commit 523d5e0
Show file tree
Hide file tree
Showing 34 changed files with 577 additions and 338 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ With coremltools, you can do the following:
To get the latest version of coremltools:

```shell
pip install coremltools==4.0b4
pip install --upgrade coremltools
```

For the latest changes please see the [release notes](https://github.com/apple/coremltools/releases/).
Expand Down
2 changes: 1 addition & 1 deletion coremltools/_deps/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ def __get_sklearn_version(version):
_TF_1_MIN_VERSION = "1.12.0"
_TF_1_MAX_VERSION = "1.15.0"
_TF_2_MIN_VERSION = "2.1.0"
_TF_2_MAX_VERSION = "2.3.0"
_TF_2_MAX_VERSION = "2.3.1"

try:
import tensorflow
Expand Down
241 changes: 127 additions & 114 deletions coremltools/converters/_converters_entry.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import collections

from coremltools.converters.mil.input_types import InputType, ClassifierConfig
from coremltools.converters.mil.converter import _convert
from coremltools.converters.mil.converter import mil_convert
from coremltools.converters.mil.mil import Program
from coremltools._deps import _HAS_TORCH, _HAS_TF_1, _HAS_TF_2
from coremltools.converters._profile_utils import _profile
Expand Down Expand Up @@ -39,6 +39,7 @@ def convert(
outputs=None,
classifier_config=None,
minimum_deployment_target=None,
convert_to='nn_proto',
**kwargs
):
"""
Expand Down Expand Up @@ -68,7 +69,7 @@ def convert(
- Path to a `.pt` file
source: str (optional)
One of `auto`, `tensorflow`, or `pytorch`. `auto` determines the
One of [`auto`, `tensorflow`, `pytorch`, `mil`]. `auto` determines the
framework automatically for most cases. Raise ValueError if it fails
to determine the source framework.
Expand Down Expand Up @@ -108,12 +109,22 @@ def convert(
minimum_deployment_target: coremltools.target enumeration (optional)
- one of the members of enum "coremltools.target."
- When not-specified or None, converter aims for as minimum of a deployment target as possible
- When not-specified or None, converter aims for as minimum of a
deployment target as possible
convert_to: str (optional)
- Must be one of ['nn_proto', 'mil'].
- 'nn_proto': Returns MLModel containing a NeuralNetwork
proto
- 'mil': Returns MIL program object. MIL program is primarily used
for debugging purpose and currently cannot be compiled to
executable.
Returns
-------
model: MLModel
A Core ML MLModel object
model: `coremltools.models.MLModel` or
`coremltools.converters.mil.Program`
A Core ML MLModel object or MIL Program object (see `convert_to`)
Examples
--------
Expand Down Expand Up @@ -157,24 +168,55 @@ def convert(
See `here <https://coremltools.readme.io/docs/neural-network-conversion>`_ for
more advanced options
"""
if minimum_deployment_target is not None and not isinstance(
minimum_deployment_target, AvailableTarget
):
_check_deployment_target(minimum_deployment_target)
exact_source = _determine_source(model, source, outputs)
_validate_inputs(model, exact_source, inputs, outputs, classifier_config,
**kwargs)

mlmodel = mil_convert(
model,
convert_from=exact_source,
convert_to=convert_to,
inputs=inputs,
outputs=outputs,
classifier_config=classifier_config,
**kwargs
)

if convert_to == 'mil':
return mlmodel # Returns the MIL program

if minimum_deployment_target is not None:
check_deployment_compatibility(
spec=mlmodel.get_spec(),
representation=convert_to,
deployment_target=minimum_deployment_target,
)

gc.collect()

mlmodel = _record_src_version(mlmodel, exact_source)
mlmodel.user_defined_metadata[_METADATA_VERSION] = ct_version

return mlmodel


def _check_deployment_target(minimum_deployment_target):
if minimum_deployment_target is not None and \
not isinstance(minimum_deployment_target, AvailableTarget):
msg = (
"Unrecognized value of argument 'minimum_deployment_target': {}. "
"It needs to be a member of 'coremltools.target' enumeration. "
"For example, coremltools.target.iOS13"
)
raise TypeError(msg.format(minimum_deployment_target))

source = source.lower()
if source not in {"auto", "tensorflow", "pytorch"}:
msg = (
'Unrecognized value of argument "source": {}. '
'It must be one of ["auto", "tensorflow", "pytorch"].'
)
raise ValueError(msg.format(source))

def _validate_inputs(model, exact_source, inputs, outputs, classifier_config,
**kwargs):
"""
Validate and process model, inputs, outputs, classifier_config based on
`exact_source` (which cannot be `auto`)
"""
def raise_if_duplicated(input_list):
# Detect duplicated inputs
input_names = [t.name for t in input_list if t.name is not None]
Expand All @@ -196,56 +238,11 @@ def raise_if_duplicated(input_list):
msg = '"classifier_config" must be of type ClassifierConfig'
raise ValueError(msg)

if source == "tensorflow" and _HAS_TF_2:
source = "tensorflow2"

if source == "auto" and _HAS_TF_1:
try:
loader = TF1Loader(model, outputs=outputs)
loader._graph_def_from_model(outputs=outputs)
source = "tensorflow"
except:
pass

if source == "auto" and _HAS_TF_2:
try:
loader = TF2Loader(model, outputs=outputs)
loader._graph_def_from_model(outputs=outputs)
source = "tensorflow2"
except:
pass

if source == "auto" and _HAS_TORCH:
try:
pytorch_load(model)
source = "pytorch"
except:
pass

if source == "auto" and isinstance(model, Program):
source = "mil"

convert_to = kwargs.get("convert_to", "nn_proto")
kwargs.pop("convert_to", None)

if source == "auto":
msg = (
"Unable to determine the type of the model, i.e. the source framework. "
'Please provide the value of argument "source", from one of '
'["tensorflow", "pytorch"]. Note that model conversion requires the '
"source package that generates the model. Please make sure you have "
"the appropriate version of source package installed. E.g., if you're "
"converting model originally trained with TensorFlow 1.14, make sure "
"you have `tensorflow==1.14` installed."
)
raise ValueError(msg)

elif source in {"tensorflow", "tensorflow2"}:

if source == "tensorflow" and not _HAS_TF_1:
raise ValueError(
'Converter was called with source="tensorflow", but missing tensorflow package'
)
if exact_source in {"tensorflow", "tensorflow2"}:
if exact_source == "tensorflow" and not _HAS_TF_1:
msg = 'Converter was called with source="tensorflow", ' +\
'but missing tensorflow package'
raise ValueError(msg)

if inputs is not None:
raise_if_duplicated(inputs)
Expand All @@ -255,17 +252,7 @@ def raise_if_duplicated(input_list):
):
raise ValueError("Input should be a list of TensorType or ImageType")

proto_spec = _convert(
model,
convert_from=source,
convert_to=convert_to,
inputs=inputs,
outputs=outputs,
classifier_config=classifier_config,
**kwargs
)

elif source == "pytorch":
elif exact_source == "pytorch":
if "example_inputs" in kwargs:
msg = 'Unexpected argument "example_inputs" found'
raise ValueError(msg)
Expand Down Expand Up @@ -300,55 +287,81 @@ def _flatten_list(_inputs):
if outputs is not None:
raise ValueError("outputs must not be specified for PyTorch")

proto_spec = _convert(
model,
convert_from="torch",
convert_to=convert_to,
inputs=inputs,
outputs=outputs,
classifier_config=classifier_config,
**kwargs
)

elif source == "mil":
elif exact_source == "mil":
if not isinstance(model, Program):
msg = "Converter was asked to convert MIL input, but input is not a MIL program!"
raise ValueError(msg)

proto_spec = _convert(
model,
convert_from="mil",
convert_to=convert_to,
example_inputs=inputs,
classifier_config=classifier_config,
**kwargs

def _determine_source(model, source, outputs):
"""
Infer source (which can be auto) to the precise framework.
"""
source = source.lower()
if source not in {"auto", "tensorflow", "pytorch", "mil"}:
msg = (
'Unrecognized value of argument "source": {}. '
'It must be one of ["auto", "tensorflow", "pytorch"].'
)
raise ValueError(msg.format(source))

if convert_to == 'mil':
return proto_spec # Returns the MIL program

useCPUOnly = kwargs.get("useCPUOnly", True)
model = coremltools.models.MLModel(proto_spec, useCPUOnly=useCPUOnly)
# Determine tensorflow version
if source == "tensorflow" and _HAS_TF_2:
return "tensorflow2"

if minimum_deployment_target is not None:
check_deployment_compatibility(
spec=proto_spec,
representation=convert_to,
deployment_target=minimum_deployment_target,
)
if source != 'auto':
return source

del proto_spec
gc.collect()
# Determine `auto` source
if source == "auto" and _HAS_TF_1:
try:
loader = TF1Loader(model, outputs=outputs)
loader._graph_def_from_model(outputs=outputs)
return "tensorflow"
except:
pass

if source == "auto" and _HAS_TF_2:
try:
loader = TF2Loader(model, outputs=outputs)
loader._graph_def_from_model(outputs=outputs)
return "tensorflow2"
except:
pass

if source == "auto" and _HAS_TORCH:
try:
pytorch_load(model)
return "pytorch"
except:
pass

if source == "auto" and isinstance(model, Program):
return "mil"

msg = (
"Unable to determine the type of the model, i.e. the source framework. "
'Please provide the value of argument "source", from one of '
'["tensorflow", "pytorch", "mil"]. Note that model conversion requires the '
"source package that generates the model. Please make sure you have "
"the appropriate version of source package installed. E.g., if you're "
"converting model originally trained with TensorFlow 1.14, make sure "
"you have `tensorflow==1.14` installed."
)
raise ValueError(msg)


def _record_src_version(mlmodel, exact_source):
# recording metadata: coremltools version, source framework and version
if source in {"tensorflow", "tensorflow2"} and (_HAS_TF_1 or _HAS_TF_2):
if exact_source in {"tensorflow", "tensorflow2"} and (_HAS_TF_1 or _HAS_TF_2):
src_pkg_version = "tensorflow=={0}".format(tf.__version__)
elif source == "pytorch" and _HAS_TORCH:
elif exact_source == "pytorch" and _HAS_TORCH:
src_pkg_version = "torch=={0}".format(torch.__version__)
elif exact_source == 'mil':
src_pkg_version = "mil"
else:
src_pkg_version = "unknown"

model.user_defined_metadata[_METADATA_VERSION] = ct_version
model.user_defined_metadata[_METADATA_SOURCE] = src_pkg_version
raise ValueError('Unsupported source {}'.format(exact_source))

return model
mlmodel.user_defined_metadata[_METADATA_SOURCE] = src_pkg_version
return mlmodel
4 changes: 2 additions & 2 deletions coremltools/converters/mil/backend/nn/op_mapping.py
Original file line number Diff line number Diff line change
Expand Up @@ -1366,11 +1366,11 @@ def l2_pool(const_context, builder, op):
def linear(const_context, builder, op):
out_channels, in_channels = op.weight.shape
if op.x.rank and op.x.rank <= 3 and op.x.rank > 0:
has_bias = op.bias.val is not None
has_bias = op.bias is not None and op.bias.val is not None
builder.add_inner_product(
name=op.name,
W=op.weight.val,
b=op.bias.val,
b=op.bias.val if has_bias else None,
input_channels=in_channels,
output_channels=out_channels,
has_bias=has_bias,
Expand Down
Loading

0 comments on commit 523d5e0

Please sign in to comment.