Skip to content
0

文章发布较早,内容可能过时,阅读注意甄别。

深度学习笔记

1、pytorch学习

1.1、卷积计算公式

python
torch.nn.Conv2d(in_channels, out_channels, kernel_size, stride=1, padding=0, dilation=1, groups=1, bias=True, padding_mode='zeros', device=None, dtype=None)
Hout=Hin+2×padding[0]dilation[0]×(kernel_size[0]1)1stride[0]+1Wout=Win+2×padding[1]dilation[1]×(kernel_size[1]1)1stride[1]+1

1.2、pytorch的矩阵乘法

  • torch.mul(a, b) 是矩阵a和b对应位相乘,a和b的维度必须相等,比如a的维度是(1, 2),b的维度是(1, 2),返回的仍是(1, 2)的矩阵;
  • torch.mm(a, b) 是矩阵a和b矩阵相乘,比如a的维度是(1, 2),b的维度是(2, 3),返回的就是(1, 3)的矩阵。
  • torch.bmm() 强制规定维度和大小相同
  • torch.matmul() 没有强制规定维度和大小,可以用利用广播机制进行不同维度的相乘操作

2、炼丹框架

2.1、搭建环境

1、创建虚拟环境

sh
conda create -n name python=(版本)

2、安装pytorch包

根据PyTorch官网,以及电脑是否有Nvidia的显卡安装对应版本的pytorch

电脑查看Nvidia显卡,nvidia-sminvidia-smi -L

3、判断pytorch是否安装好,GPU是否可供pytorch使用,无报错即成功

python
import torch
torch.cuda.is_available()

2.2、导入必要的包

python
import torchvision
from torchvision import transforms, datasets
from torch.utils.data import DataLoader

2.3、配置超参数

1、device,常用写法:

python
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')

2.4、获取数据集

2.4.1、定义图片转换(transform)

常见的图片转换:

1、ToTensor()

用法:torchvision.transforms.ToTensor()

作用:==Convert a PIL Image or ndarray to tensor==

2、Normalize()

用法:torchvision.transforms.Normalize(mean, std, inplace=False)

作用:==Normalize a tensor image with mean and standard deviation. This transform does not support PIL Image==

3、Resize()

用法: torchvision.transforms.Resize(size, interpolation=InterpolationMode.BILINEAR, max_size=None, antialias=True)

作用:==Resize the input image to the given size. If the image is torch Tensor, it is expected to have […, H, W] shape, where … means a maximum of two leading dimensions==

4、RandomHorizontalFlip()

用法:torchvision.transforms.RandomHorizontalFlip(p=0.5)

作用:以给定的概率随机水平旋转给定的PIL的图像,默认为0.5;

5、Compose()

用法:torchvision.transforms.Compose(transforms)

作用:Compose用于组合多个图像转换(transform)操作。通过 Compose,可以创建一个转换流程,这个流程可以按顺序执行多个图像处理操作,这些操作可以包括缩放、裁剪、归一化等,其需要的参数是一个列表,其元素类型是transforms类型。 格式:Compose([transforms参数1,transforms参数2,…])

举个例子:

python
img = Image.open(r"/path/of/your/picture")  # 未设置
# ToTensor的用法
trans_totensor = transforms.Compose([
    torchvision.transforms.ToTensor()])
img_tensor = trans_totensor(img)
print(type(img_tensor))
'''
<class 'torch.Tensor'>
'''

2.4.2、加载数据集

2.4.2.1、已知数据集

1、torchvision.datasets的使用

torchvision.datasets中有很多已知的数据集,不同的数据集使用方法略有不同

举例:加载手写字符MNIST数据集

python
dataset_transform = transforms.Compose([
    torchvision.transforms.ToTensor()
])
train_set = torchvision.datasets.MNIST(root=r"/path/to/save/dataset",
                                       train=True, transform=dataset_transform, download=True)
test_set = torchvision.datasets.MNIST(root=r"/path/to/save/dataset",
                                      train=False, transform=dataset_transform, download=True)
# print(type(train_set), type(test_set))
'''
<class 'torchvision.datasets.mnist.MNIST'>
<class 'torchvision.datasets.mnist.MNIST'>
'''

具体了解一下train_settest_set

python
print(len(train_set), len(test_set))  # 60000 10000
print(test_set.classes)  # 查看数据集有哪些类
# ['0 - zero', '1 - one', '2 - two', '3 - three', '4 - four', '5 - five', '6 - six', '7 - seven', '8 - eight','9 - nine']

