小编说:时间衰变算法在很多行业都会被应用,就像电商行业,在给用户推荐商品时,会分析用户对于平台商品的兴趣偏好度,同时这个兴趣偏好度也会随着时间的流逝而发生变化。 本文选自《轻松学大数据挖掘:算法、场景与数据产品》
- 1 何为时间衰变
大家或许都听过一个故事——“遗忘曲线”。
遗忘曲线是由德国心理学家艾宾浩斯(Hermann Ebbinghaus )研究发现的,其描述了人类大脑对新事物遗忘的规律,人们可以从遗忘曲线中掌握遗忘规律并加以利用,从而提升自我记忆的能力。
人的记忆衰变过程,如图1所示。
图1 人的记忆衰变过程(来自百度百科)
这条曲线告诉人们在学习中的遗忘是有规律的,遗忘的进程很快,并且先快后慢。在分析用户对电商平台琳琅满目的商品的兴趣偏好的变化时,也可以借鉴遗忘曲线。
- 2 如何理解兴趣和偏好
对于兴趣和偏好这两个概念,从不同的角度分析会有不同的定义,这里从时间的角度分析。
兴趣:对事物喜好或关切的情绪,它是参与实践的基础,可以看作是一个短期行为。随着时间的推移,这种喜好也随之变化。
偏好:具有浓厚的积极情绪,伴随着成长过程中的主观倾向,可以看作是一个长期行为,它是基本稳定的。
对于电商平台来说,通过营销活动达到的目的是关心用户的“短期兴趣”。看看用户短期内会更倾向于购买哪些宝贝,从而更好地去做精准营销,如短信、站内广告等。
- 3 时间衰变算法的抽象
最简单的场景,如果用户在半年前购买过某件商品,但从此以后没有再次对其产生过任何行为(浏览、收藏、加入购物车和购买),那么用户对于该商品的兴趣衰变曲线如图2所示。
图2 单次行为的衰变曲线
重复行为是指用户在半年内针对同一商品多次浏览、收藏、购买该商品。
令t1、t2、t3表示3次相邻重复行为的时刻,用户兴趣度的衰变曲线如图3所示。
图3 用户兴趣度的衰变曲线
- 4 采用Spark实现模型
在分析用户的商品推荐时,我们会选择动手实践其中的熵权重算法和时间衰变算法,最终结合业务的实际场景重新组合一个综合模型。
1.数据源的获取
这里会考虑从HBase中读取数据源,具体数据特征会涉及用户ID、商品类目、宝贝、行为类型、次数和操作时间。Apache HBase经过长达8年的发展,在2017年1月中旬又发布了新版本(Hbase 1.3.0),多个方面的性能也得到了提升。
为了能够成功调用HBase的API,我们优先在Maven工程的pom.xml中添加如下代码。
代码语言:javascript复制<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中的表数据,代码如下。
代码语言:javascript复制/**
* 扫描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运行的代码模块,可以检测数据获取流程是否正常,代码如下。
代码语言:javascript复制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"))
}
最后补充整个工程相关的依赖包,代码如下。
代码语言:javascript复制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)确定算法过程中的统计指标,代码如下。
代码语言:javascript复制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
}
)
其中涉及的统计指标都会在后期的计算中用到,需要缓存在RDD中。
(2)确定指标的熵值,代码如下。
代码语言:javascript复制val resultSet = ArrayBuffer[Double]()
for(i <- 1 to featureNum) {
val sumAndCount = standDatas.map(_.apply(i-1)).stats()
val value=div(sumAndCount.sum,-math.log(sumAndCount.count),4)
resultSet = value
}
这步主要是计算每个特征向量的熵值大小,也是为计算最后的权重大小做准备的。
(3)确定特征向量的权重值,代码如下。
代码语言:javascript复制//确定每个特征向量的权重值
val weightSet = ArrayBuffer[Double]()
for(i <- 0 until featureNum){
weightSet =div(resultSet.apply(i),resultSet.sum,4)
}
weightSet.toArray
最终将计算出的用户行为权重单独保存在缓存Cache中,为了后期做兴趣衰变分析计算时可以再使用。
3.用户兴趣衰变的量化
结合上述的Hbase数据源和行为权重值,计算每个用户的兴趣衰变值,主要有以下两个步骤。
(1)计算用户兴趣衰变值,代码如下。
代码语言:javascript复制/*
* @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进行归一化处理,代码如下。
代码语言:javascript复制/*
* @describe: 对兴趣衰变进行归一化处理
* @param: decayValue为衰变的兴趣度,factorsigmoid为归一化参数
*/
def decayRate(decayValue:Double,factorsigmoid:Double):Double = {
round(1.0/(1 math.exp(3.0-factorsigmoid*decayValue)),4)
}
上述是对用户最终的兴趣值进行归一化的过程,得到用户对宝贝列表的兴趣排名,从而进行有针对性的推荐。和大家以往熟知的协同过滤推荐有所差异,基于用户兴趣偏好的衰变分析也可以做一定业务场景下的用户推荐。
数据化运营中的精准推荐涉及的业务场景很多,更多时候会从多面分析用户,甚至包括用户画像体系和商品画像体系。