主讲人: [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与小蓝更相似。")
代码执行逻辑分析:
- 数据加载: 将表格数据转化为NumPy数组,以便进行数学运算。
- 向量化计算:
vec1 - vec2利用NumPy的广播机制,一次性计算所有维度的差值,避免了手动的循环。 - 平方和开方: 严格遵循欧氏距离公式。
- 归一化: 将距离转化为0-1之间的相似度分数,便于业务人员理解(1表示完全一致)。
第五部分:常见的生产应用场景
基于距离的相似度度量在实际生产中非常常见,尤其是在以下场景:
-
协同过滤的"用户/物品"冷启动补充:
- 当用户刚注册,只有消费金额、地域等数值型属性,没有行为评分时,用欧氏距离找"消费能力、画像最相似"的用户进行物物相关性推荐。
-
LBS(基于位置的服务)推荐:
- 美团、大众点评、高德地图。
- 核心需求: 找"离我最近"的餐厅。
- 技术实现: 使用哈弗辛公式(球面距离,本质也是距离度量)计算经纬度之间的距离。距离越近,推荐优先级越高。
-
向量召回(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的平方),导致距离计算失效。
第八部分:总结
今天我们主要学习了以下几点:
- 引出背景: 余弦相似度关注"方向",无法区分"消费能力"等量级差异。
- 核心概念: 基于距离的相似度关注空间中点的绝对位置,距离越短,越相似。
- 核心算法: 欧几里得距离及其相似度转换公式 ( S = 1/(1+d) )。
- 实战对比: 通过手动演算和Python代码,我们验证了读者A与小蓝在三维空间中的距离更近。
- 应用场景: 主要用于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"的数据集。
- 请手动计算"小蓝"与"读者A"的曼哈顿距离。
- 请手动计算"小蓝"与"读者B"的曼哈顿距离。
- 使用Python编写代码,验证你的计算结果。
- 思考题: 在这个具体的图书评分数据集下,欧几里得距离和曼哈顿距离得出的结论(谁更像小蓝)是否一致?如果不一致,为什么?哪种距离更适合这个场景?
(提示:欧氏距离对异常值(Outlier)更敏感,因为它计算的是平方和;曼哈顿距离是绝对值之和,对异常值的容忍度更高。)
(课件结束)