跳到主要内容

高等数学与人工智能 (Math & AI)

“数学是人工智能的灵魂。” —— 本章节旨在建立从抽象数学结构到现代神经网络原语的严密映射体系。

1. 代数结构与张量分析 (Algebraic Structures & Tensors)

在 AI 中,数据不再是孤立的标量,而是存在于多维向量空间中的点。

1.1 向量空间与线性变换

模型的核心是 线性变换 f(x)=Wx+bf(x) = Wx + b

  • 特征提取:本质上是将数据从高维流形投影到低维线性子空间(或更高维)。
  • 秩 (Rank):决定了模型表达能力的上限与参数冗余度。

1.2 张量积与多维数组

张量(Tensor)是线性空间及其对偶空间的笛卡尔积。在计算图中,张量是信息的载体。

数学定义

一个 (p,q)(p, q) 型张量是一个多重线性映射: T:V××Vp×V××VqRT: \underbrace{V^* \times \dots \times V^*}_{p} \times \underbrace{V \times \dots \times V}_{q} \to \mathbb{R} 而在工程实现中,我们通常将其简化为 nn 维数组。


2. 微积分:优化的动力源 (Calculus & Optimization)

2.1 链式法则与梯度向量

梯度 f\nabla f 是函数增长最快的方向,而深度学习通过反向传播 (Backpropagation) 逆向应用链式法则。

对于复合函数 L(w)=f(g(h(w)))L(w) = f(g(h(w))),其梯度推导为: Lw=Lffgghhw\frac{\partial L}{\partial w} = \frac{\partial L}{\partial f} \cdot \frac{\partial f}{\partial g} \cdot \frac{\partial g}{\partial h} \cdot \frac{\partial h}{\partial w}

2.2 Jacobian 与 Hessian 矩阵

  • Jacobian 矩阵:描述多元向量函数的一阶偏导,用于多输出层的反向传播。
  • Hessian 矩阵:描述二阶偏导,反映损失函数的曲率,是二阶优化(如 Newton 法)的基础。

3. 机器学习原语:建模与损失 (ML Primitives)

3.1 线性建模与最小二乘

建立输入 XX 与输出 YY 的线性映射。 Y=Xβ+ϵY = X\beta + \epsilon

3.2 损失函数 (Loss Functions)

损失函数衡量预测值与真实值之间的“距离”。

  • 均方误差 (MSE):对应高斯噪声假设。
  • 交叉熵 (Cross-Entropy):对应伯努利分布或多项式分布假设。

LCE=iyilog(y^i)L_{CE} = -\sum_{i} y_i \log(\hat{y}_i)


4. 深度学习原语:神经元与反向传播 (DL Primitives)

4.1 神经元模型

一个神经元由线性加权与非线性激活组成: a=σ(Wx+b)a = \sigma(Wx + b)

4.2 优化算法:梯度下降族

从基础 SGD 到 Adam,优化的核心在于动量 (Momentum) 与自适应学习率。


5. C++ 工业级实现例题 (Examples)

例题 1:矩阵乘法优化原语

在 AI 推理中,矩阵乘法 (GEMM) 是耗时占比最高的操作。

例题 2:从零实现简单梯度下降 (SGD)

手动推导并实现一元线性回归的参数更新。


6. 进阶练习与挑战 (Exercises)

练习 1:Sigmoid 激活函数的导数性质

证明对于 Sigmoid 函数 σ(x)=11+ex\sigma(x) = \frac{1}{1 + e^{-x}},其导数满足 σ(x)=σ(x)(1σ(x))\sigma'(x) = \sigma(x)(1 - \sigma(x))。 并思考这一性质如何加速反向传播的计算。

Check Solution (C++ Verification)

