def label_smoothing(labels, epsilon=0.1):
    """
    Implements label smoothing. This prevents the model from becoming
    over-confident about its predictions and thus, less prone to overfitting.
    Label smoothing regularizes the model and makes it more adaptable.
    :param labels: 3D tensor with the last dimension as the number of labels
    :param epsilon: smoothing rate
    :return: smoothed labels
    """
    num_labels = labels.get_shape().as_list()[-1]
    return ((1 - epsilon) * labels) + (epsilon / num_labels)