向量空间与线性变换

一句话概述

向量空间是线性代数的核心舞台——它定义了一群向量必须遵守的"游戏规则"(加法和数乘的封闭性),而线性变换则是这个舞台上最优雅的"变形术",它把空间中的一切按照线性规则重新排列,却始终保持着向量之间的线性关系。理解向量空间,你就理解了AI模型中特征数据的"家园";理解线性变换,你就理解了神经网络每一层如何对数据进行"加工"——从图像特征提取到词嵌入映射,从主成分分析到注意力机制,无一不是向量空间上的线性变换在发挥作用。

💡 核心要点:①向量空间是满足加法封闭和数乘封闭的向量集合,是AI特征数据的"合法居住地" ②基是空间的"坐标系骨架",维度就是基向量的个数,决定了数据的自由度 ③线性变换保持线性关系不变,是神经网络层运算的数学本质 ④矩阵是线性变换的数字描述,矩阵乘法就是变换的复合

教学与演示

一、向量空间是什么——满足规则的一群向量

是什么(定义):向量空间(Vector Space)是一个非空集合 V,配备了两种运算——向量加法和标量乘法,并且满足八条公理(封闭性、结合律、交换律、零元、负元、标量分配律等)。简单来说,向量空间就是一群向量组成的"俱乐部",只有满足特定规则的向量才能加入,而且俱乐部里的运算结果也不能"跑出去"。

大白话 想象向量空间是一个"合法社区"——社区里的居民(向量)可以互相"合住"(加法),也可以"分身"(数乘),而且无论怎么操作,结果还是社区里的居民,不会跑到社区外面去。这就是"封闭性"——社区的大门是关着的,内部怎么折腾都行,但不会凭空消失也不会凭空出现外人。

为什么(原理):向量空间的八条公理看似繁琐,但每一条都保证了运算的"合理性"。比如加法交换律保证你先加谁后加谁都一样,零元保证"什么都不加"是有定义的,数乘分配律保证"批量操作"和"逐个操作"等价。这些公理共同构建了一个运算自洽的世界,使得我们可以放心地在其中进行代数推导,而不用担心运算结果"出界"。

大白话 八条公理就像社区的"法律"——虽然看着多,但每条都是为了让社区运转有序。比如"零元"就是社区里的"空房子",谁住进去都还是自己;"负元"就是"反义词",加上它就归零。有了这些法律,你才能放心地做各种计算,不会算出矛盾的结果。

怎么做(实现)

import numpy as np

# ========== 验证向量空间的封闭性 ==========
# 定义两个二维向量
v1 = np.array([2.0, 3.0])   # 向量1:坐标(2, 3)
v2 = np.array([1.0, -1.0])  # 向量2:坐标(1, -1)

# --- 验证加法封闭性 ---
v_sum = v1 + v2              # 两个向量相加
print("加法封闭性验证:")
print(f"  v1 + v2 = {v1} + {v2} = {v_sum}")
# 结果仍然是二维向量,属于同一个空间 → 封闭!

# --- 验证数乘封闭性 ---
alpha = 2.5                  # 标量(实数)
v_scaled = alpha * v1        # 标量乘以向量
print(f"\n数乘封闭性验证:")
print(f"  {alpha} × v1 = {alpha} × {v1} = {v_scaled}")
# 结果仍然是二维向量 → 封闭!

# --- 验证八条公理中的几条 ---
# 1. 加法交换律:v1 + v2 = v2 + v1
print(f"\n加法交换律:v1 + v2 = {v1 + v2}")
print(f"            v2 + v1 = {v2 + v1}")
print(f"            相等?{np.allclose(v1 + v2, v2 + v1)}")

# 2. 零元存在:存在零向量 0,使得 v + 0 = v
zero = np.array([0.0, 0.0])  # 零向量
print(f"\n零元存在:v1 + 0 = {v1 + zero}")
print(f"         等于 v1?{np.allclose(v1 + zero, v1)}")

# 3. 负元存在:对任意 v,存在 -v 使得 v + (-v) = 0
print(f"\n负元存在:v1 + (-v1) = {v1 + (-v1)}")
print(f"         等于零向量?{np.allclose(v1 + (-v1), zero)}")

# 4. 数乘分配律:α(v1 + v2) = αv1 + αv2
alpha = 3.0
lhs = alpha * (v1 + v2)      # 左边:先加后乘
rhs = alpha * v1 + alpha * v2 # 右边:先乘后加
print(f"\n数乘分配律:α(v1+v2) = {lhs}")
print(f"           αv1+αv2 = {rhs}")
print(f"           相等?{np.allclose(lhs, rhs)}")
大白话 代码里我们就像社区管理员一样,逐一检查"社区法律"是否被遵守。加法封闭性检查"两个居民合住后还是不是居民",数乘封闭性检查"居民分身后还是不是居民",交换律检查"谁先搬进来都一样",零元检查"空房子确实存在",负元检查"每个居民都有反义词邻居"。

什么用(应用):在AI中,向量空间是所有数据的"合法居住地"。一张28×28的灰度图像可以表示为784维向量空间中的一个点,一段文本经过嵌入后变成768维向量空间中的一个点。神经网络之所以能处理这些数据,正是因为它们都在向量空间中,可以合法地进行加法和数乘运算。Word2Vec中著名的"king - man + woman ≈ queen",就是在词向量空间中利用向量运算实现的语义推理。

哪些坑(缺点):初学者容易混淆"向量空间"和"向量集合"——不是随便一堆向量放在一起就是向量空间,必须验证封闭性。比如所有第一分量为1的二维向量就不是向量空间(加法不封闭:(1,2)+(1,3)=(2,5),第一分量变成了2≠1)。另外,向量空间的定义是抽象的,不限于"箭头"——多项式、函数、矩阵都可以构成向量空间,这种抽象性常常让人困惑。

二、子空间——大空间里的"小社区"

是什么(定义):子空间(Subspace)是向量空间 V 的一个非空子集 W,并且 W 本身也是一个向量空间(对加法和数乘封闭)。换句话说,子空间是"大社区"里的"小社区",小社区里的居民互相合住或分身,结果仍然留在小社区里。

