pytorch工作流

初学机器学习,从线性回归入手,了解机器学习的基本工作流程,从而进一步理解深度学习模型训练的本质。本博文主要用于整理个人的知识框架,希望也能帮到大家。如有不足,欢迎留言。🙏

Pytorch工作流

运行环境:https://colab.google/

将一般Pytorch机器学习的工作流程划分为:数据准备建立模型训练模型预测这四个部分

数据准备

准备数据集

1
2
3
4
5
6
7
8
9
10
11
import torch
# y = 0.7 * x + 0.3 (一系列散点)
weight = 0.7
bias = 0.3

# Create data
start = 0
end = 1
step = 0.02
X = torch.arange(start,end, step)
y = weight * X +bias
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
X # -->表示单步运行结果
-->tensor([0.0000, 0.0200, 0.0400, 0.0600, 0.0800, 0.1000, 0.1200, 0.1400, 0.1600,
0.1800, 0.2000, 0.2200, 0.2400, 0.2600, 0.2800, 0.3000, 0.3200, 0.3400,
0.3600, 0.3800, 0.4000, 0.4200, 0.4400, 0.4600, 0.4800, 0.5000, 0.5200,
0.5400, 0.5600, 0.5800, 0.6000, 0.6200, 0.6400, 0.6600, 0.6800, 0.7000,
0.7200, 0.7400, 0.7600, 0.7800, 0.8000, 0.8200, 0.8400, 0.8600, 0.8800,
0.9000, 0.9200, 0.9400, 0.9600, 0.9800])

y
-->tensor([0.3000, 0.3140, 0.3280, 0.3420, 0.3560, 0.3700, 0.3840, 0.3980, 0.4120,
0.4260, 0.4400, 0.4540, 0.4680, 0.4820, 0.4960, 0.5100, 0.5240, 0.5380,
0.5520, 0.5660, 0.5800, 0.5940, 0.6080, 0.6220, 0.6360, 0.6500, 0.6640,
0.6780, 0.6920, 0.7060, 0.7200, 0.7340, 0.7480, 0.7620, 0.7760, 0.7900,
0.8040, 0.8180, 0.8320, 0.8460, 0.8600, 0.8740, 0.8880, 0.9020, 0.9160,
0.9300, 0.9440, 0.9580, 0.9720, 0.9860])

划分数据集

1
2
3
4
5
6
7
# train/test split
train_split = int(0.8 * len(X)) # 8:2的划分
X_train, y_train = X[:train_split], y[:train_split] #左闭右开
X_test, y_test = X[train_split:], y[train_split:]

len(X_train), len(y_train), len(X_test), len(y_test)
-->(40, 40, 10, 10)

可视化

1
2
3
4
5
6
7
8
9
10
11
import matplotlib.pyplot as plt #import as 为引入模块并起别名
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") # s = 4, 为指定散点的直径,大约为4个点
plt.scatter(test_data, test_labels, c = "g", s = 4, label = "Test Data")

if predictions is not None:
plt.scatter(test_data, predictions, c= "r", s = 4, label = "Prediction")
plt.legend(prop = {"size":14}) # 图例的属性,其中 size 参数指定了图例中文本的大小为 14 磅

plot_predictions()
1
2
plot_predictions()
-->

download

建立模型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from torch import nn

class LinearRegressionModel(nn.Module): #这个类继承自 nn.Module 类,这是 PyTorch 中定义神经网络模型的基类。
def __init__(self):
super().__init__()
# nn.Parameter 是 PyTorch 中的一个类,用于将张量(tensor)封装为可学习的模型参数,
# 这些参数将被自动地添加到模型的参数列表中,并能够被优化器识别和更新,并使得其具有梯度计算和自动求导的功能。
# 在神经网络中,模型参数通常表示为权重和偏置,它们需要在训练过程中通过梯度下降等优化算法进行更新。
self.weights = nn.Parameter(torch.randn(1, dtype = torch.float), requires_grad = True) #requires_grad = true 表示用于梯度计算,以便在训练过程中更新它们的值。
self.bias = nn.Parameter(torch.randn(1,dtype = torch.float), requires_grad = True)
# 我们定义了两个参数:权重(weights)和偏置(bias),它们都是可学习的模型参数,用于线性回归模型中的线性部分。
# 这些参数通过 nn.Parameter 函数封装为模型参数,从而使得它们可以被优化器更新。
def forward(self, x:torch.tensor): #x:torch.tensor标识注释(免责声明), 不强制,也可以接受其他类型, ->torch.tensor也表示注释,意为最后得到的数据类型
return self.weights * x + self.bias
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
torch.manual_seed(666) # 做个种子,保证伪随机的一致性
model_0 = LinearRegressionModel() # model_0 赋为一个具有相应类属性的对象
# 注意model_0没有在训练模型处声明,所以每次调参训练结果会追加。所以在google colab中每次调参重新训练,应该从这里开始。

model_0.parameters()
# model_0.parameters() 用于获取神经网络模型 model_0 中的所有模型参数。
# 在 PyTorch 中,模型参数通常是由 nn.Parameter 类型的对象表示的,它们存储了模型的权重和偏置等可学习的参数。
# model_0.parameters() 返回一个迭代器,通过迭代器可以依次访问模型中的每一个参数。这个迭代器通常用于将模型的参数传递给优化器,以便在训练过程中更新参数。
--><generator object Module.parameters at 0x7a74a42ae7a0>


