一般来说,神经网络处理的东西都是连续的浮点数,标准的输出也是连续型的数字。但实际问题中,我们很多时候都需要一个离散的结果,比如分类问题中我们希望输出正确的类别,“类别”是离散的,“类别的概率”才是连续的;又比如我们很多任务的评测指标实际上都是离散的,比如分类问题的正确率和F1、机器翻译中的BLEU,等等。

还是以分类问题为例,常见的评测指标是正确率,而常见的损失函数是交叉熵。交叉熵的降低与正确率的提升确实会有一定的关联,但它们不是绝对的单调相关关系。换句话说,交叉熵下降了,正确率不一定上升。显然,如果能用正确率的相反数做损失函数,那是最理想的,但正确率是不可导的(涉及到argmax等操作),所以没法直接用。

这时候一般有两种解决方案;一是动用强化学习,将正确率设为奖励函数,这是“用牛刀杀鸡”的方案;另外一种是试图给正确率找一个光滑可导的近似公式。本文就来探讨一下常见的不可导函数的光滑近似,有时候我们称之为“光滑化”,有时候我们也称之为“软化”。

max #

后面谈到的大部分内容,基础点就是max操作的光滑近似,我们有:
max(x1,x2,,xn)=limK+1Klog(ni=1eKxi)


所以选定常数K,我们就有近似:
max(x1,x2,,xn)1Klog(ni=1eKxi)

在模型中,很多时候可以设K=1,这等价于把K融合到模型自身之中,所以最简单地有:
max(x1,x2,,xn)log(ni=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]


我们可以简单地给出一个从logsumexpsoftmax的推导过程。考虑向量x=[x1,x2,,xn],然后考虑
x=[x1,x2,,xn]max(x1,x2,,xn)

即每一位都减去整体的最大值,这样的新向量与原向量最大值所在的位置是一样的,即onehot(argmax(x))=onehot(argmax(x))。不失一般性,考虑x1,x2,,xn两两不相等的情况,那么新向量的最大值显然为0,并且除去最大值外,其余各位都是负数。这样一来,我们可以考虑
ex=[ex1max(x1,x2,,xn),ex2max(x1,x2,,xn),,exnmax(x1,x2,,xn)]

作为onehot(argmax(x))的近似,因为最大值为0,所以对应的位置是e0=1,而其余为负,取指数后会比较接近于0。

最后将近似(3)代入上式,化简,就可以得到
onehot(argmax(x))=onehot(argmax(x))(ex1ni=1exi,ex2ni=1exi,,exnni=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)ni=1i×softmax(x)i

正确率 #

上述讨论的若干个近似,基本都是在one hot向量的基础上推导出正确形式,然后用softmax近似one hot,从而得到光滑近似。用这种思想,还可以导出很多算子的光滑近似,比如正确率。

简单起见,引入记号1k,它表示第k位为1的one hot向量。假设在分类问题中,目标类别是i,预测类别是j,那么可以考虑one hot向量1i1j,然后考虑内积
1i,1j={1,(i=j)0,(ij)


也就是说两个类别一样时,内积刚好为1,而两个类别不一样时,内积刚好为0,所以目标类别和预测类别对应的one hot向量的内积,刚好定义了一个“预测正确”的计数函数,而有了计数函数,就可以计算正确率:
正确率=1|B|xB1i(x),1j(x)

其中B表示当前batch,上式正是计算一个batch内正确率的函数。而在神经网络中,为了保证可导性,最后的输出只能是一个概率分布(softmax后的结果),所以正确率的光滑近似,就是将预测类别的one hot向量,换成概率分布:
正确率1|B|xB1i(x),p(x)

类似地,可以导出recall、f1等指标的光滑近似。以二分类为例,假设p(x)是正类的概率,t(x)是样本x的标签(0或1),则正类的f1的光滑近似是:
正类F12xBt(x)p(x)xB[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=xmin(x)(保证所有元素非负)

对于i=1,2,,k,执行:
     y=(1p(i1))x;
     p(i)=p(i1)+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}},
}