Python 现代开发学习笔记

(十九)多层网络与非线性

如果每一层都只做线性变换,连在一起仍可以合并成一次线性变换。写成两个步骤:h=xW1+b1h=xW_1+b_1,z=hW2+b2z=hW_2+b_2,展开后为 z=x(W1W2)+(b1W2+b2)z=x(W_1W_2)+(b_1W_2+b_2)。增加这样的层数没有改变可表达的关系类型。

以异或为例,(0,0)、(1,1) 属于类别零,另外两个角属于类别一,不能用同一条直线把这两组角分开。可以先把输入转换成几个中间特征,每个特征再经过非线性函数,然后组合它们。

先手工构造两项:h1=max⁡(0,a+b)h_1=\max(0,a+b)、h2=max⁡(0,a+b−1)h_2=\max(0,a+b-1),再输出 h1−2h2h_1-2h_2。输入和为零、一、二时,中间项分别是 (0,0)、(1,0)、(2,1),最后得到 0、1、0,正好区分四个角。这里的非线性就是把负数截为零。

接着让网络通过训练寻找一组可用中间特征:

# 项目文件:demos/xor_network.py
import torch
from torch import nn

torch.set_num_threads(1)
torch.manual_seed(7)
x = torch.tensor([[0.0, 0.0], [0.0, 1.0], [1.0, 0.0], [1.0, 1.0]])
y = torch.tensor([0, 1, 1, 0])
model = nn.Sequential(nn.Linear(2, 4), nn.Tanh(), nn.Linear(4, 2))
optimizer = torch.optim.Adam(model.parameters(), lr=0.05)
for _ in range(200):
    # 中间层的非线性让四个角不必由同一条直线分开。
    optimizer.zero_grad(set_to_none=True)
    loss = nn.CrossEntropyLoss()(model(x), y)
    loss.backward()
    optimizer.step()
model.eval()
with torch.inference_mode():
    print("四个输入的类别", model(x).argmax(1).tolist())

nn.Linear(2,4) 产生四个中间特征,Tanh 将各项按双曲正切函数压到 -1 与 1 之间,再用 Linear(4,2) 输出两个类别分数。nn.Sequential 按登记次序调用各层,训练后四个输入预测为 [0,1,1,0]。

常用的 ReLU 是 max⁡(0,x)\max(0,x),负数变零,正数保留;Tanh 在两端逐渐变平。它们使中间变换不再能够简单合并成一层线性式。这个四点实验展示网络表达异或的过程,完整分类项目随后使用单独的验证和测试图。

异或四点的两组类别交错分布,一条直线无法分开,输入经四个中间非线性特征后再组合为两类输出

笔记正文与全部题解 · Ctrl / ⌘ K