AI: 神经网络

最后更新:2026-08-26

"深度学习"听起来高深莫测,但它的基本单元——神经元——只是一个简单的加权求和再做一次非线性变换。本章从感知机出发,带你一步步理解多层网络、激活函数、前向传播与反向传播,并用纯 Python 从零实现一个能解决 XOR 问题的 MLP。

1. 你将学到


2. 故事:10 行代码的"大脑"

(1) 痛点:深度学习到底是什么

Bob 听说"深度学习"能识别人脸、翻译语言、写文章,但他完全不理解神经网络是什么。他试过看 Wikipedia,满屏的偏导数和链式法则让他直接关掉了页面。"难道就不能用最简单的方式告诉我,一个'神经元'到底在干什么吗?"

(2) Alice 的 10 行代码

Alice 笑着打开 Python,10 行代码写了一个感知机——输入两个数,输出 0 或 1:

PYTHON
# A single perceptron: AND gate
def perceptron(x1, x2, w1=1, w2=1, b=-1.5):
    return 1 if w1 * x1 + w2 * x2 + b > 0 else 0

for x1, x2 in [(0,0),(0,1),(1,0),(1,1)]:
    print(f"AND({x1},{x2}) = {perceptron(x1, x2)}")
💻 输出:

TEXT 📖 仅展示
AND(0,0) = 0
AND(0,1) = 0
AND(1,0) = 0
AND(1,1) = 1

Bob 恍然:"这不就是一个加权投票嘛!两个输入都投 1,加权求和超过阈值就输出 1——原来神经网络就是把很多个这种简单决策单元连起来!"

(3) 收益:从简单单元到强大网络

Alice 点头:"没错。一个感知机只能做线性分类,但把几十、几百个感知机叠成多层网络,就能拟合极其复杂的函数——这就是深度学习的起点。"


3. 感知机——最简单的神经网络

(1) 感知机的数学定义

感知机(Perceptron)由心理学家 Rosenblatt 于 1958 年提出,是最早的人工神经元模型。它接收多个输入 $x_1, x_2, \ldots, x_n$,每个输入有一个权重 $w_i$,加上偏置 $b$,最后通过阶跃函数输出 0 或 1:

$$y = \text{step}\left(\sum_{i=1}^{n} w_i x_i + b\right)$$

其中 $\text{step}(z) = 1$ 若 $z > 0$,否则 $0$。

直觉:想象你在决定是否出门跑步——温度、空气质量、是否有空、心情——每个因素有一个"权重",加起来超过你的"懒惰阈值"就出门。

(2) 感知机的几何意义

感知机在二维空间画出一条直线 $w_1 x_1 + w_2 x_2 + b = 0$,直线一侧输出 1,另一侧输出 0。它能完美解决 AND、OR 等线性可分问题,但无法解决 XOR 问题——因为 XOR 的两类点无法被一条直线分开。

▶ 示例:纯 Python 实现感知机 AND 门(难度⭐)

PYTHON
# Perceptron AND gate with training
def step(z):
    return 1 if z > 0 else 0

def train_perceptron(X, y, lr=0.1, epochs=10):
    n_features = len(X[0])
    weights = [0.0] * n_features
    bias = 0.0
    for epoch in range(epochs):
        total_error = 0
        for xi, yi in zip(X, y):
            z = sum(w * x for w, x in zip(weights, xi)) + bias
            pred = step(z)
            error = yi - pred
            total_error += abs(error)
            for j in range(n_features):
                weights[j] += lr * error * xi[j]
            bias += lr * error
        if total_error == 0:
            break
    return weights, bias

X = [[0,0],[0,1],[1,0],[1,1]]
y_and = [0, 0, 0, 1]
w, b = train_perceptron(X, y_and)
print(f"Weights: {w}, Bias: {b}")
for xi in X:
    print(f"AND{xi} = {step(sum(ww*xx for ww,xx in zip(w,xi)) + b)}")
💻 输出:

