mobile wallpaper 1mobile wallpaper 2mobile wallpaper 3mobile wallpaper 4mobile wallpaper 5mobile wallpaper 6mobile wallpaper 7mobile wallpaper 8mobile wallpaper 9mobile wallpaper 10mobile wallpaper 11mobile wallpaper 12mobile wallpaper 13
1936 字
5 分鐘
感知机 线性模型 多层感知机
2026-06-03

第 3 章 感知机、线性模型与多层感知机#

3.1 学习目标#

  1. 掌握感知机、线性回归、逻辑回归与神经元之间的关系。
  2. 理解激活函数为什么是神经网络的关键组件。
  3. 理解多层感知机(MLP)的结构、前向传播与表达能力。
  4. 能从零实现一个小型前馈神经网络解决 XOR 问题。
  5. 能解释为什么“单层线性模型”无法解决所有非线性任务。

能力矩阵

能力域入门进阶熟练
感知机知道线性分隔理解收敛条件能分析线性不可分问题
MLP会搭层理解激活与堆叠能独立设计网络宽深
公式会看前向传播会看损失函数能写出完整向量化表达
实战能跑示例能改结构能解决 XOR / MNIST 子任务

3.2 感知机模型#

3.2.1 感知机定义#

感知机是最早的可学习分类模型之一。它的核心是:

y=sign(wTx+b)y = \text{sign}(w^Tx + b)

其中:

  • wTx+b0w^Tx+b \ge 0,则预测为正类;
  • 否则预测为负类。

3.2.2 几何解释#

感知机学习的是一个超平面:

wTx+b=0w^Tx+b = 0

这个超平面把输入空间分成两侧,分别对应不同类别。

3.2.3 感知机学习规则#

对于误分类样本 (xi,yi)(x_i, y_i),其中 yi{1,+1}y_i \in \{-1, +1\},更新规则通常为:

ww+ηyixiw \leftarrow w + \eta y_i x_ibb+ηyib \leftarrow b + \eta y_i

其中 η\eta 是学习率。

3.2.4 收敛性#

若训练数据线性可分,感知机算法在有限步内收敛;若数据线性不可分,则会不断震荡,无法找到完美超平面。


3.3 线性回归与逻辑回归#

3.3.1 线性回归#

线性回归输出连续值:

y^=wTx+b\hat{y} = w^Tx + b

损失函数常用均方误差:

L=1ni=1n(y^iyi)2L = \frac{1}{n}\sum_{i=1}^{n}(\hat{y}_i - y_i)^2

3.3.2 逻辑回归#

逻辑回归在输出端加上 Sigmoid:

y^=σ(wTx+b)\hat{y} = \sigma(w^Tx+b)σ(z)=11+ez\sigma(z) = \frac{1}{1+e^{-z}}

它输出的是类别为正类的概率近似。

3.3.3 二分类交叉熵#

若标签 y{0,1}y\in\{0,1\},则损失为:

L=[ylogy^+(1y)log(1y^)]L = -\left[y\log\hat{y} + (1-y)\log(1-\hat{y})\right]

这比均方误差更适合分类。


3.4 激活函数#

3.4.1 为什么需要激活函数#

如果网络中没有非线性激活,无论堆叠多少层,整体仍然是线性映射,表达能力不会显著提升。

3.4.2 常见激活函数#

激活函数公式特点常见问题
Sigmoid11+ex\frac{1}{1+e^{-x}}输出在 0 到 1易饱和,梯度消失
Tanhexexex+ex\frac{e^x-e^{-x}}{e^x+e^{-x}}零中心仍可能饱和
ReLUmax(0,x)\max(0,x)简单高效死亡 ReLU
Leaky ReLUmax(αx,x)\max(\alpha x, x)缓解死亡需设定 α\alpha
GELU平滑门控现代网络常用计算更复杂

3.4.3 激活函数的选择#

  • 二分类输出层通常用 Sigmoid。
  • 多分类输出层通常用 Softmax。
  • 隐藏层通常优先用 ReLU、Leaky ReLU 或 GELU。

3.5 多层感知机#

3.5.1 定义#

多层感知机由多个线性层和非线性激活层堆叠而成:

A(1)=ϕ(W(1)X+b(1))A^{(1)} = \phi(W^{(1)}X + b^{(1)})A(2)=ϕ(W(2)A(1)+b(2))A^{(2)} = \phi(W^{(2)}A^{(1)} + b^{(2)})Y^=g(W(L)A(L1)+b(L))\hat{Y} = g(W^{(L)}A^{(L-1)} + b^{(L)})

3.5.2 结构示意#

输入层 -> [线性层] -> [激活层] -> [线性层] -> [激活层] -> 输出层

