草庐IT

两个视角给你解读 熵、交叉熵、KL散度

51Ann 2023-03-28 原文
本文从两方面进行解释:数学和编码方面。总有一个角度能让你更好理解。

数学解释

熵 Entropy

熵用于计算一个离散随机变量的信息量。对于一个概率分布$X$,$X$的熵就是它的不确定性。 用大白话来说,假设你预测一个东西,有时候结果会出乎意料,熵就表示出乎意料的程度。熵越大你越不容易预测对,事情就越容易出乎意料。

离散型概率分布$X$的熵定义为自信息的平均值: $$ H(X)=E_{p(x)}[I(x)]=-\sum_{x} p(x) \log p(x) $$

注意: 熵的单位可以是比特(bits)也可以是奈特(nats)。二者区别在于前者是用$\log_2$计算,后者是用$\log_e$计算。我们这里是用$\log_2$计算。

举个栗子算一下熵。

两个城市明天的天气状况如下:

现在有两个事件:

  • A市明天的天气状况
  • B市明天的天气状况
$H(A)=-0.8 \times \log 0.8-0.15 \times \log 0.15-0.05 \times \log 0.05=0.884$

$H(B)=-0.4 \times \log 0.4-0.3 \times \log 0.3-0.3 \times \log 0.3=1.571$

可以看到B的熵比A大,因此B城市的天气具有更大的不确定性。

交叉熵 Cross-Entropy

交叉熵用于度量两个概率分布间的差异性信息。 再用大白话说一下,比如你认为一件事有六成概率能成功,实际上你去做的时候你又八成概率能成功。这时候结果出乎意料的程度就是交叉熵。

交叉熵的数学定义:

$$ H(A, B)=-\Sigma_{i} P_{A}\left(x_{i}\right) \log \left(P_{B}\left(x_{i}\right)\right) $$

举个栗子算一下交叉熵。

改了一下表头。 现在还是有两个事件:

  • $P$实际A城市明天的天气状况
  • $Q$你以为的A城市的天气状况
$H(P,Q)=-0.8 \times \log0.4-0.15 \times \log0.3 - 0.05 \times \log 0.3 = 1.405$

KL散度 Kullback-Leibler divergence

KL散度又称相对熵、信息增益,相对于交叉熵来说,是从另一个角度计算两个分布的差异程度。相对于分布X,分布Y有多大的不同?这个不同的程度就是KL散度。

注意,KL散度是不对称的,也就是说X关于Y的KL散度 不等于 Y关于X的KL散度。

若 $A$ 和 $B$ 为定义在同一概率空间的两个概率测度,定义 $A$ 相对于 $B$ 的相对熵为 $$ D(A | B)=\sum_{x} P_A(x) \log \frac{P_A(x)}{P_B(x)} $$

举个栗子算一下KL散度。

还是用这个例子:

现在还是有两个事件:

  • $P$实际A城市明天的天气状况
  • $Q$你以为的A城市的天气状况
$D(P |Q) = 0.8 \times \log(0.8 \div0.4) + 0.15 \times \log(0.15 \div 0.3) + 0.05 \times \log(0.0.5\div 0.3) =0.521$

熵、KL散度和交叉熵的关系

我们从上边三个例子中可以看到:

  • A城市明天实际天气状况的熵$H(A)=0.884$
  • A城市明天实际天气状况和你预测的天气状况的交叉熵为$H(P,Q)=1.405$
  • A城市明天实际天气状况和你预测的天气状况的KL散度为$D(P |Q) =0.521$
然后我们可以发现:$0.884+0.521=1.405$

这里可以引出一个结论 $$ 熵 + KL散度 = 交叉熵 $$

从编码的角度解释

<font color=red>注意:下边这个举的例子是能整除的情况下,不能整除的情况下是算不出来的。</font>

能整除的例子

假设我们现在有一条消息皮皮卡皮,皮卡丘

让我们对这条消息统计一下:

数量 4 2 1 1
比例 $\frac{4}{8}$ $\frac{2}{8}$ $\frac{1}{8}$ $\frac{1}{8}$
画个哈夫曼树:

数量 4 2 1 1
比例 $\frac{4}{8}$ $\frac{2}{8}$ $\frac{1}{8}$ $\frac{1}{8}$
哈夫曼编码 11 100 101
编码长度 1 2 3 3
最短编码平均长度:

$\frac{4}{8} \times 1+\frac{2}{8} \times 2+\frac{1}{8} \times 3+\frac{1}{8} \times 3=1.75$

