def compare_shapes(mlmodel, input_key_values, expected_outputs, pred=None):
    """
    Inputs:
        - mlmodel: MLModel.

        - input_key_values: str -> np.array or PIL.Image. Keys must match those in
          input_placeholders.

        - expected_outputs: dict[str, np.array].

        - pred: Prediction to use, if it has already been computed.
    """
    if _IS_MACOS:
        if not pred:
            pred = run_core_ml_predict(mlmodel, input_key_values)
        for o, expected in expected_outputs.items():
            coreml_out = _get_coreml_out_from_dict(pred, o)

            # output is dictionary (for classifier)
            if isinstance(coreml_out, dict) and isinstance(expected, dict):
                assert len(coreml_out) == len(expected)
                continue

            # output is numpy objects
            np_types = (np.generic, np.ndarray)
            if isinstance(coreml_out, np_types) and isinstance(expected, np_types):
                msg = "Output: {}. expected shape {} != actual shape {}".format(
                    o, expected.shape, coreml_out.shape
                )
                # Core ML does not support scalar as output
                # remove this special case when support is added
                if expected.shape == () and coreml_out.shape == (1,):
                    continue
                assert coreml_out.shape == expected.shape, msg

                # Validate the shape consistency across runtime returned values and
                # the output information in the mlprogram proto.
                spec = mlmodel.get_spec()
                if spec.WhichOneof("Type") == "mlProgram":

                    if mlmodel._is_multifunction():
                        desc = mlmodel._get_function_description(mlmodel.function_name)
                    else:
                        desc = spec.description

                    # The proto output and the runtime outputs are different for classifier
                    if desc.predictedFeatureName != "":
                        continue

                    proto_shape = _get_proto_output_shape(desc, o)

                    if proto_shape != []:
                        assert proto_shape == list(
                            coreml_out.shape
                        ), f"the output shape, for output named {o}, returned by the model is {coreml_out.shape} which does match with the shape present in the proto spec, which is {proto_shape}"
                continue

            # output is other types (for classifier)
            assert type(coreml_out) == type(expected)

    Inputs:
        - mlmodel: MLModel.

        - input_key_values: str -> np.array or PIL.Image. Keys must match those in
          input_placeholders.

        - expected_outputs: dict[str, np.array].

        - pred: Prediction to use, if it has already been computed.
    
coremltools/converters/mil/testing_utils.py