TEXT 📖 仅展示
Weights: [0.2, 0.1], Bias: -0.2
AND[0, 0] = 0
AND[0, 1] = 0
AND[1, 0] = 0
AND[1, 1] = 1
💡 提示: 感知机学习规则非常简单——预测对了不动,预测错了就按误差方向调整权重。这就是最原始的"从错误中学习"。


4. 多层感知机(MLP)结构

(1) 为什么需要多层

单层感知机只能画直线,解决不了 XOR。但如果把两个感知机的输出再送进第三个感知机——就能用两条直线组合出非线性边界。这就是多层感知机(Multi-Layer Perceptron, MLP)的核心思想。

(2) MLP 的三层结构

作用 例子
输入层 接收原始特征 2 个节点(x₁, x₂)
隐藏层 提取中间特征 4 个节点(学习到的特征)
输出层 给出最终预测 1 个节点(0 或 1)

每一层的每个节点都与上一层的所有节点相连(全连接),连接的强度就是权重

(3) 神经网络前向传播流程

100%
graph LR
    X1["x₁"] --> H1["h₁ (sigmoid)"]
    X1 --> H2["h₂ (sigmoid)"]
    X2["x₂"] --> H1
    X2 --> H2
    H1 --> Y["y (output)"]
    H2 --> Y
    style X1 fill:#e1f5fe
    style X2 fill:#e1f5fe
    style H1 fill:#fff9c4
    style H2 fill:#fff9c4
    style Y fill:#c8e6c9

直觉:输入层像眼睛,隐藏层像大脑中间的处理区域,输出层像嘴巴说出答案。每一层都在做"加权求和 + 非线性变换"。

(4) 对比:感知机 vs MLP vs 深度网络

维度 感知机 MLP 深度网络
层数 1 层(无隐藏层) 2-3 层 ≥ 4 层(含多个隐藏层)
能力 线性分类 非线性分类/回归 复杂特征自动提取
训练 感知机规则 反向传播 反向传播 + 优化技巧
典型应用 简单逻辑门 小规模分类/回归 图像识别、NLP、生成
参数量 几十 几百~几千 tens of thousands~billions
代表模型 Rosenblatt 感知机 2 层 MLP CNN、Transformer

5. 激活函数——让网络拥有非线性

(1) 为什么需要激活函数

如果没有激活函数,不管有多少层,网络本质上还是一个线性变换——多层线性变换的叠加仍然是线性的。激活函数引入非线性,让网络有能力拟合复杂的曲线和边界。

类比:线性变换像"拉伸和旋转",无论拉伸旋转多少次,结果还是直线/平面。激活函数像"折叠"——把纸折一下,空间就变了。

(2) 常见激活函数对比

函数 公式 输出范围 优点 缺点
Sigmoid $\sigma(z)=\frac{1}{1+e^{-z}}$ (0, 1) 输出可当概率 梯度消失、非零中心
Tanh $\tanh(z)=\frac{e^z-e^{-z}}{e^z+e^{-z}}$ (-1, 1) 零中心 梯度消失
ReLU $\max(0, z)$ [0, +∞) 计算快、缓解梯度消失 神经元死亡(负区梯度为 0)
Leaky ReLU $\max(0.01z, z)$ (-∞, +∞) 解决神经元死亡 需调超参

▶ 示例:不同激活函数输出值对比(难度⭐)

PYTHON
# Compare activation functions
import numpy as np

def sigmoid(z):
    return 1 / (1 + np.exp(-z))

def tanh(z):
    return np.tanh(z)

def relu(z):
    return np.maximum(0, z)

def leaky_relu(z, alpha=0.01):
    return np.where(z > 0, z, alpha * z)

z_values = np.array([-5, -2, -1, 0, 1, 2, 5])
print(f"{'z':>5} | {'Sigmoid':>8} | {'Tanh':>8} | {'ReLU':>8} | {'LeakyReLU':>10}")
print("-" * 55)
for z in z_values:
    s = sigmoid(z)
    t = tanh(z)
    r = float(relu(np.array([z]))[0])
    lr = float(leaky_relu(np.array([z]))[0])
    print(f"{z:5.1f} | {s:8.4f} | {t:8.4f} | {r:8.4f} | {lr:10.4f}")
💻 输出:

TEXT 📖 仅展示
    z |  Sigmoid |     Tanh |     ReLU |  LeakyReLU
-------------------------------------------------------
 -5.0 |   0.0067 |  -0.9999 |   0.0000 |    -0.0500
 -2.0 |   0.1192 |  -0.9640 |   0.0000 |    -0.0200
 -1.0 |   0.2689 |  -0.7616 |   0.0000 |    -0.0100
  0.0 |   0.5000 |   0.0000 |   0.0000 |     0.0000
  1.0 |   0.7311 |   0.7616 |   1.0000 |     1.0000
  2.0 |   0.8808 |   0.9640 |   2.0000 |     2.0000
  5.0 |   0.9933 |   0.9999 |   5.0000 |     5.0000
💡 提示: Sigmoid 和 Tanh 在 |z| 较大时梯度趋近 0(梯度消失),ReLU 在正区间梯度恒为 1,因此深层网络通常优先选择 ReLU。


6. 前向传播与反向传播

(1) 前向传播:从输入到输出

前向传播(Forward Propagation)就是数据从输入层出发,逐层计算到输出层的过程:

  1. 计算隐藏层输入:$z^{(1)} = W^{(1)} \cdot x + b^{(1)}$
  2. 应用激活函数:$h = \sigma(z^{(1)})$
  3. 计算输出层输入:$z^{(2)} = W^{(2)} \cdot h + b^{(2)}$
  4. 输出预测:$\hat{y} = \sigma(z^{(2)})$

(2) 损失函数:衡量预测与真实的差距

常用均方误差(MSE)作为损失:

$$L = \frac{1}{2}(y - \hat{y})^2$$

损失越小,预测越准。训练的目标就是找到使损失最小的权重。

(3) 反向传播:从误差到权重更新

反向传播(Backpropagation)的核心思想:从输出层的误差出发,沿着网络反向逐层计算每个权重对误差的"贡献",然后调整权重

直觉类比:一个工厂产品出问题——

数学上就是链式法则(Chain Rule):$\frac{\partial L}{\partial w} = \frac{\partial L}{\partial \hat{y}} \cdot \frac{\partial \hat{y}}{\partial z} \cdot \frac{\partial z}{\partial w}$

(4) 反向传播关键概念表

概念 含义 直觉
梯度(Gradient) 损失对权重的偏导数 权重该往哪个方向调、调多少
学习率(Learning Rate) 每步调整的步长 太大→跳过最优,太小→训练太慢
链式法则 逐层传递梯度 从输出到输入逐层追问"谁该负责"
权重更新 $w \leftarrow w - \eta \cdot \frac{\partial L}{\partial w}$ 沿梯度反方向走一步
梯度消失 梯度逐层缩小趋近 0 前面几层几乎学不到东西
梯度爆炸 梯度逐层放大 权重更新太猛,模型不稳定

(5) 损失曲面直觉

想象你站在一座山上,目标是走到谷底(最小损失)。你每一步都沿着最陡的方向往下走——这就是梯度下降。学习率决定了你的步幅:


7. 用 sklearn 快速体验 MLP

▶ 示例:sklearn MLPClassifier 分类(难度⭐)

PYTHON
# MLPClassifier on XOR problem
from sklearn.neural_network import MLPClassifier
import numpy as np

X = np.array([[0,0],[0,1],[1,0],[1,1]])
y = np.array([0, 1, 1, 0])

mlp = MLPClassifier(hidden_layer_sizes=(4,), activation='relu',
                    max_iter=2000, random_state=42)
mlp.fit(X, y)

print("XOR Predictions:")
for xi in X:
    print(f"  {xi} -> {mlp.predict([xi])[0]}")
print(f"Accuracy: {mlp.score(X, y):.2f}")
💻 输出:

TEXT 📖 仅展示
XOR Predictions:
  [0 0] -> 0
  [0 1] -> 1
  [1 0] -> 1
  [1 1] -> 0
Accuracy: 1.00
💡 提示: sklearn 的 MLPClassifier 自动处理了前向传播、反向传播、权重初始化等所有细节。理解原理后,用框架是最高效的方式。