上述编码的熵:

$-\frac{4}{8} \times \log \frac{4}{8}-\frac{2}{8} \times \log \frac{2}{8}-\frac{1}{8} \times \log \frac{1}{8}-\frac{1}{8} \times \log \frac{1}{8}=1.75$

从编码角度看,一串编码的熵等于它的最短编码平均长度。

数量 4 2 1 1
比例 $\frac{4}{8}$ $\frac{2}{8}$ $\frac{1}{8}$ $\frac{1}{8}$
哈夫曼编码 11 100 101
错误的哈夫曼编码 11 100 101
如果你编码时候写错了

现在的平均编码长度是:

$\frac{4}{8} \times 2+\frac{2}{8} \times 1+\frac{1}{8} \times 3+\frac{1}{8} \times 3=2$

此时交叉熵为:

$-\frac{4}{8} \times \log \frac{2}{8}-\frac{2}{8} \times \log \frac{4}{8}-\frac{1}{8} \times \log \frac{1}{8}-\frac{1}{8} \times \log \frac{1}{8}=2$

使用错误的编码时候,编码平均长度就是交叉熵。

而KL散度呢?

$\frac{4}{8} \times \log(\frac{4}{8}\div\frac{2}{8})+\frac{2}{8} \times \log (\frac{2}{8} \div \frac{4}{8})+\frac{1}{8} \times \log (\frac{1}{8} \div \frac{1}{8})+\frac{1}{8} \times \log (\frac{1}{8} \div \frac{1}{8})=0.25$

KL散度就是错误编码平均长度和正确编码平均长度的差异。

不能整除的例子

<font color=red>注意:你看,不能整除的情况下是算不出来的。</font>

假设我们现在有一条消息皮卡皮卡,皮卡皮,皮卡丘

让我们对这条消息统计一下:

数量 5 4 1 2
比例 $\frac{5}{12}$ $\frac{4}{12}$ $\frac{1}{12}$ $\frac{2}{12}$
画个哈夫曼树:

数量 5 4 2 1
比例 $\frac{5}{12}$ $\frac{4}{12}$ $\frac{2}{12}$ $\frac{1}{12}$
哈夫曼编码 11 101 100
编码长度 1 2 3 3
最短编码平均长度:

$\frac{5}{12} \times 1 +\frac{4}{12} \times 2+\frac{2}{12} \times 3+\frac{1}{12} \times 3 = 1.83$

上述编码的熵:

$-\frac{5}{12} \times \log\frac{5}{12} -\frac{4}{12} \times \log\frac{4}{12}-\frac{2}{12} \times \log\frac{2}{12}-\frac{1}{12} \times \log\frac{1}{12} = 1.78$

后边不算了。可以看到不能整除情况下因为一些误差是不相等的。

