• Posts tagged "entropy"

Blog Archives

信息熵的时间度量模型muti

R的极客理想系列文章,涵盖了R的思想,使用,工具,创新等的一系列要点,以我个人的学习和体验去诠释R的强大。

R语言作为统计学一门语言,一直在小众领域闪耀着光芒。直到大数据的爆发,R语言变成了一门炙手可热的数据分析的利器。随着越来越多的工程背景的人的加入,R语言的社区在迅速扩大成长。现在已不仅仅是统计领域,教育,银行,电商,互联网….都在使用R语言。

要成为有理想的极客,我们不能停留在语法上,要掌握牢固的数学,概率,统计知识,同时还要有创新精神,把R语言发挥到各个领域。让我们一起动起来吧,开始R的极客理想。

关于作者:

  • 张丹(Conan), 程序员/Quant: Java,R,Nodejs
  • blog: http://blog.fens.me
  • email: bsspirit@gmail.com

转载请注明出处:
http://blog.fens.me/r-entropy-muti

前言

信息熵作为信息度量的基础,被广泛的算法模型所使用,比如决策树,熵值法等,基于信息熵的扩展我们可以做很多的事情。

本文将给大家介绍一个新模型思路,用信息熵来衡量具有时间特征的两随机变量的信息差。由Mark D. Scheuerell教授开发,所属美国西雅图西雅图国家海洋和大气管理局,国家海洋渔业局西北渔业科学中心,鱼类生态学部。

目录

  1. muti包介绍
  2. muti包函数定义
  3. muti包使用
  4. 实战案例

1. muti包介绍

muti包是用于计算两个离散随机变量之间的互信息(MI)的R包。 muti包设计目标,是考虑时间序列分析的情况下开发的,但没有任何方法可以将方法与时间索引本身联系起来。

muti项目的主页:https://mdscheuerell.github.io/muti/

熵是用来含量信息的单位,我在文章 用R语言实现信息度量 中已有详细介绍。那么,通过计算两随机变量的互信息,来估计一个变量中包含的另一个变量的信息量,可以理解为是两个变量之间协方差的非参数度量。

muti包中用来计算互信息的计算过程,包括以下几步:

第一步,计算信息熵H(x),H(y):

第二步,计算联合熵H(x,y):

第三步,计算互信息MI(x;y):

第四步,标准化互信息,映射到[0,1]区间:

开发环境所使用的系统环境

  • Win10 64bit
  • R: 3.4.2 x86_64-w64-mingw32/x64 b4bit

muti目前还没有发布到CRAN上面,需要通过devtools包从github上进行安装。安装操作如下。


> if(!require("devtools")) {
>  install.packages("devtools")
>  library("devtools")
> }
> devtools::install_github("mdscheuerell/muti")

2. muti包函数定义

muti包,有很强的专业性,没有太多的函数定义,函数都是为了实现模型的算法。

  • muti(),主函数,用于计算两个向量的互信息并画图输出
  • mutual_info(),计算互信息
  • newZ(),基于原始数据的重新采样创建新矢量。
  • symbolize(),将数字向量转换为符号向量。
  • transM(),估计用于计算互信息上的临界阈值的转换矩阵。
  • plotMI(),画图输出

2.1 muti()函数:
主函数,用于计算两个向量的互信息并画图输出。

函数定义:


muti(x, y, sym = TRUE, n_bins = NULL, normal = FALSE, lags = seq(-4, 4), mc = 100, alpha = 0.05)

参数列表:

  • x, 向量
  • y, 向量
  • sym,是否转换成符号向量,默认TRUE,
  • n_bins, 离散化方法,当sym=FALSE时, n_bins = ceiling(2 * length(x)^(1/3))
  • normal, 是否标准化,默认FALSE
  • lags, 滞后周期,默认为seq(-4, 4)
  • mc, 用于估计互信息的临界阈值的蒙特卡洛数量的模拟,默认100,
  • alpha, 用于定义临界阈值的取值点0.05

2.2 mutual_info()函数:
计算互信息,源函数的定义过于复杂,我重新进行的优化,使得结构简单易懂。


# 源函数
mutual_info <- function(su,normal) {
  ## function to calculate mutual information between 2 time series
  ## need to remove obs with NA's b/c they don't contribute to info
  su <- su[!apply(apply(su,1,is.na),2,any),]
  ## get number of bins
  bb <- max(su)
  ## get ts length
  TT <- dim(su)[1]
  ## init entropies
  H_s <- H_u <- H_su <- 0
  for(i in 1:bb) {
    ## calculate entropy for 1st series
    p_s <- sum(su[,1] == i)/TT
    if(p_s != 0) { H_s <- H_s - p_s*log(p_s, 2) }
    ## calculate entropy for 2nd series
    p_u <- sum(su[,2] == i)/TT
    if(p_u != 0) { H_u <- H_u - p_u*log(p_u, 2) }
    for(j in 1:bb) {
      ## calculate joint entropy
      p_su <- sum(su[,1]==i & su[,2]==j)/TT
      if(p_su != 0) { H_su <- H_su - p_su*log(p_su, 2) }
    } ## end j loop
  } ## end i loop
  ## calc mutual info
  MI <- H_s + H_u - H_su
  if(!normal) { return(MI) } else { return(MI/sqrt(H_s*H_u)) }
}

