Pytorch笔记
调用帮助文档
在Python中,help()
和dir()
是两个内置函数,它们提供了对Python对象(如模块、类、方法、函数、变量等)的有用信息。
help()
函数用于获取有关Python对象的信息。1
help(print) #这将显示有关print()函数的详细信息,包括它的用途、参数、返回值等。
dir()
函数用于列出Python对象的所有属性和方法。获取内置
math
模块的所有属性和方法:
1 | import math |
这将显示math
模块中定义的所有函数、变量和常量的名称。
也可以在不传递任何参数的情况下调用dir()
函数,这将返回一个包含当前作用域中所有名称的列表。
1 | print(dir()) |
图像处理
必要的库
1 | from PIL import Image |
读取和显示图像
1 | img = Image.open(img_path) # 打开图像文件 |
用PIL
中的Image
可以通过图像路径来打开某个图像并显示图像文件。
处理图像
将PIL图像转换为numpy数组
1 | image_path = "data/train/ants_image/0013035.jpg" |
TensorBoard可视化
使用PyTorch进行深度学习实验时,我们经常需要跟踪和可视化训练过程中的各种指标,如损失、准确率等。TensorBoard是一个强大的可视化工具,可以帮助我们实现这一目标。在PyTorch中,我们可以通过SummaryWriter
类轻松地将数据写入TensorBoard可以读取的日志文件。
其就相当于一个日志
创建并启动TensorBoard
从
torch.utils.tensorboard
导入SummaryWriter
。创建一个
SummaryWriter
对象,并指定一个日志目录,例如”logs”。1
2from torch.utils.tensorboard import SummaryWriter
writer = SummaryWriter("logs")启动TensorBoard:
要在命令行中启动TensorBoard并加载我们的日志,请使用以下命令:
1
tensorboard --logdir=logs
这将在本地启动一个TensorBoard服务器,并在默认浏览器中打开TensorBoard的Web界面。
写入日志文件
添加标量数据
可以使用add_scalar
方法将标量数据写入日志文件。这个方法接受三个参数:标签(用于在TensorBoard中标识数据),标量值,以及全局步骤值(通常用于表示训练的迭代次数或时间步)。
1 | from torch.utils.tensorboard import SummaryWriter |
添加图像
使用SummaryWriter
的add_image
方法将图像添加到TensorBoard中。需要注意的是,add_image
方法要求图像数据的格式与指定的dataformats
参数相匹配。在这里,我们指定为'HWC'
,即高度、宽度和通道数。
1 | writer = SummaryWriter("logs") |
Pytorch基础
张量(Tensors)
创建Tensors变量
有多种创建Tensors变量的方法:
1 | #创建未初始化的矩阵 |
还可以用tensor.new_ones()
和torch.randn_like()
从一个tensor
变量创建另一个tensor
变量。
处理Tensors变量
加法
加法有几种实现方式:
+
运算符torch.add(tensor1, tensor2, [out=tensor3])
tensor1.add_(tensor2)
:直接修改 tensor 变量
可以改变 tensor 变量的操作都带有一个后缀
_
, 例如x.copy_(y), x.t_()
都可以改变 x 变量
访问tensor数据
可以使用索引来访问某一维的数据
1 | # 访问 tensor 第一列数据 |
修改维度
1 | #对 Tensor 的尺寸修改,可以采用 torch.view() |
输出:
1 | torch.Size([4, 4]) torch.Size([16]) torch.Size([2, 8]) |
转换为其他数据类型
转换为Numpy数组
调用 tensor.numpy()
可以实现这个转换操作。两者是共享同个内存空间的,修改 tensor
变量 a
,Numpy 数组变量 b
也会发生变化。
1 | a = torch.ones(5) |
Numpy 数组转换为 Tensor
转换的操作是调用 torch.from_numpy(numpy_array)
方法.\
1 | a = np.ones(5) |
定义自定义数据集类
创建自定义数据集类
要创建一个自定义的数据集类,我们首先需要导入必要的库,并继承Dataset
类。
1 | from torch.utils.data import Dataset |
使用自定义数据集类
创建了自定义的数据集类之后,就可以将其传递给PyTorch的数据加载器(DataLoader
)来使用。
1 | # 实例化自定义数据集类 |
DataLoader的使用
需要导入必要的库。
1 | from torch.utils.data import DataLoader |
加载数据集:
1 | test_loader = DataLoader(dataset=test_data, batch_size=64, shuffle=True, num_workers=0, drop_last=True) |
dataset:
- 参数类型:
Dataset
- 作用: 指定
DataLoader
需要加载的数据集。数据集必须实现Dataset
接口,这样才能被DataLoader
识别和加载。
- 参数类型:
batch_size
参数类型:
int
默认值:
1
作用: 指定每个批次的样本数量。批次大小会影响到模型的收敛速度和更新频率。
shuffle
参数类型:
bool
默认值:
False
作用: 如果设置为
True
,则每个epoch开始时,DataLoader
会随机打乱数据集中的样本顺序。
num_workers
参数类型:
int
默认值:
0
作用: 指定有多少个子进程用于加载数据。
0
表示数据将在主进程中加载(不使用子进程)。增加num_workers
可以提高数据加载的效率,特别是在数据预处理较为复杂或者数据存储在慢速存储介质上时。
drop_last
- 参数类型:
bool
- 默认值:
False
- 作用: 当样本数量不能被批次大小整除时,如果
drop_last
设置为True
,则DataLoader
会丢弃最后一个不完整的批次。
- 参数类型:
Torchvision Transforms
导入必要的库
1 | from torchvision import transforms |
常用的Transforms操作
PIL图像转换为Tensor
torchvision.transforms
库中的ToTensor()
函数可以将PIL图像或NumPy ndarray转换为FloatTensor,并且会将图像的像素值范围从0-255缩放到0-1。这对于神经网络来说是一个常见的预处理步骤。
1 | tensor_trans = transforms.ToTensor() |
使用SummaryWriter
的add_image()
方法将Tensor图像添加到TensorBoard中。这样,我们就可以在TensorBoard中查看和处理这个图像了。
1 | writer.add_image("Tensor_img", tensor_img) |
归一化操作
下面是一个示例代码,展示了如何使用PyTorch的transforms模块进行图像归一化:
1 | # Totensor |
图像调整大小
在调整大小之前,可以通过 img.size
属性获取原始图像的大小。
1 | print(img.size) # 输出原始图像的宽度和高度,例如:(width, height) |
创建一个 transforms.Resize
对象,指定新的图像大小。
1 | trans_resize = transforms.Resize((512, 512)) # 调整图像到 512x512 像素大小 |
使用创建的 trans_resize
转换器来调整图像大小。
1 | img_resize = trans_resize(img) # img_resize 是调整大小后的 PIL 图像 |
使用Compose组合多个操作
在实际应用中,我们通常需要按顺序执行多个图像预处理操作。这时,可以使用transforms.Compose
来组合这些操作,从而创建一个可重复使用的预处理流程。以下是一个示例代码:
1 | from PIL import Image |
损失函数
Pytorch中也提供了许多的损失函数
1 | # L1损失函数 |
计算正确率
output=model(input)
得到的是浮点数矩阵,如果想要知道类别,则可以用.argmax()
函数得到输出的类别,然后用predict==targets
得到布尔矩阵。最后对矩阵torch.sum()
得到正确个数。
1 | import torch |
PyTorch卷积
PyTorch中torch.nn模块的使用
在PyTorch中,torch.nn
模块提供了构建神经网络所需的所有构建块。
1 | import torch.nn as nn |
接下来,我们定义一个模型类Model
,它继承自nn.Module
。在构造函数__init__
中,我们初始化两个卷积层conv1
和conv2
。nn.Conv2d
是二维卷积层,它接收的参数包括输入和输出通道数、卷积核大小等。1
2
3
4
5class Model(nn.Module):
def __init__(self):
super(Model, self).__init__() # 记得初始化父类
self.conv1 = nn.Conv2d(1, 20, 5) # 输入通道1,输出通道20,卷积核大小5
self.conv2 = nn.Conv2d(20, 20, 5) # 输入通道20,输出通道20,卷积核大小5
在forward
方法中,我们定义了数据通过网络的方式。这里使用了F.relu
作为激活函数,对每个卷积层的输出应用ReLU激活。
其它的一些层可以在官方文档中找到
1 | def forward(self, x): |
这样,我们就定义了一个简单的卷积神经网络模型。在实际使用时,我们会创建这个模型类的实例,然后传入数据来进行训练和测试。1
2
3
4
5
6
7
8
9# 创建模型实例
model = Model()
# 假设我们有一些输入数据x(例如,图像数据),形状为(N, 1, 28, 28)
# N是批量大小,1是通道数,28x28是图像尺寸
x = torch.randn(64, 1, 28, 28) # 随机生成一些输入数据
# 通过模型前向传播数据
output = model(x)
# 输出结果
print(output)
以上就是使用torch.nn
模块构建和运行一个简单卷积神经网络的基本步骤。在实际应用中,模型通常会包含更多的层和更复杂的结构,但基本原理是相同的。
反向传播
定义优化器
1 | optimizer = optim.SGD(model.parameters(), lr=0.01, momentum=0.9) |
主要要定义的有两个:
- 模型参数:给定模型参数优化器才知道更新哪些内容
- 学习速率
lr
训练过程
训练过程写法如下:
1 | for input, target in dataset: |
完整代码
与自己的模型综合起来,效果大概是这样
1 | loss = nn.CrossEntropyLoss() |
利用Sequential
简化模块
Sequential
也在torch.nn
模块中,利用其,可以避免在前向传播的过程中手写每层的输入输出。
1 | class Model(nn.Module): |
可视化模块
- 可以用
print(Model)
将模型显示在控制台 - 用
SummaryWriter
的add_graph(Model,input)
在TensorBorad中显示
保存模型
1 | import torch |
加载自身的模型的时候需要给出类的定义。
PyTorch 2D卷积
1 | import torch |
1. 创建输入张量和卷积核
输入张量(input
)和卷积核(kernel
)被定义为多维张量。1
2
3
4
5
6
7
8
9
10
11
12input = torch.tensor([
[1, 2, 0, 3, 1],
[0, 1, 2, 3, 1],
[1, 2, 1, 0, 0],
[5, 2, 3, 1, 1],
[2, 1, 0, 1, 1]
])
kernel = torch.tensor([
[1, 2, 1],
[0, 1, 0],
[2, 1, 0]
])
2. 调整张量的形状
为了进行二维卷积操作,我们需要将输入张量和卷积核的形状调整为四维张量。1
2input = torch.reshape(input, (1, 1, 5, 5))
kernel = torch.reshape(kernel, (1, 1, 3, 3))
在PyTorch中,使用torch.nn.functional.conv2d
函数进行卷积操作时,输入和卷积核需要是四维张量。这四个维度分别是:
- 批量大小(Batch size):表示输入数据的数量。
- 通道数(Channels):对于彩色图像,通常是3(红、绿、蓝)。
- 高度(Height):图像的高度。
- 宽度(Width):图像的宽度。
input = torch.reshape(input, (1, 1, 5, 5))
和kernel = torch.reshape(kernel, (1, 1, 3, 3))
这两行代码的作用是将输入和卷积核的形状从二维扩展到四维,以满足conv2d
函数的要求。
3.进行卷积操作
使用F.conv2d
函数进行卷积操作,并设置步长(stride
)为1。
1 | output = F.conv2d(input, kernel, stride=1) |
在卷积操作中,stride
(步幅)是一个非常重要的参数,它定义了卷积核在输入张量上移动时的步长。
还可以设置padding
参数:padding
参数定义了在卷积操作之前,输入数据周围是否要添加额外的边界(通常为0)。
最大池化层
最大池化与卷积的区别是,卷积是对窗口中的数对应相乘后求和,最大池化是求窗口中的最大值。
1 | # 创建一个最大池化层,池化窗口大小为2x2,步长为2 |
其的主要参数为:
kernel_size
:表示池化窗口的大小。对于二维最大池化,它可以是单个整数或两个整数的元组(height, width)。如果是一个整数,则池化窗口将是一个正方形。stride
:这是一个元组或整数,表示池化窗口在输入张量上移动的步长。默认情况下,stride
与kernel_size
相同。padding
:这是一个元组或整数,表示在输入张量的每个边上添加的填充数量。默认情况下,没有填充(padding=0
)。
完整的训练过程
准备数据集
1
2train_data = datasets.CIFAR10(root="/data", train=True, transform=transforms.ToTensor(), download=True)
test_data = datasets.CIFAR10(root="/data", train=False, transform=transforms.ToTensor(), download=True)加载数据集
1
2train_dataloader = DataLoader(train_data, batch_size=64)
test_dataloader = DataLoader(test_data, batch_size=64)搭建神经网络
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18# 搭建神经网络
from torch import nn
class Model(nn.Module):
def _init_(self):
super(Model, self)._init_()
self.model = nn.Sequential(
nn.Conv2d(3, 32, 5, 1, 2),
nn.MaxPool2d(2),
nn.Conv2d(32, 32, 5, 1, 2),
nn.MaxPool2d(2),
nn.Conv2d(32, 64, 5, 1, 2),
nn.MaxPool2d(2),
nn.Flatten(),
nn.Linear(64*4*4, 64),
nn.Linear(64, 10))
def forward(self, x):
x = self.model(x)
return x创建网络模型,定义损失函数及优化器
1
2
3
4
5
6
7#创建网络模型
model = Model()
#损失函数
loss_fn = nn.CrossEntropyLoss()
#优化器
learning_rate = 0.01
optimizer = torch.optim.SGD(model.parameters(), lr=learning_rate)设置训练网络中的参数
1
2
3
4
5
6
7
8
9
10# 设置训练网络的一些参数
# 记录训练的次数
total_train_step = 0
# 记录测试的次数
total_test_step = 0
# 训练的轮数
epoch = 10开始训练
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16for i in range(epoch):
print("--------第{}轮训练开始-------".format(i+1))
# 训练步骤开始
model.train() # 如果有Dropout层,调用这个会激活其
for data in train_dataloader:
imgs, targets = data
outputs = model(imgs)
loss = loss_fn(outputs, targets)
# 优化器优化模型
optimizer.zero_grad()
loss.backward()
optimizer.step()
total_train_step = total_train_step + 1
print("训练次数:{}, Loss:{".format(total_train_step,loss.item()))在测试集上测试模型
1
2
3
4
5
6
7
8
9#测试步骤开始
model.eval() # 如果有Dropout层,调用这个会将其失效
total_test_loss =0
with torch.no_grad():
for data in test_dataloader:
imgs, targets = data
outputs = model(imgs)
loss = loss_fn(outputs, targets)
total_test_loss = total_test_loss + loss
在GPU上训练
可以用Python命令!nvidia-smi
查看GPU配置。
第一种方法
对模型model
、训练和测试时的数据imgs,targets
、损失函数loss
,调用.cuda()
,让它们转移到GPU上。
1 | if torch.cuda.is_available(): |
第二种方法
可以用以下内容代替.cuda()
1 | device = torch.device("gpu") |