为什么Keras /张量流的S形和交叉熵精度较低?

时间:2018-09-01 07:27:15

标签: python tensorflow keras classification cross-entropy

我有以下简单的神经网络(仅具有1个神经元)来测试Keras的sigmoid激活和binary_crossentropy的计算精度:

model = Sequential()
model.add(Dense(1, input_dim=1, activation='sigmoid'))
model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])

为简化测试,我将唯一的权重手动设置为1,将偏倚设置为0,然后使用2点训练集{(-a, 0), (a, 1)}对模型进行评估,即

y = numpy.array([0, 1])
for a in range(40):
    x = numpy.array([-a, a])
    keras_ce[a] = model.evaluate(x, y)[0] # cross-entropy computed by keras/tensorflow
    my_ce[a] = np.log(1+exp(-a)) # My own computation

我的问题:我发现,当keras_ce约为2时,由Keras / Tensorflow计算出的二进制互熵(1.09e-7)达到了a的下限。如图16所示(蓝线)。随着“ a”的增长,它不会进一步减少。这是为什么?

enter image description here

此神经网络只有1个神经元,其权重设置为1,偏差为0。在2点训练集{(-a, 0), (a, 1)}下,binary_crossentropy就是

-1/2 [log(1-1 /(1 + exp(-a)))+ log(1 /(1 + exp(-a)))] = log(1 + exp(-a))

因此,交叉熵应随着a的增加而减小,如上面的橙色(“我的”)所示。是否可以更改某些Keras / Tensorflow / Python设置以提高其精度?还是我在某个地方弄错了?我将不胜感激任何建议/评论/答案。

2 个答案:

答案 0 :(得分:4)

TL; DR版本:由于计算损失函数时的数值稳定性,裁剪了概率值(即S型函数的输出)。


如果检查源代码,您会发现使用binary_crossentropy作为丢失将导致调用losses.py文件中的binary_crossentropy函数:

def binary_crossentropy(y_true, y_pred):
    return K.mean(K.binary_crossentropy(y_true, y_pred), axis=-1)
如您所见,

依次调用等效的后端函数。如果使用Tensorflow作为后端,则会导致调用tensorflow_backend.py文件中的binary_crossentropy函数:

def binary_crossentropy(target, output, from_logits=False):
    """ Docstring ..."""

    # Note: tf.nn.sigmoid_cross_entropy_with_logits
    # expects logits, Keras expects probabilities.
    if not from_logits:
        # transform back to logits
        _epsilon = _to_tensor(epsilon(), output.dtype.base_dtype)
        output = tf.clip_by_value(output, _epsilon, 1 - _epsilon)
        output = tf.log(output / (1 - output))

    return tf.nn.sigmoid_cross_entropy_with_logits(labels=target,
                                                   logits=output)

您可以看到from_logits参数默认设置为False。因此,if条件的计算结果为true,结果输出中的值将被裁剪为[epsilon, 1-epislon]范围。这就是为什么无论概率大小如何,都不能小于epsilon并且大于1-epsilon。这就解释了binary_crossentropy损失的输出为何也受限制的原因。

现在,这是什么ε?这是一个非常小的常数,可用于数值稳定性(例如,防止被零除或未定义的行为等)。要了解其价值,您可以进一步检查源代码,并在common.py文件中找到它:

_EPSILON = 1e-7

def epsilon():
    """Returns the value of the fuzz factor used in numeric expressions.
    # Returns
        A float.
    # Example
    ```python
        >>> keras.backend.epsilon()
        1e-07
    ```
    """
    return _EPSILON

如果出于任何原因想要更高的精度,则可以从后端使用set_epsilon函数将epsilon值设置为较小的常数:

def set_epsilon(e):
    """Sets the value of the fuzz factor used in numeric expressions.
    # Arguments
        e: float. New value of epsilon.
    # Example
    ```python
        >>> from keras import backend as K
        >>> K.epsilon()
        1e-07
        >>> K.set_epsilon(1e-05)
        >>> K.epsilon()
        1e-05
    ```
    """
    global _EPSILON
    _EPSILON = e

但是,请注意将epsilon设置为极低的正值或零可能会破坏整个Keras的计算稳定性。

答案 1 :(得分:2)

