从线性到非线性——神经网络的原理、训练与可解释性探索

"人脑有大约 860 亿个神经元,而现代神经网络可能只有几百万个参数。

但我们已经能用它识别图像、翻译语言、生成文本------

这不是因为网络像大脑,而是因为我们找到了一种通用的函数逼近方式。"

------深度学习的工程哲学


一、为什么需要神经网络?

在前六章中,我们系统学习了线性模型决策树集成方法(随机森林、XGBoost) 等经典机器学习算法。它们在结构化数据(表格数据)上表现卓越,具备良好的可解释性和工程稳定性。

然而,当我们面对以下任务时,传统方法开始力不从心:

  • 图像识别:像素之间存在复杂的局部与全局空间关系;
  • 语音识别:音频信号是高维、时序、非平稳的;
  • 自然语言处理:词语组合具有高度上下文依赖性;
  • 高维非线性模式:特征交互呈指数级增长(如"用户点击 A 且未购买 B 且浏览 C")。

这些任务的共同特点是:输入维度极高、特征间存在深层非线性交互、且难以通过人工特征工程有效表达

🎯 本章目标

  • 理解神经网络如何通过"层叠非线性变换"实现通用函数逼近;
  • 掌握前向传播、反向传播、梯度下降的核心机制;
  • 从零实现一个全连接神经网络(MLP);
  • 深入探讨激活函数、损失函数、优化器的选择逻辑;
  • 辩证看待神经网络的"黑箱"属性,探索可解释性工具;
  • 对比神经网络与传统模型的适用边界。

二、神经网络的直觉:从感知机到多层网络

2.1 感知机:神经网络的起点

1957 年,Frank Rosenblatt 提出感知机(Perceptron),这是最早的神经网络模型。

对于输入 x = [ x 1 , x 2 , . . . , x n ] ⊤ \mathbf{x} = [x_1, x_2, ..., x_n]^\top x=[x1,x2,...,xn]⊤,感知机计算:

z = w ⊤ x + b = ∑ i = 1 n w i x i + b z = \mathbf{w}^\top \mathbf{x} + b = \sum_{i=1}^{n} w_i x_i + b z=w⊤x+b=i=1∑nwixi+b

然后通过阶跃函数输出:

y ^ = { 1 , if z ≥ 0 0 , otherwise \hat{y} = \begin{cases} 1, & \text{if } z \geq 0 \\ 0, & \text{otherwise} \end{cases} y^={1,0,if z≥0otherwise

这本质上是一个线性分类器------它只能解决线性可分问题(如 AND、OR),但无法解决 XOR(异或)问题。

局限性:单层感知机无法表示非线性决策边界。

2.2 多层感知机(MLP):引入隐藏层

为突破线性限制,我们在输入和输出之间加入隐藏层(Hidden Layer):

复制代码
输入层 → 隐藏层 → 输出层

每一层由多个神经元(Neuron)组成,每个神经元执行:

a j ( l ) = σ ( ∑ k w j k ( l ) a k ( l − 1 ) + b j ( l ) ) a^{(l)}j = \sigma \left( \sum{k} w^{(l)}_{jk} a^{(l-1)}_k + b^{(l)}_j \right) aj(l)=σ(k∑wjk(l)ak(l−1)+bj(l))

其中:

  • a j ( l ) a^{(l)}_j aj(l):第 l l l 层第 j j j 个神经元的激活值(activation);
  • w j k ( l ) w^{(l)}_{jk} wjk(l):从第 l − 1 l-1 l−1 层第 k k k 个神经元到第 l l l 层第 j j j 个神经元的权重
  • b j ( l ) b^{(l)}_j bj(l):第 l l l 层第 j j j 个神经元的偏置
  • σ ( ⋅ ) \sigma(\cdot) σ(⋅):激活函数(非线性函数)。

🔑 关键洞见
只要激活函数是非线性的,多层网络就能逼近任意连续函数(Universal Approximation Theorem)。

2.3 通用逼近定理(Universal Approximation Theorem)

1989 年,George Cybenko 证明:

对于任意连续函数 f : R n → R m f: \mathbb{R}^n \to \mathbb{R}^m f:Rn→Rm,在紧集上,存在一个单隐藏层的前馈神经网络,使用 Sigmoid 激活函数,可以以任意精度逼近 f f f。

这意味着:神经网络不是某种神秘的"智能",而是一种极其灵活的函数拟合器

✅ 它的强大不在于"像人脑",而在于通过组合简单非线性单元,构建复杂映射


三、数学基础:前向传播与损失函数

3.1 前向传播(Forward Propagation)

设网络有 L L L 层(含输入和输出),第 l l l 层有 n l n_l nl 个神经元。

定义:

  • A ( 0 ) = X ∈ R m × n 0 \mathbf{A}^{(0)} = \mathbf{X} \in \mathbb{R}^{m \times n_0} A(0)=X∈Rm×n0:输入矩阵( m m m 个样本, n 0 n_0 n0 个特征);
  • W ( l ) ∈ R n l − 1 × n l \mathbf{W}^{(l)} \in \mathbb{R}^{n_{l-1} \times n_l} W(l)∈Rnl−1×nl:第 l l l 层权重矩阵;
  • b ( l ) ∈ R 1 × n l \mathbf{b}^{(l)} \in \mathbb{R}^{1 \times n_l} b(l)∈R1×nl:偏置向量(广播);
  • Z ( l ) = A ( l − 1 ) W ( l ) + b ( l ) \mathbf{Z}^{(l)} = \mathbf{A}^{(l-1)} \mathbf{W}^{(l)} + \mathbf{b}^{(l)} Z(l)=A(l−1)W(l)+b(l):线性变换;
  • A ( l ) = σ ( l ) ( Z ( l ) ) \mathbf{A}^{(l)} = \sigma^{(l)}(\mathbf{Z}^{(l)}) A(l)=σ(l)(Z(l)):激活输出。

最终输出为 A ( L ) \mathbf{A}^{(L)} A(L)。

💡 向量化计算使得一次前向传播可处理整个批量(batch),极大提升效率。

3.2 激活函数:引入非线性

若没有激活函数,多层网络等价于单层线性变换(因为矩阵乘法满足结合律)。

常用激活函数:

函数 公式 特点
Sigmoid σ ( z ) = 1 1 + e − z \sigma(z) = \frac{1}{1 + e^{-z}} σ(z)=1+e−z1 输出 ( 0 , 1 ) (0,1) (0,1),适合概率;但易饱和、梯度消失
Tanh tanh ⁡ ( z ) = e z − e − z e z + e − z \tanh(z) = \frac{e^z - e^{-z}}{e^z + e^{-z}} tanh(z)=ez+e−zez−e−z 输出 ( − 1 , 1 ) (-1,1) (−1,1),零中心;仍会饱和
ReLU ReLU ( z ) = max ⁡ ( 0 , z ) \text{ReLU}(z) = \max(0, z) ReLU(z)=max(0,z) 最常用;计算快、缓解梯度消失;但有"死神经元"问题
Leaky ReLU LReLU ( z ) = max ⁡ ( 0.01 z , z ) \text{LReLU}(z) = \max(0.01z, z) LReLU(z)=max(0.01z,z) 解决死神经元
Softmax softmax ( z i ) = e z i ∑ j e z j \text{softmax}(z_i) = \frac{e^{z_i}}{\sum_j e^{z_j}} softmax(zi)=∑jezjezi 多分类输出层专用,保证概率和为1

现代实践 :隐藏层用 ReLU 或其变体,输出层根据任务选择(Sigmoid/Softmax)。

3.3 损失函数:衡量预测误差

损失函数需与任务匹配:

回归任务:均方误差(MSE)

L MSE = 1 m ∑ i = 1 m ( y ^ i − y i ) 2 \mathcal{L}{\text{MSE}} = \frac{1}{m} \sum{i=1}^{m} (\hat{y}_i - y_i)^2 LMSE=m1i=1∑m(y^i−yi)2

二分类:二元交叉熵(Binary Cross-Entropy)

L BCE = − 1 m ∑ i = 1 m [ y i log ⁡ ( y ^ i ) + ( 1 − y i ) log ⁡ ( 1 − y ^ i ) ] \mathcal{L}{\text{BCE}} = -\frac{1}{m} \sum{i=1}^{m} \left[ y_i \log(\hat{y}_i) + (1 - y_i) \log(1 - \hat{y}_i) \right] LBCE=−m1i=1∑m[yilog(y^i)+(1−yi)log(1−y^i)]

其中 y ^ i = σ ( z i ) ∈ ( 0 , 1 ) \hat{y}_i = \sigma(z_i) \in (0,1) y^i=σ(zi)∈(0,1)。

多分类:分类交叉熵(Categorical Cross-Entropy)

L CE = − 1 m ∑ i = 1 m ∑ k = 1 K y i k log ⁡ ( y ^ i k ) \mathcal{L}{\text{CE}} = -\frac{1}{m} \sum{i=1}^{m} \sum_{k=1}^{K} y_{ik} \log(\hat{y}_{ik}) LCE=−m1i=1∑mk=1∑Kyiklog(y^ik)

其中 y i \mathbf{y}_i yi 是 one-hot 标签, y ^ i = softmax ( z i ) \hat{\mathbf{y}}_i = \text{softmax}(\mathbf{z}_i) y^i=softmax(zi)。

⚠️ 注意:不要对 Softmax 输出再加 Sigmoid!Softmax 已确保输出为概率分布。


四、核心算法:反向传播(Backpropagation)

反向传播是神经网络训练的基石,它高效地计算损失函数对所有参数的梯度。

4.1 链式法则(Chain Rule)回顾

设 L = f ( g ( h ( x ) ) ) L = f(g(h(x))) L=f(g(h(x))),则:

d L d x = d L d g ⋅ d g d h ⋅ d h d x \frac{dL}{dx} = \frac{dL}{dg} \cdot \frac{dg}{dh} \cdot \frac{dh}{dx} dxdL=dgdL⋅dhdg⋅dxdh

神经网络的损失是参数的复合函数,链式法则是计算梯度的自然工具。

4.2 反向传播的推导(以两层网络为例)

考虑一个简单网络:

  • 输入: x ∈ R n \mathbf{x} \in \mathbb{R}^n x∈Rn
  • 隐藏层: h = ReLU ( W 1 x + b 1 ) \mathbf{h} = \text{ReLU}(\mathbf{W}_1 \mathbf{x} + \mathbf{b}_1) h=ReLU(W1x+b1)
  • 输出: y ^ = σ ( w 2 ⊤ h + b 2 ) \hat{y} = \sigma(\mathbf{w}_2^\top \mathbf{h} + b_2) y^=σ(w2⊤h+b2)
  • 损失: L = BCE ( y , y ^ ) \mathcal{L} = \text{BCE}(y, \hat{y}) L=BCE(y,y^)

我们希望计算 ∂ L ∂ W 1 \frac{\partial \mathcal{L}}{\partial \mathbf{W}_1} ∂W1∂L, ∂ L ∂ W 2 \frac{\partial \mathcal{L}}{\partial \mathbf{W}_2} ∂W2∂L 等。

步骤 1:计算输出层梯度

令 z 2 = w 2 ⊤ h + b 2 z_2 = \mathbf{w}_2^\top \mathbf{h} + b_2 z2=w2⊤h+b2,则:

∂ L ∂ z 2 = y ^ − y (这是 BCE + Sigmoid 的优雅性质!) \frac{\partial \mathcal{L}}{\partial z_2} = \hat{y} - y \quad \text{(这是 BCE + Sigmoid 的优雅性质!)} ∂z2∂L=y^−y(这是 BCE + Sigmoid 的优雅性质!)

于是:

∂ L ∂ w 2 = ∂ L ∂ z 2 ⋅ ∂ z 2 ∂ w 2 = ( y ^ − y ) ⋅ h \frac{\partial \mathcal{L}}{\partial \mathbf{w}_2} = \frac{\partial \mathcal{L}}{\partial z_2} \cdot \frac{\partial z_2}{\partial \mathbf{w}_2} = (\hat{y} - y) \cdot \mathbf{h} ∂w2∂L=∂z2∂L⋅∂w2∂z2=(y^−y)⋅h

∂ L ∂ b 2 = y ^ − y \frac{\partial \mathcal{L}}{\partial b_2} = \hat{y} - y ∂b2∂L=y^−y

步骤 2:传播到隐藏层

令 z 1 = W 1 x + b 1 \mathbf{z}_1 = \mathbf{W}_1 \mathbf{x} + \mathbf{b}_1 z1=W1x+b1, h = ReLU ( z 1 ) \mathbf{h} = \text{ReLU}(\mathbf{z}_1) h=ReLU(z1)。

首先计算 ∂ L ∂ h \frac{\partial \mathcal{L}}{\partial \mathbf{h}} ∂h∂L:

∂ L ∂ h = ∂ L ∂ z 2 ⋅ ∂ z 2 ∂ h = ( y ^ − y ) ⋅ w 2 ⊤ \frac{\partial \mathcal{L}}{\partial \mathbf{h}} = \frac{\partial \mathcal{L}}{\partial z_2} \cdot \frac{\partial z_2}{\partial \mathbf{h}} = (\hat{y} - y) \cdot \mathbf{w}_2^\top ∂h∂L=∂z2∂L⋅∂h∂z2=(y^−y)⋅w2⊤

然后,由于 ReLU 的导数为:

d d z ReLU ( z ) = { 1 , z > 0 0 , z ≤ 0 = I ( z > 0 ) \frac{d}{dz} \text{ReLU}(z) = \begin{cases} 1, & z > 0 \\ 0, & z \leq 0 \end{cases} = \mathbb{I}(z > 0) dzdReLU(z)={1,0,z>0z≤0=I(z>0)

所以:

∂ L ∂ z 1 = ∂ L ∂ h ⊙ I ( z 1 > 0 ) \frac{\partial \mathcal{L}}{\partial \mathbf{z}_1} = \frac{\partial \mathcal{L}}{\partial \mathbf{h}} \odot \mathbb{I}(\mathbf{z}_1 > 0) ∂z1∂L=∂h∂L⊙I(z1>0)

其中 ⊙ \odot ⊙ 表示逐元素相乘(Hadamard product)。

最后:

∂ L ∂ W 1 = ∂ L ∂ z 1 ⋅ x ⊤ \frac{\partial \mathcal{L}}{\partial \mathbf{W}_1} = \frac{\partial \mathcal{L}}{\partial \mathbf{z}_1} \cdot \mathbf{x}^\top ∂W1∂L=∂z1∂L⋅x⊤

∂ L ∂ b 1 = ∂ L ∂ z 1 \frac{\partial \mathcal{L}}{\partial \mathbf{b}_1} = \frac{\partial \mathcal{L}}{\partial \mathbf{z}_1} ∂b1∂L=∂z1∂L

反向传播的本质

从输出层开始,逐层反向计算"误差信号" (即 ∂ L ∂ z ( l ) \frac{\partial \mathcal{L}}{\partial \mathbf{z}^{(l)}} ∂z(l)∂L),再用它计算当前层参数的梯度。

4.3 一般形式:向量化反向传播

对第 l l l 层,定义误差项(error term):

δ ( l ) = ∂ L ∂ Z ( l ) \boldsymbol{\delta}^{(l)} = \frac{\partial \mathcal{L}}{\partial \mathbf{Z}^{(l)}} δ(l)=∂Z(l)∂L

则:

  • 输出层(假设用 Softmax + CE):
    δ ( L ) = A ( L ) − Y \boldsymbol{\delta}^{(L)} = \mathbf{A}^{(L)} - \mathbf{Y} δ(L)=A(L)−Y

  • 隐藏层( l < L l < L l<L):
    δ ( l ) = ( W ( l + 1 ) δ ( l + 1 ) ) ⊙ σ ′ ( l ) ( Z ( l ) ) \boldsymbol{\delta}^{(l)} = \left( \mathbf{W}^{(l+1)} \boldsymbol{\delta}^{(l+1)} \right) \odot \sigma'^{(l)}(\mathbf{Z}^{(l)}) δ(l)=(W(l+1)δ(l+1))⊙σ′(l)(Z(l))

  • 参数梯度:
    ∂ L ∂ W ( l ) = A ( l − 1 ) ⊤ δ ( l ) \frac{\partial \mathcal{L}}{\partial \mathbf{W}^{(l)}} = \mathbf{A}^{(l-1)\top} \boldsymbol{\delta}^{(l)} ∂W(l)∂L=A(l−1)⊤δ(l)
    ∂ L ∂ b ( l ) = ∑ i = 1 m δ i ( l ) (按样本求和) \frac{\partial \mathcal{L}}{\partial \mathbf{b}^{(l)}} = \sum_{i=1}^{m} \boldsymbol{\delta}^{(l)}_i \quad \text{(按样本求和)} ∂b(l)∂L=i=1∑mδi(l)(按样本求和)

🔑 反向传播的计算复杂度与前向传播同阶,这是它被广泛采用的关键原因。


五、动手实现:从零构建一个全连接神经网络

我们将用 NumPy 实现一个支持任意层数的 MLP,并用于手写数字分类(MNIST 简化版)。

python 复制代码
import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets import load_digits
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler

class MLP:
    def __init__(self, layer_sizes, learning_rate=0.01, epochs=1000):
        """
        layer_sizes: list, e.g., [64, 32, 10] for input=64, hidden=32, output=10
        """
        self.layer_sizes = layer_sizes
        self.lr = learning_rate
        self.epochs = epochs
        self.weights = []
        self.biases = []
        self.losses = []
        
        # 初始化参数(Xavier 初始化)
        for i in range(len(layer_sizes) - 1):
            w = np.random.randn(layer_sizes[i], layer_sizes[i+1]) * np.sqrt(2.0 / layer_sizes[i])
            b = np.zeros((1, layer_sizes[i+1]))
            self.weights.append(w)
            self.biases.append(b)
    
    def relu(self, z):
        return np.maximum(0, z)
    
    def relu_derivative(self, z):
        return (z > 0).astype(float)
    
    def softmax(self, z):
        exp_z = np.exp(z - np.max(z, axis=1, keepdims=True))  # 防止溢出
        return exp_z / np.sum(exp_z, axis=1, keepdims=True)
    
    def cross_entropy(self, y_true, y_pred):
        m = y_true.shape[0]
        log_likelihood = -np.log(y_pred[range(m), y_true.argmax(axis=1)] + 1e-8)
        return np.sum(log_likelihood) / m
    
    def forward(self, X):
        activations = [X]
        z_values = []
        
        for i in range(len(self.weights)):
            z = np.dot(activations[-1], self.weights[i]) + self.biases[i]
            z_values.append(z)
            
            if i == len(self.weights) - 1:  # 输出层
                a = self.softmax(z)
            else:  # 隐藏层
                a = self.relu(z)
            activations.append(a)
        
        return activations, z_values
    
    def backward(self, X, y, activations, z_values):
        m = X.shape[0]
        grads_w = [None] * len(self.weights)
        grads_b = [None] * len(self.biases)
        
        # 输出层误差
        dz = activations[-1] - y  # Softmax + CE 的梯度
        grads_w[-1] = np.dot(activations[-2].T, dz) / m
        grads_b[-1] = np.sum(dz, axis=0, keepdims=True) / m
        
        # 反向传播隐藏层
        for l in range(len(self.weights) - 2, -1, -1):
            dz = np.dot(dz, self.weights[l+1].T) * self.relu_derivative(z_values[l])
            grads_w[l] = np.dot(activations[l].T, dz) / m
            grads_b[l] = np.sum(dz, axis=0, keepdims=True) / m
        
        # 更新参数
        for i in range(len(self.weights)):
            self.weights[i] -= self.lr * grads_w[i]
            self.biases[i] -= self.lr * grads_b[i]
    
    def fit(self, X, y):
        for epoch in range(self.epochs):
            activations, z_values = self.forward(X)
            loss = self.cross_entropy(y, activations[-1])
            self.losses.append(loss)
            
            if epoch % 100 == 0:
                print(f"Epoch {epoch}, Loss: {loss:.4f}")
            
            self.backward(X, y, activations, z_values)
    
    def predict(self, X):
        activations, _ = self.forward(X)
        return np.argmax(activations[-1], axis=1)

# 加载数据:sklearn digits(1797 张 8x8 手写数字)
digits = load_digits()
X, y = digits.data, digits.target

# 标准化(对神经网络很重要!)
scaler = StandardScaler()
X = scaler.fit_transform(X)

# 转换标签为 one-hot
y_onehot = np.eye(10)[y]

# 划分数据
X_train, X_test, y_train, y_test, y_train_oh, y_test_oh = train_test_split(
    X, y, y_onehot, test_size=0.2, random_state=42
)

# 训练模型
mlp = MLP(layer_sizes=[64, 32, 16, 10], learning_rate=0.1, epochs=1000)
mlp.fit(X_train, y_train_oh)

# 评估
y_pred = mlp.predict(X_test)
accuracy = np.mean(y_pred == y_test)
print(f"\nTest Accuracy: {accuracy:.4f}")

# 可视化损失
plt.plot(mlp.losses)
plt.title("Training Loss")
plt.xlabel("Epoch")
plt.ylabel("Cross-Entropy Loss")
plt.show()

关键实现细节

  • 使用 Xavier 初始化np.sqrt(2.0 / fan_in))缓解梯度消失;
  • 标准化输入(StandardScaler)加速收敛;
  • Softmax + Cross-Entropy 的梯度简化为 pred - true
  • 向量化实现高效批量计算。

六、训练神经网络的挑战与解决方案

尽管原理清晰,但训练神经网络常面临以下挑战:

6.1 梯度消失与爆炸(Vanishing/Exploding Gradients)

在深层网络中,反向传播的梯度是多个矩阵乘积:

∂ L ∂ W ( 1 ) ∝ W ( 2 ) W ( 3 ) ⋯ W ( L ) \frac{\partial \mathcal{L}}{\partial \mathbf{W}^{(1)}} \propto \mathbf{W}^{(2)} \mathbf{W}^{(3)} \cdots \mathbf{W}^{(L)} ∂W(1)∂L∝W(2)W(3)⋯W(L)

若权重初始化不当:

  • ∣ W ∣ < 1 |\mathbf{W}| < 1 ∣W∣<1 → 梯度指数级衰减(消失);
  • ∣ W ∣ > 1 |\mathbf{W}| > 1 ∣W∣>1 → 梯度指数级增长(爆炸)。

解决方案

  • 合理初始化:Xavier(Sigmoid/Tanh)、He(ReLU);
  • 使用 ReLU:避免 Sigmoid/Tanh 的饱和区;
  • Batch Normalization:对每层输入标准化,稳定分布;
  • 残差连接(ResNet):允许梯度直接跨层流动。

6.2 过拟合(Overfitting)

神经网络参数量大,极易过拟合小数据集。

正则化技术

  • L2 正则化 (Weight Decay):
    L total = L data + λ ∑ l ∥ W ( l ) ∥ F 2 \mathcal{L}{\text{total}} = \mathcal{L}{\text{data}} + \lambda \sum_{l} \|\mathbf{W}^{(l)}\|_F^2 Ltotal=Ldata+λl∑∥W(l)∥F2
  • Dropout:训练时随机"关闭"一部分神经元(如 50%),强制网络不依赖特定路径;
  • 早停(Early Stopping):监控验证集损失,不再下降时停止;
  • 数据增强:对图像旋转、裁剪、加噪等。

6.3 优化困难:局部极小与鞍点

高维损失 landscape 存在大量鞍点(saddle points),而非局部极小。

现代优化器

  • SGD with Momentum
    v t = β v t − 1 + ( 1 − β ) ∇ L t θ t = θ t − 1 − η v t \mathbf{v}t = \beta \mathbf{v}{t-1} + (1 - \beta) \nabla \mathcal{L}t \\ \theta_t = \theta{t-1} - \eta \mathbf{v}_t vt=βvt−1+(1−β)∇Ltθt=θt−1−ηvt

    动量帮助穿越平坦区域。

  • Adam (Adaptive Moment Estimation):
    m t = β 1 m t − 1 + ( 1 − β 1 ) ∇ L t v t = β 2 v t − 1 + ( 1 − β 2 ) ( ∇ L t ) 2 m ^ t = m t 1 − β 1 t , v ^ t = v t 1 − β 2 t θ t = θ t − 1 − η m ^ t v ^ t + ϵ \mathbf{m}t = \beta_1 \mathbf{m}{t-1} + (1 - \beta_1) \nabla \mathcal{L}_t \\ \mathbf{v}t = \beta_2 \mathbf{v}{t-1} + (1 - \beta_2) (\nabla \mathcal{L}_t)^2 \\ \hat{\mathbf{m}}_t = \frac{\mathbf{m}_t}{1 - \beta_1^t}, \quad \hat{\mathbf{v}}_t = \frac{\mathbf{v}t}{1 - \beta_2^t} \\ \theta_t = \theta{t-1} - \eta \frac{\hat{\mathbf{m}}_t}{\sqrt{\hat{\mathbf{v}}_t} + \epsilon} mt=β1mt−1+(1−β1)∇Ltvt=β2vt−1+(1−β2)(∇Lt)2m^t=1−β1tmt,v^t=1−β2tvtθt=θt−1−ηv^t +ϵm^t

    Adam 自适应调整每个参数的学习率,是目前最常用的优化器。


七、使用 PyTorch 构建现代神经网络

在实践中,我们使用深度学习框架(如 PyTorch)而非手动实现。

python 复制代码
import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import DataLoader, TensorDataset

# 转换数据为 PyTorch 张量
X_train_t = torch.tensor(X_train, dtype=torch.float32)
y_train_t = torch.tensor(y_train, dtype=torch.long)
X_test_t = torch.tensor(X_test, dtype=torch.float32)
y_test_t = torch.tensor(y_test, dtype=torch.long)

train_loader = DataLoader(TensorDataset(X_train_t, y_train_t), batch_size=32, shuffle=True)

# 定义模型
class Net(nn.Module):
    def __init__(self):
        super().__init__()
        self.fc1 = nn.Linear(64, 32)
        self.fc2 = nn.Linear(32, 16)
        self.fc3 = nn.Linear(16, 10)
        self.relu = nn.ReLU()
        self.dropout = nn.Dropout(0.2)
    
    def forward(self, x):
        x = self.relu(self.fc1(x))
        x = self.dropout(x)
        x = self.relu(self.fc2(x))
        x = self.dropout(x)
        x = self.fc3(x)  # 无需 Softmax,CrossEntropyLoss 内部处理
        return x

model = Net()
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=0.001)

# 训练循环
for epoch in range(100):
    model.train()
    for batch_x, batch_y in train_loader:
        optimizer.zero_grad()
        outputs = model(batch_x)
        loss = criterion(outputs, batch_y)
        loss.backward()
        optimizer.step()
    
    if epoch % 20 == 0:
        model.eval()
        with torch.no_grad():
            test_outputs = model(X_test_t)
            pred = test_outputs.argmax(dim=1)
            acc = (pred == y_test_t).float().mean().item()
        print(f"Epoch {epoch}, Test Acc: {acc:.4f}")

PyTorch 优势

  • 自动微分(loss.backward());
  • GPU 加速(.to('cuda'));
  • 丰富的预定义层(Linear, Conv2d, LSTM 等);
  • 动态计算图,调试友好。

八、可解释性:揭开神经网络的黑箱

神经网络常被诟病为"黑箱",但近年来出现了多种解释方法。

8.1 全局可解释性

  • 特征重要性:通过扰动输入特征,观察输出变化;
  • 激活可视化:对隐藏层神经元,找出使其激活最大的输入(如 DeepDream)。

8.2 局部可解释性:LIME 与 SHAP

  • LIME(Local Interpretable Model-agnostic Explanations):

    • 在预测点附近生成扰动样本;
    • 用简单模型(如线性回归)拟合局部行为;
    • 解释该简单模型的系数。
  • SHAP(SHapley Additive exPlanations):

    • 基于博弈论,计算每个特征对预测的边际贡献;
    • 对神经网络,可用 DeepSHAPIntegrated Gradients 近似。
python 复制代码
# 使用 SHAP 解释 PyTorch 模型
import shap

explainer = shap.DeepExplainer(model, X_train_t[:100])  # 背景数据
shap_values = explainer.shap_values(X_test_t[:1])

shap.image_plot(shap_values, -X_test_t[:1].reshape(1, 8, 8))  # 对图像 reshape

8.3 注意力机制(Attention)

在 NLP 和 CV 中,注意力权重本身提供了解释性:

"模型关注了句子中的'not',因此将情感判断为负面。"


九、神经网络 vs 传统模型:何时选择谁?

维度 神经网络 传统模型(XGBoost 等)
数据类型 图像、文本、语音、图、序列 结构化表格数据
数据规模 需大量数据(>10k 样本) 小数据也能 work
特征工程 自动学习特征表示 依赖人工特征工程
可解释性 黑箱(需额外工具) 相对透明(树路径、系数)
训练资源 需 GPU、调参复杂 CPU 即可,调参简单
默认性能 在非结构化数据上碾压 在表格数据上 often 更优

经验法则

  • 结构化数据:先试 XGBoost/LightGBM;
  • 图像/文本/语音:直接上 CNN/Transformer;
  • 混合数据:用神经网络处理非结构化部分,传统模型处理表格部分,再融合。

十、进阶方向:从 MLP 到现代架构

全连接网络(MLP)只是起点,现代深度学习包含:

  • 卷积神经网络(CNN):利用局部性和平移不变性处理图像;
  • 循环神经网络(RNN/LSTM):处理序列数据;
  • Transformer:通过自注意力机制统一处理各类序列;
  • 图神经网络(GNN):处理图结构数据;
  • 自监督学习:在无标签数据上预训练(如 BERT、SimCLR)。

这些架构的核心思想仍是:堆叠可微模块 + 端到端训练


十一、结语:函数逼近的艺术

神经网络不是魔法,而是一种工程化的函数逼近技术。它的力量源于:

  • 通用逼近能力:理论上可拟合任意函数;
  • 端到端学习:从原始输入到最终输出,无需中间表示;
  • 可扩展性:增加层数/宽度即可提升容量。

但也要清醒认识到:

  • 它需要大量数据和算力;
  • 它缺乏因果推理能力;
  • 它的决策过程难以审计。

最好的实践,是在理解其原理的基础上,审慎选择工具,负责任地应用

下一篇文章,我们将进入无监督学习的世界------那里没有标签,只有数据自身的结构等待发现。

但在那之前,请记住:

模型的价值,不在于它有多深,而在于它解决了什么问题


行动建议

  1. 在 MNIST 或 Fashion-MNIST 上复现 MLP,并尝试不同激活函数、初始化方法
  2. 对比 MLP 与 XGBoost 在 digits 数据集上的性能与训练时间
  3. 使用 SHAP 或 LIME 解释一个神经网络的预测结果
  4. 尝试添加 BatchNorm、Dropout,观察对过拟合的影响
  5. 阅读《Deep Learning》(Goodfellow et al.)第 6 章"深度前馈网络"

真正的理解,始于亲手实现,成于反复实验。


相关推荐
蒸蒸yyyyzwd2 小时前
go语言学习
开发语言·学习·golang
froginwe112 小时前
R 数据框
开发语言
EstherNi2 小时前
小程序中,下拉多选的组件,有写死的三级下拉,样式需要修改
javascript·小程序·vue
曹牧2 小时前
C#:重载窗体构造函数
开发语言·c#
一颗小行星!2 小时前
我用AI“ vibe“出了一个小程序的记录和感想
人工智能·小程序
T_Fire_of_Square2 小时前
crewai 知识库针对信息安全应急演练的定位和使用
网络·人工智能
花归去2 小时前
Vue Router 的导航守卫
开发语言·前端·javascript
chatexcel2 小时前
ChatExcel实测:多模态识别自动做表 + 对话式数据分析 + 一键生成PPT
人工智能·powerpoint
Beginner x_u2 小时前
ES6 中的 class 是什么?和ES5构造函数差别是什么?
javascript·es6·class