大白话 子空间就像大社区里的"小区"——小区的居民也是大社区的居民,但小区有自己的规矩:小区里的人合住,还在小区里;小区里的人分身,也还在小区里。最极端的子空间就是"零小区"——只有零向量一个人住的"单身公寓"。

为什么(原理):子空间的重要性在于,它让我们可以在"小范围"内研究问题。很多AI算法(如PCA降维)的核心思想就是找到一个"有意义的子空间",把高维数据投影到低维子空间中,同时保留最重要的信息。子空间还是理解线性方程组解结构的关键——齐次方程 Ax=0 的所有解构成一个子空间(称为核空间或零空间)。

怎么做(实现)

import numpy as np

# ========== 子空间验证与示例 ==========
# 在 R³ 中,考虑所有形如 (x, y, 0) 的向量
# 这就是 xy 平面,是 R³ 的一个子空间

# --- 生成子空间的向量 ---
# 用两个"生成元"来生成子空间
e1 = np.array([1.0, 0.0, 0.0])  # x轴方向的基向量
e2 = np.array([0.0, 1.0, 0.0])  # y轴方向的基向量

# 任意线性组合都在子空间中
alpha, beta = 2.5, -1.3         # 任意标量
v_sub = alpha * e1 + beta * e2  # 线性组合
print("子空间中的向量:")
print(f"  {alpha}×e1 + {beta}×e2 = {v_sub}")
print(f"  第三分量是否为0?{v_sub[2] == 0.0}")  # 始终为0 → 在子空间中

# --- 验证封闭性 ---
v_a = np.array([3.0, -2.0, 0.0])  # 子空间中的向量A
v_b = np.array([-1.0, 4.0, 0.0])  # 子空间中的向量B
v_sum = v_a + v_b                   # 加法
print(f"\n加法封闭性:{v_a} + {v_b} = {v_sum}")
print(f"  第三分量为0?{v_sum[2] == 0.0}")

gamma = 0.7
v_scaled = gamma * v_a              # 数乘
print(f"数乘封闭性:{gamma} × {v_a} = {v_scaled}")
print(f"  第三分量为0?{v_scaled[2] == 0.0}")

# --- 反例:不是子空间的集合 ---
# 所有形如 (x, y, 1) 的向量不是子空间
v_c = np.array([2.0, 3.0, 1.0])   # 集合中的向量C
v_d = np.array([1.0, -1.0, 1.0])  # 集合中的向量D
v_bad = v_c + v_d                   # 加法
print(f"\n反例(不是子空间):")
print(f"  {v_c} + {v_d} = {v_bad}")
print(f"  第三分量为1?{v_bad[2] == 1.0}(实际为{v_bad[2]},不封闭!)")

# --- 用 numpy 验证列空间 ---
A = np.array([[1, 2],             # 2×2矩阵
              [3, 4]])
# 矩阵A的列空间:A的列向量的所有线性组合
col1 = A[:, 0]                     # 第一列
col2 = A[:, 1]                     # 第二列
print(f"\n矩阵A的列空间:")
print(f"  列1 = {col1}")
print(f"  列2 = {col2}")
# 列空间的维度 = 矩阵的秩
rank = np.linalg.matrix_rank(A)
print(f"  列空间的维度(秩)= {rank}")
大白话 子空间验证就像检查小区的围墙是否完整——任选两个小区居民合住,结果还在小区里吗?任选一个居民分身,分身还在小区里吗?如果都是"是",围墙就是完整的;如果有一个"不是",围墙就有漏洞,这个小区就不合法。代码里的反例就是"有漏洞的小区"——两个人合住后,第三分量变成了2而不是1,跑出去了。

什么用(应用):在AI中,子空间的概念无处不在。主成分分析(PCA)就是找到数据方差最大的子空间(主成分子空间),把高维数据投影到这个低维子空间上实现降维。在自然语言处理中,词向量的某些方向对应特定的语义属性(如"性别方向"、"时态方向"),这些方向张成的子空间被称为"语义子空间",可以用来控制和理解语言模型的输出。在推荐系统中,用户偏好和物品特征都存在于高维向量空间中,协同过滤本质上是在寻找用户和物品的"共同子空间"。

三、基与维度——空间的"坐标系"

是什么(定义):基(Basis)是向量空间中一组线性无关的向量,且它们能生成(span)整个空间。维度(Dimension)就是基中向量的个数。基就像空间的"坐标系骨架"——有了基,空间中任何一个向量都可以用基向量的线性组合唯一表示,组合系数就是坐标。

大白话 基就像给空间装了一套"导航系统"——你只需要知道几个"基准方向"(基向量),就能用它们的组合到达空间中任何位置。基向量的个数就是维度,也就是你需要几个"坐标轴"才能描述清楚一个位置。二维空间需要2个基向量(比如x轴和y轴),三维空间需要3个,768维的词嵌入空间需要768个。

为什么(原理):基的重要性在于它提供了"坐标表示"。没有基,向量只是一个抽象的"箭头";有了基,向量就变成了一组数字(坐标),可以进行计算。同一个向量在不同的基下有不同的坐标——就像同一个位置用不同的地图坐标系会得到不同的坐标值。维度则衡量了空间的"大小"或"自由度"——维度越高,空间能容纳的信息越丰富,但计算也越复杂。

怎么做(实现)

import numpy as np

# ========== 基与维度的演示 ==========
# --- 标准基 ---
# R² 的标准基是 e1=(1,0), e2=(0,1)
e1 = np.array([1.0, 0.0])  # 标准基向量1
e2 = np.array([0.0, 1.0])  # 标准基向量2

# 任意向量可以用标准基表示
v = np.array([3.0, 4.0])   # 目标向量
# v = 3*e1 + 4*e2
print("标准基表示:")
print(f"  v = {v} = {v[0]}×e1 + {v[1]}×e2")

# --- 非标准基 ---
# 也可以选择不同的基,比如 b1=(1,1), b2=(1,-1)
b1 = np.array([1.0, 1.0])  # 非标准基向量1
b2 = np.array([1.0, -1.0]) # 非标准基向量2

# 验证 b1, b2 线性无关(行列式不为0)
B = np.column_stack([b1, b2])  # 把基向量拼成矩阵
det_B = np.linalg.det(B)       # 计算行列式
print(f"\n非标准基验证:")
print(f"  矩阵 B = \n{B}")
print(f"  行列式 = {det_B:.4f}(≠0,线性无关,可以作为基)")

