独热编码与余弦相似度

一、独热编码(One-Hot Encoding)

1.1 为什么需要独热编码?

机器学习模型只能处理数值型数据 ,但现实世界充满类别型数据

  • 颜色:{红, 绿, 蓝}
  • 城市:{北京, 上海, 广州, 深圳}
  • 商品类别:{电子产品, 服装, 食品, 家居}

直接赋整数值的问题

颜色 整数编码 问题
0 模型会认为 绿(1) > 红(0),且 蓝(2) - 绿(1) = 绿(1) - 红(0)
绿 1 这些"数值关系"在类别数据中毫无意义
2

独热编码解决:将类别转换为二进制向量,消除虚假数值关系

1.2 数学定义

对于具有 KKK 个类别的变量,第 iii 个类别的独热编码为:

OneHot(i)=[0,⋯ ,0,1⏟第 i 位,0,⋯ ,0]∈RK \text{OneHot}(i) = [0, \cdots, 0, \underbrace{1}_{\text{第 } i \text{ 位}}, 0, \cdots, 0] \in \mathbb{R}^K OneHot(i)=[0,⋯,0,第 i 位 1,0,⋯,0]∈RK

示例 :颜色 {红, 绿, 蓝},K=3K=3K=3

颜色 独热编码
[1,0,0][1, 0, 0][1,0,0]
绿 [0,1,0][0, 1, 0][0,1,0]
[0,0,1][0, 0, 1][0,0,1]

1.3 代码实现

python 复制代码
import numpy as np
from sklearn.preprocessing import OneHotEncoder

# 方法1:手动实现
def one_hot_manual(labels, num_classes):
    """
    手动实现独热编码
    
    :param labels: 类别标签列表,如 [0, 2, 1, 0]
    :param num_classes: 类别总数 K
    :return: 独热编码矩阵
    """
    n = len(labels)
    one_hot = np.zeros((n, num_classes))
    one_hot[np.arange(n), labels] = 1
    return one_hot

# 示例
labels = [0, 2, 1, 0]  # 红, 蓝, 绿, 红
result = one_hot_manual(labels, 3)
print(result)
# [[1. 0. 0.]
#  [0. 0. 1.]
#  [0. 1. 0.]
#  [1. 0. 0.]]

# 方法2:sklearn
encoder = OneHotEncoder(sparse_output=False)
colors = [['红'], ['绿'], ['蓝'], ['红']]
encoded = encoder.fit_transform(colors)
print(encoded)
# [[1. 0. 0.]
#  [0. 1. 0.]
#  [0. 0. 1.]
#  [1. 0. 0.]]

1.4 独热编码的特性

特性 说明 影响
正交性 任意两个不同类别的编码内积为 0 类别间无偏序关系,距离相等
等距性 任意两个不同类别的欧氏距离为 2\sqrt{2}2 不引入虚假的相似度
稀疏性 向量中只有一个 1,其余为 0 存储效率高,但维度膨胀
维度灾难 KKK 个类别需要 KKK 维向量 高基数类别(如用户ID、商品ID)不适用

1.5 优缺点与替代方案

优点

  • 简单直观,实现容易
  • 不引入类别间的虚假关系
  • 与神经网络兼容性好

缺点

  • 维度爆炸:10000 个类别 → 10000 维向量
  • 语义缺失:无法表达类别间的内在联系(如"猫"和"虎"比"猫"和"汽车"更相似)

替代方案

方法 适用场景 核心思想
嵌入(Embedding) 高基数类别、语义相似度 学习低维稠密向量,如 Word2Vec、DeepWalk
标签编码(Label Encoding) 有序类别(如评分 1-5 星) 保留顺序关系
目标编码(Target Encoding) 高基数 + 有监督任务 用目标变量统计值替代类别
二进制编码 极端高基数 用 log⁡2K\log_2 Klog2K 位二进制表示

二、余弦相似度(Cosine Similarity)

2.1 为什么需要余弦相似度?