img, target = test_set[0]
print(img.shape)  # torch.Size([1, 28, 28])
print(type(img))  # <class 'torch.Tensor'>
print(target)  # 7

2、DataLoader的使用

DataLoader主要用于将数据集按批量(batch_size)进行打包,让读取数据按批量进行读取

python
torch.utils.data.DataLoader(dataset, batch_size=1, shuffle=None, sampler=None, batch_sampler=None, num_workers=0, collate_fn=None, pin_memory=False, drop_last=False, timeout=0, worker_init_fn=None, multiprocessing_context=None, generator=None, *, prefetch_factor=None, persistent_workers=False, pin_memory_device='')

主要参数说明

1、dataset,数据集

2、batch_size,批量大小

3、shuffle,为True时表示将数据集进行随机打乱

4、drop_last,为True 表示如果最后一个批次的样本数量小于批次大小,则丢弃该批次;而 为False 则表示保留最后一个不完整的批次

举例,MNIST数据集

python
train_loader = DataLoader(dataset=train_set, batch_size=64, shuffle=True, drop_last=True)
test_loader = DataLoader(dataset=test_set, batch_size=64, shuffle=True, drop_last=True)

查看训练集的第一个批量的数据:

python
# 查看训练集的第一个批量的数据
img, target = next(iter(train_loader))
print(img.shape)  # torch.Size([64, 1, 28, 28])
print(target.shape)  # torch.Size([64])
print(type(target))  # <class 'torch.Tensor'>
print(type(next(iter(train_loader))))  # <class 'list'>

NOTE

这里表示的是train_loader的第一个数据里,数据类型为列表list,列表中有两个元素:

1、批量的图片,形状为[64, 1, 28, 28],分别代表[Batch_size,channels,height,width],表示有64张图片,每张图片的尺寸为(1, 28, 28)

2、同时有64个标签,分别对应这64张图片

2.4.2.2、自定义数据集

2.4.2.2.1、ImageFolder

1、使用这个torchvision.datasets.ImageFolder函数的目录结构需要如下所示:

python
root/
|-- class1/
|   |-- image1.jpg
|   |-- image2.jpg
|   |-- ...
|
|-- class2/
|   |-- image3.jpg
|   |-- image4.jpg
|   |-- ...
|
|-- class3/
|   |-- image5.jpg
|   |-- image6.jpg
|   |-- ...
  • root/ 是数据集的根目录。
  • 每个子文件夹(如 class1/, class2/)代表一个类别(class)。
  • 每个类别文件夹中包含该类别的所有图像文件(如 image1.jpg, image2.jpg 等)

如果符合,则跳过第二步,否则需要进行数据集的划分。

2、数据集划分

请看Data_Split.md

3、之后,使用ImageFolder来读取文件夹