# 在新基下的坐标:v = c1*b1 + c2*b2 → Bc = v → c = B⁻¹v
coords_new = np.linalg.solve(B, v)  # 解线性方程组求新坐标
print(f"\n新基下的坐标:")
print(f"  v = {coords_new[0]:.4f}×b1 + {coords_new[1]:.4f}×b2")
# 验证
v_reconstructed = coords_new[0] * b1 + coords_new[1] * b2
print(f"  验证:{coords_new[0]:.4f}×b1 + {coords_new[1]:.4f}×b2 = {v_reconstructed}")
print(f"  与原向量相等?{np.allclose(v, v_reconstructed)}")

# --- 维度的意义 ---
# R^n 的维度就是 n
print(f"\n维度示例:")
print(f"  R² 的维度 = 2(需要2个坐标描述位置)")
print(f"  R³ 的维度 = 3(需要3个坐标描述位置)")
print(f"  词嵌入空间(如Word2Vec)的维度 = 300")
print(f"  BERT的隐藏层维度 = 768")
print(f"  GPT-3的隐藏层维度 = 12288")

# --- 用numpy求矩阵的秩(=列空间的维度)---
A = np.array([[1, 2, 3],         # 3×3矩阵
              [4, 5, 6],
              [7, 8, 9]])
rank_A = np.linalg.matrix_rank(A)
print(f"\n矩阵A的秩(列空间维度)= {rank_A}")
print(f"  注意:虽然A是3×3矩阵,但秩只有{rank_A}")
print(f"  因为第3列 = 第1列 + 第2列(线性相关)")
大白话 同一个向量在不同基下的坐标不同,就像同一个地点用"经纬度"和"门牌号"描述会得到不同的数字,但指的都是同一个地方。选择好的基可以让问题变简单——比如PCA就是自动帮你找到"最好的基",让数据在新基下的表示更简洁。

什么用(应用):在AI中,基和维度的概念至关重要。神经网络的每一层都在做"换基"操作——把输入从一种表示(基)变换到另一种表示(基),使得分类或预测更容易。词嵌入(Word Embedding)的维度决定了语义信息的精细程度:300维的Word2Vec能捕捉基本的语义关系,而768维的BERT能理解更复杂的上下文语义。降维算法(PCA、t-SNE、UMAP)的核心思想就是找到低维基,用更少的坐标保留最重要的信息。

哪些坑(缺点):初学者常犯的错误是认为"维度越高越好"——实际上维度过高会导致"维度灾难"(Curse of Dimensionality),数据在高维空间中变得极其稀疏,距离度量失效,模型容易过拟合。另一个误区是混淆"矩阵的维度"和"向量空间的维度"——矩阵的维度是行数×列数,而向量空间的维度是基向量的个数(等于矩阵的秩),两者不是一回事。

四、线性变换——空间中的"变形"

是什么(定义):线性变换(Linear Transformation)是从向量空间 V 到向量空间 W 的映射 T: V → W,满足两个条件:(1) 可加性:T(u + v) = T(u) + T(v);(2) 齐次性:T(αv) = αT(v)。合起来就是 T(αu + βv) = αT(u) + βT(v)——线性组合的变换等于变换的线性组合。

线性变换条件\(T(αu + βv) = αT(u) + βT(v)\)
大白话 线性变换就像一块"有弹性的网格布"——你可以拉伸它、旋转它、倾斜它,但网格线始终是直的,不会弯曲。而且原来在同一条直线上的点,变换后还在同一条直线上;原来均匀分布的点,变换后还是均匀分布的。这就是"保持线性关系"的含义。

为什么(原理):线性变换之所以重要,是因为它把复杂的非线性问题分解为"线性部分+非线性部分"。在AI中,神经网络每一层先做线性变换(矩阵乘法),再通过激活函数引入非线性。线性变换保证了信息的"结构化传递"——输入之间的线性关系在输出中得以保留,而非线性激活则赋予模型拟合复杂函数的能力。

怎么做(实现)

import numpy as np

# ========== 线性变换的验证与示例 ==========
# 定义一个2×2矩阵作为线性变换
A = np.array([[2.0, 1.0],   # 旋转+缩放矩阵
              [0.0, 1.5]])

# --- 验证线性变换的两个条件 ---
u = np.array([1.0, 2.0])    # 向量u
v = np.array([3.0, -1.0])   # 向量v
alpha, beta = 2.0, -1.0     # 标量

# 条件1:可加性 T(u+v) = T(u) + T(v)
T_u_plus_v = A @ (u + v)     # 先加后变换
T_u_plus_T_v = A @ u + A @ v # 先变换后加
print("可加性验证:")
print(f"  T(u+v) = {T_u_plus_v}")
print(f"  T(u)+T(v) = {T_u_plus_T_v}")
print(f"  相等?{np.allclose(T_u_plus_v, T_u_plus_T_v)}")

# 条件2:齐次性 T(αv) = αT(v)
T_alpha_v = A @ (alpha * v)   # 先数乘后变换
alpha_T_v = alpha * (A @ v)   # 先变换后数乘
print(f"\n齐次性验证:")
print(f"  T(αv) = {T_alpha_v}")
print(f"  αT(v) = {alpha_T_v}")
print(f"  相等?{np.allclose(T_alpha_v, alpha_T_v)}")

# --- 综合条件:T(αu + βv) = αT(u) + βT(v) ---
lhs = A @ (alpha * u + beta * v)    # 左边:先线性组合后变换
rhs = alpha * (A @ u) + beta * (A @ v)  # 右边:先变换后线性组合
print(f"\n综合条件验证:")
print(f"  T(αu+βv) = {lhs}")
print(f"  αT(u)+βT(v) = {rhs}")
print(f"  相等?{np.allclose(lhs, rhs)}")

# --- 常见线性变换示例 ---
print("\n=== 常见线性变换 ===")

# 1. 旋转(逆时针旋转θ角)
theta = np.pi / 4  # 45度
R = np.array([[np.cos(theta), -np.sin(theta)],  # 旋转矩阵
              [np.sin(theta),  np.cos(theta)]])
