Machine-Learning-Mastery-PyTorch-教程-五-

64 阅读43分钟

Machine Learning Mastery PyTorch 教程(五)

原文:Machine Learning Mastery

协议:CC BY-NC-SA 4.0

使用 PyTorch 进行多元线性回归预测

原文:machinelearningmastery.com/making-predictions-with-multilinear-regression-in-pytorch/

多元线性回归模型是一种监督学习算法,可用于在给定多个输入变量 x 的情况下预测目标变量 y。它是一个线性回归问题,其中使用了多个输入变量 x 或特征来预测目标变量 y。该算法的一个典型使用案例是根据房屋的大小、房间数量和年龄预测房价。

在之前的教程中,我们关注了简单线性回归,其中我们使用了单个变量 x 来预测目标变量 y。从现在开始,我们将使用多个输入变量进行预测。虽然本教程仅关注从多个输入变量 x 中进行单个输出预测 y,但在后续教程中,我们将介绍多个输入-多个输出回归问题。通常,实际场景中会采用相同的实践来构建更复杂的神经网络架构。

本教程将展示如何实现一个多元

PyTorch 中的线性回归模型。特别地,您将学习:

  • 如何在多维中回顾线性回归。

  • 如何使用 PyTorch 进行多元线性回归模型预测。

  • 如何在 PyTorch 中使用 Linear 类进行多元线性回归。

  • 如何使用 PyTorch 中的 nn.Module 构建自定义模块。

启动您的项目,请参阅我的书 Deep Learning with PyTorch。它提供了 自学教程可运行的代码

让我们开始吧!

使用 PyTorch 的优化器。

图片由 Mark Boss 提供。保留所有权利。

概述

本教程分为三个部分;它们是

  • 准备预测数据

  • 使用 Linear 类进行多元线性回归

  • 可视化结果

准备预测数据

与简单线性回归模型的情况类似,我们来初始化模型的权重和偏差。请注意,由于我们将处理多个输入变量,因此我们使用了多维张量来表示权重和偏差。

import torch
torch.manual_seed(42)

# Setting weights and bias
w = torch.tensor([[3.0], 
                  [4.0]], requires_grad=True)
b = torch.tensor([[1.0]], requires_grad=True)

接下来,我们将定义用于预测的前向函数。之前我们使用了标量乘法,但在这里我们使用 PyTorch 的 mm 函数进行 矩阵乘法。这个函数实现了一个具有多个输入变量的线性方程。请注意,多维张量是矩阵,并且需要遵循一些规则,例如矩阵乘法。我们将在后面进一步讨论这些规则。

# Defining our forward function for prediction
def forward(x):
    # using mm module for matrix multiplication 
    y_pred = torch.mm(x, w) + b
    return y_pred

既然我们已经初始化了权重和偏差,并构建了用于预测的前向函数,我们来定义一个用于输入变量的张量 x

# define a tensor 'x'
x = torch.tensor([[2.0, 4.0]])
# predict the value with forward function
y_pred = forward(x)
# show the result
print("Printing Prediction: ", y_pred)

这将打印

Printing Prediction:  tensor([[23.]], grad_fn=<AddBackward0>)

注意,在矩阵乘法torch.mm(x, w)中,矩阵x中的列数必须等于w中的行数。在这种情况下,我们有一个1×21\times 2的张量x和一个2×12\times 1的张量w,矩阵乘法后得到一个1×11\times 1的张量。

类似地,我们可以对多个样本应用线性方程。例如,让我们创建一个张量X,其中每一行代表一个样本。

# define a tensor 'X' with multiple rows
X = torch.tensor([[1.0, 2.0],
                  [3.0, 4.0], 
                  [5.0, 6.0]])

对于预测,我们将使用上面相同的函数。

# Making predictions for Multi-Dimensional tensor "X"
y_pred = forward(X)
print("Predictions for 'X': ", y_pred)

其输出为

Predictions for 'X':  tensor([[12.],
        [26.],
        [40.]], grad_fn=<AddBackward0>)

如你所见,我们已经得到了多个输入变量的结果。

使用Linear类进行多线性回归

与其从头编写函数,我们可以使用 PyTorch 的内置Linear类进行预测。这在构建复杂而强大的模型架构时更为有用。

让我们创建一个Linear模型,并对上面定义的相同张量X进行预测。这里我们将定义两个参数:

  • in_features:表示输入变量X的数量以及模型权重的数量,在这种情况下为 2。

  • out_features:表示输出/预测值的数量,在这种情况下为 1。

# using Pytorch's own built-in fuction to define the LR model
lr_model = torch.nn.Linear(in_features=2, out_features=1)

现在,让我们使用随机初始化的权重和偏差,通过lr_model对象对X进行预测。

# Making predictions for X
y_pred = lr_model(X)
print("Predictions for 'X': ", y_pred)

在这种情况下,输出如下:

Predictions for 'X':  tensor([[-0.5754],
        [-1.2430],
        [-1.9106]], grad_fn=<AddmmBackward0>)

注意输出的形状,而不是值。这与我们使用矩阵乘法的先前情况相同。

想要开始使用 PyTorch 进行深度学习吗?

立即获取我的免费电子邮件速成课程(包括示例代码)。

点击注册并获得课程的免费 PDF 电子书版本。

使用nn.Module创建自定义模块

另外,我们还可以为线性模型创建自定义模块。虽然目前看来这可能有些多余,但当我们构建最先进的神经网络时,这可能是必要的。

注意,自定义模块是对象和类。在这种情况下,我们将定义一个线性回归类LR,并将其设置为nn.Module包的子类。因此,nn.Module包中的所有方法和属性都将被继承。

我们将在构造函数的参数中定义输入和输出的大小,即input_featuresoutput_features。此外,我们将在对象构造函数中调用super(),这使我们能够使用父类nn.Module中的方法和属性。现在我们可以使用torch.nn.Linear对象,并在其中定义参数input_featuresoutput_features

最后,为了进行预测,我们将定义forward函数。

...
# creating custom modules with package 'nn.Module'
class LR(torch.nn.Module):
    # Object Constructor
    def __init__(self, input_features, output_features):
        super().__init__()
        self.linear = torch.nn.Linear(input_features, output_features)
    # define the forward function for prediction
    def forward(self, x):
        y_pred = self.linear(x)
        return y_pred

我们将按照以下方式构建具有两个输入和一个输出的线性回归模型。

# build the model object
LR_model = LR(2, 1)

现在,让我们再次使用自定义模块对包含多个输入样本的张量X进行预测。

# make predictions for multiple input samples of 'X'
y_pred  = LR_model(X)
print("Predictions for 'X': ", y_pred)

其输出为

Predictions for 'X':  tensor([[0.3405],
        [0.5596],
        [0.7787]], grad_fn=<AddmmBackward0>)

使用parameters()方法,我们可以获得随机初始化参数的列表。

print(list(LR_model.parameters()))

其输出为

[Parameter containing:
tensor([[ 0.6496, -0.1549]], requires_grad=True), Parameter containing:
tensor([0.1427], requires_grad=True)]

另外,我们还可以使用state_dict()方法检查模型的参数。

将所有内容结合起来,以下是用不同方式创建多元线性回归模型的完整代码:

import torch

# Setting weights and bias
w = torch.tensor([[3.0], 
                  [4.0]], requires_grad=True)
b = torch.tensor([[1.0]], requires_grad=True)

# Defining our forward function for prediction
def forward(x):
    # using .mm module for matrix multiplication 
    y_pred = torch.mm(x, w) + b
    return y_pred

# define a tensor 'x'
x = torch.tensor([[2.0, 4.0]])
# predict the value with forward function
y_pred = forward(x)
# show the result
print("Printing Prediction: ", y_pred)

# define a tensor 'X' with multiple rows
X = torch.tensor([[1.0, 2.0],
                  [3.0, 4.0], 
                  [5.0, 6.0]])

# Making predictions for Multi-Dimensional tensor "X"
y_pred = forward(X)
print("Predictions for 'X': ", y_pred)

# using Pytorch's own built-in fuction to define the LR model
lr_model = torch.nn.Linear(in_features=2, out_features=1)

# Making predictions for X
y_pred = lr_model(X)
print("Predictions for 'X': ", y_pred)

# creating custom modules with package 'nn.Module'
class LR(torch.nn.Module):
    # Object Constructor
    def __init__(self, input_features, output_features):
        super().__init__()
        self.linear = torch.nn.Linear(input_features, output_features)
    # define the forward function for prediction
    def forward(self, x):
        y_pred = self.linear(x)
        return y_pred

# build the model object
LR_model = LR(2, 1)

# make predictions for multiple input samples of 'X'
y_pred  = LR_model(X)
print("Predictions for 'X': ", y_pred)

print(list(LR_model.parameters()))

总结

在本教程中,你学习了如何使用多元线性回归模型进行预测。特别是,你学习了:

  • 如何回顾多维线性回归。

  • 如何使用 PyTorch 进行多元线性回归模型预测。

  • 如何在 PyTorch 中使用类 Linear 进行多元线性回归。

  • 如何在 PyTorch 中使用 nn.Module 构建自定义模块。

使用检查点和早停管理 PyTorch 训练过程

原文:machinelearningmastery.com/managing-a-pytorch-training-process-with-checkpoints-and-early-stopping/

大型深度学习模型可能需要很长时间来训练。如果训练过程在中间被中断,你会丢失大量工作。但有时,你实际上会想在中间中断训练过程,因为你知道继续下去不会给你更好的模型。在这篇文章中,你将发现如何控制 PyTorch 中的训练循环,以便你可以恢复被中断的过程或提前停止训练循环。

完成这篇文章后,你将知道:

  • 训练时检查点的重要性

  • 如何在训练过程中创建检查点并在之后恢复

  • 如何通过检查点提前终止训练循环

通过我的书 Deep Learning with PyTorch 快速启动你的项目。它提供了自学教程可运行的代码

让我们开始吧!

使用检查点和早停管理 PyTorch 训练过程

图片由 Arron Choi 提供。保留所有权利。

概述

本章分为两部分;它们是:

  • 检查点神经网络模型

  • 具有早停的检查点

检查点神经网络模型

许多系统都有状态。如果你可以保存系统的所有状态并在以后恢复,你可以随时回到系统行为的特定时间点。如果你在 Microsoft Word 上工作并保存了多个版本的文档,因为你不知道是否要恢复编辑,这里也是同样的想法。

同样适用于长时间运行的过程。应用程序检查点是一种容错技术。在这种方法中,系统状态的快照会被拍摄以防系统故障。如果出现问题,你可以从快照中恢复。检查点可以直接使用,也可以作为新运行的起点,从中断的地方继续。当训练深度学习模型时,检查点捕获模型的权重。这些权重可以直接用于预测或作为持续训练的基础。

