pytorch神经网络实例
PyTorch是一个强大的Python库,用于构建深度学习模型。它提供了定义和训练神经网络并将其用于推理所需的一切。
推荐:将NSDT场景编辑器加入你的3D工具链
PyTorch是一个强大的Python库,用于构建深度学习模型。它提供了定义和训练神经网络并将其用于推理所需的一切。您无需编写太多代码即可完成所有这些操作。在这个姿势中,您将了解如何使用 PyTorch 在 Python 中创建您的第一个深度学习神经网络模型。完成这篇文章后,您将知道:
- 如何加载CSV数据集并准备将其与PyTorch一起使用
- 如何在 PyToch 中定义多层感知器模型
- 如何在验证数据集上训练和评估 PyToch 模型
用我的书《Deep Learning with PyTorch》开始你的项目。它提供了带有工作代码的自学教程。
让我们开始吧。
概述
不需要很多代码。您将慢慢复习它,以便将来知道如何创建自己的模型。您将在这篇文章中学习的步骤如下:
- 加载数据
- 定义 PyToch 模型
- 定义损失函数和优化器
- 运行训练循环
- 评估模型
- 做出预测
加载数据
第一步是定义您打算在这篇文章中使用的函数和类。您将使用 NumPy 库加载数据集,并使用 PyTorch 库加载深度学习模型。
下面列出了所需的进口:
1 2 3 4 | import numpy as np import torch import torch.nn as nn import torch.optim as optim |
现在可以加载数据集。
在这篇文章中,您将使用皮马印第安人糖尿病发病数据集。自该领域早期以来,这一直是标准的机器学习数据集。它描述了皮马印第安人的患者医疗记录数据以及他们是否在五年内患有糖尿病。
这是一个二元分类问题(糖尿病发病为1或不是0)。描述每个患者的所有输入变量都经过变换和数值。这使得它很容易直接与需要数字输入和输出值的神经网络一起使用,并且是我们在 PyTorch 中的第一个神经网络的理想选择。
您也可以在此处下载。
下载数据集并将其放在本地工作目录中,与 Python 文件的位置相同。使用文件名保存它。看看文件内部;您应该看到如下所示的数据行:pima-indians-diabetes.csv
1 2 3 4 5 6 | 6,148,72,35,0,33.6,0.627,50,1 1,85,66,29,0,26.6,0.351,31,0 8,183,64,0,0,23.3,0.672,32,1 1,89,66,23,94,28.1,0.167,21,0 0,137,40,35,168,43.1,2.288,33,1 ... |
现在,您可以使用 NumPy 函数将文件加载为数字矩阵。有八个输入变量和一个输出变量(最后一列)。您将学习一个模型,将输入变量行 (�) 映射到输出变量 (�),输出变量通常总结为()�=�(�)。变量汇总如下:loadtxt()
输入变量 (�):
- 怀孕次数
- 口服葡萄糖耐量试验中 2 小时血浆葡萄糖浓度
- 舒张压(毫米汞柱)
- 三头肌皮肤褶皱厚度(毫米)
- 2 小时血清胰岛素(μIU/ml)
- 体重指数(体重公斤/(身高米)2)
- 糖尿病谱系功能
- 年龄(岁)
输出变量 (�):
- 类标签(0 或 1)
将 CSV 文件加载到内存后,您可以将数据列拆分为输入变量和输出变量。
数据将存储在二维数组中,其中第一个维度是行,第二个维度是列,例如(行、列)。通过使用标准 NumPy 切片运算符 “” 选择列的子集,可以将数组拆分为两个数组。您可以通过切片从索引 2 到索引 0 选择前八列。然后,您可以通过索引 7 选择输出列(第 9 个变量)。:
0:8
1 2 3 4 5 6 | ... # load the dataset, split into input (X) and output (y) variables dataset = np.loadtxt('pima-indians-diabetes.csv', delimiter=',') X = dataset[:,0:8] y = dataset[:,8] |
但是这些数据应该首先转换为 PyTorch 张量。一个原因是PyTorch通常在32位浮点中运行,而默认情况下,NumPy使用64位浮点数。大多数操作中不允许混合搭配。转换为 PyTorch 张量可以避免可能导致问题的隐式转换。你也可以借此机会纠正形状以适应 PyTorch 的期望,例如,更喜欢�×1矩阵而不是�-向量。
要进行转换,请从 NumPy 数组中创建一个张量:
1 2 | X = torch.tensor(X, dtype=torch.float32) y = torch.tensor(y, dtype=torch.float32).reshape(-1, 1) |
现在可以定义神经网络模型了。
想开始使用 PyTorch 进行深度学习吗?
立即参加我的免费电子邮件速成课程(带有示例代码)。
单击以注册并获得该课程的免费PDF电子书版本。
下载您的免费迷你课程
定义模型
事实上,有两种方法可以在 PyTorch 中定义模型。目标是使它像一个接受输入并返回输出的函数。
模型可以定义为一系列层。创建列出层的模型。要做到这一点,您需要做的第一件事是确保第一层具有正确数量的输入要素。在此示例中,您可以将八个输入变量的输入维度指定为一个向量。Sequential
8
图层的其他参数或模型需要多少层并不是一个简单的问题。您可以使用启发式方法来帮助您设计模型,也可以在处理类似问题时参考其他人的设计。通常,最好的神经网络结构是通过试错实验过程找到的。通常,您需要一个足够大的网络来捕获问题的结构,但又足够小以使其快速。在此示例中,让我们使用具有三层的全连接网络结构。
全连接层或密集层使用 PyTorch 中的类定义。它只是意味着类似于矩阵乘法的操作。可以将输入数指定为第一个参数,将输出数指定为第二个参数。输出的数量有时称为神经元的数量或层中的节点数量。Linear
您还需要在图层之后使用激活功能。如果未提供,您只需将矩阵乘法的输出带到下一步,或者有时使用线性激活来调用它,因此称为层。
在此示例中,您将在前两层上使用整流线性单元激活函数(称为 ReLU),在输出层上使用 sigmoid 函数。
输出层上的 sigmoid 可确保输出介于 0 和 1 之间,这很容易映射到类 1 的概率,或者捕捉到任一类的硬分类(截止阈值为 0.5)。过去,您可能对所有层都使用了 sigmoid 和 tanh 激活函数,但事实证明,sigmoid 激活会导致深度神经网络中的梯度消失问题,并且发现 ReLU 激活在速度和准确性方面都提供了更好的性能。
您可以通过添加每个图层将其拼凑在一起,以便:
- 该模型需要具有 8 个变量的数据行(第一层的第一个参数设置为
8
) - 第一个隐藏层有 12 个神经元,然后是 ReLU 激活函数
- 第二个隐藏层有 8 个神经元,然后是另一个 ReLU 激活函数
- 输出层有一个神经元,后跟一个 sigmoid 激活函数
1 2 3 4 5 6 7 8 9 | ... model = nn.Sequential( nn.Linear(8, 12), nn.ReLU(), nn.Linear(12, 8), nn.ReLU(), nn.Linear(8, 1), nn.Sigmoid() |
您可以通过打印模型来检查模型,如下所示:
1 | print(model) |
您将看到:
1 2 3 4 5 6 7 8 | Sequential( (0): Linear(in_features=8, out_features=12, bias=True) (1): ReLU() (2): Linear(in_features=12, out_features=8, bias=True) (3): ReLU() (4): Linear(in_features=8, out_features=1, bias=True) (5): Sigmoid() ) |
您可以自由更改设计,看看是否比本文的后续部分获得更好或更差的结果。
但请注意,在 PyTorch 中,有一种更冗长的模型创建方式。上面的模型可以创建为继承自以下 Python 的 Python:class
nn.Module
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | ... class PimaClassifier(nn.Module): def __init__(self): super().__init__() self.hidden1 = nn.Linear(8, 12) self.act1 = nn.ReLU() self.hidden2 = nn.Linear(12, 8) self.act2 = nn.ReLU() self.output = nn.Linear(8, 1) self.act_output = nn.Sigmoid() def forward(self, x): x = self.act1(self.hidden1(x)) x = self.act2(self.hidden2(x)) x = self.act_output(self.output(x)) return x model = PimaClassifier() print(model) |
在这种情况下,打印的模型将是:
1 2 3 4 5 6 7 8 | PimaClassifier( (hidden1): Linear(in_features=8, out_features=12, bias=True) (act1): ReLU() (hidden2): Linear(in_features=12, out_features=8, bias=True) (act2): ReLU() (output): Linear(in_features=8, out_features=1, bias=True) (act_output): Sigmoid() ) |
在此方法中,类需要在构造函数中定义所有层,因为您需要在创建类时准备其所有组件,但尚未提供输入。请注意,您还需要调用父类的构造函数(行)来引导模型。您还需要在类中定义一个函数,以告知如果提供了输入张量,则如何生成输出张量作为回报。super().__init__()
forward()
x
从上面的输出中可以看到,模型会记住您如何调用每个层。
培训准备
定义的模型已准备好进行训练,但需要指定训练的目标。在此示例中,数据具有输入要素 $X$ 和输出标注 $y$。您希望神经网络模型产生尽可能接近 $y$ 的输出。训练网络意味着找到最佳权重集,以将输入映射到数据集中的输出。损失函数是衡量预测到 $y$ 的距离的指标。在此示例中,应使用二进制交叉熵,因为它是一个二元分类问题。
一旦你决定了损失函数,你还需要一个优化器。优化器是用于逐步调整模型权重以产生更好输出的算法。有许多优化器可供选择,在此示例中,使用了 Adam。这种流行的梯度下降版本可以自动调整自己,并在各种问题中给出良好的结果。
1 2 | loss_fn = nn.BCELoss() # binary cross entropy optimizer = optim.Adam(model.parameters(), lr=0.001) |
优化器通常具有一些配置参数。最值得注意的是, 学习率 .但是所有优化器都需要知道要优化什么。因此。你传递 ,它是你创建的模型的所有参数的生成器。lr
model.parameters()
训练模型
您已经定义了模型、损失指标和优化器。通过对某些数据执行模型,它已准备好进行训练。
训练神经网络模型通常需要纪元和批次。它们是如何将数据传递到模型的惯用语:
- 纪元:将整个训练数据集传递给模型一次
- 批处理:传递给模型的一个或多个样本,将从中执行梯度下降算法进行一次迭代
简单地说,整个数据集被分成几批,你使用训练循环将批次一个接一个地传递到模型中。一旦你用尽了所有的批次,你就完成了一个纪元。然后,您可以使用相同的数据集重新开始,并开始第二个纪元,继续优化模型。重复此过程,直到您对模型的输出感到满意。
批处理的大小受系统内存的限制。此外,所需的计算次数与批处理的大小成线性比例。多个纪元的批次总数是运行梯度下降以优化模型的次数。这是一种权衡,您希望对梯度下降进行更多迭代,以便可以生成更好的模型,但同时,您不希望训练花费太长时间才能完成。周期数和批次大小可以通过反复试验来选择。
训练模型的目标是确保它学习输入数据到输出分类的足够好的映射。它不会是完美的,错误是不可避免的。通常,在后期纪元中,您会看到误差量减少,但最终会趋于平稳。这称为模型收敛。
构建训练循环的最简单方法是使用两个嵌套的 for 循环,一个用于 epoch,一个用于批处理:
1 2 3 4 5 6 7 8 9 10 11 12 13 | n_epochs = 100 batch_size = 10 for epoch in range(n_epochs): for i in range(0, len(X), batch_size): Xbatch = X[i:i+batch_size] y_pred = model(Xbatch) ybatch = y[i:i+batch_size] loss = loss_fn(y_pred, ybatch) optimizer.zero_grad() loss.backward() optimizer.step() print(f'Finished epoch {epoch}, latest loss {loss}') |
当它运行时,它将打印以下内容:
1 2 3 4 5 6 7 | Finished epoch 0, latest loss 0.6271069645881653 Finished epoch 1, latest loss 0.6056771874427795 Finished epoch 2, latest loss 0.5916517972946167 Finished epoch 3, latest loss 0.5822567939758301 Finished epoch 4, latest loss 0.5682642459869385 Finished epoch 5, latest loss 0.5640913248062134 ... |
评估模型
您已经在整个数据集上训练了我们的神经网络,并且可以在同一数据集上评估网络的性能。这只会让你了解你对数据集的建模程度(例如,训练准确性),但不知道算法在新数据上的表现如何。这样做是为了简单起见,但理想情况下,您可以将数据分为训练数据集和测试数据集,以便训练和评估模型。
可以在训练数据集上评估模型,方法与在训练中调用模型的方式相同。这将为每个输入生成预测,但您仍然需要计算评估的分数。这个分数可以与你的损失函数相同,也可以是不同的。由于您正在执行二元分类,因此可以通过将输出(0 到 1 范围内的浮点数)转换为整数(0 或 1)并与我们知道的标签进行比较,从而使用准确性作为评估分数。
这是按如下方式完成的:
1 2 3 4 5 6 | # compute accuracy (no_grad is optional) with torch.no_grad(): y_pred = model(X) accuracy = (y_pred.round() == y).float().mean() print(f"Accuracy {accuracy}") |
该函数将浮点舍入到最接近的整数。运算符比较并返回一个布尔张量,该张量可以转换为浮点数 1.0 和 0.0。该函数将为您提供 1 的数量(即预测与标签匹配)除以样本总数的计数。上下文是可选的,但建议,因此您无需记住它是如何得出数字的,因为您不会对其进行区分。round()
==
mean()
no_grad()
y_pred
将所有内容放在一起,以下是完整的代码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | import numpy as np import torch import torch.nn as nn import torch.optim as optim # load the dataset, split into input (X) and output (y) variables dataset = np.loadtxt('pima-indians-diabetes.csv', delimiter=',') X = dataset[:,0:8] y = dataset[:,8] X = torch.tensor(X, dtype=torch.float32) y = torch.tensor(y, dtype=torch.float32).reshape(-1, 1) # define the model model = nn.Sequential( nn.Linear(8, 12), nn.ReLU(), nn.Linear(12, 8), nn.ReLU(), nn.Linear(8, 1), nn.Sigmoid() ) print(model) # train the model loss_fn = nn.BCELoss() # binary cross entropy optimizer = optim.Adam(model.parameters(), lr=0.001) n_epochs = 100 batch_size = 10 for epoch in range(n_epochs): for i in range(0, len(X), batch_size): Xbatch = X[i:i+batch_size] y_pred = model(Xbatch) ybatch = y[i:i+batch_size] loss = loss_fn(y_pred, ybatch) optimizer.zero_grad() loss.backward() optimizer.step() print(f'Finished epoch {epoch}, latest loss {loss}') # compute accuracy (no_grad is optional) with torch.no_grad(): y_pred = model(X) accuracy = (y_pred.round() == y).float().mean() print(f"Accuracy {accuracy}") |
您可以将所有代码复制到 Python 文件中,并将其作为 “” 保存在与数据文件 “” 相同的目录中。然后,可以从命令行将 Python 文件作为脚本运行。pytorch_network.py
pima-indians-diabetes.csv
运行此示例,您应该看到训练循环在每个 epoch 上进行,损失最后打印最终精度。理想情况下,您希望损失为零,精度为1.0(例如,100%)。除了最微不足道的机器学习问题之外,这是不可能的。相反,您的模型中总是会出现一些错误。目标是选择模型配置和训练配置,以实现给定数据集的最低损失和最高的准确性。
神经网络是随机算法,这意味着每次运行代码时,对相同数据相同的算法可以训练具有不同技能的不同模型。这是一个功能,而不是一个错误。模型性能的差异意味着,要获得模型性能的合理近似值,您可能需要多次拟合模型并计算准确度分数的平均值。例如,以下是重新运行示例五次的准确度分数:
1 2 3 4 5 | Accuracy: 0.7604166865348816 Accuracy: 0.7838541865348816 Accuracy: 0.7669270634651184 Accuracy: 0.7721354365348816 Accuracy: 0.7669270634651184 |
您可以看到所有准确率得分大致在 77% 左右。
做出预测
您可以改编上述示例并使用它来生成对训练数据集的预测,假装它是您以前从未见过的新数据集。进行预测就像调用模型一样简单,就好像它是一个函数一样。您在输出层上使用 sigmoid 激活函数,以便预测将是介于 0 和 1 之间的概率。通过对它们进行舍入,您可以轻松地将它们转换为此分类任务的清晰二进制预测。例如:
1 2 3 4 5 6 | ... # make probability predictions with the model predictions = model(X) # round predictions rounded = predictions.round() |
或者,您可以将概率转换为 0 或 1,以直接预测清晰的类;例如:
1 2 3 | ... # make class predictions with the model predictions = (model(X) > 0.5).int() |
下面的完整示例对数据集中的每个示例进行预测,然后打印数据集中前五个示例的输入数据、预测类和预期类。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 | import numpy as np import torch import torch.nn as nn import torch.optim as optim # load the dataset, split into input (X) and output (y) variables dataset = np.loadtxt('pima-indians-diabetes.csv', delimiter=',') X = dataset[:,0:8] y = dataset[:,8] X = torch.tensor(X, dtype=torch.float32) y = torch.tensor(y, dtype=torch.float32).reshape(-1, 1) # define the model class PimaClassifier(nn.Module): def __init__(self): super().__init__() self.hidden1 = nn.Linear(8, 12) self.act1 = nn.ReLU() self.hidden2 = nn.Linear(12, 8) self.act2 = nn.ReLU() self.output = nn.Linear(8, 1) self.act_output = nn.Sigmoid() def forward(self, x): x = self.act1(self.hidden1(x)) x = self.act2(self.hidden2(x)) x = self.act_output(self.output(x)) return x model = PimaClassifier() print(model) # train the model loss_fn = nn.BCELoss() # binary cross entropy optimizer = optim.Adam(model.parameters(), lr=0.001) n_epochs = 100 batch_size = 10 for epoch in range(n_epochs): for i in range(0, len(X), batch_size): Xbatch = X[i:i+batch_size] y_pred = model(Xbatch) ybatch = y[i:i+batch_size] loss = loss_fn(y_pred, ybatch) optimizer.zero_grad() loss.backward() optimizer.step() # compute accuracy y_pred = model(X) accuracy = (y_pred.round() == y).float().mean() print(f"Accuracy {accuracy}") # make class predictions with the model predictions = (model(X) > 0.5).int() for i in range(5): print('%s => %d (expected %d)' % (X[i].tolist(), predictions[i], y[i])) |
此代码使用不同的方法来构建模型,但在功能上应与以前相同。训练模型后,将对数据集中的所有示例进行预测,并打印前五个示例的输入行和预测类值,并将其与预期的类值进行比较。您可以看到大多数行都是正确预测的。事实上,您可以预期根据上一节中模型的估计性能正确预测大约 77% 的行。
1 2 3 4 5 | [6.0, 148.0, 72.0, 35.0, 0.0, 33.599998474121094, 0.6269999742507935, 50.0] => 1 (expected 1) [1.0, 85.0, 66.0, 29.0, 0.0, 26.600000381469727, 0.35100001096725464, 31.0] => 0 (expected 0) [8.0, 183.0, 64.0, 0.0, 0.0, 23.299999237060547, 0.671999990940094, 32.0] => 1 (expected 1) [1.0, 89.0, 66.0, 23.0, 94.0, 28.100000381469727, 0.16699999570846558, 21.0] => 0 (expected 0) [0.0, 137.0, 40.0, 35.0, 168.0, 43.099998474121094, 2.2880001068115234, 33.0] => 1 (expected 1) |
总结
在这篇文章中,你发现了如何使用PyTorch创建你的第一个神经网络模型。具体来说,您学习了使用 PyTorch 逐步创建神经网络或深度学习模型的关键步骤,包括:
- 如何加载数据
- 如何在 PyTorch 中定义神经网络
- 如何根据数据训练模型
- 如何评估模型
- 如何使用模型进行预测