在信息检索、推荐系统、NLP 中,核心问题是:如何度量两个向量的相似程度?

欧氏距离的局限

  • 受向量长度(模)影响大
  • 长文档与短文档即使内容相似,欧氏距离也可能很大

余弦相似度的优势只关心方向,不关心长度

2.2 数学定义

对于两个非零向量 a,b∈Rn\mathbf{a}, \mathbf{b} \in \mathbb{R}^na,b∈Rn:

CosineSimilarity(a,b)=a⋅b∥a∥∥b∥=∑i=1naibi∑i=1nai2⋅∑i=1nbi2 \text{CosineSimilarity}(\mathbf{a}, \mathbf{b}) = \frac{\mathbf{a} \cdot \mathbf{b}}{\|\mathbf{a}\| \|\mathbf{b}\|} = \frac{\sum_{i=1}^{n} a_i b_i}{\sqrt{\sum_{i=1}^{n} a_i^2} \cdot \sqrt{\sum_{i=1}^{n} b_i^2}} CosineSimilarity(a,b)=∥a∥∥b∥a⋅b=∑i=1nai2 ⋅∑i=1nbi2 ∑i=1naibi

几何解释:两个向量夹角的余弦值

CosineSimilarity=cos⁡(θ)=a⋅b∥a∥∥b∥ \text{CosineSimilarity} = \cos(\theta) = \frac{\mathbf{a} \cdot \mathbf{b}}{\|\mathbf{a}\| \|\mathbf{b}\|} CosineSimilarity=cos(θ)=∥a∥∥b∥a⋅b

夹角 θ\thetaθ 余弦值 相似度
0∘0^\circ0∘(同向) 1 完全相同
90∘90^\circ90∘(正交) 0 无相关性
180∘180^\circ180∘(反向) -1 完全相反

2.3 代码实现

python 复制代码
import numpy as np
from sklearn.metrics.pairwise import cosine_similarity

# 方法1:手动实现
def cosine_similarity_manual(a, b):
    """
    手动实现余弦相似度
    
    :param a: 向量a
    :param b: 向量b
    :return: 余弦相似度,范围[-1, 1]
    """
    dot_product = np.dot(a, b)
    norm_a = np.linalg.norm(a)
    norm_b = np.linalg.norm(b)
    
    if norm_a == 0 or norm_b == 0:
        return 0.0  # 避免除零
    
    return dot_product / (norm_a * norm_b)

# 示例:文档相似度
doc1 = np.array([1, 2, 0, 1, 0])  # 词频向量
doc2 = np.array([2, 3, 0, 1, 0])
doc3 = np.array([0, 0, 5, 0, 5])  # 完全不同的主题

sim_12 = cosine_similarity_manual(doc1, doc2)
sim_13 = cosine_similarity_manual(doc1, doc3)

print(f"doc1 vs doc2: {sim_12:.4f}")  # 0.9759,高度相似
print(f"doc1 vs doc3: {sim_13:.4f}")  # 0.0000,完全不相关

# 方法2:sklearn
similarity_matrix = cosine_similarity([doc1, doc2, doc3])
print(similarity_matrix)
# [[1.         0.97590007 0.        ]
#  [0.97590007 1.         0.        ]
#  [0.         0.         1.        ]]

2.4 余弦相似度 vs 其他度量