# 如果想print一个东西,但是打印出类似于<generator object Module.parameters at 0x79fc67df00b0>
# 则强制类型转换为list
list(model_0.parameters())
-->[Parameter containing:
tensor([-2.1188], requires_grad=True),
Parameter containing:
tensor([0.0635], requires_grad=True)]


model_0.state_dict() # 另一种得到他的参数的方式 用于返回模型的参数字典(state dictionary)。这个字典包含了模型中所有可学习参数的名称及其对应的张量值。
-->OrderedDict([('weights', tensor([-2.1188])), ('bias', tensor([0.0635]))])

训练模型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
### 训练一个模型

### loss function ,损失函数值,越大,数据越乱。用于度量y_pred和y_test之间的差距
# MAE 3 4 5; 2 6 4 = 1.33
# MSE 3 4 5; 2 6 4 = 2
# 两种比较方式,每种参数只和自己比

### optimizer ,告诉我们的模型怎样朝向正确的参数走过去
"""
由于google colab是单步运行,所以每次调参后,应该从建立model_0处重新向后运行
"""

loss_fn = nn.L1Loss() #MAE,
# nn.L1Loss() 是 PyTorch 中的一个损失函数,用于计算预测值和目标值之间的平均绝对误差(Mean Absolute Error,MAE)。

# torch.optim.SGD: 这是 PyTorch 中的随机梯度下降优化器的类。它实现了随机梯度下降算法,用于更新模型的参数以最小化损失函数。这个优化器需要指定要优化的参数和学习率等超参数。
optimizer = torch.optim.SGD(params = model_0.parameters(), lr = 0.1) #lr 怎么调(跑了一个模型后观察一下,曲线平,则加大10倍),一般0.01、0.1没问题
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
epochs = 100

train_loss_values = []
test_loss_values = []
epoch_count = []

for epoch in range(epochs):

# Put model in training mode:
model_0.train()

# 1. forward pass, using forward function
y_pred = model_0(X_train)

# 2. Calculate loss
loss = loss_fn(y_pred, y_train)

# 3. zero_grad 在每一次迭代中,我们都需要在反向传播之前将梯度归零,
#这是因为 PyTorch 默认会在进行梯度计算时,将新计算得到的梯度累加到已存在的梯度上,
#而我们希望每次迭代都是独立的。因此,使用 optimizer.zero_grad() 就可以很方便地实现这一步骤。
optimizer.zero_grad()

# 4. back prop 梯度计算
loss.backward()

# 5. Progress optimizer 更新梯度
optimizer.step()

# 6. 调用 model_0.eval() 将模型切换到评估模式(不会梯度计算)。
model_0.eval()

with torch.inference_mode():
test_pred = model_0(X_test)
epoch_count.append(epoch)
test_loss = loss_fn(test_pred, y_test.type(torch.float))
train_loss_values.append(loss.detach().numpy()) # loss.detach().numpy() 将 PyTorch 张量转换为 NumPy 数组时,通常是为了利用 NumPy 提供的丰富功能进行数据处理、分析或者可视化。
test_loss_values.append(test_loss.detach().numpy())

print(f"Epoch:{epoch} | Train Loss:{loss} | Test Loss:{test_loss}")

-->Epoch:0 | Train Loss:1.3358044624328613 | Test Loss:2.610489845275879
Epoch:1 | Train Loss:1.2205944061279297 | Test Loss:2.4757800102233887
Epoch:2 | Train Loss:1.1053844690322876 | Test Loss:2.3410699367523193
Epoch:3 | Train Loss:0.9938250780105591 | Test Loss:2.216449022293091
Epoch:4 | Train Loss:0.9000433683395386 | Test Loss:2.097005844116211
Epoch:5 | Train Loss:0.8182994723320007 | Test Loss:1.9881856441497803
Epoch:6 | Train Loss:0.7505138516426086 | Test Loss:1.8903448581695557
......
1
2
3
4
5
6
7
8
9
# 绘图
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()

pytorch_工作流2

预测

1
2
3
4
5
6
7
8
9
10
11
12
model_0.eval() #评价模式

with torch.inference_mode(): #做预测
y_preds = model_0(X_test)

y_preds
-->tensor([0.5704, 0.5693, 0.5683, 0.5672, 0.5661, 0.5650, 0.5639, 0.5629, 0.5618,
0.5607])
model_0.state_dict()
-->OrderedDict([('weights', tensor([-0.0539])), ('bias', tensor([0.6135]))])

plot_predictions(predictions = y_preds)

pytorch_工作流3

保存模型与数据读取

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 保存模型到google 云端硬盘
from pathlib import Path
MODEL_PATH = Path("models")

MODEL_PATH.mkdir(parents = True, exist_ok = True)
MODEL_NAME = "pytorch_workflow_model_0.pth"
MODEL_SAVE_PATH = MODEL_PATH / MODEL_NAME
MODEL_SAVE_PATH

torch.save(obj = model_0.state_dict(), f = MODEL_SAVE_PATH)


# 读取数据至新模型
loaded_model_0 = LinearRegressionModel()
loaded_model_0.state_dict()
# 赋值参数
loaded_model_0.load_state_dict(torch.load(f = MODEL_SAVE_PATH))



评论