有关两个视角给你解读 熵、交叉熵、KL散度的更多相关文章

  1. ruby-on-rails - 如何在 ruby​​ 中使用两个参数异步运行 exe? - 2

    exe应该在我打开页面时运行。异步进程需要运行。有什么方法可以在ruby​​中使用两个参数异步运行exe吗?我已经尝试过ruby​​命令-system()、exec()但它正在等待过程完成。我需要用参数启动exe,无需等待进程完成是否有任何ruby​​gems会支持我的问题? 最佳答案 您可以使用Process.spawn和Process.wait2:pid=Process.spawn'your.exe','--option'#Later...pid,status=Process.wait2pid您的程序将作为解释器的子进程执行。除

  2. ruby - 这两个 Ruby 类初始化定义有什么区别? - 2

    我正在阅读一本关于Ruby的书,作者在编写类初始化定义时使用的形式与他在本书前几节中使用的形式略有不同。它看起来像这样:classTicketattr_accessor:venue,:datedefinitialize(venue,date)self.venue=venueself.date=dateendend在本书的前几节中,它的定义如下:classTicketattr_accessor:venue,:datedefinitialize(venue,date)@venue=venue@date=dateendend在第一个示例中使用setter方法与在第二个示例中使用实例变量之间是

  3. ruby - 具有两个参数的 block - 2

    我从用户Hirolau那里找到了这段代码:defsum_to_n?(a,n)a.combination(2).find{|x,y|x+y==n}enda=[1,2,3,4,5]sum_to_n?(a,9)#=>[4,5]sum_to_n?(a,11)#=>nil我如何知道何时可以将两个参数发送到预定义方法(如find)?我不清楚,因为有时它不起作用。这是重新定义的东西吗? 最佳答案 如果您查看Enumerable#find的文档,您会发现它只接受一个block参数。您可以将它发送两次的原因是因为Ruby可以方便地让您根据它的“并行赋

  4. arrays - 如何在下面的示例中将两个值数组分组为 n 个值数组? - 2

    我已经有很多两个值数组,例如下面的例子ary=[[1,2],[2,3],[1,3],[4,5],[5,6],[4,7],[7,8],[4,8]]我想把它们分组到[1,2,3],[4,5],[5,6],[4,7,8]因为意思是1和2有关系,2和3有关系,1和3有关系,所以1,2,3都有关系我如何通过ruby​​库或任何算法来做到这一点? 最佳答案 这是基本Bron–Kerboschalgorithm的Ruby实现:classGraphdefinitialize(edges)@edges=edgesenddeffind_maximum_

  5. ruby - 尝试比较两个文本文件,并根据信息创建第三个 - 2

    我有两个文本文件,master.txt和926.txt。如果926.txt中有一行不在master.txt中,我想写入一个新文件notinbook.txt。我写了我能想到的最好的东西,但考虑到我是一个糟糕的/新手程序员,它失败了。这是我的东西g=File.new("notinbook.txt","w")File.open("926.txt","r")do|f|while(line=f.gets)x=line.chompifFile.open("master.txt","w")do|h|endwhile(line=h.gets)ifline.chomp!=xputslineendende

  6. ruby - 在两个 ActiveRecord 类之间合并/复制属性的好方法? - 2

    之前有人问过这个问题,我发现了以下clip关于如何一次设置一个类对象的所有属性,但由于批量分配保护,这在Rails中是不可能的。(例如,您不能Object.attributes={})有没有一种很好的方法可以将一个类的属性合并到另一个类中?object1.attributes=object2.attributes.inject({}){|h,(k,v)|h[k]=vifObjectModel.column_names.include?(k);h}谢谢。 最佳答案 利用assign_attributes使用:without_prote

  7. ruby-on-rails - ruby 中两个哈希之间的变化 - 2

    我有两个具有以下格式的哈希mydetails[x['Id']]=x['Amount']这将包含如下数据hash1={"A"=>"0","B"=>"1","C"=>"0","F"=>"1"}hash2={"A"=>"0","B"=>"3","C"=>"0","E"=>"1"}我期待这样的输出:Differencesinhash:"B,F,E"非常感谢任何帮助。 最佳答案 这个解决方案可能更容易理解:(hash1.keys|hash2.keys).select{|key|hash1[key]!=hash2[key]}Array#|返回2

  8. ruby - 检查 ruby 中的两个范围是否重叠 - 2

    我知道我能做到:(1..30).cover?(2)=>true但是当我尝试对另一个范围执行相同操作时,它总是返回false:(1..30).cover?(2..3)=>false所以我的问题是-是否有任何优雅的方法来比较ruby​​中的两个范围?在我的例子中,我想检查两个日期时间范围是否重叠。提前致谢。 最佳答案 给定范围A的两个范围重叠,当:范围B从范围A开始,范围B在范围A内结束或范围B在范围A之前开始,在范围A之后结束例子:RangeA|-----||-----|Case1|-----|Case2|-|Case1+2|----

  9. ruby-on-rails - Ruby:如何在 Ruby 中读取包含两个 header 的 CSV 文件? - 2

    我有一个“.CSV”文件,我正尝试在ruby​​中使用CSV对其进行解析。该文件虽然有两行标题,但我以前从未遇到过这种情况,也不知道如何处理。以下是标题和行的示例。第1行"InstitutionID","Institution","GameDate","UniformNumber","LastName","FirstName","Rushing","","","","","Passing","","","","","","TotalOff.","","Receiving","","","PassInt","","","FumbleRet","","","Punting","","Pun

  10. arrays - 在两个数组中查找不相交元素的有效方法是什么? - 2

    我有以下数组:A=[1,2,3,4,5]B=[2,6,7,1]我想找到不相交的元素,如下:output=[3,4,5,6,7]我是这样实现的,output=A+B-(A&B)但它效率低下,因为我添加了两个数组,然后删除了公共(public)元素。它类似于查找不相交的元素。我能做得比这更好吗?如果是,怎么办? 最佳答案 如何只选择A中的元素而不是B中的元素以及B中的元素而不是A中的元素。(A-B)+(B-A) 关于arrays-在两个数组中查找不相交元素的有效方法是什么?,我们在Stack

随机推荐