python
torchvision.datasets.ImageFolder(root: str, transform: ~typing.Optional[~typing.Callable] = None, target_transform: ~typing.Optional[~typing.Callable] = None, loader: ~typing.Callable[[str], ~typing.Any] = <function default_loader>, is_valid_file: ~typing.Optional[~typing.Callable[[str], bool]] = None, allow_empty: bool = False

重要参数:

  • root (str or pathlib.Path) – Root directory path.
  • transform (callable, optional) – A function/transform that takes in a PIL image and returns a transformed version. E.g, transforms.RandomCrop

代码示例如下(基于第二步的划分结果):

python
import torchvision
from torch.utils.data import DataLoader

transform = transforms.Compose([transforms.Resize((224, 224)), transforms.ToTensor()])
train_data = torchvision.datasets.ImageFolder(root="/path/of/train", transform=transform)
test_data = torchvision.datasets.ImageFolder(root="/path/of/test", transform=transform)

# 利用DataLoader来加载数据集
train_dataloader = DataLoader(train_data, batch_size=16, shuffle=True)
test_dataloader = DataLoader(test_data, batch_size=16, shuffle=True)

查看DataLoader情况

python
print(len(train_dataloader), len(test_dataloader))

for i, (image, label) in enumerate(test_dataloader):
    print(image.shape)
    print(label)
'''
10 3
torch.Size([16, 3, 224, 224])
tensor([7, 6, 9, 3, 2, 8, 8, 1, 7, 5, 0, 7, 6, 3, 3, 1])
torch.Size([16, 3, 224, 224])
tensor([7, 8, 4, 3, 4, 0, 6, 0, 4, 9, 1, 2, 2, 9, 5, 5])
torch.Size([5, 3, 224, 224])
tensor([5, 2, 6, 0, 4])
'''

2.5、搭建神经网络

神经网络的搭建有很多,可以自己搭建,也可以用预训练的网络。根据不同的任务搭建的网络也不同,这里主要基于==分类==任务进行神经网络的搭建。在搭建之前先了解一下有哪些网络层。

2.5.1、卷积层

python
torch.nn.Conv2d(in_channels, out_channels, kernel_size, stride=1, padding=0, dilation=1, groups=1, bias=True, padding_mode='zeros', device=None, dtype=None)

参数说明:其中in_channels表示输入的特征图的通道数,一般为shape的第二个数字,out_channels是指经过卷积核之后输出的特征图的通道数,kernel_size表示卷积核大小,stride表示移动步长

作用:主要进行特征提取

卷积计算公式:

Hout=Hin+2×padding[0]dilation[0]×(kernel_size[0]1)1stride[0]+1Wout=Win+2×padding[1]dilation[1]×(kernel_size[1]1)1stride[1]+1

输入特征图的通道数 = 卷积核的通道数 输出特征图的通道数 = 卷积核的个数 解释:卷积核的通道数一定和输入的通道数相等,输入对应的每个通道与卷积核对应的每个通道进行计算再求和得到一个通道的卷积输出;而输出特征图的通道数与卷积核的个数相关,有多少个卷积核最终就有多少个输出通道

2.5.2、池化层

2.5.2.1、最大池化层

python
torch.nn.MaxPool2d(kernel_size, stride=None, padding=0, dilation=1, return_indices=False, ceil_mode=False)

(1)参数说明:

1、stride步长的默认大小为kernel_size

2、另外,对于ceil_mode,为True时表示ceil,即向上取整,同时允许kernel有出界部分;为False表示floor,即向下取整,不允许kernel有出界部分。

(2)作用:保留输入的特征,同时减少数据量 加快训练速度

(3)特点:1.没有需要学习的参数;2.通道数保持不变;3.对微小位置的变化具有鲁棒性

2.5.3、非线性激活层

作用:引入非线性的特性,使得神经网络具有更强的表达能力和适应能力

非线性激活层有很多,常见的有ReLUSigmoidTanh等,使用非线性激活层的==输入输出维度相同==

python
torch.nn.ReLU(inplace=False)
torch.nn.Sigmoid(*args, **kwargs)
torch.nn.Tanh(*args, **kwargs)
...

2.5.4、线性层(全连接层)

python
torch.nn.Linear(in_features, out_features, bias=True, device=None, dtype=None)

举例说明:

python
input = torch.randn(128, 20)
m = nn.Linear(20, 30)
output = m(input)
print(output.size())  # torch.Size([128, 30])

2.5.5、其他层

2.5.5.1、Flatten层

python
torch.nn.Flatten(start_dim=1, end_dim=-1)

将输入张量扁平化(flatten)的函数。它将输入张量沿着指定的维度范围进行扁平化处理,并返回一个一维张量作为结果。

举例说明:

python
input = torch.randn(32, 1, 5, 5, 5)
# With default parameters
m = nn.Flatten()
output = m(input)
print(output.size())  # torch.Size([32, 25])
# With non-default parameters
m = nn.Flatten(0, 2)  # 将0-2维度展平,其余不变
output = m(input)
print(output.size())  # torch.Size([160, 5, 5])

2.5.5.2、Sequential层

python
torch.nn.Sequential(*args: Module)

Sequential是一个顺序容器,可以按照添加的顺序依次执行包含的各个模块,torch.nn.Sequential提供了一种简单的方式来构建神经网络模型,代码十分简洁。

举例说明:

python
model = nn.Sequential(
          nn.Conv2d(1,20,5),
          nn.ReLU(),
          nn.Conv2d(20,64,5),
          nn.ReLU()
        )

2.6、定义损失函数

2.6.1、L1Loss(绝对值损失)

python
torch.nn.L1Loss(size_average=None, reduce=None, reduction='mean')

举例说明:

python
import torch
from torch.nn import L1Loss, MSELoss, CrossEntropyLoss

inputs = torch.tensor([1, 2, 3], dtype=torch.float32)
targets = torch.tensor([1, 2, 5], dtype=torch.float32)
#inputs = torch.reshape(inputs, (1, 1, 1, 3))  # 可有可无
#targets = torch.reshape(targets, (1, 1, 1, 3))  # 可有可无
loss_l1_sum = L1Loss(reduction="sum")(inputs, targets)  # (1-1)+(2-2)+(5-3)
loss_l1_mean = L1Loss(reduction="mean")(inputs, targets)  # ((1-1)+(2-2)+(5-3))/3
print(loss_l1_sum, loss_l1_mean)
'''
tensor(2.) tensor(0.6667)
'''

2.6.2、MSELoss(均方损失)

python
torch.nn.MSELoss(size_average=None, reduce=None, reduction='mean')

举例说明:

python
import torch
from torch.nn import L1Loss, MSELoss, CrossEntropyLoss

inputs = torch.tensor([1, 2, 3], dtype=torch.float32)
targets = torch.tensor([1, 2, 5], dtype=torch.float32)
#inputs = torch.reshape(inputs, (1, 1, 1, 3))  # 可有可无
#targets = torch.reshape(targets, (1, 1, 1, 3))  # 可有可无
loss_mse = MSELoss()(inputs, targets)  # ((1-1)^2+(2-2)^2+(5-3)^2)/3
print(loss_mse)
'''
tensor(1.3333)
'''

2.6.3、CrossEntropyLoss(交叉熵损失)

python
torch.nn.CrossEntropyLoss(weight=None, size_average=None, ignore_index=-100, reduce=None, reduction='mean', label_smoothing=0.0)

交叉熵损失函数(Cross-Entropy Loss Function)是在分类问题中经常使用的一种损失函数,特别是在多分类问题中。它衡量了模型输出的概率分布与真实标签之间的差异,通过最小化交叉熵损失来调整模型参数,使得模型更好地适应分类任务。

举例说明:

python
x = torch.tensor([0.1, 0.2, 0.3])
y = torch.tensor([1])
x = torch.reshape(x, (1, 3))
loss_cross = CrossEntropyLoss()(x, y)
print(x.shape, y.shape)  # torch.Size([1, 3]) torch.Size([1])
print(loss_cross)  # tensor(1.1019)

交叉熵损失函数 nn.CrossEntropyLoss() 期望 outputs 的形状是 [batch_size, num_classes],并且 targets 的形状是 [batch_size],它会自动将 targets 进行 one-hot 编码并计算交叉熵损失。

其中 num_classes 是输出的类别数量。

2.7、反向传播

张量梯度的属性:grad 自动求导(Autograd): 在张量上进行操作时,PyTorch 会自动跟踪操作并构建计算图,可以使用 .backward() 方法(反向传播)计算梯度,然后通过 .grad 属性获取梯度值。

2.8、定义优化器

优化器在torch.optim中,常见的有SGDAdam

python
torch.optim.SGD(params, lr=0.001, momentum=0, dampening=0, weight_decay=0, nesterov=False, *, maximize=False, foreach=None, differentiable=False, fused=None)

torch.optim.Adam(params, lr=0.001, betas=(0.9, 0.999), eps=1e-08, weight_decay=0, amsgrad=False, *, foreach=None, maximize=False, capturable=False, differentiable=False, fused=None)

在深度学习中,optimizer.zero_grad()是一个非常重要的操作,它的含义是将模型参数的梯度清零。

在训练神经网络时,通常采用反向传播算法(Backpropagation)来计算损失函数关于模型参数的梯度,并利用优化器(optimizer)来更新模型参数以最小化损失函数。在每次反向传播计算梯度后,梯度信息会被累积在对应的参数张量(tensor)中。如果不清零梯度,在下一次计算梯度时,这些梯度将会被新计算的梯度累加,导致梯度信息错误。

常见的做法:

python
for epoch in range(20):
    for imgs, targets in dataloader:
        optim.zero_grad()
        outputs = model(imgs)
        result_loss = loss(outputs, targets)
        result_loss.backward()  # 反向传播
        optim.step()

for data in dataloader循环: 这个循环通常用于遍历数据集中的每个批次(batch)数据。 dataloader是一个用于批次化处理数据的工具,它会将数据集按照指定的批次大小分割,并提供数据加载的迭代器。 在每次迭代中,for data in dataloader会从数据加载器中获取一个批次的数据,然后你可以对这个批次的数据进行前向传播、计算损失、反向传播和参数更新等操作。 这个循环通常嵌套在训练循环中,用于处理每个训练批次的数据。

for epoch in range(X)循环: 这个循环用于控制整个训练过程的迭代次数,其中X代表训练的总轮数(epochs)。 一个epoch表示将数据集中的所有样本都用于训练一次,通常情况下,训练过程会重复多个epoch以便模型能够更好地学习数据的特征。 在每个epoch循环中,你会执行多次for data in dataloader循环,每次处理一个批次的数据,并进行前向传播、损失计算、反向传播和参数更新等训练步骤。 一般来说,训练过程会在每个epoch结束时进行模型评估,例如计算验证集上的准确率或损失,以便监控模型的训练情况和避免过拟合。

总之,for data in dataloader循环用于处理单个批次的数据,而for epoch in range(X)循环用于控制整个训练过程的迭代次数,确保模型能够在整个数据集上进行多次学习和优化。在实际的训练过程中,这两个循环通常会结合使用,以完成模型的训练任务。

2.9、模式

2.9.1、训练模式

model.train()开启训练模式,模型会跟踪所有层的梯度,以便在优化器(如 torch.optim.SGD 或 torch.optim.Adam)进行梯度下降时更新模型的权重

2.9.2、评估模式

model.eval():开启评估模式,在评估模式下,模型不会跟踪梯度,这有助于减少内存消耗并提高计算效率。

在推理或评估模型时常常搭配使用torch.no_grad(),表明当前计算不需要反向传播,使用之后,强制后边的内容不进行计算图的构建。

2.10、评估指标

2.10.1、准确率(正确率)

准确率的就算方法有很多,下面有两种。

1、使用torch.argmax()

argmax 是一个数学和编程中常用的术语,它表示找到一个函数或数组中最大值的索引或位置。在 PyTorch 中,torch.argmax 是一个函数,用于返回输入张量(Tensor)中最大值的索引。

举例:

python
import torch

output = torch.tensor([[0.1, 0.2],
                       [0.3, 0.2]])
print(output.argmax(1))  # tensor([1, 0])
preds = output.argmax(1)
target = torch.tensor([1, 1])
print((preds == target).sum())  # tensor(1)

所以在训练中可以用accuracy = (outputs.argmax(1) == targets).sum()计算准确率

2、使用torch.max()

torch.max(input, dim, keepdim=False, *, out=None)用于按维度计算最大值,返回值和索引

举例:

python
output = torch.tensor([[1, 2, 3], [5, 3, 2]])
predict = torch.max(output, dim=0)
print(predict)
'''
torch.return_types.max(values=tensor([5, 3, 3]),indices=tensor([1, 1, 0]))
'''
predict = torch.max(output, dim=1)
print(predict)
'''
torch.return_types.max(values=tensor([3, 5]),indices=tensor([2, 0]))
'''

所以在训练中可以使用:

python
# 准确率
def getAccuracy(y_true, y_pred):
    return (y_true == y_pred).sum().item()

values, indices = torch.max(output, 1)
accuracy = getAccuracy(indices, label)

2.11、可视化展示

2.11.1、使用Tensorboard

1、安装

bash
pip install tensorboard

2、使用

python
from torch.utils.tensorboard import SummaryWriter

writer = SummaryWriter("logs") # 日志文件存储位置
writer.add_scalar('....')
writer.close()

3、查看

bash
# 查看logs中的event
tensorboard --logdir="event所在的文件夹路径"  --port=6008(可以修改端口)

2.11.2、使用matplotlib

2.12、模型的保存与读取

2.12.1、模型的保存

两种保存方式: 1、torch.save(model,"model.pth")保存模型结构及模型参数 2、torch.save(model.state_dict(),"model.pth")仅保存模型参数存为字典,不保存模型结构(官方推荐)

例如:

python
import torchvision
import torch
vgg16 = torchvision.models.vgg16(pretrained=False)
# 保存方式1--保存模型结构及模型参数
torch.save(vgg16,"vgg16_method1.pth")

# 保存方式2--仅保存模型参数存为字典,不保存模型结构(官方推荐)
torch.save(vgg16.state_dict(),"vgg16_method2.pth")

2.12.2、模型的读取

1、直接加载模型结构 + 参数方式 2、先定义模型,再加载参数 例如:

python
import torch
import torchvision
# 保存方式1对应的加载模型结构 + 参数方式
model = torch.load("vgg16_method1.pth")
# print(model)

# 保存方式2,先定义模型,再加载参数
model2 = torchvision.models.vgg16(weights=None)
model2.load_state_dict(torch.load("vgg16_model2.pth"))
# print(model2)

2.13、完整的代码框架

2.13.1、训练评估

2.13.1.1、MNIST分类

python
import torch
from torch import nn
from torchvision import datasets, transforms
from torch.utils.data import DataLoader


class NET(nn.Module):
    def __init__(self):
        super().__init__()
        self.model = nn.Sequential(
            nn.Conv2d(1, 10, 5),
            nn.ReLU(),
            nn.MaxPool2d(2, 2),
            nn.Conv2d(10, 20, 3),
            nn.ReLU(),
            nn.Flatten(),
            nn.Linear(20 * 10 * 10, 500),  # 全连接层为线性层
            nn.ReLU(),
            nn.Linear(500, 10),
            # nn.Softmax(dim=1)
        )

    def forward(self, x):
        return self.model(x)


# 定义训练的方法
def train_model(model, device, train_load, loss_fn, optim, epo):
    model.train()
    total_train_loss = 0
    total_train_accu = 0.0
    for batch_index, (img, target) in enumerate(train_load):
        img, target = img.to(device), target.to(device)
        optim.zero_grad()
        output = model(img)
        loss = loss_fn(output, target)
        total_train_loss += loss.item()
        accu = (output.argmax(dim=1) == target).sum().item()
        total_train_accu += accu
        loss.backward()
        optim.step()
        if batch_index % 100 == 0:  # 60000/128=469,每100次输出一个
            print(f"Train Epoch:{epo} [{batch_index}/{len(train_load)}] loss:{loss.item():.6f}")
    total_train_loss /= len(train_load.dataset)
    total_train_accu /= len(train_load.dataset)
    print(f"Train Average Loss:{total_train_loss}, Accuracy:{total_train_accu}")


# 定义测试的方法
def test_model(model, device, test_load, loss_fn):
    model.eval()
    total_test_loss = 0
    total_test_accu = 0.0
    with torch.no_grad():
        for batch_index, (data, target) in enumerate(test_load):
            data, target = data.to(device), target.to(device)
            output2 = model(data)
            loss2 = loss_fn(output2, target)
            total_test_loss += loss2
            accuracy = (output2.argmax(1) == target).sum().item()
            total_test_accu += accuracy
        total_test_loss /= len(test_load.dataset)
        total_test_accu /= len(test_load.dataset)
        print(f"Test Average Loss:{total_test_loss}, Accuracy:{total_test_accu}")


if __name__ == '__main__':
    batch_size = 128
    device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
    epochs = 10
    trans = transforms.Compose([transforms.ToTensor()])
    # 准备数据集
    train_data = datasets.MNIST(root="./dataset", train=True, transform=trans, download=True)
    test_data = datasets.MNIST(root="./dataset", train=False, transform=trans, download=False)
    train_loader = DataLoader(train_data, batch_size=batch_size, shuffle=False)
    test_loader = DataLoader(test_data, batch_size=batch_size, shuffle=True)

    model = NET()
    model.to(device)
    # 损失函数
    loss_fun = nn.CrossEntropyLoss().to(device)
    # 学习率
    learning_rate = 1e-2
    # 优化器
    optimizer = torch.optim.SGD(model.parameters(), lr=learning_rate)
    # 进行训练评估
    for epoch in range(1, epochs + 1):
        train_model(model, device, train_loader, loss_fun, optimizer, epoch)
        test_model(model, device, test_loader, loss_fun)
        torch.save(model.state_dict(), f"model_{epoch}.pth")

2.13.1.2、CIFAR10分类

python
import torchvision
import torch
from torch import nn
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter
import time
'''
    GPU:
    方法一:数据集,损失函数,网络模型加cuda()
    方法二:.to(device)
'''


class Module(nn.Module):
    def __init__(self):
        super().__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.ReLU(),
            nn.Linear(64, 10),
            # nn.ReLU()
            # nn.Sigmoid()
        )

    def forward(self, x):
        x = self.model(x)
        return x


