class BPNetwork(torch.nn.Module):
def __init__(self):
super(BPNetwork, self).__init__()
"""
定义第一个线性层,
输入为图片(28x28),
输出为第一个隐层的输入,大小为128。
"""
self.linear1 = torch.nn.Linear(28 * 28, 128)
# 在第一个隐层使用ReLU激活函数
self.relu1 = torch.nn.ReLU()
"""
定义第二个线性层,
输入是第一个隐层的输出,
输出为第二个隐层的输入,大小为64。
"""
self.linear2 = torch.nn.Linear(128, 64)
# 在第二个隐层使用ReLU激活函数
self.relu2 = torch.nn.ReLU()
"""
定义第三个线性层,
输入是第二个隐层的输出,
输出为输出层,大小为10
"""
self.linear3 = torch.nn.Linear(64, 10)
# 最终的输出经过softmax进行归一化
self.softmax = torch.nn.LogSoftmax(dim=1)
def forward(self, x):
"""
定义神经网络的前向传播
x: 图片数据, shape为(64, 1, 28, 28)
"""
# 首先将x的shape转为(64, 784)
x = x.view(x.shape[0], -1)
# 接下来进行前向传播
x = self.linear1(x)
x = self.relu1(x)
x = self.linear2(x)
x = self.relu2(x)
x = self.linear3(x)
x = self.softmax(x)
# 上述一串,可以直接使用 x = self.model(x) 代替。
return x
定义了一个简单的神经网络模型,该模型继承自PyTorch的torch.nn.Module
。这个模型用于处理图像数据,并最后输出一个经过softmax归一化的10维向量。下面是对代码的详细解释:
-
初始化函数
__init__
:self.linear1 = torch.nn.Linear(28 * 28, 128)
: 定义第一个线性层(全连接层)。输入是28x28的图像数据(即784个像素值),输出是一个128维的向量。self.relu1 = torch.nn.ReLU()
: 在第一个隐层后使用ReLU激活函数。self.linear2 = torch.nn.Linear(128, 64)
: 定义第二个线性层。输入是第一个隐层的128维输出,输出是一个64维的向量。self.relu2 = torch.nn.ReLU()
: 在第二个隐层后使用ReLU激活函数。self.linear3 = torch.nn.Linear(64, 10)
: 定义第三个线性层。输入是第二个隐层的64维输出,输出是一个10维的向量。self.softmax = torch.nn.LogSoftmax(dim=1)
: 在输出层使用LogSoftmax进行归一化。dim=1
表示在第二个维度(即每一行)上进行归一化。
-
前向传播函数
forward
:- 输入
x
是一个四维张量,其shape为(64, 1, 28, 28)
。这表示有64张图像,每张图像为单通道(灰度图),大小为28x28。 x = x.view(x.shape[0], -1)
: 这行代码将输入的四维张量转换为一个二维张量,其shape为(64, 784)
。这样做的目的是将每张图像展平为一个784维的向量,以便可以输入到第一个线性层。- 接下来的几行
x = self.layer(x)
是对网络进行前向传播。数据依次通过线性层、ReLU激活函数,直到最后的LogSoftmax归一化。 - 最后,函数返回经过LogSoftmax归一化的输出
x
。
- 输入
- 在
forward
函数注释中提到“上述一串,可以直接使用 x = self.model(x) 代替。”,但实际上在代码中并没有定义self.model
。这可能是一个错误或者是一个未完成的代码片段。如果你想使用这种方式来简化代码,你需要先定义一个包含所有层的Sequential
模型,并将其赋值给self.model
。
整体来说,这是一个非常基础的神经网络模型,适用于处理简单的图像分类任务。
# 第2步:加载数据集
# MNIST包含70,000张手写数字图像: 60,000张用于训练,10,000张用于测试。
# 图像是灰度的,28×28像素的,并且居中的,以减少预处理和加快运行。
transform = transforms.Compose([transforms.ToTensor(), transforms.Normalize((0.5,), (0.5,))])
# 使用torchvision读取数据
trainset = torchvision.datasets.MNIST(root='./data', train=False, download=True, transform=transform)
testset = torchvision.datasets.MNIST(root='./data', train=True, download=True, transform=transform)
# 使用DataLoader加载数据
trainloader = torch.utils.data.DataLoader(trainset, batch_size=64, shuffle=False)
testloader = torch.utils.data.DataLoader(testset, batch_size=64, shuffle=True)
首先,定义了一个数据预处理流程transform
,用于在加载MNIST数据集时对图像进行转换。transforms.Compose
是PyTorch中的一个工具,用于将多个图像转换操作组合成一个转换流程。在这个例子中,使用了两个转换操作:
transforms.ToTensor()
: 将PIL图像或NumPy的ndarray转换为torch.FloatTensor,并将其范围缩放到[0.0, 1.0]。transforms.Normalize((0.5,), (0.5,))
: 对图像进行标准化。这意味着每个通道(在这种情况下,MNIST图像是灰度图,因此只有一个通道)都将减去均值0.5并除以标准差0.5。标准化是一个常见的预处理步骤,有助于模型更好地收敛。
接下来,使用torchvision.datasets.MNIST
来加载MNIST数据集。可以通过train
参数来区分训练集和测试集:
trainset
:当train=True
时,加载的是训练集,通常用于训练模型。testset
:当train=False
时,加载的是测试集,用于在模型训练完成后评估其性能。
此外,download=True
表示如果数据集尚未下载,则自动从互联网上下载。transform=transform
指定了前面定义的图像预处理流程。
最后,使用torch.utils.data.DataLoader
来加载数据,并设置了一些参数:
trainloader
:用于训练的数据加载器。设置了shuffle=True
,这意味着在每个训练epoch开始时,数据会被打乱,这有助于防止模型过拟合并提高其泛化能力。testloader
:用于测试的数据加载器。设置了shuffle=False
,这意味着测试数据时不会打乱顺序。在评估模型时,我们通常希望以相同的顺序处理测试数据,以便获得一致的结果。
在后续的训练和测试过程中,使用这些加载器来迭代访问数据。在训练循环中,使用trainloader
来获取批次(batch)的训练数据;而在测试或评估模型时,使用testloader
来获取测试数据。
# 第3步:定义和训练模型
model = BPNetwork()
# criterion = torch.nn.MSELoss()
criterion = torch.nn.NLLLoss() # 定义loss函数
optimizer = torch.optim.SGD(model.parameters(), lr=0.0011, momentum=0.986) # 定义优化器
epochs = 100 # 一共训练15轮
for i in range(epochs):
running_loss = 0 # 本轮的损失值
for images, labels in trainloader:
# 前向传播获取预测值
output = model(images)
# 计算损失
loss = criterion(output, labels)
# 进行反向传播
loss.backward()
# 更新权重
optimizer.step()
# 清空梯度
optimizer.zero_grad()
# 累加损失
running_loss += loss.item()
# 一轮循环结束后打印本轮的损失函数
print("Epoch {} - Training loss: {}".format(i, running_loss / len(trainloader)))
它定义了一个模型、设置了损失函数和优化器,并进行了训练循环。
- 定义模型:
model = BPNetwork()
这里创建了一个名为BPNetwork
的模型实例。BPNetwork
是之前定义的神经网络类
- 定义损失函数:
# criterion = torch.nn.MSELoss()
criterion = torch.nn.NLLLoss()
损失函数用于衡量模型预测与实际标签之间的差距。在这里,选择了torch.nn.NLLLoss()
,这是负对数似然损失,通常用于分类问题,特别是当输出经过log_softmax
层处理时。
- 定义优化器:
optimizer = torch.optim.SGD(model.parameters(), lr=0.0011, momentum=0.986)
这里选择了随机梯度下降(SGD)作为优化器。它用于根据计算出的梯度来更新模型的参数。lr=0.0011
是学习率,控制参数更新的步长大小;momentum=0.986
是动量,用于加速SGD在某些方向上的收敛并抑制震荡。
- 训练循环:
epochs = 100
for i in range(epochs):
running_loss = 0
for images, labels in trainloader:
# 前向传播获取预测值
output = model(images)
# 计算损失
loss = criterion(output, labels)
# 进行反向传播
loss.backward()
# 更新权重
optimizer.step()
# 清空梯度
optimizer.zero_grad()
# 累加损失
running_loss += loss.item()
# 一轮循环结束后打印本轮的损失函数
print("Epoch {} - Training loss: {}".format(i, running_loss / len(trainloader)))
* `epochs = 100`:定义了训练的轮数,即整个数据集将被迭代100次。
* 外层循环:遍历所有的训练轮数。
* `running_loss = 0`:初始化本轮的损失值为0,用于后续累加每一批次的损失。
* 内层循环:遍历`trainloader`中的每一批次数据。
+ `output = model(images)`:前向传播,模型根据输入图像`images`计算预测值`output`。
+ `loss = criterion(output, labels)`:计算预测值`output`与实际标签`labels`之间的损失。
+ `loss.backward()`:进行反向传播,计算梯度。
+ `optimizer.step()`:根据计算出的梯度更新模型的参数。
+ `optimizer.zero_grad()`:清空梯度,为下一批次的反向传播做准备。
+ `running_loss += loss.item()`:累加损失值。
* 每一轮结束后,打印本轮的平均损失值。
整个训练循环的目的是通过不断地调整模型的参数来最小化损失函数,从而使模型能够更好地预测数据。在这个过程中,模型会逐渐学习到输入与输出之间的复杂关系。
第四段代码是用于测试训练好的神经网络模型在测试集上的表现,并可视化一些预测结果。
- 获取测试数据:
examples = enumerate(testloader)
batch_idx, (imgs, labels) = next(examples)
这里,testloader
是测试集的数据加载器,它用于加载测试数据。enumerate(testloader)
会返回测试数据批次的枚举,其中每个元素是一个元组,包含批次的索引和批次的数据(图像和标签)。next(examples)
用于获取测试集的第一个批次的数据。
- 创建图像窗口:
fig = plt.figure()
使用 matplotlib
的 figure
函数来创建一个新的图像窗口。
- 遍历并可视化测试数据:
for i in range(64):
...
这里,代码假设每个批次有64张图像,并遍历这些图像进行可视化和预测。
- 通过模型进行预测:
logps = model(imgs[i])
将单张测试图像 imgs[i]
输入到模型中,并得到模型的输出 logps
。这里的 logps
通常是对数概率值,代表模型对每个类别的预测概率。
- 将预测结果转为概率列表并获取预测标签:
probab = list(logps.detach().numpy()[0])
pred_label = probab.index(max(probab))
首先,通过 detach()
方法将 logps
从计算图中分离出来,这样就不会在后续操作中影响模型的梯度计算。接着,使用 numpy()
方法将张量转换为 NumPy 数组。由于 logps
是一个批次的输出,这里通过 [0]
选取第一张图像的输出。然后,将概率列表转换为 Python 列表,并找到概率最大的索引作为预测的标签 pred_label
。
- 处理图像数据:
img = torch.squeeze(imgs[i])
img = img.numpy()
torch.squeeze()
用于去除图像张量中维度为1的条目,确保图像数据是二维的。然后,将张量转换为 NumPy 数组以便使用 matplotlib
进行可视化。
- 可视化图像和预测结果:
plt.subplot(8, 8, i + 1)
plt.tight_layout()
plt.imshow(img, cmap='gray', interpolation='none')
plt.title("预测值: {}".format(pred_label))
plt.xticks([])
plt.yticks([])
使用 subplot
创建一个子图,并在该子图上显示图像。imshow
用于显示图像,title
设置子图的标题为预测值,xticks
和 yticks
用于去除 x 轴和 y 轴的刻度。
- 显示图像窗口:
plt.show()
使用 show
函数显示整个图像窗口,其中包含了所有子图和对应的图像及预测值。
这段代码的目的是直观地展示模型在测试集上的预测能力,通过显示图像及其对应的预测标签,可以判断模型是否正确地识别了图像中的数字。
测试集bp
# BP神经网络 实战minist数据集
import torch
import torchvision
import torchvision.transforms as transforms
import matplotlib
matplotlib.use('TkAgg')
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif'] = ['SimHei'] # 用来正常显示中文标签
# 第1步:构建BP网络模型
class BPNetwork(torch.nn.Module):
def __init__(self):
super(BPNetwork, self).__init__()
"""
定义第一个线性层,
输入为图片(28x28),
输出为第一个隐层的输入,大小为128。
"""
self.linear1 = torch.nn.Linear(28 * 28, 128)
# 在第一个隐层使用ReLU激活函数
self.relu1 = torch.nn.ReLU()
"""
定义第二个线性层,
输入是第一个隐层的输出,
输出为第二个隐层的输入,大小为64。
"""
self.linear2 = torch.nn.Linear(128, 64)
# 在第二个隐层使用ReLU激活函数
self.relu2 = torch.nn.ReLU()
"""
定义第三个线性层,
输入是第二个隐层的输出,
输出为输出层,大小为10
"""
self.linear3 = torch.nn.Linear(64, 10)
# 最终的输出经过softmax进行归一化
self.softmax = torch.nn.LogSoftmax(dim=1)
def forward(self, x):
"""
定义神经网络的前向传播
x: 图片数据, shape为(64, 1, 28, 28)
"""
# 首先将x的shape转为(64, 784)
x = x.view(x.shape[0], -1)
# 接下来进行前向传播
x = self.linear1(x)
x = self.relu1(x)
x = self.linear2(x)
x = self.relu2(x)
x = self.linear3(x)
x = self.softmax(x)
# 上述一串,可以直接使用 x = self.model(x) 代替。
return x
# 第2步:加载数据集
# MNIST包含70,000张手写数字图像: 60,000张用于训练,10,000张用于测试。
# 图像是灰度的,28×28像素的,并且居中的,以减少预处理和加快运行。
# 定义数据预处理流程
transform = transforms.Compose([transforms.ToTensor(), transforms.Normalize((0.5,), (0.5,))])
# 使用torchvision读取数据
trainset = torchvision.datasets.MNIST(root='./data', train=True, download=True, transform=transform) # 训练集
testset = torchvision.datasets.MNIST(root='./data', train=False, download=True, transform=transform) # 测试集
# 使用DataLoader加载数据
trainloader = torch.utils.data.DataLoader(trainset, batch_size=64, shuffle=True) # 训练时通常打乱数据
testloader = torch.utils.data.DataLoader(testset, batch_size=64, shuffle=False) # 测试时通常不打乱数据
# 第3步:定义和训练模型
model = BPNetwork()
# criterion = torch.nn.MSELoss()
criterion = torch.nn.NLLLoss() # 定义loss函数
optimizer = torch.optim.SGD(model.parameters(), lr=0.0011, momentum=0.986) # 定义优化器
epochs = 100 # 一共训练15轮
for i in range(epochs):
running_loss = 0 # 本轮的损失值
for images, labels in trainloader:
# 前向传播获取预测值
output = model(images)
# 计算损失
loss = criterion(output, labels)
# 进行反向传播
loss.backward()
# 更新权重
optimizer.step()
# 清空梯度
optimizer.zero_grad()
# 累加损失
running_loss += loss.item()
# 一轮循环结束后打印本轮的损失函数
print("Epoch {} - Training loss: {}".format(i, running_loss / len(trainloader)))
# 第4步:测试模型
examples = enumerate(testloader)
batch_idx, (imgs, labels) = next(examples)
fig = plt.figure()
for i in range(64):
logps = model(imgs[i]) # 通过模型进行预测
probab = list(logps.detach().numpy()[0]) # 将预测结果转为概率列表。[0]是取第一张照片的10个数字的概率列表(因为一次只预测一张照片)
pred_label = probab.index(max(probab)) # 取最大的index作为预测结果
img = torch.squeeze(imgs[i])
img = img.numpy()
plt.subplot(8, 8, i + 1)
plt.tight_layout()
plt.imshow(img, cmap='gray', interpolation='none')
plt.title("预测值: {}".format(pred_label))
plt.xticks([])
plt.yticks([])
plt.show()
``