# 修改后的函数
library(philentropy)
mutual_info <- function(su,normal){
  n<-nrow(su)
  px<-table(su[,1])/n
  py<-table(su[,2])/n
  
  hX<-H(px)
  hY<-H(py)
  
  tf<-table(su[,1],su[,2])
  pXY<-prop.table(tf)
  hXY<-JE(pXY)
  
  MI<-hX+hY-hXY
  if(normal){
    return(MI/sqrt(hX * hY))
  }else{
    return(MI)
  }
}

2.3 symbolize()函数:
将数字向量转换为符号向量,同样源函数定义过于复杂,我重写了一下。


# 源函数
symbolize <- function(xy) {
  ## of interest and converts them from numeric to symbolic.
  ## check input for errors
  if(dim(xy)[2] != 2) {
    stop("xy must be an [n x 2] matrix \n\n", call.=FALSE)
  }
  ## get ts length
  TT <- dim(xy)[1]
  ## init su matrix
  su <- matrix(NA, TT, 2)
  ## convert to symbols
  ## loop over 2 vars
  for(i in 1:2) {
    for(t in 2:(TT-1)) {
      ## if xy NA, also assign NA to su
      if(any(is.na(xy[(t-1):(t+1),i]))) { su[t,i] <- NA }
      ## else get correct symbol
      else {
        if(xy[t,i] == xy[t-1,i] & xy[t,i] == xy[t+1,i]) {
          ## same
          su[t,i] <- 3
          }
        if(xy[t,i] > xy[t-1,i]) {
          ## peak
          if(xy[t,i] > xy[t+1,i]) { su[t,i] <- 5 }
          ## increase
          else { su[t,i] <- 4 }
        }
        if(xy[t,i] < xy[t-1,i]) {
          ## trough
          if(xy[t,i] < xy[t+1,i]) { su[t,i] <- 1 }
          ## decrease
          else { su[t,i] <- 2 }
        }
      } ## end else
    } ## end t loop
  } ## end i loop
  ## return su matrix
  return(su)
}

# 优化后的函数
symbolize<-function(xy){
  if(ncol(xy)!=2) {
    stop("xy must be an [n x 2] matrix \n\n", call. = FALSE)
  }
  
  idx<-2:(nrow(xy)-1)
  apply(xy,2,function(row){
    na<-which(is.na(row[idx]+row[idx-1]+row[idx+1]))
    n3<-which(row[idx]==row[idx-1] & row[idx]==row[idx+1])
    n5<-which(row[idx]>row[idx-1] & row[idx]>row[idx+1])
    n4<-which(row[idx]>row[idx-1] & row[idx]<=row[idx+1])
    n1<-which(row[idx]<row[idx-1] & row[idx]<row[idx+1])
    n2<-which(row[idx]<row[idx-1] & row[idx]>=row[idx+1])

    line<-rep(0,length(idx))
    line[na]<-NA
    line[n1]<-1
    line[n2]<-2
    line[n3]<-3
    line[n4]<-4
    line[n5]<-5
    c(NA,line,NA)
  })
}

2.4 transM()函数:
估计用于计算互信息上的临界阈值的转换矩阵,同样源函数定义过于复杂,我重写了一下。


# 源函数
transM <- function(x,n_bins) {
  ## helper function for estimating transition matrix used in
  ## creating resampled ts for the CI on mutual info
  ## replace NA with runif()
  x[is.na(x)] <- runif(length(x[is.na(x)]),min(x,na.rm=TRUE),max(x,na.rm=TRUE))
  ## length of ts
  tt <- length(x)
  ## get bins via slightly extended range
  bins <- seq(min(x)-0.001,max(x),length.out=n_bins+1)
  ## discretize ts
  hin <- vector("numeric",tt)
  for(b in 1:n_bins) {
    hin[x > bins[b] & x <= bins[b+1]] <- b
  }
  ## matrix of raw-x & discrete-x
  xn <- cbind(x,hin)
  ## transition matrix from bin-i to bin-j
  MM <- matrix(0,n_bins,n_bins)
  for(i in 1:n_bins) {
    for(j in 1:n_bins) {
      MM[i,j] <- sum(hin[-tt]==i & hin[-1]==j)
    }
    if(sum(MM[i,])>0) { MM[i,] <- MM[i,]/sum(MM[i,]) }
    else { MM[i,] <- 1/n_bins }
  }
  return(list(xn=xn,MM=MM))
} ## end function