PyTorch 不提供任何检查点功能,但它有检索和恢复模型权重的功能。因此,你可以利用这些功能实现检查点逻辑。让我们创建一个检查点和恢复函数,这些函数简单地保存模型的权重并将其加载回来:

import torch

def checkpoint(model, filename):
    torch.save(model.state_dict(), filename)

def resume(model, filename):
    model.load_state_dict(torch.load(filename))

以下是你通常训练 PyTorch 模型的方式。使用的数据集从 OpenML 平台获取。它是一个二分类数据集。这个示例中使用了 PyTorch DataLoader,使训练循环更简洁。

import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import TensorDataset, DataLoader, random_split, default_collate
from sklearn.datasets import fetch_openml
from sklearn.preprocessing import LabelEncoder

data = fetch_openml("electricity", version=1, parser="auto")

# Label encode the target, convert to float tensors
X = data['data'].astype('float').values
y = data['target']
encoder = LabelEncoder()
encoder.fit(y)
y = encoder.transform(y)
X = torch.tensor(X, dtype=torch.float32)
y = torch.tensor(y, dtype=torch.float32).reshape(-1, 1)

# train-test split for model evaluation
trainset, testset = random_split(TensorDataset(X, y), [0.7, 0.3])

# Define the model
model = nn.Sequential(
    nn.Linear(8, 12),
    nn.ReLU(),
    nn.Linear(12, 12),
    nn.ReLU(),
    nn.Linear(12, 1),
    nn.Sigmoid(),
)

# Train the model
n_epochs = 100
loader = DataLoader(trainset, shuffle=True, batch_size=32)
X_test, y_test = default_collate(testset)
loss_fn = nn.BCELoss()
optimizer = optim.SGD(model.parameters(), lr=0.1)
for epoch in range(n_epochs):
    model.train()
    for X_batch, y_batch in loader:
        y_pred = model(X_batch)
        loss = loss_fn(y_pred, y_batch)
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()
    model.eval()
    y_pred = model(X_test)
    acc = (y_pred.round() == y_test).float().mean()
    print(f"End of epoch {epoch}: accuracy = {float(acc)*100:.2f}%")

如果您希望在上述训练循环中添加检查点,您可以在外部 for 循环结束时执行,这时模型通过测试集进行验证。例如,以下内容:

...
for epoch in range(n_epochs):
    model.train()
    for X_batch, y_batch in loader:
        y_pred = model(X_batch)
        loss = loss_fn(y_pred, y_batch)
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()
    model.eval()
    y_pred = model(X_test)
    acc = (y_pred.round() == y_test).float().mean()
    print(f"End of epoch {epoch}: accuracy = {float(acc)*100:.2f}%")
    checkpoint(model, f"epoch-{epoch}.pth")

您将在工作目录中看到创建的多个文件。这段代码将从第 7 轮保存模型的示例文件epoch-7.pth。每一个这样的文件都是一个带有序列化模型权重的 ZIP 文件。不禁止在内部 for 循环中设置检查点,但由于引入的开销,频繁设置检查点并不是一个好主意。

作为一种容错技术,在训练循环之前添加几行代码,您可以从特定的轮次恢复:

start_epoch = 0
if start_epoch > 0:
    resume_epoch = start_epoch - 1
    resume(model, f"epoch-{resume_epoch}.pth")

for epoch in range(start_epoch, n_epochs):
    model.train()
    for X_batch, y_batch in loader:
        y_pred = model(X_batch)
        loss = loss_fn(y_pred, y_batch)
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()
    model.eval()
    y_pred = model(X_test)
    acc = (y_pred.round() == y_test).float().mean()
    print(f"End of epoch {epoch}: accuracy = {float(acc)*100:.2f}%")
    checkpoint(model, f"epoch-{epoch}.pth")

也就是说,如果训练循环在第 8 轮中间被中断,因此最后一个检查点是从第 7 轮开始的,则在上面设置start_epoch = 8

注意,如果这样做,生成训练集和测试集的random_split()函数可能由于随机性而导致不同的分割。如果这对你来说是个问题,你应该有一种一致的方法来创建数据集(例如,保存分割的数据以便重用)。

有时,模型外部存在状态,您可能希望对其进行检查点。一个特别的例子是优化器,在像 Adam 这样的情况下,具有动态调整的动量。如果重新启动训练循环,您可能还希望恢复优化器中的动量。这并不难做到。关键是使您的checkpoint()函数更复杂,例如:

torch.save({
    'optimizer': optimizer.state_dict(),
    'model': model.state_dict(),
}, filename)

并相应地更改您的resume()函数:

checkpoint = torch.load(filename)
model.load_state_dict(checkpoint['model'])
optimizer.load_state_dict(checkpoint['optimizer'])

这有效是因为在 PyTorch 中,torch.save()torch.load()函数都由pickle支持,因此您可以在包含listdict容器的情况下使用它。

为了将所有内容整合在一起,下面是完整的代码:

import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import TensorDataset, DataLoader, random_split, default_collate
from sklearn.datasets import fetch_openml
from sklearn.preprocessing import LabelEncoder

data = fetch_openml("electricity", version=1, parser="auto")

# Label encode the target, convert to float tensors
X = data['data'].astype('float').values
y = data['target']
encoder = LabelEncoder()
encoder.fit(y)
y = encoder.transform(y)
X = torch.tensor(X, dtype=torch.float32)
y = torch.tensor(y, dtype=torch.float32).reshape(-1, 1)

# train-test split for model evaluation
trainset, testset = random_split(TensorDataset(X, y), [0.7, 0.3])

def checkpoint(model, filename):
    torch.save(model.state_dict(), filename)

def resume(model, filename):
    model.load_state_dict(torch.load(filename))

# Define the model
model = nn.Sequential(
    nn.Linear(8, 12),
    nn.ReLU(),
    nn.Linear(12, 12),
    nn.ReLU(),
    nn.Linear(12, 1),
    nn.Sigmoid(),
)

# Train the model
n_epochs = 100
start_epoch = 0
loader = DataLoader(trainset, shuffle=True, batch_size=32)
X_test, y_test = default_collate(testset)
loss_fn = nn.BCELoss()
optimizer = optim.SGD(model.parameters(), lr=0.1)

if start_epoch > 0:
    resume_epoch = start_epoch - 1
    resume(model, f"epoch-{resume_epoch}.pth")

for epoch in range(start_epoch, n_epochs):
    model.train()
    for X_batch, y_batch in loader:
        y_pred = model(X_batch)
        loss = loss_fn(y_pred, y_batch)
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()
    model.eval()
    y_pred = model(X_test)
    acc = (y_pred.round() == y_test).float().mean()
    print(f"End of epoch {epoch}: accuracy = {float(acc)*100:.2f}%")
    checkpoint(model, f"epoch-{epoch}.pth")

想要开始使用 PyTorch 进行深度学习吗?

立即参加我的免费电子邮件崩溃课程(附有示例代码)。

点击注册并获得课程的免费 PDF 电子书版本。

使用早停技术进行检查点

检查点不仅用于容错。您还可以使用它来保持最佳模型。如何定义最佳模型是主观的,但考虑来自测试集的分数是一个明智的方法。假设只保留找到的最佳模型,您可以修改训练循环如下:

...
best_accuracy = -1
for epoch in range(start_epoch, n_epochs):
    model.train()
    for X_batch, y_batch in loader:
        y_pred = model(X_batch)
        loss = loss_fn(y_pred, y_batch)
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()
    model.eval()
    y_pred = model(X_test)
    acc = (y_pred.round() == y_test).float().mean()
    acc = float(acc) * 100
    print(f"End of epoch {epoch}: accuracy = {acc:.2f}%")
    if acc > best_accuracy:
        best_accuracy = acc
        checkpoint(model, "best_model.pth")

resume(model, "best_model.pth")

变量best_accuracy用于跟踪迄今为止获得的最高准确率(acc),其范围在 0 到 100 之间的百分比。每当观察到更高的准确率时,模型将被保存到文件best_model.pth。在整个训练循环之后,通过您之前创建的resume()函数恢复最佳模型。之后,您可以使用模型对未见数据进行预测。请注意,如果您使用不同的指标进行检查点,例如交叉熵损失,更好的模型应该伴随着更低的交叉熵。因此,您应该跟踪获取的最低交叉熵。

你还可以在每个训练周期无条件地保存模型检查点,并与最佳模型检查点保存一起,因为你可以创建多个检查点文件。由于上面的代码是找到最佳模型并复制它,你通常会看到对训练循环的进一步优化,如果希望看到模型改善的希望很小,可以提前停止训练。这就是可以节省训练时间的早停技术。

上面的代码在每个训练周期结束时使用测试集验证模型,并将找到的最佳模型保存在检查点文件中。最简单的早停策略是设置一个 kk 训练周期的阈值。如果你没有看到模型在过去 kk 个训练周期内有所改善,你可以在中间终止训练循环。实现方式如下:

early_stop_thresh = 5
best_accuracy = -1
best_epoch = -1
for epoch in range(start_epoch, n_epochs):
    model.train()
    for X_batch, y_batch in loader:
        y_pred = model(X_batch)
        loss = loss_fn(y_pred, y_batch)
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()
    model.eval()
    y_pred = model(X_test)
    acc = (y_pred.round() == y_test).float().mean()
    acc = float(acc) * 100
    print(f"End of epoch {epoch}: accuracy = {acc:.2f}%")
    if acc > best_accuracy:
        best_accuracy = acc
        best_epoch = epoch
        checkpoint(model, "best_model.pth")
    elif epoch - best_epoch > early_stop_thresh:
        print("Early stopped training at epoch %d" % epoch)
        break  # terminate the training loop

resume(model, "best_model.pth")

阈值 early_stop_thresh 上面设置为 5。还有一个变量 best_epoch 用于记住最佳模型的周期。如果模型在足够长的时间内没有改善,外部 for 循环将被终止。

这个设计减轻了一个设计参数 n_epochs 的压力。你现在可以将 n_epochs 设置为训练模型的最大周期数,因此可以比需要的周期数更大,并且通常可以确保你的训练循环会更早停止。这也是避免过拟合的一种策略:如果模型在测试集上表现更差,早停逻辑将中断训练并恢复最佳检查点。

总结所有内容,以下是带有早停的检查点的完整代码:

import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import TensorDataset, DataLoader, random_split, default_collate
from sklearn.datasets import fetch_openml
from sklearn.preprocessing import LabelEncoder

data = fetch_openml("electricity", version=1, parser="auto")

