时间衰变算法

Jessica瑾妞

2018-01-31

时间衰变算法在很多行业都会被应用,就像电商行业,在给用户推荐商品时,会分析用户对于平台商品的兴趣偏好度,同时这个兴趣偏好度也会随着时间的流逝而发生变化。

1 何为时间衰变

大家或许都听过一个故事——“遗忘曲线”。

遗忘曲线是由德国心理学家艾宾浩斯(Hermann Ebbinghaus )研究发现的,其描述了人类大脑对新事物遗忘的规律,人们可以从遗忘曲线中掌握遗忘规律并加以利用,从而提升自我记忆的能力。

人的记忆衰变过程,如图1所示。

这条曲线告诉人们在学习中的遗忘是有规律的,遗忘的进程很快,并且先快后慢。在分析用户对电商平台琳琅满目的商品的兴趣偏好的变化时,也可以借鉴遗忘曲线。

2 如何理解兴趣和偏好

对于兴趣和偏好这两个概念,从不同的角度分析会有不同的定义,这里从时间的角度分析。
兴趣:对事物喜好或关切的情绪,它是参与实践的基础,可以看作是一个短期行为。随着时间的推移,这种喜好也随之变化。

偏好:具有浓厚的积极情绪,伴随着成长过程中的主观倾向,可以看作是一个长期行为,它是基本稳定的。
对于电商平台来说,通过营销活动达到的目的是关心用户的“短期兴趣”。看看用户短期内会更倾向于购买哪些宝贝,从而更好地去做精准营销,如短信、站内广告等。

3 时间衰变算法的抽象

最简单的场景,如果用户在半年前购买过某件商品,但从此以后没有再次对其产生过任何行为(浏览、收藏、加入购物车和购买),那么用户对于该商品的兴趣衰变曲线如图2所示。

重复行为是指用户在半年内针对同一商品多次浏览、收藏、购买该商品。
令t1、t2、t3表示3次相邻重复行为的时刻,用户兴趣度的衰变曲线如图3所示。

伴随着每一次的行为调整新的初始衰变值和衰变速率。可以通过下面的函数表达式来进行更深入的理解。

最后结合Sigmoid函数对其进行归一化,确定用户对某商品的兴趣值。

基于遗忘曲线的兴趣度分析,它能够从侧面反映用户对有过“行为”的所有商品进行的兴趣排序,从而更好地进行商品推荐。

4 采用Spark实现模型

在分析用户的商品推荐时,我们会选择动手实践其中的熵权重算法和时间衰变算法,最终结合业务的实际场景重新组合一个综合模型。

1.数据源的获取

这里会考虑从HBase中读取数据源,具体数据特征会涉及用户ID、商品类目、宝贝、行为类型、次数和操作时间。Apache HBase经过长达8年的发展,在2017年1月中旬又发布了新版本(Hbase 1.3.0),多个方面的性能也得到了提升。

为了能够成功调用HBase的API,我们优先在Maven工程的pom.xml中添加如下代码。

<dependency>         
    <groupId>org.apache.hbase</groupId>         
    <artifactId>hbase-client</artifactId>         
    <version>1.2.4</version>
</dependency>
注:这里使用的是Hbase 1.2.4版本,可以在中央仓库mvnrepository中搜索hbase-client来进行选择。

接下来给出一个连接HBase的测试版本,检测是否能够成功获取HBase中的表数据,代码如下。

/**
  * 扫描rowkey返回行数
  *
  * @param prefixRowKey rowkey前缀
  * @return 行数
  */
def getRowPrefixNum(table: Table,prefixRowKey: String): Option[Int] = {
   var num = 1
   try {
       val scan: Scan = new Scan()
       scan.setStartRow(Bytes.toBytes(prefixRowKey))
       scan.setStopRow(Bytes.toBytes(prefixRowKey + "|"))
       val resultScanner: ResultScanner = table.getScanner(scan)
       val it = resultScanner.iterator()
       while (it.hasNext) {
         it.next()
         num += 1
       }
  resultScanner.close()
  table.close()
  Some(num)
  } catch {
       case e: Exception => logger.error("统计行数出错,{}",e.getMessage)
       table.close()
       Some(1)
   }
}

main运行的代码模块,可以检测数据获取流程是否正常,代码如下。

def main(args: Array[String]): Unit = {
       val conf = HBaseConfiguration.create()
       conf.set(HConstants.ZOOKEEPER_CLIENT_PORT, "端口号")
       conf.set(HConstants.ZOOKEEPER_QUORUM, "data1,data2,data3")
       val connection= ConnectionFactory.createConnection(conf)
       val table=connection.getTable(TableName.valueOf("t_user"))
       val s=getRowPrefixNum(table,"rowkey")
       println(s.getOrElse("0"))
}