# 优化后的函数
transM <- function(x,n_bins){
  x[is.na(x)] <- runif(length(x[is.na(x)]), min(x, na.rm = TRUE),max(x, na.rm = TRUE))
  n<-length(x)
  xn<-cbind(x,hin=cut(x,7))

  hin<-xn[,2]
  a<-data.frame(id=1:(n-1),a=hin[1:(n-1)])
  b<-data.frame(id=1:(n-1),b=hin[2:n])
  ab<-merge(a,b,by="id",all=TRUE)
  ab$v<-1
  abm<-aggregate(v~a+b,data=ab,sum)
  
  MM <- matrix(0, n_bins, n_bins)
  apply(abm,1,function(row){
    MM[row[1],row[2]]<<-row[3]
    invisible()
  })
  
  MN<-apply(MM,1,function(row){
    s<-sum(row)
    if(s>0){
      row/s
    }else{
      rep(1/n_bins,n_bins)
    }
  })
  MN<-t(MN) 
  return(list(xn = xn, MM = MN))
}

由于作者的代码,是以it的逻辑写的,其实并不太符合数据计算的逻辑,我用向量化代码对源代码进行了优化,使用算法逻辑更清晰一下,同时我也上传到了github:https://github.com/bsspirit/muti

3. muti包使用

接下来,就是对muti包的使用了,作者提供了一个实例给我们做参考。

3.1 数据的输入和输出
muti()函数,是muti包的主函数,调用muti()就完成了这个包的全部功能。

  • 输入,要求是2个数字型向量。
  • 输出,包括了2个部分,1.数据计算结果,2.可视化的结果。

新建一个程序

> set.seed(123)
> TT <- 30

# 创建两个数字型向量
> x1 <- rnorm(TT)
> y1 <- x1 + rnorm(TT)

# 计算互信息
> muti(x1, y1)
  lag MI_xy MI_tv
1  -4 0.238 0.595
2  -3 0.473 0.532
3  -2 0.420 0.577
4  -1 0.545 0.543
5   0 0.712 0.584
6   1 0.393 0.602
7   2 0.096 0.546
8   3 0.206 0.573
9   4 0.403 0.625

数据计算结果,包括3列:

  • lag: 滞后期,当lag=-1,length(x)=length(y)=26时,x取值[1:25],y取值[2:26];当lag=1,length(x)=length(y)=26时,x取值[2:26],y取值[1:25];
  • MI_xy: 互信息
  • MI_tv: 相应显着性阈值,用蒙特卡洛方法生成互信息,按从大到小排序后取1-alpha位置的值。


可视化的结果,包括3个图:

  • 上图,原始数据的x,y两个变量
  • 中图,符号转型后的x,y两个变量,把原始数据离散化为5个值,差异化更大
  • 下图,滞后期的互信息(实线)和相应显着性阈值(虚线),同数据计算结果的输出。

3.2 离散化处理

在计算信息熵时,muti包是将源数据进行了离散化处理,通过离散化进行分组,计算不同分组的概率矩阵,从而得出互信息。

muti包提供了2种离散化。

  • 符号化: 把连续型的数据,映射到5个值的上,分别对应peak(峰值), increasing(增加), same(没变),decreasing(减少),trough(低谷)。比如,向量c(1.1,2.1,3.3,1.2,3.1),不计算第一个值和最后一个值,第二个值2.1>第一个值1.1,第二个值2.1<第三个值3.3时,第二个值为增加。第2,3,4值,分别为c(“增加”,“峰值”,“低谷”)。
  • 分箱法: 等宽分箱,如果没有指定箱数,则根据莱斯规则计算,其中n = ceiling(2*length(x)^(1/3))。

4. 实战案例

利用muti的模型特点,我们取一组金融市场的数据进行分析,满足x和y是两个连续型变量,同时x和y之间有一些在金融市场上有一定的关系,通过分析x对y的确定性的贡献度来解释互信息值的合理性。

我们取中国A股市场上证综指(000001.SH)和工商银行(601398.SH)的每日收盘价的向前复权的数据,从2014-10-21到2018-12-31。工商银行是上证综指的权重股,所以让x=工商银行,y=上证综指,通过计算互信息来验证工商银行能够给上证综指带来多少确定性的信息增益。

整个案例部分的代码,有多处省略,并不影响阅读和分析。如果需要代码的朋友,请扫文章下面的二维码,请作者喝杯咖啡。

整理来xts类型的时间序列数据,数据格式如下:

// 省略部分代码
> head(stock,20)
         date X601398 X000001
