def add_embedding(self, tag, embedding, labels=None, images=None, global_step=None):
        """Adds embedding projector data to the event file. It will also create a config file
        used by the embedding projector in TensorBoard. The folder containing the embedding
        data is named using the formula:
        If global_step is not None, the folder name is `tag + '_' + str(global_step).zfill(6)`;
        else, the folder name is `tag`.
        For example, tag = 'mnist', global_step = 12, the folder's name is 'mnist_000012';
        when global_step = None, the folder's name is 'mnist'.
        See the following reference for the meanings of labels and images.
        Ref: https://www.tensorflow.org/versions/r1.2/get_started/embedding_viz

        Note: This function internally calls `asnumpy()` for MXNet `NDArray` inputs.
        Since `asnumpy()` is a blocking function call, this function would block the main
        thread till it returns. It may consequently affect the performance of async execution
        of the MXNet engine.

        Parameters
        ----------
            tag : str
                Name for the `embedding`.
            embedding : MXNet `NDArray` or  `numpy.ndarray`
                A matrix whose each row is the feature vector of a data point.
            labels : MXNet `NDArray` or `numpy.ndarray` or a list of elements convertible to str.
                Labels corresponding to the data points in the `embedding`. If the labels are 2D
                the first row is considered the column names.
            images : MXNet `NDArray` or `numpy.ndarray`
                Images of format NCHW corresponding to the data points in the `embedding`.
            global_step : int
                Global step value to record. If not set, default to zero.
        """
        embedding_shape = embedding.shape
        if len(embedding_shape) != 2:
            raise ValueError('expected 2D NDArray as embedding data, while received an array with'
                             ' ndim=%d' % len(embedding_shape))
        data_dir = _get_embedding_dir(tag, global_step)
        save_path = os.path.join(self.get_logdir(), data_dir)
        try:
            os.makedirs(save_path)
        except OSError:
            logging.warning('embedding dir %s exists, files under this dir will be overwritten',
                            save_path)
        if labels is not None:
            if (embedding_shape[0] != len(labels) and
                    (not _is_2D_matrix(labels) or len(labels) != embedding_shape[0] + 1)):
                raise ValueError('expected equal values of embedding first dim and length of '
                                 'labels or embedding first dim + 1 for 2d labels '
                                 ', while received %d and %d for each'
                                 % (embedding_shape[0], len(labels)))
            if self._logger is not None:
                self._logger.info('saved embedding labels to %s', save_path)
            _make_metadata_tsv(labels, save_path)
        if images is not None:
            img_labels_shape = images.shape
            if embedding_shape[0] != img_labels_shape[0]:
                raise ValueError('expected equal first dim size of embedding and images,'
                                 ' while received %d and %d for each' % (embedding_shape[0],
                                                                         img_labels_shape[0]))
            if self._logger is not None:
                self._logger.info('saved embedding images to %s', save_path)
            _make_sprite_image(images, save_path)
        if self._logger is not None:
            self._logger.info('saved embedding data to %s', save_path)
        _save_embedding_tsv(embedding, save_path)
        _add_embedding_config(self.get_logdir(), data_dir, labels is not None,
                              images.shape if images is not None else None)