Redis 实时推荐系统:用户行为数据与协同过滤

各位朋友,大家好!今天咱们聊聊如何用 Redis 打造一个风骚的实时推荐系统,重点是用户行为数据和协同过滤。准备好了吗?系好安全带,发车啦!

第一站:需求分析与架构设计

想象一下,你是一个电商网站的老板,用户每天在你网站上浏览、点击、购买,产生海量数据。你希望根据这些数据,实时地向用户推荐他们可能感兴趣的商品,提高转化率。这就是实时推荐系统要解决的问题。

核心需求:

  • 实时性: 推荐结果要随着用户行为的改变而迅速调整。
  • 准确性: 推荐的商品要尽可能符合用户的兴趣。
  • 可扩展性: 系统要能够处理海量用户和商品。

架构设计:

一个简单的实时推荐系统架构大致如下:

  1. 用户行为数据收集: 收集用户的浏览、点击、购买等行为数据。
  2. 数据预处理: 清洗、过滤、转换数据,方便后续计算。
  3. 数据存储: 将处理后的数据存储在 Redis 中。
  4. 协同过滤计算: 基于 Redis 中的数据,进行协同过滤计算,得到用户或商品的相似度。
  5. 推荐生成: 根据相似度和用户历史行为,生成推荐列表。
  6. 推荐服务: 将推荐列表推送给用户。

第二站:Redis 数据模型设计

Redis 是咱们的秘密武器,要用好它,数据模型设计至关重要。 咱们先来考虑下,哪些数据需要存入 Redis,以及如何存储。

  • 用户行为数据:

    • 用户 ID (user_id)
    • 商品 ID (item_id)
    • 行为类型 (action_type): 例如 "view", "click", "purchase"
    • 时间戳 (timestamp)
  • 存储方式: 我们可以用多种方式存储,这里推荐两种:

    • Sorted Set:user_id 为 key,item_id 为 member,timestamp 为 score。 这样可以按照时间顺序存储用户的行为,方便后续分析。

    • Hash:user_id:item_id 为 key,action_typetimestamp 作为 field 和 value 。适用于需要记录更多行为类型和属性的情况。

代码示例 (Python + redis-py):

import redis
import time

# 连接 Redis
redis_client = redis.Redis(host='localhost', port=6379, db=0)

def record_user_action(user_id, item_id, action_type):
    """记录用户行为数据"""
    timestamp = time.time()

    # 使用 Sorted Set
    redis_client.zadd(f"user:{user_id}:actions", {item_id: timestamp})

    # 或者使用 Hash
    redis_client.hset(f"user:{user_id}:item:{item_id}", "action_type", action_type)
    redis_client.hset(f"user:{user_id}:item:{item_id}", "timestamp", timestamp)

# 模拟用户行为
record_user_action("user123", "item456", "view")
record_user_action("user123", "item789", "click")
record_user_action("user456", "item456", "purchase")

第三站:协同过滤算法实战

协同过滤 (Collaborative Filtering) 是推荐系统中最常用的算法之一。它基于用户的行为数据,找到相似的用户或商品,然后进行推荐。 咱们这里重点介绍两种:

  • 基于用户的协同过滤 (User-Based CF): 找到和目标用户兴趣相似的用户,然后将这些用户喜欢的商品推荐给目标用户。
  • 基于物品的协同过滤 (Item-Based CF): 找到和目标商品相似的商品,然后将这些商品推荐给喜欢目标商品的用户。

1. 基于用户的协同过滤 (User-Based CF):

  • 步骤:

    1. 计算用户之间的相似度。
    2. 找到和目标用户最相似的 N 个用户。
    3. 将这 N 个用户喜欢但目标用户未购买的商品推荐给目标用户。
  • 相似度计算: 常用的相似度计算方法有:

    • 余弦相似度 (Cosine Similarity): 计算两个向量的夹角余弦值。
    • 皮尔逊相关系数 (Pearson Correlation Coefficient): 计算两个变量之间的线性相关程度。
  • Redis 实现:

    • 计算用户相似度: 可以使用 Redis 的 SINTERSTORE 命令计算两个用户共同喜欢的商品数量,然后结合总共喜欢的商品数量,计算相似度。
    • 找到相似用户: 可以使用 Redis 的 ZADD 命令将用户相似度存储在 Sorted Set 中,然后使用 ZREVRANGE 命令找到最相似的 N 个用户。
    • 生成推荐列表: 遍历相似用户的行为数据,找到他们喜欢但目标用户未购买的商品,进行排序,生成推荐列表。