▶ 示例:可视化决策边界变化(难度⭐⭐)

PYTHON
# Visualize MLP decision boundary on XOR
import numpy as np
import matplotlib.pyplot as plt
from sklearn.neural_network import MLPClassifier

X = np.array([[0,0],[0,1],[1,0],[1,1]])
y = np.array([0, 1, 1, 0])

fig, axes = plt.subplots(1, 3, figsize=(15, 4))
hidden_configs = [(2,), (4,), (8,)]

for ax, config in zip(axes, hidden_configs):
    mlp = MLPClassifier(hidden_layer_sizes=config, activation='relu',
                        max_iter=3000, random_state=42)
    mlp.fit(X, y)

    xx, yy = np.meshgrid(np.linspace(-0.5, 1.5, 100),
                         np.linspace(-0.5, 1.5, 100))
    Z = mlp.predict(np.c_[xx.ravel(), yy.ravel()]).reshape(xx.shape)

    ax.contourf(xx, yy, Z, alpha=0.3, cmap='coolwarm')
    ax.scatter(X[:,0], X[:,1], c=y, cmap='coolwarm', edgecolors='k', s=100)
    ax.set_title(f"Hidden: {config}, Acc: {mlp.score(X,y):.2f}")
    ax.set_xlabel("x1")
    ax.set_ylabel("x2")

plt.tight_layout()
plt.savefig("mlp_decision_boundary.png", dpi=100)
plt.show()
💡 提示: 隐藏层节点越多,决策边界越灵活,但也越容易过拟合。2 个节点可能学不好 XOR,8 个节点则绰绰有余。

▶ 示例:隐藏层节点数对性能影响(难度⭐⭐)

PYTHON
# Effect of hidden layer size on training loss
import numpy as np
from sklearn.neural_network import MLPClassifier
import matplotlib.pyplot as plt

X = np.array([[0,0],[0,1],[1,0],[1,1]])
y = np.array([0, 1, 1, 0])

fig, ax = plt.subplots(figsize=(8, 5))
for n_hidden in [2, 4, 8, 16]:
    mlp = MLPClassifier(hidden_layer_sizes=(n_hidden,), activation='relu',
                        max_iter=3000, random_state=42, solver='lbfgs')
    mlp.fit(X, y)
    ax.plot(range(len(mlp.loss_curve_)), mlp.loss_curve_,
            label=f"Hidden={n_hidden}")

ax.set_xlabel("Iteration")
ax.set_ylabel("Loss")
ax.set_title("Training Loss vs Hidden Layer Size")
ax.legend()
ax.set_yscale('log')
plt.savefig("hidden_size_loss.png", dpi=100)
plt.show()
💡 提示: 更多的隐藏节点通常收敛更快、损失更低,但参数更多,在小数据集上可能过拟合。


8. 深度学习 = 多层神经网络

(1) 传统 ML vs 深度学习对比

维度 传统机器学习 深度学习
特征工程 需要人工设计和选择特征 自动从数据中学习特征
数据量需求 少量数据即可 通常需要大量数据
计算资源 CPU 即可 通常需要 GPU
模型可解释性 较高(决策树、线性模型) 较低(黑箱)
性能天花板 受特征质量限制 随数据和模型规模增长
代表算法 SVM、随机森林、XGBoost CNN、RNN、Transformer
适用场景 结构化数据、小样本 图像、文本、语音等非结构化数据

(2) "深"是指什么

"深度"指的是隐藏层的数量。一般约定:

层数越多,网络能提取的特征越抽象——第 1 层可能学边缘,第 2 层学纹理,第 3 层学部件,越往后越接近"语义"。

(3) 深度不一定更好

网络越深不等于效果越好。层数过多会导致:

选择模型深度要根据任务复杂度和数据量——就像盖楼,不是越高越好,要考虑地基和预算。


9. 综合实战:从零实现 MLP 分类器

▶ 示例:从零实现 MLP 解决 XOR 问题(难度⭐⭐⭐)

下面我们用纯 Python(不用任何框架)实现一个完整的 MLP,包括前向传播、反向传播和训练循环,目标是解决 XOR 问题。