1  2014-10-21    2.92 2339.66
2  2014-10-22    2.91 2326.55
3  2014-10-23    2.92 2302.42
4  2014-10-24    2.92 2302.28
5  2014-10-27    2.90 2290.44
6  2014-10-28    2.92 2337.87
7  2014-10-29    2.94 2373.03
8  2014-10-30    2.98 2391.08
9  2014-10-31    3.04 2420.18
10 2014-11-03    3.01 2430.03
11 2014-11-04    3.02 2430.68
12 2014-11-05    2.97 2419.25
13 2014-11-06    2.97 2425.86
14 2014-11-07    2.97 2418.17
15 2014-11-10    3.05 2473.67
16 2014-11-11    3.16 2469.67
17 2014-11-12    3.12 2494.48
18 2014-11-13    3.12 2485.61
19 2014-11-14    3.14 2478.82
20 2014-11-17    3.11 2474.01

3列数据,第一列是时间列,第二列是X601398人收盘价,第三列是X000001收盘价。从价格上看,2个数据集并不在同一个度量内,我们转化度量单位为每日收益率。

数据变型后,每日收益率结果如下:

> head(ret,20)
                X601398       X000001
2014-10-21           NA            NA
2014-10-22 -0.003424658 -5.603378e-03
2014-10-23  0.003436426 -1.037158e-02
2014-10-24  0.000000000 -6.080559e-05
2014-10-27 -0.006849315 -5.142728e-03
2014-10-28  0.006896552  2.070781e-02
2014-10-29  0.006849315  1.503933e-02
2014-10-30  0.013605442  7.606309e-03
2014-10-31  0.020134228  1.217023e-02
2014-11-03 -0.009868421  4.069945e-03
2014-11-04  0.003322259  2.674864e-04
2014-11-05 -0.016556291 -4.702388e-03
2014-11-06  0.000000000  2.732252e-03
2014-11-07  0.000000000 -3.170010e-03
2014-11-10  0.026936027  2.295124e-02
2014-11-11  0.036065574 -1.617031e-03
2014-11-12 -0.012658228  1.004588e-02
2014-11-13  0.000000000 -3.555851e-03
2014-11-14  0.006410256 -2.731724e-03
2014-11-17 -0.009554140 -1.940439e-03

然后,我们把每日收益率转换为累积收益率,让收益曲线为连续值,这样就构建好了,muti模型需要的输入数据。

> head(rdf,20)
                X601398       X000001
2014-10-22 -0.003424658 -0.0056033783
2014-10-23  0.000000000 -0.0159168426
2014-10-24  0.000000000 -0.0159766804
2014-10-27 -0.006849315 -0.0210372447
2014-10-28  0.000000000 -0.0007650684
2014-10-29  0.006849315  0.0142627561
2014-10-30  0.020547945  0.0219775523
2014-10-31  0.041095890  0.0344152569
2014-11-03  0.030821918  0.0386252703
2014-11-04  0.034246575  0.0389030885
2014-11-05  0.017123288  0.0340177633
2014-11-06  0.017123288  0.0368429601
2014-11-07  0.017123288  0.0335561577
2014-11-10  0.044520548  0.0572775531
2014-11-11  0.082191781  0.0555679030
2014-11-12  0.068493151  0.0661720079
2014-11-13  0.068493151  0.0623808588
2014-11-14  0.075342466  0.0594787277
2014-11-17  0.065068493  0.0574228734
2014-11-18  0.058219178  0.0498833164

取前50个点,把x和y带入muti()函数。


> x<-rdf$X601398
> y<-rdf$X000001
> result<-muti(x,y)
> result
  lag MI_xy MI_tv
1  -4 0.148 0.534
2  -3 0.158 0.464
3  -2 0.190 0.385
4  -1 0.351 0.425
5   0 0.646 0.398
6   1 0.528 0.340
7   2 0.262 0.461
8   3 0.207 0.437
9   4 0.260 0.518

可视化输出:

  • 上图,为原始数据的x(上证综指)和y(工商银行)的累积收益率曲线。
  • 中图,为符号转型后的x(上证综指)和y(工商银行)的累积收益率曲线。
  • 下图,符号转型后的数据,滞后期从-4到4的9个的互信息(实线)和和相应显着性阈值(虚线)。

当滞后期为lag=0时,MI互信息最大为0.646,表示工商银行给上证综指带来的信息是最确定的。而当有不同的滞后期时,MI互信息都为小,表示工商银行给上证综指的信息确定性变小了。

这个结果是符合金融市场的业务规则的。工商银行是上证综指的权重股,工商银行的当天股价的走势是会影响当天的上证综指的走势的,但是并不会影响上证综指前一天或第二天的走势,所以得到的结果是符合逻辑的。

另外,我们再引入一个统计变量进行验证。假设越2组数据越确定,那么2组数据的差值的标准差应该是越小,差异越大则标准差越大,然后再进行验证。

输出数据集为4列,最后一列增加了标准差(sd)。

// 省略部分代码
> rsdf
  lag MI_xy MI_tv       sd