if __name__ == '__main__':
    # 准备数据集
    train_data = torchvision.datasets.CIFAR10(root="./dataset", train=True,
                 transform=torchvision.transforms.ToTensor(), download=True)
    test_data = torchvision.datasets.CIFAR10(root="./dataset", train=False,
                transform=torchvision.transforms.ToTensor(), download=True)
    train_data_size = len(train_data)
    test_data_size = len(test_data)
    # print(f"训练数据集的长度:{train_data_size}")
    # print(f"测试数据集的长度:{test_data_size}")

    # 利用DataLoader来加载数据集
    train_dataloader = DataLoader(train_data, batch_size=64)
    test_dataloader = DataLoader(test_data, batch_size=64)

    device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

    exam = Module()
    exam.to(device)

    # 损失函数
    loss_fn = nn.CrossEntropyLoss()
    loss_fn.to(device)

    # 优化器
    learning_rate = 1e-2
    optimizer = torch.optim.SGD(exam.parameters(), lr=learning_rate)

    # 设置网络模型的一些参数
    # 记录训练的次数
    total_train_step = 0
    # 测试记录的次数
    total_test_step = 0
    # 训练的轮数
    i = 0
    epoch = 50

    # 添加TensorBoard
    writer = SummaryWriter("logs")
    start_time = time.time()
    for i in range(epoch):
        print(f"-----第{i + 1}轮训练开始-----")
        # 训练步骤开始
        exam.train()
        total_train_loss = 0
        train_accu = 0.0
        for data in train_dataloader:
            imgs, targets = data
            imgs = imgs.to(device)
            targets = targets.to(device)
            optimizer.zero_grad()
            outputs = exam(imgs)
            loss = loss_fn(outputs, targets)
            total_train_loss += loss.item()
            accu = (outputs.argmax(1) == targets).sum()
            train_accu += accu
            # 优化器优化模型
            loss.backward()
            optimizer.step()

            # total_train_step += 1
            # if total_train_step % 100 == 0:
        end_time = time.time()
        print(f'time, {((end_time - start_time)):.2f} s')
        # print(f"训练次数:{total_train_step},Loss:{loss.item()}")
        print(f"整体训练集上的Loss:{total_train_loss / train_data_size}")
        print(f"整体训练集的正确率:{train_accu / train_data_size}")
        writer.add_scalar("train_loss", total_train_loss / train_data_size, total_test_step)
        writer.add_scalar("train_accuracy", train_accu / train_data_size, total_test_step)
        # 测试步骤开始
        exam.eval()
        total_test_loss = 0
        total_accuracy = 0
        with torch.no_grad():
            for data in test_dataloader:
                imgs_2, targets_2 = data
                imgs_2 = imgs_2.to(device)
                targets_2 = targets_2.to(device)
                outputs_2 = exam(imgs_2)
                loss_2 = loss_fn(outputs_2, targets_2)
                total_test_loss += loss_2.item()
                accuracy = (outputs_2.argmax(1) == targets_2).sum()
                total_accuracy += accuracy
        print(f"整体测试集上的Loss:{total_test_loss / test_data_size}")
        print(f"整体测试集的正确率:{total_accuracy / test_data_size}")
        writer.add_scalar("test_loss", total_test_loss / test_data_size, total_test_step)
        writer.add_scalar("test_accuracy", total_accuracy / test_data_size, total_test_step)
        total_test_step += 1
        # 保存模型
        torch.save(exam.state_dict(), f"module_{i + 1}.pth")
        print("模型已保存")
    writer.close()

