调用帮助文档

在Python中,help()dir()是两个内置函数,它们提供了对Python对象(如模块、类、方法、函数、变量等)的有用信息。

  • help()函数用于获取有关Python对象的信息。

    1
    help(print) #这将显示有关print()函数的详细信息,包括它的用途、参数、返回值等。
  • dir()函数用于列出Python对象的所有属性和方法。

    获取内置math模块的所有属性和方法:

1
2
import math
print(dir(math))

这将显示math模块中定义的所有函数、变量和常量的名称。

也可以在不传递任何参数的情况下调用dir()函数,这将返回一个包含当前作用域中所有名称的列表。

1
print(dir())

图像处理

必要的库

1
2
from PIL import Image
import os

读取和显示图像

1
2
img = Image.open(img_path)  # 打开图像文件
img.show() # 显示图像文件(注意:这可能会打开多个图像查看器窗口)

PIL中的Image可以通过图像路径来打开某个图像并显示图像文件。

处理图像

将PIL图像转换为numpy数组

1
2
3
image_path = "data/train/ants_image/0013035.jpg"  
img_PIL = Image.open(image_path)
img_array = np.array(img_PIL)

TensorBoard可视化

使用PyTorch进行深度学习实验时,我们经常需要跟踪和可视化训练过程中的各种指标,如损失、准确率等。TensorBoard是一个强大的可视化工具,可以帮助我们实现这一目标。在PyTorch中,我们可以通过SummaryWriter类轻松地将数据写入TensorBoard可以读取的日志文件。

其就相当于一个日志

创建并启动TensorBoard

  1. torch.utils.tensorboard导入SummaryWriter

  2. 创建一个SummaryWriter对象,并指定一个日志目录,例如”logs”。

    1
    2
    from torch.utils.tensorboard import SummaryWriter
    writer = SummaryWriter("logs")
  3. 启动TensorBoard:

    要在命令行中启动TensorBoard并加载我们的日志,请使用以下命令:

    1
    tensorboard --logdir=logs

    这将在本地启动一个TensorBoard服务器,并在默认浏览器中打开TensorBoard的Web界面。

写入日志文件

添加标量数据

可以使用add_scalar方法将标量数据写入日志文件。这个方法接受三个参数:标签(用于在TensorBoard中标识数据),标量值,以及全局步骤值(通常用于表示训练的迭代次数或时间步)。

1
2
3
4
5
6
7
from torch.utils.tensorboard import SummaryWriter
writer = SummaryWriter("logs")

for i in range(100):
writer.add_scalar("y=x", i, i)

writer.close() # 记得关闭

添加图像

使用SummaryWriteradd_image方法将图像添加到TensorBoard中。需要注意的是,add_image方法要求图像数据的格式与指定的dataformats参数相匹配。在这里,我们指定为'HWC',即高度、宽度和通道数。

1
2
writer = SummaryWriter("logs")  
writer.add_image("test", img_array, global_step=0, dataformats='HWC')#默认是'CHW'

Pytorch基础

张量(Tensors)

创建Tensors变量

有多种创建Tensors变量的方法:

1
2
3
4
5
6
7
8
9
10
#创建未初始化的矩阵
x = torch.empty(5, 3)
#随机初始化一个矩阵
rand_x = torch.rand(5, 3)
#创建数值皆为 0 的矩阵(类型为 long 的矩阵)
zero_x = torch.zeros(5, 3, dtype=torch.long)
#创建数值都是 1 的矩阵
one_x = torch.ones(5,3)
#直接传递 tensor 数值来创建
tensor = torch.tensor([5.5, 3])

还可以用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
2
# 访问 tensor 第一列数据
print(tensor[:, 0])

修改维度

1
2
3
4
5
6
#对 Tensor 的尺寸修改,可以采用 torch.view()
x = torch.randn(4, 4)
y = x.view(16)
# -1 表示除给定维度外的其余维度的乘积
z = x.view(-1, 8)
print(x.size(), y.size(), z.size())

