函数光滑化杂谈:不可导函数的可导逼近
By 苏剑林 | 2019-05-20 | 134877位读者 |一般来说,神经网络处理的东西都是连续的浮点数,标准的输出也是连续型的数字。但实际问题中,我们很多时候都需要一个离散的结果,比如分类问题中我们希望输出正确的类别,“类别”是离散的,“类别的概率”才是连续的;又比如我们很多任务的评测指标实际上都是离散的,比如分类问题的正确率和F1、机器翻译中的BLEU,等等。
还是以分类问题为例,常见的评测指标是正确率,而常见的损失函数是交叉熵。交叉熵的降低与正确率的提升确实会有一定的关联,但它们不是绝对的单调相关关系。换句话说,交叉熵下降了,正确率不一定上升。显然,如果能用正确率的相反数做损失函数,那是最理想的,但正确率是不可导的(涉及到argmax等操作),所以没法直接用。
这时候一般有两种解决方案;一是动用强化学习,将正确率设为奖励函数,这是“用牛刀杀鸡”的方案;另外一种是试图给正确率找一个光滑可导的近似公式。本文就来探讨一下常见的不可导函数的光滑近似,有时候我们称之为“光滑化”,有时候我们也称之为“软化”。
max #
后面谈到的大部分内容,基础点就是max操作的光滑近似,我们有:
max(x1,x2,…,xn)=limK→+∞1Klog(n∑i=1eKxi)
所以选定常数K,我们就有近似:
max(x1,x2,…,xn)≈1Klog(n∑i=1eKxi)
在模型中,很多时候可以设K=1,这等价于把K融合到模型自身之中,所以最简单地有:
max(x1,x2,…,xn)≈log(n∑i=1exi)≜logsumexp(x1,x2,…,xn)
这里出现了logsumexp,这是一个很常见的算子,在这里它是max函数的光滑近似。没错,max函数的光滑近似其实是logsumexp,而不是字面上很像的softmax。相关推导还可以参考我之前写的《寻求一个光滑的最大值函数》。
softmax #
刚才说softmax不是max的光滑近似,那它是谁的光滑近似呢?它事实上是onehot(argmax(x))的光滑近似,即先求出最大值所在的位置,然后生成一个等长的向量,最大值那一位置1,其它位置都置0,比如:
[2,1,4,5,3]→[0,0,0,1,0]
我们可以简单地给出一个从logsumexp到softmax的推导过程。考虑向量x=[x1,x2,…,xn],然后考虑
x′=[x1,x2,…,xn]−max(x1,x2,…,xn)
即每一位都减去整体的最大值,这样的新向量与原向量最大值所在的位置是一样的,即onehot(argmax(x))=onehot(argmax(x′))。不失一般性,考虑x1,x2,…,xn两两不相等的情况,那么新向量的最大值显然为0,并且除去最大值外,其余各位都是负数。这样一来,我们可以考虑
ex′=[ex1−max(x1,x2,…,xn),ex2−max(x1,x2,…,xn),…,exn−max(x1,x2,…,xn)]
作为onehot(argmax(x′))的近似,因为最大值为0,所以对应的位置是e0=1,而其余为负,取指数后会比较接近于0。
最后将近似(3)代入上式,化简,就可以得到
onehot(argmax(x))=onehot(argmax(x′))≈(ex1n∑i=1exi,ex2n∑i=1exi,…,exnn∑i=1exi)≜softmax(x1,x2,…,xn)
argmax #
argmax是指直接给出向量最大值所在的下标(一个整数),比如
[2,1,4,5,3]→4
这里我们遵循平时的使用习惯,下标是从1开始的,所以返回结果是4;但在编程语言中一般是从0开始的,所以在编程语言中返回结果一般是3。
如果是argmax的光滑近似,自然是希望输出一个接近4的浮点数。为了构造这样的近似,我们先观察到argmax实际上等于
sum([1,2,3,4,5]⏟序向量[1, 2, ..., n]⊗[0,0,0,1,0]⏟onehot(argmax(x)))
即数组[1,2,…,n]与onehot(argmax(x))的内积。那构造argmax的软化版就简单了,将onehot(argmax(x))换成softmax(x)就行了,即
argmax(x)≈n∑i=1i×softmax(x)i
正确率 #
上述讨论的若干个近似,基本都是在one hot向量的基础上推导出正确形式,然后用softmax近似one hot,从而得到光滑近似。用这种思想,还可以导出很多算子的光滑近似,比如正确率。
简单起见,引入记号1k,它表示第k位为1的one hot向量。假设在分类问题中,目标类别是i,预测类别是j,那么可以考虑one hot向量1i和1j,然后考虑内积
⟨1i,1j⟩={1,(i=j)0,(i≠j)
也就是说两个类别一样时,内积刚好为1,而两个类别不一样时,内积刚好为0,所以目标类别和预测类别对应的one hot向量的内积,刚好定义了一个“预测正确”的计数函数,而有了计数函数,就可以计算正确率:
正确率=1|B|∑x∈B⟨1i(x),1j(x)⟩
其中B表示当前batch,上式正是计算一个batch内正确率的函数。而在神经网络中,为了保证可导性,最后的输出只能是一个概率分布(softmax后的结果),所以正确率的光滑近似,就是将预测类别的one hot向量,换成概率分布:
正确率≈1|B|∑x∈B⟨1i(x),p(x)⟩
类似地,可以导出recall、f1等指标的光滑近似。以二分类为例,假设p(x)是正类的概率,t(x)是样本x的标签(0或1),则正类的f1的光滑近似是:
正类F1≈2∑x∈Bt(x)p(x)∑x∈B[t(x)+p(x)]
这样导出来的正确率近似公式是可导的,可以直接将它的相反数作为loss。但事实上在采样估计过程中,它是f1的有偏估计(分母也有对batch的求和),有时候会影响优化轨迹甚至导致发散,所以一般情况下最好不要直接用,而是先用普通的交叉熵训练到差不多了,然后再用f1的相反数作为loss来微调。
softkmax #
softmax是“最大值那一位置1、其余置0”的光滑近似,那如果是“最大的k个值的位置都置1、其余置0”的光滑近似呢?我们可以称之为soft-k-max。
我没有构造出soft-k-max的简单形式,但可以利用递归地构造出来:
输入为x,初始化p(0)为全0向量;
执行x=x−min(x)(保证所有元素非负)对于i=1,2,…,k,执行:
y=(1−p(i−1))⊗x;
p(i)=p(i−1)+softmax(y)返回p(k)。
至于原理,将softmax(y)换成onehot(argmax(y))然后递归下去就明白了,其实就是先max,然后把将max那一位减掉,这样次最大值就变成了最大值,然后重新softmax,递归下去即可。
总结 #
函数光滑化是一个比较有意思的数学内容,在机器学习中也经常出现。一方面,它是处理将某些操作可导化的技巧,使得模型可以直接使用反向传播求解,而不需要“动用”强化学习;另一方面,在某些情况下它也能增强模型的解释性,因为往往对应的不可导函数解释性是比较好的,将光滑化版本代入训练完成后,也许可能恢复为不可导版本来解释模型的输出结果。
当然,作为纯粹的数学之美来欣赏,也是相当赏心悦目的~
转载到请包括本文地址:https://kexue.fm/archives/6620
更详细的转载事宜请参考:《科学空间FAQ》
如果您还有什么疑惑或建议,欢迎在下方评论区继续讨论。
如果您觉得本文还不错,欢迎分享/打赏本文。打赏并非要从中获得收益,而是希望知道科学空间获得了多少读者的真心关注。当然,如果你无视它,也不会影响你的阅读。再次表示欢迎和感谢!
如果您需要引用本文,请参考:
苏剑林. (May. 20, 2019). 《函数光滑化杂谈:不可导函数的可导逼近 》[Blog post]. Retrieved from https://kexue.fm/archives/6620
@online{kexuefm-6620,
title={函数光滑化杂谈:不可导函数的可导逼近},
author={苏剑林},
year={2019},
month={May},
url={\url{https://kexue.fm/archives/6620}},
}
May 20th, 2019
正确率的损失,能给一个实验demo吗
同名啊。。老哥
May 26th, 2019
有一个Gumbel-Softmax,也是这种效果。大神可以讲讲。
CATEGORICAL REPARAMETERIZATION WITH GUMBEL-SOFTMAX
确实对它有兴趣,待我认真读读再写~
May 28th, 2019
如果这样的话,用以下公式
(e2x1n∑i=1e2xi,e2x2n∑i=1e2xi,…,e2xnn∑i=1e2xi)
代替softmax会不会效果更好,因为更接近onehot(argmax)的近似
不是说了吗,很多时候x也是学习而来的,直接设K=1,相当于让模型自己决定K的大小。
您好,可以给一些有关这篇文章引用吗?有一些数学概念没看明白。谢谢!
没有参考文献,不知道哪个概念不明白?本文的介绍已经很基础了啊~
November 21st, 2019
能不能分享下召回率的光滑化结果。。。我推导出来感觉和正确率的一样。。。
召回率、F1这些,都是相对于单个类来说的,(14)已经给出了F1的近似,所以可以很自然给出precision和recall的近似:
正类precision≈∑x∈Bt(x)p(x)∑x∈B[p(x)]正类recall≈∑x∈Bt(x)p(x)∑x∈B[t(x)]
您好,这种平滑近似请问要如何实现呢?我理解代码实现涉及到要判断一个样本是否是正类,就需要argmax函数,是不是意味着实现它们需要用到argmax函数的光滑近似?有没有源码分享下,谢谢!
像(14)式本身就已经是实现,至于参考源码,没有~
多分类情况要将argmax用one hot代替,然后转化为softmax。
这是我尝试以负正确率作为损失函数的代码实现,不知道为何效果不好,是不是实现有问题,烦请指教一下。
def neg_accuracy_loss(y_true, y_pred):
mul=tf.multiply(y_true,y_pred)
sum=tf.reduce_sum(mul)
total=tf.shape(y_true)[0]
total=tf.cast(total,"float32")
return tf.math.divide(sum,total)*(-1.0)
实现倒是没什么问题,但是你要注意一点,直接优化指标只是一个途径,但它未必是最好的途径,通常来说只是用于后期微调,前期还是用交叉熵居多。
参考:https://kexue.fm/archives/7708
你好,式子(6)的精度不太够,感觉加个n次方更好一些。
你随意。
March 8th, 2020
我的理解,这里正确率的光滑化之后感觉只有正样例在起作用,因为负样例为的标签是0,与预测的概率相乘也是0,所以负样例的预测概率无论是多少感觉都是无所谓的,这里感觉可以将负样例的标签更换为-1,这样光滑出来的正确率更符合定义,否则的话感觉更像精确率光滑化之后的结果。求大神解答一下疑问。
理解不正确。公式(13)用于任意多分类问题,多分类里边没什么正样例的说法,每个类都是平等的,用独自的one hot表示,公式里的1i(x)是one hot向量,p(x)是预测的概率分布向量。
原来如此,明白了,感谢大神解惑
这样的话,和交叉熵有什么区别呢?
谁跟交叉熵有什么区别?
jianjiu17可能问的是公式(13)和CrossEntropy的区别。
差在交叉熵是用 log p(x) 而不是 p(x)吧?
March 14th, 2020
这是我看过最好的关于softmax和one hot由来的文章,知道正态分布的重参数技巧,但分类离散分布如何采样,理解为什么要Gumbel-max 再到Gumbel softmax,这篇文章说到了本质,非常感谢!作者其他博客也非常精彩!
客气了~谢谢捧场。
April 6th, 2020
请问公式(9),内积本身就是求和了,还要外层写Sum吗?谢谢!
一般来说⊗并不是表示内积,它只表示逐位相乘,它的结果仍然是一个向量,比如[a,b]⊗[c,d]=[ac,bd]
July 20th, 2020
总觉得公式(10)过于简单粗暴,因为这么算即使和实际标签接近(比如(10)算出来5.6,实际标签是5),那么也会造成严重错误(5.6->6,标签6和标签5或许是质的差别)。
在下愚见,公式(10)考虑的似乎是argmax的下标而非类标,用onehot(argmax(x))进行分类似乎更合适。
不是很理解你的焦点。(10)式本来就是用来回归位置的,并不是用来预测类别的。
September 3rd, 2020
我感觉F1那里不能理解啊。。∑x∈Bt(x)p(x)是否应该是TP+TN呢,不应该是TP啊
否。
April 12th, 2022
[...]softmax(τx),τ→0 如果需要具体解释,参考《函数光滑化杂谈:不可导函数的可导逼近》。[...]