v = np.array([1.0, 0.0])
v_rotated = R @ v
print(f"旋转45°:{v} → {v_rotated.round(4)}")

# 2. 缩放
S = np.array([[2.0, 0.0],   # x方向放大2倍,y方向放大0.5倍
              [0.0, 0.5]])
v_scaled = S @ v
print(f"缩放:{v} → {v_scaled}")

# 3. 剪切
H = np.array([[1.0, 1.0],   # 水平剪切
              [0.0, 1.0]])
v_sheared = H @ v
print(f"剪切:{v} → {v_sheared}")

# 4. 反射(关于y轴)
F = np.array([[-1.0, 0.0],  # y轴反射
              [ 0.0, 1.0]])
v_reflected = F @ v
print(f"反射:{v} → {v_reflected}")
大白话 验证线性变换就像检查一台"变形机器"是否合格——把两个东西一起放进去变形,和分别放进去变形再组合,结果必须一样;把一个东西放大后放进去变形,和放进去变形后再放大,结果也必须一样。如果机器通过了这两项检查,它就是"线性变形机器",不会把直线变弯。

什么用(应用):线性变换是深度学习的数学基石。全连接层(Dense Layer)的核心运算 y = Wx + b 中,Wx 就是线性变换(W是变换矩阵,x是输入向量)。卷积层本质上是特殊的线性变换(卷积核与输入的局部线性组合)。注意力机制中的 Q、K、V 投影也是线性变换。甚至数据预处理中的标准化(z-score normalization)也可以看作线性变换。理解线性变换,就理解了神经网络"层"的数学本质。

哪些坑(缺点):线性变换最大的局限是"太简单"——它只能做旋转、缩放、剪切、投影等"直来直去"的变换,无法弯曲空间。这就是为什么神经网络需要非线性激活函数(如ReLU、Sigmoid)——纯线性变换无论叠加多少层,等价于一个线性变换,无法拟合非线性关系。另外,初学者常把"线性变换"和"仿射变换"混淆——仿射变换多了偏移项 b(y = Ax + b),严格来说不是线性变换(除非 b=0),但在AI文献中常被统称为"线性层"。

五、变换的矩阵表示——用数字描述变形

是什么(定义):给定向量空间 V 的一组基和向量空间 W 的一组基,线性变换 T: V → W 可以唯一地表示为一个矩阵 A,使得 T(v) = Av。矩阵 A 的第 j 列就是 V 的第 j 个基向量在 T 变换下的像在 W 的基下的坐标。矩阵就是线性变换的"数字身份证"。

变换矩阵\(T(v) = Av\)
大白话 矩阵就是线性变换的"操作手册"——手册里记录了每个基准方向经过变换后变成了什么样子。你只需要查手册,就能知道任何向量变换后的结果。就像菜谱记录了每样食材加工后的样子,你照着菜谱做,就能把任何一道菜做出来。

为什么(原理):矩阵表示的威力在于"计算化"——抽象的线性变换变成了具体的矩阵乘法,可以在计算机上高效执行。更重要的是,矩阵运算有丰富的理论工具:特征值分解揭示变换的"主方向",奇异值分解(SVD)揭示变换的"信息分布",矩阵的秩揭示变换的"信息损失"。这些工具直接支撑了PCA、LSA、推荐系统等AI核心算法。

怎么做(实现)

import numpy as np

# ========== 变换的矩阵表示 ==========
# --- 从变换构造矩阵 ---
# 定义线性变换 T:R² → R²
# T 把 (1,0) 映射到 (2,1),把 (0,1) 映射到 (1,3)
# 那么变换矩阵 A 的列就是 T(e1) 和 T(e2)

T_e1 = np.array([2.0, 1.0])  # T(e1) = (2, 1)
T_e2 = np.array([1.0, 3.0])  # T(e2) = (1, 3)

# 构造变换矩阵:第1列=T(e1),第2列=T(e2)
A = np.column_stack([T_e1, T_e2])
print("变换矩阵 A:")
print(f"  A = \n{A}")

# 验证:T(v) = Av
v = np.array([3.0, 4.0])     # 任意向量
# 手动计算:T(v) = T(3e1 + 4e2) = 3T(e1) + 4T(e2)
T_v_manual = 3 * T_e1 + 4 * T_e2
T_v_matrix = A @ v
print(f"\n验证 T(v) = Av:")
print(f"  手动计算:3×T(e1) + 4×T(e2) = {T_v_manual}")
print(f"  矩阵乘法:A @ v = {T_v_matrix}")
print(f"  相等?{np.allclose(T_v_manual, T_v_matrix)}")

# --- 变换的复合 = 矩阵的乘法 ---
# 定义第二个变换 S
S_e1 = np.array([0.0, 1.0])  # S(e1) = (0, 1)
S_e2 = np.array([-1.0, 0.0]) # S(e2) = (-1, 0) → 逆时针旋转90°
B = np.column_stack([S_e1, S_e2])
print(f"\n第二个变换矩阵 B(逆时针旋转90°):")
print(f"  B = \n{B}")

# 复合变换:先做T,再做S → 矩阵乘法 BA
C = B @ A  # 复合变换的矩阵
print(f"\n复合变换 BA:")
print(f"  BA = \n{C}")

# 验证:S(T(v)) = B(Av) = (BA)v
v = np.array([1.0, 1.0])
result_step = B @ (A @ v)     # 逐步变换
result_composed = C @ v       # 复合变换
print(f"\n验证复合变换:")
print(f"  S(T(v)) = B(Av) = {result_step}")
print(f"  (BA)v = {result_composed}")
print(f"  相等?{np.allclose(result_step, result_composed)}")

# --- 特征值与特征向量 ---
# 特征向量:经过变换后方向不变的向量
# 特征值:变换后长度的缩放比例
eigenvalues, eigenvectors = np.linalg.eig(A)
print(f"\n矩阵A的特征值:{eigenvalues}")
print(f"矩阵A的特征向量(列):\n{eigenvectors}")
for i in range(len(eigenvalues)):
    ev = eigenvectors[:, i]           # 第i个特征向量
    lam = eigenvalues[i]              # 第i个特征值
    lhs = A @ ev                      # A × 特征向量
    rhs = lam * ev                    # 特征值 × 特征向量
    print(f"  验证 A×v{i+1} = λ{i+1}×v{i+1}:{np.allclose(lhs, rhs)}")
