语音识别学习日志 2018-7-25 [softmax溢出的解决(softmax结果张量中的元素大部分是0或1)]

昨天做的MLP,网络中每一层都没用任何激活函数,所以输出层的结果_logits中的部分数值比较大(绝对值在3000左右)。最后使用softmax函数对输出层进行处理得到各个结果的概率,发现训练的结果很不理想,准确率在50%以下。最后发现softmax的结果大部分是[0,0,0,0,0,1,0,0,0,0]这种单位向量。最后又看了看softmax,softmax函数的定义如下:

                                                   \small f{(X)} = \frac{{{e^{{x_i}}}}}{{\sum\limits_{j = 1}^n {{e^{{x_j}}}} }},j = 1,2,...,n. \: \: \: \: X=\{ x_1,x_2,...,x_n\}

盗一张描述softmax的图:

softmax函数容易出现的问题就是溢出:

  • 指数\small x_i极其大,导致分子计算 \small e^{x_i} 时上溢出
  • \small x_j为负数,且 \small |x_j|很大,有可能四舍五入为0,导致下溢出

解决以上问题的方法也很简单,令\small m = \max ({x_i}),i = 1,2, \cdots ,n,即\small m为所有\small x_i中最大的值,令\small M=\{ m,m,...,m\},size(M)=n,那么我们只需要把计算\small f(X)的值,改为计算  \small f(X-M) 的值,就可以解决上溢出、下溢出的问题了,并且,计算结果理论上仍然和\small f(X)保持一致。公式的推导也非常简单:

               \small \displaystyle \displaystyle \frac{{{e^{{z_2}}}}}{{{e^{{z_1}}} + {e^{{z_2}}} + {e^{{z_3}}}}} = \frac{{\frac{{{e^{{z_2}}}}}{{{e^M}}}}}{{\frac{{{e^{{z_1}}} + {e^{{z_2}}} + {e^{{z_3}}}}}{{{e^M}}}}} = \frac{{\frac{{{e^{{z_2}}}}}{{{e^M}}}}}{{\frac{{{e^{{z_1}}}}}{{{e^M}}} + \frac{{{e^{{z_2}}}}}{{{e^M}}} + \frac{{{e^{{z_3}}}}}{{{e^M}}}}} = \frac{{{e^{\left( {{z_2} - M} \right)}}}}{{{e^{\left( {{z_1} - M} \right)}} + {e^{\left( {{z_2} - M} \right)}} + {e^{\left( {{z_3} - M} \right)}}}}

此外,在实际使用中(比如计算交叉熵时)经常要对softmax的结果取log,而如果softmax的结果中包含0,计算时就会出现inf(nan)的结果。这种问题的解决方法也很简单,直接看推导:

\small \log [f({X})] = \log \left( {\frac{{{e^{{x_i}}}}}{{{e^{{x_1}}} + {e^{{x_2}}} + \cdots {e^{{x_n}}}}}} \right) = \log \left( {\frac{{\frac{{{e^{{x_i}}}}}{{{e^m}}}}}{{\frac{{{e^{{x_1}}}}}{{{e^m}}} + \frac{{{e^{{x_2}}}}}{{{e^m}}} + \cdots \frac{{{e^{{x_n}}}}}{{{e^m}}}}}} \right) \, = \log \left( {\frac{{{e^{\left( {{x_i} - m} \right)}}}}{{\sum\limits_j^n {{e^{\left( {{x_j} - m} \right)}}} }}} \right) = \, \, \, \, \, \, \, \, \, \, \, \, \, \, \, \, \, \, \, \log \left( {{e^{\left( {{x_i} - m} \right)}}} \right) - \log \left( {\sum\limits_j^n {{e^{\left( {{x_j} - m} \right)}}} } \right) = \left( {{x_i} - m} \right) - \log \left( {\sum\limits_j^n {{e^{\left( {{x_j} - m} \right)}}} } \right)

经过上式的变化后就不会在出现log(0)的情况了。

昨天使用tensorflow实现MLP时,因为每一层都没用激活函数,所以出现了softmax结果溢出的问题,可以在每一层增加sigmoid函数将结果控制在一定范围内就可以了,也可以使用tensorflow.nn.log_softmax(_logits)代替tensorflow.log(tensorflow.nn.softmax(_logits))来解决该问题,tensorflow中log_softmax的实现应该已经考虑到了以上问题,但是tensorflow.nn.softmax函数并没有对以上问题进行优化。下面时我修改后的MLP的代码,增加了sigmoid激活函数,求交叉熵的方法除了第三种应该是精度有问题外,使用

扫描二维码关注公众号,回复: 2755750 查看本文章
__loss_cross_entropy = -tf.reduce_mean(__Y_true*tf.nn.log_softmax(_logits))

和使用

__loss_cross_entropy = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(logits=_logits, labels=__Y_true)) 

的训练结果的正确率都在95%左右。再贴一次代码吧,省的想看的再翻上一篇博客。

# coding:utf-8

