4.1.2 向量:AI 世界的基本单元

- 直觉理解向量是什么(方向 + 大小)
- 掌握向量的加法、数乘运算
- 理解点积(Dot Product)的含义
- 掌握余弦相似度——AI 中最常用的相似度度量
- 用 NumPy 实现所有向量操作
先说一个很重要的学习预期
Section titled “先说一个很重要的学习预期”这一节虽然会配代码,但代码不是用来替代理解的。 它更像在做两件事:
- 帮你把抽象对象变成能看到的东西
- 帮你确认“我理解的直觉是不是对的”
如果你读完这节后,还不能马上熟练做题,这很正常。 更重要的标准是:
- 你能不能把“一个现实对象”写成向量
- 你能不能说清点积和余弦相似度到底在比较什么
- 你能不能把它们和推荐、检索、RAG 这些 AI 场景连起来
先建立一张地图
Section titled “先建立一张地图”这一节最重要的不是记住多少名词,而是先抓住一条主线:

你可以把这节课理解成:
- 前半部分解决“一个对象怎样写成向量”
- 后半部分解决“两个向量怎样比较是否相似”
随手查的小词典
Section titled “随手查的小词典”| 术语 | 含义 | 新人为什么需要知道 |
|---|---|---|
scalar | 标量,也就是一个单独的数,比如 2 或 0.5 | 数乘就是“用一个数整体缩放向量”。 |
dimension | 维度,也就是向量里有多少个分量 | [90, 85, 92] 有 3 个数字,所以是 3 维。 |
shape | NumPy 对数组结构的描述 | (3,)、(1, 3)、(3, 1) 都有 3 个数字,但在乘法里行为不同。 |
norm | 范数,也就是向量长度 | np.linalg.norm(a) 用来计算向量有多长、强度多大。 |
NLP | Natural Language Processing,自然语言处理 | 文本向量、词向量是 AI 中最常见的向量例子之一。 |
vector database | 向量数据库,专门存储和搜索向量的数据库 | 很多 RAG 和语义搜索系统都靠它做检索。 |
这张表不是让你背单词,而是当作安全网。后面代码再次出现这些词时,回来对照它和当前操作的关系即可。
一、向量是什么?
Section titled “一、向量是什么?”向量 = 一组有序的数字。
一个更适合新人的类比
Section titled “一个更适合新人的类比”如果你第一次学向量,总觉得“方向 + 大小”还是有点抽象,可以先把它想成:
- 一张对象的信息卡片
比如一次模型评估记录:
- 准确率 0.86
- 延迟 120 ms
- 显存 3.8 GB
把这三项按固定顺序排好, 就得到一张可以被计算机处理的信息卡片:
[0.86, 120, 3.8]
所以向量最朴素的意义不是“几何图形”,而是:
把一个对象稳定地写成一串数字。
就这么简单。在 AI 领域,向量无处不在:
| AI 场景 | 向量表示 | 维度 |
|---|---|---|
| 一次模型运行 | [准确率, 延迟ms, 显存GB] = [0.86, 120, 3.8] | 3 维 |
| 一个像素的颜色 | [R, G, B] = [255, 128, 0] | 3 维 |
| 一个词的含义(词向量) | [0.2, -0.5, 0.8, …] | 通常 100~300 维 |
| 一张图片(展平后) | [像素1, 像素2, …, 像素n] | 几万到几百万维 |
root((向量在 AI 中)) 数据表示 每一行数据就是一个向量 图片是像素向量 文本是词向量 相似度计算 推荐系统 搜索引擎 人脸识别 模型参数 神经网络的权重 梯度也是向量在二维空间中,向量可以画成一个带箭头的线段——既有方向,又有大小(长度)。
import numpy as npimport matplotlib.pyplot as plt
plt.rcParams['font.sans-serif'] = ['Arial Unicode MS']plt.rcParams['axes.unicode_minus'] = False
# 定义两个二维向量a = np.array([3, 2])b = np.array([1, 4])
# 画向量fig, ax = plt.subplots(figsize=(6, 6))ax.quiver(0, 0, a[0], a[1], angles='xy', scale_units='xy', scale=1, color='steelblue', linewidth=2, label=f'a = {a}')ax.quiver(0, 0, b[0], b[1], angles='xy', scale_units='xy', scale=1, color='coral', linewidth=2, label=f'b = {b}')
ax.set_xlim(-1, 6)ax.set_ylim(-1, 6)ax.set_aspect('equal')ax.grid(True, alpha=0.3)ax.axhline(y=0, color='k', linewidth=0.5)ax.axvline(x=0, color='k', linewidth=0.5)ax.legend(fontsize=12)ax.set_title('二维向量的几何表示')plt.show()解读:向量 a = [3, 2] 从原点出发,向右走 3 步、向上走 2 步。
从一条真实数据记录到向量
Section titled “从一条真实数据记录到向量”新人最容易卡住的一点是:知道“向量是一串数字”,但不知道它怎么和真实数据连起来。
import numpy as np
model_run = { "accuracy": 0.86, "latency_ms": 120, "memory_gb": 3.8,}
model_vector = np.array([ model_run["accuracy"], model_run["latency_ms"], model_run["memory_gb"],])
print("模型运行向量:", model_vector)print("向量形状:", model_vector.shape) # (3,)这里的本质是:
- 现实世界里是“有意义的字段”
- 到计算机里要变成“固定顺序的数字数组”
一旦你把对象写成向量,就能开始做数学运算。
weights = np.array([0.7, -0.002, -0.03])score = model_vector @ weightsprint("部署评分:", round(score, 3)) # 0.248这里其实已经提前连到了后面的机器学习主线:
- 数据是向量
- 规则也是向量
- 二者做点积,可以得到一个分数
二、向量的基本运算
Section titled “二、向量的基本运算”两个向量相加 = 对应位置的数字相加。
a = np.array([3, 2])b = np.array([1, 4])
# 向量加法c = a + bprint(f"a + b = {c}") # [4, 6]几何含义:把 b 接在 a 的末端,结果指向终点。
fig, ax = plt.subplots(figsize=(7, 7))
# 画 aax.quiver(0, 0, a[0], a[1], angles='xy', scale_units='xy', scale=1, color='steelblue', linewidth=2, label=f'a = {a}')# 画 b(从 a 的末端开始)ax.quiver(a[0], a[1], b[0], b[1], angles='xy', scale_units='xy', scale=1, color='coral', linewidth=2, label=f'b = {b}')# 画 a + bax.quiver(0, 0, c[0], c[1], angles='xy', scale_units='xy', scale=1, color='green', linewidth=2.5, label=f'a + b = {c}')
ax.set_xlim(-1, 7)ax.set_ylim(-1, 8)ax.set_aspect('equal')ax.grid(True, alpha=0.3)ax.legend(fontsize=11)ax.set_title('向量加法:首尾相接')plt.show()数乘(标量乘法)
Section titled “数乘(标量乘法)”向量乘以一个数 = 每个分量都乘以这个数。
a = np.array([3, 2])
# 数乘print(f"2 * a = {2 * a}") # [6, 4] —— 方向不变,长度变 2 倍print(f"0.5 * a = {0.5 * a}") # [1.5, 1.0] —— 方向不变,长度变一半print(f"-1 * a = {-1 * a}") # [-3, -2] —— 方向反转fig, ax = plt.subplots(figsize=(8, 6))
vectors = [ (a, 'steelblue', f'a = {a}'), (2 * a, 'green', f'2a = {2*a}'), (0.5 * a, 'orange', f'0.5a = {0.5*a}'), (-1 * a, 'red', f'-a = {-1*a}'),]
for vec, color, label in vectors: ax.quiver(0, 0, vec[0], vec[1], angles='xy', scale_units='xy', scale=1, color=color, linewidth=2, label=label)
ax.set_xlim(-5, 8)ax.set_ylim(-4, 6)ax.set_aspect('equal')ax.grid(True, alpha=0.3)ax.axhline(y=0, color='k', linewidth=0.5)ax.axvline(x=0, color='k', linewidth=0.5)ax.legend(fontsize=11)ax.set_title('数乘:缩放和反转')plt.show()向量的长度(模/范数)
Section titled “向量的长度(模/范数)”
向量的长度(也叫模或范数)用勾股定理计算:
对于向量 a = [a1, a2],长度 = 根号(a1 的平方 + a2 的平方)
a = np.array([3, 4])
# 方法 1:手算length_manual = np.sqrt(a[0]**2 + a[1]**2)print(f"手算长度: {length_manual}") # 5.0
# 方法 2:NumPy 内置函数(推荐)length = np.linalg.norm(a)print(f"NumPy 长度: {length}") # 5.0长度为 1 的向量叫单位向量。任何向量除以它的长度,就变成同方向的单位向量:
a = np.array([3, 4])
# 单位化(归一化)unit_a = a / np.linalg.norm(a)print(f"单位向量: {unit_a}") # [0.6, 0.8]print(f"单位向量长度: {np.linalg.norm(unit_a)}") # 1.0为什么重要? 在 AI 中,我们经常需要比较两个向量的方向而不是大小。单位化后就只保留了方向信息。
新人一定要建立的 shape 感
Section titled “新人一定要建立的 shape 感”很多人一开始学向量,概念能懂,但一写代码就被 shape 绕晕。
import numpy as np
a = np.array([1, 2, 3]) # 一维向量row = a.reshape(1, 3) # 行向量col = a.reshape(3, 1) # 列向量
print("a.shape =", a.shape) # (3,)print("row.shape =", row.shape) # (1, 3)print("col.shape =", col.shape) # (3, 1)它们看起来都像“3 个数字”,但在矩阵乘法里含义不同:
(3,)是 NumPy 里的普通一维数组(1, 3)明确表示“1 行 3 列”(3, 1)明确表示“3 行 1 列”
后面学矩阵和神经网络时,shape 感比死背公式更重要。
三、点积——向量最重要的运算
Section titled “三、点积——向量最重要的运算”什么是点积?
Section titled “什么是点积?”两个向量的点积(Dot Product)= 对应位置相乘,再求和。
a = np.array([1, 2, 3])b = np.array([4, 5, 6])
# 方法 1:手算dot_manual = a[0]*b[0] + a[1]*b[1] + a[2]*b[2]print(f"手算: {dot_manual}") # 1*4 + 2*5 + 3*6 = 32
# 方法 2:NumPy(推荐)dot_np = np.dot(a, b)print(f"NumPy: {dot_np}") # 32
# 方法 3:@ 运算符(Python 3.5+)dot_at = a @ bprint(f"@ 运算符: {dot_at}") # 32点积的几何含义
Section titled “点积的几何含义”点积反映了两个向量的方向关系:
flowchart LR A["a · b > 0"] --> A1["方向大致相同<br/>夹角 < 90°"] B["a · b = 0"] --> B1["完全垂直<br/>夹角 = 90°"] C["a · b < 0"] --> C1["方向大致相反<br/>夹角 > 90°"]
style A fill:#e8f5e9,stroke:#2e7d32,color:#333 style B fill:#e3f2fd,stroke:#1565c0,color:#333 style C fill:#ffebee,stroke:#c62828,color:#333# 同方向a = np.array([1, 0])b = np.array([1, 1])print(f"同方向: a · b = {np.dot(a, b)}") # 1(正数)
# 垂直a = np.array([1, 0])b = np.array([0, 1])print(f"垂直: a · b = {np.dot(a, b)}") # 0
# 反方向a = np.array([1, 0])b = np.array([-1, 0])print(f"反方向: a · b = {np.dot(a, b)}") # -1(负数)为什么点积可以理解成“对齐程度”?
Section titled “为什么点积可以理解成“对齐程度”?”点积还有一个非常重要的理解角度:
一个向量在另一个向量方向上投影得越多,点积通常越大。
它的公式可以写成:
a · b = |a| × |b| × cos(theta)
你现在不用先推导它,只要先记住三件事:
- 两个向量越同向,
cos(theta)越接近 1 - 两个向量越垂直,
cos(theta)越接近 0 - 两个向量越反向,
cos(theta)越接近 -1
所以点积同时包含了:
- 长度信息
- 方向信息
用可视化理解点积
Section titled “用可视化理解点积”fig, axes = plt.subplots(1, 3, figsize=(15, 4))
cases = [ ([2, 1], [1, 2], '同向(点积 > 0)'), ([2, 0], [0, 2], '垂直(点积 = 0)'), ([2, 1], [-1, -2], '反向(点积 < 0)'),]
for ax, (a, b, title) in zip(axes, cases): a, b = np.array(a), np.array(b) dot = np.dot(a, b)
ax.quiver(0, 0, a[0], a[1], angles='xy', scale_units='xy', scale=1, color='steelblue', width=0.02, label='a') ax.quiver(0, 0, b[0], b[1], angles='xy', scale_units='xy', scale=1, color='coral', width=0.02, label='b')
ax.set_xlim(-3, 4) ax.set_ylim(-3, 4) ax.set_aspect('equal') ax.grid(True, alpha=0.3) ax.axhline(y=0, color='k', linewidth=0.5) ax.axvline(x=0, color='k', linewidth=0.5) ax.set_title(f'{title}\na·b = {dot}') ax.legend()
plt.tight_layout()plt.show()四、余弦相似度——AI 中最常用的相似度
Section titled “四、余弦相似度——AI 中最常用的相似度”从点积到余弦相似度
Section titled “从点积到余弦相似度”点积的大小不仅取决于方向,还取决于向量的长度。如果我们只关心方向有多相似,需要消除长度的影响:
余弦相似度 = 点积 / (向量 A 的长度 × 向量 B 的长度)
def cosine_similarity(a, b): """计算两个向量的余弦相似度""" 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: raise ValueError("零向量没有方向,无法计算余弦相似度。") return dot_product / (norm_a * norm_b)这里检查零向量很重要,因为长度为 0 的向量没有方向。余弦相似度比较的是方向,如果除以 0,就会得到误导性的结果或运行警告。
余弦相似度的取值范围:
| 值 | 含义 |
|---|---|
| 1 | 方向完全相同 |
| 0 | 完全不相关(垂直) |
| -1 | 方向完全相反 |
实例:运行画像相似度
Section titled “实例:运行画像相似度”假设有三个模型服务画像,分别记录五个归一化信号:
# [准确率, 吞吐, 低延迟, 低显存, 稳定性] 的画像分数(1-5)baseline = np.array([4, 3, 2, 2, 4])quantized = np.array([4, 3, 3, 3, 4])oversized = np.array([5, 1, 1, 1, 3])
# 计算两两相似度print(f"Baseline vs quantized: {cosine_similarity(baseline, quantized):.4f}")print(f"Baseline vs oversized: {cosine_similarity(baseline, oversized):.4f}")print(f"Quantized vs oversized:{cosine_similarity(quantized, oversized):.4f}")输出:
Baseline vs quantized: 0.9857Baseline vs oversized: 0.9159Quantized vs oversized:0.8775解读:量化模型比 oversized 模型更接近 baseline;而量化和 oversized 的方向差异更大。向量搜索和推荐系统也是类似思路:先比较方向,再结合业务判断检查最接近的候选。
余弦相似度在 AI 中的应用
Section titled “余弦相似度在 AI 中的应用”flowchart TD CS["余弦相似度"] CS --> NLP["自然语言处理<br/>词向量相似度<br/>king - man + woman ≈ queen"] CS --> RS["推荐系统<br/>用户偏好匹配<br/>协同过滤"] CS --> RAG["RAG 检索增强<br/>查询与文档匹配<br/>向量数据库"] CS --> IR["图像检索<br/>找相似图片<br/>以图搜图"]
style CS fill:#e3f2fd,stroke:#1565c0,color:#333 style NLP fill:#fff3e0,stroke:#e65100,color:#333 style RS fill:#fff3e0,stroke:#e65100,color:#333 style RAG fill:#fff3e0,stroke:#e65100,color:#333 style IR fill:#fff3e0,stroke:#e65100,color:#333可视化:不同余弦相似度的向量
Section titled “可视化:不同余弦相似度的向量”fig, axes = plt.subplots(1, 4, figsize=(16, 4))
# 不同相似度的向量对pairs = [ ([1, 0], [1, 0.1], '≈ 1.0(几乎相同)'), ([1, 0], [0.7, 0.7], '≈ 0.7(比较相似)'), ([1, 0], [0, 1], '= 0(不相关)'), ([1, 0], [-0.9, -0.3], '≈ -0.95(相反)'),]
for ax, (a, b, desc) in zip(axes, pairs): a, b = np.array(a), np.array(b) sim = cosine_similarity(a, b)
ax.quiver(0, 0, a[0], a[1], angles='xy', scale_units='xy', scale=1, color='steelblue', width=0.02) ax.quiver(0, 0, b[0], b[1], angles='xy', scale_units='xy', scale=1, color='coral', width=0.02)
ax.set_xlim(-1.5, 1.5) ax.set_ylim(-1, 1.5) ax.set_aspect('equal') ax.grid(True, alpha=0.3) ax.set_title(f'cos = {sim:.2f}\n{desc}', fontsize=10)
plt.tight_layout()plt.show()一个最小检索示例:从 3 条候选里找最像的一条
Section titled “一个最小检索示例:从 3 条候选里找最像的一条”下面这个例子虽然用的是手工构造的小向量,但思路和向量检索、RAG、语义搜索是一样的。
import numpy as np
def cosine_similarity(a, b): return np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b))
query = np.array([0.9, 0.1, 0.8, 0.2])
docs = { "文档A:机器学习入门": np.array([0.8, 0.2, 0.75, 0.1]), "文档B:旅行攻略": np.array([0.1, 0.9, 0.2, 0.8]), "文档C:深度学习基础": np.array([0.85, 0.15, 0.7, 0.25]),}
scores = []for name, vec in docs.items(): sim = cosine_similarity(query, vec) scores.append((name, sim))
scores.sort(key=lambda x: x[1], reverse=True)
for name, sim in scores: print(f"{name}: {sim:.4f}")预期输出:
文档C:深度学习基础: 0.9964文档A:机器学习入门: 0.9922文档B:旅行攻略: 0.3333你会发现,相似度最高的通常是语义方向更接近查询的文档。
五、NumPy 向量操作汇总
Section titled “五、NumPy 向量操作汇总”把本节学到的所有操作用 NumPy 整理一遍:
import numpy as np
# ========== 创建向量 ==========a = np.array([1, 2, 3])b = np.array([4, 5, 6])
# ========== 基本运算 ==========print("加法:", a + b) # [5, 7, 9]print("减法:", a - b) # [-3, -3, -3]print("数乘:", 3 * a) # [3, 6, 9]print("逐元素乘:", a * b) # [4, 10, 18]
# ========== 点积 ==========print("点积:", np.dot(a, b)) # 32print("点积:", a @ b) # 32(等价写法)
# ========== 长度(范数) ==========print("长度:", np.linalg.norm(a)) # 3.742
# ========== 单位化 ==========unit_a = a / np.linalg.norm(a)print("单位向量:", unit_a)
# ========== 余弦相似度 ==========cos_sim = np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b))print("余弦相似度:", cos_sim) # 0.9746
# scikit-learn 也提供了内置函数# from sklearn.metrics.pairwise import cosine_similarity学到这里,下一节该带着什么问题走?
Section titled “学到这里,下一节该带着什么问题走?”看完向量以后,最值得带去下一节的问题是:
- 如果一个对象能写成向量,那很多对象怎么一起写?
- 如果两个向量能比较相似度,那一整批向量怎么一起做变换?
- 为什么神经网络里不会每次只处理一个向量,而总是一次处理一批?
这三个问题,正好会把你自然带到:
学完这一页,至少保留这张证据卡:
- 数学对象
- 向量、矩阵、特征值、基或向量空间概念
- 数值示例
- 用于计算它的简单数字或 NumPy 片段
- 可视化或输出
- 形状、变换后的点、相似度分数、特征方向或投影
- AI 关联
- 这里出现在 embeddings、批次、PCA、神经层或注意力中
- 期望产出
- 计算过程,以及一句把它和 AI 操作联系起来的话
| 概念 | 直觉理解 | NumPy 实现 |
|---|---|---|
| 向量 | 一组有序数字 | np.array([1, 2, 3]) |
| 向量加法 | 对应位置相加 | a + b |
| 数乘 | 缩放向量 | k * a |
| 向量长度 | 从原点到终点的距离 | np.linalg.norm(a) |
| 点积 | 衡量两个向量的方向关系 | np.dot(a, b) 或 a @ b |
| 余弦相似度 | 只看方向,不看长度的相似度 | dot / (norm_a * norm_b) |
这节最该带走什么
Section titled “这节最该带走什么”- 向量首先是在表示对象,不只是画箭头
- 点积最值得先理解成“对齐程度”
- 余弦相似度最值得先理解成“方向接近程度”
- 这就是为什么 AI 里很多检索、推荐和匹配系统都离不开向量
练习 1:向量运算
Section titled “练习 1:向量运算”给定向量 a = [2, 3, -1] 和 b = [1, -2, 4],用 NumPy 计算:
- a + b
- 3a - 2b
- a 的长度
- a 和 b 的点积
- a 和 b 的余弦相似度
练习 2:找最相似的运行画像
Section titled “练习 2:找最相似的运行画像”已知三个模型服务画像:
profiles = { "baseline": np.array([4, 3, 2, 2, 4]), "quantized": np.array([4, 3, 3, 3, 4]), "oversized": np.array([5, 1, 1, 1, 3]),}任务:计算每两个画像之间的余弦相似度,找出最接近和差异最大的一对。
练习 3:可视化向量加法
Section titled “练习 3:可视化向量加法”用 Matplotlib 画出以下向量加法的过程(带箭头):
- a = [2, 3],b = [-1, 2],画出 a、b 和 a+b
提示:参考 2.1 节的代码。
参考实现与讲解
- 对
a=[2,3,-1]和b=[1,-2,4],a+b=[3,1,3],3a-2b=[4,13,-11],||a||=sqrt(14)≈3.742,a·b=-8,余弦相似度约为-0.8018。 - 运行画像向量中,最接近的应该是 baseline 和 quantized。如果你改变延迟或显存维度,最近的画像可能会变;这正是把产品取舍写成数字的意义。
- 向量加法图应先画
a,再从a的终点画b,并把a+b=[1,5]作为从原点出发的最终箭头。图形必须和数值一致。