2、向量化基础:从原始数据到数学向量的第一公里

各位同学,今天我们聊一个非常基础但又极其关键的话题——向量化。说白了,就是把文本、图像这些人类能理解的东西,变成机器能计算的数字序列。我在做向量数据库选型时发现,很多人只关心检索性能,却忽略了向量质量才是决定系统上限的命门。

2.1 Embedding 技术原理:万物皆可向量

Embedding 这个词,直译是“嵌入”。什么意思呢?就是把一个高维的、离散的符号,映射到一个低维的、连续的向量空间里。你想想看,一个词、一张图、一段音频,本质上都是符号。计算机不认识“苹果”这个词,但它能计算 [0.12, -0.45, 0.78, ...] 这个向量。

核心思想就一句话:语义相近的对象,在向量空间里距离也近。

Embedding 的本质:它是一个从原始特征空间到分布式表示空间的映射函数 f: X → Rd,其中 d 是向量维度。这个映射要保留原始数据的语义结构。

我在项目中遇到过一种典型误区:有人直接把 one-hot 编码当向量用。比如“苹果”=[1,0,0],“香蕉”=[0,1,0],“手机”=[0,0,1]。这三个向量两两之间的夹角都是 90 度,语义距离完全一样。这显然不合理——苹果和香蕉都是水果,应该比手机更近才对。Embedding 要解决的就是这个问题。

2.2 文本向量化:从 Word2Vec 到 BERT

2.4.1 Word2Vec:静态向量的经典

Word2Vec 是 Google 在 2013 年提出的。它有两种训练方式:CBOW(连续词袋)和 Skip-gram。CBOW 用上下文预测当前词,Skip-gram 用当前词预测上下文。我个人习惯用 Skip-gram,因为它在处理低频词时效果更好。

训练完成后,每个词对应一个固定向量。比如“国王”减去“男人”加上“女人”,结果会非常接近“女王”。这就是经典的向量偏移现象。

# Word2Vec 训练示例(Gensim)
from gensim.models import Word2Vec

sentences = [["我", "喜欢", "吃", "苹果"],
             ["他", "喜欢", "吃", "香蕉"]]
model = Word2Vec(sentences, vector_size=128, window=5, min_count=1, sg=1)
vector = model.wv["苹果"]  # 获取向量,shape: (128,)

避坑指南:Word2Vec 的向量是静态的,一个词只有一个向量。这意味着“苹果”(水果)和“苹果”(手机品牌)会被映射到同一个点。我曾经在一个电商搜索项目中踩过这个坑,后来不得不改用 BERT 做消歧。

2.4.2 BERT:动态向量的革命

BERT 的出现改变了游戏规则。它不再是给每个词一个固定向量,而是根据上下文动态生成。同样的“苹果”,在“吃苹果”和“买苹果”中,向量完全不同。

BERT 的核心是 Transformer 的 Encoder 部分,通过双向注意力机制捕捉上下文信息。输出时,我们可以取最后一层的 [CLS] 作为句子向量,也可以对所有 做平均池化。

# BERT 向量提取示例(HuggingFace)
from transformers import BertTokenizer, BertModel
import torch

tokenizer = BertTokenizer.from_pretrained("bert-base-chinese")
model = BertModel.from_pretrained("bert-base-chinese")

text = "我喜欢吃苹果"
inputs = tokenizer(text, return_tensors="pt")
outputs = model(**inputs)

# 取 [CLS]  作为句子向量
sentence_vector = outputs.last_hidden_state[:, 0, :].detach().numpy()
# shape: (1, 768)

注意:BERT 的向量维度通常是 768(base 版本)或 1024(large 版本)。这比 Word2Vec 的 128-300 维大得多。维度越高,表达能力越强,但存储和检索成本也越高。在流批一体架构中,你需要权衡精度和延迟。

2.3 图像向量化:从 CNN 到 ResNet

图像向量化的思路和文本类似,但输入是像素矩阵。早期做法是把像素拉平成一维向量,但效果很差。为什么?因为图像有空间结构,拉平会丢失局部特征。

CNN(卷积神经网络)解决了这个问题。它通过卷积核在图像上滑动,提取局部特征,再通过池化层降维。最后接一个全连接层,输出固定长度的向量。

2.3.1 CNN 做图像向量的基本流程

  1. 输入图像,尺寸归一化(如 224x224x3)
  2. 经过多层卷积+池化,提取特征图
  3. 全局平均池化,把特征图压缩成向量
  4. 可选:再接一个全连接层,输出指定维度