# Label encode the target, convert to float tensors
X = data['data'].astype('float').values
y = data['target']
encoder = LabelEncoder()
encoder.fit(y)
y = encoder.transform(y)
X = torch.tensor(X, dtype=torch.float32)
y = torch.tensor(y, dtype=torch.float32).reshape(-1, 1)

# train-test split for model evaluation
trainset, testset = random_split(TensorDataset(X, y), [0.7, 0.3])

def checkpoint(model, filename):
    torch.save(model.state_dict(), filename)

def resume(model, filename):
    model.load_state_dict(torch.load(filename))

# Define the model
model = nn.Sequential(
    nn.Linear(8, 12),
    nn.ReLU(),
    nn.Linear(12, 12),
    nn.ReLU(),
    nn.Linear(12, 1),
    nn.Sigmoid(),
)

# Train the model
n_epochs = 10000  # more than we needed
loader = DataLoader(trainset, shuffle=True, batch_size=32)
X_test, y_test = default_collate(testset)
loss_fn = nn.BCELoss()
optimizer = optim.SGD(model.parameters(), lr=0.1)

early_stop_thresh = 5
best_accuracy = -1
best_epoch = -1

for epoch in range(n_epochs):
    model.train()
    for X_batch, y_batch in loader:
        y_pred = model(X_batch)
        loss = loss_fn(y_pred, y_batch)
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()
    model.eval()
    y_pred = model(X_test)
    acc = (y_pred.round() == y_test).float().mean()
    acc = float(acc) * 100
    print(f"End of epoch {epoch}: accuracy = {acc:.2f}%")
    if acc > best_accuracy:
        best_accuracy = acc
        best_epoch = epoch
        checkpoint(model, "best_model.pth")
    elif epoch - best_epoch > early_stop_thresh:
        print("Early stopped training at epoch %d" % epoch)
        break  # terminate the training loop

resume(model, "best_model.pth")

你可能会看到上述代码产生:

End of epoch 0: accuracy = 61.84%
End of epoch 1: accuracy = 55.90%
End of epoch 2: accuracy = 63.95%
End of epoch 3: accuracy = 66.87%
End of epoch 4: accuracy = 64.77%
End of epoch 5: accuracy = 60.03%
End of epoch 6: accuracy = 67.16%
End of epoch 7: accuracy = 66.01%
End of epoch 8: accuracy = 62.88%
End of epoch 9: accuracy = 64.28%
End of epoch 10: accuracy = 68.63%
End of epoch 11: accuracy = 70.56%
End of epoch 12: accuracy = 64.62%
End of epoch 13: accuracy = 65.63%
End of epoch 14: accuracy = 66.81%
End of epoch 15: accuracy = 65.11%
End of epoch 16: accuracy = 55.81%
End of epoch 17: accuracy = 54.59%
Early stopped training at epoch 17

它在第 17 个训练周期结束时停止,使用了从第 11 个周期获得的最佳模型。由于算法的随机性,你可能会看到结果有所不同。但可以肯定的是,即使将最大训练周期数设置为 10000,训练循环确实会更早停止。

当然,你可以设计更复杂的早停策略,例如,至少运行 NN 个周期,然后在 kk 个周期后允许提前停止。你可以自由调整上述代码,以使最佳训练循环满足你的需求。

总结

在本章中,你发现了在长时间训练过程中保存深度学习模型检查点的重要性。你学习了:

  • 什么是检查点,为什么它很有用

  • 如何保存模型检查点以及如何恢复检查点

  • 使用检查点的不同策略

  • 如何实现带有检查点的早停

在 PyTorch 中操作张量

原文:machinelearningmastery.com/manipulating-tensors-in-pytorch/

PyTorch 是一个深度学习库。就像一些其他深度学习库一样,它对称为 张量 的数值数组执行操作。简而言之,张量就是多维数组。当我们处理张量时,某些操作使用得非常频繁。在 PyTorch 中,有一些函数专门用于处理张量。

在接下来的内容中,我们将简要概述 PyTorch 在张量方面的提供以及如何使用它们。完成本教程后,你将了解:

  • 如何创建和操作 PyTorch 张量

  • PyTorch 的张量语法类似于 NumPy

  • 你可以使用 PyTorch 中的常用函数来操作张量。

启动你的项目,请参阅我的书 Deep Learning with PyTorch。它提供了 自学教程可运行的代码

让我们开始吧。

在 PyTorch 中操作张量。照片由 Big Dodzy 提供。版权所有。

概述

本教程分为四个部分;它们是:

  • 创建张量

  • 检查张量

  • 操作张量

  • 张量函数

创建张量

如果你对 NumPy 熟悉,你应该会记得有多种方式来创建数组。在 PyTorch 中创建张量也是如此。创建特定常量矩阵的最简单方法如下:

[123456] \begin{bmatrix} 1 & 2 & 3 \\ 4 & 5 & 6 \end{bmatrix}

这是通过使用:

import torch
a = torch.tensor([[1,2,3], [4,5,6]], dtype=torch.int32)
print(a)

它打印:

tensor([[1, 2, 3],
        [4, 5, 6]], dtype=torch.int32)

dtype 参数指定张量中值的数据类型。它是可选的。你还可以提供来自 NumPy 数组的值,并将其转换为 PyTorch 张量。

通常,你会为了某个特定目的创建张量。例如,如果你想要十个在 -1 和 1 之间均匀分布的值,你可以使用 linspace() 函数:

a = torch.linspace(-1, 1, 10)
print(a)

它打印:

tensor([-1.0000, -0.7778, -0.5556, -0.3333, -0.1111,  0.1111,  0.3333,  0.5556,
         0.7778,  1.0000])

不过,如果你想要一个包含随机值的张量(这在测试你的函数时非常有用),你可以创建一个如下的张量:

a = torch.rand(3,4)
print(a)

例如,它打印:

tensor([[0.4252, 0.1029, 0.9858, 0.7502],
        [0.1993, 0.6412, 0.2424, 0.6451],
        [0.7878, 0.7615, 0.9170, 0.8534]])

结果张量的维度为 3×43\times 4,每个值在 0 和 1 之间均匀分布。如果你想要值呈正态分布,只需将函数更改为 randn()

a = torch.randn(3,4)

如果你想要随机值为整数,例如,在 3 到 10 之间,你可以使用 randint() 函数:

a = torch.randint(3, 10, size=(3,4))
print(a)

例如,这将产生:

tensor([[4, 5, 7, 9],
        [3, 8, 8, 9],
        [4, 7, 7, 6]])

这些值的范围是 3x<103 \le x < 10。默认情况下,下限是零,因此如果你希望值的范围是 0x<100 \le x < 10,你可以使用:

a = torch.randint(10, size=(3,4))

其他常用的张量包括零张量和所有值相同的张量。要创建一个零张量(例如,维度为 2×3×42\times 3\times 4),你可以使用:

a = torch.zeros(2, 3, 4)
print(a)

它打印:

tensor([[[0., 0., 0., 0.],
         [0., 0., 0., 0.],
         [0., 0., 0., 0.]],

        [[0., 0., 0., 0.],
         [0., 0., 0., 0.],
         [0., 0., 0., 0.]]])

如果你想创建一个所有值都是 5 的张量,你可以使用:

a = torch.full((2,3,4), 5)
print(a)

它打印:

tensor([[[5, 5, 5, 5],
         [5, 5, 5, 5],
         [5, 5, 5, 5]],

        [[5, 5, 5, 5],
         [5, 5, 5, 5],
         [5, 5, 5, 5]]])

但如果你想要所有值都为一,有一个更简单的函数:

a = torch.ones(2,3,4)

最后,如果你想要一个单位矩阵,可以使用 diag()eye() 来获得:

a = torch.eye(4)
print(a)

它将打印出:

tensor([[1., 0., 0., 0.],
        [0., 1., 0., 0.],
        [0., 0., 1., 0.],
        [0., 0., 0., 1.]])

想要开始使用 PyTorch 进行深度学习?

立即参加我的免费电子邮件速成课程(包括示例代码)。

点击注册,还可以获得课程的免费 PDF 电子书版本。

检查张量

一旦你有了一个张量,并且你想了解更多关于它的信息,你可以简单地使用 print() 将其打印到屏幕上。但如果张量太大,可以通过检查其形状来更容易地显示其维度:

a = torch.zeros(2, 3, 4)
print(a.shape)
print(a.size())

它将打印出:

torch.Size([2, 3, 4])
torch.Size([2, 3, 4])

张量的形状可以通过 shape 属性或 size() 函数来访问。如果你想查看有多少个维度(例如,2×3×42\times 3\times 4 是 3,而 3×43\times 4 是 2),你可以读取 ndim 属性:

print(a.ndim)

这将给你“3”。如果你使用 len() 检查张量,它只会给你第一个维度的大小,例如:

a = torch.zeros(2, 3, 4)
print(len(a))

它将打印出:

2

另一个你可能想了解的张量属性是其数据类型。通常,在深度学习中你使用浮点数,但有时张量应为整数(例如,在图像作为像素值中)。要检查数据类型,你可以读取 dtype 属性:

print(a.dtype)

它将打印出:

torch.float32

如果你想更改数据类型,你可以使用新的类型重新创建张量:

b = a.type(torch.int32)
print(a.dtype)
print(b.dtype)

上面的打印结果是:

torch.float32
torch.int32

操作张量

深度学习中的一个常见操作是改变张量的形状。例如,你可能想要将 2D 张量转换为 1D 或向张量添加一个虚拟维度。你也可能想从较大的张量中提取子张量。

例如,你可以创建一个如下所示的张量:

a = torch.randn(3,4,5)
print(a)

如果你得到:

tensor([[[-1.1271e-01, -7.1124e-01,  1.1335e+00, -8.5644e-01, -1.4191e+00],
         [-1.9065e+00, -6.8386e-02,  5.8727e-01,  6.5890e-03, -2.6947e-01],
         [ 6.3194e-01, -7.7426e-01,  1.6546e+00,  1.2647e-01, -1.0944e+00],
         [ 3.7819e-01, -8.8670e-01,  5.3772e-01,  1.4985e+00,  5.8396e-01]],

        [[ 1.8704e+00,  2.0914e+00, -9.1604e-01,  1.2317e+00, -1.5722e-01],
         [ 2.4689e-01, -2.3157e-01, -3.3033e-01,  1.4021e+00, -6.9540e-01],
         [ 3.0298e-01, -1.4936e-01, -6.8863e-01,  1.6977e-01,  2.4682e+00],
         [-8.1375e-01,  4.8489e-01, -1.2024e+00, -4.9771e-01,  1.1728e-01]],

        [[-1.6011e+00,  1.5686e-03, -1.0560e-01, -1.2938e+00,  5.3077e-01],
         [-9.7636e-01, -9.1854e-01, -1.0002e+00,  1.1852e+00,  1.0328e+00],
         [ 9.6664e-01,  5.3752e-01, -3.1340e-02, -6.7852e-02, -7.2317e-01],
         [-5.5263e-01,  9.4754e-01, -5.4503e-01,  6.3850e-02,  1.2879e+00]]])

