特征选择--信息熵(information entropy)--spark实现和优化

       准备了很久,终于开始决定开始写关于机器学习相关的文章。深刻体会刚刚涉足一个新领域时的那种茫然和不知所措,而后在各个大神的文章帮助下渐渐走出了自己的一条路。现在想以分享设计方案的方式回馈技术社区和技术分享平台。虽然,这些不一定是最优的设计方案,希望这些技术方案对正在开发中正在迷惑的你有所帮助。

      【背景】

       在spark的架构中MLlib的工具库非常的全面,几乎包含大部分的机器学习的算法和场景。但是有些组件包却没有实现或者实现了没有暴露出来,在这些场景下用户就不得不自己实现相关的工程包的封装。最近一段时间,将会逐步分享一系列的机器算法的spark工程实现,其中包含IV值计算和分箱计算。

      【概念准备】       

       今天打算分享的主题是信息熵(information gain),我们先来看一下百度词条对于信息熵的定义和设计背景。信息熵,是一个数学上颇为抽象的概念,在这里不妨把信息熵理解成某种特定信息的出现概率。信息量度量的是一个具体事件发生了所带来的信息,而熵则是在结果出来之前对可能产生的信息量的期望——考虑该随机变量的所有可能取值,即所有可能发生事件所带来的信息量的期望。

      【计算公式】

                     H(D)=-\sum_{k=1}^{K}p_{i}\log_{2}p_{i}

      【spark实现单列计算】

    df.cache()
    val count = df.count()
    val percentEtlPlan = col("group_count")/count
    val entropyEtlPlan = sum(-percentEtlPlan*log(percentEtlPlan)).alias("entropy")
    df.groupBy("a").count()
      .select(col("count").alias("group_count"))
      .select(entropyEtlPlan)
      .head().getAs[Double]("entropy")

       【优化】

         信息熵在我们项目中,主要目的是为了计算后期的信息增益和增益率做准备。是用来做特征筛选工具策略,在工程调用上会有大量的列需要计算信息熵。假如需要计算一万列的信息熵,即使一秒钟计算一列也需要三个多小时,更何况一秒钟也无法完成这些计算,这个时间消耗是无法接受的。所以后面对这个计算又做了多列批量计算的优化。根据上面的代码大家可以看出,计算一列需要两次的shuffle,一万列就需要两万次的shuffle。主要的优化点是考虑,多列计算时shuffle是否可以合并,在新的设计中规约了shuffle次数,以有限次shuffle来完成无数个列信息熵的计算。

/**
    * @desc 计算信息熵 
    * @param df
    * @param calCols 需要计算的列
    * @param dfRowNum 当前数据集总行数
    * @return
    */
  override def calculator(
     df: DataFrame, 
     calCols: Array[String],
     ycol: String, 
     dfRowNum: Double
   ): Map[String, Double] = {
   
    if (df.storageLevel == StorageLevel.NONE) {
      df.persist(StorageLevel.DISK_ONLY)
    }
    val result = if (dfRowNum == 0) {
      calCols.map(col => (col, 0.0D)).toMap
    } else {
      /** 将列分成多组,一次计算多列的信息熵 */
      val calculaAggColNum = PropertiesUtils.calculaAggColNum
      val colsGroup: Array[Array[String]] = calCols.sliding(calculaAggColNum, calculaAggColNum).toArray
      val result = colsGroup.map(cols => {
        /** 拼接数据行转列逻辑计划 */
        val expodeEtlPlan = explode(array(cols.map(colT => array(lit(colT), col(colT))): _*)).alias("arr")
        /** 拼接每组百分比的计算的逻辑计划 */
        val percent = col("count") / dfRowNum.toDouble
        /** 信息计算逻辑 */
        val percentMultLogPercent = -percent * log(percent)

        df.select(expodeEtlPlan)
          .select(col("arr").getItem(0).alias("key"), col("arr").getItem(1).alias("value"))
          .groupBy(col("key"), col("value"))
          .agg(count("key").alias("count"))
          .select(percentMultLogPercent.alias("percent"), col("key"))
          .groupBy("key")
          .agg(sum(col("percent")).alias("percent_sum"))
          .collect()
          .map(row => (row.getAs[String]("key"), row.getAs[Double]("percent_sum")))
          .toMap
      })
      if (result.isEmpty) Map[String, Double]() else result.reduce(_ ++ _)
    }
    val remainCols = (calCols.toSet -- result.keySet).map((_, 0.0D)).toMap
    result ++ remainCols
  }

   【设计思路】

    在计算多列的场景,需要将多列拼接成两列(列名,列值),如下

            

   后面按照group by(key,value)计算count,就可以计算出每列的每个值出现的频率。再做百分比转换以及取对数,最后加和。

【后记】

        可能会有人提出异议,在第一步将多列转换成两列的过程中,会不会导致行数暴增导致内存内存膨胀,并且在explode拼接过程中消耗大量时间。根据实验结果和spark的特性分析,以上问题不存在,因为不会存在只存放key和value的dataFrame的这个中间结果,只是逻辑上会存在这个表达。实际计算过程中,原始多列在shuffle过程中就开始计算count,只留下不重复的key,value,count三列。所以不会存在内存膨胀现象。我们一个计算批次(2亿条,7000列)的数据集上,同时计算信息熵的情况下才用到20分钟左右,并且计算过程中GC时间稳定,内存使用剧增不明显。

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
CEEMD分解是一种对非线性和非平稳信号进行分解的方法,通过将原始信号分解成多个固有模态函数(IMF)来获得信号的时频特征。IMF分量相关系数用于描述不同分量之间的相关性,通过计算不同IMF分量之间的相关系数可以了解不同分量之间的相互作用。信息熵是用于评估信号复杂度和不确定性的指标,用于描述信号的信息量和随机性。在CEEMD分解中,通过计算每个IMF分量的信息熵特征,可以揭示每个分量的信息量大小和对原始信号的重要性。 CEEMD分解通过迭代的方式将原始信号分解成多个IMF分量,每个分量都具有不同的频率和振幅。通过计算不同IMF分量之间的相关系数,可以了解不同分量之间的相关性。相关系数的取值范围为-1到1,数值越接近1表示两个分量正相关,数值越接近-1表示两个分量负相关,数值为0表示两个分量不相关。通过分析不同分量之间的相关系数,可以了解信号中不同频率和振幅的相互作用情况。 信息熵是一个用于度量信号复杂度和不确定性的指标。在CEEMD分解中,通过计算每个IMF分量的信息熵特征,可以获得每个分量的信息量大小。信息熵的计算方式是对每个分量的概率密度函数进行求解,其数值越大表示分量的复杂度和不确定性越高,数值越小表示分量的信息量越少。通过分析每个分量的信息熵特征,可以揭示每个分量对原始信号的贡献和重要性。 综上所述,CEEMD分解-IMF分量相关系数-信息熵特征三者相互关联,在CEEMD分解中,通过计算IMF分量之间的相关系数和每个分量的信息熵特征,可以了解信号的时频特征、不同分量之间的相关性以及每个分量对原始信号的贡献和重要性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值