平推 NumPy 核心:np.linalg.norm
np.linalg.norm是 NumPy 线性代数模块(linalg)中最核心的函数之一,专门用来计算范数(Norm)。
在默认情况下,它就是用来计算向量的几何模长(L2 范数)。
但在实际复杂的 RAG 工程、机器学习和数据分析中,这个函数还承载了更多强大的功能(比如计算矩阵的范数、行/列的批量归一化等)。我们今天把它彻底拆解通透。
📑 本文结构一览
章节
内容
1. 函数原型
参数签名与核心参数解读
2. ord 参数详解
L2 / L1 / 无穷范数,选哪把"尺子"
3. 高频使用场景
单向量模长、批量归一化、欧氏距离
4. 性能心法
循环陷阱与零除防御
5. axis 切蛋糕游戏
用切水果的方式理解行/列方向
6. 现场算术
逐行拆解 NumPy 的计算报告
7. RAG 闭环心法
Embedding 归一化的三条铁律
1. 📦 函数原型与核心参数
PLAINTEXT
np.linalg.norm(x, ord=None, axis=None, keepdims=False)参数 | 大白话解释 | 默认行为 |
|---|---|---|
| 输入数据:一维向量、二维矩阵,甚至多维张量 | 必填 |
| 范数的类型(Order)—— 决定用哪把"尺子"去量大小 |
|
| 计算轴 —— 顺着哪个方向算(按行 / 按列)。批量处理 Embedding 时极重要 |
|
| 保持原维度。 |
|
2. 📐 ord 参数详解:选择你的"度量尺"
针对一维向量(比如一个 1024 维的文本 Embedding),ord 的不同取值对应着不同的数学含义:
2.1 ord=2 或 None:L2 范数(几何模长)
数学公式:\sqrt{\sum |x_i|^2}
工程场景:计算绝对几何距离,配合 L2 归一化
代码:
np.linalg.norm(v, ord=2)
💡 核心计算三部曲
不管向量有多少维,计算几何模长的物理公式永远只有三步:
各自平方:把向量里的每个数字自己乘以自己
全部加起来:把所有平方后的数字相加
最后开根号:对相加后的总和开根号
2.2 ord=1:L1 范数(曼哈顿距离 / 绝对值之和)
数学公式:\sum |x_i|
工程场景:稀疏编码、L1 正则化(让向量中很多元素变成 0)
代码:
np.linalg.norm(v, ord=1)
2.3 ord=np.inf:正无穷范数(最大绝对值)
数学公式:\max(|x_i|)
工程场景:寻找向量中波动或贡献最大的单维特征
代码:
np.linalg.norm(v, ord=np.inf)
3. 🎯 工业级三大高频使用场景
3.1 最基础的单向量模长计算
PLAINTEXT
import numpy as np
v = np.array([3, -4, 0])
np.linalg.norm(v) # 5.0 → L2 范数 (3²+4²=25, √25=5)
np.linalg.norm(v, ord=1) # 7.0 → L1 范数 (|3|+|-4|+|0|)
np.linalg.norm(v, ord=np.inf) # 4.0 → 无穷范数 max(|-4|)3.2 【RAG 核心】对二维矩阵进行"批量 L2 归一化"
在实际写 RAG 时,大模型接口返回的往往是一个矩阵(比如包含 5 个 Chunk 的 Embedding,形状为 (5, 1024))。你必须利用 axis 参数,一瞬间把这 5 个向量各自的模长算出来,并完成归一化。
PLAINTEXT
# 模拟 3 个文本 Chunk 的 Embedding 向量,维度为 4
# 形状 (3, 4) 代表 3 行 4 列
embeddings = np.array([
[3.0, 4.0, 0.0, 0.0], # 向量 1 → 模长 5
[1.0, 1.0, 1.0, 1.0], # 向量 2 → 模长 2
[0.0, 0.0, 0.0, 12.0] # 向量 3 → 模长 12
])
# ① 核心微操:axis=1 横着跨列算,分别算每一行的模长
# keepdims=True 保持 (3,1) 形状,方便广播除法
norms = np.linalg.norm(embeddings, axis=1, keepdims=True)
print(norms)
# [[ 5.]
# [ 2.]
# [12.]]
# ② 工业级一句话全量 L2 归一化(NumPy 广播机制)
normalized_embeddings = embeddings / norms
print(normalized_embeddings)
# [[0.6 0.8 0. 0. ]
# [0.5 0.5 0.5 0.5]
# [0. 0. 0. 1. ]]
# ③ 验证
np.linalg.norm(normalized_embeddings[0]) # 1.0 ✓3.3 快速计算两个向量的"欧氏距离"
利用 np.linalg.norm 结合向量减法,可以写出极其优雅的单行代码:
PLAINTEXT
user_query_vector = np.array([1.0, 2.0, 3.0])
doc_chunk_vector = np.array([4.0, 6.0, 3.0])
euclidean_dist = np.linalg.norm(user_query_vector - doc_chunk_vector)
print("两者的欧氏距离为:", euclidean_dist) # 5.0 → sqrt(3²+4²+0²)4. ⚡ 性能心法
🚫 不要在原生 Python 循环里用它
如果你有 1 万个向量,千万不要写
for v in list: np.linalg.norm(v)。
把它堆成一个大矩阵,利用axis=1, keepdims=True让 NumPy 内部的 C 语言底层和 CPU 的 SIMD 寄存器去疯狂加速,速度会有上百倍的提升。
🛡️ 防零除崩溃(Engine Defender)
在做 RAG 工业落地时,如果偶遇空文本清洗出错,Embedding 接口可能会返回全 0 向量。全 0 向量的模长是 0,直接做除法会触发
RuntimeWarning: divide by zero导致程序崩溃。解法:在归一化前,分母加一个极小的扰动项:
PLAINTEXTnorms + 1e-9 # 防御性编程,避免零除崩溃
在开发大模型 RAG 系统或搞机器学习时,
np.linalg.norm(范数/模长)和axis(轴)是决定向量检索生死、但又最反人类的两个底层工程概念。接下来,我们用最朴实的算术和切蛋糕的游戏,带你彻底融会贯通 👇
5. 🧁 axis(轴)的切蛋糕游戏
想象你的数据是一盒"小蛋糕"
假设你在宿舍里买了一盒精致的切块小蛋糕,一共 3 排(行),每排有 4 个(列):
列 0 | 列 1 | 列 2 | 列 3 | |
|---|---|---|---|---|
第 1 排(0) | 🍓 草莓 | 🍓 草莓 | 🍓 草莓 | 🍓 草莓 |
第 2 排(1) | 🥭 芒果 | 🥭 芒果 | 🥭 芒果 | 🥭 芒果 |
第 3 排(2) | 🫐 蓝莓 | 🫐 蓝莓 | 🫐 蓝莓 | 🫐 蓝莓 |
在计算机里,"排" =
axis=0,"个" =axis=1
5.1 切蛋糕游戏开始!
现在,我给你一把水果刀,对你下达两个不同的命令,你看看你会怎么切:
5.1.1 🍰 游戏 A:顺着 axis=1(个)的方向切!
axis=1 代表"每一排里的那几个果子"。顺着它的方向切,意思就是让你横着下刀,把每一排里的 4 个果子,像串糖葫芦一样串在一起、拍扁:
PLAINTEXT
切之前(3×4) 切之后(3×1)
🍓 🍓 🍓 🍓 ──→ [ 🍓] = 草莓泥
🥭 🥭 🥭 🥭 ──→ [ 🥭] = 芒果泥
🫐 🫐 🫐 🫐 ──→ [ 🫐] = 蓝莓泥收获:3 坨水果泥(草莓泥、芒果泥、蓝莓泥)
消灭了:原本每一排"有 4 个"的那个属性
🌟 对齐 RAG 场景
每一排蛋糕 = 一个文本分块(Chunk) 的 Embedding 向量
那 4 个果子 = 这个文本块的 4 维 Embedding 特征
写axis=1= 把 4 维特征横向拍扁,算出这一个文本块自己的模长
3 排蛋糕 → 最终得到 3 个模长数字
5.1.2 🍰 游戏 B:顺着 axis=0(排)的方向切!
规则变了。axis=0 代表"排"。顺着排的方向,让你纵向往下切,把同位置的果子垂直串在一起、拍扁:
PLAINTEXT
切之前(3×4) 切之后(1×4)
🍓 🍓 🍓 🍓 [ 混合泥1 | 混合泥2 | 混合泥3 | 混合泥4 ]
🥭 🥭 🥭 🥭 ──→
🫐 🫐 🫐 🫐
↓ ↓ ↓ ↓收获:4 坨混合泥
消灭了:原本"有 3 排"的那个属性
5.2 🎬 总结
方向 | 操作 | RAG 里的含义 |
|---|---|---|
| 每一排自己管自己,拍扁成一个数字 | 算每个文本块自己的模长 |
| 跨越不同的排,把每一列压扁 | 跨文本块做特征聚合 |
6. 🔬 现场算术:拆解 NumPy 的"计算报告单"
当我们把这盒蛋糕送进 Python 运行后,写下不同的 axis,NumPy 会吐出截然不同的数组报告。
6.1 报告一:np.linalg.norm(embeddings, axis=1)
终端打印结果:
PLAINTEXT
[ 5. 2. 12.]6.1.1 这个数组怎么理解?为什么数字后面带一个小点?
它的含义:包含 3 个元素的一维数组,由左到右对应 3 排蛋糕(3 个 Chunk 各自的模长)
为什么带小点(
5.):NumPy 的习惯 —— 代表浮点数(Float)。虽然算出来是整数5,但写成5.(=5.0)提醒你它是可带小数的精确特征值
6.1.2 里面的 12 是怎么算出来的?
抓起第 2 排的数据 [0, 0, 0, 12] 丢进三部曲:
PLAINTEXT
0² + 0² + 0² + 12² = 0 + 0 + 0 + 144 = 144
√144 = 12💡 架构师直觉:如果向量里除了一个数字外全是
0,那它的模长直接就等于那个唯一非零数的绝对值!
6.2 报告二:np.linalg.norm(embeddings, axis=0)
终端打印结果:
PLAINTEXT
[3.16227766 4.12310563 1. 12.04159458]6.2.1 这个数组怎么理解?为什么 1. 后面空了这么大一个位?
它的含义:包含 4 个元素的一维数组,由左到右对应 4 列果子(纵向特征压缩值)
为什么有空位(对齐美学):
NumPy 在打印数组前,会先扫描所有数字,找到小数点后最长的那位(
3.16227766,共 11 字符宽)。而中间第 2 列恰好是整数
1(显示为1.),为了让整个数组上下对齐,NumPy 在1.后面默默补了 9 个空格占位符,把最后的大数字12.04159458推到应有的格子上。
6.2.2 里面的 3.16227766 和 4.12310563 是怎么算出来的?
PLAINTEXT
第 0 列 [3, 1, 0]: 3² + 1² + 0² = 9 + 1 + 0 = 10 → √10 ≈ 3.16227766
第 1 列 [4, 1, 0]: 4² + 1² + 0² = 16 + 1 + 0 = 17 → √17 ≈ 4.123105637. 🤖 RAG 闭环心法
在写大模型 RAG 知识库代码时,我们之所以横竖看都不顺眼,往往是因为把行和列弄反了。
📌 记住这三条铁律:
① 每一个文本分块(Chunk)的 Embedding 向量都占据矩阵的一整行
② 我们希望分别求出每一个独立文本块各自的长度,从而进行 L2 归一化
③ 因此,在对 Embedding 矩阵做批量特征缩放时,请死死焊住
axis=1
横着下刀,让 NumPy 贴心地为你吐出最干净的[5. 2. 12.]报告单!
np.linalg.norm(x, ord=None, axis=None, keepdims=False)