只有数据和标签(imgs和targets)需要进行数据 = 数据.cuda() 或者数据 = 数据.to(device) 模型和损失函数可以直接model.to() ,model.cuda(),loss.to(),loss.cuda()而无需赋值

2.13.2、预训练模型推理

CIFAR10模型推理:

python
from PIL import Image
import torchvision
import torch
from torch import nn


class Tudui(nn.Module):
    def __init__(self):
        super(Tudui, 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(),  # 展平后的序列长度为 64*4*4=1024
            nn.Linear(64 * 4 * 4, 64),
            nn.Linear(64, 10)

        )

    def forward(self, x):
        x = self.model(x)
        return x


if __name__ == '__main__':
    class_names = ['airplane', 'automobile', 'bird', 'cat', 'deer',
                   'dog', 'frog', 'horse', 'ship', 'truck']
    image_path = "../image/frog.jpg"  # 随便一张图片路径
    image = Image.open(image_path)
    image = image.convert("RGB")  # 转换为RGB图
    # image = image.convert('L')    # 转换为灰度图
    # print(image.size)
    device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
    transform = torchvision.transforms.Compose([torchvision.transforms.Resize((32, 32)), torchvision.transforms.ToTensor()])
    image = transform(image)
    # print(image.shape)

    model = Tudui()
    model.load_state_dict(torch.load("module_9.pth", map_location=device))
    # print(model)

    image = torch.reshape(image, (1, 3, 32, 32)).to(device)
    # print(image.shape)
    model.eval()
    with torch.no_grad():
        output = model(image)
    # print(output)
    print("It is a " + class_names[output.argmax(1)] + ".")