3.5.3 表达能力#

MLP 可以逼近很多连续函数。直观上:

  • 宽度决定单层可表达的模式数量;
  • 深度决定组合层级与抽象层次;
  • 激活函数决定非线性程度。

这也是深度学习能够表示复杂决策边界的原因。


3.6 XOR 问题#

3.6.1 为什么 XOR 不能被单层感知机解决#

XOR 数据点无法用一条直线分开:

(0,0) -> 0
(0,1) -> 1
(1,0) -> 1
(1,1) -> 0

单层线性模型无法实现这种非线性边界。

3.6.2 用 MLP 解决 XOR#

一个简单的两层网络就可以解决 XOR:

  • 输入层 2 维;
  • 隐藏层 2 或 4 个神经元;
  • 输出层 1 个神经元;
  • 隐藏层使用非线性激活;
  • 输出层使用 Sigmoid。

3.6.3 直觉#

隐藏层可以先把输入映射到一个更高维的特征空间,再让输出层在新空间中做线性分隔。


3.7 从零实现一个前馈神经网络#

3.7.1 前向传播伪代码#

import numpy as np
def sigmoid(x):
return 1 / (1 + np.exp(-x))
X = np.array([[0, 0], [0, 1], [1, 0], [1, 1]], dtype=np.float32)
y = np.array([[0], [1], [1], [0]], dtype=np.float32)
W1 = np.random.randn(2, 4) * 0.1
b1 = np.zeros((1, 4))
W2 = np.random.randn(4, 1) * 0.1
b2 = np.zeros((1, 1))
Z1 = X @ W1 + b1
A1 = np.maximum(0, Z1)
Z2 = A1 @ W2 + b2
Y_hat = sigmoid(Z2)

3.7.2 训练要点#

  • 需要定义损失函数;
  • 需要计算梯度;
  • 需要更新参数;
  • 需要观察 loss 是否下降。

3.7.3 为什么手写实现有价值#

手写版本可以帮助你理解:

  • 每一层的输入输出形状;
  • 参数如何参与计算;
  • 梯度如何在链路上传递;
  • 框架到底替你做了什么。

3.8 形状推导#

假设:

  • 输入 batch 大小为 NN
  • 输入维度为 dd
  • 隐藏层宽度为 hh
  • 输出维度为 kk

则:

XRN×dX \in \mathbb{R}^{N\times d}W(1)Rd×h,b(1)R1×hW^{(1)} \in \mathbb{R}^{d\times h}, \quad b^{(1)} \in \mathbb{R}^{1\times h}W(2)Rh×k,b(2)R1×kW^{(2)} \in \mathbb{R}^{h\times k}, \quad b^{(2)} \in \mathbb{R}^{1\times k}

这类维度推导是后面写网络代码时最容易出错的地方。


3.9 常见误区#

  1. 以为神经网络的关键只是“参数很多”。实际上结构和非线性更重要。
  2. 以为感知机和逻辑回归是完全不同的东西。实际上它们都属于线性模型家族。
  3. 以为激活函数可有可无。实际上没有激活函数,深层网络仍然是线性的。
  4. 以为隐藏层越多越好。实际上深度增加也会带来优化难度。
  5. 以为 XOR 只是一个玩具题。实际上它揭示了线性模型的根本局限。

3.10 本章小结#

本章完成了从单个神经元到 MLP 的过渡:

  • 感知机解决线性可分问题;
  • 逻辑回归把分类转成概率建模;
  • 激活函数引入非线性;
  • MLP 通过层的堆叠获得更强表达能力;
  • XOR 是理解神经网络非线性的经典入口。

3.11 课后练习#

  1. 推导感知机的更新规则为什么会推动误分类样本被修正。
  2. 解释为什么没有激活函数的多层网络仍然是线性的。
  3. 用文字说明 Sigmoid 和 ReLU 的差异。
  4. 画出 XOR 的数据分布,并说明为什么一条直线无法分开它们。
  5. 用 Numpy 设计一个 2-4-1 的小网络尝试解决 XOR。

3.12 扩展阅读#

  • 感知机收敛定理
  • Universal Approximation Theorem
  • 激活函数梯度分析
  • 逻辑回归与最大似然

3.13 从理论到代码:两层网络完整实现(含训练循环)#

下面给出一个更完整的二层网络 NumPy 实现,包含前向、反向、SGD 更新与训练日志,适合用来实验不同激活与超参。