我记得在做一个商品图片检索系统时,直接用 VGG16 的 fc7 层输出(4096 维)做向量。结果检索速度慢得离谱。后来改用 ResNet50 的全局平均池化层输出(2048 维),精度没降多少,速度翻了一倍。

2.3.2 ResNet:解决退化问题

ResNet 的核心创新是残差连接。简单说,就是让网络学习“残差”而不是“原始映射”。假设期望输出是 H(x),传统网络直接学 H(x),ResNet 学 F(x) = H(x) - x,然后输出 F(x) + x。

这样做的好处是:梯度可以直接通过恒等映射传播,不会消失。所以 ResNet 可以做到 152 层甚至更深,而 VGG 到 19 层就饱和了。

# 用 ResNet50 提取图像向量(PyTorch)
import torch
import torchvision.models as models
from torchvision import transforms
from PIL import Image

model = models.resnet50(pretrained=True)
model = torch.nn.Sequential(*list(model.children())[:-1])  # 去掉最后的分类层

transform = transforms.Compose([
    transforms.Resize((224, 224)),
    transforms.ToTensor(),
    transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
])

img = Image.open("apple.jpg")
img_tensor = transform(img).unsqueeze(0)
with torch.no_grad():
    vector = model(img_tensor).squeeze().numpy()
# shape: (2048,)

经验之谈:用预训练模型做图像向量时,建议去掉最后的全连接分类层,用全局平均池化层的输出。这样得到的向量更具泛化性,适合做相似度检索。我一般用 ResNet50 的 2048 维输出,效果和速度比较均衡。

2.4 向量维度与相似度度量

2.4.1 维度选择:不是越大越好

向量维度是个需要仔细权衡的参数。维度越高,表达能力越强,但也会带来三个问题:

  • 存储成本:每个向量占用 d × 4 字节(float32),100 亿条 768 维向量需要约 2.8TB
  • 计算成本:高维向量的距离计算更慢,O(d) 的复杂度在 d 很大时不可忽视
  • 维度灾难:高维空间中,所有点之间的距离趋于相等,区分度下降
模型 典型维度 适用场景 存储成本(1亿条)
Word2Vec 128-300 关键词匹配、主题分类 ~120GB
BERT-base 768 语义搜索、问答系统 ~286GB
ResNet50 2048 图像检索、视觉搜索 ~763GB
Ada-002 1536 通用文本嵌入 ~572GB

2.4.2 相似度度量:选对距离公式

有了向量,怎么比较它们是否相似?常用的度量有三种:

  • 余弦相似度:cos(A, B) = A·B / (|A|×|B|),范围 [-1, 1]。只关心方向,不关心长度。文本向量常用。
  • 欧氏距离:d(A, B) = sqrt(Σ(Ai-Bi)²),范围 [0, +∞)。关心位置。图像向量常用。
  • 点积:A·B = Σ(Ai×Bi),范围 (-∞, +∞)。如果向量已归一化,等价于余弦相似度。

我的建议:在流批一体架构中,如果向量已经做了 L2 归一化,点积和余弦相似度等价。此时优先用点积,因为计算更快(少一次除法)。我在 Milvus 和 Faiss 中都是这么配置的。

为什么会这样?因为归一化后 |A|=|B|=1,余弦公式简化为 A·B。而点积只需要乘法和加法,不需要开方和除法,硬件更友好。

2.5 本章知识体系总览

下面这张图总结了向量化的核心流程和关键决策点。你可以把它当作一个快速参考。

向量化知识体系总览 向量化知识体系总览 原始数据 文本 Word2Vec / BERT 图像 CNN / ResNet 向量表示 (d 维) 维度选择:128 ~ 2048 余弦 / 欧氏 / 点积 关键决策点 模型选型 → 维度权衡 归一化 → 度量选择 流批一体关注点 批量预计算 vs 实时推理 向量存储与索引策略

从这张图可以看出,向量化的核心链路是:原始数据 → 模型提取 → 向量表示 → 维度选择 → 相似度度量。每一步都有多个选项,没有银弹。你需要根据业务场景、数据规模、延迟要求来做取舍。

最后提醒一句:向量化是整个向量检索系统的起点。如果这一步没做好,后面再牛的索引算法也救不了。我见过太多团队花大量时间调优 Milvus 参数,结果发现是向量质量太差——词都没分对,模型没选对,维度没调好。嗯,先把基础打牢。