推荐算法核心课:基于距离的相似度度量

主讲人: [XXXX]
适用对象: 数据分析师、算法工程师、初入职场的技术人员
课时: 45-60分钟


第一部分:引言与回顾

1.1 回顾:余弦相似度

大家好,在之前的课程中,我们学习了推荐系统中常用的余弦相似度

核心思想:

余弦相似度衡量的是两个向量在方向 上的差异,而不是大小。
cosine_similarity = A ⋅ B ∣ ∣ A ∣ ∣ ∣ ∣ B ∣ ∣ \text{cosine\_similarity} = \frac{A \cdot B}{||A|| ||B||} cosine_similarity=∣∣A∣∣∣∣B∣∣A⋅B
举个例子:

假设有两个用户对电影的打分(1-5分):

  • 用户A:喜欢动作片,打分偏高,向量为 [5, 4, 1]
  • 用户B:喜欢动作片,打分偏保守,向量为 [4, 3, 1]

虽然A的打分普遍比B高1分,但他们的喜好趋势(方向)是一致的。余弦相似度会认为他们非常相似。

1.2 引导问题:余弦相似度的"盲点"

但是,余弦相似度也有它看不到的地方。

场景:

在电商网站,有两个用户:

  • 用户甲: 购物车金额 [100元, 200元, 50元] (高消费用户)
  • 用户乙: 购物车金额 [10元, 20元, 5元] (低消费用户)

如果他们买的东西品类比例完全一样(都是:大衣、裤子、袜子),余弦相似度会认为他们是100%相似

提问: 推荐系统能把"百亿补贴"的100元商品推给只买10元东西的用户乙吗?
答案: 不能。因为消费能力不同。

这里,我们就需要引入基于距离的相似度度量来解决"量级"差异带来的问题。


第二部分:基于距离的相似度度量

2.1 什么是距离?

在几何学中,两点之间的距离越短,代表它们越接近,也就是越相似

在推荐算法中,我们把用户或物品抽象成多维空间中的点,通过计算两点之间的空间距离来衡量相似度。距离越短,相似度越高。

2.2 核心公式:欧几里得距离

最经典的距离度量是欧几里得距离,就是我们常说的"直线距离"。

公式:
d ( x , y ) = ∑ i = 1 n ( x i − y i ) 2 d(x, y) = \sqrt{\sum_{i=1}^{n} (x_i - y_i)^2} d(x,y)=i=1∑n(xi−yi)2

  • ( x_i ) 和 ( y_i ) 代表两个用户在某个维度(如:某件商品的价格、某类电影的评分)上的数值。
  • ( n ) 代表维度总数。

通俗理解:

想象你在一个房间里(二维空间),你的位置是( (3,4) ),朋友的位置是( (6,8) )。你们之间的直线距离就是( \sqrt{(6-3)^2 + (8-4)^2} = 5 )。这个"5"就是你们的距离。如果朋友走到( (3.1, 4.1) ),距离只有0.14,说明你们非常相似(几乎重合)。

2.3 从距离到相似度的转换

距离和相似度是负相关的。通常我们需要将距离映射到( [0, 1] )区间,方便理解。

常用转换公式:
Similarity = 1 1 + d \text{Similarity} = \frac{1}{1 + d} Similarity=1+d1

  • 优点: 当( d=0 )(完全重合),相似度为1;当( d )趋近无穷大,相似度趋近0。

第三部分:应用案例引出与手动演算

3.1 案例背景:图书推荐系统

我们有一个小型图书馆系统。我们想找到和"读者小蓝"品味最相似的人,以便给他推荐新书。

数据如下(对书籍的评分 1-10分):

读者 《三体》 (科幻) 《活着》 (文学) 《算法导论》 (技术)
小蓝 8 7 2
读者A 9 8 3
读者B 3 2 9

问题: 读者A和读者B,谁和小蓝更像?

3.2 手动演算:欧几里得距离

我们将每个读者看作三维空间的一个点,坐标就是评分。

第一步:计算小蓝和读者A的距离
d ( 小蓝 , A ) = ( 8 − 9 ) 2 + ( 7 − 8 ) 2 + ( 2 − 3 ) 2 = ( − 1 ) 2 + ( − 1 ) 2 + ( − 1 ) 2 = 1 + 1 + 1 = 3 ≈ 1.732 \begin{aligned} d(\text{小蓝}, A) &= \sqrt{(8-9)^2 + (7-8)^2 + (2-3)^2} \\ &= \sqrt{(-1)^2 + (-1)^2 + (-1)^2} \\ &= \sqrt{1 + 1 + 1} = \sqrt{3} \approx 1.732 \end{aligned} d(小蓝,A)=(8−9)2+(7−8)2+(2−3)2 =(−1)2+(−1)2+(−1)2 =1+1+1 =3 ≈1.732

