(从0-1带你了解)Pytorch之模型的读取

目录

简述

代码实现

导入所需要的库

数据预处理

加载图像数据和标签

GPU的检测

定义卷积神经网络

训练深度学习模型

测试模型的性能

保存最优模型的2种方

提取模型的2种方法

执行测试


简述

我们有时需要把训练好的模型部署到很多不同的设备。在这种情况下,我们可以把内存中训练好的模型参数存储在硬盘上供后续读取使用。

我们想要保存训练好的模型,等需要用来进行图像分类等任务的时候,不经训练,直接加载使用。
这时,可以采用torch.save(model, 'best.pt')保存模型。

代码实现

代码所需文件请点击此处免费下载

train.txt       test.txt

接下来用一个小案例来解释模型的读取

导入所需要的库

import torch  #这行代码引入了PyTorch库,可以用于张量计算和神经网络的构建。
#从PyTorch库中引入了Dataset和DataLoader类。Dataset类是用于加载和预处理数据的,而DataLoader则是用于批量加载和打散数据
from torch.utils.data import Dataset,DataLoader

import numpy as np #用于处理数组
from PIL import Image #引入了Pillow库中的Image模块,可以用于处理图像
from torchvision import transforms  #这个模块包含了很多用于图像预处理的函数,例如裁剪、旋转、翻转等

数据预处理

data_transforms = {
    'train':    #训练集
        transforms.Compose([       #transforms.Compose对象的列表,该列表包含一系列的图像转换操作。
        transforms.Resize([300,300]),   #是图像变换大小

        transforms.RandomRotation(45),#随机旋转,-45到45度之间随机选
        transforms.CenterCrop(256),#从中心开始裁剪
        transforms.RandomHorizontalFlip(p=0.5),#随机水平翻转 选择一个概率概率
        transforms.RandomVerticalFlip(p=0.5),#随机垂直翻转
        # transforms.ColorJitter(brightness=0.2, contrast=0.1, saturation=0.1, hue=0.1),#参数1为亮度,参数2为对比度,参数3为饱和度,参数4为色相
        transforms.RandomGrayscale(p=0.1),#概率转换成灰度率,3通道就是R=G=B
        transforms.ToTensor(),
        transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])#归一化,均值,标准差
    ]),
    'valid':  #测试集
        transforms.Compose([
        transforms.Resize([256,256]),
        transforms.ToTensor(),
        transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
    ]),
}

加载图像数据和标签

class food_dataset(Dataset):    #food_dataset是自己创建的类名称,可以改为你需要的名称
##file_path是包含图像文件路径和标签的文本文件的路径,transform是一个可选的图像转换操作,默认为None
    def __init__(self, file_path,transform=None): 
        self.file_path = file_path
        self.imgs = []    #初始化两个空列表,用于存储图像文件路径和对应的标签
        self.labels = []
        self.transform = transform  # 将传入的transform参数存储在类的实例变量transform中
        with open(self.file_path) as f:
#读取文件的每一行,去除首尾空格并使用空格分割,形成一个列表。
            samples = [x.strip().split(' ') for x in f.readlines()]
#遍历列表中的每个元素,其中img_path是图像文件的路径,label是对应的标签
            for img_path, label in samples:
#将图像文件路径添加到self.imgs列表中
                self.imgs.append(img_path)
#将标签添加到self.labels列表中
                self.labels.append(label) 

    def __len__(self):  #类实例化对象后,可以使用len函数测量对象的个数
        return len(self.imgs)

    def __getitem__(self, idx): #关键,可通过索引的形式获取每一个图片数据及标签
        image = Image.open(self.imgs[idx])   #使用PIL库的Image.open方法打开指定路径的图像文件
        if self.transform:
            image = self.transform(image)

        label = self.labels[idx]  #获取与图像对应的标签
#将标签转换为NumPy数组,然后使用torch.from_numpy将其转换为PyTorch张量,并指定数据类型为torch.int64。
        label = torch.from_numpy(np.array(label,dtype = np.int64))
        return image, label    #返回图像和标签的元组

#传入训练数据文件的路径和指定的图像转换操作
training_data = food_dataset(file_path = './train.txt',transform = data_transforms['train'])
#用于测试数据
test_data = food_dataset(file_path = './test.txt',transform = data_transforms['valid'])

GPU的检测

目的:GPU检测的目的是充分利用可用的硬件资源,提高计算性能,并确保代码在不同环境中的稳定运行。这对于深度学习等需要大量计算的应用尤为重要。