'''
Multilayer Perceptron 多层感知机。
[MNIST Dataset](http://yann.lecun.com/exdb/mnist/)
参考 "https://github.com/aymericdamien/TensorFlow-Examples/blob/master/examples/3_NeuralNetworks/multilayer_perceptron.py"
项目地址:https://github.com/IMLHF/tensorflow_practice

'''
from __future__ import print_function
import tensorflow as tf
import numpy as np

# 加载MNIST数据集
from tensorflow.examples.tutorials.mnist import input_data
__mnist = input_data.read_data_sets("MNIST_data/", one_hot=True)

# 模型训练相关参数
__learning_rate = 0.001
__training_epochs = 15
__batch_size = 100  # 每批训练数据的大小
__display_step = 1  # 每隔__display_step批次显示一次进度

# 神经网络参数
__n_hidden_1 = 256  # 隐层第一层神经元个数
__n_hidden_2 = 256  # 隐层第二层神经元个数
__n_input = 784  # 输入层维度,与MNIST数据集的输入维度对应(图片大小28*28,展开成784维向量)
__n_output = 10  # 输出层维度,与MNIST数据集的输出维度(分类个数)对应,数字(0-9)

# tensorflow Graph输入口
__X_input = tf.placeholder("float", [None, __n_input])
__Y_true = tf.placeholder("float", [None, __n_output])

# 定义网络中的权重和阈值(偏置)
__weights = {
    'w_hidden_layer1': tf.Variable(tf.random_normal([__n_input, __n_hidden_1])),
    'w_hidden_layer2': tf.Variable(tf.random_normal([__n_hidden_1, __n_hidden_2])),
    'w_out': tf.Variable(tf.random_normal([__n_hidden_2, __n_output])),
}
__biases = {
    'b_hidden_layer1': tf.Variable(tf.random_normal([__n_hidden_1])),
    'b_hidden_layer2': tf.Variable(tf.random_normal([__n_hidden_2])),
    'b_out': tf.Variable(tf.random_normal([__n_output])),
}


# 创建多层感知机模型(双隐层)
def multilayer_perceptron(__x_input_t):
  __hidden_layer1 = tf.nn.sigmoid(tf.add(tf.matmul(__x_input_t, __weights['w_hidden_layer1']),
                                         __biases['b_hidden_layer1']))
  __hidden_layer2 = tf.nn.sigmoid(tf.add(tf.matmul(__hidden_layer1, __weights['w_hidden_layer2']),
                                         __biases['b_hidden_layer2']))
  ___out_layer = tf.nn.sigmoid(tf.add(tf.matmul(__hidden_layer2, __weights['w_out']),
                                      __biases['b_out']))
  return ___out_layer


# 训练和测试
if __name__ == '__main__':
  _logits = multilayer_perceptron(__X_input)
  # 使用softmax建立回归模型
  # # 使用交叉熵作为损失函数
  '''三种不同的方法求交叉熵'''
  __loss_cross_entropy = -tf.reduce_mean(__Y_true*tf.nn.log_softmax(_logits)) # 准确率在95%左右
  # __loss_cross_entropy = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(
  #     logits=_logits, labels=__Y_true)) # 准确率在95%左右
  # __loss_cross_entropy = -tf.reduce_mean(__Y_true*tf.log(tf.nn.softmax(_logits))) # 准确率在75%左右

  # 使用Adam算法优化模型
  __train_op = tf.train.AdamOptimizer(
      learning_rate=__learning_rate).minimize(__loss_cross_entropy)
  # 初始化变量
  __init = tf.global_variables_initializer()
  # 开始训练并测试
  with tf.Session() as __session_t:
    __session_t.run(__init)

    for epoch in range(__training_epochs):
      __avg_lost = 0
      __total_batch = int(__mnist.train.num_examples/__batch_size)
      for i in range(__total_batch):
        __x_batch, __y_batch = __mnist.train.next_batch(__batch_size)
        __nouse, __loss_t = __session_t.run([__train_op, __loss_cross_entropy],
                                            feed_dict={__X_input: __x_batch,
                                                       __Y_true: __y_batch})
        __avg_lost += float(__loss_t)/__total_batch
        # region debug
        # print(__loss_t)
        # tmp = __session_t.run(
        #     _logits, feed_dict={__X_input: __x_batch, __Y_true: __y_batch})
        # print(np.shape(_logits))
        # for tt in tmp:
        #   for pp in tt:
        #     print("%.2f, " % pp, end="")
        #   print()
        # print(__y_batch)
        # endregion edbug
      if epoch % __display_step == 0:
        print("Epoch:", '%04d' % (epoch+1), "Avg_Loss=", __avg_lost)
    print("Optimizer Finished!")

    # 测试模型
    __predict = tf.nn.softmax(multilayer_perceptron(__X_input))
    __correct = tf.equal(tf.argmax(__predict, 1), tf.argmax(__Y_true, 1))
    __accuracy_rate = tf.reduce_mean(tf.cast(__correct, tf.float32))
    print("Accuracy:", __session_t.run(__accuracy_rate,
                                       feed_dict={__X_input: __mnist.test.images,
                                                  __Y_true: __mnist.test.labels}))

猜你喜欢

转载自blog.csdn.net/u013569304/article/details/81202389
今日推荐