在实际场景中,用户与评分对象的交互行为远不止单一的评分(rating),还包括例如点击、加购、购买、停留时长等多种行为。
而ALS算法通常只提供三个参数,user, item, rate。
通常有两种解决办法,一种是将多个行为通过权重的方式整合成一个rate;另一种是为每个item都构建一个ALS算法。
一、加权求和
1. 行为类型及权重分配
行为类型 | 权重示例 | 行为特点 |
---|---|---|
浏览/点击 | 1-3分 | 频次高,偏好信号弱 |
商品详情停留 | 2-5分 | 时长越长权重越高 |
加入购物车 | 5-8分 | 强购买意向信号 |
收藏商品 | 7-9分 | 长期兴趣信号 |
购买行为 | 10分 | 最强烈正向信号 |
退货行为 | -5分 | 负向信号 |
差评行为 | -3分 | 负向信号 |
2. 行为权重动态计算
public class BehaviorWeightCalculator {
public static double calculateWeight(String behaviorType, Map<String, Object> context) {
double baseWeight = 0;
// 基础权重
switch (behaviorType) {
case "view":
baseWeight = 1.0;
break;
case "detail_view":
// 停留时间越长权重越高(30秒为基准)
long duration = (long) context.getOrDefault("duration", 0L);
baseWeight = 2.0 + Math.min(duration / 30000.0, 3.0);
break;
case "add_to_cart":
baseWeight = 5.0;
break;
// 其他行为...
}
// 时间衰减(半衰期7天)
long daysPassed = (long) context.getOrDefault("daysPassed", 0L);
double decay = Math.pow(0.5, daysPassed / 7.0);
// 用户活跃度加权(活跃用户行为权重降低)
double activityLevel = (double) context.getOrDefault("activityLevel", 1.0);
double activityFactor = 1.0 / Math.log(1.0 + activityLevel);
return baseWeight * decay * activityFactor;
}
}
3. 综合评分计算
// 将用户对同一商品的不同行为合并为综合评分
public JavaRDD<Rating> aggregateBehaviors(JavaRDD<UserBehavior> behaviors) {
return behaviors
// 按(userId, itemId)分组
.groupBy(ub -> Tuple2.apply(ub.getUserId(), ub.getItemId()))
// 计算加权综合评分
.map(tuple -> {
int userId = tuple._1();
int itemId = tuple._2();
double totalScore = 0;
for (UserBehavior ub : tuple._2()) {
Map<String, Object> context = new HashMap<>();
context.put("duration", ub.getDuration());
context.put("daysPassed", ub.getDaysPassed());
totalScore += BehaviorWeightCalculator.calculateWeight(
ub.getBehaviorType(), context);
}
// 应用sigmoid函数压缩到(0,1)区间
double finalRating = 1 / (1 + Math.exp(-totalScore));
return new Rating(userId, itemId, (float) finalRating);
});
}
二、多行为矩阵分解
// 为每种行为类型创建单独的评分矩阵
public List<ALSModel> trainMultiBehaviorALS(List<JavaRDD<Rating>> behaviorRatings) {
List<ALSModel> models = new ArrayList<>();
for (JavaRDD<Rating> ratings : behaviorRatings) {
ALS als = new ALS()
.setRank(10)
.setMaxIter(10)
.setRegParam(0.1);
models.add(als.fit(ratings));
}
return models;
}
// 合并多个行为模型的预测结果
public JavaRDD<Rating> blendPredictions(List<ALSModel> models, double[] weights) {
// 实现模型预测结果加权融合
// ...
}
三、Spark ALS实现多行为推荐
1. 多行为数据加载
public Dataset<Row> loadMultiBehaviorData(SparkSession spark, String path) {
Dataset<Row> df = spark.read()
.option("header", "true")
.csv(path);
// 解析多种行为类型
df = df.selectExpr(
"cast(userId as int) as userId",
"cast(itemId as int) as itemId",
"cast(view_count as int) as views",
"cast(add_to_cart as int) as carts",
"cast(purchase as int) as purchases",
"cast(dwell_time as double) as dwellTime",
"cast(timestamp as long) as timestamp"
);
return df;
}
2. 行为数据转换
public Dataset<Row> transformBehaviors(Dataset<Row> behaviorData) {
// 定义UDF计算综合评分
spark.udf().register("calculate_rating",
(Integer views, Integer carts, Integer purchases, Double dwellTime) -> {
double score = views * 0.2 +
carts * 1.0 +
purchases * 3.0 +
Math.log1p(dwellTime) * 0.5;
return (float) (1 / (1 + Math.exp(-score)));
}, DataTypes.FloatType);
// 应用转换
return behaviorData.selectExpr(
"userId",
"itemId",
"calculate_rating(views, carts, purchases, dwellTime) as rating",
"timestamp"
);
}
3. 时间加权ALS实现
public ALSModel trainTimeWeightedALS(Dataset<Row> ratings) {
// 计算时间衰减权重
Dataset<Row> withWeights = ratings.withColumn("timeWeight",
functions.exp(functions.lit(-0.1).multiply(
functions.datediff(
functions.current_date(),
functions.to_date(functions.from_unixtime(functions.col("timestamp")))
));
// 创建自定义ALS模型(扩展原生ALS支持样本权重)
TimeWeightedALS als = new TimeWeightedALS()
.setRank(10)
.setMaxIter(15)
.setRegParam(0.1)
.setUserCol("userId")
.setItemCol("itemId")
.setRatingCol("rating")
.setWeightCol("timeWeight") // 使用时间衰减权重
.setColdStartStrategy("drop");
return als.fit(withWeights);
}
四、多行为推荐系统架构
1. 离线训练流程
2. 在线服务流程
五、多行为推荐优化技巧
1. 行为分层处理
// 对不同行为类型分层处理
public Map<String, Dataset<Row>> stratifyBehaviors(Dataset<Row> behaviors) {
Map<String, Dataset<Row>> stratified = new HashMap<>();
// 浏览行为(轻量级交互)
stratified.put("view", behaviors.filter("views > 0"));
// 转化行为(强购买意向)
stratified.put("conversion", behaviors.filter("carts > 0 OR purchases > 0"));
// 负反馈行为
stratified.put("negative", behaviors.filter("returns > 0 OR negative_reviews > 0"));
return stratified;
}
2. 注意力机制加权
// 使用注意力机制动态调整行为权重
public Dataset<Row> applyAttentionWeights(Dataset<Row> behaviors) {
// 加载预训练的注意力模型
AttentionModel attentionModel = AttentionModel.load("models/attention");
// 应用注意力权重
return attentionModel.transform(behaviors)
.withColumn("finalRating",
col("attentionWeight").multiply(col("baseRating")));
}
3. 多目标优化
// 同时优化点击率和转化率
public MultiObjectiveALS trainMultiObjective(List<Dataset<Row>> behaviorData) {
MultiObjectiveALS als = new MultiObjectiveALS()
.setRanks(10, 10)
.setMaxIter(10)
.setObjectives("ctr", "cvr");
return als.fit(behaviorData);
}
六、生产环境注意事项
-
行为数据时效性:
- 实时行为数据(最近1小时)使用流处理
- 短期行为(7天)使用增量更新
- 长期行为使用全量更新
-
异常行为处理:
// 过滤机器人行为 df = df.filter("not (userId in (select userId from bot_users))"); // 平滑极端值 df = df.withColumn("dwellTime", when(col("dwellTime") > 3600, 3600).otherwise(col("dwellTime")));
-
动态权重调整:
// 根据业务阶段调整行为权重 double purchaseWeight = getCurrentPurchaseWeight(); // 大促期间提高购买权重
-
A/B测试框架:
// 不同行为权重策略的A/B测试 if (userBucket == "A") { // 策略A:重视加购行为 weights = new double[]{0.2, 1.0, 0.8}; } else { // 策略B:重视浏览深度 weights = new double[]{0.5, 0.7, 0.3}; }