代码示例 (Python + redis-py):

import redis
import math

redis_client = redis.Redis(host='localhost', port=6379, db=0)

def calculate_user_similarity(user1_id, user2_id):
    """计算用户相似度 (余弦相似度)"""
    user1_items_key = f"user:{user1_id}:actions"
    user2_items_key = f"user:{user2_id}:actions"

    # 获取用户喜欢的商品列表
    user1_items = redis_client.zrange(user1_items_key, 0, -1)
    user2_items = redis_client.zrange(user2_items_key, 0, -1)

    # 计算共同喜欢的商品数量
    common_items = set(user1_items) & set(user2_items)
    common_count = len(common_items)

    # 计算用户喜欢的商品数量
    user1_count = len(user1_items)
    user2_count = len(user2_items)

    # 计算余弦相似度
    if user1_count == 0 or user2_count == 0:
        return 0.0
    similarity = common_count / (math.sqrt(user1_count) * math.sqrt(user2_count))
    return similarity

def get_similar_users(user_id, top_n=5):
    """获取相似用户"""
    similarities = {}
    for other_user_id in redis_client.keys("user:*:actions"):
        other_user_id = other_user_id.decode('utf-8').split(':')[1]
        if other_user_id != user_id:
            similarity = calculate_user_similarity(user_id, other_user_id)
            similarities[other_user_id] = similarity

    # 按照相似度排序
    sorted_similarities = sorted(similarities.items(), key=lambda x: x[1], reverse=True)
    return sorted_similarities[:top_n]

def generate_user_based_recommendations(user_id, top_n=10):
    """生成基于用户的推荐列表"""
    similar_users = get_similar_users(user_id)
    recommended_items = {}

    for similar_user, similarity in similar_users:
        # 获取相似用户喜欢的商品
        similar_user_items_key = f"user:{similar_user}:actions"
        similar_user_items = redis_client.zrange(similar_user_items_key, 0, -1)

        # 过滤掉用户已经喜欢的商品
        user_items_key = f"user:{user_id}:actions"
        user_items = redis_client.zrange(user_items_key, 0, -1)
        unseen_items = set(similar_user_items) - set(user_items)

        # 统计推荐次数
        for item in unseen_items:
            item = item.decode('utf-8')
            if item not in recommended_items:
                recommended_items[item] = 0
            recommended_items[item] += similarity  # 使用相似度作为权重

    # 按照推荐次数排序
    sorted_recommendations = sorted(recommended_items.items(), key=lambda x: x[1], reverse=True)
    return [item for item, score in sorted_recommendations[:top_n]]

# 示例
user_id = "user123"
recommendations = generate_user_based_recommendations(user_id)
print(f"为用户 {user_id} 推荐的商品: {recommendations}")

2. 基于物品的协同过滤 (Item-Based CF):

  • 步骤:

    1. 计算商品之间的相似度。
    2. 找到和目标商品最相似的 N 个商品。
    3. 将用户历史上喜欢的商品中,和目标商品相似的 N 个商品推荐给用户。
  • 相似度计算: 与 User-Based CF 类似,可以使用余弦相似度或皮尔逊相关系数。 区别在于,这里的向量是商品向量,表示喜欢该商品的用户集合。

  • Redis 实现:

    • 计算商品相似度: 可以使用 Redis 的 SINTERSTORE 命令计算同时喜欢两个商品的用户数量,然后结合喜欢每个商品的用户数量,计算相似度。
    • 找到相似商品: 可以使用 Redis 的 ZADD 命令将商品相似度存储在 Sorted Set 中,然后使用 ZREVRANGE 命令找到最相似的 N 个商品。
    • 生成推荐列表: 遍历用户历史行为,找到用户喜欢的商品,然后找到和这些商品相似的商品,进行排序,生成推荐列表。

代码示例 (Python + redis-py):

import redis
import math

redis_client = redis.Redis(host='localhost', port=6379, db=0)

def calculate_item_similarity(item1_id, item2_id):
    """计算物品相似度 (余弦相似度)"""
    # 获取喜欢 item1 的用户
    item1_users_key = f"item:{item1_id}:users"
    item1_users = redis_client.smembers(item1_users_key)

    # 获取喜欢 item2 的用户
    item2_users_key = f"item:{item2_id}:users"
    item2_users = redis_client.smembers(item2_users_key)

    # 计算共同喜欢的用户数量
    common_users = set(item1_users) & set(item2_users)
    common_count = len(common_users)

    # 计算喜欢 item1 和 item2 的用户数量
    item1_count = len(item1_users)
    item2_count = len(item2_users)

    # 计算余弦相似度
    if item1_count == 0 or item2_count == 0:
        return 0.0
    similarity = common_count / (math.sqrt(item1_count) * math.sqrt(item2_count))
    return similarity

