1.Swing算法介绍

Swing算法原理比较简单,是阿里早期使用到的一种召回算法,在阿里多个业务被验证过非常有效的一种召回方式,它认为 user-item-user 的结构比 itemCF 的单边结构更稳定,截止目前并没有公开的论文进行介绍和说明(可能是因为比较简单,阿里看不上哈哈),但是根据网上的各种资料,对该算法的原理进行介绍,如有错误,欢迎指正。

Swing指的是秋千,例如用户  和用户  ,都购买过同一件商品,则三者之间会构成一个类似秋千的关系图。若用户  和用户  之间除了购买过  外,还购买过商品 ,则认为两件商品是具有某种程度上的相似的。

也就是说,商品与商品之间的相似关系,是通过用户关系来传递的。为了衡量物品  和  的相似性,考察都购买了物品  和  的用户  和用户  , 如果这两个用户共同购买的物品越少,则物品  和  的相似性越高。

Swing算法的表达式如下:

2.Swing Python实现

# -*- coding: utf-8 -*-"""Author  : ThinkgamerFile    : Swing.pySoftware: PyCharmDesc    : 基于movie lens数据集实现Swing算法
"""import pandas as pd
from itertools import combinations
import json
import osalpha = 0.5
top_k = 20def load_data(train_path, test_path):train_data = pd.read_csv(train_path, sep="\t", engine="python", names=["userid", "movieid", "rate", "event_timestamp"])test_data = pd.read_csv(test_path, sep="\t", engine="python", names=["userid", "movieid", "rate", "event_timestamp"])print(train_data.head(5))print(test_data.head(5))return train_data, test_datadef get_uitems_iusers(train):u_items = dict()i_users = dict()for index, row in train.iterrows():u_items.setdefault(row["userid"], set())i_users.setdefault(row["movieid"], set())u_items[row["userid"]].add(row["movieid"])i_users[row["movieid"]].add(row["userid"])print("使用的用户个数为:{}".format(len(u_items)))print("使用的item个数为:{}".format(len(i_users)))return u_items, i_usersdef cal_similarity(u_items, i_users):item_pairs = list(combinations(i_users.keys(), 2))print("item pairs length:{}".format(len(item_pairs))) # 1410360item_sim_dict = dict()cnt = 0for (i, j) in item_pairs:cnt += 1print(cnt)user_pairs = list(combinations(i_users[i] & i_users[j], 2))result = 0.0for (u, v) in user_pairs:result += 1 / (alpha + list(u_items[u] & u_items[v]).__len__())item_sim_dict.setdefault(i, dict())item_sim_dict[i][j] = result# print(item_sim_dict[i][j])return item_sim_dictdef save_item_sims(item_sim_dict, path):new_item_sim_dict = dict()for item, sim_items in item_sim_dict.items():new_item_sim_dict.setdefault(item, dict())new_item_sim_dict[item] = dict(sorted(sim_items.items(), key = lambda k:k[1], reverse=True)[:top_k])json.dump(new_item_sim_dict, open(path, "w"))print("item 相似 item({})保存成功!".format(top_k))return new_item_sim_dictdef evaluate(item_sim_dict, test):# 可以参考《推荐系统开发实战》中的cf验证方式passif __name__ == "__main__":train_data_path = "../../data/ml-100k/ua.base"test_data_path = "../../data/ml-100k/ua.test"item_sim_save_path = "../../model/swing/item_sim_dict.json"train, test = load_data(train_data_path, test_data_path)if not os.path.exists(item_sim_save_path):u_items, i_users = get_uitems_iusers(train)item_sim_dict = cal_similarity(u_items, i_users)new_item_sim_dict = save_item_sims(item_sim_dict, item_sim_save_path)else:new_item_sim_dict = json.load(open(item_sim_save_path, "r"))evaluate(new_item_sim_dict, test)

3.Swing Spark实现

创建Swing类,其中的评估函数和predict函数这里并未提供,感兴趣的可以自己实现

/*** @ClassName: Swing* @Description: 实现Swing算法* @author: Thinkgamer**/class SwingModel(spark: SparkSession) extends Serializable{var alpha: Option[Double] = Option(0.0)var items: Option[ArrayBuffer[String]] = Option(new ArrayBuffer[String]())var userInterpMap: Option[Map[String, Map[String, Int]]] = Option(Map[String, Map[String, Int]]())/** @Description 给参数 alpha赋值* @Param double* @return cf.SwingModel**/def setAlpha(alpha: Double): SwingModel = {this.alpha = Option(alpha)this}/** @Description 给所有的item进行赋值* @Param [array]* @return cf.SwingModel**/def setAllItems(array: Array[String]): SwingModel = {this.items = Option(array.toBuffer.asInstanceOf[ArrayBuffer[String]])this}/** @Description 获取两两用户有行为的item交集个数* @Param [spark, data]* @return scala.collection.immutable.Map<java.lang.String,scala.collection.immutable.Map<java.lang.String,java.lang.Object>>**/def calUserRateItemInterp(data: RDD[(String, String, Double)]): Map[String, Map[String, Int]] = {val rdd = data.map(l => (l._1, l._2)).groupByKey().map(l => (l._1, l._2.toSet))val map = (rdd cartesian rdd).map(l => (l._1._1, (l._2._1, (l._1._2 & l._2._2).toArray.length))).groupByKey().map(l => (l._1, l._2.toMap)).collectAsMap().toMapmap.take(10).foreach(println)map}def fit(data: RDD[(String, String, Double)]): RDD[(String, String, Double)]= {this.userInterpMap = Option(this.calUserRateItemInterp(data))println(this.userInterpMap.take(10))val rdd = data.map(l => (l._2, l._1)).groupByKey().map(l => (l._1, l._2.toSet))val result: RDD[(String, String, Double)] = (rdd cartesian rdd).map(l => {val item1 = l._1._1val item2 = l._2._1val interpUsers = l._1._2 & l._2._2var score = 0.0for(u1 <- interpUsers){for(u2 <- interpUsers){score += 1.0 / (this.userInterpMap.get.get(u1).get(u2).toDouble + this.alpha.get)}}(item1, item2, score) // (item1, item2, swingsocre)})result}def evalute(test: RDD[(String, String, Double)]) = { }def predict(userid: String) = { }def predict(userids: Array[String]) = { }}

main函数调用

object Swing {def main(args: Array[String]): Unit = {val spark = SparkSession.builder().master("local[10]").appName("Swing").enableHiveSupport().getOrCreate()Logger.getRootLogger.setLevel(Level.WARN)val trainDataPath = "data/ml-100k/ua.base"val testDataPath = "data/ml-100k/ua.test"import spark.sqlContext.implicits._val train: RDD[(String, String, Double)] = spark.sparkContext.textFile(trainDataPath).map(_.split("\t")).map(l => (l(0), l(1), l(2).toDouble))val test: RDD[(String, String, Double)] = spark.sparkContext.textFile(testDataPath).map(_.split("\t")).map(l => (l(0), l(1), l(2).toDouble))val items: Array[String] = train.map(_._2).collect()val swing = new SwingModel(spark).setAlpha(1).setAllItems(items)val itemSims: RDD[(String, String, Double)] = swing.fit(train)swing.evalute(test)swing.predict("")swing.predict(Array("", ""))spark.close()}
}

4.Swing在阿里飞猪的应用

航旅用户的行为有稀疏和发散的特点。利用右图一个具体的用户实例来说明这两个特点:用户在第一天点击了两个大理一日游,第 20 天点击了一些马尔代夫蜜月相关的商品,第 21 天又点击了大理的一日游。稀疏性体现在一个月只来了 3 次,点击了 8 个宝贝。发散性体现在用户大理一日游和出国蜜月游两个 topic 感兴趣。

在用户有行为的情况下进行召回,我们常采用的方法是基于 User-Rate 矩阵的协同过滤方法 ( 如 ItemCF,Swing。ItemCF 认为同时点击两个商品的用户越多则这两个商品越相似。Swing 是在阿里多个业务被验证过非常有效的一种召回方式,它认为 user-item-user 的结构比 itemCF 的单边结构更稳定 ),但是由于航旅用户行为稀疏,基于 User-Rate 矩阵召回结果的准确率比较低,泛化性差。针对这两个问题我们可以通过扩充历史数据来增加样本覆盖。航旅场景因为用户点击数据比较稀疏,需要比电商 ( 淘宝 ) 扩充更多 ( 时间更长 ) 的数据才够。这又带来了兴趣点转移多的问题。在这里我们采用对行为序列进行 session 划分,保证相关性。

这里以 swing 为例讲解一下构造约束的方式。我们以用户的行为意图为中心,将表示共同意图的商品聚合在一个序列中,如上图对用户行为序列的切分。

在这个 case 中,上面是传统 swing 的召回结果,下面是基于 session 的召回结果。当 trigger 是沙溪古镇一日游的时候,上面有一个杭州莫干山和玉龙雪山一日游,这两个不相关结果的出现是因为它们是热门商品,也称哈利波特效应。下面的召回结果就都是和沙溪古镇相关的了。从指标来看,session-based 召回比 swing 和 itemCF 都高。


参考:

  • https://zhuanlan.zhihu.com/p/67126386

  • https://www.infoq.cn/article/qfl1nxcxhuxv723imb7v

更多干货请点击:

2021年3月热门报告盘点(附下载链接)某视频APP推荐策略详细拆解(万字长文)哔哩哔哩推荐策略分析与参考华为到底在研发怎样的核心技术?

2020年轻人性和爱调查报告.pdf(附下载链接)

全网最全数字化资料包(附下载链接)

短视频直播资料包(附下载链接)

关注我们

省时查报告

专业、及时、全面的行研报告库

长按并识别关注

Spark推荐实战系列之Swing算法介绍、实现与在阿里飞猪的实战应用(附代码)相关推荐

  1. Swing算法介绍、实现与在阿里飞猪的实战应用

    本系列主要是基于Spark的推荐算法实战系列,本文为首篇,欢迎关注! 1.Swing算法介绍 Swing算法原理比较简单,是阿里早期使用到的一种召回算法,在阿里多个业务被验证过非常有效的一种召回方式, ...

  2. Spark推荐系列之Word2vec算法介绍、实现和应用说明

    Spark推荐实战系列目前已经更新: Spark推荐实战系列之Swing算法介绍.实现与在阿里飞猪的实战应用 Spark推荐实战系列之ALS算法实现分析 Spark中如何使用矩阵运算间接实现i2i F ...

  3. 推荐系统[八]算法实践总结V0:淘宝逛逛and阿里飞猪个性化推荐:召回算法实践总结【冷启动召回、复购召回、用户行为召回等算法实战】

    搜索推荐系统专栏简介:搜索推荐全流程讲解(召回粗排精排重排混排).系统架构.常见问题.算法项目实战总结.技术细节以及项目实战(含码源) 专栏详细介绍:搜索推荐系统专栏简介:搜索推荐全流程讲解(召回粗排 ...

  4. 阿里手淘猜你喜欢Swing算法介绍

    Swing算法原理比较简单,是阿里早期使用到的一种召回算法,在阿里多个业务被验证过非常有效的一种召回方式,它认为 user-item-user 的结构比 itemCF 的单边结构更稳定. Swing指 ...

  5. 推荐业务多目标建模算法介绍:MMOE、OMOE、Shared-Bottom

    在推荐业务中经常有"既要.也要.还要"的场景,比如做视频推荐业务的时候既要提升用户对于视频的点击率,也希望同时提升用户观看视频的时长.面对这样的诉求,通常需要在推荐系统中使用多目标 ...

  6. 【推荐实践】阿里飞猪“猜你喜欢”推荐排序实践

    导读:飞猪猜你喜欢排序模型由线性模型升级到端到端的深度模型,并进行了多个版本的迭代.本文分享模型迭代中的一些技术沉淀. 引言 俗话说有多少米,就下多大锅.在特征体系构建上,我们已经准备了很多米了,并且 ...

  7. python蜡烛图预测_【Python量化投资】系列之SVR预测第二天开盘趋势和股价的正负统计分析(附代码)...

    原标题:[Python量化投资]系列之SVR预测第二天开盘趋势和股价的正负统计分析(附代码) 本期导读 ⊙ML.SVM介绍 ⊙股价的正负统计分析 ⊙预测第二天开盘趋势 机器学习方法是计算机科学的一个分 ...

  8. 深度学习核心技术精讲100篇(四十一)-阿里飞猪个性化推荐:召回篇

    前言 召回几乎是所有推荐系统的基础模块,对应到电商的推荐中,它的作用是从海量的商品池中,筛选出一部分用户可能感兴趣的商品作为上层排序系统的候选集.因此,可以说召回效果的好坏直接决定了推荐效果的上界. ...

  9. Spark推荐系列之Word2vec算法介绍、实现和应用说明(附代码)

    1. 背景 word2vec 是Google 2013年提出的用于计算词向量的工具,在论文Efficient Estimation of Word Representations in Vector ...

最新文章

  1. 计算机电缆线对成缆系数,计算机电缆绞合系数 - 无图版
  2. 完整的项目工程目录结构
  3. Dart对列表进行排序
  4. mybatis批量插入(insert)和批量更新(update)
  5. ITK:提取二值图像中连接区域的边界
  6. 常用自动化框架简单的分析与介绍
  7. Outlook 2013 电子邮件账户设置备份与恢复
  8. 2019年税收分类编码_通过分析112,654个编码测试,我们了解了2019年开发人员的招聘趋势...
  9. STM32 Flash详解
  10. leetcode-345-Reverse Vowels of a String
  11. 你们一年大概可以存多少钱?
  12. [php基础]Mysql日期函数:日期时间格式转换函数详解
  13. C/C++ 计算程序运行时间的代码
  14. weblogic部署静态资源文件html,weblogic部署静态html
  15. MIPS学习笔记(1)
  16. abaqus中六面体单元对比四面体
  17. java运行速度慢的原因_java运行速度慢的原因 | 学步园
  18. 英特尔前任 CEO 安迪·格鲁夫的传奇一生
  19. 数据库应用(mysql)数据库编程
  20. Illustrator script 脚本 基本的使用

热门文章

  1. 特斯拉CEO马斯克:将离开推特一段时间
  2. DeFi智能投顾Rari Capital协议已产生超165000美元费用
  3. SAP License:SAP顾问该不该参与数据搜集
  4. SAP License:FICO面试问题
  5. 商城小程序、实例原型设计、电商app、积分商城、领券中心、会员中心、每日签到、小程序电商、优惠券、移动端电商、Axure原型、rp原型、产品原型、积分、会员卡
  6. C# 数组练习题及答案解析
  7. HDU 4622 求解区间字符串中的不同子串的个数
  8. HDU-1151 Air Raid
  9. 【leetcode】length of last word (easy)
  10. Swing透明和变换