"""
定义你的神经网络架构
初始化优化器和损失函数
循环您的训练时期数
在每个时期内循环数据批次
对当前批次数据进行预测并计算损失
将梯度归零
执行反向传播
告诉优化器更新网络的梯度
告诉 PyTorch 用 GPU 训练你的网络(当然,如果你的机器上有 GPU)
"""
首先创建的文件夹,里面是mlp.py文件,同级目录是train.py
第 2 行和第 3 行导入我们需要的 Python 包: 然后我们定义 get_training_mode l函数(第 5 行),它接受三个参数:
- 神经网络的输入节点数
- 网络隐藏层的节点数
- 输出节点的数量(即输出预测的维度)
根据提供的默认值,可以看到我们正在构建一个 4-8-3 神经网络,这意味着输入层有 4 个节点,隐藏层有 8 个节点,神经网络的输出将由 3 个值组成. 然后在第 7-11 行通过首先初始化一个nn.Sequential 对象(非常类似于 Keras/TensorFlow 的 Sequential的 class)。 在 网络 的里面 nn.Sequential 我们建立一个类 有序字典 其中字典中的每个条目都包含两个值:
- 隐藏层的可读名称的字符串(在使用 PyTorch 调试神经网络架构时非常有用)
- PyTorch 层定义本身
Linear class 是我们的全连接层定义,这意味着每个输入都连接到该层中的每个输出。Linear class接受两个必需的参数:
在第 8 行,我们定义hidden_layer_1 它由一个完全连接的层组成,接受 inFeatures 输入然后产生输出 hiddenDim . 第 9 行我们应用一个 ReLU 激活函数,然后是另一个线性作为我们的输出的层(第 10 行)。
注意第二个Linear 定义包含相同数量的输入 对前 Linear layer 做了输出——这不是偶然的! 上一层的输出维度必须与下一层的输入维度相匹配,否则 PyTorch 会出错(然后你将有相当繁琐的任务自己调试层维度)。
PyTorch在这方面并不宽容(与 Keras/TensorFlow 不同),因此在指定层尺寸时要格外小心。
然后将生成的 PyTorch 神经网络返回给调用函数。
"""
创建我们的 PyTorch 训练脚本
实施我们的神经网络架构后,我们可以继续使用 PyTorch 训练模型。
为了完成这个任务,我们需要实现一个训练脚本:
创建我们的神经网络架构的一个实例
构建我们的数据集
确定我们是否在 GPU 上训练我们的模型
定义一个训练循环(我们脚本中最难的部分)
创建train.py,:
"""
第 2-7 行导入我们需要的 Python 包,包括:
MLP :我们对多层感知器架构的定义,在 PyTorch 中实现SGD :我们将用于训练模型的随机梯度下降优化器make_blob :构建示例数据的合成数据集train_test_split :将我们的数据集拆分为训练和测试拆分nn :PyTorch 的神经网络功能torch :基础 PyTorch 库
在训练神经网络时,我们会分批进行。next_batch ,为我们的训练循环产生这样的批次: 函数接受三个参数:
- inputs:我们输入到神经网络的数据
- targets:我们的目标输出值(即我们希望神经网络准确预测的值)
- batchSize: 数据批的大小
然后我们循环我们的输入数据 batchSize (第 11 行)并将它们交给调用函数(第 13 行)。 接下来,我们需要处理一些重要的初始化:
当使用 PyTorch 训练我们的神经网络时,我们将使用 64 的批量大小,训练 10 个epoch,并使用 1e-2 的学习率(第 16-18 行)。 我们在第 21 行设置了我们的训练设备(CPU 或 GPU)。 GPU 肯定会加速训练,但在本示例中不是必需的。
接下来,我们需要一个示例数据集来训练我们的神经网络。我们将在本系列的下一个教程中学习如何从磁盘加载图像并在图像数据上训练神经网络, 但现在,让我们使用scikit-learn 的 make_blobs 函数为我们创建一个合成数据集:
第 27 和 28 行构建了我们的数据集,包括:
- 三类标签(
centers = 3) - 神经网络的四个总特征/输入(
n_features = 4) - 总共1000个数据点(
n_samples = 1000)
本质上, make_blob 函数正在生成聚集数据点的高斯 blob。对于二维数据,make_blob 函数将创建类似于以下内容的数据:
注意这里有三组数据。但是我们有四个维度而不是两个维度(这意味着我们无法轻松地进行可视化)。
生成数据后,我们应用 train_test_split 函数(第 32 和 33 行)创建我们的训练分割,85% 用于训练,15% 用于评估。 从那里,训练和测试数据从 NumPy 数组转换为 PyTorch 张量,然后转换为浮点数据类型(第 34-37 行)。
现在让我们实例化我们的 PyTorch 神经网络架构: 第 40 行初始化我们的 MLP 并将其推送到任何设备 我们用于训练(CPU 或 GPU)。
第 44 行定义了我们的 SGD 优化器,它接受两个参数:
MLP模型参数,通过简单调用mlp.parameters() 获得学习率
最后,我们初始化我们的分类交叉熵损失函数,这是您在对> 2 个类执行分类时将使用的标准损失方法。
我们现在到达我们最重要的代码块,训练循环。不像 Keras/TensorFlow,它允许你简单地调用model.fit 为了训练您的模型,PyTorch要求您手动实现您的训练循环。
让我们回顾一下我们的训练循环:
第 48 行初始化trainTemplate ,一个字符串,可以让我们方便地显示时代数,以及每一步的损失和准确性。
然后,我们对我们所期望的训练epoch的数进行设置 行51这里面:
显示epoch编号,这对调试很有用(第 53 行) 初始化我们的训练损失和准确性(第 54 和 55 行) 初始化训练循环的当前迭代中使用的数据点总数(第 56 行) 将 PyTorch 模型置于训练模式(第 57 行) 调用 train() 在反向传播期间更新模型参数需要 PyTorch 模型的方法。
在我们的下一个代码块中,您将看到我们将模型放入 eval() ,以便我们可以评估测试集上的损失和准确性。如果我们忘了然后调用 train() 在下一个训练循环的顶部,那么我们的模型参数将不会更新。
外层 为了循环(第 51 行)循环遍历我们的 epoch 数。第 60 行然后开始一个内部为了循环遍历训练集中的每个批次。 几乎您使用 PyTorch 编写的每个训练过程都包含一个外循环(在epoch数上)和一个内循环(在数据批次上)。
在内部循环(即批处理循环)中,我们继续:
移动 batchX 和 batchY 数据到我们的 CPU 或 GPU(取决于设备是否有GPU) 通过 batchX 数据通过神经网络并对其进行预测 使用我们的损失函数通过比较输出来计算我们的损失 预测 到我们的真实类标签 现在我们有了我们的 失利,我们可以更新我们的模型参数——这是 PyTorch 训练过程中最重要的一步,通常也是最初学者搞砸的一步。
要更新我们模型的参数,我们必须按照指定的确切顺序调用第 69-71 行:
opt.zero_grad() :将模型的前一个批次/步骤累积的梯度归零loss.backward() : 执行反向传播opt.step() :根据反向传播的结果更新我们神经网络中的权重
再次强调,必须将梯度归零,执行反向传递,然后按照确切顺序更新模型参数。最常见的错误是忘记将梯度归零。如果您不将梯度归零,那么将会在多个batch 和多个epoch 内累积梯度。这会打乱你的反向传播并导致错误的权重更新。
说真的,不要搞砸这些步骤。在我们更新模型的权重后,我们在第 75-77 行计算我们的训练损失、训练准确度和检查的样本数量(即批次中的数据点数量)。
然后我们应用我们的trainTemplate 显示我们的epoch、训练损失和训练准确率。请注意 代码如何将损失和准确度除以批次中的样本总数以获得平均值。
此时,我们已经在一个 epoch 中的所有数据点上训练了我们的 PyTorch 模型——现在我们需要在我们的测试集上评估它:
类似于我们如何初始化我们的训练损失、训练准确率和批次中的样本数量,我们对第 86-88 行的测试集做同样的事情。在这里,我们初始化变量来存储我们的测试损失、测试精度和测试集中的样本数量。
我们还将我们的模型放入 eval() 第89行 我们需要把我们的模型在评估模式下,当我们需要计算损失/精度的测试或验证集。
但是有什么用 eval() 模式究竟怎么办? 您可以将 eval() model 视为关闭特定层功能的开关, 例如停止应用 dropout,或允许应用批量归一化的累积状态。
其次,通常将eval() 与torch.no_grad() 上下文一起使用,这意味着在求值模式中关闭梯度计算(第92行)。
从那里开始,我们循环遍历测试集中的所有批次(第94行),类似于在前面的代码块中循环遍历训练批次。 对于每个批处理(第96行),我们使用我们的模型进行预测,然后计算损失(第99和100行)。 然后更新testLoss 、testAcc 和样本数量(第104-106行)。 最后,我们在终端上显示我们的epoch号、测试损失和测试准确度(第109-112行)。 总的来说,我们的训练循环的评估部分与训练部分非常相似,没有细微但非常重要的变化: 我们使用eval() 将模型放入求值模式 我们使用torch.no_grad() 上下文来确保没有执行分度计算 从那里,我们可以使用我们的模型进行预测,并计算测试集上的精度/损失。
我们的前几行输出显示了简单的4-8-3 MLP体系结构,这意味着神经网络有4个输入,单个隐含层有8个节点,最后的输出层有3个节点。 然后我们总共训练我们的网络10个epoch。在训练过程结束时,我们在训练集上获得了99.1%的准确率,在测试集上获得了98%的准确率。
完整代码
from collections import OrderedDict
import torch.nn as nn
def get_training_model(inFeatures=4, hiddenDim=8, nbClasses=3):
mlpModel = nn.Sequential(OrderedDict([
("hidden_layer_1", nn.Linear(inFeatures, hiddenDim)),
("activation_1", nn.ReLU()),
("output_layer", nn.Linear(hiddenDim, nbClasses))
]))
return mlpModel
from pyimagesearch import mlp
from torch.optim import SGD
from sklearn.model_selection import train_test_split
from sklearn.datasets import make_blobs
import torch.nn as nn
import torch
def next_batch(inputs, targets, batchSize):
for i in range(0, inputs.shape[0], batchSize):
yield (inputs[i:i + batchSize], targets[i:i + batchSize])
BATCH_SIZE = 64
EPOCHS = 10
LR = 1e-2
DEVICE = "cuda" if torch.cuda.is_available() else "cpu"
print("[INFO] training using {}...".format(DEVICE))
print("[INFO] preparing data...")
(X, y) = make_blobs(n_samples=1000, n_features=4, centers=3,
cluster_std=2.5, random_state=95)
(trainX, testX, trainY, testY) = train_test_split(X, y,
test_size=0.15, random_state=95)
trainX = torch.from_numpy(trainX).float()
testX = torch.from_numpy(testX).float()
trainY = torch.from_numpy(trainY).float()
testY = torch.from_numpy(testY).float()
mlp = mlp.get_training_model().to(DEVICE)
print(mlp)
opt = SGD(mlp.parameters(), lr=LR)
lossFunc = nn.CrossEntropyLoss()
trainTemplate = "epoch: {} test loss: {:.3f} test accuracy: {:.3f}"
for epoch in range(0, EPOCHS):
print("[INFO] epoch: {}...".format(epoch + 1))
trainLoss = 0
trainAcc = 0
samples = 0
mlp.train()
for (batchX, batchY) in next_batch(trainX, trainY, BATCH_SIZE):
(batchX, batchY) = (batchX.to(DEVICE), batchY.to(DEVICE))
predictions = mlp(batchX)
loss = lossFunc(predictions, batchY.long())
opt.zero_grad()
loss.backward()
opt.step()
trainLoss += loss.item() * batchY.size(0)
trainAcc += (predictions.max(1)[1] == batchY).sum().item()
samples += batchY.size(0)
trainTemplate = "epoch: {} train loss: {:.3f} train accuracy: {:.3f}"
print(trainTemplate.format(epoch + 1, (trainLoss / samples),
(trainAcc / samples)))
testLoss = 0
testAcc = 0
samples = 0
mlp.eval()
with torch.no_grad():
for (batchX, batchY) in next_batch(testX, testY, BATCH_SIZE):
(batchX, batchY) = (batchX.to(DEVICE), batchY.to(DEVICE))
predictions = mlp(batchX)
loss = lossFunc(predictions, batchY.long())
testLoss += loss.item() * batchY.size(0)
testAcc += (predictions.max(1)[1] == batchY).sum().item()
samples += batchY.size(0)
testTemplate = "epoch: {} test loss: {:.3f} test accuracy: {:.3f}"
print(testTemplate.format(epoch + 1, (testLoss / samples),
(testAcc / samples)))
print("")
|