PyTorch框架基础实践

一、PyTorch简介

1.1 PyTorch是什么

Ø 开源的机器学习/深度学习框架( https://pytorch.org/ )

Ø 2017年1月,FAIR(Facebook AI Research)发布了PyTorch 0.1

Ø 它强调易用性和灵活性,并允许用深度学习领域惯用的 Python 来表示深度学习模型

Ø PyTorch 提供了一个核心数据结构—张量(Tensor)

Ø 类似的框架还有TensorFlow、PaddlePaddle、MindSpore等

1.2 环境搭建

1
2
3
4
#• 详见官网: https://pytorch.org/
#• 快速安装:pip install torch
#• 课程代码运行环境:Python 3.9.x 、PyTorch 1.13.0
#搭建环境参考:https://pytorch.org/get-started/previous-versions/#v1130
1
2
3
4
5
6
7
8
9
10
11
12
13
###Linux and Windows platform
root@ksp-registry:~# conda create -n py3.9 python=3.9
conda activate py3.9
#python -V
Python 3.9.21

# CPU only
pip install torch==1.13.0+cpu torchvision==0.14.0+cpu torchaudio==0.13.0 --extra-index-url https://download.pytorch.org/whl/cpu
pip install NumPy==1.24.0
pip install matplotlib==3.9.4 #画图的python库

# 如果有GPU,CUDA 11.7
pip install torch==1.13.0+cu117 torchvision==0.14.0+cu117 torchaudio==0.13.0 --extra-index-url https://download.pytorch.org/whl/cu117
image-20250226173613976

二、Tensor操作

张量(Tensor):PyTorch网络运算中的基本数据结构(类似ndarray )

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
###张量(Tensor):PyTorch网络运算中的基本数据结构(类似ndarray )
###创建张量
import torch
a = torch.tensor(0.1)
b = torch.tensor(0.3, dtype=torch.float64)
#执行运算
result = a + b
print(result)


###创建特定的张量
import torch
# 全1张量
ones = torch.ones((3, 2))
# 等差序列张量
range_tensor = torch.arange(1, 11, 1)
print('ones:', ones, '\n', 'range_tensor:', range_tensor)


###tensor与ndarray相互转化
import torch
output = torch.zeros((2, 2), dtype=torch.float32)
print("1--->", output)
print("2--->output: {}".format(type(output)))
n_output = output.numpy()
print("3--->n_output: {}".format(type(n_output)))
t_output = torch.from_numpy(n_output) # 将ndarray转为tensor
print("4--->t_output: {}".format(type(t_output)))


###PyTorch常用数据类型
#官方教程: https://pytorch.org/tutorials/beginner/basics/tensorqs_tutorial.html
#官方API文档: https://pytorch.org/docs/stable/index.html
image-20250227101746078
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
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
###官方教程: https://pytorch.org/tutorials/beginner/basics/tensorqs_tutorial.html
import torch
import numpy as np
data = [[1, 2],[3, 4]]
x_data = torch.tensor(data) #直接从数据创建一个tensor,数据类型可以被自动推断出来
print("1--->x_data:", x_data)
print("x_data type:{}".format(type(x_data)))
np_array = np.array(data) #直接从数据创建一个numpy变量
print("2--->np_array", np_array)
x_np = torch.from_numpy(np_array) #从numpy变量创建一个tensor
print("3--->x_np", x_np)
print("x_np type:{}".format(type(x_np)))
x_ones = torch.ones_like(x_data) # 根据x_data的创建一个新tensor,新tensor的属性(此处因为没有指定数据类型,所以新tensor数据类型跟x_data也一样。其他属性也是类似规律)跟x_data一样,但数值可以指定为全是1
print(f"Ones Tensor: \n {x_ones} \n")
x_rand = torch.rand_like(x_data, dtype=torch.float) # 根据x_data的创建一个新tensor,形状与原tensor一样,但指定了新的数据类型为torch.float、且它的元素值都是一些随机生成的数
print(f"Random Tensor: \n {x_rand} \n")

###-------------------------------------------------------
############输出如下:
1--->x_data: tensor([[1, 2],
[3, 4]])
x_data type:<class 'torch.Tensor'>
2--->np_array [[1 2]
[3 4]]
3--->x_np tensor([[1, 2],
[3, 4]], dtype=torch.int32)
x_np type:<class 'torch.Tensor'>
Ones Tensor:
tensor([[1, 1],
[1, 1]])

Random Tensor:
tensor([[0.1867, 0.2627],
[0.4514, 0.6730]])


###官方教程: https://pytorch.org/tutorials/beginner/basics/tensorqs_tutorial.html
#With random or constant values
import torch
import numpy as np
shape = (2,3,)
rand_tensor = torch.rand(shape)
ones_tensor = torch.ones(shape)
zeros_tensor = torch.zeros(shape)

print(f"Random Tensor: \n {rand_tensor} \n")
print(f"Ones Tensor: \n {ones_tensor} \n")
print(f"Zeros Tensor: \n {zeros_tensor}")
###-------------------------------------------------------
############输出如下:
Random Tensor:
tensor([[0.3904, 0.6009, 0.2566],
[0.7936, 0.9408, 0.1332]])

Ones Tensor:
tensor([[1., 1., 1.],
[1., 1., 1.]])

Zeros Tensor:
tensor([[0., 0., 0.],
[0., 0., 0.]])


###官方教程: https://pytorch.org/tutorials/beginner/basics/tensorqs_tutorial.html
##Attributes of a Tensor,Tensor attributes describe their shape, datatype, and the device on which they are stored.
import torch
import numpy as np
tensor = torch.rand(3,4)
print(f"Shape of tensor: {tensor.shape}")
print(f"Datatype of tensor: {tensor.dtype}")
print(f"Device tensor is stored on: {tensor.device}")
###-------------------------------------------------------
############输出如下:
Shape of tensor: torch.Size([3, 4])
Datatype of tensor: torch.float32
Device tensor is stored on: cpu

#Operations on Tensors
#Over 1200 tensor operations, including arithmetic, linear algebra, matrix manipulation (transposing, indexing, slicing), sampling and more are comprehensively described here(https://pytorch.org/docs/stable/torch.html).
#Try out some of the operations from the list. If you’re familiar with the NumPy API, you’ll find the Tensor API a breeze to use.
#Standard numpy-like indexing and slicing:
tensor = torch.ones(4, 4)
print(f"First row: {tensor[0]}")
print(f"First column: {tensor[:, 0]}") #也可以指定为其他列
print(f"Last column: {tensor[..., -1]}")
tensor[:,1] = 0
print(tensor)
###-------------------------------------------------------
############输出如下:
First row: tensor([1., 1., 1., 1.])
First column: tensor([1., 1., 1., 1.])
Last column: tensor([1., 1., 1., 1.])
tensor([[1., 0., 1., 1.],
[1., 0., 1., 1.],
[1., 0., 1., 1.],
[1., 0., 1., 1.]])

三、构建一个线性模型

任务描述

image-20250227112224067

image-20250227112605556


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
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
import pandas as pd
import torch
import matplotlib.pyplot as plt

#1. 读取数据
data = pd.read_csv('line_fit_data.csv').values # 将数据读取后存储为ndarray格式(类似于数组形式)
X = torch.tensor(data[:, 0], dtype=torch.float32) # 样本自变量
y = torch.tensor(data[:, 1], dtype=torch.float32) # 目标变量
#print(data)
W = torch.tensor(-10.0, requires_grad=True) #初始权值。requires_grad=True的作用是让 backward 可以追踪这个参数并且计算它的梯度。
b = torch.tensor(7.0, requires_grad=True) #初始阈值
#关于参数 requires_grad 的详细解释:https://blog.csdn.net/weixin_42572656/article/details/116117780
learning_rate = 0.35 #学习速速率,或叫步长

#2. 构造一个线性模型
# 构造一个线性模型
def linear_model(W, X, b):
return W * X + b

def loss_fn(y_pre, y_true):
return ((y_pre - y_true) ** 2).mean() # mean() 方法 用于计算张量中所有元素的平均值
y_pre = linear_model(W, X, b) # 前向传播
loss = loss_fn(y_pre, y) # 模型损失值
loss.backward() # 误差反向传播
W.data = W.data - W.grad * learning_rate # 沿着梯度的反方向更新权值
b.data = b.data - b.grad * learning_rate # 沿着梯度的反方向更新阈值
W.grad.zero_() # 将权值的梯度清零
b.grad.zero_() # 将阈值的梯度清零
print("Epoch: ", 0, "Loss:", loss.item(), "W:", W.item(), "b:", b.item())


####3. 构造优化器
####输出y_pre,可以看到y_pre与y是有很大差异的,原因在我们的W与b是随便取的
###y_pre = linear_model(W, X, b) #前向传播
###loss = loss_fn(y_pre, y) #模型损失值
###loss.backward() #误差反向传播
###print(W.grad) #W的梯度,其实就是loss 关于W的偏导数
###print(b.grad) #b的梯度,其实就是loss 关于b的偏导数
####有了W与b的梯度后,就可以在训练过程中不断更新W与b的取值了,最终实现loss的减小
###
####4. 最小化方差(训练)
###W
###b
###W.data = W.data - W.grad * learning_rate #沿着梯度的反方向更新权值
###b.data = b.data - b.grad * learning_rate #沿着梯度的反方向更新阈值
####查看更新后的W与b
###W
###b
####默认情况下变量的梯度是会累积的(有些场景需要)。此场景不需要梯度累积,更新对应变量后需要将变量梯度置零
###W.grad.zero_() #将权值的梯度清零
###b.grad.zero_() #将阈值的梯度清零
###
####5. 性能评估
###y_pre = linear_model(W, X, b) #前向传播
###loss = loss_fn(y_pre, y) #模型损失值
###loss

y_pre = linear_model(W, X, b) # 前向传播
loss = loss_fn(y_pre, y) # 模型损失值
loss.backward() # 误差反向传播
W.data = W.data - W.grad * learning_rate # 沿着梯度的反方向更新权值
b.data = b.data - b.grad * learning_rate # 沿着梯度的反方向更新阈值
W.grad.zero_() # 将权值的梯度清零
b.grad.zero_() # 将阈值的梯度清零
print("Epoch: ", 0, "Loss:", loss.item(), "W:", W.item(), "b:", b.item())

plt.figure(figsize=(10, 5)) #设置画布大小
plt.axis([-0.01, 1, -3, 10]) #指定坐标轴的取值范围
plt.scatter(X, y, color="blue") #绘制样本实际分布图
plt.plot(X, linear_model(W, X, b).data, color="red") #绘制模型预测结果分布图
#plt.legend(['target_y', 'predict_y']) #标示图例
#plt.show() #展示画布,不执行看不到弹窗与画布

#以下for循环演示,执行多轮权值与阈值的更新,尽量实现线性模型对原数据的拟合
#def linear_model_train(W, X, b, y_true, range_):
for epoch in range(1, 200):
y_pre = linear_model(W, X, b) # 前向传播
loss = loss_fn(y_pre, y) # 模型损失值
loss.backward() # 误差反向传播

W.data = W.data - W.grad * learning_rate # 沿着梯度的反方向更新权值
b.data = b.data - b.grad * learning_rate # 沿着梯度的反方向更新阈值
W.grad.zero_() # 将权值的梯度清零
b.grad.zero_() # 将阈值的梯度清零

print("Epoch: ", epoch, "Loss:", loss.item(), "W:", W.item(), "b:", b.item())
plt.plot(X, y_pre.data, color="red", alpha=0.2) # 绘制模型预测结果分布图
#linear_model_train(W, X, b, y, 10)

plt.show()
#下图是此程序的实际执行结果(跟那些蓝色点基本重合的那条模拟直线就是最后的回归方程对应的直线)
image-20250227221735425

四、识别手写数字

案例流程

image-20250227230342049

image-20250227230359444

image-20250227230429605


各个步骤与代码片段

image-20250301114431359
image-20250301114511908
image-20250301114752851
image-20250301114850257
image-20250301114908341
image-20250301114926605
image-20250301115050517
image-20250301115106097
image-20250301115114458
image-20250301115123276
image-20250301115133657
image-20250301115142324

具体可执行代码

1
2
3
#相关代码与文件已经保存在阿里云盘(由于阿里云盘不支持分享.zip,故将分享为自释放程序文件即.exe格式,下载双击即可解压)
https://www.alipan.com/s/QdEhU2t9E1H
提取码: o49e

(1)文件“识别手写数字2.py”内容如下:

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
import numpy
import torch
from mnist_net_work2 import Network2

#1. 加载数据
#读取支持读取和保存numpy数组,其中.npz是numpy中使用的特定的数据类型
#指定allow_pickle参数来设置是否允许使用pickle序列化,默认值是False,意味着 numpy.load() 函数不能加载包含 Python 对象(例如 None)的数组
data = numpy.load('mnist.npz', allow_pickle=True)
#4个变量分别代表训练集样本自变量、训练集样本标签、测试集样本自变量、测试集样本标签
X_train, y_train, X_test, y_test = data['x_train'], data['y_train'], data['x_test'], data['y_test']


#2. 数据加工
X_train_tensor = torch.tensor(X_train/255, dtype=torch.float32) #将训练集样本自变量转变为tensor类型
y_train_tensor = torch.tensor(y_train, dtype=torch.int64) #将训练集样本标签转变为tensor类型
X_test_tensor = torch.tensor(X_test/255, dtype=torch.float32) #将测试集样本自变量转变为tensor类型
#测试集样本标签,在此处不需要其tensor类型、直接使用其numpy数组类型即可,故无须转换
train_ds = torch.utils.data.TensorDataset(X_train_tensor, y_train_tensor) #将训练集样本自变量、标签整理成成对数据集
train_dl = torch.utils.data.DataLoader(train_ds, batch_size=32, shuffle=True) #训练集样本整理成对后,再转变成可迭代的格式、迭代前会打乱、每轮迭代取32个数据为一批

#3. 构建模型(搭建网络)
network = Network2() #实例化得到一个网络模型

#4. 模型配置
#损失函数
loss_fn = torch.nn.CrossEntropyLoss() #使用torch默认存在的交叉熵函数
#优化器。用来在多次训练中使用梯度修改相关参数值的,此处使用随机梯度下降方法SGD,其中第一个参数是代表着要训练过程中需要不断修改的模型参数、第2个参数是学习率
optimizer = torch.optim.SGD(network.parameters(), lr=0.01)

#5. 模型训练与保存
for epoch in range(20):
for image, label in train_dl:
y_pre = network(image) # 前向传播
loss = loss_fn(y_pre, label) # 计算模型损失值
network.zero_grad() # 将网络中所有参数的梯度进行清零
loss.backward() # 计算梯度
optimizer.step() # 对网络参数(权值和阈值)进行优化
print('第 {} 轮训练的最后一批样本的训练损失值为:{}'.format(epoch, loss.item())) # 打印训练误差

#6. 模型应用
predict = network.forward(X_test_tensor) #调用已经训练好的模型对测试集样本进行预测
result = predict.data.numpy().argmax(axis=1) # 模型对测试样本的预测标签
acc_test = (result == y_test).mean() #测试准确度

torch.save(network.state_dict(), 'mnist_net_work2.pt') #保存已经训练好的模型(此处只保存参数)

(2)网络(模型)定义文件“mnist_net_work2.py”如下:

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
import torch
from torch.nn import Module

class Network2(Module):
# __init__方法中,定义神经网络用到的层及函数
def __init__(self):
super(Network2, self).__init__()
self.flatten = torch.nn.Flatten() #展平层

self.fc1 = torch.nn.Linear(784, 128) #全连接(隐藏层)
# 定义一个简单的线性层,输入维度为784,输出维度为128

self.fc2 = torch.nn.Linear(128, 10) #全连接(输出层)
# 定义一个简单的线性层,输入维度为128,输出维度为10

self.relu = torch.nn.ReLU() #激活函数
# ReLU(Rectified Linear Unit)是一种常用的激活函数,全称为修正线性单元。
# 它的主要作用是将输入值限制在一个非负的范围内,即当输入值小于0时,输出值为0;当输入值大于等于0时,输出值等于输入值本身。ReLU函数的表达式为:f(x) = max(0, x)。
# https://blog.csdn.net/u011775793/article/details/135422687

#前向传播函数
def forward(self, x):
#print("x: ", x)
# 初始化函数中定义的各个函数的调用
x = self.flatten(x)
#print("after flatten: ", x)
x = self.fc1(x)
#print("after fc1: ", x)
x = self.relu(x)
#print("after relu: ", x)
x = self.fc2(x)
#print("after fc2: ", x)
return x

(3)模型应用即模型效果的验证,对应的文件“predict2.py”如下:

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
#import torch
#from mnist_net_work import NetWork
#import matplotlib.pyplot as plt
#
#model = NetWork() # 实例化得到一个网络模型
#model_state_dict = torch.load('mnist_1.pt') # 加载已保存好的模型(参数)
#model.load_state_dict(model_state_dict) # 将加载进来的参数导入网络中
#
#image = plt.imread('test_images/{}.jpg'.format(0)) # 读取照片
#plt.imshow(image)

import torch
from mnist_net_work2 import Network2
import matplotlib.pyplot as plt

model = Network2() #实例化得到一个网络模型
model_state_dict = torch.load('mnist_net_work2.pt') #加载模型参数
model.load_state_dict(model_state_dict) #将加载进来的模型参数导入到网络(模型)中

for i in range(30):
image = plt.imread('test_images/{}.jpg'.format(i)) #读取照片。image的类型是 numpy.ndarray
image = torch.tensor(image/255, dtype=torch.float32) #转换成tensor类型
image = image.view(1, 28, 28) #数据结构由二维转成三维
y_pre = model(image) #执行预测
result = y_pre.data.numpy().argmax(axis=1) #处理预测返回数据,获取预测到的数字
print("预测照片{}.jpg 的数字是:{}".format(i, result))
ID Name Required Description
1 enqueue Y 判断集群中的空闲资源是否能满足作业调度的基本需求,如果能,则将作业的podgroup设置为 inqueue状态,否则保持podgroup为pending状态。
2 allocate Y 尝试为inqueue状态的podgroup作业分配资源。
3 backfill N 尝试为未指明pod资源申请量的作业分配资源。
4 preempt N 识别高优先级的调度作业。尝试驱逐低优先级的Pod,并为高优先级作业分配资源。
5 reclaim N 选择资源被其他queue占用的queue,并回收相应资源。
6 shuffle N 将任务队列中的任务顺序随机化,提高资源利用率、优化调度性能
ID Name Arguments Registered Functions Description
1 binpack * binpack.weight
* binpack.cpu
* binpack.memory
* binpack.resources
* nodeOrderFn 尽量将Pod绑定到资源利用率高的节点上, 以减少碎片化。
2 conformance / * preemptableFn
* reclaimableFn
跳过关键Pod,而不是驱逐它们。
3 drf / * preemptableFn
* queueOrderFn
* reclaimFn
* jobOrderFn
* namespaceOrderFn
为所有队列提供公平的资源共享。
ID Name Arguments Registered Functions Description
4 extender * extender.urlPrefix
* extender.httpTimeout
* extender.onSessionOpenVerb
* extender.onSessionCloseVerb
* extender.predicateVerb
* extender.prioritizeVerb
* extender.preemptableVerb
* extender.reclaimableVerb
* extender.queueOverusedVerb
* extender.jobEnqueueableVerb
* extender.ignorable
* predicateFn
* batchNodeOrderFn
* preemptableFn
* reclaimableFn
* jobEnqueueableFn
* overusedFn
添加外部http server,用 以执行自定义action。
5 gang / * jobValidFn
* reclaimableFn
* preemptableFn
* jobOrderFn
* JobReadyFn
* jobPipelineFn
* jobStarvingFn
为作业分配资源时,重点 考虑作业的最低资源需求 和pod最小运行数量,执 行“All or nothing”的调度 策略。
6 nodeorder * nodeaffinity.weight
* podaffinity.weight
* leastrequested.weight
* balancedresource.weight
* mostrequested.weight
* tainttoleration.weight
* imagelocality.weight
* nodeOrderFn
* batchNodeOrderFn
以自定义的方式对所有节点 进行排序。
ID Name Arguments Registered Functions Description
7 numaaware * weight * predicateFn
* batchNodeOrderFn
在将pod绑定到node节点时, 重点考虑CPU Numa因素。
8 overcommit * overcommit-factor * jobEnqueueableFn
* jobEnqueuedFn
将可用资源设置为群集整体 资源的指定倍数。
9 predicate * predicate.GPUSharingEnable
* predicate.CacheEnable
* predicate.ProportionalEnable
* predicate.resources
* predicate.resources.nvidia.com/gpu.cpu
* predicate.resources.nvidia.com/gpu.memory
* predicateFn 添加关于如何为pod调度过滤 node的自定义函数。
10 priority / * taskOrderFn
* jobOrderFn
* preemptableFn
* jobStarvingFn
定义调度作业的优先级。
11 proportion / * queueOrderFn
* reclaimableFn
* overusedFn
* allocatableFn
* jobEnqueueableFn
根据queue的配置,将集群的整 个资源按比例划分到所有的 queue。
ID Name Arguments Registered Functions Description
12 sla * sla-waiting-time * jobOrderFn
* jobEnqueueableFn
* JobPipelinedFn
根据SLA设置对工作负载进行排序。
13 task-topology / * taskOrderFn
* nodeOrderFn
根据给定策略,将不同角色的Pod绑定到节点上。
14 tdm * tdm.revocable-zone.rz1
* tdm.revocable-zone.rz2
* tdm.evict.period
* predicateFn
* nodeOrderFn
* preemptableFn
* victimTasksFn
* jobOrderFn
* jobPipelinedFn
* jobStarvingFn
在不同的时间段内,允许部分节点承接K8s和其他 集群的作业调度。

PyTorch框架基础实践
https://jiangsanyin.github.io/2025/03/01/PyTorch框架基础实践/
作者
sanyinjiang
发布于
2025年3月1日
许可协议