Pytorch快速入门基本操作

视频链接:PyTorch深度学习快速入门教程(绝对通俗易懂!)【小土堆】
Pytorch官网:https://pytorch.org/
Pytorch官方文档:https://pytorch.org/docs/stable/index.html

一. 数据集

首先要构建数据集之前我们必须先了解两个概念,分别是 DatasetDataloader

  • Dataset:Dataset 是一个抽象的类,我们需要定义一个自己的类来继承这个Dataset类的资源,用于获取数据及其标签

  • Dataloader:DataLoader是一个可以直接调用的类,可以对他实例化调用,用于为后面的网络提供不同的数据形式

1.1 构建自己的Dataset

构建自己的Dataset时,只需要构建一个如下所示的类,这个类继承了官方Dataset类的资源:

在这里插入图片描述

  • __getitem__:用于获取索引 index 对应的图片及其标签。
  • __len__:用于获取数据集的长度。

下面用一个实例介绍如何构建自己的Dataset。

我在dataset->train这个路径下有两组图片,标签分别是ants和bees:
在这里插入图片描述
用下面这段代码即可构建训练集的Dataset:

from torch.utils.data import Dataset
from PIL import Image
import os


class MyData(Dataset):

    def __init__(self, root_dir, 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 = os.listdir(self.path)
        self.data_len = len(self.img_path)

    def __getitem__(self, idx):
        img_name = self.img_path[idx]
        img_item_path = os.path.join(self.path, img_name)
        img = Image.open(img_item_path)
        label = self.label_dir
        return img, label

    def __len__(self):
        return self.data_len


root_dire = "dataset/train" #根目录地址
ants_label_dir = "ants"
bees_label_dir = "bees"
ants_dataset = MyData(root_dire, ants_label_dir) # ants数据的dataset
bees_dataset = MyData(root_dire, bees_label_dir) # bees数据的dataset

train_dataset = ants_dataset+bees_dataset # 将两组dataset合并

其他数据集的结构或许与上述案例的结构不同,但是我们只需要构建一个满足上述要求的类即可,程序根据数据集结构的不同需要做相应的调整。

1.2 使用官方提供的Dataset

我们可以调用Pytorch的API下载官方提供的数据集,我们在官网上找到数据集对应的API格式,然后调用即可。

我们以CIFAR10数据集为例,这是一个10分类的数据集,官方API调用方法如图所示:

在这里插入图片描述
运行下面两行代码即可创建训练集和测试集的Dataset:

train_dataset = torchvision.datasets.CIFAR10("./dataset", train=True, transform=torchvision.transforms.ToTensor(), download=True)
test_dataset = torchvision.datasets.CIFAR10("./dataset", train=False, transform=torchvision.transforms.ToTensor(), download=True)

1.3 创建Dataloader

如图是Dataloader函数的调用方法:

在这里插入图片描述
其中比较常用的参数有:

  • dataset:即前面创建好的dataset
  • batch_size:即每次取出多少份数据
  • shuffle:每次取数据之前是否进行打乱操作
  • num_workers:num_workers=0表示只有主进程去加载batch数据,num_workers>0 表示只有指定数量的worker进程去加载数据,主进程不参与。增加num_works也同时会增加cpu内存的消耗。所以num_workers的值依赖于batch_size和机器性能。
  • drop_last:当最后一组数据不足batch_size大小时是否要丢弃

每次从dataloader中取数据时就会同时取出batch_size份数据和他们对应的标签。

下面这段代码展示了dataloader的用法:

import torchvision
from torch.utils.data import DataLoader

test_set = torchvision.datasets.CIFAR10(root="./dataset", train=False, transform=torchvision.transforms.ToTensor())

test_loader = DataLoader(dataset=test_set, batch_size=4, shuffle=True, num_workers=0, drop_last=False)

# 取出第一组查看数据结构
for imgs, labels in test_loader:
    print(imgs.shape)
    print(labels.shape)
    break

输出为:

torch.Size([4, 3, 32, 32]) #四张图片,每张图片大小为32*32*3
torch.Size([4]) #四个标签,代表四个类别

二. Transforms的用法

2.1 什么是Transforms

Transforms是用于对输入图片进行变换的一个工具,主要包括修改大小Resize、标准化Normalize、格式转换ToTensor等操作,还包括一些随机裁剪、旋转、翻转等数据增强的操作,常在构建Dataset时使用。

在这里插入图片描述

在这里插入图片描述

2.2 Transforms使用方法

Transforms的使用方法如下图所示:
在这里插入图片描述
ToTensor为例,下面几行代码即可实现将PIL格式的图片转换为Tensor格式 (封装了神经网络训练需要的参数)

from PIL import Image
from torchvision import transforms

img_path = "dataset/dog.jpg"
img = Image.open(img_path)

tensor_trans = transforms.ToTensor()
tensor_img = tensor_trans(img)

如果我们需要按顺序对图片进行多种变换,那么我们可以使用Compose将多种变换组合到一起,下面用一个例子讲解如何使用。

输入一张图片,对它进行中心裁剪后转换为Tensor数据格式,然后将其数据类型转换为浮点型,代码如下:

import torch
from PIL import Image
from torchvision import transforms

img_trans = transforms.Compose([
    transforms.CenterCrop(100),
    transforms.PILToTensor(),
    transforms.ConvertImageDtype(torch.float),
])
img = Image.open("airplane.jpg")
img2 = img_trans(img)

变换前后数据格式如下所示:

在这里插入图片描述

三. 模型

3.1 创建自己的神经网络模型

下面我们构建一个如图所示的神经网络模型,这是cifar10的模型结构:
在这里插入图片描述

import torch
from torch import nn
from torch.nn import Sequential, Conv2d, MaxPool2d, Flatten, Linear

class model(nn.Module):
    def __init__(self):
        super(model, self).__init__()
        self.cifar = Sequential(
            Conv2d(3, 32, kernel_size=5, padding=2),
            MaxPool2d(2),
            Conv2d(32, 32, kernel_size=5, padding=2),
            MaxPool2d(2),
            Conv2d(32, 64, kernel_size=5, padding=2),
            MaxPool2d(2),
            Flatten(),
            Linear(1024, 64),
            Linear(64, 10)
        )

    def forward(self, input):
        output = self.cifar(input)
        return output


if __name__ == "__main__":
    Model = model()
    input = torch.ones((64, 3, 32, 32))
    output = Model(input)
    print(output.shape)

输出为:

torch.Size([64, 10])

即当输入为64张(32,32,3)大小的图片时,输出为这64张图片在10个类别中对应的分数。

3.2 调用官方提供的模型

官方提供了很多常用的模型结构,通过调用相应的API即可创建模型:

在这里插入图片描述
以VGG16为例:

在这里插入图片描述
其中,当 pretrained 为True时,返回模型中的参数为在ImageNet数据集中训练好的参数;当 pretrained 为False时,返回模型中的参数为随机初始化的参数。

import torchvision

VGG16_false = torchvision.models.vgg16(pretrained=False)
VGG16_true = torchvision.models.vgg16(pretrained=True)

3.3 增加/删除/修改模型层

还是以上面提到的VGG16为例,我们输出模型的结构看看:

print(VGG16_true)

输出为:
VGG(
  (features): Sequential(
    (0): Conv2d(3, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (1): ReLU(inplace=True)
    (2): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (3): ReLU(inplace=True)
    (4): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
    (5): Conv2d(64, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (6): ReLU(inplace=True)
    (7): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (8): ReLU(inplace=True)
    (9): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
    (10): Conv2d(128, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (11): ReLU(inplace=True)
    (12): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (13): ReLU(inplace=True)
    (14): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (15): ReLU(inplace=True)
    (16): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
    (17): Conv2d(256, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (18): ReLU(inplace=True)
    (19): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (20): ReLU(inplace=True)
    (21): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (22): ReLU(inplace=True)
    (23): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
    (24): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (25): ReLU(inplace=True)
    (26): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (27): ReLU(inplace=True)
    (28): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (29): ReLU(inplace=True)
    (30): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
  )
  (avgpool): AdaptiveAvgPool2d(output_size=(7, 7))
  (classifier): Sequential(
    (0): Linear(in_features=25088, out_features=4096, bias=True)
    (1): ReLU(inplace=True)
    (2): Dropout(p=0.5, inplace=False)
    (3): Linear(in_features=4096, out_features=4096, bias=True)
    (4): ReLU(inplace=True)
    (5): Dropout(p=0.5, inplace=False)
    (6): Linear(in_features=4096, out_features=1000, bias=True)
  )
)

通过调用add_module函数,即可在模型中添加层。例如我们想在模型中名为classifier的序列中添加第七层:

VGG16_true.classifier.add_module('7', nn.Linear(1000,10)) 

可以得到:
在这里插入图片描述
如果我们想把第7层删除,运行如下语句即可:

del(VGG16_true.classifier[7])

在这里插入图片描述
如果我们想修改第6层,运行如下语句即可:

VGG16_true.classifier[6] = nn.Linear(4096, 10) 

在这里插入图片描述

3.4 保存/加载模型

模型的保存和加载有两种方式,第一种是保存完整的模型结构和参数

# 保存方式1:保存模型结构和参数
torch.save(vgg16, "vgg16_method1.pth")
# 加载方式1: 加载时需要导入网络模型
model = torch.load("vgg16_method.pth")

在用第一种方法加载模型时有一个易错点,就是我们在导入前需要导入完整的模型结构,例如:

class model(nn.Module):
    def __init__(self):
        super(model, self).__init__()
        self.cifar = Sequential(
            Conv2d(3, 32, kernel_size=5, padding=2),
            MaxPool2d(2),
            Conv2d(32, 32, kernel_size=5, padding=2),
            MaxPool2d(2),
            Conv2d(32, 64, kernel_size=5, padding=2),
            MaxPool2d(2),
            Flatten(),
            Linear(1024, 64),
            Linear(64, 10)
        )

    def forward(self, input):
        output = self.cifar(input)
        return output


model = torch.load("best_Model.pth")

我们需要把模型结构放在加载模型的前面,但是无需初始化。

第二种方法是只保存模型的参数(官方推荐),加载时需要初始化模型,再导入参数。以VGG16为例:

# 保存方式2: 仅保存参数
torch.save(vgg16.load_state_dict(), "vgg16_method2.pth")
# 加载方式2
vgg16 = torchvision.models.vgg16(pretrained=False) # 获取模型结构
vgg16.load_state_dict(torch.load("vgg16_method.pth")) #加载模型参数

四. 完整的模型训练及预测套路

4.1 完整的模型训练套路

数据集用的是官方提供的CIFAR10,即10分类数据集。

import torch.optim
import torchvision
from torch import nn
from torch.nn import Sequential, Conv2d, MaxPool2d, Flatten, Linear
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter
import time
from model import *

# 定义训练设备
# device = torch.device("cpu") # device = torch.device("cuda") GPU

# 准备数据集
train_data = torchvision.datasets.CIFAR10("./dataset", train=True, transform=torchvision.transforms.ToTensor(),
                                          download=True)
test_data = torchvision.datasets.CIFAR10("./dataset", train=False, transform=torchvision.transforms.ToTensor(),
                                          download=True)

train_data_size = len(train_data)
test_data_size = len(test_data)

print("训练数据集的长度为:{}".format(train_data_size))
print("测试数据集的长度为:{}".format(test_data_size))

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

# 初始化网络模型
Model = model() 
# 定义损失函数
Loss = nn.CrossEntropyLoss() 
# 使用GPU训练:仅模型,损失函数和数据集可用
if torch.cuda.is_available():
    # Model = Model.to(device)
    # Loss = Loss.to(device)
    Model = Model.cuda()
    Loss = Loss.cuda()
# 创建优化器
learning_rate = 0.02 # 学习率
optimizer = torch.optim.SGD(Model.parameters(), lr=learning_rate) 
# 定义训练时需要用到的参数
train_step = 0 # 记录当前训练的数据批次
test_step = 0
epoches = 50 # 总训练轮数
best_accurate = 0 # 最高的准确率
# 用于可视化
writer = SummaryWriter("logs")

start_time = time.time()

for epoch in range(epoches):
    train_step = 0
    print("----------第{}轮训练开始----------".format(epoch+1))

    # 训练步骤开始
    # Model.train() # 有 dropout、batchnorm等特殊层时才需调用
    for data in train_dataloader:
        imgs, label = data
        # 用GPU
        if torch.cuda.is_available():
            # imgs = imgs.to(device)
            # label = label.to(device)
            imgs = imgs.cuda()
            label = label.cuda()
        predict = Model(imgs)
        training_loss = Loss(predict, label)

        # 优化器优化模型(下面三句顺序不能颠倒)
        optimizer.zero_grad() # 清空之前梯度
        training_loss.backward() # 反向传播计算梯度
        optimizer.step() # 更新参数

        train_step += 1
        if train_step % 100 == 0:
            end_time = time.time()
            print("训练轮数: {}, 训练批次: {}, Loss: {}, 花费时间: {}".format(epoch+1,train_step,training_loss.item(),end_time-start_time))
            writer.add_scalar("train_loss", training_loss.item(), train_step)

    # 测试步骤开始
    # Model.eval()
    total_test_loss = 0
    total_accuracy = 0
    with torch.no_grad():
        for data in test_dataloader:
            imgs, label = data
            # 用GPU
            if torch.cuda.is_available():
                # imgs = imgs.to(device)
                # label = label.to(device)
                imgs = imgs.cuda()
                label = label.cuda()
            predict = Model(imgs)
            testing_loss = Loss(predict, label)
            total_test_loss += testing_loss
            accuracy = (predict.argmax(axis=1) == label).sum()
            total_accuracy += accuracy
            
    print("整体测试集上的Loss: {}".format(total_test_loss))
    print("整体测试集上的正确率: {}".format(total_accuracy/test_data_size))
    
    # 可视化,在命令行输入 tensorboard --logdir=logs 后打开链接即可看到可视化结果
    writer.add_scalar("test_loss", total_test_loss, test_step)
    writer.add_scalar("test_accuracy", total_accuracy/test_data_size, test_step)
    test_step += 1

    # 保存准确率最高的模型
    if total_accuracy/test_data_size > best_accurate:
        best_accurate = total_accuracy/test_data_size
        torch.save(Model, "best_Model.pth")
        print("best_Model已保存, 准确率为: {}".format(best_accurate))

    # 保存方式1
    # torch.save(Model, "Model_{}.pth".format(epoch+1))
    # 保存方式2
    # torch.save(Model.state_dict(), "Model_{}.pth".format(epoch+1))
    # print("模型 {} 已保存".format(epoch+1))

writer.close()

在这里插入图片描述

运行结束后将会保存一个名为best_Model.pth的模型文件(用第一种方式保存)。

理解optimizer.zero_grad(), loss.backward(), optimizer.step()的作用及原理

4.2 完整的模型预测套路

import torch
import torchvision.transforms
from PIL import Image
from torch import nn
from torch.nn import Sequential, Conv2d, MaxPool2d, Flatten, Linear

class_name = ['airplane', 'automobile', 'bird', 'cat', 'deer', 'dog', 'frog', 'horse', 'ship', 'truck']

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

img_path = "airplane.jpg"
img = Image.open(img_path)
img.show(img)

transform = torchvision.transforms.Compose([torchvision.transforms.Resize((32, 32)),
                                            torchvision.transforms.ToTensor()])

img = transform(img)
print(img.shape)


class model(nn.Module):
    def __init__(self):
        super(model, self).__init__()
        self.cifar = Sequential(
            Conv2d(3, 32, kernel_size=5, padding=2),
            MaxPool2d(2),
            Conv2d(32, 32, kernel_size=5, padding=2),
            MaxPool2d(2),
            Conv2d(32, 64, kernel_size=5, padding=2),
            MaxPool2d(2),
            Flatten(),
            Linear(1024, 64),
            Linear(64, 10)
        )

    def forward(self, input):
        output = self.cifar(input)
        return output


model = torch.load("best_Model.pth")
# model = torch.load("best_Model.pth", map_location=torch.device('cpu')) #用了这句就不需要将图片转为GPU类型

img = torch.reshape(img, (1, 3, 32, 32))
model.eval()
with torch.no_grad():
    img = img.to(device)
    output = model(img)
print(output)
print(class_name[output.argmax(1).item()])

输入一张图片:
在这里插入图片描述
输出:

torch.Size([3, 32, 32])
tensor([[10.7850, -7.1911,  7.7740,  0.5391,  4.8232, -1.3408, -6.9061, -6.8120,
         -0.7704, -2.6107]], device='cuda:0')
airplane
  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一个PyTorch的主函数框架,可以用于训练和测试神经网络模型: ```python import torch import torch.nn as nn import torch.optim as optim from torch.utils.data import DataLoader # 定义神经网络模型 class Net(nn.Module): def __init__(self): super(Net, self).__init__() # 定义网络结构 def forward(self, x): # 定义前向传播过程 return x # 定义训练函数 def train(model, train_loader, optimizer, criterion): model.train() for batch_idx, (data, target) in enumerate(train_loader): optimizer.zero_grad() output = model(data) loss = criterion(output, target) loss.backward() optimizer.step() # 定义测试函数 def test(model, test_loader, criterion): model.eval() test_loss = 0 correct = 0 with torch.no_grad(): for data, target in test_loader: output = model(data) test_loss += criterion(output, target).item() pred = output.argmax(dim=1, keepdim=True) correct += pred.eq(target.view_as(pred)).sum().item() test_loss /= len(test_loader.dataset) accuracy = 100. * correct / len(test_loader.dataset) print('Test set: Average loss: {:.4f}, Accuracy: {}/{} ({:.0f}%)'.format( test_loss, correct, len(test_loader.dataset), accuracy)) # 加载数据集 train_loader = DataLoader(...) test_loader = DataLoader(...) # 初始化模型、损失函数和优化器 model = Net() criterion = nn.CrossEntropyLoss() optimizer = optim.SGD(model.parameters(), lr=0.01, momentum=0.5) # 训练和测试模型 for epoch in range(1, 11): train(model, train_loader, optimizer, criterion) test(model, test_loader, criterion) ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值