第二步:计算小蓝和读者B的距离
d ( 小蓝 , B ) = ( 8 − 3 ) 2 + ( 7 − 2 ) 2 + ( 2 − 9 ) 2 = ( 5 ) 2 + ( 5 ) 2 + ( − 7 ) 2 = 25 + 25 + 49 = 99 ≈ 9.95 \begin{aligned} d(\text{小蓝}, B) &= \sqrt{(8-3)^2 + (7-2)^2 + (2-9)^2} \\ &= \sqrt{(5)^2 + (5)^2 + (-7)^2} \\ &= \sqrt{25 + 25 + 49} = \sqrt{99} \approx 9.95 \end{aligned} d(小蓝,B)=(8−3)2+(7−2)2+(2−9)2 =(5)2+(5)2+(−7)2 =25+25+49 =99 ≈9.95

第三步:转换为相似度

  • 小蓝与A的相似度: 1 1 + 1.732 ≈ 1 2.732 ≈ 0.366 \frac{1}{1+1.732} \approx \frac{1}{2.732} \approx 0.366 1+1.7321≈2.7321≈0.366
  • 小蓝与B的相似度: 1 1 + 9.95 ≈ 1 10.95 ≈ 0.091 \frac{1}{1+9.95} \approx \frac{1}{10.95} \approx 0.091 1+9.951≈10.951≈0.091

结论:
0.366 > 0.091 0.366 > 0.091 0.366>0.091,因此读者A与小蓝更相似。

为什么?

因为读者A在所有书籍上的评分量级都与小蓝接近(都是高分给科幻文学,低分给技术),而读者B虽然也偏爱技术(高分),但小蓝不爱技术,且B不爱科幻文学,所以距离很远。


第四部分:Python代码验证

下面我们用Python代码复现上述手动计算过程,并进行简单的可视化分析。

python 复制代码
import numpy as np
import pandas as pd

# 1. 数据定义
# 我们将数据定义在DataFrame中,方便查看
data = {
    '读者': ['小蓝', '读者A', '读者B'],
    '《三体》': [8, 9, 3],
    '《活着》': [7, 8, 2],
    '《算法导论》': [2, 3, 9]
}
df = pd.DataFrame(data)
df.set_index('读者', inplace=True)

print("="*30)
print("原始数据矩阵:")
print(df)
print("="*30)

# 2. 定义基于欧几里得距离的相似度计算函数
def euclidean_similarity(vec1, vec2):
    """
    计算两个向量之间的欧几里得距离,并转换为相似度。
    逻辑:
    1. 计算差值的平方和 (np.sum((vec1 - vec2)**2))
    2. 开方得到欧氏距离 (np.sqrt(...))
    3. 通过 1/(1+距离) 映射到[0,1]区间,距离越小,相似度越高。
    """
    # 计算欧几里得距离
    distance = np.sqrt(np.sum((vec1 - vec2) ** 2))
    # 防止距离为0时的除零问题,但这里1+0=1,没问题
    similarity = 1 / (1 + distance)
    return similarity

# 提取向量
xiao_lan = df.loc['小蓝'].values
user_a = df.loc['读者A'].values
user_b = df.loc['读者B'].values

# 3. 计算相似度
sim_A = euclidean_similarity(xiao_lan, user_a)
sim_B = euclidean_similarity(xiao_lan, user_b)

print("相似度计算结果:")
print(f"小蓝 与 读者A 的相似度: {sim_A:.4f} (手动演算结果约为 0.366)")
print(f"小蓝 与 读者B 的相似度: {sim_B:.4f} (手动演算结果约为 0.091)")
print("="*30)

# 4. 逻辑分析
if sim_A > sim_B:
    print("结论: 读者A与小蓝更相似。推荐系统应优先考虑读者A的阅读记录来给小蓝推荐书籍。")
else:
    print("结论: 读者B与小蓝更相似。")

代码执行逻辑分析:

  1. 数据加载: 将表格数据转化为NumPy数组,以便进行数学运算。
  2. 向量化计算: vec1 - vec2 利用NumPy的广播机制,一次性计算所有维度的差值,避免了手动的循环。
  3. 平方和开方: 严格遵循欧氏距离公式。
  4. 归一化: 将距离转化为0-1之间的相似度分数,便于业务人员理解(1表示完全一致)。

第五部分:常见的生产应用场景

基于距离的相似度度量在实际生产中非常常见,尤其是在以下场景:

  1. 协同过滤的"用户/物品"冷启动补充:

    • 当用户刚注册,只有消费金额、地域等数值型属性,没有行为评分时,用欧氏距离找"消费能力、画像最相似"的用户进行物物相关性推荐。
  2. LBS(基于位置的服务)推荐:

    • 美团、大众点评、高德地图。
    • 核心需求: 找"离我最近"的餐厅。
    • 技术实现: 使用哈弗辛公式(球面距离,本质也是距离度量)计算经纬度之间的距离。距离越近,推荐优先级越高。
  3. 向量召回(Embedding召回):

    • 在深度学习中,我们将用户和物品映射为Embedding向量。
    • 虽然常用内积或余弦,但当要求用户兴趣强度(如点击率预估中的CTR值)也参与相似度计算时,欧氏距离能更好地平衡方向与模长。