def get_similar_items(item_id, top_n=5):
    """获取相似物品"""
    similarities = {}
    for other_item_id in redis_client.keys("item:*:users"):
        other_item_id = other_item_id.decode('utf-8').split(':')[1]
        if other_item_id != item_id:
            similarity = calculate_item_similarity(item_id, other_item_id)
            similarities[other_item_id] = similarity

    # 按照相似度排序
    sorted_similarities = sorted(similarities.items(), key=lambda x: x[1], reverse=True)
    return sorted_similarities[:top_n]

def generate_item_based_recommendations(user_id, top_n=10):
    """生成基于物品的推荐列表"""
    # 获取用户喜欢的商品
    user_items_key = f"user:{user_id}:actions"
    user_items = redis_client.zrange(user_items_key, 0, -1)

    recommended_items = {}

    for item_id in user_items:
        item_id = item_id.decode('utf-8')
        # 获取相似商品
        similar_items = get_similar_items(item_id)

        for similar_item, similarity in similar_items:
            # 过滤掉用户已经喜欢的商品
            if similar_item.decode('utf-8') not in user_items:

                if similar_item.decode('utf-8') not in recommended_items:
                    recommended_items[similar_item.decode('utf-8')] = 0
                recommended_items[similar_item.decode('utf-8')] += similarity

    # 按照推荐次数排序
    sorted_recommendations = sorted(recommended_items.items(), key=lambda x: x[1], reverse=True)
    return [item for item, score in sorted_recommendations[:top_n]]

# 示例
user_id = "user123"

# 假设需要先创建 item 和 user 的对应关系
record_user_action(user_id, "item123", "view")
record_user_action(user_id, "item456", "click")

redis_client.sadd("item:item123:users", user_id)
redis_client.sadd("item:item456:users", user_id)

recommendations = generate_item_based_recommendations(user_id)
print(f"为用户 {user_id} 推荐的商品 (基于物品): {recommendations}")

重要提示: 上面的代码是示例,仅仅为了演示算法逻辑,实际应用中,需要考虑性能优化,例如:

  • 数据预处理: 对用户行为数据进行清洗、过滤、归一化等处理,提高计算准确性。
  • 缓存: 将计算结果缓存起来,减少重复计算。
  • 分布式计算: 使用 Redis Cluster 或其他分布式计算框架,提高计算效率。
  • 优化相似度计算: 采用更高效的相似度计算方法,例如 MinHash 或 Locality Sensitive Hashing (LSH)。

第四站:实时性优化

实时性是实时推荐系统的生命线。 如何保证推荐结果的实时更新呢?

  • 增量计算: 每次只计算新增的用户行为数据对相似度的影响,而不是重新计算所有数据。
  • 消息队列: 使用消息队列 (例如 Kafka 或 RabbitMQ) 异步处理用户行为数据,避免阻塞主线程。
  • 定时更新: 定期重新计算相似度,保证推荐结果的准确性。

第五站:效果评估与改进

推荐系统上线后,需要定期评估效果,并进行改进。常用的评估指标有:

指标 描述
点击率 (CTR) 推荐商品被用户点击的比例。
转化率 (CVR) 推荐商品被用户购买的比例。
购买率 被推荐商品带来的销售额
覆盖率 推荐系统能够推荐的商品占总商品数量的比例。
多样性 推荐列表中不同商品的比例。
平均排序倒数 (MRR) 衡量推荐列表中第一个相关商品的平均排名。
归一化折损累计增益 (NDCG) 衡量推荐列表的排序质量,考虑了相关性强度和位置。

改进方向:

  • 调整算法参数: 例如,调整相似用户的数量、相似商品的数量等。
  • 引入更多特征: 例如,用户的人口统计信息、商品的属性信息等。
  • 采用更复杂的算法: 例如,深度学习模型。
  • A/B 测试: 同时运行多个推荐策略,选择效果最好的策略。

总结:

今天咱们一起学习了如何用 Redis 构建一个实时推荐系统。 核心是用户行为数据的存储和协同过滤算法的实现。 记住,没有最好的算法,只有最适合的算法。 根据你的业务场景,选择合适的算法,并不断优化,才能打造一个真正风骚的推荐系统。 希望今天的分享对你有所帮助! 祝大家早日升职加薪!

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注