最后补充整个工程相关的依赖包,代码如下。

import org.slf4j.{Logger, LoggerFactory}
import org.apache.hadoop.hbase.{HBaseConfiguration, HConstants, TableName}
import org.apache.hadoop.hbase.client._
import org.apache.hadoop.hbase.util.Bytes

2.用户行为权重的调整

这里的数据输入来源于从HBase获取到的用户数据。优先选择用户行为的数据计算出5种行为(浏览、点击、收藏、加入购物车和购买)的权重值。

(1)确定算法过程中的统计指标,代码如下。

val standDatas = rdd.map(_.split(SEPARATOR0)).map(record =>
  {
    var str = ""
    for(i <- 1 until record.length) {
    //其中round为方法调用,保留4位有效数字
      val standValue = round((record(i).toDouble+1)/ 
                (indexMap.get(i).get._1+indexMap.get(i).get._2),4)
      str = str.concat((standValue*math.log(standValue)).toString).         concat(SEPARATOR0)
     }
     str.trim()
  }
).map(record => 
      {
       val arraySet = ArrayBuffer[Double]()
       for(i <- 0 until record.length) {
           arraySet+=record(i).toDouble
       }
       arraySet
       }
)

这步主要是计算每个特征向量的熵值大小,也是为计算最后的权重大小做准备的。

(3)确定特征向量的权重值,代码如下。

//确定每个特征向量的权重值
val weightSet = ArrayBuffer[Double]()
for(i <- 0 until featureNum){
    weightSet+=div(resultSet.apply(i),resultSet.sum,4)
}
weightSet.toArray

最终将计算出的用户行为权重单独保存在缓存Cache中,为了后期做兴趣衰变分析计算时可以再使用。

3.用户兴趣衰变的量化

结合上述的Hbase数据源和行为权重值,计算每个用户的兴趣衰变值,主要有以下两个步骤。

(1)计算用户兴趣衰变值,代码如下。

/*
 * @describe: 对兴趣衰变的计算
 * @param: behav为行为集,factor为衰变因子,weightSet为权重集
 */
  def decayAlgorithm(behav:String,factor:Double,weightSet:Map [String,Double]):Double={
    val behavSet = behav.split("_")
    val behavCategory = behavSet.apply(0)
    val behavDiff = behavSet.apply(1).toDouble
    val behavNum = behavSet.apply(2).toDouble

    val interestValue = math.exp(-factor * behavDiff)*behavNum
    behavCategory match {
      case "browse" => weightSet.get("browse").get*interestValue
      case "click" => weightSet.get("click").get*interestValue
      case "collect" => weightSet.get("collect").get*interestValue
      case "addCar" => weightSet.get("addCar").get*interestValue
      case "buy" => weightSet.get("buy").get*interestValue
    }
  }

在RDD中调用上述函数进行处理,计算用户兴趣随着时间的衰变。

(2)采用Sigmoid进行归一化处理,代码如下。

/*
 * @describe: 对兴趣衰变进行归一化处理
 * @param: decayValue为衰变的兴趣度,factorsigmoid为归一化参数
 */
  def decayRate(decayValue:Double,factorsigmoid:Double):Double = {
    round(1.0/(1+math.exp(3.0-factorsigmoid*decayValue)),4)

上述是对用户最终的兴趣值进行归一化的过程,得到用户对宝贝列表的兴趣排名,从而进行有针对性的推荐。和大家以往熟知的协同过滤推荐有所差异,基于用户兴趣偏好的衰变分析也可以做一定业务场景下的用户推荐。

数据化运营中的精准推荐涉及的业务场景很多,更多时候会从多面分析用户,甚至包括用户画像体系和商品画像体系。

本文选自《轻松学大数据挖掘》

读者评论

相关博文

  • 社区使用反馈专区

    陈晓猛 2016-10-04

    尊敬的博文视点用户您好: 欢迎您访问本站,您在本站点访问过程中遇到任何问题,均可以在本页留言,我们会根据您的意见和建议,对网站进行不断的优化和改进,给您带来更好的访问体验! 同时,您被采纳的意见和建议,管理员也会赠送您相应的积分...

    陈晓猛 2016-10-04
    5529 742 3 7
  • 迎战“双12”!《Unity3D实战核心技术详解》独家预售开启!

    陈晓猛 2016-12-05

    时隔一周,让大家时刻挂念的《Unity3D实战核心技术详解》终于开放预售啦! 这本书不仅满足了很多年轻人的学习欲望,并且与实际开发相结合,能够解决工作中真实遇到的问题。预售期间优惠多多,实在不容错过! Unity 3D实战核心技术详解 ...

    陈晓猛 2016-12-05
    3341 36 0 1
  • czk 2017-07-29
    6031 28 0 1