点击上方,选择星标置顶,每天给你送干货

阅读大概需要14分钟

跟随小博主,每天进步一丢丢

来自 | 知乎

地址 | https://zhuanlan.zhihu.com/p/27223959

作者 | 杀手XIII

编辑 | 机器学习算法与自然语言处理公众号

本文仅作学术分享,若侵权,请联系后台删文处理

Softmax函数

背景与定义

在Logistic regression二分类问题中,我们可以使用sigmoid函数将输入映射到区间中,从而得到属于某个类别的概率。将这个问题进行泛化,推广到多分类问题中,我们可以使用softmax函数,对输出的值归一化为概率值。

这里假设在进入softmax函数之前,已经有模型输出值,其中是要预测的类别数,模型可以是全连接网络的输出,其输出个数为,即输出为

所以对每个样本,它属于类别的概率为:

通过上式可以保证,即属于各个类别的概率和为1。

导数

对softmax函数进行求导,即求

项的输出对第项输入的偏导。
代入softmax函数表达式,可以得到:

用我们高中就知道的求导规则:对于

它的导数为

所以在我们这个例子中,

上面两个式子只是代表直接进行替换,而非真的等式。

(即)对进行求导,要分情况讨论:

  1. 如果,则求导结果为

  2. 如果,则求导结果为

再来看求导,结果为

所以,当时:

时:

其中,为了方便,令

对softmax函数的求导,我在两年前微信校招面试基础研究岗位一面的时候,就遇到过,这个属于比较基础的问题。

softmax的计算与数值稳定性

在Python中,softmax函数为:

def softmax(x):exp_x = np.exp(x)return exp_x / np.sum(exp_x)

传入[1, 2, 3, 4, 5]的向量

>>> softmax([1, 2, 3, 4, 5])
array([ 0.01165623,  0.03168492,  0.08612854,  0.23412166,  0.63640865])

但如果输入值较大时:

>>> softmax([1000, 2000, 3000, 4000, 5000])
array([ nan,  nan,  nan,  nan,  nan])

这是因为在求exp(x)时候溢出了:

import math
math.exp(1000)
# Traceback (most recent call last):
#   File "<stdin>", line 1, in <module>
# OverflowError: math range error

一种简单有效避免该问题的方法就是让exp(x)中的x值不要那么大或那么小,在softmax函数的分式上下分别乘以一个非零常数:

这里是个常数,所以可以令它等于。加上常数之后,等式与原来还是相等的,所以我们可以考虑怎么选取常数。我们的想法是让所有的输入在0附近,这样的值不会太大,所以可以让的值为:

这样子将所有的输入平移到0附近(当然需要假设所有输入之间的数值上较为接近),同时,除了最大值,其他输入值都被平移成负数,为底的指数函数,越小越接近0,这种方式比得到nan的结果更好。

def softmax(x):shift_x = x - np.max(x)exp_x = np.exp(shift_x)return exp_x / np.sum(exp_x)>>> softmax([1000, 2000, 3000, 4000, 5000])
array([ 0.,  0.,  0.,  0.,  1.])

当然这种做法也不是最完美的,因为softmax函数不可能产生0值,但这总比出现nan的结果好,并且真实的结果也是非常接近0的。

UPDATE(2017-07-07):

有同学问这种近似会不会影响计算结果,为了看原来的softmax函数计算结果怎么样,尝试计算`softmax([1000, 2000, 3000, 4000, 5000])`的值。由于numpy是会溢出的,所以使用Python中的bigfloat库。

import bigfloatdef softmax_bf(x):exp_x = [bigfloat.exp(y) for y in x]sum_x = sum(exp_x)return [y / sum_x for y in exp_x]res = softmax_bf([1000, 2000, 3000, 4000, 5000])
print('[%s]' % ', '.join([str(x) for x in res]))

结果:

[6.6385371046556741e-1738, 1.3078390189212505e-1303, 2.5765358729611501e-869, 5.0759588975494576e-435, 1.0000000000000000]

可以看出,虽然前四项结果的量级不一样,但都是无限接近于0,所以加了一个常数的softmax对原来的结果影响很小。

Loss function

对数似然函数

机器学习里面,对模型的训练都是对Loss function进行优化,在分类问题中,我们一般使用最大似然估计(Maximum likelihood estimation)来构造损失函数。对于输入的,其对应的类标签为,我们的目标是找到这样的使得最大。在二分类的问题中,我们有:

其中,是模型预测的概率值,是样本对应的类标签。

将问题泛化为更一般的情况,多分类问题:

由于连乘可能导致最终结果接近0的问题,一般对似然函数取对数的负数,变成最小化对数似然函数。

交叉熵

说交叉熵之前先介绍相对熵,相对熵又称为KL散度(Kullback-Leibler Divergence),用来衡量两个分布之间的距离,记为

这里的熵。

假设有两个分布,它们在给定样本集上的交叉熵定义为:

从这里可以看出,交叉熵和相对熵相差了,而当已知的时候,是个常数,所以交叉熵和相对熵在这里是等价的,反映了分布之间的相似程度。关于熵与交叉熵等概念,可以参考该博客再做了解。

回到我们多分类的问题上,真实的类标签可以看作是分布,对某个样本属于哪个类别可以用One-hot的编码方式,是一个维度为的向量,比如在5个类别的分类中,[0, 1, 0, 0, 0]表示该样本属于第二个类,其概率值为1。我们把真实的类标签分布记为,该分布中,属于它的真实类别。同时,分类模型经过softmax函数之后,也是一个概率分布,因为,所以我们把模型的输出的分布记为,它也是一个维度为的向量,如[0.1, 0.8, 0.05, 0.05, 0]。
对一个样本来说,真实类标签分布与模型预测的类标签分布可以用交叉熵来表示:

可以看出,该等式于上面对数似然函数的形式一样!

最终,对所有的样本,我们有以下loss function:

其中是样本属于类别的概率,是模型对样本预测为属于类别的概率。

Loss function求导

对单个样本来说,loss function对输入的导数为:

上面对求导结果已经算出:

时:

时:

所以,将求导结果代入上式:

TensorFlow

方法1:手动实现(不建议使用)

在TensorFlow中,已经有实现好softmax函数,所以我们可以自己构造交叉熵损失函数:

import tensorflow as tf
import input_datax = tf.placeholder("float", shape=[None, 784])
label = tf.placeholder("float", shape=[None, 10])w_fc1 = tf.Variable(tf.truncated_normal([784, 1024], stddev=0.1))
b_fc1 = tf.Variable(tf.constant(0.1, shape=[1024]))
h_fc1 = tf.matmul(x, w_fc1) + b_fc1w_fc2 = tf.Variable(tf.truncated_normal([1024, 10], stddev=0.1))
b_fc2 = tf.Variable(tf.constant(0.1, shape=[10]))
y = tf.nn.softmax(tf.matmul(h_fc1, w_fc2) + b_fc2)cross_entropy = -tf.reduce_sum(label * tf.log(y))

cross_entropy = -tf.reduce_sum(label * tf.log(y))是交叉熵的实现。先对所有的输出用softmax进行转换为概率值,再套用交叉熵的公式。

方法2:使用tf.nn.softmax_cross_entropy_with_logits(推荐使用)

import tensorflow as tf
import input_datax = tf.placeholder("float", shape=[None, 784])
label = tf.placeholder("float", shape=[None, 10])w_fc1 = tf.Variable(tf.truncated_normal([784, 1024], stddev=0.1))
b_fc1 = tf.Variable(tf.constant(0.1, shape=[1024]))
h_fc1 = tf.matmul(x, w_fc1) + b_fc1w_fc2 = tf.Variable(tf.truncated_normal([1024, 10], stddev=0.1))
b_fc2 = tf.Variable(tf.constant(0.1, shape=[10]))
y = tf.matmul(h_fc1, w_fc2) + b_fc2cross_entropy = -tf.reduce_sum(tf.nn.softmax_cross_entropy_with_logits(labels=label, logits=y))

TensorFlow已经实现好函数,用来计算label和logits的softmax交叉熵。注意,该函数的参数logits在函数内会用softmax进行处理,所以传进来时不能是softmax的输出了。

区别

既然我们可以自己实现交叉熵的损失函数,为什么TensorFlow还要再实现tf.nn.softmax_cross_entropy_with_logits函数呢?

这个问题在Stack overflow上已经有Google的人出来回答(传送门),原话是:

If you want to do optimization to minimize the cross entropy, AND you’re softmaxing after your last layer, you should use tf.nn.softmax_cross_entropy_with_logits instead of doing it yourself, because it covers numerically unstable corner cases in the mathematically right way. Otherwise, you’ll end up hacking it by adding little epsilons here and there.

也就是说,方法1自己实现的方法会有在前文说的数值不稳定的问题,需要自己在softmax函数里面加些trick。所以官方推荐如果使用的loss function是最小化交叉熵,并且,最后一层是要经过softmax函数处理,则最好使用tf.nn.softmax_cross_entropy_with_logits函数,因为它会帮你处理数值不稳定的问题。

总结

全文到此就要结束了,可以看到,前面介绍这么多概念,其实只是为了解释在具体实现时候要做什么样的选择。可能会觉得有些小题大做,但对于NN这个黑盒子来说,我们现暂不能从理论上证明其有效性,那在工程实现上,我们不能再将它当作黑盒子来使用。

Reference

The Softmax function and its derivative Peter’s Notes CS231n Convolutional Neural Networks for Visual Recognition cs229.stanford.edu/note 交叉熵(Cross-Entropy) - rtygbwwwerr的专栏 - 博客频道 - CSDN.NET difference between tensorflow tf.nn.softmax and tf.nn.softmax_cross_entropy_with_logits

文章同时发在CSDN上:blog.csdn.net/behamcheu#


投稿或交流学习,备注:昵称-学校(公司)-方向,进入DL&NLP交流群。