使用gpu训练保存的模型在cpu上使用

python
model = torch.load("XXXX.pth",map_location= torch.device("cpu"))
# 或者
model.load_state_dict(torch.load("XXX.pth", map_location=torch.device('cpu')))

MNIST推理同理

python
class_name = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']
image_path = '../image/3.png'  # 随便一张图片路径
image = Image.open(image_path)
image = image.convert('L')    # 转换为灰度图
transformer = torchvision.transforms.Compose([torchvision.transforms.Resize((28,28)),
                                torchvision.transforms.ToTensor()])
image = transformer(image)
# print(image.shape)
...

2.14、其他代码技巧

2.14.1、提取网络层

获取一个网络的每一层以及每一层的输出维度大小

python
import torch
from torch import nn
net = torch.nn.Sequential(
    nn.Conv2d(1, 6, kernel_size=5, padding=2),
    nn.Sigmoid(),
    nn.AvgPool2d(2, stride=2),
    nn.Conv2d(6, 16, kernel_size=5),
    nn.Sigmoid(),
    nn.AvgPool2d(kernel_size=2, stride=2),
    nn.Flatten(),
    nn.Linear(16 * 5 * 5, 120),
    nn.Sigmoid(),
    nn.Linear(120, 84),
    nn.Sigmoid(),
    nn.Linear(84, 10)
)
X = torch.rand(size=(64, 1, 28, 28), dtype=torch.float32)
for layer in net:
    # print(layer)
    X = layer(X)
    print(layer.__class__.__name__, 'output shape:\t', X.shape)