1  -4 0.148 0.534 1.991886
2  -3 0.158 0.464 2.400721
3  -2 0.190 0.385 2.107189
4  -1 0.351 0.425 2.378768
5   0 0.646 0.398 1.477030
6   1 0.528 0.340 2.342482
7   2 0.262 0.461 1.997676
8   3 0.207 0.437 2.267211
9   4 0.260 0.518 2.116934

从数据可以看出,第5行当lag=0时,sd=1.477是最小的,说明这个时候,2组数据的差异是最小的,而其他的时候sd都比较大。我们再有可视化进行输出。

图中有3条线,黑实线为互信息,灰色线为阈值,红色线为标准差。这样就更直观地看出,标准化的曲线和互信息的曲线刚好相反。当差异越小时,标准差越小,互信息越大,x对y的确定性越大。这样就证明了,muti包也是适合用在金融市场的模型。

虽然本案例没有使用模型构建策略生成交易信号,但论证了模型的可行性和正确性。大家可以基于文章的思路,找到适合的场景进行策略应用,比如跨期套利或跨品种套利时,2种不同合约之间的确定性的依赖程度,来形成交易策略。

我们完成对muti包的解读,使用,测试,验证的全过程。通过新的模型思路,获得新的知识,才能在金融市场上有更多的机会,从而突破信息相同,技术工具相同等的白热化竞争环境。

整个案例部分的代码,有多处省略,并不影响阅读和分析。如果需要代码的朋友,请扫文章下面的二维码,请作者喝杯咖啡。

转载请注明出处:
http://blog.fens.me/r-entropy-muti

打赏作者

用R语言实现信息度量

R的极客理想系列文章,涵盖了R的思想,使用,工具,创新等的一系列要点,以我个人的学习和体验去诠释R的强大。

R语言作为统计学一门语言,一直在小众领域闪耀着光芒。直到大数据的爆发,R语言变成了一门炙手可热的数据分析的利器。随着越来越多的工程背景的人的加入,R语言的社区在迅速扩大成长。现在已不仅仅是统计领域,教育,银行,电商,互联网….都在使用R语言。

要成为有理想的极客,我们不能停留在语法上,要掌握牢固的数学,概率,统计知识,同时还要有创新精神,把R语言发挥到各个领域。让我们一起动起来吧,开始R的极客理想。

关于作者:

  • 张丹(Conan), 程序员/Quant: Java,R,Nodejs
  • blog: http://blog.fens.me
  • email: bsspirit@gmail.com

转载请注明出处:
http://blog.fens.me/r-entropy

前言

香农的《通信的数学理论》是20世纪非常伟大的著作,被认为是现代信息论研究的开端。信息论定义了信息熵,用于把信息进行度量,以比特(bit)作为量纲单位,为如今发达的信息产业和互联网产业奠定了基础。本文接上一篇文章R语言实现46种距离算法,继续philentropy包的介绍,包括信息度量函数的使用。

目录

  1. 信息熵介绍
  2. 关键概念
  3. 信息度量函数
  4. 应用举例

1.信息熵介绍

信息论(Information Theory)是概率论与数理统计的一个分枝,用于研究信息处理、信息熵、通信系统、数据传输、率失真理论、密码学、信噪比、数据压缩等问题的应用数学学科。信息论将信息的传递作为一种统计现象来考虑,给出了估算通信信道容量的方法。信息传输和信息压缩是信息论研究中的两大领域。

香农被称为是“信息论之父”,香农于1948年10月发表的A Mathematical Theory of Communication,通信的数学理论(中文版),通常被认为是现代信息论研究的开端。

信息熵,是对信息随机性的量度,又指信息能被压缩的极限,用bit作为衡量信息的最小单位。一切信息所包含的信息量,都是1bit的正整数倍。计算机系统中常采用二进制编码,一个0或1就是1bit。

举例来说明一下信息熵的计算原理,假设小明最喜欢5种水果,苹果、香蕉、西瓜、草莓、樱桃中的一种,如果小明没有偏爱,选择每种水果的概率都是20%,那么这一信息的信息熵为

H(A) = -1*(0.2*log2(0.2)*5)
= 2.321928 bits

如果小明偏爱香蕉,选择这5种水果的概率分别是10%,20%,45%,15%,10%,那么这一信息信息熵为


H(B)=-1*(0.1*log2(0.1)+0.2*log2(0.2)+0.45*log2(0.45)+0.15*log2(0.15)+0.1*log2(0.1))
= 2.057717 bits

从结果得到H(A)大于H(B),信息熵越大表示越不确定。对于B的情况,对某一种水果的偏好,比A增加了确定性的因素,所以H(B)小于H(A)是符合对于信息熵的定义的。

2. 关键概念