输出:

1
torch.Size([4, 4]) torch.Size([16]) torch.Size([2, 8])

转换为其他数据类型

转换为Numpy数组

调用 tensor.numpy() 可以实现这个转换操作。两者是共享同个内存空间的,修改 tensor 变量 a,Numpy 数组变量 b也会发生变化。

1
2
a = torch.ones(5)
b = a.numpy()

Numpy 数组转换为 Tensor

转换的操作是调用 torch.from_numpy(numpy_array) 方法.\

1
2
a = np.ones(5)
b = torch.from_numpy(a)

定义自定义数据集类

创建自定义数据集类

要创建一个自定义的数据集类,我们首先需要导入必要的库,并继承Dataset类。

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
from torch.utils.data import Dataset
from PIL import Image
import os

class MyData(Dataset):
def __init__(self, root_dir, label_dir):
"""
初始化函数,接收根目录和标签目录作为参数。
:param root_dir: 根目录路径,包含所有标签的文件夹。
:param label_dir: 标签目录名称,即根目录下的子文件夹名,代表某一类别。
"""
self.root_dir = root_dir
self.label_dir = label_dir
# 拼接得到完整的目录路径
self.path = os.path.join(self.root_dir, self.label_dir)
# 列出目录中的所有文件,注意这里没有进行文件类型过滤
self.img_path_list = os.listdir(self.path)

def __getitem__(self, idx):
"""
根据索引获取单个样本(图像和标签)。
:param idx: 样本索引。
:return: 返回图像和标签。
"""
# 获取图像文件名
img_name = self.img_path_list[idx]
# 拼接得到完整的图像文件路径
img_item_path = os.path.join(self.root_dir, self.label_dir, img_name)
# 使用PIL库打开图像文件
img = Image.open(img_item_path)
# 这里简单地将标签设置为标签目录的名称
# 在实际应用中,标签可能需要更复杂的处理,比如转换为数字、one-hot编码等
label = self.label_dir
return img, label

def __len__(self):
"""
返回数据集中的样本数量(图像数量)。
"""
return len(self.img_path_list)

使用自定义数据集类

创建了自定义的数据集类之后,就可以将其传递给PyTorch的数据加载器(DataLoader)来使用。

1
2
3
4
5
6
7
8
9
10
11
12
# 实例化自定义数据集类
dataset = MyData(root_dir="path/to/root", label_dir="label_a")

# 使用DataLoader加载数据集(这里仅为示例,实际使用时需要设置batch_size等参数)
from torch.utils.data import DataLoader

dataloader = DataLoader(dataset, batch_size=4, shuffle=True)

# 在训练循环中迭代dataloader获取数据和标签
for images, labels in dataloader:
# 训练代码...
pass

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)
  1. dataset:

    • 参数类型: Dataset
    • 作用: 指定DataLoader需要加载的数据集。数据集必须实现Dataset接口,这样才能被DataLoader识别和加载。
  2. batch_size

    • 参数类型: int

    • 默认值: 1

    • 作用: 指定每个批次的样本数量。批次大小会影响到模型的收敛速度和更新频率。

  3. shuffle

    • 参数类型: bool

    • 默认值: False

    • 作用: 如果设置为True,则每个epoch开始时,DataLoader会随机打乱数据集中的样本顺序。

  4. num_workers

    • 参数类型: int

    • 默认值: 0

    • 作用: 指定有多少个子进程用于加载数据。0表示数据将在主进程中加载(不使用子进程)。增加num_workers可以提高数据加载的效率,特别是在数据预处理较为复杂或者数据存储在慢速存储介质上时。

  5. 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
2
tensor_trans = transforms.ToTensor()
tensor_img = tensor_trans(img)

使用SummaryWriteradd_image()方法将Tensor图像添加到TensorBoard中。这样,我们就可以在TensorBoard中查看和处理这个图像了。