'''
Conv2d output shape:	 torch.Size([64, 6, 28, 28])
Sigmoid output shape:	 torch.Size([64, 6, 28, 28])
AvgPool2d output shape:	 torch.Size([64, 6, 14, 14])
Conv2d output shape:	 torch.Size([64, 16, 10, 10])
Sigmoid output shape:	 torch.Size([64, 16, 10, 10])
AvgPool2d output shape:	 torch.Size([64, 16, 5, 5])
Flatten output shape:	 torch.Size([64, 400])
Linear output shape:	 torch.Size([64, 120])
Sigmoid output shape:	 torch.Size([64, 120])
Linear output shape:	 torch.Size([64, 84])
Sigmoid output shape:	 torch.Size([64, 84])
Linear output shape:	 torch.Size([64, 10])
'''

3、numpy

3.1、矩阵运算

  • 当进行向量的内积运算时,可以通过==np.dot()==

  • 当进行矩阵的乘法运算时,可以通过==np.matmul()==或者==@==

  • 当进行标量的乘法运算时,可以通过==np.multiply()==或者==*==

python
a = np.arange(1, 11).reshape(2, 5)
b = np.arange(5, 15).reshape(5, 2)
print('a', a)
print('b', b)
print('-----------------------------')
# 矩阵乘法
print('a @ b', a @ b)
print('np.matmul(a, b)', np.matmul(a, b))
print('b @ a', b @ a)
print('np.dot(a, b)', np.dot(a, b))
print('-----------------------------')
# 标量乘法,即各元素相乘
print('b.T', b.T)
print('a * b.T', a * b.T)
a [[ 1  2  3  4  5]
 [ 6  7  8  9 10]]
