跳转至

第4章:基础概念解析 - 嵌入、维度、相似度度量

4.1 向量嵌入(Embedding)

什么是向量嵌入

向量嵌入(Embedding) 是将离散、高维的原始数据(如文字、图片、音频)转换为连续、低维的稠密向量的过程。

核心目标:让"语义相似"的数据在向量空间中"距离相近"。

# 向量化示例
from sentence_transformers import SentenceTransformer

model = SentenceTransformer('all-MiniLM-L6-v2')

# 文本嵌入
texts = ["我想买一部iPhone手机", "iPhone拍照效果很好", "今天天气真不错"]
embeddings = model.encode(texts)

print(f"向量形状: {embeddings.shape}")  # (3, 384) - 3个文本,每个384维
print(f"第一个文本的向量: {embeddings[0][:5]}...")  # [0.12, -0.34, 0.56, ...]

嵌入的直观理解

graph TD
    subgraph 嵌入过程
        A["原始数据"] -->|Embedding Model| B["高维向量"]
    end

    subgraph 不同数据的嵌入
        B -->|文本| T["文字向量"]
        B -->|图片| I["图像向量"]
        B -->|音频| S["音频向量"]
        B -->|视频| V["视频向量"]
    end

    subgraph 向量空间
        T -->|"位置相近"| T1["'狗' → [0.2, 0.8]"]
        T -->|"位置相近"| T2["'猫' → [0.2, 0.7]"]
        T1 -->|"位置远离"| T3["'汽车' → [0.9, 0.1]"]
    end

    style A fill:#e1f5fe
    style B fill:#fff3e0
    style T fill:#c8e6c9
    style I fill:#c8e6c9
    style S fill:#c8e6c9
    style V fill:#c8e6c9

嵌入的几个关键特性

1. 语义保持

语义相近的内容在向量空间中距离较近:

# 语义相似 → 向量距离近
result = cosine_similarity(
    embed("国王"),    # [0.8, 0.2, 0.1, ...]
    embed("王后")     # [0.7, 0.3, 0.2, ...]
)
print(f"国王 vs 王后: {result:.3f}")  # ~0.95 (很接近)

result = cosine_similarity(
    embed("国王"),
    embed("汽车")
)
print(f"国王 vs 汽车: {result:.3f}")  # ~0.1 (距离远)

2. 可计算性

向量之间可以进行数学运算:

# 著名的 king - man + woman ≈ queen
# 这个例子完美展示了向量嵌入的语义空间

king = embed("king")
man = embed("man")
woman = embed("woman")

# 向量运算
result = king - man + woman

# 验证结果与 queen 的相似度
queen = embed("queen")
similarity = cosine_similarity(result, queen)
print(f"king - man + woman 接近 queen: {similarity:.3f}")  # ~0.8

3. 维度约简

将高维稀疏数据转换为低维稠密向量:

原始数据 One-Hot编码维度 嵌入后维度 压缩比
英文单词 ~50,000 128-512 100-400x
图片像素 224×224×3 512-2048 60-300x
文档词汇 10,000+ 768-1536 10-15x

主流嵌入模型

模型 开发者 向量维度 特点
Word2Vec Google 100-300 词嵌入经典
BERT Google 768-1024 上下文相关
Sentence-BERT UKPLab 384-1024 句子级别
OpenAI Embeddings OpenAI 1536 API调用
CLIP OpenAI 512-1024 多模态
text-embedding-3 OpenAI 3072 最新版本
# 不同嵌入模型对比
from sentence_transformers import SentenceTransformer

# 轻量级模型
light_model = SentenceTransformer('all-MiniLM-L6-v2')
light_dim = light_model.get_sentence_embedding_dimension()  # 384

# 高质量模型
quality_model = SentenceTransformer('all-mpnet-base-v2')
quality_dim = quality_model.get_sentence_embedding_dimension()  # 768

print(f"轻量模型维度: {light_dim}, 质量模型维度: {quality_dim}")

4.2 维度(Dimension)

什么是维度

维度(Dimension) 是指向量中包含的数值元素的个数。

# 一维向量
v1 = [0.5]  # 1维

# 二维向量(平面上的点)
v2 = [0.3, 0.7]  # 2维

# 三维向量(空间中的点)
v3 = [0.2, 0.5, 0.8]  # 3维

# 高维向量(AI中的常见形式)
v384 = [0.1] * 384  # 384维
v1536 = [0.1] * 1536  # 1536维

维度与信息量

graph LR
    subgraph 维度与表达能力
        A["低维度 (2-50)"] -->|"信息有限