我们从一幅图来认识信息熵,图中显示了随机变量X和Y的2个集合,在信息熵的概念里的所有可能逻辑关系。两个圆所包含的面积为联合熵H(X,Y), 左边的整个圆表示X的熵H(X),左边半圆是条件熵H(X|Y)。 右边的整个圆表示Y的熵H(Y),右边半圆条件熵H(Y|X),中间交集的部分是互信息I(X; Y)

信息熵(Entropy):是对信息随机性的量度,用于计算信息能被压缩的极限。对随机变量X,不确定性越大,X的信息熵H(X)也就越大。

公式定义:

H(x)的取值范围,0<=H(x)<=log(n), 其中n是随机变量x取值的种类数。需要注意的是,熵只依赖于随机变量的分布,与随机变量取值无关。

条件熵(Conditional Entropy):表示两个随机变量X和Y,在已知Y的情况下对随机变量X的不确定性,称之为条件熵H(X|Y),

公式定义:

联合熵(Joint Entropy):表示为两个随机事件X和Y的熵的并集,联合熵解决将一维随机变量分布推广到多维随机变量分布。

公式定义:

互信息(Mutual Information, 信息增益):两个随机变量X和Y,Y对X的互信息,为后验概率与先验概率比值的对数,即原始的熵H(X)和已知Y的情况下的条件熵H(X|Y)的比值的对数,信息增益越大表示条件Y对于确定性的贡献越大。互信息,也可以用来衡量相似性。

公式定义:

当MI(X,Y)=0时,表示两个事件X和Y完全不相关。决策树ID3算法就是使用信息增益来划分特征,信息增益大时,说明对数据划分帮助很大,优先选择该特征进行决策树的划分。

信息增益比率:是信息增益与该特征的信息熵之比,用于解决信息增益对多维度特征的选择,决策树C4.5算法使用信息增益比率进行特征划分。

KL散度(Kullback–Leibler Divergence, 相对熵):随机变量x取值的两个概率分布p和q,用来衡量这2个分布的差异,通常用p表示真实分布,用q表示预测分布。

公式定义:

n为事件的所有可能性,如果两个分布完全相同,那么它们的相关熵为0。如果相对熵KL越大,说明它们之间的差异越大,反之相对熵KL越小,说明它们之间的差异越小。

交叉熵(Cross Entropy):是对KL散度的一种变型,把KL散度log(p(x)/q(x))进行拆分,前面部分就是p的熵H(p),后面就是交叉熵H(p,q)。

公式定义:

交叉熵可以用来计算学习模型分布与训练分布之间的差异,一般在机器学习中直接用交叉熵做损失函数,用于评估模型。

信息论是通信理论的基础,也是xx的基础,关于信息论的理论,等后面有时时间再做分享,本文重要研究信息熵的函数计算问题。

3. 信息度量函数

philentropy包的函数,主要分为3种类别的函数,第一类是距离测量的函数,第二类是相关性分析,第三类是信息度量函数,本文重点介绍这些信息度量的函数。有关于距离测量函数和相关性分析函数,请参考文章R语言实现46种距离算法

我们来看一下,philentropy包里信息度量的函数:

  • H(): 香农熵, Shannon’s Entropy H(X)
  • JE() : 联合熵, Joint-Entropy H(X,Y)
  • CE() : 条件熵, Conditional-Entropy H(X|Y)
  • MI() : 互信息, Shannon’s Mutual Information I(X,Y)
  • KL() : KL散度, Kullback–Leibler Divergence
  • JSD() : JS散度,Jensen-Shannon Divergence
  • gJSD() : 通用JS散度,Generalized Jensen-Shannon Divergence

本文的系统环境为:

  • Win10 64bit
  • R: 3.4.2 x86_64-w64-mingw32

3.1 H()香农熵
H()函数,可用于快速计算任何给定概率向量的香农熵。

H()函数定义:

H (x, unit = "log2") 

参数列表:

  • x, 概率向量
  • unit,对数化的单位,默认为log2

函数使用:


# 创建数据x
> x<-1:10;x
 [1]  1  2  3  4  5  6  7  8  9 10
> px<-x/sum(x);x1
 [1] 0.01818182 0.03636364 0.05454545 0.07272727
 [5] 0.09090909 0.10909091 0.12727273 0.14545455
 [9] 0.16363636 0.18181818

# 计算香农熵
> H(px)
[1] 3.103643

同样地,我们也可以用程序实现公式自己算一下。


# 创建数据x
> x<-1:10
#计算x的概率密度px
> px<-x/sum(x)  

# 根据公式计算香农熵
> -1*sum(px*log2(px))
[1] 3.103643

我们动手的计算结果,用于H()函数的计算结果是一致的。

3.2 CE()条件熵

CE()函数,基于给定的联合概率向量P(X,Y)和概率向量P(Y),根据公式 H(X|Y)= H(X,Y)-H(Y)计算香农的条件熵。