方向有很多:机器学习、深度学习,python,情感分析、意见挖掘、句法分析、机器翻译、人机对话、知识图谱、语音识别等。

记得备注呦

【小知识】Softmax函数与交叉熵相关推荐

  1. SoftMax函数,交叉熵损失函数与熵,对数似然函数

    深度学习以及机器学习中都会用到SoftMax函数,交叉熵损失函数与熵,对数似然函数等一些数学方面的知识,此文作为个人学习笔记. 1.softmax函数 (1)定义 多分类问题中,我们可以使用SoftM ...

  2. Softmax函数与交叉熵

    Softmax函数 背景与定义 导数 softmax的计算与数值稳定性 Loss function 对数似然函数 交叉熵 Loss function求导 TensorFlow 方法1手动实现不建议使用 ...

  3. softmax函数与交叉熵损失函数

    本文主要介绍了当前机器学习模型中广泛应用的交叉熵损失函数与softmax激励函数. 这个损失函数主要应用于多分类问题,用于衡量预测值与实际值之间的相似程度. 交叉熵损失函数定义如下: LCE(y^,y ...

  4. Softmax函数+信息熵+交叉熵

    信息熵 https://blog.csdn.net/hearthougan/article/details/76192381 https://blog.csdn.net/Hearthougan/art ...

  5. 【AI-1000问】softmax loss和交叉熵有什么关系?

    文章首发于微信公众号<有三AI> [AI-1000问]softmax loss和交叉熵有什么关系? 往期视频(前往公众号观看) softmax loss和交叉熵有什么关系? 想必大家也都听 ...

  6. softmax回归与交叉熵损失

    前言 回归与分类是机器学习中的两个主要问题,二者有着紧密的联系,但又有所不同.在一个预测任务中,回归问题解决的是多少的问题,如房价预测问题,而分类问题用来解决是什么的问题,如猫狗分类问题.分类问题又以 ...

  7. softmax,softmax loss和交叉熵的关系

    交叉熵 对于分类问题的交叉熵损失函数: L = 1 n ∑ i = 1 n ( − ∑ j = 1 C y i , j log ⁡ p i , j ) L=\frac{1}{n}\sum^{n}_{i ...

  8. 二分类交叉熵损失函数python_【深度学习基础】第二课:softmax分类器和交叉熵损失函数...

    [深度学习基础]系列博客为学习Coursera上吴恩达深度学习课程所做的课程笔记. 本文为原创文章,未经本人允许,禁止转载.转载请注明出处. 1.线性分类 如果我们使用一个线性分类器去进行图像分类该怎 ...

  9. 数学小课堂:库尔贝勒交叉熵(K-L divergence,也叫KL散度)【量化度量错误预测所要付出的成本,避免制订出与事实相反的计划】

    文章目录 引言 I 预备知识:置信度(Confidence Level) 1.1 置信度的定义 1.1 提高置信度 II 误判的代价函数 2.1 信息偏差带来的损失 2.2 库尔贝勒交叉熵的应用 2. ...

  10. C语言小知识---printf()函数转换符的意义

      printf()函数大家已经很熟悉了,它的转换符在打印数据的时候也会经常使用,比如%c,%d,%f等.那么为什么打印的时候一定需要转换符呢?系统难道不能自动识别吗?转换符存在的意义又是什么?    ...

最新文章

  1. 到底什么级别才算是高并发?
  2. zz为什么人人都该学点心理学
  3. 一个WEB网站高并发量的解决方案
  4. 稳定直播服务器主板,云直播服务器
  5. Linux查看所有用户和组信息
  6. trove mysql 镜像_Linux运维----03.制作trove-mysql5.7镜像
  7. Facade(外观)--对象结构型模式
  8. 服务器部署docker
  9. linux ls 中文乱码_每天一个linux命令:Linux文件类型与扩展名
  10. “成长”必经之路:越努力越幸运
  11. VS2008无法下断点调试的若干解决办法
  12. Java并发编程知识总结
  13. java+mysql实现图书管理系统
  14. 基于RFM模型的用户价值的数据分析报告
  15. 聊一聊我常用的6种绘制地图的方法
  16. Windows开发之VC++仿QQ迷你首页(迷你资讯)
  17. html有哪些好玩的地方,武汉本地好玩的地方有哪些?盘点武汉十大必看景点
  18. ubuntu 批量改名
  19. win10如何开启电源高性能模式
  20. (一)通用定时器的相关介绍

热门文章

  1. (转)fiddler使用简介--其二
  2. smali注入常用代码
  3. Android APK代码混淆与资源混淆详解,你确定不看?
  4. My third homework
  5. ubuntu下搭建的lamp环境新建站点
  6. 【转】Asp.NET大文件上传组件开发总结(二)---提取文件内容
  7. 中国统计网又出干货了——会员分层与顾客忠诚度分析
  8. 我的2017:从工作再到学生
  9. scrapy_全站爬取
  10. 浅谈OWASP TOP 10