You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于Python3.6的推荐模型:曼哈顿距离结果异常及欧氏/皮尔逊对比问题

问题分析与解决方案

我来帮你理一理这个问题——在基于用户的协同过滤里用曼哈顿距离踩坑太常见了,尤其是刚上手推荐系统的时候。咱们一步步拆解:

1. 曼哈顿距离“不符合逻辑”的核心原因:混淆了「距离」和「相似度」

曼哈顿距离(Manhattan Distance)的定义是两个用户对同一物品评分的绝对差之和:
d(u, v) = Σ|r_u,i - r_v,i|
这里的距离越小,代表两个用户的偏好越相似,但如果你直接把这个距离值当成“相似度”来用,就会出现逻辑反转——比如距离大的反而被当成相似度高,结果自然不对。

正确的做法是把距离转换为相似度,常用的转换方式有两种:

  • 倒数转换:similarity = 1 / (1 + d(u, v)),这样相似度范围是(0,1],值越大越相似
  • 归一化转换:如果知道最大可能的距离值max_d,可以用similarity = 1 - (d(u, v)/max_d),范围[0,1]

2. 欧氏/曼哈顿距离的“单位问题”:未做评分归一化

你提到的“单位问题”,本质是不同用户的评分尺度不一致导致的。比如:

  • 用户A习惯给所有物品打3-5分(满分5)
  • 用户B习惯给所有物品打1-3分(满分5)
    这时候计算出来的距离数值会被用户的评分习惯放大,看起来“单位不对”。另外,Pearson相关系数是无量纲的(范围-1到1),它会自动消除用户评分尺度的影响(因为计算的是评分的相对变化),而曼哈顿/欧氏距离是基于绝对评分差的,所以两者的数值范围完全不同,不能直接对比数值大小。

解决方法是先对每个用户的评分做归一化:

# 以用户为单位,将评分归一化到0-1区间
def normalize_ratings(user_ratings):
    min_r = min(user_ratings.values())
    max_r = max(user_ratings.values())
    if max_r == min_r:
        return {item: 0.5 for item in user_ratings}  # 避免除以0
    return {item: (r - min_r)/(max_r - min_r) for item, r in user_ratings.items()}

归一化后,所有用户的评分都在同一尺度下,欧氏和曼哈顿距离的数值就会更合理,也能和Pearson的结果在逻辑上对齐(比如相似用户的距离小,Pearson相关系数高)。

3. 代码示例:正确计算曼哈顿相似度的完整流程

这里给你一个Python3.6的示例,基于用户对物品X的评分找相似用户,再推荐物品:

from collections import defaultdict

# 模拟用户评分数据:{用户ID: {物品ID: 评分}}
user_ratings = {
    "user1": {"X": 4, "Y": 5, "Z": 3},
    "user2": {"X": 3, "Y": 4, "W": 2},
    "user3": {"X": 5, "Z": 4, "W": 5},
    "user4": {"X": 2, "Y": 2, "Z": 1}
}

# 步骤1:归一化每个用户的评分
normalized_ratings = {user: normalize_ratings(ratings) for user, ratings in user_ratings.items()}

# 步骤2:计算目标用户与其他用户的曼哈顿相似度
target_user = "user1"
target_ratings = normalized_ratings[target_user]

similarities = defaultdict(float)
for user, ratings in normalized_ratings.items():
    if user == target_user:
        continue
    # 只计算共同评分的物品(这里以物品X为核心,也可以扩展到所有共同物品)
    common_items = set(target_ratings.keys()) & set(ratings.keys())
    if not common_items:
        continue
    # 计算曼哈顿距离
    manhattan_dist = sum(abs(target_ratings[item] - ratings[item]) for item in common_items)
    # 转换为相似度
    similarities[user] = 1 / (1 + manhattan_dist)

# 步骤3:按相似度排序,取最相似的用户
sorted_similar_users = sorted(similarities.items(), key=lambda x: x[1], reverse=True)

# 步骤4:推荐相似用户偏好的物品(排除目标用户已评分的)
recommended_items = set()
for user, sim in sorted_similar_users[:2]:  # 取前2个最相似用户
    for item in normalized_ratings[user]:
        if item not in target_ratings:
            recommended_items.add(item)

print("推荐物品:", recommended_items)
print("用户相似度:", sorted_similar_users)

4. 额外注意点

  • 如果你的场景只基于物品X的评分计算相似度,那共同物品只有X,这时候曼哈顿距离就是两个用户对X评分的绝对差,转换后的相似度是合理的,但推荐的多样性会比较差,建议扩展到多个共同评分的物品。
  • Pearson相关系数适合处理用户评分尺度差异的情况,但它对数据稀疏性比较敏感;曼哈顿/欧氏距离在数据密集、评分尺度统一时表现更好,根据你的场景选择合适的度量方式。

内容的提问来源于stack exchange,提问作者user1940212

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.25 06:35:51