表达能力弱"| B["只能捕捉简单特征"] C["中维度 (100-500)"] -->|"平衡选择
性价比高"| D["语义信息丰富"] E["高维度 (1000+)"] -->|"信息丰富
但存储成本高"| F["细微语义差异"] end style A fill:#ffccbc style C fill:#c8e6c9 style E fill:#e1f5fe

维度的实际影响

维度范围 典型应用 存储空间(1M向量) 搜索速度
128 简单语义 ~512MB 极快
384 通用文本 ~1.5GB
768 高质量文本 ~3GB 中等
1536 精细语义 ~6GB 较慢
3072 最高质量 ~12GB
# 计算向量存储空间
def calculate_storage(num_vectors, dimension, bytes_per_float=4):
    """计算存储空间"""
    total_bytes = num_vectors * dimension * bytes_per_float
    return total_bytes / (1024**3)  # 转换为GB

print(f"1M向量 x 384维 = {calculate_storage(1_000_000, 384):.2f} GB")
print(f"1M向量 x 1536维 = {calculate_storage(1_000_000, 1536):.2f} GB")

维度灾难

当维度非常高时,向量之间的距离区分度会降低,这就是"维度灾难"。

# 维度灾难示意
import numpy as np

def avg_distance_in_unit_hyperball(dim, n_samples=10000):
    """计算单位超球体中随机点的平均距离"""
    points = np.random.uniform(-1, 1, (n_samples, dim))
    # 随机计算一些距离
    distances = []
    for i in range(min(1000, n_samples)):
        for j in range(i+1, min(1000, n_samples)):
            distances.append(np.linalg.norm(points[i] - points[j]))
    return np.mean(distances)

dims = [2, 10, 50, 100, 500, 1000, 5000]
for d in dims:
    avg_dist = avg_distance_in_unit_hyperball(d)
    print(f"维度 {d:4d}: 平均距离 {avg_dist:.3f}")

# 维度越高,所有点之间的距离差异越小
# 这使得区分"相似"和"不相似"变得困难
graph TD
    subgraph 维度灾难示意
        A["低维度空间"] -->|"点分布紧密
距离差异明显"| B["容易区分相似/不相似"] C["高维度空间"] -->|"点分布稀疏
距离趋于一致"| D["难以区分相似/不相似"] end style A fill:#c8e6c9 style B fill:#c8e6c9 style C fill:#ffccbc style D fill:#ffccbc

4.3 相似度度量(Similarity Metrics)

相似度度量对比

graph TD
    subgraph 欧氏距离 L2
        L2_1["√[(a₁-b₁)² + (a₂-b₂)² + ...]"]
        L2_2["值越小越相似"]
        L2_3["范围: [0, +∞)"]
    end

    subgraph 余弦相似度 Cosine
        C_1["(A · B) / (|A| × |B|)"]
        C_2["值越大越相似"]
        C_3["范围: [-1, 1]"]
    end

    subgraph 点积 Dot Product
        D_1["A₁B₁ + A₂B₂ + ..."]
        D_2["值越大越相似"]
        D_3["范围: (-∞, +∞)"]
    end

    subgraph 曼哈顿距离 L1
        M_1["|a₁-b₁| + |a₂-b₂| + ..."]
        M_2["值越小越相似"]
        M_3["范围: [0, +∞)"]
    end

1. 欧氏距离(L2 Distance)

定义:向量各维度差值平方和的平方根。

import numpy as np

def euclidean_distance(a, b):
    """欧氏距离"""
    return np.linalg.norm(a - b)

# 示例
a = np.array([1, 2, 3])
b = np.array([4, 6, 3])

dist = euclidean_distance(a, b)
print(f"欧氏距离: {dist:.2f}")  # 4.47

# 几何解释:在二维空间中,这是两点之间的直线距离

2. 余弦相似度(Cosine Similarity)

定义:两个向量夹角的余弦值。

def cosine_similarity(a, b):
    """余弦相似度"""
    dot = np.dot(a, b)
    norm_a = np.linalg.norm(a)
    norm_b = np.linalg.norm(b)
    return dot / (norm_a * norm_b)

# 示例
a = np.array([1, 1, 0])
b = np.array([1, 1, 1])
c = np.array([-1, -1, 0])

print(f"a与b: {cosine_similarity(a, b):.3f}")  # 0.816 (夹角45°)
print(f"a与c: {cosine_similarity(a, c):.3f}")  # -1.000 (夹角180°)

3. 点积相似度(Dot Product)

定义:对应元素相乘后求和。

def dot_product(a, b):
    """点积"""
    return np.dot(a, b)

# 示例
a = np.array([1, 2, 3])
b = np.array([4, 5, 6])