我认为keras考虑到数值稳定性, 让我们跟踪keras的计算方式

首先

def binary_crossentropy(y_true, y_pred):
    return K.mean(K.binary_crossentropy(y_true, y_pred), axis=-1)

然后

def binary_crossentropy(target, output, from_logits=False):
    """Binary crossentropy between an output tensor and a target tensor.

    # Arguments
        target: A tensor with the same shape as `output`.
        output: A tensor.
        from_logits: Whether `output` is expected to be a logits tensor.
            By default, we consider that `output`
            encodes a probability distribution.

    # Returns
        A tensor.
    """
    # Note: tf.nn.sigmoid_cross_entropy_with_logits
    # expects logits, Keras expects probabilities.
    if not from_logits:
        # transform back to logits
        _epsilon = _to_tensor(epsilon(), output.dtype.base_dtype)
        output = tf.clip_by_value(output, _epsilon, 1 - _epsilon)
        output = tf.log(output / (1 - output))


    return tf.nn.sigmoid_cross_entropy_with_logits(labels=target,
                                                   logits=output)

通知tf.clip_by_value用于保持数值稳定性

让我们比较keras binary_crossentropy,tensorflow tf.nn.sigmoid_cross_entropy_with_logits和自定义损失函数(消除谷值裁剪)

import numpy as np
import matplotlib.pyplot as plt
import tensorflow as tf
from keras.models import Sequential
from keras.layers import Dense
import keras

# keras
model = Sequential()
model.add(Dense(units=1, activation='sigmoid', input_shape=(
    1,), weights=[np.ones((1, 1)), np.zeros(1)]))
# print(model.get_weights())
model.compile(loss='binary_crossentropy',
              optimizer='adam', metrics=['accuracy'])

# tensorflow
G = tf.Graph()
with G.as_default():
    x_holder = tf.placeholder(dtype=tf.float32, shape=(2,))
    y_holder = tf.placeholder(dtype=tf.float32, shape=(2,))
    entropy = tf.reduce_mean(tf.nn.sigmoid_cross_entropy_with_logits(
        logits=x_holder, labels=y_holder))
sess = tf.Session(graph=G)


# keras with custom loss function
def customLoss(target, output):
    # if not from_logits:
    #     # transform back to logits
    #     _epsilon = _to_tensor(epsilon(), output.dtype.base_dtype)
    #     output = tf.clip_by_value(output, _epsilon, 1 - _epsilon)
    #     output = tf.log(output / (1 - output))
    output = tf.log(output / (1 - output))
    return tf.nn.sigmoid_cross_entropy_with_logits(labels=target,
                                                   logits=output)
model_m = Sequential()
model_m.add(Dense(units=1, activation='sigmoid', input_shape=(
    1,), weights=[np.ones((1, 1)), np.zeros(1)]))
# print(model.get_weights())
model_m.compile(loss=customLoss,
                optimizer='adam', metrics=['accuracy'])


N = 100
xaxis = np.linspace(10, 20, N)
keras_ce = np.zeros(N)
tf_ce = np.zeros(N)
my_ce = np.zeros(N)
keras_custom = np.zeros(N)

y = np.array([0, 1])
for i, a in enumerate(xaxis):
    x = np.array([-a, a])
    # cross-entropy computed by keras/tensorflow
    keras_ce[i] = model.evaluate(x, y)[0]
    my_ce[i] = np.log(1+np.exp(-a))  # My own computation
    tf_ce[i] = sess.run(entropy, feed_dict={x_holder: x, y_holder: y})
    keras_custom[i] = model_m.evaluate(x, y)[0]
# print(model.get_weights())

plt.plot(xaxis, keras_ce, label='keras')
plt.plot(xaxis, my_ce, 'b',  label='my_ce')
plt.plot(xaxis, tf_ce, 'r:', linewidth=5, label='tensorflow')
plt.plot(xaxis, keras_custom, '--', label='custom loss')
plt.xlabel('a')
plt.ylabel('xentropy')
plt.yscale('log')
plt.legend()
plt.savefig('compare.jpg')
plt.show()

我们可以看到tensorflow与手动计算相同,但是具有自定义损失的keras遇到了预期的数值溢出。 enter image description here