它允许你使用与 NumPy 相同的语法进行切片:

print(a[1])

这将是:

tensor([[ 1.8704,  2.0914, -0.9160,  1.2317, -0.1572],
        [ 0.2469, -0.2316, -0.3303,  1.4021, -0.6954],
        [ 0.3030, -0.1494, -0.6886,  0.1698,  2.4682],
        [-0.8137,  0.4849, -1.2024, -0.4977,  0.1173]])

或者如果你使用:

print(a[1:, 2:4])

它将是:

tensor([[[ 0.3030, -0.1494, -0.6886,  0.1698,  2.4682],
         [-0.8137,  0.4849, -1.2024, -0.4977,  0.1173]],

        [[ 0.9666,  0.5375, -0.0313, -0.0679, -0.7232],
         [-0.5526,  0.9475, -0.5450,  0.0638,  1.2879]]])

你还可以利用相同的切片语法来 添加 一个新维度。例如,

print(a[:, None, :, None].shape)

你将看到:

torch.Size([3, 1, 4, 1, 5])

在这里,你使用 None 在特定位置插入一个新维度。如果你需要将图像转换为只有一张图像的批次时,这很有用。如果你熟悉 NumPy,你可能会记得有一个 expand_dims() 函数用于此目的,但 PyTorch 没有提供。一个类似的函数是 unsqueeze(),如下所示:

b = torch.unsqueeze(a, dim=2)
print(a.shape)
print(b.shape)

这将打印出:

torch.Size([3, 4, 5])
torch.Size([3, 4, 1, 5])

NumPy 切片语法的一个强大特性是布尔索引。这在 PyTorch 张量中也受支持。例如:

a = torch.randn(3,4)
print(a)
print(a[:, (a > -1).all(axis=0)])

你可能会看到:

tensor([[ 1.2548,  0.4078,  0.5548, -0.7016],
        [-0.3720, -0.5682, -0.3430,  0.0886],
        [ 0.2151,  0.3626, -2.0275,  1.8121]])
tensor([[ 1.2548,  0.4078, -0.7016],
        [-0.3720, -0.5682,  0.0886],
        [ 0.2151,  0.3626,  1.8121]])

上面的代码选择了所有元素都大于 -1 的列。你也可以通过选择特定的列来操作张量:

print(a[:, [1,0,0,1]])

这将导致:

tensor([[ 0.4078,  1.2548,  1.2548,  0.4078],
        [-0.5682, -0.3720, -0.3720, -0.5682],
        [ 0.3626,  0.2151,  0.2151,  0.3626]])

要将 2D 张量转换为 1D,你可以使用:

a = torch.randn(3,4)
print(a)
print(a.ravel())

结果将是:

tensor([[-0.2718, -0.8309,  0.6263, -0.2499],
        [-0.1780,  1.1735, -1.3530, -1.2374],
        [-0.6050, -1.5524, -0.1008, -1.2782]])
tensor([-0.2718, -0.8309,  0.6263, -0.2499, -0.1780,  1.1735, -1.3530, -1.2374,
        -0.6050, -1.5524, -0.1008, -1.2782])

你也可以使用 reshape() 函数来实现相同的效果:

print(a.reshape(-1))

结果应该与 ravel() 相同。但通常,reshape() 函数用于更复杂的目标形状:

print(a.reshape(3,2,2))

这将打印出:

tensor([[[-0.2718, -0.8309],
         [ 0.6263, -0.2499]],

        [[-0.1780,  1.1735],
         [-1.3530, -1.2374]],

        [[-0.6050, -1.5524],
         [-0.1008, -1.2782]]])

张量重塑的一个常见情况是矩阵转置。对于 2D 矩阵,它可以像 NumPy 一样轻松完成:

print(a.T)

这将打印出:

tensor([[-0.2718, -0.1780, -0.6050],
        [-0.8309,  1.1735, -1.5524],
        [ 0.6263, -1.3530, -0.1008],
        [-0.2499, -1.2374, -1.2782]])

但 PyTorch 中的 transpose() 函数要求你显式指定要交换的轴:

print(a.transpose(0, 1))

这个结果与上面相同。如果你有多个张量,可以通过堆叠它们来组合(vstack() 用于垂直堆叠,hstack() 用于水平堆叠)。例如:

a = torch.randn(3,3)
b = torch.randn(3,3)
print(a)
print(b)
print(torch.vstack([a,b]))

这可能会打印:

tensor([[ 1.1739,  1.3546, -0.2886],
        [ 1.0444,  0.4437, -2.7933],
        [ 0.6805,  0.8401, -1.2527]])
tensor([[ 1.6273,  1.2622, -0.4362],
        [-1.6529,  0.6457, -0.1454],
        [-2.0960, -1.3024, -0.1033]])
tensor([[ 1.1739,  1.3546, -0.2886],
        [ 1.0444,  0.4437, -2.7933],
        [ 0.6805,  0.8401, -1.2527],
        [ 1.6273,  1.2622, -0.4362],
        [-1.6529,  0.6457, -0.1454],
        [-2.0960, -1.3024, -0.1033]])

连接函数类似:

c = torch.concatenate([a, b])
print(c)

你将得到相同的张量:

tensor([[ 1.1739,  1.3546, -0.2886],
        [ 1.0444,  0.4437, -2.7933],
        [ 0.6805,  0.8401, -1.2527],
        [ 1.6273,  1.2622, -0.4362],
        [-1.6529,  0.6457, -0.1454],
        [-2.0960, -1.3024, -0.1033]])

反向操作是分割,例如,

print(torch.vsplit(c, 2))

它会打印

(tensor([[ 1.1739,  1.3546, -0.2886],
        [ 1.0444,  0.4437, -2.7933],
        [ 0.6805,  0.8401, -1.2527]]), tensor([[ 1.6273,  1.2622, -0.4362],
        [-1.6529,  0.6457, -0.1454],
        [-2.0960, -1.3024, -0.1033]]))

这个函数告诉你要将张量分割成多少个,而不是每个张量的大小。后者在深度学习中确实更有用(例如,将一个大数据集的张量分割成许多小批量的张量)。等效的函数是:

print(torch.split(c, 3, dim=0))

这应与你之前得到的结果相同。所以 split(c, 3, dim=0) 意味着在维度 0 上分割,使得每个结果张量的大小为 3。

张量函数

PyTorch 张量可以被视为数组。因此,你通常可以像使用 NumPy 数组一样使用它。例如,你可以使用常见数学函数的函数:

a = torch.randn(2,3)
print(a)
print(torch.exp(a))
print(torch.log(a))
print(torch.sin(a))
print(torch.arctan(a))
print(torch.abs(a))
print(torch.square(a))
print(torch.sqrt(a))
print(torch.ceil(a))
print(torch.round(a))
print(torch.clip(a, 0.1, 0.9))

这会打印:

tensor([[ 1.0567, -1.2609, -1.0856],
        [-0.9633,  1.3163, -0.4325]])
tensor([[2.8770, 0.2834, 0.3377],
        [0.3816, 3.7298, 0.6489]])
tensor([[0.0552,    nan,    nan],
        [   nan, 0.2749,    nan]])
tensor([[ 0.8708, -0.9524, -0.8846],
        [-0.8211,  0.9678, -0.4191]])
tensor([[ 0.8130, -0.9003, -0.8264],
        [-0.7667,  0.9211, -0.4082]])
tensor([[1.0567, 1.2609, 1.0856],
        [0.9633, 1.3163, 0.4325]])
tensor([[1.1167, 1.5898, 1.1785],
        [0.9280, 1.7328, 0.1871]])
tensor([[1.0280,    nan,    nan],
        [   nan, 1.1473,    nan]])
tensor([[ 2., -1., -1.],
        [-0.,  2., -0.]])
tensor([[ 1., -1., -1.],
        [-1.,  1., -0.]])
tensor([[0.9000, 0.1000, 0.1000],
        [0.1000, 0.9000, 0.1000]])

注意,如果函数未定义(例如,负数的平方根),nan 将是结果,但不会引发异常。在 PyTorch 中,你可以使用一个函数来检查张量的值是否为 nan

b = torch.sqrt(a)
print(b)
print(torch.isnan(b))

你将得到:

tensor([[1.0280,    nan,    nan],
        [   nan, 1.1473,    nan]])
tensor([[False,  True,  True],
        [ True, False,  True]])

确实,除了这些定义的函数,Python 运算符也可以应用于张量:

a = torch.randn(2, 3)
b = torch.randn(2, 3)
print(a)
print(b)
print(a+b)
print(a/b)
print(a ** 2)

你得到:

tensor([[ 0.7378, -0.3469,  1.3089],
        [-1.9152,  0.3745, -0.7248]])
tensor([[-0.3650, -0.4768,  0.9331],
        [ 0.5095,  1.7169, -0.5463]])
tensor([[ 0.3729, -0.8237,  2.2421],
        [-1.4058,  2.0914, -1.2711]])
tensor([[-2.0216,  0.7275,  1.4027],
        [-3.7594,  0.2181,  1.3269]])
tensor([[0.5444, 0.1203, 1.7133],
        [3.6682, 0.1403, 0.5254]])

但在运算符中,矩阵乘法在深度学习中非常重要。你可以使用以下方法实现:

print(torch.matmul(a, b.T))
print(a @ b.T)

这会打印

tensor([[ 1.1176, -0.9347],
        [-0.1560,  0.0632]])
tensor([[ 1.1176, -0.9347],
        [-0.1560,  0.0632]])

这两者是相同的。实际上,Python 的 @ 运算符也可以用于向量点积,例如:

a = torch.randn(3)
b = torch.randn(3)
print(a)
print(b)
print(torch.dot(a, b))
print(a @ b)

它会打印:

tensor([-0.8986, -0.6994,  1.1443])
tensor([-1.0666,  0.1455,  0.1322])
tensor(1.0081)
tensor(1.0081)

如果你把张量中的值视为样本,你可能还想找出一些关于它的统计数据。PyTorch 也提供了一些:

a = torch.randn(3,4)
print(a)
print(torch.mean(a, dim=0))
print(torch.std(a, dim=0))
print(torch.cumsum(a, dim=0))
print(torch.cumprod(a, dim=0))

它会打印:

