欢迎光临
我们一直在努力

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

“人脑有大约 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=wx+b=i=1nwixi+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 z0otherwise

这本质上是一个线性分类器——它只能解决线性可分问题(如 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)=σ(kwjk(l)ak(l1)+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

    l1 层第

    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:RnRm,在紧集上,存在一个单隐藏层的前馈神经网络,使用 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)=XRm×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)Rnl1×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(l1)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+ez1

输出

(

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+ezezez

输出

(

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=1m(y^iyi)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=1m[yilog(y^i)+(1yi)log(1y^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=1mk=1Kyiklog(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=dgdLdhdgdxdh

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

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

考虑一个简单网络:

  • 输入:

    x

    R

    n

    \\mathbf{x} \\in \\mathbb{R}^n

    xRn

  • 隐藏层:

    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^=σ(w2h+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}

W1L,

L

W

2

\\frac{\\partial \\mathcal{L}}{\\partial \\mathbf{W}_2}

W2L 等。

步骤 1:计算输出层梯度

z

2

=

w

2

h

+

b

2

z_2 = \\mathbf{w}_2^\\top \\mathbf{h} + b_2

z2=w2h+b2,则:

L

z

2

=

y

^

y

(这是 BCE + Sigmoid 的优雅性质!)

\\frac{\\partial \\mathcal{L}}{\\partial z_2} = \\hat{y} – y \\quad \\text{(这是 BCE + Sigmoid 的优雅性质!)}

z2L=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}

w2L=z2Lw2z2=(y^y)h

L

b

2

=

y

^

y

\\frac{\\partial \\mathcal{L}}{\\partial b_2} = \\hat{y} – y

b2L=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}}

hL

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

hL=z2Lhz2=(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>0z0=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)

z1L=hLI(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

W1L=z1Lx

L

b

1

=

L

z

1

\\frac{\\partial \\mathcal{L}}{\\partial \\mathbf{b}_1} = \\frac{\\partial \\mathcal{L}}{\\partial \\mathbf{z}_1}

b1L=z1L

✅ 反向传播的本质: 从输出层开始,逐层反向计算“误差信号”(即

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(l1)δ(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=1mδi(l)(按样本求和)

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


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

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

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 张 8×8 手写数字)
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)LW(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+λlW(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=βvt1+(1β)Ltθt=θt1η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=β1mt1+(1β1)Ltvt=β2vt1+(1β2)(Lt)2m^t=1β1tmt,v^t=1β2tvtθt=θt1ηv^t

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


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

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

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):

    • 基于博弈论,计算每个特征对预测的边际贡献;
    • 对神经网络,可用 DeepSHAP 或 Integrated Gradients 近似。

# 使用 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)。

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


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

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

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

但也要清醒认识到:

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

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

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

但在那之前,请记住:

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


行动建议

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


    赞(0)
    未经允许不得转载:171主机测评 » 从线性到非线性——神经网络的原理、训练与可解释性探索
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址