第六部分:算法对比分析

维度 余弦相似度 基于距离的相似度(以欧氏距离为例)
核心逻辑 关注方向,忽略大小。 关注绝对位置,包含大小(量级)。
对量级的敏感度 不敏感。 (1,2) 和 (2,4) 被视为完全相似。 敏感。 (1,2) 和 (2,4) 距离不为0,不视为完全相同。
适用场景 文本相似度(TF-IDF)、用户兴趣偏好(如评分趋势)、忽视绝对值的场景。 消费能力判定、地理位置、包含明显数值差异的多维数据。
优点 解决了"膨胀"问题(归一化后的比较),关注相对比例。 直观,符合物理直觉,对绝对值差异敏感。
缺点 忽略了关键的量级信息(如钱、购买力)。 量纲影响大(如果数据没做归一化,数值大的特征会主导距离)。
归一化要求 通常无需刻意归一化(因为方向不变)。 必须进行归一化/标准化,否则大数值特征(如价格)会掩盖小数值特征(如评分)。

第七部分:面试高频题

问题1:

"在推荐系统中,什么时候用余弦相似度,什么时候用欧几里得距离?"

参考答案:

  • 如果数据主要体现偏好 (例如用户对不同电影的评分),且我们关心的是相对喜好 (用户A比用户B更喜欢动作片,即使A打分严格),用余弦相似度
  • 如果数据包含绝对值含义 (例如商品价格、消费金额、地理位置),且绝对值差异对业务有直接影响(例如不能把奢侈品推给低消费人群),用基于距离的度量
  • 加分项: 在实际工程中,如果特征已经经过了标准化处理(StandardScaler),两者效果可能会趋同,但本质逻辑不同。

问题2:

"如果使用欧几里得距离,特征维度之间量纲不同(例如一个特征是价格(0-1000),一个是评分(0-5)),该怎么办?"

参考答案:

必须进行数据归一化 (Min-Max Scaling)或标准化(Z-score Normalization)。如果不处理,价格维度的波动(1000的平方)将完全淹没评分维度的贡献(5的平方),导致距离计算失效。


第八部分:总结

今天我们主要学习了以下几点:

  1. 引出背景: 余弦相似度关注"方向",无法区分"消费能力"等量级差异。
  2. 核心概念: 基于距离的相似度关注空间中点的绝对位置,距离越短,越相似。
  3. 核心算法: 欧几里得距离及其相似度转换公式 ( S = 1/(1+d) )。
  4. 实战对比: 通过手动演算和Python代码,我们验证了读者A与小蓝在三维空间中的距离更近。
  5. 应用场景: 主要用于LBS、消费能力分层、以及需要平衡模长与方向的Embedding召回中。

关键记忆点:

"余弦看方向,欧氏看位置。"


第九部分:课后作业题

题目:曼哈顿距离的实战

曼哈顿距离(街区距离)是另一种常见的距离度量,公式为:
d m a n h a t t a n = ∑ i = 1 n ∣ x i − y i ∣ d_{manhattan} = \sum_{i=1}^{n} |x_i - y_i| dmanhattan=i=1∑n∣xi−yi∣

任务:

沿用本次课中"小蓝、读者A、读者B"的数据集。

  1. 请手动计算"小蓝"与"读者A"的曼哈顿距离。
  2. 请手动计算"小蓝"与"读者B"的曼哈顿距离。
  3. 使用Python编写代码,验证你的计算结果。
  4. 思考题: 在这个具体的图书评分数据集下,欧几里得距离和曼哈顿距离得出的结论(谁更像小蓝)是否一致?如果不一致,为什么?哪种距离更适合这个场景?

(提示:欧氏距离对异常值(Outlier)更敏感,因为它计算的是平方和;曼哈顿距离是绝对值之和,对异常值的容忍度更高。)


(课件结束)

相关推荐
Trouvaille ~2 小时前
【优选算法篇】队列与宽度优先搜索(BFS)——层层递进的视野
c++·算法·leetcode·青少年编程·面试·蓝桥杯·宽度优先
hanlin032 小时前
动态规划专练:力扣第509、70、746题
算法·leetcode·动态规划
CoderIsArt2 小时前
shor算法
算法·量子计算
m0_743890512 小时前
3月27日 模拟题
算法
北顾笙9803 小时前
day09-数据结构力扣
数据结构·算法·leetcode
旖-旎3 小时前
位运算(判断字符是否唯一)(1)
c++·算法·leetcode·位运算
词元Max3 小时前
1.4 核心名词解释:Token、RAG、Agent、MCP是什么
人工智能·算法
mzhan0173 小时前
Linux: sched: pick_next_task_fair 这个函数的功能
linux·运维·算法
AI职业加油站3 小时前
数字时代先机:大数据采集工程师
大数据·人工智能·机器学习·职场和发展