PYTHON
# MLP from scratch: solve XOR with pure Python + NumPy
import numpy as np

# --- Activation functions and their derivatives ---
def sigmoid(z):
    return 1 / (1 + np.exp(-np.clip(z, -500, 500)))

def sigmoid_deriv(a):
    return a * (1 - a)

# --- MLP class ---
class MLP:
    def __init__(self, layer_sizes):
        # layer_sizes e.g. [2, 4, 1] => 2-input, 4-hidden, 1-output
        self.weights = []
        self.biases = []
        for i in range(len(layer_sizes) - 1):
            w = np.random.randn(layer_sizes[i], layer_sizes[i+1]) * 0.5
            b = np.zeros((1, layer_sizes[i+1]))
            self.weights.append(w)
            self.biases.append(b)

    def forward(self, X):
        self.activations = [X]
        for i in range(len(self.weights)):
            z = self.activations[-1] @ self.weights[i] + self.biases[i]
            a = sigmoid(z)
            self.activations.append(a)
        return self.activations[-1]

    def backward(self, y, lr=0.5):
        m = y.shape[0]
        deltas = [None] * len(self.weights)

        # Output layer delta
        output = self.activations[-1]
        deltas[-1] = (output - y) * sigmoid_deriv(output)

        # Hidden layer deltas (backprop)
        for i in range(len(self.weights) - 2, -1, -1):
            deltas[i] = (deltas[i+1] @ self.weights[i+1].T) * sigmoid_deriv(self.activations[i+1])

        # Update weights and biases
        for i in range(len(self.weights)):
            self.weights[i] -= lr * (self.activations[i].T @ deltas[i]) / m
            self.biases[i] -= lr * np.mean(deltas[i], axis=0, keepdims=True)

    def train(self, X, y, epochs=5000, lr=0.5):
        self.losses = []
        for epoch in range(epochs):
            pred = self.forward(X)
            loss = np.mean((y - pred) ** 2) / 2
            self.losses.append(loss)
            self.backward(y, lr)
            if epoch % 1000 == 0:
                print(f"Epoch {epoch:4d} | Loss: {loss:.6f}")

    def predict(self, X):
        return (self.forward(X) > 0.5).astype(int)

# --- Train on XOR ---
np.random.seed(42)
X = np.array([[0,0],[0,1],[1,0],[1,1]], dtype=float)
y = np.array([[0],[1],[1],[0]], dtype=float)

mlp = MLP([2, 4, 1])
mlp.train(X, y, epochs=5000, lr=1.0)

print("\nFinal Predictions:")
preds = mlp.predict(X)
for i in range(4):
    print(f"  XOR{X[i].astype(int).tolist()} = {preds[i,0]} (target: {y[i,0].astype(int)})")
💻 输出:

TEXT 📖 仅展示
Epoch    0 | Loss: 0.132415
Epoch 1000 | Loss: 0.004726
Epoch 2000 | Loss: 0.002517
Epoch 3000 | Loss: 0.001751
Epoch 4000 | Loss: 0.001330

Final Predictions:
  XOR[0, 0] = 0 (target: 0)
  XOR[0, 1] = 1 (target: 1)
  XOR[1, 0] = 1 (target: 1)
  XOR[1, 1] = 0 (target: 0)
💡 提示: XOR 是经典非线性问题——单层感知机无法解决,但一个 2→4→1 的 MLP 只需几千次迭代就能完美搞定。这就是多层网络 + 非线性激活的力量。


❓ 常见问题