1
writer.add_image("Tensor_img", tensor_img)

归一化操作

下面是一个示例代码,展示了如何使用PyTorch的transforms模块进行图像归一化:

1
2
3
4
5
6
7
# Totensor
trans_totensor = transforms.ToTensor()
img_tensor = trans_totensor(img)

# Normalize - 注意这里的参数可能不适用于所有图像,应根据实际情况进行调整
trans_norm = transforms.Normalize([0.5, 0.5, 0.5], [0.5, 0.5, 0.5]) # 假设图像范围为[0, 1],则均值和标准差都设为0.5进行归一化
img_norm = trans_norm(img_tensor)

图像调整大小

在调整大小之前,可以通过 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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from PIL import Image
from torch.utils.tensorboard import SummaryWriter
from torchvision import transforms

writer = SummaryWriter("test04")
img = Image.open("image.jpg")

# 定义预处理流程:先调整大小,然后转换为张量,并进行归一化(这里省略了归一化步骤以简化示例)
preprocessing = transforms.Compose([
transforms.Resize([300, 500]), # 调整图像大小(注意保持纵横比或适当填充以避免失真)
transforms.ToTensor(), # 将PIL图像转换为PyTorch张量(值范围在[0, 255]内)
# 添加归一化等其他操作(如果需要的话)...
])

# 应用预处理流程并保存结果到TensorBoard日志中(实际应用中可能需要进一步处理或添加标签等)
img_preprocessed = preprocessing(img)
writer.add_image("Preprocessed Image", img_preprocessed, 0) # 使用全局步数0,实际应用中应根据需要动态设置(例如训练迭代次数等)

损失函数

Pytorch中也提供了许多的损失函数

1
2
3
4
5
6
7
# L1损失函数
loss = L1Loss(reduction='sum')
result = loss(inputs, targets)

# MSE损失函数
loss_mse = nn.MSELoss()
result_mse = loss_mse(inputs, targets)

计算正确率

output=model(input)得到的是浮点数矩阵,如果想要知道类别,则可以用.argmax()函数得到输出的类别,然后用predict==targets得到布尔矩阵。最后对矩阵torch.sum()得到正确个数。

1
2
3
4
5
6
7
import torch  
outputs = torch.tensor([[0.1, 0.2], [0.3, 0.4]])
#.argmax(1)是对每一行求最大值的位置,.argmax(0)是对每一列
print(outputs.argmax(1))
preds = outputs.argmax(1)
targets = torch.tensor([0, 1])
print(preds == targets)

PyTorch卷积

PyTorch中torch.nn模块的使用

在PyTorch中,torch.nn模块提供了构建神经网络所需的所有构建块。

1
2
import torch.nn as nn
import torch.nn.functional as F

接下来,我们定义一个模型类Model,它继承自nn.Module。在构造函数__init__中,我们初始化两个卷积层conv1conv2nn.Conv2d是二维卷积层,它接收的参数包括输入和输出通道数、卷积核大小等。

1
2
3
4
5
class 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
2
3
def forward(self, x):
x = F.relu(self.conv1(x)) # 第一个卷积层后接ReLU激活
return F.relu(self.conv2(x)) # 第二个卷积层后接ReLU激活

这样,我们就定义了一个简单的卷积神经网络模型。在实际使用时,我们会创建这个模型类的实例,然后传入数据来进行训练和测试。

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
2
optimizer = optim.SGD(model.parameters(), lr=0.01, momentum=0.9)  
optimizer = optim.Adam([var1, var2], lr=0.0001)

主要要定义的有两个:

  1. 模型参数:给定模型参数优化器才知道更新哪些内容
  2. 学习速率lr

训练过程

训练过程写法如下:

1
2
3
4
5
6
for input, target in dataset:  
optimizer.zero_grad() # 梯度清零
output = model(input) # 前向传播
loss = loss_fn(output, target) #损失
loss.backward() # 反向传播
optimizer.step()

完整代码