tensor([[ 0.3331, -0.0190,  0.4814, -1.1484],
        [-0.5712,  0.8430, -1.6147, -1.1664],
        [ 1.7298, -1.7665, -0.5918,  0.3024]])
tensor([ 0.4972, -0.3142, -0.5750, -0.6708])
tensor([1.1593, 1.3295, 1.0482, 0.8429])
tensor([[ 0.3331, -0.0190,  0.4814, -1.1484],
        [-0.2381,  0.8240, -1.1333, -2.3148],
        [ 1.4917, -0.9425, -1.7251, -2.0124]])
tensor([[ 0.3331, -0.0190,  0.4814, -1.1484],
        [-0.1903, -0.0160, -0.7774,  1.3395],
        [-0.3291,  0.0283,  0.4601,  0.4051]])

但对于线性代数函数,你应在 PyTorch 的 linalg 子模块中找到它。例如:

print(torch.linalg.svd(a))

你将看到:

torch.return_types.linalg_svd(
U=tensor([[-0.0353,  0.1313,  0.9907],
        [-0.5576,  0.8201, -0.1286],
        [ 0.8294,  0.5569, -0.0443]]),
S=tensor([2.7956, 1.9465, 1.2715]),
Vh=tensor([[ 0.6229, -0.6919,  0.1404,  0.3369],
        [ 0.2767, -0.1515, -0.8172, -0.4824],
        [ 0.2570, -0.0385,  0.5590, -0.7874],
        [ 0.6851,  0.7048, -0.0073,  0.1840]]))

特别是对于卷积神经网络,填充张量可以使用以下方法:

b = torch.nn.functional.pad(a, (1,1,0,2), value=0)
print(b)

这会打印:

tensor([[ 0.0000,  0.3331, -0.0190,  0.4814, -1.1484,  0.0000],
        [ 0.0000, -0.5712,  0.8430, -1.6147, -1.1664,  0.0000],
        [ 0.0000,  1.7298, -1.7665, -0.5918,  0.3024,  0.0000],
        [ 0.0000,  0.0000,  0.0000,  0.0000,  0.0000,  0.0000],
        [ 0.0000,  0.0000,  0.0000,  0.0000,  0.0000,  0.0000]])

这个 pad() 函数的例子是创建维度 0 上的 (1,1) 填充和维度 1 上的 (0,2) 填充。换句话说,对于每个维度 0(行),我们在开始和结束各添加一个虚拟值(0)。对于每个维度 1(列),我们在开始处添加零个虚拟值,但在结束处添加两个虚拟值。

最后,由于 PyTorch 张量可以被视为数组,你可以直接将它们与其他工具如 matplotlib 一起使用。下面是一个使用 PyTorch 张量绘制表面的例子:

import matplotlib.pyplot as plt
import torch

# create tensors
x = torch.linspace(-1, 1, 100)
y = torch.linspace(-2, 2, 100)
# create the surface
xx, yy = torch.meshgrid(x, y, indexing="xy")  # xy-indexing is matching numpy
z = torch.sqrt(1 - xx**2 - (yy/2)**2)
print(xx)

fig = plt.figure(figsize=(8,8))
ax = plt.axes(projection="3d")
ax.set_xlim([-2, 2])
ax.set_ylim([-2, 2])
ax.set_zlim([0, 2])
ax.plot_surface(xx, yy, z, cmap="cividis")
ax.view_init(45, 35)
plt.show()

网格生成器产生的 xx 张量为:

tensor([[-1.0000, -0.9798, -0.9596,  ...,  0.9596,  0.9798,  1.0000],
        [-1.0000, -0.9798, -0.9596,  ...,  0.9596,  0.9798,  1.0000],
        [-1.0000, -0.9798, -0.9596,  ...,  0.9596,  0.9798,  1.0000],
        ...,
        [-1.0000, -0.9798, -0.9596,  ...,  0.9596,  0.9798,  1.0000],
        [-1.0000, -0.9798, -0.9596,  ...,  0.9596,  0.9798,  1.0000],
        [-1.0000, -0.9798, -0.9596,  ...,  0.9596,  0.9798,  1.0000]])

创建的图像是:

总结

在本教程中,你发现了如何操作 PyTorch 张量。具体来说,你学到了:

  • 什么是张量

  • 如何在 PyTorch 中创建各种类型的张量

  • 如何在 PyTorch 中重新形状、切片和操作张量

  • 可以应用于 PyTorch 张量的常见函数

PyTorch 中的小批量梯度下降和 DataLoader

原文:machinelearningmastery.com/mini-batch-gradient-descent-and-dataloader-in-pytorch/

小批量梯度下降是一种用于训练深度学习模型的梯度下降算法变体。该算法的核心思想是将训练数据分成批次,然后逐批次进行处理。在每次迭代中,我们同时更新属于特定批次的所有训练样本的权重。这个过程在不同的批次上重复,直到整个训练数据集被处理完毕。与批量梯度下降相比,这种方法的主要优势在于它可以显著减少计算时间和内存使用,因为它不是一次性处理所有训练样本。

DataLoader是 PyTorch 中加载和预处理数据的模块。它可用于从文件加载数据,或生成合成数据。

在本教程中,我们将向您介绍小批量梯度下降的概念。您还将了解如何使用 PyTorch 的DataLoader来实现它。具体来说,我们将涵盖以下内容:

  • 在 PyTorch 中实现小批量梯度下降。

  • PyTorch 中 DataLoader 的概念以及如何使用它加载数据。

  • 随机梯度下降与小批量梯度下降的区别。

  • 如何使用 PyTorch DataLoader 实现随机梯度下降。

  • 如何使用 PyTorch DataLoader 实现小批量梯度下降。

通过我的书籍《PyTorch 深度学习》来启动您的项目。它提供了带有工作代码自学教程

让我们开始吧!

PyTorch 中的小批量梯度下降和 DataLoader。

图片由Yannis Papanastasopoulos拍摄。部分权利保留。

概述

本教程分为六个部分,它们分别是:

  • PyTorch 中的 DataLoader

  • 准备数据和线性回归模型

  • 构建数据集和 DataLoader 类

  • 使用随机梯度下降和 DataLoader 进行训练

  • 使用小批量梯度下降和 DataLoader 进行训练

  • 绘制图表进行比较

PyTorch 中的 DataLoader

当您计划构建深度学习管道来训练模型时,一切都始于数据加载。数据越复杂,加载到管道中就越困难。PyTorch 的DataLoader是一个方便的工具,不仅能够轻松加载数据,还可以帮助应用数据增强策略,并在较大数据集中迭代样本。您可以从torch.utils.data中导入DataLoader类,如下所示。

from torch.utils.data import DataLoader

DataLoader类中有几个参数,我们只讨论datasetbatch_sizedataset是你在DataLoader类中找到的第一个参数,它将数据加载到管道中。第二个参数是batch_size,表示在一次迭代中处理的训练样本数。

DataLoader(dataset, batch_size=n)

准备数据和线性回归模型

我们将重用在之前教程中生成的线性回归数据:

import torch
import numpy as np
import matplotlib.pyplot as plt

# Creating a function f(X) with a slope of -5
X = torch.arange(-5, 5, 0.1).view(-1, 1)
func = -5 * X

# Adding Gaussian noise to the function f(X) and saving it in Y
Y = func + 0.4 * torch.randn(X.size())

和之前的教程一样,我们初始化了一个变量X,其值范围从5-555,并创建了一个斜率为5-5的线性函数。然后,加入高斯噪声以生成变量Y

我们可以使用 matplotlib 绘制数据以可视化模式:

...
# Plot and visualizing the data points in blue
plt.plot(X.numpy(), Y.numpy(), 'b+', label='Y')
plt.plot(X.numpy(), func.numpy(), 'r', label='func')
plt.xlabel('x')
plt.ylabel('y')
plt.legend()
plt.grid('True', color='y')
plt.show()

回归模型的数据点

接下来,我们将基于简单的线性回归方程构建一个前向函数。我们将训练模型以获取两个参数(wwbb)。所以,让我们定义一个模型的前向传播函数以及一个损失标准函数(MSE 损失)。参数变量wb将定义在函数外部:

...
# defining the function for forward pass for prediction
def forward(x):
    return w * x + b

# evaluating data points with Mean Square Error (MSE)
def criterion(y_pred, y):
    return torch.mean((y_pred - y) ** 2)

想开始使用 PyTorch 进行深度学习吗?

立即获取我的免费电子邮件速成课程(包含示例代码)。

点击注册并获取课程的免费 PDF 电子书版本。

构建数据集和DataLoader

让我们构建我们的DatasetDataLoader类。Dataset类允许我们构建自定义数据集并对其应用各种变换。DataLoader类则用于将数据集加载到模型训练的管道中。它们的创建方式如下。

# Creating our dataset class
class Build_Data(Dataset):    
    # Constructor
    def __init__(self):
        self.x = torch.arange(-5, 5, 0.1).view(-1, 1)
        self.y = -5 * X
        self.len = self.x.shape[0]        
    # Getting the data
    def __getitem__(self, index):    
        return self.x[index], self.y[index]    
    # Getting length of the data
    def __len__(self):
        return self.len

# Creating DataLoader object
dataset = Build_Data()
train_loader = DataLoader(dataset = dataset, batch_size = 1)

使用随机梯度下降和DataLoader进行训练

当批量大小设置为 1 时,训练算法称为随机梯度下降。类似地,当批量大小大于 1 但小于整个训练数据的大小时,训练算法称为迷你批量梯度下降。为了简便起见,我们将使用随机梯度下降和DataLoader进行训练。

如之前所示,我们将随机初始化可训练参数wwbb,定义其他参数如学习率或步长,创建一个空列表来存储损失,并设置训练的轮数。

w = torch.tensor(-10.0, requires_grad = True)
b = torch.tensor(-20.0, requires_grad = True)

step_size = 0.1
loss_SGD = []
n_iter = 20

在 SGD 中,我们只需在每次训练迭代中从数据集中选择一个样本。因此,一个简单的 for 循环加上前向和反向传播就是我们所需要的:

for i in range (n_iter):    
    # calculating loss as in the beginning of an epoch and storing it
    y_pred = forward(X)
    loss_SGD.append(criterion(y_pred, Y).tolist())
    for x, y in train_loader:
        # making a prediction in forward pass
        y_hat = forward(x)
        # calculating the loss between original and predicted data points
        loss = criterion(y_hat, y)    
        # backward pass for computing the gradients of the loss w.r.t to learnable parameters
        loss.backward()
        # updating the parameters after each iteration
        w.data = w.data - step_size * w.grad.data
        b.data = b.data - step_size * b.grad.data    
        # zeroing gradients after each iteration
        w.grad.data.zero_()
        b.grad.data.zero_()