数学证明: σ(x)=ddx(1+ex)1=(1+ex)2(ex)=ex(1+ex)2\sigma'(x) = \frac{d}{dx}(1 + e^{-x})^{-1} = -(1 + e^{-x})^{-2} \cdot (-e^{-x}) = \frac{e^{-x}}{(1+e^{-x})^2} =11+exex1+ex=σ(x)(1+ex)11+ex=σ(x)(1σ(x))= \frac{1}{1+e^{-x}} \cdot \frac{e^{-x}}{1+e^{-x}} = \sigma(x) \cdot \frac{(1+e^{-x}) - 1}{1+e^{-x}} = \sigma(x)(1-\sigma(x))

C++ 数值验证:

#include <iostream>
#include <cmath>

double sigmoid(double x) { return 1.0 / (1.0 + exp(-x)); }
double sigmoid_prime_theory(double x) { return sigmoid(x) * (1.0 - sigmoid(x)); }
double sigmoid_prime_numeric(double x, double eps = 1e-7) {
return (sigmoid(x + eps) - sigmoid(x - eps)) / (2 * eps);
}

int main() {
double x = 2.5;
std::cout << "理论导数: " << sigmoid_prime_theory(x) << std::endl;
std::cout << "数值导数: " << sigmoid_prime_numeric(x) << std::endl;
return 0;
}

练习 2:Softmax 层与交叉熵梯度的耦合

在多分类问题中,Softmax 层 ai=ezijezja_i = \frac{e^{z_i}}{\sum_j e^{z_j}} 常常与交叉熵损失 L=kyklogakL = -\sum_k y_k \log a_k 配合。 证明损失 LL 对 Logit ziz_i 的梯度为: Lzi=aiyi\frac{\partial L}{\partial z_i} = a_i - y_i

Check Solution (Jacobian Analysis)

推导过程:

  1. 考虑 ajzi\frac{\partial a_j}{\partial z_i}
    • i=ji=j 时,aizi=ai(1ai)\frac{\partial a_i}{\partial z_i} = a_i(1-a_i)
    • iji \neq j 时,ajzi=aiaj\frac{\partial a_j}{\partial z_i} = -a_i a_j
  2. 使用链式法则: Lzi=jLajajzi=j(yjaj)ajzi\frac{\partial L}{\partial z_i} = \sum_j \frac{\partial L}{\partial a_j} \frac{\partial a_j}{\partial z_i} = \sum_j (-\frac{y_j}{a_j}) \frac{\partial a_j}{\partial z_i} =yiaiai(1ai)+jiyjajaiaj= -\frac{y_i}{a_i} a_i(1-a_i) + \sum_{j \neq i} \frac{y_j}{a_j} a_i a_j =yi+yiai+jiyjai=yi+aijyj= -y_i + y_i a_i + \sum_{j \neq i} y_j a_i = -y_i + a_i \sum_j y_j 由于 jyj=1\sum_j y_j = 1(One-hot 标签),得证: Lzi=aiyi\frac{\partial L}{\partial z_i} = a_i - y_i

这种形式极其简洁,减少了反向传播的乘法开销。

练习 3:简单的多层感知机 (MLP) 前向引擎

编写一个 C++ 类,支持通过线性代数原语(矩阵-向量乘法)执行 MLP 的前向传播。

Check Solution (C++ Engine)
#include <iostream>
#include <vector>
#include <cmath>
#include <numeric>

using namespace std;

class Layer {
public:
vector<vector<double>> weights;
vector<double> bias;

Layer(int in_dim, int out_dim) {
// 简单初始化
weights.resize(out_dim, vector<double>(in_dim, 0.1));
bias.resize(out_dim, 0.0);
}

vector<double> forward(const vector<double>& input) {
vector<double> output(weights.size(), 0.0);
for (int i = 0; i < weights.size(); ++i) {
double dot = inner_product(input.begin(), input.end(), weights[i].begin(), 0.0);
output[i] = tanh(dot + bias[i]); // 使用 tanh 激活
}
return output;
}
};

int main() {
vector<double> input = {0.5, -0.2, 0.1};
Layer l1(3, 4);
Layer l2(4, 2);

auto hidden = l1.forward(input);
auto output = l2.forward(hidden);

cout << "Output: [" << output[0] << ", " << output[1] << "]" << endl;
return 0;
}