device = "cuda" if torch.cuda.is_available() else "mps" if torch.backends.mps.is_available() else "cpu"
print(f"Using {device} device"

运行结果:

定义卷积神经网络

from torch import nn
class CNN(nn.Module):
    def __init__(self):         # 输入大小 (3, 256, 256)
        super(CNN, self).__init__()
        self.conv1 = nn.Sequential(  #将多个层组合成一起。
            nn.Conv2d(          #2d一般用于图像,3d用于视频数据(多一个时间维度),1d一般用于结构化的序列数据
                in_channels=3,  # 图像通道个数,1表示灰度图(确定了卷积核 组中的个数),
                out_channels=16,# 要得到几多少个特征图,卷积核的个数
                kernel_size=5,  # 卷积核大小,5*5
                stride=1,       # 步长
                padding=2,      # 一般希望卷积核处理后的结果大小与处理前的数据大小相同,效果会比较好。那padding改如何设计呢?建议stride为1,kernel_size = 2*padding+1
            ),                  # 输出的特征图为 (16, 256, 256)
            nn.ReLU(),  # relu层
            nn.MaxPool2d(kernel_size=2),  # 进行池化操作(2x2 区域), 输出结果为: (16, 128, 128)
        )
        self.conv2 = nn.Sequential(  #输入 (16, 128, 128)
            nn.Conv2d(16, 32, 5, 1, 2),  # 输出 (32, 128, 128)
            nn.ReLU(),  # relu层
            nn.Conv2d(32, 32, 5, 1, 2), # 输出 (32, 128, 128)
            nn.ReLU(),
            nn.MaxPool2d(2),  # 输出 (32, 64, 64)
        )

        self.conv3 = nn.Sequential(  #输入 (32, 64, 64)
            nn.Conv2d(32, 64, 5, 1, 2),
            nn.ReLU(),  # 输出 (64, 64, 64)
        )

        self.out = nn.Linear(64 * 64 * 64, 20)  # 全连接层得到的结果

    def forward(self, x):
        x = self.conv1(x)
        x = self.conv2(x)
        x = self.conv3(x)# 输出 (64,64, 32, 32)
        x = x.view(x.size(0), -1)  # flatten操作,结果为:(batch_size, 64 * 32 * 32)
        output = self.out(x)
        return output

model = CNN().to(device)

loss_fn = nn.CrossEntropyLoss() #创建交叉熵损失函数对象,因为手写字识别中一共有10个数字,输出会有10个结果
optimizer = torch.optim.Adam(model.parameters(), lr=0.001)#创建一个优化器Adam

训练深度学习模型

def train(dataloader,model,optimizer,loss_fn):   
    model.train()
  
    for x,y in dataloader:  #x,y 分别是图像和标签
        x,y = x.to(device),y.to(device)  #将数据放入GPU,提高训练结果

        pred = model.forward(x)  #对x进行前向传播,得到训练模型的预测结果
        loss = loss_fn(pred,y)  #使用损失函数来计算预测值pred 和 真实标签y 之间的损失
        optimizer.zero_grad()  # PyTorch 优化器的方法,用于将模型参数的梯度归零,以准备进行新一轮的反向传播。
        loss.backward()   #执行反向传播,计算模型参数相对于损失的梯度
        optimizer.step()    #PyTorch 优化器的方法,用于根据梯度更新模型参数,以减小损失

测试模型的性能

best_acc = 0 
#dataloader(用于提供测试数据批次的数据加载器)、model(深度学习模型),和 loss_fn(损失函数,用于计算模型的预测与真实标签之间的损失)
def test(dataloader, model, loss_fn):
    global best_acc  #全局变量 best_acc
    size = len(dataloader.dataset) #获取测试数据集的大小,用于计算准确率
    num_batches = len(dataloader)  #获取数据加载器中批次的数量,用于计算平均损失
    model.eval()    #将模型设置为评估模式。在评估模式下,模型不会进行训练
    test_loss, correct = 0, 0  #初始化测试损失和正确分类的样本数
    with torch.no_grad():   #一个上下文管理器,关闭梯度计算。当你确认不会调用Tensor.backward()的时候。这可以减少计算所用内存消耗。
        for X, y in dataloader:  # 循环,用于迭代测试数据集中的批次。X 表示图像数据,y 表示标签
            X, y = X.to(device), y.to(device)
            pred = model.forward(X)  #执行前向传播,将输入数据 X 通过深度学习模型 model,并得到模型的预测结果 pred
            test_loss += loss_fn(pred, y).item() #计算模型的预测结果 pred 和真实标签 y 之间的损失,并将其累加到 test_loss 变量中
#使用 argmax 函数找到最大概率的类别)和真实标签 y,然后将匹配的结果转换为浮点数类型,累加到 correct 变量中
            correct += (pred.argmax(1) == y).type(torch.float).sum().item()
    test_loss /= num_batches  #计算平均测试损失,将总测试损失除以批次的数量
    correct /= size  #计算准确率,将正确分类的样本数除以测试数据集的总大小
#印测试结果,包括准确率和平均损失
    print(f"Test result: \n Accuracy: {(100*correct)}%, Avg loss: {test_loss}")
    acc_s.append(correct)
    loss_s.append(test_loss)

保存最优模型的2种方

(模型的文件扩展名一般为:pt)

方法一:

  if correct > best_acc:
        best_acc = correct
        # (w, b) ) 通常用来表示模型的参数,具体指模型的权重 ( w ) 和偏置 ( b )。
#   1、保存模型参数方法:torch.save(model.state_dict(), path)(w,b) 
        print(model.state_dict().keys())    # 输出模型参数名称 
        torch.save(model.state_dict(), "model_parameter.pt")  #保存模型的参数到名为 "model_parameter.pt" 的文件中

方法二:

#   2、保存完整模型(w,b,模型cnn),
        torch.save(model, 'best.pt')

提取模型的2种方法

这里我们不做深度的解析,(详解请点击此处

acc_s = []
loss_s = []

# 提取模型的2种方法:
#   1、读取参数的方法
# model = CNN().to(device)
# model.load_state_dict(torch.load("model_parameter.pt"))
  # 2、读取完整模型的方法,无需提前创建model
model = torch.load('best.pt')

执行测试

model.eval()    #固定模型参数和数据,防止后面被修改
test_dataloader = DataLoader(test_data, batch_size=64, shuffle=True)  
test(test_dataloader, model, loss_fn)  #执行测试

输出结果:

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
你可以按照以下步骤在PyTorch中实现CNN手写数字识别,包括使用CSV文件进行数据读取、保存和加载模型: 1. 导入所需的库和模块: ```python import torch import torch.nn as nn import torch.optim as optim import pandas as pd from torch.utils.data import DataLoader, Dataset ``` 2. 创建一个自定义的数据集类,用于读取CSV文件中的数据: ```python class DigitDataset(Dataset): def __init__(self, csv_file): self.data = pd.read_csv(csv_file) def __len__(self): return len(self.data) def __getitem__(self, idx): image = self.data.iloc[idx, 1:].values.reshape(28, 28).astype('float32') / 255.0 label = self.data.iloc[idx, 0] return image, label ``` 3. 定义CNN模型: ```python class CNN(nn.Module): def __init__(self): super(CNN, self).__init__() self.conv1 = nn.Conv2d(1, 16, kernel_size=3, stride=1, padding=1) self.relu1 = nn.ReLU() self.pool1 = nn.MaxPool2d(kernel_size=2) self.conv2 = nn.Conv2d(16, 32, kernel_size=3, stride=1, padding=1) self.relu2 = nn.ReLU() self.pool2 = nn.MaxPool2d(kernel_size=2) self.fc = nn.Linear(7*7*32, 10) def forward(self, x): x = self.conv1(x) x = self.relu1(x) x = self.pool1(x) x = self.conv2(x) x = self.relu2(x) x = self.pool2(x) x = x.view(x.size(0), -1) x = self.fc(x) return x ``` 4. 定义训练函数和测试函数: ```python def train(model, train_loader, criterion, optimizer): model.train() for images, labels in train_loader: optimizer.zero_grad() outputs = model(images.unsqueeze(1)) loss = criterion(outputs, labels) loss.backward() optimizer.step() def test(model, test_loader): model.eval() correct = 0 with torch.no_grad(): for images, labels in test_loader: outputs = model(images.unsqueeze(1)) _, predicted = torch.max(outputs.data, 1) correct += (predicted == labels).sum().item() accuracy = correct / len(test_loader.dataset) return accuracy ``` 5. 加载数据集并创建数据加载器: ```python train_dataset = DigitDataset('train.csv') test_dataset = DigitDataset('test.csv') train_loader = DataLoader(train_dataset, batch_size=64, shuffle=True) test_loader = DataLoader(test_dataset, batch_size=64, shuffle=False) ``` 6. 创建CNN模型实例、损失函数和优化器: ```python model = CNN() criterion = nn.CrossEntropyLoss() optimizer = optim.Adam(model.parameters(), lr=0.001) ``` 7. 进行训练和测试: ```python num_epochs = 10 for epoch in range(num_epochs): train(model, train_loader, criterion, optimizer) accuracy = test(model, test_loader) print(f'Epoch {epoch+1}, Test Accuracy: {accuracy}') torch.save(model.state_dict(), 'digit_model.pt') ``` 8. 加载保存的模型并进行预测: ```python model = CNN() model.load_state_dict(torch.load('digit_model.pt')) # 假设有一个名为image的张量用于预测 output = model(image.unsqueeze(0).unsqueeze(0)) _, predicted = torch.max(output.data, 1) print(f'Predicted digit: {predicted.item()}') ``` 这就是使用CSV文件进行手写数字识别的基本步骤。你可以根据自己的需求进行修改和优化。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值