Export the model in Core ML format.

        Parameters
        ----------
        filename: str
          A valid filename where the model can be saved.

        Examples
        --------
        >>> model.export_coreml("MyModel.mlmodel")

def export_coreml(self, filename):
        """
        Export the model in Core ML format.

        Parameters
        ----------
        filename: str
          A valid filename where the model can be saved.

        Examples
        --------
        >>> model.export_coreml("MyModel.mlmodel")
        """
        import coremltools as _cmt
        import mxnet as _mx
        from ._mx_model_architecture import _net_params

        prob_name = self.target + 'Probability'
        label_name = self.target

        input_features = [
            ('features', _cmt.models.datatypes.Array(*(1, self.prediction_window, self.num_features)))
        ]
        output_features = [
            (prob_name, _cmt.models.datatypes.Array(*(self.num_classes,)))
        ]

        model_params = self._pred_model.get_params()
        weights = {k: v.asnumpy() for k, v in model_params[0].items()}
        weights = _mx.rnn.LSTMCell(num_hidden=_net_params['lstm_h']).unpack_weights(weights)
        moving_weights = {k: v.asnumpy() for k, v in model_params[1].items()}

        builder = _cmt.models.neural_network.NeuralNetworkBuilder(
            input_features,
            output_features,
            mode='classifier'
        )

        # Conv
        # (1,1,W,C) -> (1,C,1,W)
        builder.add_permute(name='permute_layer', dim=(0, 3, 1, 2),
                            input_name='features', output_name='conv_in')
        W = _np.expand_dims(weights['conv_weight'], axis=0).transpose((2, 3, 1, 0))
        builder.add_convolution(name='conv_layer',
                                kernel_channels=self.num_features,
                                output_channels=_net_params['conv_h'],
                                height=1, width=self.prediction_window,
                                stride_height=1, stride_width=self.prediction_window,
                                border_mode='valid', groups=1,
                                W=W, b=weights['conv_bias'], has_bias=True,
                                input_name='conv_in', output_name='relu0_in')
        builder.add_activation(name='relu_layer0', non_linearity='RELU',
                               input_name='relu0_in', output_name='lstm_in')

        # LSTM
        builder.add_optionals([('lstm_h_in', _net_params['lstm_h']),
                               ('lstm_c_in', _net_params['lstm_h'])],
                              [('lstm_h_out', _net_params['lstm_h']),
                               ('lstm_c_out', _net_params['lstm_h'])])

        W_x = [weights['lstm_i2h_i_weight'], weights['lstm_i2h_f_weight'],
               weights['lstm_i2h_o_weight'], weights['lstm_i2h_c_weight']]
        W_h = [weights['lstm_h2h_i_weight'], weights['lstm_h2h_f_weight'],
               weights['lstm_h2h_o_weight'], weights['lstm_h2h_c_weight']]
        bias = [weights['lstm_h2h_i_bias'], weights['lstm_h2h_f_bias'],
                weights['lstm_h2h_o_bias'], weights['lstm_h2h_c_bias']]

        builder.add_unilstm(name='lstm_layer',
                            W_h=W_h, W_x=W_x, b=bias,
                            input_size=_net_params['conv_h'],
                            hidden_size=_net_params['lstm_h'],
                            input_names=['lstm_in', 'lstm_h_in', 'lstm_c_in'],
                            output_names=['dense0_in', 'lstm_h_out', 'lstm_c_out'],
                            inner_activation='SIGMOID')

        # Dense
        builder.add_inner_product(name='dense_layer',
                                  W=weights['dense0_weight'], b=weights['dense0_bias'],
                                  input_channels=_net_params['lstm_h'],
                                  output_channels=_net_params['dense_h'],
                                  has_bias=True,
                                  input_name='dense0_in',
                                  output_name='bn_in')

        builder.add_batchnorm(name='bn_layer',
                              channels=_net_params['dense_h'],
                              gamma=weights['bn_gamma'], beta=weights['bn_beta'],
                              mean=moving_weights['bn_moving_mean'],
                              variance=moving_weights['bn_moving_var'],
                              input_name='bn_in', output_name='relu1_in',
                              epsilon=0.001)
        builder.add_activation(name='relu_layer1', non_linearity='RELU',
                               input_name='relu1_in', output_name='dense1_in')

        # Softmax
        builder.add_inner_product(name='dense_layer1',
                                  W=weights['dense1_weight'], b=weights['dense1_bias'],
                                  has_bias=True,
                                  input_channels=_net_params['dense_h'],
                                  output_channels=self.num_classes,
                                  input_name='dense1_in', output_name='softmax_in')

        builder.add_softmax(name=prob_name,
                            input_name='softmax_in',
                            output_name=prob_name)


        labels = list(map(str, sorted(self._target_id_map.keys())))
        builder.set_class_labels(labels)
        mlmodel = _cmt.models.MLModel(builder.spec)
        model_type = 'activity classifier'
        mlmodel.short_description = _coreml_utils._mlmodel_short_description(model_type)
        # Add useful information to the mlmodel
        features_str = ', '.join(self.features)
        mlmodel.input_description['features'] = u'Window \xd7 [%s]' % features_str
        mlmodel.input_description['lstm_h_in'] = 'LSTM hidden state input'
        mlmodel.input_description['lstm_c_in'] = 'LSTM cell state input'
        mlmodel.output_description[prob_name] = 'Activity prediction probabilities'
        mlmodel.output_description['classLabel'] = 'Class label of top prediction'
        mlmodel.output_description['lstm_h_out'] = 'LSTM hidden state output'
        mlmodel.output_description['lstm_c_out'] = 'LSTM cell state output'
        _coreml_utils._set_model_metadata(mlmodel, self.__class__.__name__, {
                'prediction_window': str(self.prediction_window),
                'session_id': self.session_id,
                'target': self.target,
                'features': ','.join(self.features),
                'max_iterations': str(self.max_iterations),
            }, version=ActivityClassifier._PYTHON_ACTIVITY_CLASSIFIER_VERSION)
        spec = mlmodel.get_spec()
        _cmt.models.utils.rename_feature(spec, 'classLabel', label_name)
        _cmt.models.utils.rename_feature(spec, 'lstm_h_in', 'hiddenIn')
        _cmt.models.utils.rename_feature(spec, 'lstm_c_in', 'cellIn')
        _cmt.models.utils.rename_feature(spec, 'lstm_h_out', 'hiddenOut')
        _cmt.models.utils.rename_feature(spec, 'lstm_c_out', 'cellOut')
        _cmt.utils.save_spec(spec, filename)