python - 为什么 Keras/tensorflow 的 sigmoid 和交叉熵精度低?
问题描述
我有以下简单的神经网络(只有 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/Tensorflow 计算的二元交叉keras_ce
熵(16,如下图所示(蓝线)。随着'a'不断增长,它不会进一步减少。这是为什么? 1.09e-7
a
该神经网络只有 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
增加而减少,如上面的橙色('my')所示。我可以更改一些 Keras/Tensorflow/Python 设置以提高其精度吗?还是我在某个地方弄错了?我很感激任何建议/评论/答案。
解决方案
TL;DR 版本:在计算损失函数时,由于数值稳定性,概率值(即 sigmoid 函数的输出)被裁剪。
如果您检查源代码,您会发现使用binary_crossentropy
as 损失会导致调用loss.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,因此输出中的值被裁剪到 range [epsilon, 1-epislon]
。这就是为什么无论概率多么小或大,它都不能小于epsilon
和大于1-epsilon
。这就解释了为什么binary_crossentropy
损失的输出也是有界的。
现在,这里的 epsilon 是什么?这是一个非常小的常数,用于数值稳定性(例如,防止除以零或未定义的行为等)。要找出它的值,您可以进一步检查源代码,您会在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 计算的稳定性。
推荐阅读
- mysql - mysql需要对一列进行完整计数并按某些列分组
- c# - Autofac 没有从 Multi Target .Net 标准库中加载模块
- pixi.js - 使用法线贴图旋转对象而不破坏照明的策略
- python - 使用正则表达式提取文件名
- android - 如何在Android中只查看两个TextView
- python - 如何在 SQLAlchemy ORM 中同时过滤多列中的位置
- css - 删除行之间的空格
- django - 我如何将参数传递给 celery 任务?
- ios - collectionView 在标题中隐藏我的自定义视图
- java - 覆盖第 3 方库类的 XmlAdapter