Q 深度学习中的"深"是指什么?
A "深"指的是隐藏层的数量。传统 MLP 只有 1-2 个隐藏层,而深度网络通常有 4 层以上,甚至上百层。"深"意味着更多的特征抽象层级,每层都在上一层的基础上提取更高级的特征。
Q 为什么需要激活函数?
A 没有激活函数,多层线性变换的叠加仍然是线性的——无论你叠多少层,等价于一个单层线性变换。激活函数引入非线性,让网络有能力拟合复杂的曲线和决策边界。没有它,深度学习就不可能存在。
Q 反向传播太复杂怎么办?
A 不需要手写反向传播。现代框架(PyTorch、TensorFlow)通过自动微分(Autograd)自动计算梯度。你需要理解的核心直觉只有三点:① 从输出层误差出发逐层往回传;② 每层的"责任"由链式法则计算;③ 权重沿梯度反方向更新。实际编码时,框架帮你做所有求导。
Q 神经网络需要多少层和多少节点?
A 没有万能公式。实践中:① 从小网络开始(1-2 个隐藏层,每层 32-128 节点);② 观察训练/验证误差决定是否增加;③ 数据量小时用较少节点防止过拟合;④ 数据量大、任务复杂时才考虑更深更宽的网络。超参数调优(网格搜索、随机搜索)是常用手段。
Q 深度学习一定比传统机器学习好吗?
A 不一定。深度学习在图像、文本、语音等非结构化数据上有明显优势,但在以下场景传统 ML 更好:① 数据量小(< 1000 样本);② 特征已经很好(结构化表格数据);③ 需要可解释性;④ 计算资源有限。选择模型要看任务、数据和约束,而非一味追"深"。

📖 小节

概念 一句话总结
感知机 加权求和 + 阶跃函数,最简单的线性分类器
MLP 多层感知机,隐藏层带来非线性能力
激活函数 引入非线性,ReLU 是现代默认选择
前向传播 数据从输入层逐层计算到输出层
反向传播 误差从输出层逐层传回,更新权重
深度学习 多层神经网络,自动学习多层次特征
梯度下降 沿损失曲面最陡方向下山,寻找最优权重

核心记忆:一个神经元 = 加权求和 + 非线性变换。神经网络 = 很多神经元叠在一起。深度学习 = 很多层神经网络。从 10 行代码的感知机到 GPT,本质都是同一个范式。


📝 作业

基础(⭐)

实现 OR 门的感知机。OR 门的真值表为:(0,0)→0, (0,1)→1, (1,0)→1, (1,1)→1。修改本章感知机训练代码的标签,训练并验证结果。

PYTHON
# Starter code: OR gate perceptron
X = [[0,0],[0,1],[1,0],[1,1]]
y_or = [0, 1, 1, 1]
# TODO: train and test

进阶(⭐⭐)

用 sklearn 的 MLPClassifier 在 Iris 数据集上做分类,调参并记录不同配置的准确率:

PYTHON
# Starter code: MLP on Iris
from sklearn.datasets import load_iris
from sklearn.neural_network import MLPClassifier
from sklearn.model_selection import train_test_split

iris = load_iris()
X_train, X_test, y_train, y_test = train_test_split(
    iris.data, iris.target, test_size=0.3, random_state=42)

# TODO: try different hidden_layer_sizes, activation, learning_rate
# Record accuracy for each config

尝试至少 3 种不同的 hidden_layer_sizes(如 (10,), (50,50), (100,))和 2 种激活函数,记录哪种配置效果最好。

挑战(⭐⭐⭐)

基于本章的纯 Python MLP 实现,画出手动实现的 MLP 的损失下降曲线:

PYTHON
# After training MLP from Section 9, plot the loss curve
import matplotlib.pyplot as plt

# mlp.losses is already recorded during training
plt.figure(figsize=(8, 5))
plt.plot(range(len(mlp.losses)), mlp.losses, linewidth=1)
plt.xlabel("Epoch")
plt.ylabel("Loss (MSE)")
plt.title("MLP Training Loss Curve (XOR)")
plt.yscale('log')
plt.grid(True, alpha=0.3)
plt.savefig("mlp_loss_curve.png", dpi=100)
plt.show()

进阶扩展:① 在同一张图上对比不同学习率(0.1, 0.5, 1.0, 2.0)的损失曲线;② 尝试不同隐藏层大小(2, 4, 8)对收敛速度的影响;③ 观察学习率过大时损失是否震荡甚至发散。

Web-Tutorial.com

Web-Tutorial 技术团队

由多位开发者共同维护的编程教程平台。每篇教程由对应领域的开发者编写和审核,确保内容准确可靠。如发现任何问题,欢迎向我们反馈。

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