为了解决信息过载和用户无明确需求的问题,找到用户感兴趣的物品,才有了个性化推荐系统。
其实,解决信息过载的问题,代表性的解决方案是分类目录和搜索引擎,如hao123,电商首页的分类目录以及百度,360搜索等。
不过分类目录和搜索引擎只能解决用户主动查找信息的需求,即用户知道自己想要什么,并不能解决用户没用明确需求很随便的问题。
通过分析用户的历史行为给用户的兴趣建模,从而主动给用户推荐能够满足他们兴趣和需求的信息。这就是推荐系统。
最早出现推荐系统的应该是电商平台,比如淘宝,京东等。后来逐步发展到抖音,小红书等社交平台。
推荐系统广泛存在于各类网站中,作为一个应用为用户提供个性化的推荐。它需要一些用户的历史数据,一般由三个部分组成:基础数据、推荐算法系统、前台展示。
迄今为止,在个性化推荐系统中,协同过滤技术是应用最成功的技术。目前国内外有许多大型网站应用这项技术为用户更加智能(个性化、千人千面)的推荐内容。
核心思想:
协同过滤一般是在海量的用户中发掘出一小部分和你品位比较类似的,在协同过滤中,这些用户成为邻居,然后根据他们喜欢的其他东西组织成一个排序的目彔作为推荐给你。
基于用户的协同过滤算法先计算的是用户与用户的相似度(兴趣相投,物以类聚人以群分),然后将相似度比较接近的用户A购买的物品推荐给用户B,专业的说法是该算法用最近邻居(nearest-neighbor)算法找出一个用户的邻居集合,该集合的用户和该用户有相似的喜好,算法根据邻居的偏好对该用户进行预测。
如上图所示:用户C喜欢物品A,C和D。用户A喜欢商品A和C。那么用户A和用户C就是一个邻居,拥有相同的喜好,那么推荐系统就会将物品D推荐给用户A。
总的来说基于用户的推荐方式就是以用户为中心,根据不同用户的喜好将用户进行划分,从而实现推荐。
在实际应用中,会根据用户的不同行为定义一个用户对某个事物的感兴趣程度。常见的有如下:
基于ItemCF的原理和基于UserCF类似,只是在计算邻居时采用物品本身,而不是从用户的角度,即基于用户对物品的偏好找到相似的物品,然后根据用户的历史偏好,推荐相似的物品给他。
从计算的角度看,就是将所有用户对某个物品的偏好作为一个向量来计算物品之间的相似度,得到物品的相似物品后,根据用户历史的偏好预测当前用户还没有表示偏好的物品,计算得到一个排序的物品列表作为推荐。
如上图所示:对于物品A,喜欢物品A的用户同样也会喜欢物品C,那么推断出物品A和物品C应该比较类似。那么当用户C喜欢了商品A以后,那么推荐系统因为商品A和商品C相似,因此会把商品C推荐给用户A。
社交网络中,好友推荐已经是不可或缺的功能。
通过用户的个人资料推荐与其具有相似兴趣爱好的其他用户
根据‘社交链’进行推荐
对于好友推荐,也有一些常用的评分标准来衡量两个人之间的相似度
字段 | 权重分 | |||
---|---|---|---|---|
年龄差 | 0-2岁 30分 | 3-5 20分 | 5-10岁 10分 | 10岁以上 0分 |
性别 | 异性 30分 | 同性 0分 | ||
位置 | 同城 20分 | 不同 0分 | ||
学历 | 相同 20分 | 不同 0分 |
大数据推荐系统由专业人员实现,并已经通过docker的方式部署到虚拟机中。具体的工作流程如下
动态推荐的具体流程如下:
用户的业务操作,发送日志消息到RabbitMQ
数据采集系统获取消息,将数据存入MongoDB
推荐系统(Spark)拉取数据,进行推荐计算并存入Redis,同时保存推荐信息到MongoDB中
探花系统从Redis获取推荐数据结果
核心推荐逻辑:
在之前的系统中,我们已经通过自定义注解配合AOP实现了用户行为的发送。
接收消息的工作需要新创建tanhua-recommend工程,在此工程中完成相关的操作。
引入相关依赖
<dependencies>
<dependency>
<groupId>com.itheimagroupId>
<artifactId>tanhua-modelartifactId>
<version>1.0-SNAPSHOTversion>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-amqpartifactId>
dependency>
<dependency>
<groupId>cn.hutoolgroupId>
<artifactId>hutool-allartifactId>
<version>5.4.3version>
dependency>
dependencies>
配置文件
spring:
rabbitmq:
host: 192.168.136.160
port: 5672
username: guest
password: guest
data:
mongodb:
uri: mongodb://192.168.136.160:27017/tanhua
启动类
package com.tanhua.recommend;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class RecommendApplication {
public static void main(String[] args) {
SpringApplication.run(RecommendApplication.class,args);
}
}
实体类
//大数据动态评分实体类
@Data
@NoArgsConstructor
@AllArgsConstructor
@Document("recomment_movement_score")
public class MovementScore {
private ObjectId id;
private Long userId;// 用户id
private Long movementId; //动态id,需要转化为Long类型
private Double score; //得分
private Long date; //时间戳
}
监听器
@Component
public class RecommendMovementListener {
@Resource
private MongoTemplate mongoTemplate;
@RabbitListener(
bindings = @QueueBinding(
value = @Queue(
value = "tanhua.movement.queue",
durable = "true"
),
exchange = @Exchange(
Constants.LOG_EXCHANGE_NAME
),
key = "log.movement"
)
)
public void recommend(String msg) {
try {
// 解析数据
Map<String, String> map = JSON.parseObject(msg, Map.class);
String userId = map.get("userId");
String type = map.get("type");
String logTime = map.get("logTime");
String movementId = map.get("busId");
// 根据动态id查询到动态对象
Movement movement = this.mongoTemplate.findById(movementId, Movement.class);
if (movement != null) {
// 封装
MovementScore score = new MovementScore();
score.setUserId(Long.parseLong(userId));
score.setDate(System.currentTimeMillis());
score.setMovementId(movement.getPid());
score.setScore(getScore(type, movement));
this.mongoTemplate.save(score);
}
} catch (NumberFormatException e) {
throw new RuntimeException(e);
}
}
private static Double getScore(String type, Movement movement) {
//0201为发动态 基础5分 50以内1分,50~100之间2分,100以上3分
//0202为浏览动态, 1
//0203为动态点赞, 5
//0204为动态喜欢, 8
//0205为评论, 10
//0206为动态取消点赞, -5
//0207为动态取消喜欢 -8
Double score = 0d;
switch (type) {
case "0201":
score = 5d;
score += movement.getMedias().size();
int length = StrUtil.length(movement.getTextContent());
if (length >= 0 && length < 50) {
score += 1;
} else if (length < 100) {
score += 2;
} else {
score += 3;
}
break;
case "0202":
score = 1d;
break;
case "0203":
score = 5d;
break;
case "0204":
score = 8d;
break;
case "0205":
score = 10d;
break;
case "0206":
score = -5d;
break;
case "0207":
score = -8d;
break;
default:
break;
}
return score;
}
}
注意:大数据系统从MongoDB中读取到我们记录的用户日志和相关评分,最终经过计算后得到推荐的动态,将推荐动态的pid写入redis中。
小视频的推荐流程和动态推荐流程是一样的,这里就不在赘述。