微分几何深度实战:从黎曼流形、列维-奇维塔联络与测地线到平行移动与高斯-博内定理的完整工程链路
微分几何把微积分从平直的欧几里得空间搬到了弯曲的流形(manifold)上:当你描述地球表面、神经网络的参数空间、或者大模型的隐空间时,局部看起来像平面、整体却高度弯曲。本文不走纯数学推导,而是用可运行的 NumPy 代码把微分几何的核心对象——切空间、黎曼度量、列维-奇维塔(Levi-Civita)联络、测地线、平行移动、高斯曲率、高斯-博内(Gauss-Bonnet)定理——逐一从第一性原理实现出来,并贯穿一条与 AI/深度学习连接的工程主线。
读完你将拥有:一个能从度量张量自动算出克里斯托费尔符号、在球面上积分测地线、量化平行移动和乐角、并从曲率数值积分回收欧拉示性数的最小但完整的微分几何引擎。
一、流形与切空间:从嵌入到抽象
最直观的流形是嵌入在 ℝ³ 中的曲面,例如单位球面。在球坐标 $(\theta,\phi)$(余纬度与方位角)下,球面由映射 $r:\mathbb{R}^2\to\mathbb{R}^3$ 参数化。流形上某点的切空间由参数方向的两个偏导张成:
$$
\mathbf{r}_\theta=\frac{\partial r}{\partial\theta},\qquad
\mathbf{r}_\phi=\frac{\partial r}{\partial\phi}
$$
对单位球面 $r(\theta,\phi)=(\sin\theta\cos\phi,\sin\theta\sin\phi,\cos\theta)$,这两个切向量处处正交、长度分别为 $1$ 与 $\sin\theta$。我们用代码验证这一点,并由此得到曲面的第一基本形式分量 $E=\mathbf{r}_\theta\!\cdot\!\mathbf{r}_\theta,\;F=\mathbf{r}_\theta\!\cdot\!\mathbf{r}_\phi,\;G=\mathbf{r}_\phi\!\cdot\!\mathbf{r}_\phi$。
import numpy as np
def sphere(u, v):
# u=theta(余纬度, 0..pi), v=phi(方位角, 0..2pi), 单位球面
return np.array([np.sin(u)*np.cos(v),
np.sin(u)*np.sin(v),
np.cos(u)])
def sphere_basis(u, v):
# 切空间两个基向量 d/du, d/dv
ru = np.array([np.cos(u)*np.cos(v), np.cos(u)*np.sin(v), -np.sin(u)])
rv = np.array([-np.sin(u)*np.sin(v), np.sin(u)*np.cos(v), 0.0])
return ru, rv
u, v = np.pi/3, np.pi/4
ru, rv = sphere_basis(u, v)
print("|ru| =", round(float(np.linalg.norm(ru)), 6)) # 期望 1.0
print("|rv| =", round(float(np.linalg.norm(rv)), 6)) # 期望 sin(pi/3)=0.866025
print("ru·rv =", round(float(ru @ rv), 6)) # 期望 0.0 (正交)
E = ru @ ru; F = ru @ rv; G = rv @ rv
print("E,F,G =", E, F, G) # 期望 1, 0, sin^2(pi/3)=0.75
运行结果:
|ru| = 1.0
|rv| = 0.866025
ru·rv = 0.0
E,F,G = 1.0 0.0 0.75
切空间是流形局部线性的“显微镜”:在每一点上,我们可以像在欧氏空间那样做向量加法,但不同点的切空间彼此“倾斜”,需要一个额外的结构——联络(connection)——才能把一个点的向量搬运到另一个点。
二、第一基本形式与曲线弧长
第一基本形式 $I=E\,du^2+2F\,du\,dv+G\,dv^2$ 是流形上的“尺子”:它定义了切向量的内积与曲线的弧长。曲面上一条参数曲线 $(\theta(t),\phi(t))$ 的长度为
$$
L=\int \sqrt{E\dot\theta^2+2F\dot\theta\dot\phi+G\dot\phi^2}\,dt
$$
以赤道(固定 $\theta=\pi/2$,$\phi$ 从 $0$ 扫到 $2\pi$)为例,理论上长度应为 $2\pi$。代码数值积分验证:
import numpy as np
def g_metric(u):
return 1.0, 0.0, np.sin(u)**2 # E, F, G (球面)
def arc_length(u0, du_func, dv_func, t0, t1, N=200000):
ts = np.linspace(t0, t1, N)
E, F, G = g_metric(u0)
# 这里 u 固定 -> du/dt=0, dv/dt=1
integrand = np.sqrt(E*0.0 + 2*F*0.0 + G*1.0) * np.ones_like(ts)
return np.trapezoid(integrand, ts)
L = arc_length(np.pi/2, None, None, 0, 2*np.pi)
print("赤道弧长(数值) =", L, " 解析值 =", 2*np.pi)
运行结果:
赤道弧长(数值) = 6.283185307179586 解析值 = 6.283185307179586
三、度量张量与克里斯托费尔符号
列维-奇维塔联络是“与度量相容且对称(无挠)”的唯一联络。它的系数——克里斯托费尔(Christoffel)符号 $\Gamma^k_{ij}$——完全由度量张量决定:
$$
\Gamma^k_{ij}=\frac{1}{2}\,g^{kl}\left(\partial_i g_{jl}+\partial_j g_{il}-\partial_l g_{ij}\right)
$$
对球面度量 $g_{ij}=\mathrm{diag}(1,\sin^2\theta)$,我们手算得到唯一非零分量 $\Gamma^\theta_{\phi\phi}=-\sin\theta\cos\theta$ 与 $\Gamma^\phi_{\theta\phi}=\Gamma^\phi_{\phi\theta}=\cot\theta$。下面从度量出发自动算出它们:
import numpy as np
def christoffel(u):
# 对角度量 g = diag(E, G),仅依赖 theta=u
E = 1.0
G = np.sin(u)**2
dG = 2*np.sin(u)*np.cos(u) # dG/du
ginv = np.array([[1.0/E, 0.0],
[0.0, 1.0/G]]) # g^{ij}
Gam = np.zeros((2, 2, 2)) # Gam[k, i, j] = Gamma^k_{ij}
# 仅 G 对 u(指标0) 有导数
Gam[0, 1, 1] = -0.5 * ginv[0,0] * dG # Gamma^theta_{phiphi}
Gam[1, 0, 1] = 0.5 * ginv[1,1] * dG # Gamma^phi_{thetaphi}
Gam[1, 1, 0] = 0.5 * ginv[1,1] * dG # Gamma^phi_{phitheta}
return Gam
u = np.pi/3
Gam = christoffel(u)
print("Gamma^theta_{phiphi} =", Gam[0,1,1], " 期望", -np.sin(u)*np.cos(u))
print("Gamma^phi_{thetaphi} =", Gam[1,0,1], " 期望", 1/np.tan(u))
运行结果:
Gamma^theta_{phiphi} = -0.4330127018922193 期望 -0.4330127018922193
Gamma^phi_{thetaphi} = 0.5773502691896257 期望 0.5773502691896257
克里斯托费尔符号不是张量,它编码了坐标网格在弯曲空间里的“扭动”——正是它让我们能定义“方向不改变”的平行移动与测地线。
四、测地线方程与数值积分
测地线是流形上“最直”的曲线(局部最短路径),满足
$$
\frac{d^2x^k}{d\lambda^2}+\Gamma^k_{ij}\frac{dx^i}{d\lambda}\frac{dx^j}{d\lambda}=0
$$
在球面上,测地线就是大圆。我们从 $(\theta_0,\phi_0)=(\pi/3,0)$ 出发,给一个单位切速度,用 RK4 积分该二阶方程组,并验证两件事:① 整条曲线始终落在半径 1 的球面上;② 曲线位于一个过原点的固定平面内(大圆的判据:角动量 $\mathbf{L}=\mathbf{r}\times\dot{\mathbf{r}}$ 大小方向恒定)。
import numpy as np
def deriv(state, Gam_func):
u, v, du, dv = state
Gam = Gam_func(u)
d2u = -(Gam[0,1,1]*dv*dv + 2*Gam[0,0,1]*du*dv + Gam[0,0,0]*du*du)
d2v = -(Gam[1,0,1]*du*dv + Gam[1,1,0]*du*dv + Gam[1,1,1]*dv*dv + Gam[1,0,0]*du*du)
return np.array([du, dv, d2u, d2v])
def r_of(u, v):
return np.array([np.sin(u)*np.cos(v), np.sin(u)*np.sin(v), np.cos(u)])
def sphere_basis(u, v):
ru = np.array([np.cos(u)*np.cos(v), np.cos(u)*np.sin(v), -np.sin(u)])
rv = np.array([-np.sin(u)*np.sin(v), np.sin(u)*np.cos(v), 0.0])
return ru, rv
Gam_func = christoffel
u0, v0 = np.pi/3, 0.0
du0, dv0 = 0.0, 1.0/np.sin(u0) # 单位速度 (|vdot|=1)
st = np.array([u0, v0, du0, dv0])
def rk4(state, h):
k1 = deriv(state, Gam_func)
k2 = deriv(state + 0.5*h*k1, Gam_func)
k3 = deriv(state + 0.5*h*k2, Gam_func)
k4 = deriv(state + h*k3, Gam_func)
return state + h/6*(k1 + 2*k2 + 2*k3 + k4)
radii, Ls = [], []
for _ in range(4000):
st = rk4(st, 0.0025)
r = r_of(st[0], st[1])
ru, rv = sphere_basis(st[0], st[1])
rdot = ru*st[2] + rv*st[3]
L = np.cross(r, rdot)
radii.append(np.linalg.norm(r))
Ls.append(L)
radii = np.array(radii); Ls = np.array(Ls)
print("半径 min/max =", round(radii.min(),5), round(radii.max(),5), " (期望 1.0)")
print("|L| 起点/终点 =", round(np.linalg.norm(Ls[0]),4), round(np.linalg.norm(Ls[-1]),4))
print("L 方向偏差(终点与起点夹角, 度) =",
round(float(np.degrees(np.arccos(np.clip(np.dot(Ls[0],Ls[-1])/(np.linalg.norm(Ls[0])*np.linalg.norm(Ls[-1])),-1,1)))),4))
运行结果:
半径 min/max = 1.0 1.0 (期望 1.0)
|L| 起点/终点 = 1.0 1.0
L 方向偏差(终点与起点夹角, 度) = 0.0
曲线始终贴在球面上、角动量方向几乎零漂移——这正是大圆的数值指纹。若换一条初速度(如同时给 $d\theta/d\lambda\neq0$),你会得到另一条大圆,而非小圆。
五、平行移动与和乐角(Holonomy)
沿一条闭曲线平行移动一个切向量:移动中要求向量的协变导数为零,即 $\nabla_{\dot\gamma}V=0$,在坐标下为
$$
\frac{dV^k}{d\lambda}+\Gamma^k_{ij}\frac{dx^i}{d\lambda}V^j=0
$$
我们沿球面纬度圈(固定 $\theta=\theta_0$)走一整圈,把初始切向量 $V(0)$ 平行搬一圈回到起点。由于球面的正曲率,回到起点时向量会“偏转”一个和乐角,其大小等于该纬度圈所围球面帽的曲率积分(即帽面积 $A=2\pi(1-\cos\theta_0)$)——这正是高斯-博内定理在“无穷小闭环”上的局部版本。下面取三个非退化的纬度圈验证:和乐角(弧度)与帽面积逐一对齐。
import numpy as np
def parallel_transport(theta0, N=4000):
# 纬度圈 theta=theta0, phi: 0->2pi,用坐标基 (∂/∂θ, ∂/∂φ) 成分 (V^θ, V^φ) 积分。
# 平行移动方程 dV^k/dphi = -Γ^k_{φj} V^j 在球面上化为:
# dV^θ/dphi = sin(theta0)cos(theta0) V^φ
# dV^φ/dphi = -cot(theta0) V^θ
a = np.sin(theta0)*np.cos(theta0)
b = -1.0/np.tan(theta0)
V = np.array([1.0, 0.0]) # 初始 = 单位切向量 e_theta(0)
dphi = 2*np.pi/N
for _ in range(N):
k1 = np.array([a*V[1], b*V[0]])
k2 = np.array([a*(V[1]+0.5*dphi*k1[1]), b*(V[0]+0.5*dphi*k1[0])])
k3 = np.array([a*(V[1]+0.5*dphi*k2[1]), b*(V[0]+0.5*dphi*k2[0])])
k4 = np.array([a*(V[1]+dphi*k3[1]), b*(V[0]+dphi*k3[0])])
V = V + dphi/6*(k1 + 2*k2 + 2*k3 + k4)
# 转回笛卡尔: V_cart = V^θ·(∂r/∂θ) + V^φ·(∂r/∂φ);注意 |∂r/∂φ| = sin(theta0)
e_theta = np.array([np.cos(theta0), 0.0, -np.sin(theta0)]) # |∂r/∂θ| = 1
e_phi = np.array([0.0, np.sin(theta0), 0.0]) # |∂r/∂φ| = sin(theta0)
V_cart = V[0]*e_theta + V[1]*e_phi
V0 = e_theta # 起点切向量
ang = np.degrees(np.arccos(np.clip(np.dot(V_cart, V0), -1, 1)))
return ang
for th in [np.pi/4, np.pi/3, np.pi/6]:
ang = parallel_transport(th)
area = 2*np.pi*(1-np.cos(th)) # 纬度圈围成的球面帽面积 = 和乐角(rad)
print(f"theta0={th:.4f}: 和乐角={ang:.4f}° = {np.radians(ang):.4f} rad 球面帽面积={area:.4f} rad")
运行结果:
theta0=0.7854: 和乐角=105.4416° = 1.8403 rad 球面帽面积=1.8403 rad
theta0=1.0472: 和乐角=180.0000° = 3.1416 rad 球面帽面积=3.1416 rad
theta0=0.5236: 和乐角=48.2309° = 0.8418 rad 球面帽面积=0.8418 rad
(注:θ0=π/2 的赤道是测地线,沿它平行移动的垂直向量保持不动,和乐角为 0,属于退化情形,故未列入上表。)
(注:和乐角以度计,与面积弧度制量纲不同;这里的对应关系是“角度(度) ≈ 面积(rad) × 180/π”。)数值完美吻合高斯-博内揭示的深刻事实:曲率是平行移动绕闭圈产生的“旋转量”。
六、高斯曲率与里奇曲率
高斯曲率 $K$ 把第二基本形式的弯曲“压”成切平面上的一个数。对正交坐标 $F=0$ 的度量 $ds^2=E(u)\,du^2+G(u)\,dv^2$,有公式
$$
K=-\frac{1}{2\sqrt{EG}}\left[\frac{\partial}{\partial u}\!\left(\frac{G_u}{\sqrt{EG}}\right)
+\frac{\partial}{\partial v}\!\left(\frac{E_v}{\sqrt{EG}}\right)\right]
$$
对球面,$E=1,\;G=\sin^2\theta,\;\sqrt{EG}=\sin\theta$,代入即得 $K=1$。我们也可以从克里斯托费尔符号数值微分出里曼曲率 $R^\theta_{\phi\theta\phi}$,再用 $K=R_{1212}/\det(g)$ 验证——这更接近“从联络定义曲率”的本质路径:
import numpy as np
def riemann_R(u, h=1e-5):
Gp = christoffel(u+h); Gm = christoffel(u-h); Gc = christoffel(u)
dGam = (Gp[0,1,1] - Gm[0,1,1])/(2*h) # d/du Gamma^theta_{phiphi}
term = Gc[0,1,1] * Gc[1,0,1] # Gamma^theta_{phiphi} * Gamma^phi_{thetaphi}
R = dGam - term # R^theta_{phithetaphi}
detg = 1.0 * np.sin(u)**2
return R, R/detg # (R^theta_{phithetaphi}, K)
for u in [0.3, 1.0, np.pi/2, 2.0]:
R, K = riemann_R(u)
print(f"u={u:.3f}: R^theta_{{phithetaphi}}={R:.5f} (期望 sin^2 u={np.sin(u)**2:.5f}), K={K:.5f}")
运行结果:
u=0.300: R^theta_{phithetaphi}=0.08733 (期望 sin^2 u=0.08733), K=1.00000
u=1.000: R^theta_{phithetaphi}=0.70807 (期望 sin^2 u=0.70807), K=1.00000
u=1.571: R^theta_{phithetaphi}=1.00000 (期望 sin^2 u=1.00000), K=1.00000
u=2.000: R^theta_{phithetaphi}=0.82682 (期望 sin^2 u=0.82682), K=1.00000
球面处处 $K=1$:这就是“恒定正曲率”。换成平面 $K=0$、伪球面 $K<0$,几何性质天差地别——这也正是双曲隐空间(Poincaré 嵌入)能表达树状层级结构的根源。
七、高斯-博内定理与欧拉示性数
微分几何最震撼的定理之一:闭曲面的总曲率等于拓扑不变量。
$$
\iint_M K\,dA = 2\pi\chi(M)
$$
其中 $\chi$ 是欧拉示性数($\chi=\text{顶点}-\text{边}+\text{面}$)。对球面 $\chi=2$,故 $\iint K\,dA=4\pi$;对环面 $\chi=0$,总曲率为 0。我们用数值积分直接验证这两个标志性结果,也顺便把本文与上一期的拓扑数据分析(TDA)连接起来——$\chi$ 正是 TDA 里贝蒂数 $\beta_0-\beta_1+\beta_2$ 的连续版本。
import numpy as np
def sphere_total_curvature(nu=2000, nv=4000):
us = np.linspace(1e-6, np.pi-1e-6, nu)
vs = np.linspace(0, 2*np.pi, nv, endpoint=False) # 方位角方向周期,端点不重复
U, _ = np.meshgrid(us, vs, indexing='ij') # (nu, nv)
# K=1, 面积元 dA = sin u du dv
return float(np.sum(np.sin(U))) * (us[1]-us[0]) * (vs[1]-vs[0])
def torus_total_curvature(R=3.0, r=1.0, nu=2000, nv=4000):
us = np.linspace(0, 2*np.pi, nu, endpoint=False)
vs = np.linspace(0, 2*np.pi, nv, endpoint=False)
U, _ = np.meshgrid(us, vs, indexing='ij')
# 环面第一基本形式: E=r^2, F=0, G=(R+r cos u)^2, K = cos u / (r(R+r cos u))
# 面积元 dA = sqrt(EG) du dv = r(R+r cos u) du dv => K*dA = cos u du dv (与 R,r 无关!)
return float(np.sum(np.cos(U))) * (us[1]-us[0]) * (vs[1]-vs[0])
print("球面 ∫K dA =", sphere_total_curvature(), " 2πχ =", 2*np.pi*2)
print("环面 ∫K dA =", torus_total_curvature(), " 2πχ =", 2*np.pi*0)
运行结果:
球面 ∫K dA = 12.566368037788425 2πχ = 12.566370614359172
环面 ∫K dA = 0.0 2πχ = 0.0
一个积分算出了球面的 $4\pi$ 与环面的 $0$——几何(局部曲率)与拓扑(全局示性数)在此握手。这就是微分几何的灵魂:局部运算能“数”出全局不变量。
八、与 AI / 深度学习的工程映射
| 微分几何概念 | 在 AI / DL 中的对应 | 工程含义 |
|---|---|---|
| 流形假设 | 数据流形(manifold hypothesis) | 高维数据实际栖身于低维黎曼流形,催生 ISOMAP / LLE / 本文 TDA 篇 |
| 黎曼度量 | SPD 矩阵流形、自然梯度(Fisher 信息度量) | 在协方差/精度矩阵流形上做优化,得黎曼 Adam、SPD 网络 |
| 测地线 | 隐空间大圆插值、双曲空间测地 | VAE 潜空间沿测地线插值比线性插值更保真;Poincaré 嵌入用负曲率测地表达层级 |
| 平行移动 | 沿流形搬运特征/梯度 | 几何深度学习在 SPD/Stiefel 流形上平行传输,避免“切空间错位” |
| 高斯曲率 | 损失地貌曲率、平坦极小值 | 低曲率(平坦)盆地泛化更好;曲率连接本文「凸优化」「数值分析」主线 |
| 高斯-博内 | 欧拉示性数 $\chi=\beta_0-\beta_1+\beta_2$ | 与 TDA 贝蒂数直接对应,用连续几何“数”出拓扑 |
一个具体落点:现代大模型的对齐与优化大量依赖黎曼优化——当参数或激活落在对称正定(SPD)矩阵流形上时,普通的欧氏梯度下降会“走出”流形,必须沿测地线更新、用平行移动搬运动量。本文的克里斯托费尔/测地线引擎,正是这些高级优化器的微型内核。
九、工程边界与陷阱
- 坐标奇点:球面度量在极点 $\theta=0,\pi$ 处 $\sqrt{G}=\sin\theta\to0$,克里斯托费尔符号发散。数值积分须避开极点(本文用
1e-6截断),真实系统需多坐标卡拼接(charts/atlas)。 - 度量选择决定几何:同一流形换度量,测地线、曲率全变。务必选“有物理/语义意义”的度量(如 Fisher 信息度量),而非随手取欧氏。
- 曲率符号 = 几何类型:正曲率(球面,闭合有限)/ 零曲率(平面)/ 负曲率(双曲,指数扩张)——对应不同的容量与表达力,选错会严重误导模型假设。
- 离散化误差:数值积分曲率总量时网格分辨率直接决定能否“数”出正确的 $\chi$;粗网格会把环面误判为非零。
- 高维诅咒:本文用 2D 曲面讲清原理;到高维黎曼流形,克里斯托费尔符号达 $n^3$、曲率 $n^4$ 量级,需稀疏结构或对称群化简。
- 曲率≠可计算即有用:高斯-博内给出的是拓扑不变量,对“近似流形”或带噪声数据需用 TDA 的持久同调做稳定估计(见本站 TDA 篇)。
小结
我们从嵌入曲面的切空间出发,用纯 NumPy 实现了:第一基本形式 → 克里斯托费尔符号 → 测地线(大圆)→ 平行移动与和乐角 → 里曼曲率与高斯曲率 → 高斯-博内定理的数值积分。整套引擎零依赖、可复现,并揭示了连接 AI 的三条主线:流形假设(数据几何)、黎曼优化(曲率感知的学习)、高斯-博内(几何即拓扑)。微分几何不是抽象数学,它是描述“弯曲世界里如何测量、移动与学习”的通用语言。

发表评论 取消回复