单层感知机 (Single Layer Perceptron)
基于
Assignment 4-1.ipynb整理:用 Iris 数据集做二分类(Versicolor = +1 / Virginica = -1),两个特征(PetalLength、SepalWidth),手写一个单层感知机。
1. 定义
单层感知机是最简单的神经网络,也是最早的机器学习模型之一(1958,Rosenblatt)。它做的事很直白:
把输入特征加权求和 + 偏置,再看结果的符号,判成 +1 或 -1 两类。
它只能做线性二分类——即用一条直线(高维里是一个超平面)把两类点分开。
2. 结构:一个”神经元”
w1
x1 ──────┐
│
w2 ▼
x2 ─────► Σ (加权求和) ─► z ─► 激活函数 ─► y (±1)
▲
│ b (偏置)
+1
- 输入 x:特征向量,本例是
[PetalLength, SepalWidth],维度 = 2 - 权重 w:每个特征一个权重,衡量它有多重要,维度 = 2
- 偏置 b(也写作 t/threshold):一个额外的可调常数,相当于”门槛”
- 输出 y:+1 或 -1
作业里的答案:输入维度 2 → 需要 2 个权重 + 1 个偏置。
3. 关键:z 是什么?(你问的重点)
z = np.dot(w, x) + bz 是”加权求和 + 偏置”的结果,叫做净输入 / 预激活值 (pre-activation)。 它是还没经过激活函数的中间量。
拆开看(本例 2 个特征):
z = w1·x1 + w2·x2 + b
= np.dot(w, x) + b # np.dot(w,x) 就是 w1*x1 + w2*x2,即点积
np.dot(w, x)= 权重和特征的点积(对应位置相乘再相加),见python/matrix-operations.md+ b= 再加上偏置
z 的几何含义:z = w·x + b = 0 就是那条决策边界(分界线)。
z > 0→ 点落在边界一侧 → 判为 +1z < 0→ 落在另一侧 → 判为 -1z的绝对值越大,说明离边界越远、越”确信”
一句话记忆:z 是”离分界线多远、在哪一侧”的一个带符号得分,正负决定分类,大小反映置信度。
4. 激活函数:把 z 变成 ±1
def activation(z):
return 1 if z >= 0 else -1这是符号函数 (sign function):只看 z 的正负,输出 +1 或 -1。这就是感知机用的激活函数。
用了
1 if z >= 0 else -1这种三元表达式写法,见python/list-comprehension-idioms.md。
前向传播(做一次预测)三步:
z = np.dot(w, x) + b # ① 算净输入 z
y_pred = activation(z) # ② 过激活函数得 ±1
# ③ 就是预测结果5. 为什么 w、b 不能初始化成全 0?
作业里的一个考点:
w = np.zeros(2); b = 0.0
z = np.dot(w, x) + b # 不管 x 是什么,z 恒等于 0如果 w 和 b 全是 0,那 z = 0·x + 0 = 0 永远等于 0,激活后永远输出同一个固定值,模型无法区分任何样本、学不到东西。
所以要用随机初始化(本例用
np.random.randn正态分布随机数),打破对称性,让训练能起步。
6. 训练:感知机怎么学?(更新规则)
核心是遇错才改:预测对了不动,预测错了就朝正确方向调整 w 和 b。
for epoch in range(epochs): # 把整个训练集反复过 epochs 遍
for i in range(len(X_train)): # 逐个样本
x_i, y_i = X_train[i], y_train[i]
z = np.dot(w, x_i) + t
y_pred = 1 if z >= 0 else -1 # 当前预测
# 更新规则:只有预测错时 (y_i - y_pred) 才非零,才会改动
w = w + lr * (y_i - y_pred) * x_i
t = t + lr * (y_i - y_pred)理解更新规则 w = w + lr * (y_i - y_pred) * x_i:
| 情况 | y_i - y_pred | 结果 |
|---|---|---|
| 预测正确 | 0 | w、b 不变(对了就不动) |
| 真实 +1,预测 -1 | 1-(-1)=2 | 把 w 往 +x_i 方向推,下次 z 更容易 ≥0 |
| 真实 -1,预测 +1 | -1-1=-2 | 把 w 往 -x_i 方向推,下次 z 更容易 <0 |
- lr (learning rate 学习率):每次调整的步长。太小学得慢,太大会震荡。
- epoch:把训练集完整过一遍叫一个 epoch,本例训练 5 个 epoch。
7. 评估:准确率
train_pred = np.array([1 if np.dot(w, x) + t >= 0 else -1 for x in X_train])
train_acc = np.mean(train_pred == y_train) # 预测对的比例train_pred == y_train得到一个布尔数组(每个样本对/错)np.mean(...)对布尔数组求均值 = 预测正确的比例(True=1, False=0)
8. 调参:学习率网格搜索 (Grid Search)
作业里试了 3 个学习率,看哪个最好:
learning_rates = [0.003, 0.01, 0.03]
for lr in learning_rates:
w, t, train_acc_hist, test_acc_hist = train_perceptron(..., lr, epochs=5)
print(f"lr={lr}: train_acc={train_acc_hist}, test_acc={test_acc_hist}")从结果看 lr=0.01 最优(test_acc 最终到 1.0);lr 太小(0.003)学得太慢没收敛,lr 太大(0.03)后期有波动。
这种”试几个值挑最好”的做法叫网格搜索;打印用的 f-string 见
python/f-strings.md。
9. 数据预处理:零均值归一化
mean = X_train.mean(axis=0) # 每个特征的均值(按列,见 axis 说明)
X_train_norm = X_train - mean # 减均值 → 零均值化(广播)
X_test_norm = X_test - mean # 注意:测试集用训练集的均值!- 归一化让不同量纲的特征可比,帮助训练更稳更快
- 关键:测试集要用训练集算出的 mean,不能用测试集自己的(否则数据泄露)
X_train - mean是(80,2) - (2,)的广播,见python/broadcasting.md
10. 单层感知机的局限
- 只能线性可分:一条直线分不开的数据(经典反例:XOR 异或)它就无能为力
- 没有概率输出:只给 ±1,不给置信度(Logistic 回归用 sigmoid 才有概率)
- 解决办法:叠多层 + 非线性激活 → 多层感知机 (MLP) / 神经网络,能拟合复杂边界
11. 延伸阅读 / 关联概念
- 点积 / 矩阵运算 —
np.dot(w, x)的数学含义;见python/matrix-operations.md - 广播 — 归一化、向量化预测用到;见
python/broadcasting.md - 三元表达式 / 列表推导式 —
1 if z>=0 else -1的写法;见python/list-comprehension-idioms.md - f-string — 打印训练日志;见
python/f-strings.md - PyTorch — 用框架搭感知机/神经网络,自动求导替代手写更新规则;见
python/torch.md - 多层感知机 (MLP) / 激活函数(ReLU/sigmoid) — 单层感知机的进阶,突破线性局限
- Logistic 回归 — 和感知机结构像,但输出概率、用不同损失