大白话 变换复合就像"流水线"——先过第一台机器(变换T),再过第二台机器(变换S),最终效果等价于一台"超级机器"(复合变换BA)。矩阵乘法就是描述这台超级机器的操作手册。特征向量是变换的"不动方向"——不管怎么变形,这些方向始终指向同一边,只是长度可能变了。

什么用(应用):在AI中,矩阵表示是所有深度学习框架的底层实现。PyTorch的 nn.Linear(in_features, out_features) 本质上就是创建一个变换矩阵 W 和偏置 b,前向传播时计算 y = Wx + b。特征值分解在PCA中用于找到数据方差最大的方向(主成分),SVD在推荐系统中用于矩阵分解(协同过滤),在自然语言处理中用于潜在语义分析(LSA)。理解矩阵与变换的对应关系,是理解深度学习可解释性的关键。

六、核与像——变换的"黑洞"与"投影"

是什么(定义):核(Kernel,也叫零空间 Null Space)是线性变换 T: V → W 中所有被映射为零向量的输入向量集合:Ker(T) = {v ∈ V : T(v) = 0}。像(Image,也叫列空间 Column Space)是所有可能的输出向量的集合:Im(T) = {T(v) : v ∈ V}。核是变换的"黑洞"——掉进去就变成零;像是变换的"投影屏幕"——所有输出都落在上面。

大白话 核就像一个"黑洞区域"——任何掉进这个区域的向量,经过变换后都会消失(变成零向量)。像就像一面"投影墙"——不管你输入什么向量,变换后的结果都只能出现在这面墙上。核越大,说明变换"吞噬"的信息越多;像越大,说明变换"保留"的信息越多。

为什么(原理):核与像的关系由"秩-零化度定理"(Rank-Nullity Theorem)精确描述:dim(Ker(T)) + dim(Im(T)) = dim(V)。这个定理告诉我们,输入空间的信息要么被"吞噬"(进入核),要么被"保留"(出现在像中),两者此消彼长。核的维度(零化度)衡量了信息损失的多少,像的维度(秩)衡量了信息保留的多少。

怎么做(实现)

import numpy as np

# ========== 核与像的计算 ==========
# 定义一个3×4矩阵(从R⁴到R³的线性变换)
A = np.array([[1, 2, 3, 4],
              [2, 4, 6, 8],
              [1, 1, 1, 1]])
print(f"矩阵 A(3×4):\n{A}")

# --- 计算像(列空间)---
# 像的维度 = 矩阵的秩
rank_A = np.linalg.matrix_rank(A)
print(f"\n像的维度(秩)= {rank_A}")

# 找到线性无关的列(像的基)
# 通过SVD分解来找
U, S, Vt = np.linalg.svd(A)
# 非零奇异值的个数就是秩
print(f"奇异值:{S.round(6)}")
print(f"非零奇异值个数(秩)= {np.sum(S > 1e-10)}")

# --- 计算核(零空间)---
# 核中的向量x满足 Ax = 0
# 通过SVD的V矩阵找到
# 核空间由V中对应零奇异值的行向量张成
null_mask = S <= 1e-10
if np.any(null_mask):
    # 核空间的基向量
    null_space = Vt[rank_A:, :].T  # 取V中后几行
    print(f"\n核的维度 = {A.shape[1] - rank_A}")
    print(f"核的基向量(列):\n{null_space.round(6)}")
    # 验证:A × 核向量 ≈ 0
    for i in range(null_space.shape[1]):
        result = A @ null_space[:, i]
        print(f"  A × 核向量{i+1} = {result.round(10)}(≈零向量)")

# --- 验证秩-零化度定理 ---
dim_V = A.shape[1]              # 输入空间维度 = 列数
dim_kernel = A.shape[1] - rank_A  # 核的维度
dim_image = rank_A               # 像的维度
print(f"\n秩-零化度定理验证:")
print(f"  dim(V) = {dim_V}")
print(f"  dim(Ker) + dim(Im) = {dim_kernel} + {dim_image} = {dim_kernel + dim_image}")
print(f"  等于 dim(V)?{dim_kernel + dim_image == dim_V}")

# --- 在AI中的直观理解 ---
print(f"\n=== AI中的直观理解 ===")
print(f"  输入维度:{dim_V}(如:4个特征)")
print(f"  核维度:{dim_kernel}(丢失了{dim_kernel}个维度的信息)")
print(f"  像维度:{dim_image}(保留了{dim_image}个维度的信息)")
print(f"  信息压缩比:{dim_image}/{dim_V} = {dim_image/dim_V:.2f}")
大白话 秩-零化度定理就像"能量守恒"——输入空间的总维度等于被吞噬的维度加上被保留的维度,一分不多一分不少。如果变换矩阵的秩很小,说明大量信息被吞噬了(核很大),输出空间很"扁"(像很小)。在AI中,这对应着信息瓶颈——网络层太窄,信息就会丢失。

什么用(应用):核与像在AI中有直接的应用。在深度学习中,如果某一层的权重矩阵的核很大(秩很低),说明很多不同的输入会被映射到相同的输出,造成信息丢失——这就是"网络退化"问题,ResNet的残差连接就是为了解决这个问题。在自然语言处理中,LSA(潜在语义分析)通过SVD分解词-文档矩阵,核对应噪声信息,像对应语义信息。在图像压缩中,SVD保留大奇异值对应的像空间,丢弃小奇异值对应的核空间,实现有损压缩。

哪些坑(缺点):初学者常混淆"核"和"零向量"——核是一个子空间(可能包含很多向量),不是单个零向量。另一个误区是认为"核为空"——核至少包含零向量,所以核永远不为空。还有一个容易出错的地方是核的计算——对于非方阵,核的维度可能很大,需要仔细计算。在编程中,由于浮点精度问题,判断"零奇异值"需要设置一个阈值(如 1e-10),不能直接用 S == 0

七、向量空间在AI中的应用——特征空间与嵌入

