相关背景知识
1. XOR问题,即线性不可分问题。
2. 多层感知机MLP:在网络中加入一个或多个隐藏层来克服线性模型的限制, 使其能处理更普遍的函数关系类型。
3. 激活函数
ReLU函数
sigmoid函数
tanh函数
多层前馈网络
多层感知机是一种多层前馈网络, 由多层神经网络构成,每层网络将输出传递给下一层网络。神经元间的权值连接仅出现在相邻层之间,不出现在其他位置。如果每一个神经元都连接到上一层的所有神经元(除输入层外),则成为全连接网络。
BP算法
多层前馈网络的反向传播 (BP)学习算法是梯度下降法在多层前馈网中的应用。
正向传播:输入信号从输入层经隐层,传向输出层,若输出层得到了期望的输出,则学习算法结束;否则,转至反向传播。
反向传播:将误差(样本输出与网络输出之差)按原联接通路反向计算,由梯度下降法调整各层节点的权值和阈值,使误差减小。
算法实现
参考书目:《动手学深度学习》第二版
多层感知机从零开始实现
书中如下所示代码在实现过程中存在问题,报错内容为不存在“train_ch3”和“predict_ch3”。
d2l.train_ch3(net, train_iter, test_iter, loss, num_epochs, updater)
d2l.predict_ch3(net, test_iter)
为解决上述问题,编写如下代码,定义train_ch3和predict_and_evaluate两个函数。
def train_ch3(net, train_iter, test_iter, loss_fn, num_epochs, updater):
# 初始化动画绘图工具
animator = d2l.Animator(xlabel='epoch', ylabel='loss', xlim=[0, num_epochs], ylim=[0.2, 0.9])
for epoch in range(num_epochs):
# 每个epoch开始前,将梯度清零
updater.zero_grad()
# 在当前epoch内遍历所有批次
for i, (X, y) in enumerate(train_iter):
# 前向传播计算预测值
y_hat = net(X)
# 将one-hot编码转换为类别索引(如果已经是类别索引则不需要此步骤)
# y = y.argmax(dim=1)
# 计算损失(对于CrossEntropyLoss,通常不需要手动转换为类别索引)
l = loss_fn(y_hat, y)
# 反向传播求梯度
l.mean().backward() # 对每个批次的损失求平均后反向传播
# 更新参数
updater.step()
# 记录训练损失
animator.add(epoch + (i + 1) / len(train_iter), (l.mean().item(), None))
# 每个epoch结束时,在测试集上评估模型
test_acc = d2l.evaluate_accuracy_gpu(net, test_iter)
animator.add(epoch + 1, (None, test_acc))
# 显示最终信息
print(f'loss {l.mean().item():.3f}, accuracy on test set: {test_acc:.2f}')
def predict_and_evaluate(net, data_iter):
correct = 0
total = 0
with torch.no_grad():
for X, y in data_iter:
# 获取预测类别
predictions = torch.argmax(net(X), dim=1)
# 计算正确预测的数量
correct += (predictions == y).sum().item()
total += len(y)
# 计算并返回准确率
return correct / total
在上述修改的基础上,编写如下代码,实现一个简单的多层感知机。
import random
import torch
from torch import nn
from d2l import torch as d2l
# 激活函数(ReLU)
def relu(X):
a = torch.zeros_like(X)
return torch.max(X, a)
# 模型
def net(X):
X = X.reshape((-1, num_inputs))
H = relu(X@W1 + b1) # 这里“@”代表矩阵乘法
return (H@W2 + b2)
if __name__ == '__main__':
batch_size = 256
train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size)
# 参数初始化
num_inputs, num_outputs, num_hiddens = 784, 10, 256
W1 = nn.Parameter(torch.randn(num_inputs, num_hiddens, requires_grad=True) * 0.01)
b1 = nn.Parameter(torch.zeros(num_hiddens, requires_grad=True))
W2 = nn.Parameter(torch.randn(num_hiddens, num_outputs, requires_grad=True) * 0.01)
b2 = nn.Parameter(torch.zeros(num_outputs, requires_grad=True))
params = [W1, b1, W2, b2]
# 使用新定义的函数评估模型在测试集上的表现
test_accuracy = predict_and_evaluate(net, test_iter)
print(f'Accuracy on the test set: {test_accuracy:.2f}')
# 损失函数 #交叉熵
loss = nn.CrossEntropyLoss(reduction='none')
# 训练
num_epochs, lr = 10, 0.1
updater = torch.optim.SGD(params, lr=lr)
train_ch3(net, train_iter, test_iter, loss, num_epochs, updater)
# 训练结束后,在测试集上评估模型
test_accuracy = predict_and_evaluate(net, test_iter)
print(f'Accuracy on the test set: {test_accuracy:.2f}')
运行上述代码,输出的主要结果为:
Accuracy on the test set: 0.07
loss 2.220, accuracy on test set: 0.29
Accuracy on the test set: 0.29