b [[ 5  6]
 [ 7  8]
 [ 9 10]
 [11 12]
 [13 14]]
-----------------------------
a @ b [[155 170]
 [380 420]]
np.matmul(a, b) [[155 170]
 [380 420]]
b @ a [[ 41  52  63  74  85]
 [ 55  70  85 100 115]
 [ 69  88 107 126 145]
 [ 83 106 129 152 175]
 [ 97 124 151 178 205]]
np.dot(a, b) [[155 170]
 [380 420]]
-----------------------------
b.T [[ 5  7  9 11 13]
 [ 6  8 10 12 14]]
a * b.T [[  5  14  27  44  65]
 [ 36  56  80 108 140]]

3.2、维度求和

python
A = np.array([[56.0, 0.0, 4.4, 68.0],
              [1.2, 104.0, 52.0, 8.0],
              [1.8, 135.0, 99.0, 0.9]])
print('A', A.shape, A)
cal_1 = A.sum(axis=0, keepdims=True)
cal_2 = A.sum(axis=1, keepdims=True)
print('cal_1', cal_1.shape, cal_1)
print('cal_2', cal_2.shape, cal_2)
'''
A (3, 4) [[ 56.    0.    4.4  68. ]
		 [  1.2 104.   52.    8. ]
		 [  1.8 135.   99.    0.9]]
cal_1 (1, 4) [[ 59.  239.  155.4  76.9]]
cal_2 (3, 1) [[128.4]
			 [165.2]
			 [236.7]]
'''
最近更新