好久不写技术文章了,以前写了好多,发现都找不到了,不知道是不是某次ITeye的更新导致的,还是我自己喝多了删除了。ALS(Alternating Least Squares)交替的最小二乘法,网络上太多的文章去介绍了,同时,在电商领域也有非常多的实践,根本不算是什么新鲜的玩意儿。本身就是利用和用户的有效互动,然后通过内容(太喜欢内容这个词了,包含了一切的含义,内容可以是具体的商品,可以是一篇讨论性的文章,可以是一则广告,可以是一段视频;我们在商业环境下,把一切可以用来探知用户兴趣及与用户产生互动的一切载体都称之为内容,内容是我们了解用户的开始,也是构建用户体系完成用户运营的根本。)ALS只是我们CF算法中的一种方式而已,没有什么特别的。
上面这个公式,就是要找到用户对隐藏特征的偏好与产品包含隐藏特征的程度的最优匹配结果。目标函数中U和V相互作用,先假设U的初始值U(0),这样就将问题转化成了一个最小二乘问题,可以根据U(0)可以计算出V(0),再根据V(0)计算出U(1),反复进行迭代,直到该函数收敛,
关于函数f(x)在点x0处的收敛定义。对于任意实数b>0,存在c>0,对任意x1,x2满足0<|x1-x0|<c,0<|x2-x0|<c,有|f(x1)-f(x2)|<b。
其实就是是指会聚于一点,向某一值靠近。本身还是最小二乘法,一个利用线性方式解决问题的模式。这张图是我能找到的说的最明白的,也是为啥要求解函数E = |y1-y’1|+|y2-y’2|+...+|yn-y’n|的最小值的原因。
- 简单,算法简单,实现简单,团队接受度高,学习曲线平缓(给团队选技术的第一要务)
- 因为与用户之间可能是正反馈,可能负反馈,可能没有反馈,所以会选
- 前期开发时,对样本量的要求没有这么苛刻,当然需要架构师有对领域的深度认知,冷启动过程几乎没有
当然这需要感谢Spark MLlib的作用。我们要实现的场景非常简单,就是给用户发内容,看用户点选路径,收集用户使用行为信息,并根据过往的同类型用户的使用情况进行内容推荐。
为了让团队能够更有目标感,选用了以下基本的技术:
HBase(存储用户的行为信息,商品标签,用户标签), Kafka(消息队列),Zookeeper做了HBase/Kafka的集群,Spark(计算引擎),Redis(匹配结果),Mysql(基本数据存储,比如商品/用户)
大概的模式就是这么简单:
贴段代码看看评分是如何工作的:
import org.apache.spark.ml.evaluation.RegressionEvaluator
import org.apache.spark.ml.recommendation.ALS
case class Rating(userId: Int, movieId: Int, rating: Float, timestamp: Long)
def parseRating(str: String): Rating = {
val fields = str.split("::")
assert(fields.size == 4)
Rating(fields(0).toInt, fields(1).toInt, fields(2).toFloat, fields(3).toLong)
}
val ratings = spark.read.textFile("data/mllib/als/prd_sample.txt")
.map(parseRating)
.toDF()
val Array(training, test) = ratings.randomSplit(Array(0.9, 0.2))
// Build the recommendation model using ALS on the training data
val als = new ALS()
.setMaxIter(5)
.setRegParam(0.01)
.setUserCol("userId")
.setItemCol("prdId")
.setRatingCol("rating")
val model = als.fit(training)
// Evaluate the model by computing the RMSE on the test data
// Note we set cold start strategy to 'drop' to ensure we don't get NaN evaluation metrics
model.setColdStartStrategy("drop")
val predictions = model.transform(test)
val evaluator = new RegressionEvaluator()
.setMetricName("rmse")
.setLabelCol("rating")
.setPredictionCol("prediction")
val rmse = evaluator.evaluate(predictions)
println(s"Root-mean-square error = $rmse")
// Generate top 10 prd(whatever it is) for each user
val userRecs = model.recommendForAllUsers(10)
// Generate top 10 user recommendations for each prd
val prdRecs = model.recommendForAllItems(10)
// Generate top 10 prd recommendations for a specified set of users
val users = ratings.select(als.getUserCol).distinct().limit(3)
val userSubsetRecs = model.recommendForUserSubset(users, 10)
// Generate top 10 user recommendations for a specified set of prd
val prdlist = ratings.select(als.getItemCol).distinct().limit(3)
val prdSubSetRecs = model.recommendForItemSubset(prdlist, 10)
上文提到的关于隐式反馈的设置,只需要“setImplicitPrefs(true)”即可。如果针对具体的预测,可以按照如下去做:
val user_product = trainingRDD.map {
case Rating(user, product, rate) =>
(user, product)
}
val predictions =
alsModel.predict(user_product).map {
case Rating(user, product, rate) =>
((user, product), rate)
}
其中,ComputeService就是记录了用户的正负反馈(没有反馈的相应的行为权重比负反馈还要低,因为被认为是根本不关注)。实际使用过程中,Redis中的所有匹配结果,都会记录到我们的Hive中,因为很多的关键性指标都需要在Hive中进行存储和计算,还是那句话,推荐算法早就已经有了,所有的数字化媒体或电商都在用,一点不新鲜,但是每周的针对推荐结果的复盘才让这些工具更有价值。HBase二级索引支持不好我们有Phoenix,Spark对实时事件处理不了我们还有Flink,其实重点的工作落在了用户的属性,内容的属性标签的拆分上,内容可能标准化程度更高一些,但是人的个性化就太多了。由于涉及商业项目的具体设计和实现,在这里不写这么多了;实际过程中以下两个过程极为重要:
- 召回阶段:用些成本低、易实现、速度快的模型进行初步筛选;
- 排序阶段:用更全面的数据、更精细的特征、更复杂的模型进行精挑细选
特别注意人工规则的植入,我们的ComputeService里面会放入人工规则的排序帮助作出更好的推荐判断。
小结一下,ALS的使用过程,对训练集合,特征值,迭代的参数设定及防过拟合参数都有要求(我们需要假设,假设是一切数据期望的前提,但是别使假设变得过度严格)。大年初一,还是祝大家新春快乐,万事可期!