将所有内容结合起来,下面是训练模型的完整代码,即wb

import matplotlib.pyplot as plt
import torch
from torch.utils.data import Dataset
from torch.utils.data import DataLoader
torch.manual_seed(42)

# Creating a function f(X) with a slope of -5
X = torch.arange(-5, 5, 0.1).view(-1, 1)
func = -5 * X
# Adding Gaussian noise to the function f(X) and saving it in Y
Y = func + 0.4 * torch.randn(X.size())

w = torch.tensor(-10.0, requires_grad = True)
b = torch.tensor(-20.0, requires_grad = True)

# defining the function for forward pass for prediction
def forward(x):
    return w * x + b

# evaluating data points with Mean Square Error (MSE)
def criterion(y_pred, y):
    return torch.mean((y_pred - y) ** 2)

# Creating our dataset class
class Build_Data(Dataset):
    # Constructor
    def __init__(self):
        self.x = torch.arange(-5, 5, 0.1).view(-1, 1)
        self.y = -5 * X
        self.len = self.x.shape[0]
    # Getting the data
    def __getitem__(self, index):
        return self.x[index], self.y[index]
    # Getting length of the data
    def __len__(self):
        return self.len

# Creating DataLoader object
dataset = Build_Data()
train_loader = DataLoader(dataset=dataset, batch_size=1)

step_size = 0.1
loss_SGD = []
n_iter = 20

for i in range (n_iter):
    # calculating loss as in the beginning of an epoch and storing it
    y_pred = forward(X)
    loss_SGD.append(criterion(y_pred, Y).tolist())
    for x, y in train_loader:
        # making a prediction in forward pass
        y_hat = forward(x)
        # calculating the loss between original and predicted data points
        loss = criterion(y_hat, y)
        # backward pass for computing the gradients of the loss w.r.t to learnable parameters
        loss.backward()
        # updating the parameters after each iteration
        w.data = w.data - step_size * w.grad.data
        b.data = b.data - step_size * b.grad.data
        # zeroing gradients after each iteration
        w.grad.data.zero_()
        b.grad.data.zero_()

使用迷你批量梯度下降和DataLoader进行训练

更进一步,我们将使用迷你批量梯度下降和DataLoader训练我们的模型。我们将设置不同的批量大小进行训练,即 10 和 20。批量大小为 10 的训练如下:

...
train_loader_10 = DataLoader(dataset=dataset, batch_size=10)

w = torch.tensor(-10.0, requires_grad=True)
b = torch.tensor(-20.0, requires_grad=True)

step_size = 0.1
loss_MBGD_10 = []
iter = 20

for i in range (iter):    
    # calculating loss as in the beginning of an epoch and storing it
    y_pred = forward(X)
    loss_MBGD_10.append(criterion(y_pred, Y).tolist())
    for x, y in train_loader_10:
        # making a prediction in forward pass
        y_hat = forward(x)
        # calculating the loss between original and predicted data points
        loss = criterion(y_hat, y)
        # backward pass for computing the gradients of the loss w.r.t to learnable parameters
        loss.backward()
        # updating the parameters after each iteration
        w.data = w.data - step_size * w.grad.data
        b.data = b.data - step_size * b.grad.data    
        # zeroing gradients after each iteration
        w.grad.data.zero_()
        b.grad.data.zero_()

下面是如何以 20 的批量大小实现相同功能:

...
train_loader_20 = DataLoader(dataset=dataset, batch_size=20)

w = torch.tensor(-10.0, requires_grad=True)
b = torch.tensor(-20.0, requires_grad=True)

step_size = 0.1
loss_MBGD_20 = []
iter = 20

for i in range(iter):    
    # calculating loss as in the beginning of an epoch and storing it
    y_pred = forward(X)
    loss_MBGD_20.append(criterion(y_pred, Y).tolist())
    for x, y in train_loader_20:
        # making a prediction in forward pass
        y_hat = forward(x)
        # calculating the loss between original and predicted data points
        loss = criterion(y_hat, y)    
        # backward pass for computing the gradients of the loss w.r.t to learnable parameters
        loss.backward()
        # updating the parameters after each iteration
        w.data = w.data - step_size * w.grad.data
        b.data = b.data - step_size * b.grad.data    
        # zeroing gradients after each iteration
        w.grad.data.zero_()
        b.grad.data.zero_()

将所有内容结合起来,以下是完整的代码:

import matplotlib.pyplot as plt
import torch
from torch.utils.data import Dataset
from torch.utils.data import DataLoader
torch.manual_seed(42)

# Creating a function f(X) with a slope of -5
X = torch.arange(-5, 5, 0.1).view(-1, 1)
func = -5 * X
# Adding Gaussian noise to the function f(X) and saving it in Y
Y = func + 0.4 * torch.randn(X.size())

w = torch.tensor(-10.0, requires_grad=True)
b = torch.tensor(-20.0, requires_grad=True)

# defining the function for forward pass for prediction
def forward(x):
    return w * x + b

# evaluating data points with Mean Square Error (MSE)
def criterion(y_pred, y):
    return torch.mean((y_pred - y) ** 2)

# Creating our dataset class
class Build_Data(Dataset):
    # Constructor
    def __init__(self):
        self.x = torch.arange(-5, 5, 0.1).view(-1, 1)
        self.y = -5 * X
        self.len = self.x.shape[0]
    # Getting the data
    def __getitem__(self, index):
        return self.x[index], self.y[index]
    # Getting length of the data
    def __len__(self):
        return self.len

# Creating DataLoader object
dataset = Build_Data()
train_loader_10 = DataLoader(dataset=dataset, batch_size=10)

step_size = 0.1
loss_MBGD_10 = []
iter = 20

for i in range(n_iter):
    # calculating loss as in the beginning of an epoch and storing it
    y_pred = forward(X)
    loss_MBGD_10.append(criterion(y_pred, Y).tolist())
    for x, y in train_loader_10:
        # making a prediction in forward pass
        y_hat = forward(x)
        # calculating the loss between original and predicted data points
        loss = criterion(y_hat, y)
        # backward pass for computing the gradients of the loss w.r.t to learnable parameters
        loss.backward()
        # updateing the parameters after each iteration
        w.data = w.data - step_size * w.grad.data
        b.data = b.data - step_size * b.grad.data
        # zeroing gradients after each iteration
        w.grad.data.zero_()
        b.grad.data.zero_()

train_loader_20 = DataLoader(dataset=dataset, batch_size=20)

# Reset w and b
w = torch.tensor(-10.0, requires_grad=True)
b = torch.tensor(-20.0, requires_grad=True)

loss_MBGD_20 = []

for i in range(n_iter):
    # calculating loss as in the beginning of an epoch and storing it
    y_pred = forward(X)
    loss_MBGD_20.append(criterion(y_pred, Y).tolist())
    for x, y in train_loader_20:
        # making a prediction in forward pass
        y_hat = forward(x)
        # calculating the loss between original and predicted data points
        loss = criterion(y_hat, y)
        # backward pass for computing the gradients of the loss w.r.t to learnable parameters
        loss.backward()
        # updating the parameters after each iteration
        w.data = w.data - step_size * w.grad.data
        b.data = b.data - step_size * b.grad.data
        # zeroing gradients after each iteration
        w.grad.data.zero_()
        b.grad.data.zero_()

绘制比较图表

最后,让我们可视化所有三种算法(即随机梯度下降、小批量梯度下降(批量大小为 10)和批量大小为 20)在训练期间损失的减少情况。

plt.plot(loss_SGD,label = "Stochastic Gradient Descent")
plt.plot(loss_MBGD_10,label = "Mini-Batch-10 Gradient Descent")
plt.plot(loss_MBGD_20,label = "Mini-Batch-20 Gradient Descent")
plt.xlabel('epoch')
plt.ylabel('Cost/total loss')
plt.legend()
plt.show()

从图中可以看出,小批量梯度下降可能会更快收敛,因为我们可以通过计算每一步的平均损失来对参数进行更精确的更新。

综合来看,以下是完整代码:

import matplotlib.pyplot as plt
import torch
from torch.utils.data import Dataset
from torch.utils.data import DataLoader
torch.manual_seed(42)

# Creating a function f(X) with a slope of -5
X = torch.arange(-5, 5, 0.1).view(-1, 1)
func = -5 * X
# Adding Gaussian noise to the function f(X) and saving it in Y
Y = func + 0.4 * torch.randn(X.size())

w = torch.tensor(-10.0, requires_grad=True)
b = torch.tensor(-20.0, requires_grad=True)

# defining the function for forward pass for prediction
def forward(x):
    return w * x + b

# evaluating data points with Mean Square Error (MSE)
def criterion(y_pred, y):
    return torch.mean((y_pred - y) ** 2)

# Creating our dataset class
class Build_Data(Dataset):
    # Constructor
    def __init__(self):
        self.x = torch.arange(-5, 5, 0.1).view(-1, 1)
        self.y = -5 * X
        self.len = self.x.shape[0]
    # Getting the data
    def __getitem__(self, index):
        return self.x[index], self.y[index]
    # Getting length of the data
    def __len__(self):
        return self.len

# Creating DataLoader object
dataset = Build_Data()
train_loader = DataLoader(dataset=dataset, batch_size=1)

step_size = 0.1
loss_SGD = []
n_iter = 20

for i in range(n_iter):
    # calculating loss as in the beginning of an epoch and storing it
    y_pred = forward(X)
    loss_SGD.append(criterion(y_pred, Y).tolist())
    for x, y in train_loader:
        # making a prediction in forward pass
        y_hat = forward(x)
        # calculating the loss between original and predicted data points
        loss = criterion(y_hat, y)
        # backward pass for computing the gradients of the loss w.r.t to learnable parameters
        loss.backward()
        # updating the parameters after each iteration
        w.data = w.data - step_size * w.grad.data
        b.data = b.data - step_size * b.grad.data
        # zeroing gradients after each iteration
        w.grad.data.zero_()
        b.grad.data.zero_()

train_loader_10 = DataLoader(dataset=dataset, batch_size=10)

# Reset w and b
w = torch.tensor(-10.0, requires_grad=True)
b = torch.tensor(-20.0, requires_grad=True)

loss_MBGD_10 = []

for i in range(n_iter):
    # calculating loss as in the beginning of an epoch and storing it
    y_pred = forward(X)
    loss_MBGD_10.append(criterion(y_pred, Y).tolist())
    for x, y in train_loader_10:
        # making a prediction in forward pass
        y_hat = forward(x)
        # calculating the loss between original and predicted data points
        loss = criterion(y_hat, y)
        # backward pass for computing the gradients of the loss w.r.t to learnable parameters
        loss.backward()
        # updating the parameters after each iteration
        w.data = w.data - step_size * w.grad.data
        b.data = b.data - step_size * b.grad.data
        # zeroing gradients after each iteration
        w.grad.data.zero_()
        b.grad.data.zero_()