是什么(定义):在AI中,向量空间被称为"特征空间"(Feature Space)或"嵌入空间"(Embedding Space)。特征空间是数据经过特征提取后所在的向量空间,嵌入空间是高维数据(如文本、图像)被映射到的低维向量空间。这些空间不是随意选择的——它们是经过学习得到的"最优空间",使得相似的数据在空间中距离更近。

大白话 特征空间就像给数据建的"档案馆"——每条数据都有自己专属的"坐标位置",相似的数据被放在相邻的位置。嵌入空间则是"精简档案馆"——把高维的原始数据压缩到低维空间,但保留最重要的"邻居关系"。Word2Vec把几万维的one-hot编码压缩到300维,BERT把词压缩到768维,都是为了让数据在更紧凑的空间里保持语义关系。

为什么(原理):向量空间在AI中之所以重要,是因为它让"语义计算"成为可能。在嵌入空间中,语义相似的词距离更近,语义运算可以用向量运算实现。比如"国王-男人+女人≈王后"这个经典例子,就是在词向量空间中进行的向量运算。这种"语义几何"只有在向量空间中才能实现——因为向量空间有加法和数乘,可以进行线性组合;有距离和角度,可以衡量相似性。

怎么做(实现)

import numpy as np

# ========== 模拟词嵌入空间中的语义运算 ==========
# 用随机向量模拟词嵌入(实际中由Word2Vec/GloVe/BERT训练得到)
np.random.seed(42)  # 固定随机种子,保证可复现

# 模拟5个词的嵌入向量(3维,方便可视化)
words = ["国王", "男人", "女人", "王后", "苹果"]
# 构造有语义结构的嵌入
embeddings = {
    "国王": np.array([0.9, 0.8, 0.1]),   # 权力高,男性,人类
    "男人": np.array([0.2, 0.8, 0.1]),   # 权力低,男性,人类
    "女人": np.array([0.2, 0.2, 0.1]),   # 权力低,女性,人类
    "王后": np.array([0.9, 0.2, 0.1]),   # 权力高,女性,人类
    "苹果": np.array([0.0, 0.5, 0.9]),   # 权力无,中性,水果
}

print("=== 词嵌入空间中的语义运算 ===")
print("\n各词的嵌入向量:")
for word, vec in embeddings.items():
    print(f"  {word}: {vec}")

# --- 经典语义运算:国王 - 男人 + 女人 ≈ 王后 ---
result = embeddings["国王"] - embeddings["男人"] + embeddings["女人"]
print(f"\n语义运算:国王 - 男人 + 女人 = {result.round(4)}")

# 计算与所有词的余弦相似度
def cosine_similarity(a, b):
    """计算两个向量的余弦相似度"""
    # 余弦相似度 = 内积 / (||a|| × ||b||)
    return np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b))

print("\n与各词的余弦相似度:")
for word, vec in embeddings.items():
    sim = cosine_similarity(result, vec)
    print(f"  与'{word}'的相似度: {sim:.4f}")

# --- 找最相似的词 ---
similarities = {word: cosine_similarity(result, vec) for word, vec in embeddings.items()}
most_similar = max(similarities, key=similarities.get)
print(f"\n最相似的词:'{most_similar}'(相似度: {similarities[most_similar]:.4f})")

# --- 特征子空间分析 ---
print("\n=== 特征子空间分析 ===")
# 把嵌入向量拼成矩阵
emb_matrix = np.array(list(embeddings.values()))
print(f"嵌入矩阵形状:{emb_matrix.shape}")

# 用SVD分析主成分
U, S, Vt = np.linalg.svd(emb_matrix, full_matrices=False)
print(f"奇异值:{S.round(4)}")
print(f"第一主成分解释的方差比:{(S[0]**2 / np.sum(S**2)):.4f}")
print(f"前两个主成分解释的方差比:{np.sum(S[:2]**2) / np.sum(S**2):.4f}")

# --- 线性变换在嵌入空间中的应用 ---
print("\n=== 线性变换:性别方向投影 ===")
# "性别方向" = 男人 - 女人(近似)
gender_direction = embeddings["男人"] - embeddings["女人"]
gender_direction = gender_direction / np.linalg.norm(gender_direction)  # 归一化
print(f"性别方向向量:{gender_direction.round(4)}")

# 每个词在性别方向上的投影
for word, vec in embeddings.items():
    projection = np.dot(vec, gender_direction)
    print(f"  '{word}'在性别方向上的投影: {projection:.4f}")
大白话 词嵌入空间就像一个"语义地图"——每个词都有自己的坐标位置,意义相近的词挨在一起。线性运算就像在地图上"走"——从"国王"出发,往"男人"的反方向走(减去男人),再往"女人"的方向走(加上女人),你就走到了"王后"的位置。这就是AI理解语义的方式——把语言变成空间中的几何关系。

什么用(应用):向量空间与线性变换在AI中的应用无处不在:(1) 词嵌入(Word2Vec、GloVe、FastText)将词语映射到向量空间,使语义计算成为可能;(2) 图像嵌入(CNN特征提取)将图像映射到特征空间,使图像检索和分类成为可能;(3) Transformer中的自注意力机制通过Q、K、V三个线性变换计算注意力权重;(4) PCA降维通过特征值分解找到最重要的子空间;(5) 对抗生成网络(GAN)中的生成器和判别器都在特征空间中进行线性变换和非线性激活的交替运算。

哪些坑(缺点):嵌入空间存在"偏见放大"问题——如果训练数据中存在性别偏见(如"程序员"更接近"男人"),嵌入空间会继承甚至放大这种偏见。另外,嵌入空间的维度选择是一个经验性问题——太低会丢失信息,太高会增加计算量和过拟合风险。最后,不同的嵌入方法(Word2Vec、GloVe、BERT)产生的嵌入空间是不可直接比较的,因为它们选择了不同的"基"。

概念关系图谱