函数定义:

CE(xy, y, unit = "log2")

参数列表:

  • xy, 联合概率向量
  • y, 概率向量,必须是随机变量y的概率分布
  • unit,对数化的单位,默认为log2

函数使用:


> x3<- 1:10/sum(1:10)
> y3<- 30:40/sum(30:40)

# 计算条件熵
> CE(x3, y3)
[1] -0.3498852

3.3 JE()联合熵

JE()函数,基于给定的联合概率向量P(X,Y)计算香农的联合熵H(X,Y)。

JE()函数定义:

JE (x, unit = "log2") 

参数列表:

  • x, 联合概率向量
  • unit,对数化的单位,默认为log2

函数使用:

# 创建数据x
> x2 <- 1:100/sum(1:100)

# 联合熵
> JE(x2)
[1] 6.372236

3.4 MI()互信息
MI()函数,根据给定联合概率向量P(X,Y)、概率向量P(X)和概率向量P(X),按公式I(X,Y)= H(X)+ H(Y)-H(X,Y)计算。

函数定义:

MI(x, y, xy, unit = "log2")

参数列表:

  • x, 概率向量
  • x, 概率向量
  • xy, 联合概率向量
  • unit,对数化的单位,默认为log2

函数使用:


# 创建数据集
> x3 <- 1:10/sum(1:10)
> y3<- 20:29/sum(20:29)
> xy3 <- 1:10/sum(1:10)

# 计算互信息
> MI(x3, y3, xy3)
[1] 3.311973

3.5 KL()散度
KL()函数,计算两个概率分布P和Q的Kullback-Leibler散度。
函数定义:

KL(x, test.na = TRUE, unit = "log2", est.prob = NULL)

参数列表:

  • x, 概率向量或数据框
  • test.na, 是否检查NA值
  • unit,对数化的单位,默认为log2
  • est.prob, 用计数向量估计概率的方法,默认值NULL。

函数使用:


# 创建数据集
> df4 <- rbind(x3,y3);df4
         [,1]       [,2]       [,3]       [,4]       [,5]      [,6]      [,7]      [,8]      [,9]
x3 0.01818182 0.03636364 0.05454545 0.07272727 0.09090909 0.1090909 0.1272727 0.1454545 0.1636364
y3 0.08163265 0.08571429 0.08979592 0.09387755 0.09795918 0.1020408 0.1061224 0.1102041 0.1142857
       [,10]
x3 0.1818182
y3 0.1183673

# 计算KL散度 
> KL(df4, unit = "log2") # Default
kullback-leibler 
       0.1392629 
> KL(df4, unit = "log10")
kullback-leibler 
       0.0419223 
> KL(df4, unit = "log")
kullback-leibler 
      0.09652967 

3.5 JSD()散度

JSD()函数,基于具有相等权重的Jensen-Shannon散度,计算距离矩阵或距离值。

公式定义:

函数定义:

JSD(x, test.na = TRUE, unit = "log2", est.prob = NULL)

参数列表:

  • x, 概率向量或数据框
  • test.na, 是否检查NA值
  • unit, 对数化的单位,默认为log2
  • est.prob, 用计数向量估计概率的方法,默认值NULL。

# 创建数据
> x5 <- 1:10
> y5 <- 20:29
> df5 <- rbind(x5,y5)

# 计算JSD
> JSD(df5,unit='log2')
jensen-shannon 
      50.11323 
> JSD(df5,unit='log')
jensen-shannon 
      34.73585 
> JSD(df5,unit='log10')
jensen-shannon 
      15.08559 

# 计算JSD,满足est.prob
> JSD(df5, est.prob = "empirical")
jensen-shannon 
    0.03792749 

3.6 gJSD()散度

gJSD()函数,计算概率矩阵的广义Jensen-Shannon散度。

公式定义:

函数定义:

gJSD(x, unit = "log2", weights = NULL)

参数列表:

  • x, 概率矩阵
  • unit, 对数化的单位,默认为log2
  • weights, 指定x中每个值的权重,默认值NULL。

# 创建数据
> Prob <- rbind(1:10/sum(1:10), 20:29/sum(20:29), 30:39/sum(30:39))

# 计算gJSD
> gJSD(Prob)
[1] 0.023325

4. 应用举例

在我们了解了熵的公式原理和使用方法后,我们就可以做一个案例来试一下。我们定义一个场景的目标:通过用户的看书行为,预测用户是否爱玩游戏。通过我们一步一步地推倒,我们计算出熵,条件熵,联合熵,互信息等指标。

第一步,创建数据集为2列,X列用户看书的类型,包括旅游(Tourism)、美食(Food)、IT技术(IT),Y列用户是否喜欢打游戏,喜欢(Y),不喜欢(N)。


X,Y
Tourism,Y
Food,N
IT,Y
Tourism,N
Tourism,N
IT,Y
Food,N
Tourism,Y

