Tensorflow交叉熵计算错误

Posted 小帆的帆

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Tensorflow交叉熵计算错误相关的知识,希望对你有一定的参考价值。

转载请标明出处:小帆的帆的专栏

出现错误的原因

-y * np.log(a) - (1-y) * np.log(1 - a)

当a = y = 0.0, y * np.log(a) = 0 * -inf = nan
当a = y = 1.0, (1 - y) * np.log(1 - a) = 0 * -inf = nan

出现nan的核心原因是log(0.0) = -inf, 所以a的取值才是关键
而通常情况下a趋向0时, y多数就会等于0, 所以0 * -inf = nan

不过a=0, y=1也是有可能出现的, 就是初始化时, a就与y截然相反.之后就不会再出现了, 因为优化总是往好的方向走.

Tensorflow的处理方法

下面的代码中, 用了三种方法解决这个问题, 并对比了速度和精确度

  • 方法1. a加上一个极小值
  • 方法2. 当a接近0时, 给a一个极小值
  • 方法3. 出现nan时, 设置成本为0

结论

方法1速度最快, 并且精确度与另两种方法相当

分析代码

# coding=utf-8
import tensorflow as tf
import time

NEAR_0 = 1e-10
ZERO = tf.constant(0.0)

def cross_entropy(y, a):
    return sess.run(-(y * tf.log(a) + (1 - y) * tf.log(1 - a)))


def method1(y, a):
    return sess.run(-(y * tf.log(a + NEAR_0) + (1 - y) * tf.log(1 - a + NEAR_0)))


def method2(y, a):
    return sess.run(-(y * tf.log(nan_to_num(a)) + (1 - y) * tf.log(nan_to_num(1 - a))))


def method3(y, a):
    return sess.run(nan_to_zero(-(y * tf.log(a) + (1 - y) * tf.log(1 - a))))


def nan_to_num(n):
    return tf.clip_by_value(n, NEAR_0, 1)


def nan_to_zero(c):
    # tensorflow 条件语法 https://www.tensorflow.org/versions/r0.7/api_docs/python/control_flow_ops.html#cond
    # 后面两个参数要是方法
    return tf.cond(tf.is_nan(c), lambda: ZERO, lambda: c)


sess = tf.Session()

# 会出现nan的主要有两处
# 1. y和a都是0
# 2. y和a都是1
# 也就是loss很小的时候
# 而y=1, a=0, 或者a=1, y=1, 只有开刚开始优化的时候可能出现, 后面loss都会往好的方向走.

# log(0.0) = -inf 负无穷
print '交叉熵:'
print cross_entropy(0.0, 0.0)  # y * tf.log(a) : 0.0 * -inf = nan
print cross_entropy(1.0, 1.0)  # (1 - y) * tf.log(1 - a) : 0.0 * -inf = nan

print '\\n方法一:'
# 方法1: 加上一个接近0的数
print method1(0.0, 0.0)
print method1(1.0, 1.0)

print '\\n方法二:'
# 方法2: 当a比1e-10还小时, 等于1e-10
print method2(0.0, 0.0)
print method2(1.0, 1.0)

print '\\n方法三:'
# 方法3: 出现nan, 赋值为0
print method3(0.0, 0.0)
print method3(1.0, 1.0)

# 哪种方法更好
# 1. 速度, 方法1, 胜
#    方法1, 每次都加一个数
#    方法2, 每次都要比较大小
#    方法3, 不好说
#    结论, 加一个数计算成本更低, 所有速度更快
start_time = time.time()
for i in xrange(100):
    method1(0.0, 0.0)
duration = time.time() - start_time
print '\\n方法1用时: 0'.format(duration)

start_time = time.time()
for i in xrange(100):
    method2(0.0, 0.0)
duration = time.time() - start_time
print '\\n方法2用时: 0'.format(duration)

start_time = time.time()
for i in xrange(100):
    method3(0.0, 0.0)
duration = time.time() - start_time
print '\\n方法3用时: 0'.format(duration)

# 2. 精确度
#    方法1, 无论什么数, 都要加个1e-10
#    方法2, 只有小于1e-10时, 才会起作用
#    方法3, 出现nan, 才会起作用
#    总结, 理论上方法二, 三肯定是更精确, 但是实际看不到任何差别
print '\\n'
print '%.100f' % (0.5 + 1e-10)
print '%.100f' % sess.run(tf.clip_by_value(0.5, 1e-10, 1))

print '\\n'
print '%.100f' % method1(1.0, 0.5)
print '%.100f' % method2(1.0, 0.5)
print '%.100f' % method3(1.0, 0.5)

以上是关于Tensorflow交叉熵计算错误的主要内容,如果未能解决你的问题,请参考以下文章

TensorFlow不同交叉熵计算方式

TensorFlow不同交叉熵计算方式

Tensorflow四种交叉熵函数计算公式:tf.nn.cross_entropy

为啥 Keras/tensorflow 的 sigmoid 和交叉熵精度低?

多个正分类的 TensorFlow 损失计算

tensorflow中四种不同交叉熵函数tf.nn.softmax_cross_entropy_with_logits()