train_loader_20 = DataLoader(dataset=dataset, batch_size=20)

# Reset w and b
w = torch.tensor(-10.0, requires_grad=True)
b = torch.tensor(-20.0, requires_grad=True)

loss_MBGD_20 = []

for i in range(n_iter):
    # calculating loss as in the beginning of an epoch and storing it
    y_pred = forward(X)
    loss_MBGD_20.append(criterion(y_pred, Y).tolist())
    for x, y in train_loader_20:
        # making a prediction in forward pass
        y_hat = forward(x)
        # calculating the loss between original and predicted data points
        loss = criterion(y_hat, y)
        # backward pass for computing the gradients of the loss w.r.t to learnable parameters
        loss.backward()
        # updating the parameters after each iteration
        w.data = w.data - step_size * w.grad.data
        b.data = b.data - step_size * b.grad.data
        # zeroing gradients after each iteration
        w.grad.data.zero_()
        b.grad.data.zero_()

plt.plot(loss_SGD,label="Stochastic Gradient Descent")
plt.plot(loss_MBGD_10,label="Mini-Batch-10 Gradient Descent")
plt.plot(loss_MBGD_20,label="Mini-Batch-20 Gradient Descent")
plt.xlabel('epoch')
plt.ylabel('Cost/total loss')
plt.legend()
plt.show()

总结

在本教程中,你了解了小批量梯度下降、DataLoader及其在 PyTorch 中的实现。特别是,你学到了:

  • 在 PyTorch 中实现小批量梯度下降。

  • PyTorch 中的 DataLoader 概念以及如何使用它加载数据。

  • 随机梯度下降与小批量梯度下降之间的区别。

  • 如何使用 PyTorch DataLoader 实现随机梯度下降。

  • 如何使用 PyTorch DataLoader 实现小批量梯度下降。

使用 PyTorch 中的多线性回归进行多目标预测

原文:machinelearningmastery.com/multi-target-predictions-with-multilinear-regression-in-pytorch/

在前几个教程中,我们使用了单输出的多线性回归,而在这里,我们将探讨如何利用多线性回归进行多目标预测。复杂的神经网络架构本质上是让每个神经元单元独立执行线性回归,然后将其结果传递给另一个神经元。因此,了解这种回归的工作原理对于理解神经网络如何执行多目标预测是很有用的。

本文的目标是提供 PyTorch 中多目标预测实现的逐步指南。我们将通过使用一个线性回归模型的框架来实现,该模型接受多个特征作为输入并生成多个结果。

我们将从导入模型所需的包开始。然后,我们将定义我们的输入数据点以及我们希望通过模型实现的目标。特别是,我们将演示:

  • 如何理解多维度的多线性回归。

  • 如何使用 PyTorch 中的多线性回归进行多目标预测。

  • 如何使用 PyTorch 中的 nn.Module 构建线性类。

  • 如何使用单个输入数据样本进行多目标预测。

  • 如何使用多个输入数据样本进行多目标预测。

注意,本教程中我们不会训练 MLR 模型,我们将仅查看它如何进行简单的预测。在 PyTorch 系列的后续教程中,我们将学习如何在数据集上训练这个模型。

使用我的书 Deep Learning with PyTorch 快速启动你的项目。它提供了自学教程可运行代码

让我们开始吧!

使用 PyTorch 中的多线性回归进行多目标预测。

图片由 Dan Gold 提供。保留所有权利。

概述

本教程分为三个部分;它们是

  • 创建模块

  • 使用简单输入样本进行预测

  • 使用多个输入样本进行预测

创建模块

我们将为多线性回归模型构建一个自定义线性类。我们将定义一个线性类,并使其成为 PyTorch 包 nn.Module 的子类。这个类继承了包中的所有方法和属性,例如 nn.Linear

import torch
torch.manual_seed(42)

# define the class for multilinear regression
class MLR(torch.nn.Module):
    def __init__(self, input_dim, output_dim):
        super().__init__()
        self.linear = torch.nn.Linear(input_dim, output_dim)
    def forward(self,x):
        y_pred = self.linear(x)
        return y_pred

现在,让我们创建模型对象并相应地定义参数。由于我们计划进行多目标预测,让我们首先检查模型在单个输入样本上的工作情况。之后,我们将对多个输入样本进行预测。

使用单输入样本进行预测

我们将创建我们的模型对象,它接受一个输入样本并进行五次预测。

...
# building the model object
model = MLR(1, 5)

现在,让我们定义模型的输入张量x并进行预测。

...
# define the single input sample 'x' and make predictions
x = torch.tensor([[2.0]])
y_pred = model(x)
print(y_pred)

下面是输出的样子。

tensor([[ 1.7309,  1.1732,  0.1187,  2.7188, -1.1718]],
       grad_fn=<AddmmBackward0>)

如你所见,我们的模型从仅一个输入样本中进行了多次预测。下面是我们如何列出模型参数的方法。

...
print(list(model.parameters()))

输出如下:

[Parameter containing:
 tensor([[ 0.7645],
         [ 0.8300],
         [-0.2343],
         [ 0.9186],
         [-0.2191]], requires_grad=True),
 Parameter containing:
 tensor([ 0.2018, -0.4869,  0.5873,  0.8815, -0.7336], requires_grad=True)]

你可能会得到不同的数字结果,因为这些是随机权重,但权重张量的形状会与我们设计的一致,即一个输入得到五个输出。

想要开始使用 PyTorch 进行深度学习吗?

现在就来我的免费电子邮件速成课程(包含示例代码)。

点击注册,同时获得免费的 PDF 电子书版本课程。

使用多个输入样本进行预测

类似地,我们定义一个张量X用于多个输入样本,其中每一行代表一个数据样本。

# define the multiple input tensor 'x' and make predictions
X = torch.tensor([[2.0],[4.0],[6.0]])

我们可以使用多个输入样本进行多目标预测。

...
Y_pred = model(X)
print(Y_pred)

由于我们有三个输入样本,我们应该看到三个输出样本,如下所示:

tensor([[ 1.7309,  1.1732,  0.1187,  2.7188, -1.1718],
        [ 3.2599,  2.8332, -0.3498,  4.5560, -1.6100],
        [ 4.7890,  4.4932, -0.8184,  6.3932, -2.0482]],
       grad_fn=<AddmmBackward0>)

将所有内容整合在一起,以下是完整代码:

import torch
torch.manual_seed(42)

# define the class for multilinear regression
class MLR(torch.nn.Module):
    def __init__(self, input_dim, output_dim):
        super().__init__()
        self.linear = torch.nn.Linear(input_dim, output_dim)
    def forward(self,x):
        y_pred = self.linear(x)
        return y_pred

# building the model object
model = MLR(1, 5)

# define the single input sample 'x' and make predictions
x = torch.tensor([[2.0]])
y_pred = model(x)
print(y_pred)
print(list(model.parameters()))

# define the multiple input tensor 'x' and make predictions
X = torch.tensor([[2.0],[4.0],[6.0]])
Y_pred = model(X)
print(Y_pred)

总结

在本教程中,你学习了如何使用多元线性回归模型进行多目标预测。特别是,你学到了:

  • 如何理解多维度的多元线性回归。

  • 如何使用 PyTorch 中的多元线性回归进行多目标预测。

  • 如何使用 PyTorch 中的‘nn.Module’构建线性分类。

  • 如何使用单个输入数据样本进行多目标预测。

  • 如何使用多个输入数据样本进行多目标预测。

使用更多隐藏神经元的神经网络

原文:machinelearningmastery.com/neural-network-with-more-hidden-neurons/

传统的神经网络模型称为多层感知器。它们通常由一系列相互连接的层组成。输入层是数据进入网络的地方,输出层是网络输出结果的地方。

输入层通常连接到一个或多个隐藏层,这些层在数据到达输出层之前修改和处理数据。隐藏层是神经网络如此强大的原因:它们可以学习对程序员来说可能难以在代码中指定的复杂函数。

在上一个教程中,我们构建了一个只有几个隐藏神经元的神经网络。在这里,您将通过添加更多隐藏神经元来实现一个神经网络。这将为我们估计更复杂的函数以适应数据。

在实现过程中,您将学到:

  • 如何在 PyTorch 中构建一个使用更多隐藏神经元的神经网络。

  • 如何通过在网络中添加更多隐藏神经元来估计复杂函数。

  • 如何在 PyTorch 中训练一个神经网络。

启动您的项目,使用我的书籍PyTorch 深度学习。它提供了带有实际代码自学教程

让我们开始吧!

使用更多隐藏神经元的神经网络。

图片由Kdwk Leung拍摄。部分权利保留。

概述

本教程分为三个部分,它们是:

  • 准备数据

  • 构建模型架构

  • 训练模型

准备数据

让我们构建一个扩展自 PyTorch 的Dataset类的Data类。您可以使用它来创建一个包含 100 个从50-505050范围内的合成值的数据集。x张量存储指定范围内的值,而y张量是一个形状与x相同的零张量。

接下来,您可以使用 for 循环根据x中的值设置xy张量的值。如果x中的值介于20-202020之间,则将y中对应的值设置为 1;如果x中的值介于30-3020-20之间或者介于20203030之间,则将y中对应的值设置为 0。类似地,如果x中的值介于40-4030-30之间或者介于30304040之间,则将y中对应的值设置为 1。否则,将y中对应的值设置为 0。

Data类中,__getitem__()方法被用来获取数据集中指定索引处的xy值。__len__()方法返回数据集的长度。有了这些,您可以使用data[i]获取数据集中的样本,并使用len(data)告知数据集的大小。此类可用于创建数据对象,该对象可传递给 PyTorch 数据加载器以训练机器学习模型。

请注意,您正在构建此复杂的数据对象以查看我们具有更多隐藏神经元的神经网络估计函数的情况。以下是数据对象代码的外观。

import torch
from torch.utils.data import Dataset, DataLoader