import numpy as np
def relu(x):
return np.maximum(0, x)
def relu_grad(x):
return (x > 0).astype(float)
def softmax(logits):
shifted = logits - np.max(logits, axis=1, keepdims=True)
exp = np.exp(shifted)
return exp / np.sum(exp, axis=1, keepdims=True)
def cross_entropy_loss(probs, y):
N = probs.shape[0]
eps = 1e-12
correct_logprobs = -np.log(probs[np.arange(N), y] + eps)
return np.sum(correct_logprobs) / N
def to_onehot(y, C):
N = y.shape[0]
one = np.zeros((N, C))
one[np.arange(N), y] = 1
return one
def train_two_layer(X, y, hidden=50, epochs=1000, lr=1e-2, print_every=100):
N, D = X.shape
C = np.max(y) + 1
W1 = 0.01 * np.random.randn(D, hidden)
b1 = np.zeros((1, hidden))
W2 = 0.01 * np.random.randn(hidden, C)
b2 = np.zeros((1, C))
for epoch in range(epochs):
# forward
Z1 = X.dot(W1) + b1
A1 = relu(Z1)
logits = A1.dot(W2) + b2
probs = softmax(logits)
loss = cross_entropy_loss(probs, y)
# backward
N = X.shape[0]
dscores = probs
dscores[np.arange(N), y] -= 1
dscores /= N
dW2 = A1.T.dot(dscores)
db2 = np.sum(dscores, axis=0, keepdims=True)
dA1 = dscores.dot(W2.T)
dZ1 = dA1 * relu_grad(Z1)
dW1 = X.T.dot(dZ1)
db1 = np.sum(dZ1, axis=0, keepdims=True)
# SGD update
W1 -= lr * dW1
b1 -= lr * db1
W2 -= lr * dW2
b2 -= lr * db2
if epoch % print_every == 0:
preds = np.argmax(probs, axis=1)
acc = np.mean(preds == y)
print(f"Epoch {epoch}: loss={loss:.4f}, acc={acc:.4f}")
return W1, b1, W2, b2

练习:把训练过程改为 mini-batch SGD,加入验证集监控并实现早停。


3.14 XOR 详细实现与调试技巧#

提供一个从零实现 XOR 的完整示例,并列出可能遇到的调试点。

要点:

  • 使用小的隐藏单元数(例如 2 或 4)足够解决问题;
  • 初始化不要太大;
  • 学习率不要太大(从 0.1 开始尝试,向下调整);
  • 检查损失曲线是否单调下降。

完整示例:

import numpy as np
X = np.array([[0,0],[0,1],[1,0],[1,1]], dtype=float)
y = np.array([0,1,1,0])
def train_xor(lr=0.1, epochs=10000):
D = 2
H = 4
W1 = np.random.randn(D, H) * 0.1
b1 = np.zeros((1, H))
W2 = np.random.randn(H, 1) * 0.1
b2 = np.zeros((1, 1))
for e in range(epochs):
Z1 = X.dot(W1) + b1
A1 = np.tanh(Z1)
Z2 = A1.dot(W2) + b2
preds = 1/(1+np.exp(-Z2))
loss = np.mean((preds.squeeze() - y)**2)
dZ2 = 2*(preds.squeeze() - y).reshape(-1,1) * preds*(1-preds)
dW2 = A1.T.dot(dZ2)
db2 = np.sum(dZ2, axis=0, keepdims=True)
dA1 = dZ2.dot(W2.T)
dZ1 = dA1 * (1 - np.tanh(Z1)**2)
dW1 = X.T.dot(dZ1)
db1 = np.sum(dZ1, axis=0, keepdims=True)
W1 -= lr * dW1
b1 -= lr * db1
W2 -= lr * dW2
b2 -= lr * db2
if e % 1000 == 0:
pred_labels = (preds.squeeze() > 0.5).astype(int)
acc = np.mean(pred_labels == y)
print(e, loss, acc)
return W1, b1, W2, b2
train_xor()

调试建议:

  • 若无法收敛,先把 epochs 增大并把学习率调小;
  • 用数值梯度检验关键矩阵的梯度;
  • 检查是否忘记均值化或归一化输入(本问题不必要,但在真实任务常见)。

3.15 进阶话题(简要指引)#

  1. 激活函数的饱和性如何影响深层网络的训练?
  2. 为什么批量归一化能加速训练?数学与工程角度解释。
  3. 深度与宽度如何折衷?Universal Approximation Theorem 提供的是存在性结论,但在有限样本与有限计算下要平衡。

建议阅读:“Deep Learning”(Goodfellow 等)相关章节。

分享

如果這篇文章對你有幫助,歡迎分享給更多人!

感知机 线性模型 多层感知机
https://lemusakuya.com/posts/study-notes/neural-networks/03_感知机_线性模型_多层感知机/
作者
レム・咲く夜
發布於
2026-06-03
許可協議
CC BY-NC-SA 4.0

部分資訊可能已經過時

目錄