第 4 章 长度、点积与夹角
学习目标
- 由勾股定理推导向量的模(长度)公式
- 掌握单位向量的概念与求法
- 掌握点积的两种定义,并理解它们为什么等价
- 会由余弦定理推导点积的几何意义
- 会用点积判断垂直、求夹角
- 会推导并计算投影向量
- 会用 NumPy 与 PyTorch 计算模、点积、夹角与投影
4.1 向量的模:长度公式
向量 的模(长度、大小)记作 。把起点放在原点,终点是 ,向量的长度就是原点到终点的距离——这正是第 1 章的两点间距离公式:

也就是说,模公式就是勾股定理:水平分量 和竖直分量 是两条直角边,向量本身是斜边。
import matplotlib.pyplot as plt
from matplotlib import font_manager
for f in font_manager.findSystemFonts():
if any(k in f for k in ("NotoSansCJK", "NotoSansSC", "wqy", "SimHei", "msyh", "PingFang")):
font_manager.fontManager.addfont(f)
plt.rcParams["font.sans-serif"] = ["Noto Sans CJK SC", "WenQuanYi Zen Hei", "SimHei", "Microsoft YaHei", "PingFang SC"]
plt.rcParams["axes.unicode_minus"] = False
import numpy as np
v = np.array([3.0, 4.0])
fig, ax = plt.subplots(figsize=(6, 6))
ax.axhline(0, color="black", linewidth=1)
ax.axvline(0, color="black", linewidth=1)
ax.grid(True, linestyle=":", alpha=0.5)
ax.set_xlim(-0.5, 4.5)
ax.set_ylim(-0.5, 5.5)
ax.set_aspect("equal")
ax.quiver(0, 0, v[0], v[1], angles="xy", scale_units="xy", scale=1,
color="tab:red", width=0.02)
ax.plot([0, v[0]], [0, 0], color="tab:blue", linestyle="--")
ax.plot([v[0], v[0]], [0, v[1]], color="tab:green", linestyle="--")
ax.text(1.5, -0.35, "x = 3", color="tab:blue", fontsize=12, ha="center")
ax.text(v[0] + 0.1, 1.8, "y = 4", color="tab:green", fontsize=12)
ax.annotate("‖v‖ = 5", (1.4, 2.5), color="tab:red", fontsize=14)
ax.set_xlabel("x")
ax.set_ylabel("y")
ax.set_title("模 = 勾股定理:3² + 4² = 5²")
fig.savefig("book/public/figs/fig04-length.png", dpi=120, bbox_inches="tight")
plt.close(fig)三维向量 的模,是把它先投影到 平面再套一次勾股定理:
依此类推, 维向量 的模是所有分量平方和再开方:
NumPy 与 PyTorch 一行搞定:
import numpy as np
v = np.array([3.0, 4.0])
print("手动计算:", np.sqrt(3**2 + 4**2))
print("np.linalg.norm:", np.linalg.norm(v))运行输出:
手动计算: 5.0
np.linalg.norm: 5.0import torch
v = torch.tensor([3.0, 4.0])
print("torch.norm:", torch.norm(v))运行输出:
torch.norm: tensor(5.)4.2 单位向量:长度为 1 的「方向」本身
单位向量是模等于 1 的向量。它只表示方向,不携带大小信息。
任意非零向量 ,除以自己的模,就得到同方向的单位向量:
例:,,所以
import numpy as np
v = np.array([3.0, 4.0])
unit = v / np.linalg.norm(v)
print("单位向量:", unit)
print("它的模:", np.linalg.norm(unit))运行输出:
单位向量: [0.6 0.8]
它的模: 1.0两个最常用的单位向量是 轴方向的 和 轴方向的 。任何向量都能写成它们的「数乘之和」:
这行等式是第 5 章「线性组合」和第 11 章「基」的起点——先把这句话记住,后面会反复用到。
4.3 点积:定义与计算
点积(也叫内积、数量积)是向量的一种乘法,结果是一个数(标量),不是向量。两个二维向量:
「对应分量相乘,再全部相加」。例:
NumPy、PyTorch 的写法:
import numpy as np
u = np.array([1.0, 2.0])
w = np.array([3.0, 1.0])
print("np.dot:", np.dot(u, w))
print("u @ w:", u @ w)
print("手动:", u[0] * w[0] + u[1] * w[1])运行输出:
np.dot: 5.0
u @ w: 5.0
手动: 5.0import torch
u = torch.tensor([1.0, 2.0])
w = torch.tensor([3.0, 1.0])
print("torch.dot:", torch.dot(u, w))运行输出:
torch.dot: tensor(5.)4.4 点积的几何意义:余弦定理登场
分量定义好算,但它「是什么」?答案是:点积把长度和夹角联系了起来。
先复习余弦定理。三角形的两边 、 夹着角 ,对边是 ,则
推导(锐角情形):过顶点作高 ,把底边分成 和 两段。由勾股定理,
(用到了 。)
现在把 、 放在同一起点,夹角为 。从 的终点指向 的终点的向量是 ,所以三角形三边是 、、。对三角形用余弦定理:
另一方面,直接用分量计算 :
两条路算出的是同一个量,右边对应相等,于是得到点积的几何意义:
其中 是两向量的夹角,取 。