class Data(Dataset):
    def __init__(self):
        # Create tensor of 100 values from -50 to 50
        self.x = torch.zeros(100, 1)

        # Create tensor of zeros with the same shape as x
        self.y = torch.zeros(self.x.shape)

        # Set the values in x and y using a for loop
        for i in range(100):
            self.x[i] = -50 + i
            if self.x[i,0] > -20 and self.x[i,0] < 20:
                self.y[i] = 1
            elif (self.x[i,0] > -30 and self.x[i,0] < -20) or (self.x[i,0] > 20 and self.x[i,0] < 30):
                self.y[i] = 0
            elif (self.x[i,0] > -40 and self.x[i,0] < -30) or (self.x[i,0] > 30 and self.x[i,0] < 40):
                self.y[i] = 1
            else:
                self.y[i] = 0

        # Store the length of the dataset
        self.len = self.x.shape[0]

    def __getitem__(self, index):
        # Return the x and y values at the specified index
        return self.x[index], self.y[index]

    def __len__(self):
        # Return the length of the dataset
        return self.len

让我们实例化一个数据对象。

# Create the Data object
dataset = Data()

并编写一个函数来可视化这些数据,在以后训练模型时也会很有用。

import pandas as pd
import matplotlib.pyplot as plt

def plot_data(X, Y, model=None, leg=False):
    # Get the x and y values from the Data object
    x = dataset.x
    y = dataset.y

    # Convert the x and y values to a Pandas series with an index
    x = pd.Series(x[:, 0], index=range(len(x)))
    y = pd.Series(y[:, 0], index=range(len(y)))

    # Scatter plot of the x and y values, coloring the points by their labels
    plt.scatter(x, y, c=y)

    if model!=None:
        plt.plot(X.numpy(), model(X).detach().numpy(), label='Neural Net')

    # Show the plot
    plt.show()

如果您运行此函数,您将看到数据看起来像下面这样:

plot_data(dataset.x, dataset.y, leg=False)

想要开始使用 PyTorch 进行深度学习吗?

现在开始我的免费电子邮件崩溃课程(带有示例代码)。

单击注册,还可以获得课程的免费 PDF 电子书版本。

建立模型架构

下面,您将定义一个NeuralNetwork类,使用 PyTorch 中的nn.Module来构建自定义模型架构。该类表示一个简单的神经网络,包含一个输入层、一个隐藏层和一个输出层。

__init__()方法用于初始化神经网络,定义网络中的各层。forward方法用于定义网络的前向传播。在本例中,sigmoid 激活函数应用于输入层和输出层的输出。这意味着网络的输出将是一个介于 0 和 1 之间的值。

最后,您将创建NeuralNetwork类的一个实例,并将其存储在model变量中。该模型初始化为具有 1 个输入神经元的输入层,15 个隐藏神经元的隐藏层和 1 个输出神经元的输出层。现在,该模型已准备好在某些数据上进行训练。

import torch.nn as nn

# Define the Neural Network
class NeuralNetwork(nn.Module):
    def __init__(self, input_size, hidden_size, output_size):
        super().__init__()
        # Define the layers in the neural network
        self.input_layer = nn.Linear(input_size, hidden_size)
        self.output_layer = nn.Linear(hidden_size, output_size)

    def forward(self, x):
        # Define the forward pass through the network
        x = torch.sigmoid(self.input_layer(x))
        x = torch.sigmoid(self.output_layer(x))
        return x

# Initialize the Neural Network
model = NeuralNetwork(input_size=1, hidden_size=20, output_size=1)

训练模型

让我们定义准则、优化器和数据加载器。由于数据集是一个具有两类的分类问题,应使用二元交叉熵损失函数。使用 Adam 优化器,批量大小为 32。学习率设置为 0.01,决定了训练过程中模型权重的更新方式。损失函数用于评估模型性能,优化器更新权重,数据加载器将数据分成批次以进行高效处理。

learning_rate = 0.01
criterion = nn.BCELoss()
optimizer = torch.optim.Adam(model.parameters(), lr=learning_rate)
data_loader = DataLoader(dataset=dataset, batch_size=32)

现在,让我们构建一个包含 7000 个 epoch 的训练循环,并在训练过程中可视化结果。您将看到随着训练的进行,我们的模型如何估计数据点的情况。

n_epochs = 7000 # number of epochs to train the model
LOSS = [] # list to store the loss values after each epoch

# train the model for n_epochs
for epoch in range(n_epochs):
    total = 0 # variable to store the total loss for this epoch   
    # iterate over the data in the data loader
    for x, y in data_loader:
        # zero the gradients of the model
        optimizer.zero_grad()
        # make a prediction using the model
        yhat = model(x)
        # compute the loss between the predicted and true values
        loss = criterion(yhat, y)        
        # compute the gradients of the model with respect to the loss
        loss.backward()        
        # update the model parameters
        optimizer.step()        
        # add the loss value to the total loss for this epoch
        total += loss.item()        
    # after each epoch, check if the epoch number is divisible by 200
    if epoch % 1000 == 0:
        # if it is, plot the current data and model using the PlotData function
        plot_data(dataset.x, dataset.y, model)
        # print the current loss
        print(f"Epochs Done: {epoch+1}/{n_epochs}, Loss: {loss.item():.4f}")    
    # add the total loss for this epoch to the LOSS list
    LOSS.append(total)

当您运行此循环时,您将看到在第一个 epoch 中,神经网络对数据集建模较差,如下所示:

但随着训练的进行,准确性得到了改善。训练循环完成后,我们可以看到神经网络对数据的建模结果如下:

# plot after training loop ended
plot_data(dataset.x, dataset.y, model)

并且对应的损失指标历史可以如下绘制:

# create a plot of the loss over epochs
plt.figure()
plt.plot(LOSS)
plt.xlabel('epochs')
plt.ylabel('loss')
# show the plot
plt.show()

正如您所看到的,我们的模型相当好地估计了函数,但并非完美。例如,20 到 40 的输入范围并没有得到正确的预测。您可以尝试扩展网络以添加一个额外的层,例如以下内容,并查看是否会有所不同。

# Define the Neural Network
class NeuralNetwork(nn.Module):
    def __init__(self, input_size, hidden1_size, hidden2_size, output_size):
        super(NeuralNetwork, self).__init__()

        # Define the layers in the neural network
        self.layer1 = nn.Linear(input_size, hidden1_size)
        self.layer2 = nn.Linear(hidden1_size, hidden2_size)
        self.output_layer = nn.Linear(hidden2_size, output_size)

    def forward(self, x):
        # Define the forward pass through the network
        x = torch.sigmoid(self.layer1(x))
        x = torch.sigmoid(self.layer2(x))
        x = torch.sigmoid(self.output_layer(x))
        return x

# Initialize the Neural Network
model = NeuralNetwork(input_size=1, hidden1_size=10, hidden2_size=10, output_size=1)

将所有内容整合在一起,以下是完整的代码:

import torch.nn as nn
import pandas as pd
import matplotlib.pyplot as plt
import torch
from torch.utils.data import Dataset, DataLoader

class Data(Dataset):
    def __init__(self):
        # Create tensor of 100 values from -50 to 50
        self.x = torch.zeros(100, 1)

        # Create tensor of zeros with the same shape as x
        self.y = torch.zeros(self.x.shape)

        # Set the values in x and y using a for loop
        for i in range(100):
            self.x[i] = -50 + i
            if self.x[i,0] > -20 and self.x[i,0] < 20:
                self.y[i] = 1
            elif (self.x[i,0] > -30 and self.x[i,0] < -20) or (self.x[i,0] > 20 and self.x[i,0] < 30):
                self.y[i] = 0
            elif (self.x[i,0] > -40 and self.x[i,0] < -30) or (self.x[i,0] > 30 and self.x[i,0] < 40):
                self.y[i] = 1
            else:
                self.y[i] = 0

        # Store the length of the dataset
        self.len = self.x.shape[0]

    def __getitem__(self, index):
        # Return the x and y values at the specified index
        return self.x[index], self.y[index]

    def __len__(self):
        # Return the length of the dataset
        return self.len

# Create the Data object
dataset = Data()

def plot_data(X, Y, model=None, leg=False):
    # Get the x and y values from the Data object
    x = dataset.x
    y = dataset.y

    # Convert the x and y values to a Pandas series with an index
    x = pd.Series(x[:, 0], index=range(len(x)))
    y = pd.Series(y[:, 0], index=range(len(y)))

    # Scatter plot of the x and y values, coloring the points by their labels
    plt.scatter(x, y, c=y)

    if model!=None:
        plt.plot(X.numpy(), model(X).detach().numpy(), label='Neural Net')

    # Show the plot
    plt.show()

# Define the Neural Network
class NeuralNetwork(nn.Module):
    def __init__(self, input_size, hidden_size, output_size):
        super().__init__()
        # Define the layers in the neural network
        self.input_layer = nn.Linear(input_size, hidden_size)
        self.output_layer = nn.Linear(hidden_size, output_size)

    def forward(self, x):
        # Define the forward pass through the network
        x = torch.sigmoid(self.input_layer(x))
        x = torch.sigmoid(self.output_layer(x))
        return x

# Initialize the Neural Network
model = NeuralNetwork(input_size=1, hidden_size=20, output_size=1)

learning_rate = 0.01
criterion = nn.BCELoss()
optimizer = torch.optim.Adam(model.parameters(), lr=learning_rate)
data_loader = DataLoader(dataset=dataset, batch_size=32)

n_epochs = 7000 # number of epochs to train the model
LOSS = [] # list to store the loss values after each epoch

# train the model for n_epochs
for epoch in range(n_epochs):
    total = 0 # variable to store the total loss for this epoch   
    # iterate over the data in the data loader
    for x, y in data_loader:
        # zero the gradients of the model
        optimizer.zero_grad()
        # make a prediction using the model
        yhat = model(x)
        # compute the loss between the predicted and true values
        loss = criterion(yhat, y)        
        # compute the gradients of the model with respect to the loss
        loss.backward()        
        # update the model parameters
        optimizer.step()        
        # add the loss value to the total loss for this epoch
        total += loss.item()        
    # after each epoch, check if the epoch number is divisible by 200
    if epoch % 1000 == 0:
        # if it is, plot the current data and model using the PlotData function
        plot_data(dataset.x, dataset.y, model)
        # print the current loss
        print(f"Epochs Done: {epoch+1}/{n_epochs}, Loss: {loss.item():.4f}")    
    # add the total loss for this epoch to the LOSS list
    LOSS.append(total)

plot_data(dataset.x, dataset.y, model)

# create a plot of the loss over epochs
plt.figure()
plt.plot(LOSS)
plt.xlabel('epochs')
plt.ylabel('loss')
# show the plot
plt.show()

总结

在本教程中,你学习了如何通过向神经网络中引入更多的神经元来估计复杂函数。特别地,你学习了:

  • 如何在 PyTorch 中构建一个具有更多隐藏神经元的神经网络。

  • 如何通过向网络中添加更多隐藏神经元来估计复杂函数。

  • 如何在 PyTorch 中训练神经网络。