dot = dot_product(a, b)
print(f"点积: {dot}")  # 32 (1*4 + 2*5 + 3*6)

4. 归一化后的关系

当向量经过L2归一化后,点积等价于余弦相似度:

def normalized_dot_product(a, b):
    """归一化后的点积 = 余弦相似度"""
    a_norm = a / np.linalg.norm(a)
    b_norm = b / np.linalg.norm(b)
    return np.dot(a_norm, b_norm)

a = np.array([3, 4])  # 模长=5
b = np.array([4, 3])  # 模长=5

print(f"余弦相似度: {cosine_similarity(a, b):.3f}")  # 0.896
print(f"归一化点积: {normalized_dot_product(a, b):.3f}")  # 0.896

如何选择相似度度量

场景 推荐度量 原因
文本语义搜索 余弦相似度 文本向量方向比长度更重要
人脸识别 余弦相似度 忽略光照对向量长度的影响
推荐系统 点积 需要考虑用户偏好强度
图像特征匹配 欧氏距离 特征强度本身有意义
音乐推荐 余弦相似度 风格方向比热度更重要
# 实际选择示例

# 场景1: 语义文本搜索
query = "推荐好用的笔记本电脑"
doc_embeddings = model.encode(corpus)
query_embedding = model.encode(query)

# 使用余弦相似度
similarities = cosine_similarity([query_embedding], doc_embeddings)[0]
top_k = np.argsort(similarities)[-5:][::-1]

# 场景2: 推荐系统
user_preference = user_embedding  # 用户偏好向量
item_features = item_embeddings   # 物品特征向量

# 使用点积(考虑强度)
scores = np.dot(user_preference, item_features.T)
top_k = np.argsort(scores)[-5:][::-1]

4.4 批处理与实时处理

批量向量化

当需要处理大量数据时,使用批处理可以显著提升效率:

from sentence_transformers import SentenceTransformer

model = SentenceTransformer('all-MiniLM-L6-v2')

# 单条处理(慢)
texts = ["文本1", "文本2", "文本3"]
for text in texts:
    embedding = model.encode(text)  # 每条单独编码

# 批量处理(快)
batch_embeddings = model.encode(texts, batch_size=32)  # 一次性编码
print(f"批量形状: {batch_embeddings.shape}")  # (3, 384)

批处理性能对比

方式 100条文本 10,000条文本 1,000,000条文本
单条处理 2秒 200秒 20,000秒
批量处理(32) 0.5秒 8秒 800秒
加速比 4x 25x 25x

4.5 概念速查表

graph TD
    subgraph 核心概念
        E[Embedding
向量嵌入] D[Dimension
维度] S[Similarity
相似度度量] I[Index
索引结构] N[NNS
最近邻搜索] ANN[ANN
近似最近邻] end subgraph 关系 E -->|生成| V[Vector
向量] V -->|具有| D D -->|影响| S V -->|建立| I I -->|加速| N N -->|近似版| ANN end style E fill:#e1f5fe style D fill:#fff3e0 style S fill:#c8e6c9 style I fill:#e1f5fe style N fill:#fff3e0 style ANN fill:#c8e6c9 style V fill:#ffccbc

关键术语定义

术语 英文 定义 示例
向量嵌入 Embedding 将数据转换为高维向量的技术 embed("苹果")[0.8, 0.1, ...]
维度 Dimension 向量中元素的个数 384维、1536维
相似度度量 Similarity Metric 衡量向量间相似程度的方法 余弦相似度、欧氏距离
索引 Index 加速检索的数据结构 HNSW、IVF
最近邻 Nearest Neighbor 与查询向量最相似的向量 K=1时返回1个最近邻
近似最近邻 ANN 用精度换速度的近似搜索 99%精度+10x速度

本章小结

本章要点:

  1. 向量嵌入(Embedding)
  2. 将文本、图片等数据转换为数值向量
  3. 语义相似的内容在向量空间中距离相近
  4. 使用预训练模型可以快速获得高质量嵌入

  5. 维度(Dimension)

  6. 维度越高,表达能力越强,但存储成本越高
  7. 需要在效果和性能之间找到平衡
  8. 常见维度:384(轻量)、768(标准)、1536(高质量)

  9. 相似度度量

  10. 余弦相似度:最常用,适合文本语义搜索
  11. 欧氏距离:适合考虑向量大小的场景
  12. 点积:适合推荐系统等需要考虑强度的场景

  13. 性能优化

  14. 批量处理可以显著提升向量化效率
  15. 选择合适的向量维度平衡效果和性能

下一章预告第5章:典型应用场景 - 了解向量数据库在实际应用中的使用方法 →