PyTorch的工作流程总览:
1.准备好数据
2.建立模型
3.将模型拟合到数据(训练)
4.做出预测和评估模型(推理)
5.保存和加载模型
import torch
from torch import nn
import matplotlib.pyplot as plt
# 显示我自己pytorch的版本号
torch.__version__
'1.7.1+cpu'
机器学习是一个由两部分组成的:
1.将您的数据(无论它是什么)转换为数字(表示)。
2.选择或构建模型以尽可能最好地学习表示。
# 创建已知的参数
weight = 0.7
bias = 0.3
# 创建数据
start = 0
end = 1
step = 0.02
X = torch.arange(start, end, step).unsqueeze(dim=1)
y = weight * X + bias
X[:10], y[:10],X.shape,y.shape
(tensor([[0.0000],
[0.0200],
[0.0400],
[0.0600],
[0.0800],
[0.1000],
[0.1200],
[0.1400],
[0.1600],
[0.1800]]),
tensor([[0.3000],
[0.3140],
[0.3280],
[0.3420],
[0.3560],
[0.3700],
[0.3840],
[0.3980],
[0.4120],
[0.4260]]),
torch.Size([50, 1]),
torch.Size([50, 1]))
# 现在我们将着手构建一个可以学习X(特征)和y(标签)之间关系模型
将数据分为: 数据总量 使用频率
1.训练集:模型从这些数据中学习(比如你在学期中学习的课程) 60—80% 总是
2.验证集:该模型会根据这些数据进行调整(例如您在期末考试前参加的练习考试) 10—20% 经常但不总是
3.测试集:模型会根据这些数据进行评估,以测试它学到了什么(比如你在学期末参加的期末考试) 10—20% 总是
现在,我们将使用一个训练集和测试集,这意味着我们将有一个数据集供我们的模型学习和评估。
在处理真实数据时,此步骤通常在项目开始时完成(测试集应始终与所有其他数据分开)。
我们希望我们的模型在训练数据上学习,然后在测试数据上对其进行评估,以了解它对未见示例的泛化程度。
# 分开创建训练集和测试集。
train_split = int(0.8 * len(X)) # 百分之80的数据被用作训练集,
X_train, y_train = X[:train_split], y[:train_split]
X_test, y_test = X[train_split:], y[train_split:]#百分之20的数据被用作测试集
len(X_train), len(y_train), len(X_test), len(y_test)
(40, 40, 10, 10)
我们有 40 个训练样本(X_train& y_train)和 10 个测试样本(X_test& y_test)
X_train我们创建的模型将尝试学习&之间的关系y_train,然后我们将评估它在X_test和上学习的内容y_test。
但现在我们的数据只是页面上的数字。
让我们创建一个函数来可视化它。
def plot_predictions(train_data=X_train,
train_labels=y_train,
test_data=X_test,
test_labels=y_test,
predictions=None):
"""
Plots training data, test data and compares predictions.
"""
plt.figure(figsize=(10, 7))
# Plot training data in blue
plt.scatter(train_data, train_labels, c="b", s=4, label="Training data")
# Plot test data in green
plt.scatter(test_data, test_labels, c="g", s=4, label="Testing data")
if predictions is not None:
# Plot the predictions in red (predictions were made on the test data)
plt.scatter(test_data, predictions, c="r", s=4, label="Predictions")
# Show the legend
plt.legend(prop={"size": 14});
def plot_predictions(train_data=X_train,
train_labels=y_train,
test_data=X_test,
test_labels=y_test,
predictions=None):
plt.figure(figsize=(10, 7))
# 用蓝色绘制训练数据集
plt.scatter(train_data, train_labels, c="b", s=4, label="Training data")
# 用绿色绘制测试数据集
plt.scatter(test_data, test_labels, c="g", s=4, label="Testing data")
if predictions is not None:
# 用红色绘制预测数据集(predictions were made on the test data)
plt.scatter(test_data, predictions, c="r", s=4, label="Predictions")
# Show the legend
plt.legend(prop={"size": 14});
plot_predictions();
class LinearRegressionModel(nn.Module): #
def __init__(self):
super().__init__()
self.weights = nn.Parameter(torch.randn(1, # <- 以随机权重开始(这将随着模型的学习而调整))
requires_grad=True, # <- 我们可以用梯度下降法来更新这个值吗?
dtype=torch.float # <- PyTorch 默认float32
))
self.bias = nn.Parameter(torch.randn(1, # <- 以随机偏差开始(这将随着模型的学习而得到调整)
requires_grad=True, # <- 我们可以用梯度下降法来更新这个值吗?
dtype=torch.float # <- PyTorch 默认float32
))
# 前向义了模型中的计算
def forward(self, x: torch.Tensor) -> torch.Tensor: # <- "x "是输入数据(如训练/测试特征)
return self.weights * x + self.bias
# Pytorch模型构建要点
1、torch.nn 包含计算图的所有构建块(本质上是以特定方式执行的一系列计算)。
2、torch.nn.Parameter 存储可以与 一起使用的张量nn.Module。
如果requires_grad=True梯度(用于通过梯度下降更新模型参数)是自动计算的,这通常被称为“autograd”。
3、torch.nn.Module 所有神经网络模块的基类,神经网络的所有构建块都是子类。
如果你在 PyTorch 中构建神经网络,你的模型应该子类化nn.Module. 需要forward()实现一个方法。
4、torch.optim 包含各种优化算法(这些告诉模型中存储的参数nn.Parameter如何最好地改变以改善梯度下降,进而减少损失)。
5、def forward() 所有nn.Module子类都需要一个forward()方法,
这定义了将在传递给特定数据的数据上发生的计算nn.Module(例如上面的线性回归公式)。
# Pytorch模型的内容
现在我们已经解决了这些问题,让我们使用我们创建的类创建一个模型实例,并使用.parameters().
torch.manual_seed(42)
model_0 = LinearRegressionModel()
list(model_0.parameters())
[Parameter containing:
tensor([0.3367], requires_grad=True),
Parameter containing:
tensor([0.1288], requires_grad=True)]
练习:
尝试改变torch.manual_seed()上面两个单元格的值,看看权重和偏差值会发生什么变化。
因为我们的模型从随机值开始,所以现在它的预测能力很差。
# 我们还可以使用 获取模型的状态
model_0.state_dict()
OrderedDict([('weights', tensor([0.3367])), ('bias', tensor([0.1288]))])
使用预测torch.inference_mode()
为了检查这一点,我们可以将测试数据传递给它X_test,看看它的预测有多接近y_test。
with torch.no_grad(): #在更高的版本中可以使用torch.no_grad() 低版本中是torch.inference_model():
y_preds = model_0(X_test)
print(f"Number of testing samples: {len(X_test)}")
print(f"Number of predictions made: {len(y_preds)}")
print(f"Predicted values:\n{y_preds}")
Number of testing samples: 10
Number of predictions made: 10
Predicted values:
tensor([[0.3982],
[0.4049],
[0.4116],
[0.4184],
[0.4251],
[0.4318],
[0.4386],
[0.4453],
[0.4520],
[0.4588]])
注意每个测试样本有一个预测值。
这是因为我们使用的数据类型。对于我们的直线,一个X值映射到一个y值。
但是,机器学习模型非常灵活。您可以将 100 个X值映射到一个、两个、三个或 10 个y值。这完全取决于你在做什么。
我们的预测仍然是页面上的数字,让我们plot_predictions()使用我们在上面创建的函数将它们可视化。
y_test-y_preds
tensor([[0.4618],
[0.4691],
[0.4764],
[0.4836],
[0.4909],
[0.4982],
[0.5054],
[0.5127],
[0.5200],
[0.5272]])
这些预测看起来很糟糕......
这是有道理的,但当您记得我们的模型只是使用随机参数值进行预测时。
现在我们的模型正在使用随机参数进行预测以进行计算,它基本上是(随机)猜测。
为了解决这个问题,我们可以更新它的内部参数(我也将参数称为模式)、我们随机设置的值weights和值,以更好地代表数据。biasnn.Parameter()torch.randn()
我们可以对此进行硬编码(因为我们知道默认值weight=0.7和bias=0.3),但其中的乐趣在哪里?
很多时候,您不知道模型的理想参数是什么。
相反,编写代码以查看模型是否可以尝试自行解决它们会更有趣。
1.损失函数:
衡量你的模型预测与真实标签相比错误程度,越低越好, 位置:在torch.nn, 回归问题的平均绝对误差(MAE)torch.nn.L1Loss(),
二元分类问题的二元交叉熵(torch.nn.BCELoss)
2.优化器:
告诉你的模型如何更新其内部参数最好地降低损失,位置:在torch.optim, 随机梯度下降(torch.optim.SGD),Adam优化器(torch.optim.Adam())
让我们创建一个损失函数和一个优化器,我们可以使用它来帮助改进我们的模型。
根据您正在处理的问题类型将取决于您使用的损失函数和优化器。
但是,也有一些常见的值,已知它们可以很好地工作,例如 SGD(随机梯度下降)或 Adam 优化器。
以及用于回归问题(预测数字)的 MAE(平均绝对误差)损失函数或用于分类问题(预测一件事或另一件事)的二元交叉熵损失函数。
对于我们的问题,因为我们要预测一个数字,所以让我们使用torch.nn.L1Loss()PyTorch 中的 MAE(位于 下)作为我们的损失函数。
# 创建一个损失函数
loss_fn = nn.L1Loss() # MAE loss is same as L1Loss
# 创建一个优化器
optimizer = torch.optim.SGD(params=model_0.parameters(), # parameters of target model to optimize
lr=0.01) # learning rate
1.前向传播
2.计算损失
3.优化梯度
4.反向传播
5.更新优化器
1.forward pass
该模型遍历所有的训练数据,执行其forward()函数计算 代码示例:model(x_train)
2.calculate the loss
将模型的输出(预测)与基本事实进行比较并评估它们的错误程度。 代码示例:loss = loss_fn(y_pred,y_train)
3.optimizer zero grad
优化器梯度设置为零(默认情况下会累积),因此可以针对特定的训练步骤重新计算它们。 代码示例:optimizer.zero_grad()
4.loss backward
计算每个要更新的模型参数(每个参数带有requires_grad=True)的损失梯度。这被称为反向传播,因此是“向后”。代码示例:loss.backward()
5.update optimizetion
更新requires_grad=True关于损失梯度的参数以改进它们。optimizer.step()
torch.manual_seed(42)
epochs = 100
train_loss_values = []
test_loss_values = []
epoch_count = []
for epoch in range(epochs):
### Training
# Put model in training mode (this is the default state of a model)
model_0.train()
# 1. Forward pass on train data using the forward() method inside
y_pred = model_0(X_train)
# print(y_pred)
# 2. Calculate the loss (how different are our models predictions to the ground truth)
loss = loss_fn(y_pred, y_train)
# 3. Zero grad of the optimizer
optimizer.zero_grad()
# 4. Loss backwards
loss.backward()
# 5. Progress the optimizer
optimizer.step()
### Testing
# Put the model in evaluation mode
model_0.eval()
with torch.no_grad():
# 1. Forward pass on test data
test_pred = model_0(X_test)
# 2. Caculate loss on test data
test_loss = loss_fn(test_pred, y_test.type(torch.float)) # predictions come in torch.float datatype, so comparisons need to be done with tensors of the same type
# Print out what's happening
if epoch % 10 == 0:
epoch_count.append(epoch)
train_loss_values.append(loss.detach().numpy())
test_loss_values.append(test_loss.detach().numpy())
print(f"Epoch: {epoch} | MAE Train Loss: {loss} | MAE Test Loss: {test_loss} ")
Epoch: 0 | MAE Train Loss: 0.31288138031959534 | MAE Test Loss: 0.48106518387794495
Epoch: 10 | MAE Train Loss: 0.1976713240146637 | MAE Test Loss: 0.3463551998138428
Epoch: 20 | MAE Train Loss: 0.08908725529909134 | MAE Test Loss: 0.21729660034179688
Epoch: 30 | MAE Train Loss: 0.053148526698350906 | MAE Test Loss: 0.14464017748832703
Epoch: 40 | MAE Train Loss: 0.04543796554207802 | MAE Test Loss: 0.11360953003168106
Epoch: 50 | MAE Train Loss: 0.04167863354086876 | MAE Test Loss: 0.09919948130846024
Epoch: 60 | MAE Train Loss: 0.03818932920694351 | MAE Test Loss: 0.08886633068323135
Epoch: 70 | MAE Train Loss: 0.03476089984178543 | MAE Test Loss: 0.0805937647819519
Epoch: 80 | MAE Train Loss: 0.03132382780313492 | MAE Test Loss: 0.07232122868299484
Epoch: 90 | MAE Train Loss: 0.02788739837706089 | MAE Test Loss: 0.06473556160926819
# 绘制损失函数曲线
plt.plot(epoch_count, train_loss_values, label="Train loss")
plt.plot(epoch_count, test_loss_values, label="Test loss")
plt.title("Training and test loss curves")
plt.ylabel("Loss")
plt.xlabel("Epochs")
plt.legend();
好的!损失曲线显示损失随着时间的推移而下降。请记住,损失是衡量模型错误程度的指标,因此越低越好。
但是为什么损失会下降呢?
好吧,由于我们的损失函数和优化器,模型的内部参数 (weights和bias) 得到了更新,以更好地反映数据中的潜在模式。
让我们检查一下我们的模型.state_dict(),看看我们的模型与我们为权重和偏差设置的原始值有多接近。
# 展示我们的模型学习参数
print("The model learned the following values for weights and bias:")
print(model_0.state_dict())
print("\nAnd the original values for weights and bias are:")
print(f"weights: {weight}, bias: {bias}")
The model learned the following values for weights and bias:
OrderedDict([('weights', tensor([0.5784])), ('bias', tensor([0.3513]))])
And the original values for weights and bias are:
weights: 0.7, bias: 0.3
使用 PyTorch 模型进行预测(也称为执行推理)时,需要记住三件事:
1.将模型设置为评估模式 ( model.eval())。
2.使用推理模式上下文管理器 ( ) 进行预测with torch.inference_mode(): ...。
3.所有预测都应使用同一设备上的对象进行(例如,仅在 GPU 上的数据和模型或仅在 CPU 上的数据和模型)。
# 1. 用训练好的模型来评估
model_0.eval()
# 2. Setup the inference mode context manager
with torch.no_grad():
# 3. Make sure the calculations are done with the model and data on the same device
# in our case, we haven't setup device-agnostic code yet so our data and model are
# on the CPU by default.
# model_0.to(device)
# X_test = X_test.to(device)
y_preds = model_0(X_test)
y_preds
tensor([[0.8141],
[0.8256],
[0.8372],
[0.8488],
[0.8603],
[0.8719],
[0.8835],
[0.8950],
[0.9066],
[0.9182]])
# 好的!我们已经用训练有素的模型做了一些预测,现在它们看起来如何?
plot_predictions(predictions=y_preds)
# 对于在 PyTorch 中保存和加载模型,您应该了解三种主要方法
1.torch.save
2.torch.load
3.torch.nn.Module.load_state_dict
更推荐的方法是保存和加载模型的state_dict()
步骤:
1.我们将创建一个目录,用于保存模型以models使用 Python中的pathlib模块调用
2.我们将创建一个文件路径来保存模型。
3.我们将调用torch.save(obj, f) obj是目标模型state_dict(),并且f是保存模型的文件名
一般命名结尾是以.pt,或者.pth,举例:saved_model_01.pth.
from pathlib import Path
# 1. 创建一个模型文件夹
MODEL_PATH = Path("models")
MODEL_PATH.mkdir(parents=True, exist_ok=True)
# 2. 创建文件保存路径
MODEL_NAME = "01_pytorch_workflow_model_0.pth"
MODEL_SAVE_PATH = MODEL_PATH / MODEL_NAME
# 3. 保存模型的状态参数
print(f"Saving model to: {MODEL_SAVE_PATH}")
torch.save(obj=model_0.state_dict(), # only saving the state_dict() only saves the models learned parameters
f=MODEL_SAVE_PATH)
Saving model to: models\01_pytorch_workflow_model_0.pth
# 由于我们现在保存的是一个模型的state_dict(),它是一个学习参数的字典
用torch.nn.Module.load_state_dict(torch.load(f))
loaded_model_0 = LinearRegressionModel()
# Load the state_dict of our saved model (this will update the new instance of our model with trained weights)
loaded_model_0.load_state_dict(torch.load(f=MODEL_SAVE_PATH))
# pytorch的推理规则
# 1.把加载的模型放到评估模式中
loaded_model_0.eval()
# 2. 使用推理模式的上下文管理器进行预测
with torch.no_grad():
loaded_model_preds = loaded_model_0(X_test)
y_preds == loaded_model_preds
tensor([[True],
[True],
[True],
[True],
[True],
[True],
[True],
[True],
[True],
[True]])
看起来加载的模型预测与之前的模型预测(保存前的预测)相同。这表明我们的模型正在按预期保存和加载。
torch.manual_seed(42)
# 设置epochs的数量
epochs = 1000
# Put data on the available device
# Without this, error will happen (not all model/data on device)
X_train = X_train.to(device)
X_test = X_test.to(device)
y_train = y_train.to(device)
y_test = y_test.to(device)
for epoch in range(epochs):
#训练
model_1.train() # train mode is on by default after construction
# 1. 前向传播
y_pred = model_1(X_train)
# 2. 计算损失
loss = loss_fn(y_pred, y_train)
# 3. 梯度归零优化
optimizer.zero_grad()
# 4. 反向传播
loss.backward()
# 5. 一步步的更新参数
optimizer.step()
#评估
model_1.eval() # put the model in evaluation mode for testing (inference)
# 1. Forward pass
with torch.no_grad():
test_pred = model_1(X_test)
# 2. Calculate the loss
test_loss = loss_fn(test_pred, y_test)
if epoch % 100 == 0:
print(f"Epoch: {epoch} | Train loss: {loss} | Test loss: {test_loss}")
Epoch: 0 | Train loss: 0.5551779866218567 | Test loss: 0.5739762187004089
Epoch: 100 | Train loss: 0.006215679459273815 | Test loss: 0.014086711220443249
Epoch: 200 | Train loss: 0.0012645035749301314 | Test loss: 0.013801807537674904
Epoch: 300 | Train loss: 0.0012645035749301314 | Test loss: 0.013801807537674904
Epoch: 400 | Train loss: 0.0012645035749301314 | Test loss: 0.013801807537674904
Epoch: 500 | Train loss: 0.0012645035749301314 | Test loss: 0.013801807537674904
Epoch: 600 | Train loss: 0.0012645035749301314 | Test loss: 0.013801807537674904
Epoch: 700 | Train loss: 0.0012645035749301314 | Test loss: 0.013801807537674904
Epoch: 800 | Train loss: 0.0012645035749301314 | Test loss: 0.013801807537674904
Epoch: 900 | Train loss: 0.0012645035749301314 | Test loss: 0.013801807537674904
from pprint import pprint # pprint = pretty print, see: https://docs.python.org/3/library/pprint.html
print("The model learned the following values for weights and bias:")
pprint(model_1.state_dict())
print("\nAnd the original values for weights and bias are:")
print(f"weights: {weight}, bias: {bias}")
The model learned the following values for weights and bias:
OrderedDict([('linear_layer.weight', tensor([[0.6968]])),
('linear_layer.bias', tensor([0.3025]))])
And the original values for weights and bias are:
weights: 0.7, bias: 0.3
# 将模型转为评估模式
model_1.eval()
# 在测试集上做预测
with torch.no_grad():
y_preds = model_1(X_test)
y_preds
tensor([[0.8600],
[0.8739],
[0.8878],
[0.9018],
[0.9157],
[0.9296],
[0.9436],
[0.9575],
[0.9714],
[0.9854]])
# 很多数据科学库例如(pandas numpy matplotlib)无法使用存储在GPU上的数据,在尝试使用这些库之一中的函数时。
# 我们可以使用调用.cpu()目标张量以在CPU上返回目标张量的副本。
plot_predictions(predictions=y_preds.cpu())
from pathlib import Path
# 1. 创建模型文件夹
MODEL_PATH = Path("models")
MODEL_PATH.mkdir(parents=True, exist_ok=True)
# 2. 创建模型保存路径
MODEL_NAME = "01_pytorch_workflow_model_1.pth"
MODEL_SAVE_PATH = MODEL_PATH / MODEL_NAME
# 3. 保存模型状态字典
print(f"Saving model to: {MODEL_SAVE_PATH}")
torch.save(obj=model_1.state_dict(), # 只保存它的state_dict() 只保存模型的学习参数。
f=MODEL_SAVE_PATH)
Saving model to: models\01_pytorch_workflow_model_1.pth
loaded_model_1 = LinearRegressionModelV2()
# 加载模型的参数字典
loaded_model_1.load_state_dict(torch.load(MODEL_SAVE_PATH))
# 将模型放入目标设备(假如你的数据在GPU, 你的模型也必须在 GPU 中去做预测)
loaded_model_1.to(device)
print(f"Loaded model:\n{loaded_model_1}")
print(f"Model on device:\n{next(loaded_model_1.parameters()).device}")
Loaded model:
LinearRegressionModelV2(
(linear_layer): Linear(in_features=1, out_features=1, bias=True)
)
Model on device:
cpu
loaded_model_1.eval()
with torch.no_grad():
loaded_model_1_preds = loaded_model_1(X_test)
y_preds == loaded_model_1_preds
tensor([[True],
[True],
[True],
[True],
[True],
[True],
[True],
[True],
[True],
[True]])
https://pytorch-cn.readthedocs.io/zh/latest/
1.torch.nn.Parameters(模块参数)
2.torch.nn.Contain(模块容器)
3.torch.nn.Conv1d(卷积层)
4.toch.nn.MaxPool1d(池化层)
5.torch.nn.激活函数
6.torch.nn.BatchNorm1d,()#批归一化
7.torch.nn.RNN(),torch.nn.LSTM(),torch.nn.GRU(),torch.nn.RNNCell(),torch.nn.LSTMCell(),torch.nn.GRUCell()#
8.torch.nn.Linear(每个输入样本的大小,每个输出样本的大小,bias:True,就是学习偏置,False:这层不会学习偏置。
# 丢弃层
9.torch.nn.Dropout(p=0.5,inplace=False)#p是将元素置为0的概率,inplace:True,会在原地执行操作。
# 距离函数
10.torch.nn.PairwiseDistance(p=2,eps=1e-06)#按照批次计算向量V1,V2之间的距离。
# 损失函数
11. torch.L1Loss(),#创建一个衡量输入x(模型预测输出)和目标y之间差的绝对值的平均值的标准
torch.nn.MSELoss(),#创建一个衡量输入x(模型预测输出)和目标y之间均方误差标准。
torch.nn.CrossEntropyLoss(),#当训练一个多类分类器的时候
torch.nn.NLLLoss(),#负的log likelihood loss损失。用于训练一个n类分类器。
torch.nn.KLDivLoss(),#KL 散度损失,KL散度常用来描述两个分布的距离,并在输出分布的空间上执行直接回归是有用的。
torch.nn.BCELoss(),#
torch.nn.MarginRankingLoss(),
torch.nn.HingeEmbeddingLoss(),#通常用来测量两个输入是否相似
# 多设备并行计算
12.
net = torch.nn.DataParallel(model,device_ids=[0,1,2])
output = net(input_var)