
正文
KL散度函数python kl散度 pytorch
提示:扫一扫查出行【扫一扫了解最新限行尾号】
复制提示
什么是KL散度?
什么是KL散度?它能够测量距离吗?怎么衡量两个概率分布的相似度?
接下来需要从信息论的角度来解释什么是KL散度,在此之前你需要理解 熵 和 交叉熵 的相关知识。
KL散度中的KL代表 Kullback-Leibler ,他们两个在 1951 年提出了KL散度。
有两个概率分布为:P , Q 。KL散度告诉了Q和P的接近程度,也就是相似度,利用交叉熵减去信息熵即可。
严格的证明KL散度的非负性:
-log函数为凸函数,那么利用琴生不等式:
KL散度不仅可以从信息论的角度来看,也可以从概率的角度来看,也就是似然比
参考:
相关问答
Q1: 如何理解K-L散度(相对熵)
Kullback-Leibler Divergence ,即 K-L散度 ,是一种 量化两种概率分布P和Q之间差异 的方式,又叫 相对熵 。在概率学和统计学上,我们经常会使用一种 更简单的、近似的分布 来替代 观察数据 或 太复杂的分布 。K-L散度能帮助我们度量使用一个分布来近似另一个分布时所损失的信息量。
K-L散度定义见文末附录1。另外在附录5中解释了为什么在深度学习中,训练模型时使用的是 Cross Entropy 而非 K-L Divergence 。
我们从下面这个问题出发思考K-L散度。
这些数据很有价值,但是也有点问题。我们距离地球🌍太远了,把这些概率分布数据发送回地球过于昂贵。还好我们是一群聪明的科学家,用一个只有一两个参数的简单模型来近似原始数据会减小数据传送量。最简单的近似模型是 均分布 ,因为蠕虫牙齿不会超过10颗,所以有11个可能值,那蠕虫的牙齿数量概率都为 1/11 。分布图如下:
显然我们的原始数据并非均分布的,但也不是我们已知的分布,至少不是常见的分布。作为备选,我们想到的另一种简单模型是 二项式分布binomial distribution 。蠕虫嘴里面共有 n=10 个牙槽,每个牙槽出现牙齿与否为独立事件,且概率均为 p 。则蠕虫牙齿数量即为期望值 E[x]=n*p ,真实期望值即为观察数据的平均值,比如说 5.7 ,则 p=0.57 ,得到如下图所示的二项式分布:
对比一下原始数据,可以看出均分布和二项分布都不能完全描述原始分布。
可是,我们不禁要问,哪一种分布更加接近原始分布呢?
已经有许多度量误差的方式存在,但是我们所要考虑的是减小发送的信息量。上面讨论的均分布和二项式分布都把问题规约到只需要两个参数,牙齿数量和概率值(均分布只需要牙齿数量即可)。那么哪个分布保留了更多的原始数据分布的信息呢?这个时候就需要K-L散度登场了。
K-L散度源于信息论。信息论主要研究如何量化数据中的信息。最重要的信息度量单位是 熵 Entropy,一般用 H 表示。分布的熵的公式如下:
上面对数没有确定底数,可以是 2 、 e 或 10 ,等等。如果我们使用以 2 为底的对数计算H值的话,可以把这个值看作是编码信息所需要的最少二进制位个数bits。上面空间蠕虫的例子中,信息指的是根据观察所得的经验分布给出的蠕虫牙齿数量。计算可以得到原始数据概率分布的熵值为 3.12 bits 。这个值只是告诉我们编码蠕虫牙齿数量概率的信息需要的二进制位 bit 的位数。
可是熵值并没有给出压缩数据到最小熵值的方法,即如何编码数据才能达到最优(存储空间最优)。优化信息编码是一个非常有意思的主题,但并不是理解K-L散度所必须的。熵的主要作用是告诉我们最优编码信息方案的理论下界(存储空间),以及度量数据的信息量的一种方式。理解了熵,我们就知道有多少信息蕴含在数据之中,现在我们就可以计算当我们用一个带参数的概率分布来近似替代原始数据分布的时候,到底损失了多少信息。请继续看下节内容。↓↓↓
只需要稍加修改 熵H 的计算公式就能得到 K-L散度 的计算公式。设 p 为观察得到的概率分布, q 为另一分布来近似 p ,则 p 、 q 的 K-L散度 为:
显然,根据上面的公式,K-L散度其实是数据的原始分布p和近似分布q之间的对数差值的期望。如果继续用 2 为底的对数计算,则 K-L散度值表示信息损失的二进制位数 。下面公式以期望表达K-L散度:
一般,K-L散度以下面的书写方式更常见:
注: log a - log b = log (a/b)
OK,现在我们知道当用一个分布来近似另一个分布时如何计算信息损失量了。接下来,让我们重新回到最开始的蠕虫牙齿数量概率分布的问题。
首先是用均分布来近似原始分布的K-L散度:
接下来计算用二项式分布近似原始分布的K-L散度:
通过上面的计算可以看出,使用均分布近似原始分布的信息损失要比用二项式分布近似小。所以,如果要从均分布和二项式分布中选择一个的话,均分布更好些。
很自然地,一些同学把K-L散度看作是不同分布之间距离的度量。这是不对的,因为从K-L散度的计算公式就可以看出它不符合对称性(距离度量应该满足对称性)。如果用我们上面观察的数据分布来近似二项式分布,得到如下结果:
所以, Dkl (Observed || Binomial) != Dkl (Binomial || Observed) 。
也就是说,用 p 近似 q 和用 q 近似 p ,二者所损失的信息并不是一样的。
前面使用的二项式分布的参数是概率 p=0.57 ,是原始数据的均值。 p 的值域在 [0, 1] 之间,我们要选择一个 p 值,建立二项式分布,目的是最小化近似误差,即K-L散度。那么 0.57 是最优的吗?
下图是原始数据分布和二项式分布的K-L散度变化随二项式分布参数 p 变化情况:
通过上面的曲线图可以看出,K-L散度值在圆点处最小,即 p=0.57 。所以我们之前的二项式分布模型已经是最优的二项式模型了。注意,我已经说了,是而像是模型,这里只限定在二项式模型范围内。
前面只考虑了均分布模型和二项式分布模型,接下来我们考虑另外一种模型来近似原始数据。首先把原始数据分成两部分,1)0-5颗牙齿的概率和 2)6-10颗牙齿的概率。概率值如下:
当 p=0.47 时,K-L值取最小值 0.338 。似曾相识吗?对,这个值和使用均分布的K-L散度值是一样的(这并不能说明什么)!下面我们继续画出这个奇怪模型的概率分布图,看起来确实和均分布的概率分布图相似:
我们自己都说了,这是个奇怪的模型,在K-L值相同的情况下,更倾向于使用更常见的、更简单的均分布模型。
回头看,我们在这一小节中使用K-L散度作为目标方程,分别找到了二项式分布模型的参数 p=0.57 和上面这个随手建立的模型的参数 p=0.47 。是的,这就是本节的重点: 使用K-L散度作为目标方程来优化模型 。当然,本节中的模型都只有一个参数,也可以拓展到有更多参数的高维模型中。
如果你熟悉神经网络,你肯能已经猜到我们接下来要学习的内容。除去神经网络结构的细节信息不谈,整个神经网络模型其实是在构造一个参数数量巨大的函数(百万级,甚至更多),不妨记为 f(x) ,通过设定目标函数,可以训练神经网络逼近非常复杂的真实函数 g(x) 。训练的关键是要设定目标函数,反馈给神经网络当前的表现如何。训练过程就是不断减小目标函数值的过程。
我们已经知道K-L散度用来度量在逼近一个分布时的信息损失量。K-L散度能够赋予神经网络近似表达非常复杂数据分布的能力。变分自编码器(Variational Autoencoders,VAEs)是一种能够学习最佳近似数据集中信息的常用方法, Tutorial on Variational Autoencoders 2016 是一篇关于VAEs的非常不错的教程,里面讲述了如何构建VAE的细节。 What are Variational Autoencoders? A simple explanation 简单介绍了VAEs, Building Autoencoders in Keras 介绍了如何利用Keras库实现几种自编码器。
变分贝叶斯方法(Variational Bayesian Methods)是一种更常见的方法。 这篇文章 介绍了强大的蒙特卡洛模拟方法能够解决很多概率问题。蒙特卡洛模拟能够帮助解决许多贝叶斯推理问题中的棘手积分问题,尽管计算开销很大。包括VAE在内的变分贝叶斯方法,都能用K-L散度生成优化的近似分布,这种方法对棘手积分问题能进行更高效的推理。更多变分推理(Variational Inference)的知识可以访问 Edward library for python 。
因为本人没有学习过VAE和变分推理,所以本节内容质量无法得到保证,我会联系这方面的朋友来改善本节内容,也欢迎大家在评论区给出建议
Q2: Pytorch-交叉熵
一条信息的信息量大小和它的不确定性有很大的关系 。一句话如果需要很多外部信息才能确定KL散度函数python,我们就称这句话的信息量比较大。比如你听到“云南西双版纳下雪了”KL散度函数python,那你需要去看天气预报、问当地人等等查证(因为云南西双版纳从没下过雪)。相反,如果和你说“人一天要吃三顿饭”,那这条信息的信息量就很小,因为这条信息的确定性很高。
那我们就能将事件 的信息量定义如下(其中 表示事件 发生的概率):
信息量是对于单个事件来说的 ,但是实际情况一件事有很多种发生的可能,比如掷骰子有可能出现6种情况,明天的天气可能晴、多云或者下雨等等。 熵是表示随机变量不确定的度量,是对所有可能发生的事件产生的信息量的期望 。公式如下:
的曲线如下:
结合熵的公式(2)以及 曲线,当这些所有可能发生事件的概率比较小(接近0)或者比较大(接近1)时,熵的值会比较小;如果事件发生的概率既远离0也远离1时,熵的值就会比较大。
例如,如下三组事件比较:
1)事件概率均等,[0.2500, 0.2500, 0.2500, 0.2500],熵为2;
2)事件概率比较靠近0或者1,[0.1, 0.1, 0.1, 0.7],熵为1.3568;
3)事件概率极其靠近0或者1,[0.001, 0.001, 0.001, 0.999],熵为0.0313.
熵的一种比较特殊的情况就是掷硬币 ,只有正、反两种情况,该种情况(二项分布或者0-1分布)熵的计算可以简化如下:
其中, 表示正面概率。
相对熵又称KL散度,用于衡量对于同一个随机变量 的两个分布 和 之间的差异 。在机器学习中, 常用于描述样本的真实分布 ,例如[1,0,0,0]表示样本属于第一类,而 则常常用于表示预测的分布 ,例如[0.7,0.1,0.1,0.1]。显然使用q(x)来描述样本不如 准确, 需要不断地学习来拟合准确的分布 。
KL散度的公式如下:
KL散度的值越小表示两个分布越接近。
我们将KL散度的公式进行变形,得到:
前半部分就是 的熵,后半部分就是我们的交叉熵:
机器学习中,我们常常使用KL散度来评估predict和label之间的差别,但是由于KL散度的前半部分是一个常量,所以我们常常将后半部分的交叉熵作为损失函数,其实二者是一样的。
交叉熵代价函数(Cross-entropy cost function)是用来衡量人工神经网络(ANN)的预测值与实际值的一种方式。与二次代价函数相比,它能更有效地促进ANN的训练。在介绍交叉熵代价函数之前,本文先简要介绍二次代价函数,以及其存在的不足。
ANN的设计目的之一是为了使机器可以像人一样学习知识。人在学习分析新事物时,当发现自己犯的错误越大时,改正的力度就越大。比如投篮:当运动员发现自己的投篮方向离正确方向越远,那么KL散度函数python他调整的投篮角度就应该越大,篮球就更容易投进篮筐。同理, 我们希望:ANN在训练时,如果预测值与实际值的误差越大,那么在反向传播训练的过程中,各种参数调整的幅度就要更大,从而使训练更快收敛。 然而,如果使用二次代价函数训练ANN,看到的实际效果是,如果误差越大,参数调整的幅度可能更小,训练更缓慢。
以一个神经元的二类分类训练为例,进行两次实验(ANN常用的激活函数为sigmoid函数,该实验也采用该函数):输入一个相同的样本数据x=1.0(该样本对应的实际分类y=0);两次实验各自随机初始化参数,从而在各自的第一次前向传播后得到不同的输出值,形成不同的代价(误差):
在实验1中,随机初始化参数,使得第一次输出值为0.82(该样本对应的实际值为0);经过300次迭代训练后,输出值由0.82降到0.09,逼近实际值。而在实验2中,第一次输出值为0.98,同样经过300迭代训练,输出值只降到了0.20。
从两次实验的代价曲线中可以看出: 实验1的代价随着训练次数增加而快速降低,但实验2的代价在一开始下降得非常缓慢;直观上看,初始的误差越大,收敛得越缓慢。
其实,误差大导致训练缓慢的原因在于使用了二次代价函数。二次代价函数的公式如下:
其中, 表示代价, 表示样本, 表示实际值, 表示输出值, 表示样本的总数。为简单起见,同样一个样本为例进行说明,此时二次代价函数为:
目前训练ANN最有效的算法是反向传播算法 。简而言之,训练ANN就是通过反向传播代价,以减少代价为导向,调整参数。参数主要有:神经元之间的连接权重 ,以及每个神经元本身的偏置 。调参的方式是采用梯度下降算法(Gradient descent),沿着梯度方向调整参数大小。 和 的梯度推导如下:
其中, 表示神经元的输入, 表示激活函数。从以上公式可以看出, 和 的梯度跟激活函数的梯度成正比,激活函数的梯度越大, 和 的大小调整得越快,训练收敛得就越快。而神经网络常用的激活函数为sigmoid函数,该函数的曲线如下所示:
如图所示, 实验2的初始输出值(0.98)对应的梯度明显小于实验1的输出值(0.82),因此实验2的参数梯度下降得比实验1慢。这就是初始的代价(误差)越大,导致训练越慢的原因。 与我们的期望不符,即:不能像人一样,错误越大,改正的幅度越大,从而学习得越快。
可能有人会说,那就选择一个梯度不变化或变化不明显的激活函数不就解决问题了吗?那样虽然简单粗暴地解决了这个问题,但可能会引起其他更多更麻烦的问题。而且,类似sigmoid这样的函数(比如tanh函数)有很多优点,非常适合用来做激活函数,具体请自行google之。
换个思路,我们不换激活函数,而是换掉二次代价函数,改用交叉熵代价函数:
其中, 表示样本, 表示样本的总数。那么,重新计算参数 的梯度:
因此, 的梯度公式中原来的 被消掉了;另外,该梯度公式中的 表示输出值与实际值之间的误差。所以,当误差越大,梯度就越大,参数 调整得越快,训练速度也就越快。 实际情况证明,交叉熵代价函数带来的训练效果往往比二次代价函数要好。
在实际分类任务中,要先将输出层的输出值经过Softmax函数,再经过log函数,最后才用交叉熵损失函数计算损失。
pytorch中有计算交叉熵损失的接口,即 F.cross_entropy() ,不过该接口包含了Softmax函数、log函数、交叉熵损失函数。也就是说 F.cross_entropy() = F.softmax() + torch.log() + F.nnl_loss() 。即使如此,也要使用 F.cross_entropy() ,不仅是因为它简单,更因为它能保证数值稳定。
机器学习的过程就是希望在训练数据熵 模型学到的分布 和 真实的分布 越近越好,我们知道KL散度可以表示两个分布之间的不同。
但我们没有真实数据的分布,那么只能退而求其次,希望模型学到的分布和训练数据的分布 ,也就是把训练数据当做模型和真实数据之间的代理人 。假设训练数据是从总体中独立同步分布采样(Independent and identically distributed sampled)而来,那么我们可以利用最小化训练数据的经验误差来降低模型的泛化误差。简单说:
由此非常理想化的看法是如果 模型(左) 能够学到 训练数据(中) 的分布,那么应该近似的学到了 真实数据(右) 的分布: 近似于 近似于
简单的交叉熵,你真的懂了吗?
交叉熵损失函数
关于KL散度函数python和kl散度 pytorch的介绍到此就结束了,不知道你从中找到你需要的信息了吗 ?如果你还想了解更多这方面的信息,记得收藏关注本站。