概念核心含义与AI的关系关联概念
向量空间满足加法和数乘封闭性的向量集合AI数据的"合法居住地",所有特征数据都存在于向量空间中子空间、基、维度
子空间向量空间中自身也是向量空间的子集PCA降维的目标子空间,特征子空间向量空间、列空间、零空间
生成整个空间且线性无关的向量组神经网络每层做"换基"操作,PCA找最优基维度、坐标、线性无关
维度基向量的个数,衡量空间的自由度决定模型的容量和信息密度,维度灾难基、秩、特征空间
线性变换保持线性关系的映射 T(αu+βv)=αT(u)+βT(v)神经网络层的数学本质,权重矩阵即变换矩阵矩阵表示、特征值、核与像
变换矩阵线性变换的数字表示深度学习框架的底层运算,nn.Linear的权重线性变换、矩阵乘法、SVD
核(零空间)被变换映射为零向量的输入集合衡量信息损失,网络退化问题秩、零化度、信息瓶颈
像(列空间)变换所有可能输出的集合衡量信息保留,特征空间的表达能力秩、列空间、信息容量
特征空间数据特征所在的向量空间所有AI模型的工作空间嵌入空间、特征提取
嵌入空间高维数据映射到的低维向量空间词嵌入、图像嵌入的数学基础Word2Vec、降维、语义计算

重点答疑

Q1: 向量空间和普通集合有什么区别?

向量空间和普通集合的根本区别在于"运算结构"。普通集合只是一堆元素的堆砌,没有运算规则;而向量空间不仅有元素(向量),还配备了加法和数乘两种运算,并且这两种运算的结果必须仍然在空间中(封闭性)。打个比方,普通集合像一个"仓库"——东西放进去就完了;向量空间像一个"工厂"——不仅能存放东西,还能对东西进行加工(加法和数乘),而且加工出来的东西还是工厂里的产品。在AI中,这个区别至关重要——只有向量空间中的数据才能进行矩阵运算(线性变换),而矩阵运算是深度学习的基石。

Q2: 为什么线性变换必须满足 T(αu + βv) = αT(u) + βT(v)?

这个条件保证了变换"不扭曲"线性关系。如果 T 不满足这个条件,那么变换前共线的点变换后可能不共线,变换前等距的点变换后可能不等距,整个空间的"线性结构"就被破坏了。在AI中,这个条件意味着:如果两个输入有某种线性关系(比如一个是另一个的两倍),那么经过网络层的线性变换后,这种关系仍然保持。这使得信息可以"结构化地"在网络中传递,而不是被随机搅乱。当然,纯线性变换的能力有限,所以我们需要非线性激活函数来增加表达能力。

Q3: 矩阵的秩和向量空间的维度是什么关系?

矩阵的秩(Rank)等于其列空间的维度,也就是矩阵列向量能张成的空间的维度。如果一个 m×n 矩阵的秩为 r,说明它的 n 个列向量中只有 r 个是"真正独立的",其余 n-r 个都可以用这 r 个的线性组合表示。在向量空间的语言中,矩阵 A 表示的线性变换 T: Rⁿ → Rᵐ 的像(列空间)维度就是 r,核(零空间)维度是 n-r。在AI中,矩阵的秩衡量了线性变换的"信息容量"——秩越大,能保留的信息越多;秩越小,信息损失越严重。低秩矩阵常用于模型压缩(如LoRA微调)。

Q4: 为什么神经网络需要非线性激活函数?纯线性变换不行吗?

纯线性变换无论叠加多少层,等价于一个线性变换。数学上,如果 f(x) = A₂(A₁x) = (A₂A₁)x = Bx,两层线性变换等价于一个线性变换 B = A₂A₁。这意味着无论网络多深,它都只能拟合线性关系,无法处理图像识别、语言理解等非线性问题。非线性激活函数(如ReLU、Sigmoid)打破了这种"线性坍缩",使得每一层都有独特的变换效果。用向量空间的语言说:线性变换只能做旋转、缩放、剪切,无法"弯曲"空间;而非线性激活函数可以弯曲空间,使得网络能拟合任意复杂的函数。

Q5: 什么是"维度灾难"?和向量空间的维度有什么关系?

维度灾难(Curse of Dimensionality)是指当向量空间的维度增加时,数据变得极其稀疏,很多在低维空间中有效的算法在高维空间中失效。具体表现:(1) 高维空间中随机点之间的距离趋于相等,距离度量失效;(2) 高维球体的体积几乎全部集中在"外壳"上,中心附近几乎没有点;(3) 要达到相同的密度覆盖,所需的数据量随维度指数增长。在AI中,这解释了为什么高维特征(如原始像素)直接使用效果差,而降维(PCA、嵌入)后效果更好——降维就是在向量空间中找到信息密度最高的子空间。

Q6: 特征值和特征向量在AI中有什么用?

特征值和特征向量揭示了线性变换的"本质特征"。特征向量是变换后方向不变的向量(只缩放不旋转),特征值是缩放的比例。在AI中:(1) PCA用协方差矩阵的特征向量作为主成分方向,特征值表示各方向的方差大小;(2) 谱聚类(Spectral Clustering)用图拉普拉斯矩阵的特征向量进行降维后再聚类;(3) PageRank算法本质上是求网页转移矩阵的最大特征值对应的特征向量;(4) 在推荐系统中,SVD分解将用户-物品矩阵分解为特征向量的组合。理解特征值和特征向量,就是理解线性变换的"骨架"。

Q7: 核与像和深度学习中的"信息瓶颈"有什么关系?

信息瓶颈(Information Bottleneck)理论认为,深度学习的过程是"压缩输入信息,同时保留与输出相关的信息"。用核与像的语言说:每一层线性变换都在做信息压缩——输入空间的信息一部分进入核(被丢弃),一部分进入像(被保留)。理想的网络层应该让核尽可能小(少丢信息),像尽可能大(多保留信息)。但如果像太大(维度太高),又会过拟合。所以网络设计需要在"压缩"和"保留"之间找平衡——这就是信息瓶颈的核心思想。ResNet的残差连接通过 x + F(x) 的方式,让一部分信息直接"跳过"变换(不进入核),从而缓解信息丢失。

Q8: Word2Vec中的"国王-男人+女人≈王后"是怎么用向量空间实现的?

Word2Vec训练时,让语义相似的词在向量空间中距离更近。训练完成后,向量空间中会自然形成一些"语义方向"——比如"性别方向"(男人→女人)、"时态方向"(walked→walks)、"国别方向"(法国→巴黎)。"国王-男人+女人≈王后"的原理是:从"国王"出发,沿着"性别方向"从"男性"走到"女性",就到达了"王后"。这在数学上就是向量减法和加法:king_vec - man_vec + woman_vec ≈ queen_vec。这个例子之所以经典,是因为它展示了向量空间中线性运算可以捕捉语义关系——这是AI"理解"语言的基础。