这条公式立刻带来三个重要结论:
import matplotlib.pyplot as plt
from matplotlib import font_manager
for f in font_manager.findSystemFonts():
if any(k in f for k in ("NotoSansCJK", "NotoSansSC", "wqy", "SimHei", "msyh", "PingFang")):
font_manager.fontManager.addfont(f)
plt.rcParams["font.sans-serif"] = ["Noto Sans CJK SC", "WenQuanYi Zen Hei", "SimHei", "Microsoft YaHei", "PingFang SC"]
plt.rcParams["axes.unicode_minus"] = False
import numpy as np
u = np.array([4.0, 0.0])
w = np.array([2.0, 3.0])
theta = np.arccos(np.dot(u, w) / (np.linalg.norm(u) * np.linalg.norm(w)))
fig, ax = plt.subplots(figsize=(6, 6))
ax.axhline(0, color="black", linewidth=1)
ax.axvline(0, color="black", linewidth=1)
ax.grid(True, linestyle=":", alpha=0.5)
ax.set_xlim(-0.5, 4.5)
ax.set_ylim(-0.5, 4)
ax.set_aspect("equal")
ax.quiver(0, 0, u[0], u[1], angles="xy", scale_units="xy", scale=1,
color="tab:blue", width=0.02)
ax.quiver(0, 0, w[0], w[1], angles="xy", scale_units="xy", scale=1,
color="tab:red", width=0.02)
# 夹角弧线
arc = np.linspace(0, theta, 60)
ax.plot(0.7 * np.cos(arc), 0.7 * np.sin(arc), color="tab:green")
ax.annotate("θ", (0.9, 0.38), color="tab:green", fontsize=14)
ax.annotate("u", (u[0] / 2, 0.25), color="tab:blue", fontsize=14)
ax.annotate("w", (w[0] / 2 + 0.1, w[1] / 2 + 0.25), color="tab:red", fontsize=14)
ax.set_title("u · w = ‖u‖‖w‖cosθ")
fig.savefig("book/public/figs/fig04-dot_angle.png", dpi=120, bbox_inches="tight")
plt.close(fig)- 当且仅当 ,即 ——两个非零向量垂直,当且仅当它们的点积为 0。
- 点积大于 0,说明夹角小于 (方向偏「同向」);小于 0,说明夹角大于 (方向偏「反向」)。
- 柯西-施瓦茨不等式:因为 ,所以
这个不等式是很多机器学习理论(比如相似度、特征值扰动)的地基,现在你已经有推导它的完整材料了。
4.5 用点积求夹角
由几何意义解出 :
例:、。
所以 。
import numpy as np
u = np.array([1.0, 2.0])
w = np.array([3.0, 1.0])
cos_theta = np.dot(u, w) / (np.linalg.norm(u) * np.linalg.norm(w))
theta = np.arccos(np.clip(cos_theta, -1.0, 1.0)) # clip 防止浮点误差越界
print("cosθ =", cos_theta)
print("θ(弧度) =", theta)
print("θ(度) =", np.degrees(theta))运行输出:
cosθ = 0.7071067811865475
θ(弧度) = 0.7853981633974484
θ(度) = 45.00000000000001浮点误差让结果差一点点,所以四舍五入后才是 45°——这也是为什么书里建议用 np.allclose 而不是 ==。
AI 里的余弦相似度就是 本身:把两个词、两张图片、两段文字各自变成向量,夹角越小、余弦越接近 1,就认为它们越相似。你学的这条公式,正是现代大模型里「注意力」打分的基本工具。
4.6 投影:把一个向量「压」到另一个方向上
投影解决这样的问题:阳光垂直照下来,一根斜杆在地面上的影子有多长、朝哪个方向?
给定 与 ,把 分解成两部分:一部分平行于 (投影),另一部分垂直于 。平行部分记作 。
推导投影向量
设 方向的单位向量为 。 在 方向上的带符号长度是 (夹角为锐角时为正)。由点积公式:
把这个带符号长度乘上方向 ,就得到投影向量:

例:(水平方向)、。
import matplotlib.pyplot as plt
from matplotlib import font_manager
for f in font_manager.findSystemFonts():
if any(k in f for k in ("NotoSansCJK", "NotoSansSC", "wqy", "SimHei", "msyh", "PingFang")):
font_manager.fontManager.addfont(f)
plt.rcParams["font.sans-serif"] = ["Noto Sans CJK SC", "WenQuanYi Zen Hei", "SimHei", "Microsoft YaHei", "PingFang SC"]
plt.rcParams["axes.unicode_minus"] = False
import numpy as np
u = np.array([4.0, 0.0])
w = np.array([2.0, 3.0])
proj = (np.dot(u, w) / np.dot(u, u)) * u
fig, ax = plt.subplots(figsize=(6, 6))
ax.axhline(0, color="black", linewidth=1)
ax.axvline(0, color="black", linewidth=1)
ax.grid(True, linestyle=":", alpha=0.5)
ax.set_xlim(-0.5, 4.5)
ax.set_ylim(-0.5, 4)
ax.set_aspect("equal")
ax.quiver(0, 0, u[0], u[1], angles="xy", scale_units="xy", scale=1,
color="tab:blue", width=0.02)
ax.quiver(0, 0, w[0], w[1], angles="xy", scale_units="xy", scale=1,
color="tab:red", width=0.02)
ax.quiver(0, 0, proj[0], proj[1], angles="xy", scale_units="xy", scale=1,
color="tab:purple", width=0.02)
# 垂直辅助线:w 终点到投影终点
ax.plot([w[0], proj[0]], [w[1], proj[1]], color="gray", linestyle="--")
ax.annotate("u", (u[0] / 2, 0.25), color="tab:blue", fontsize=14)
ax.annotate("w", (w[0] / 2 + 0.1, w[1] / 2 + 0.25), color="tab:red", fontsize=14)
ax.annotate("投影", (proj[0] / 2, -0.45), color="tab:purple", fontsize=14, ha="center")
ax.set_title("w 在 u 上的投影")
fig.savefig("book/public/figs/fig04-projection.png", dpi=120, bbox_inches="tight")
plt.close(fig)影子在水平方向长 2,竖直的 3 完全被「压掉」了。
import numpy as np
u = np.array([4.0, 0.0])
w = np.array([2.0, 3.0])
proj = (np.dot(u, w) / np.dot(u, u)) * u
print("投影向量:", proj)
print("验证:投影与 u 平行:", np.isclose(proj[1], 0.0))运行输出:
投影向量: [2. 0.]
验证:投影与 u 平行: True投影在第 13 章会再次出现:最小二乘法本质上就是「把观测向量投影到模型能到达的平面上」。这一章的投影公式,就是未来那套推导的种子。
动手实践
- 计算 :、、(用公式手算再用
np.linalg.norm验证)。 - 求 的单位向量,验证它的模为 1。
- 判断下列向量对是否垂直,用点积说明理由: 与 ; 与 ; 与 。
- 用点积求 与 的夹角(精确到度),再用 4.5 的代码验证。
- 画出 、 及 在 上的投影,检查投影是否与 平行。
常见错误
| 错误写法/理解 | 原因 |
|---|---|
| 点积结果写成向量,如 | 点积是「对应分量相乘再相加」,结果是标量 5 |
| 用 计算模 | 模是平方和的平方根,,不是 7 |
| 忘记 要除 | 夹角公式的分母是两向量模的乘积 |
用 np.arccos 直接算结果出现 NaN | 浮点误差让比值略超 ,先 np.clip 再求反余弦 |
| 认为垂直必须有一个向量是零向量 | 两个非零向量夹角 90° 即垂直,等价于点积为 0 |
章末练习
基础
- 求模:、、。
- 计算点积:、、。
- 判断 与 是否垂直,用点积解释。
提高
- 求 与 的夹角,保留一位小数(度)。
- 已知 、、,求夹角 。
- 把 投影到 上,画出并计算投影向量。
挑战
- 证明:若 与 长度相等,则 垂直于 。提示:计算 ,利用点积的分配律。
- 三个向量 满足 ,能否推出 ?找出反例并解释。
章末自测
- A.
- B.
- C.
- D.
- A.
- B.
- C.
- D.
- 两个非零向量垂直,当且仅当?
- A. 它们的模相等
- B. 它们的点积为 0
- C. 它们方向相同
- D. 它们的夹角为 0°
- A.
- B.
- C.
- D.
- 单位向量的模是?
- A.
- B.
- C.
- D. 任意值
- 的单位向量是?
- A.
- B.
- C.
- D.
- 在 上的投影向量是?
- A.
- B.
- C.
- D.
- 柯西-施瓦茨不等式是?
- A.
- B.
- C.
- D.
- 余弦定理的正确形式是?
- A.
- B.
- C.
- D.
np.arccos的输入应限制在?- A.
- B.
- C.
- D. 任意实数