与自己的模型综合起来,效果大概是这样

1
2
3
4
5
6
7
8
9
10
loss = nn.CrossEntropyLoss()  
model = Model()
optim = torch.optim.SGD(tudui.parameters(), lr=0.01)
for data in dataloader:
imgs, targets = data
outputs = model (imgs)
result_loss = loss(outputs, targets)
optim.zero_grad()
result_loss.backward()
optim.step()

利用Sequential简化模块

Sequential也在torch.nn模块中,利用其,可以避免在前向传播的过程中手写每层的输入输出。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Model(nn.Module):
def __init__(self):
super(Model, self).__init__()
self.model1 = Sequential(
Conv2d(3, 32, 5, padding=2),
MaxPool2d(2),
Conv2d(32, 32, 5, padding=2),
MaxPool2d(2),
Conv2d(32, 64, 5, padding=2),
MaxPool2d(2),
Flatten(),
Linear(1024, 64),
Linear(64, 10)
)
def forward(self, x):
x = self.model1(x)
return x

可视化模块

  1. 可以用print(Model)将模型显示在控制台
  2. SummaryWriteradd_graph(Model,input)在TensorBorad中显示

保存模型

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

#保存
#方式一,保存整个模型
torch.save(vgg16, "vgg16_method1.pth")
#方式二,只保存参数
torch.save(vgg16.state_dict(), "vgg16_method2.pth")

#加载
#方式一对应的加载模式
model = torch.load("vgg16_method1.pth")
#方式二对应的加载模式
vgg16 = torchvision.models.vgg16(pretrained=False)
vgg16.load_state_dict(torch.load("vgg16_method2.pth"))

加载自身的模型的时候需要给出类的定义。

PyTorch 2D卷积

1
2
import torch
import torch.nn.functional as F

1. 创建输入张量和卷积核

输入张量(input)和卷积核(kernel)被定义为多维张量。

1
2
3
4
5
6
7
8
9
10
11
12
input = 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
2
input = torch.reshape(input, (1, 1, 5, 5))
kernel = torch.reshape(kernel, (1, 1, 3, 3))

在PyTorch中,使用torch.nn.functional.conv2d函数进行卷积操作时,输入和卷积核需要是四维张量。这四个维度分别是:

  1. 批量大小(Batch size):表示输入数据的数量。
  2. 通道数(Channels):对于彩色图像,通常是3(红、绿、蓝)。
  3. 高度(Height):图像的高度。
  4. 宽度(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
2
# 创建一个最大池化层,池化窗口大小为2x2,步长为2
max_pool = nn.MaxPool2d(kernel_size=2, stride=2)

其的主要参数为:

  1. kernel_size:表示池化窗口的大小。对于二维最大池化,它可以是单个整数或两个整数的元组(height, width)。如果是一个整数,则池化窗口将是一个正方形。
  2. stride:这是一个元组或整数,表示池化窗口在输入张量上移动的步长。默认情况下,stridekernel_size相同。
  3. padding:这是一个元组或整数,表示在输入张量的每个边上添加的填充数量。默认情况下,没有填充(padding=0)。

完整的训练过程

  1. 准备数据集

    1
    2
    train_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)
  2. 加载数据集

    1
    2
    train_dataloader = DataLoader(train_data, batch_size=64)
    test_dataloader = DataLoader(test_data, batch_size=64)
  3. 搭建神经网络

    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
  4. 创建网络模型,定义损失函数及优化器

    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)
  5. 设置训练网络中的参数

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    # 设置训练网络的一些参数

    # 记录训练的次数
    total_train_step = 0

    # 记录测试的次数
    total_test_step = 0

    # 训练的轮数
    epoch = 10
  6. 开始训练

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    for 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()))
  7. 在测试集上测试模型

    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
2
if torch.cuda.is_available():  
model = model.cuda()

第二种方法

可以用以下内容代替.cuda()

1
2
device = torch.device("gpu")
model = model.to(device)