章节单词汇总

英文音标术语/释义
vector space/ˈvektər speɪs/向量空间,满足加法和数乘封闭性的向量集合
subspace/ˈsʌbspeɪs/子空间,向量空间中自身也是向量空间的子集
basis/ˈbeɪsɪs/基,生成整个空间且线性无关的向量组
dimension/daɪˈmenʃən/维度,基向量的个数,衡量空间的自由度
linear transformation/ˈlɪniər ˌtrænsfərˈmeɪʃən/线性变换,保持线性关系的映射
kernel/ˈkɜːrnl/核,被线性变换映射为零向量的输入集合
null space/nʌl speɪs/零空间,核的同义词
image/ˈɪmɪdʒ/像,线性变换所有可能输出的集合
column space/ˈkɑːləm speɪs/列空间,矩阵列向量的所有线性组合
rank/ræŋk/秩,矩阵列空间的维度
nullity/ˈnʌləti/零化度,核的维度
span/spæn/张成/生成,向量组所有线性组合的集合
linear independence/ˈlɪniər ˌɪndɪˈpendəns/线性无关,任一向量不能被其余向量线性表示
eigenvalue/ˈaɪɡənˌvæljuː/特征值,线性变换在特征方向上的缩放比例
eigenvector/ˈaɪɡənˌvektər/特征向量,变换后方向不变的向量
embedding/ɪmˈbedɪŋ/嵌入,将高维数据映射到低维向量空间
feature space/ˈfiːtʃər speɪs/特征空间,数据特征所在的向量空间
rank-nullity theorem/ræŋk ˈnʌləti ˈθɪərəm/秩-零化度定理,dim(Ker)+dim(Im)=dim(V)
singular value decomposition/ˈsɪŋɡjələr ˈvæljuː ˌdiːkɑːmpəˈzɪʃən/奇异值分解,矩阵的SVD分解
curse of dimensionality/kɜːrs əv daɪˌmenʃəˈnæləti/维度灾难,高维空间中数据稀疏导致算法失效

面试练习

Q1 [单选] 下列哪个集合是 R² 的子空间?

    undefined
解答:选B。x = y 的向量集合对加法封闭((a,a)+(b,b)=(a+b,a+b),仍满足x=y)和数乘封闭(α(a,a)=(αa,αa),仍满足x=y),且包含零向量(0,0)。A不包含零向量且数乘不封闭(α<0时跑出去);C是单位圆,加法不封闭;D不包含零向量(0+0≠1)。

Q2 [单选] 如果矩阵 A 是 3×5 的,秩为 2,那么线性变换 T(x) = Ax 的核的维度是多少?

    undefined
解答:选C。根据秩-零化度定理:dim(Ker) + dim(Im) = dim(V)。输入空间维度 dim(V) = 5(矩阵列数),像的维度 dim(Im) = rank(A) = 2,所以 dim(Ker) = 5 - 2 = 3。

Q3 [单选] 下列哪个不是线性变换?

    undefined
解答:选C。T(x) = x + b(b≠0)不满足齐次性:T(αx) = αx + b ≠ α(x + b) = αT(x)。严格来说这是仿射变换,不是线性变换。注意:在深度学习中,y = Wx + b 常被称为"线性层",但数学上 Wx 是线性变换,b 是偏移项,整体是仿射变换。

Q4 [单选] 在向量空间 V 中,基向量的个数称为 V 的维度。如果 V 的维度为 n,那么 V 中最多有多少个线性无关的向量?

    undefined
解答:选B。维度为 n 的向量空间中,任意 n+1 个向量必定线性相关(因为基只有 n 个,任何向量都可以用基表示)。所以最多有 n 个线性无关的向量,恰好等于基向量的个数。

Q5 [单选] 矩阵 A 的特征值为 2 和 3,则矩阵 A² 的特征值为?

    undefined
解答:选B。如果 Av = λv,则 A²v = A(Av) = A(λv) = λ(Av) = λ(λv) = λ²v。所以 A² 的特征值是 A 的特征值的平方:2²=4,3²=9。在AI中,这个性质用于理解多层线性变换的复合效果。

Q6 [多选] 下列哪些是向量空间 R³ 的子空间?

    undefined
解答:选A、B、D。A是yz平面,对加法和数乘封闭;B是对角线方向,对加法和数乘封闭;C不包含零向量(第一个分量必须为1),加法也不封闭;D是最小的子空间(平凡子空间),对加法和数乘封闭。

Q7 [多选] 关于线性变换 T: V → W,下列哪些说法正确?

    undefined
解答:选A、B、D。A正确:T(0) = T(0·v) = 0·T(v) = 0;B正确:线性变换条件可以推广到任意有限个向量;C错误:线性变换不一定是一一对应的(如零变换把所有向量映射到0);D正确:核对加法和数乘封闭,且包含零向量。

Q8 [多选] 关于矩阵的秩,下列哪些说法正确?

    undefined
解答:选A、B、D。A和B正确:矩阵的行秩等于列秩,统称为秩;C错误:3×5矩阵的秩最大为 min(3,5) = 3(行数和列数的较小值);D正确:SVD分解中,非零奇异值的个数等于矩阵的秩。

Q9 [多选] 在深度学习中,线性变换的哪些性质对模型设计有重要影响?

    undefined
解答:选A、B、D。A正确:这是需要非线性激活函数的根本原因;B正确:秩低意味着信息损失,这是网络退化问题的数学解释;C错误:线性变换只能拟合线性关系,无法拟合非线性函数;D正确:矩阵乘法可以在GPU上高效并行计算,这是深度学习可行的基础。

Q10 [多选] 关于词嵌入空间,下列哪些说法正确?

    undefined
解答:选A、B、D。A正确:这是词嵌入训练的目标;B正确:嵌入空间中确实存在可解释的语义方向;C错误:不同模型的嵌入空间选择了不同的"基",向量不可直接比较(就像同一位置在不同地图上的坐标不同);D正确:这个经典例子利用了向量减法和加法,是向量空间线性运算的体现。