第二步,建立联合概率矩阵,分别计算H(X),Y(X)。

XYNp(X)
Tourism2/8=0.252/8=0.250.25+0.25=0.5
Food0/8=02/8=0.250+0.25=0.25
IT2/8=0.250/8=00.25+0=0.25
p(Y)0.25+0+0.25=0.50.25+0.25+0=0.5

计算过程


# 分别计算每种情况的概率
p(X=Tourism) = 2/8 + 2/8 = 0.5
p(X=Food) = 2/8 + 0/8 = 0.25
p(X=IT) = 0/8 + 2/8 = 0.25
p(Y=Y) = 4/8 = 0.5
p(Y=N) = 4/8 = 0.5

# 计算H(X)
H(X) = -∑p(xi)*log2(p(xi)) 
 = -p(X=Tourism)*log2(p(X=Tourism) ) -p(X=Food)*log2(p(X=Food) ) -p(X=IT)*log2(p(X=IT) ) 
 = -0.5*log(0.5) -0.25*log(0.25) - 0.25*log(0.25)
 = 1.5

# 计算H(Y)
H(Y) = -∑p(yi)*log2(p(yi)) 
 = -p(Y=Y)*log2(p(Y=Y)) -p(Y=N)*log2(p(Y=N))
 = -0.5*log(0.5) -0.5*log(0.5)
 = 1

第三步,计算每一项的条件熵,H(Y|X=Tourism),H(Y|X=Food),H(Y|X=IT)。


H(Y|X=Tourism) = -p(Y|X=Tourism)*log(p(Y|X=Tourism)) - p(N|X=Tourism)*log(p(N|X=Tourism))
 = -0.5*log(0.5) -0.5*log(0.5)
 = 1

H(Y|X=Food) = -p(Y|X=Food)*log(p(Y|X=Food)) -p(N|X=Food)*log(p(N|X=Food))
 = -0*log(0) -1*log(1)
 = 0

H(Y|X=IT) = -p(Y|X=IT)*log(p(Y|X=IT)) -p(N|X=IT)*log(p(N|X=IT))
 = -1*log(1) -0*log(0) 
 = 0

第四步,计算条件熵H(Y|X)


H(Y|X) = ∑p(xi)*H(Y|xi)
 = p(X=Tourism)*H(Y|X=Tourism) + p(X=Food)*H(Y|X=Food) + p(X=IT)*H(Y|X=IT)
 = 0.5*1 + 0.25*0 + 0.25*0
 = 0.5

第五步,计算联合熵H(X,Y)


H(X,Y) = −∑p(x,y)log(p(x,y))
 = H(X) + H(Y|X)
 = 1.5 + 0.5
 = 2

第六步,计算互信息I(X;Y)


I(X;Y) = H(Y) - H(Y|X)  = 1 - 0.5 = 0.5
 = H(X) + H(Y) - H(X,Y) = 1.5 + 1 - 2 = 0.5

我们把上面的推到过程,用程序来实现一下。


# 创建数据集
> X<-c('Tourism','Food','IT','Tourism','Tourism','IT','Food','Tourism')
> Y<-c('Y','N','Y','N','N','Y','N','Y') 
> df<-cbind(X,Y);df
     X         Y  
[1,] "Tourism" "Y"
[2,] "Food"    "N"
[3,] "IT"      "Y"
[4,] "Tourism" "N"
[5,] "Tourism" "N"
[6,] "IT"      "Y"
[7,] "Food"    "N"
[8,] "Tourism" "Y

变型为频率矩阵


> tf<-table(df[,1],df[,2]);tf
         
          N Y
  Food    2 0
  IT      0 2
  Tourism 2 2

计算概率矩阵


> pX<-margin.table(tf,1)/margin.table(tf);pX
Tourism    Food      IT 
   0.50    0.25    0.25 
> pY<-margin.table(tf,2)/margin.table(tf);pY
  Y   N 
0.5 0.5 
> pXY<-prop.table(tf);pXY
           Y    N
Tourism 0.25 0.25
Food    0.00 0.25
IT      0.25 0.00

计算熵


> H(pX)
[1] 1.5
> H(pY)
[1] 1

# 条件熵 
> CE(pX,pY)
[1] 0.5

# 联合熵 
> JE(pXY)
[1] 2

# 互信息
> MI(pX,pY,pXY)
[1] 0.5

计算原理是复杂的,用R语言的程序实现却是很简单的,几行代码就搞定了,

本文只是对的信息论的初探,重点还是在信息度量方法的R语言实现。信息熵作为信息度量的基本方法,对各种主流的机器学习的算法都有支撑,是我们必须要掌握的知识。了解本质才能发挥数据科学的潜力,学习的路上不断积累和前进。

转载请注明出处:
http://blog.fens.me/r-entropy

打赏作者