度量方法 公式 特点 适用场景
欧氏距离 ∣a−b∣2|\mathbf{a} - \mathbf{b}|_2∣a−b∣2 受长度影响,绝对差异 聚类、KNN
余弦相似度 a⋅b∣a∣∣b∣\frac{\mathbf{a} \cdot \mathbf{b}}{|\mathbf{a}| |\mathbf{b}|}∣a∣∣b∣a⋅b 归一化,只关心方向 文本、推荐、检索
点积 a⋅b\mathbf{a} \cdot \mathbf{b}a⋅b 未归一化,受长度影响 快速近似,需后续归一化
皮尔逊相关系数 Cov(a,b)σaσb\frac{\text{Cov}(a,b)}{\sigma_a \sigma_b}σaσbCov(a,b) 中心化后的余弦相似度 评分预测、去趋势数据
Jaccard 相似度 $\frac{ A \cap B }{

关键对比

复制代码
向量 a = [1, 2, 3]      向量 b = [2, 4, 6] = 2*a  (a的2倍)
向量 c = [3, 2, 1]      (与a方向不同)

欧氏距离: d(a,b) = ||a - b|| = ||a - 2a|| = ||a|| ≈ 3.74  (较大)
          d(a,c) ≈ 2.83  (较小,但b与a内容比例相同!)

余弦相似度: sim(a,b) = 1.0  (完全相同方向,完美相似)
            sim(a,c) ≈ 0.71  (方向不同,相似度较低)

2.5 应用场景

2.5.1 文本相似度(NLP)

TF-IDF + 余弦相似度 是经典的信息检索组合:

python 复制代码
from sklearn.feature_extraction.text import TfidfVectorizer

documents = [
    "机器学习是人工智能的一个分支",
    "深度学习是机器学习的一种方法",
    "自然语言处理属于人工智能领域"
]

# TF-IDF 向量化
vectorizer = TfidfVectorizer()
tfidf_matrix = vectorizer.fit_transform(documents)

# 计算余弦相似度
similarities = cosine_similarity(tfidf_matrix)
print(similarities)
# [[1.         0.42...    0.35...  ]
#  [0.42...    1.         0.31...  ]
#  [0.35...    0.31...    1.       ]]
2.5.2 推荐系统

用户-物品嵌入的相似度计算

python 复制代码
# 用户嵌入和物品嵌入(来自矩阵分解或神经网络)
user_embedding = np.array([0.5, 0.3, -0.2, 0.8])  # 用户兴趣向量
item_embeddings = np.array([
    [0.6, 0.4, -0.1, 0.7],   # 物品A:电影
    [0.1, 0.9, 0.5, -0.2],   # 物品B:音乐
    [0.5, 0.2, -0.3, 0.9]    # 物品C:电影
])

# 计算用户与所有物品的相似度
scores = cosine_similarity([user_embedding], item_embeddings)[0]
print(f"物品A相似度: {scores[0]:.4f}")  # 0.9876,最相关
print(f"物品B相似度: {scores[1]:.4f}")  # 0.2341,不太相关
print(f"物品C相似度: {scores[2]:.4f}")  # 0.9567,也很相关
2.5.3 图像检索(CLIP)

跨模态相似度计算

python 复制代码
# CLIP 编码后的图像和文本特征(已归一化)
image_features = np.array([0.3, 0.5, -0.2, 0.1, ...])  # 512维
text_features = np.array([0.2, 0.6, -0.1, 0.3, ...])  # 512维

# 余弦相似度(点积即可,因为已归一化)
similarity = np.dot(image_features, text_features)
# 或直接
similarity = cosine_similarity([image_features], [text_features])[0][0]

三、独热编码与余弦相似度的结合应用

3.1 经典场景:协同过滤推荐

用户-物品评分矩阵的独热表示 + 余弦相似度找相似用户

python 复制代码
# 用户-物品交互矩阵(隐式反馈:0/1)
# 行:用户,列:物品,1表示交互过
interaction_matrix = np.array([
    [1, 1, 0, 1, 0],  # 用户A:喜欢物品1,2,4
    [1, 1, 0, 1, 0],  # 用户B:与用户A完全相同
    [0, 0, 1, 0, 1],  # 用户C:完全不同的兴趣
])

# 计算用户相似度(基于独热向量的余弦相似度)
user_similarities = cosine_similarity(interaction_matrix)
print(user_similarities)
# [[1.         1.         0.        ]
#  [1.         1.         0.        ]
#  [0.         0.         1.        ]]

# 应用:给用户A推荐
# 找到相似用户B,推荐B交互过但A未交互的物品

3.2 多热编码(Multi-Hot)+ 余弦相似度

当用户有多个标签/兴趣时:

python 复制代码
# 用户兴趣标签(多热编码)
# 标签:科技, 体育, 音乐, 电影, 美食, 旅游
user_profiles = np.array([
    [1, 0, 1, 1, 0, 0],  # 用户A:科技+音乐+电影
    [1, 1, 1, 0, 0, 1],  # 用户B:科技+体育+音乐+旅游
    [0, 1, 0, 0, 1, 1],  # 用户C:体育+美食+旅游
])

similarities = cosine_similarity(user_profiles)
print(similarities)
# 用户A和B共享科技、音乐,相似度较高
# 用户A和C无共同兴趣,相似度为0

四、常见问题与注意事项

4.1 独热编码的陷阱

问题 原因 解决方案
维度灾难 类别数 KKK 很大 使用嵌入层(Embedding)
语义缺失 无法表达类别相似性 用预训练嵌入替代(如 Word2Vec)
稀疏存储 大量零元素 使用稀疏矩阵格式(CSR/CSC)
新类别处理 训练时未见过的类别 添加"未知"类别或使用哈希技巧

4.2 余弦相似度的局限

问题 原因 解决方案
忽略向量长度 仅归一化方向 结合点积或欧氏距离
负值难以解释 范围 [−1,1][-1, 1][−1,1] 映射到 [0,1][0, 1][0,1]:1+cos⁡2\frac{1 + \cos}{2}21+cos
对零向量未定义 分母为零 添加平滑项或特殊处理
无法捕捉非线性关系 线性度量 使用核方法或神经网络

4.3 归一化的重要性

余弦相似度计算前,通常需要对向量进行 L2 归一化

anormalized=a∥a∥2 \mathbf{a}_{\text{normalized}} = \frac{\mathbf{a}}{\|\mathbf{a}\|_2} anormalized=∥a∥2a

归一化后,余弦相似度等价于点积:
CosineSimilarity(a,b)=anorm⋅bnorm \text{CosineSimilarity}(\mathbf{a}, \mathbf{b}) = \mathbf{a}{\text{norm}} \cdot \mathbf{b}{\text{norm}} CosineSimilarity(a,b)=anorm⋅bnorm

优势:计算更快,适合大规模检索(如 FAISS、Milvus 等向量数据库)。


五、一句话总结

  • 独热编码 :将离散类别转换为正交的二进制向量 ,消除虚假数值关系,但面临维度灾难和语义缺失问题,高基数场景推荐用嵌入层替代。
  • 余弦相似度 :度量向量方向的相似程度,忽略长度影响,是文本、推荐、检索系统的核心度量,计算高效且可解释性强。

两者结合,构成了机器学习中表示离散数据并度量相似性的基础工具链。

相关推荐
雪碧聊技术2 个月前
11.RAG知识库的原理
向量·余弦相似度·rag知识库
北京地铁1号线2 个月前
2.3 相似度算法详解:Cosine Similarity 与 Euclidean Distance
算法·余弦相似度
大千AI助手5 个月前
独热编码:分类数据处理的基石技术
人工智能·机器学习·分类·数据挖掘·特征工程·one-hot·独热编码
2401_841495646 个月前
【自然语言处理】文本表示知识点梳理与习题总结
人工智能·自然语言处理·词向量·文本表示·独热编码·词-词共现矩阵·静态词嵌入
三天不学习1 年前
浅析AI大模型为何需要向量数据库?【入门基础】
数据库·人工智能·欧氏距离·向量数据库·余弦相似度
Imageshop2 年前
【工程应用十】基于十六角度量化的夹角余弦相似度模版匹配算法原理解析。
余弦相似度·模版匹配·边缘匹配
小胡说人工智能3 年前
基于Pandas+余弦相似度+大数据智能护肤品推荐系统——机器学习算法应用(含Python工程源码)+数据集
大数据·人工智能·python·机器学习·pandas·推荐系统·余弦相似度