pytroch版猫狗分类

题目描述:

问题中,你将面一个典的机器学分类挑——猫狗大。你的任是建立一个分类模型,能够准确地区分像中是猫是狗

期解决方案:

你的目标是通过训练一个机器学习模型,使其在给定一张图像时能够准确地预测图像中是猫还是狗。模型应该能够推广到未见过的图像,并在测试数据上表现良好。期待您将其部署到模的生产环境中——里推理时间和二分类准确度(F1分数)将作为评分的主要依据

数据集:

链接:百度网盘 请输入提取码 

提取码:jc34

通过问题来看,我们要进行环境配置、数据处理、搭建VGG模型、模型训练、测试集验证几个步骤。

环境配置

       对于这个问题如果要用英特尔oneAPI Developer Cloud提供的免安装基于Jupyter Lab或SSH的环境,需要进行环境配置。

        首先注册一个新的英特尔用户账号,网页地址:https://devcloud.intel.com/oneapi/home/

按照网页地址的提示信息完成注册后,进行激活。激活画面如下图

      就可以通过点击网页左侧“Get Started”进入该选项的页面,在“Get Started”选项页面中, 用户可以 通过点击页面最左下角 Connect with Jupyter* Lab 中的“Launch JupyterLab*”按钮直接启动 Jupypter 服 务。

          

进入Jupyter* Lab后选择pytroch2024版就完成环境的配置了。如果你使用其他类型的pytroch也是可以完成这个实验的

二、数据集处理:

            猫狗大战数据集共分为test和train两个文件夹,test文件夹里面的图片没有标签,因此我们仅使用train文件夹内的图片,部分图片如下,可以看到图片的标签为文件名的前三个字母,猫为cat,狗为dog。如图:

      我们将猫狗大战的数据集下载下来后,上传到Jupyter* Lab平台上,上传后需要解压才能使用。如下图:

进行到这一步我们就可以进行代码的编写了:

加载包:

import matplotlib.pyplot as plt
import torch
import torch.nn as nn
from torch.optim import Adam
from torchvision import transforms
from torchvision import models
from torchvision.io import read_image
from torch.nn import functional as F
from torch.utils.data import Dataset, DataLoader
import numpy as np
from sklearn.model_selection import StratifiedShuffleSplit
import osos.environ['TORCH_HOME']='dogandcat/download_model'

# 获取训练集图片路径
train_path = '/home/u209887/dogandcat/train/'

image_file_path = np.array([train_path + i for i in os.listdir(train_path)])

# 根据文件名获取图片对应的标签
labels = np.array([
    0 if name.split('/')[-1].startswith('cat') else 1
    for name in image_file_path
])

print(len(image_file_path),len(labels))

)

结果如图:

# 查看结果
image_file_path[:10], labels[:10]

结果如图:

    使用 StratifiedShuffleSplit 进行随机分层抽样,将25000张图片按 8:1:1 的比例分为训练集、验证集和测试集,且每个数据集中猫和狗的占比均相同 

# 分层抽样

# 将数据集按照 8:2 的比例分为训练集和其他数据集
train_val_sss = StratifiedShuffleSplit(n_splits=1, test_size=0.1, random_state=2021)

for train_index, val_index in train_val_sss.split(image_file_path, labels):
    train_path, val_path = image_file_path[train_index],image_file_path[val_index]
    train_labels, val_labels = labels[train_index],labels[val_index]
    

# 将其他数据集按照 1:1 的比例分为验证集和测试集
val_test_sss = StratifiedShuffleSplit(n_splits=1, test_size=0.5, random_state=2021)

for val_index, test_index in val_test_sss.split(val_path, val_labels):
    val_path, test_path = val_path[val_index],val_path[test_index]
    val_labels, test_labels = val_labels[val_index],val_labels[test_index]
    
# 最终我们得到 8:1:1 的训练集、验证集和测试集
# 每个数据集中猫和狗所占比例相同

查看分层抽样结果
# 由于猫的标签是0,狗的标签是1,因此我们可以用.sum()函数获取到狗的个数
print(len(train_path),len(train_labels),train_labels.sum())
print(len(val_path),len(val_labels),val_labels.sum())
print(len(test_path),len(test_labels),test_labels.sum())

结果如图:

  • 定义Pytorch数据加载类
  • # 定义Dataset加载方式
  • class MyData(Dataset):
        def __init__(self, filepath, labels=None, transform=None):
            self.filepath = filepath
            self.labels = labels
            self.transform = transform

        def __getitem__(self, index):
            image = read_image(self.filepath[index]) # 读取后的图片维度是[channl, height, width]
            image = image.to(torch.float32) / 255. # 转换为float32类型,除以255进行归一化
            if self.transform is not None:
                image = self.transform(image)
            if self.labels is not None:
                return image, self.labels[index]
            return image

        def __len__(self):
            return self.filepath.shape[0]

  • 定义一些指标
    image_size = [224, 224]  # 图片大小
    batch_size = 64 # 批大小

    定义图像增强器
    transform = transforms.Compose([
        transforms.RandomHorizontalFlip(),    # 随机水平翻转
        transforms.RandomRotation(30),    # 随机旋转
        transforms.Resize([256, 256]),    # 设置图片大小
        transforms.RandomCrop(image_size),    # 将图片随机裁剪为所需图片大小
        transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]) # 使用ImageNet的标准化参数
    ])
    获取数据集对象
    ds1 = MyData(train_path, train_labels, transform)
    ds2 = MyData(val_path, val_labels, transform)
    ds3 = MyData(test_path, test_labels, transform)

    train_ds = DataLoader(ds1, batch_size=batch_size, shuffle=True)
    val_ds = DataLoader(ds2, batch_size=batch_size, shuffle=True)
    test_ds = DataLoader(ds3, batch_size=batch_size, shuffle=True)

    针对单张图片,查看图像增强的效果
    # 对一张图片查看增强效果

    image = read_image(train_path[0])
    image = image.to(torch.float32) / 255.    # 读取单张图片,并进行归一化
    image = image.unsqueeze(0)    # 为单张图片加入batch维度

    plt.figure(figsize=(12,12))
    mean = np.array([0.485, 0.456, 0.406]) # 图像增强所用的标准化参数
    std = np.array([0.229, 0.224, 0.225])
    for i in range(25):
        plt.subplot(5,5,i+1)
        img = transform(image)
        img = img[0,:,:,:].data.numpy().transpose([1,2,0]) # 将图片从[channel,height,width]变为[height,width,channel]
        img = std * img + mean # 反标准化
        img = np.clip(img, 0, 1) # 将像素值限制在0~1之间
        plt.imshow(img)
        plt.axis('off')
    plt.show()

    结果如图

  • 查看训练集一个批次的图片
    # 查看训练集一个batch的图片
    for step, (bx, by) in enumerate(train_ds):
        if step > 0:
            break
        plt.figure(figsize=(16,16))
        for i in range(len(by)):
            plt.subplot(8,8,i+1)
            image = bx[i,:,:,:].data.numpy().transpose([1,2,0])
            image = std * image + mean # 反标准化
            image = np.clip(image, 0, 1)
            plt.imshow(image)
            plt.axis('off')
            plt.title('cat' if by[i].data.numpy()==0 else 'dog')
        plt.show()

  • 构建模型
    此次采用的是VGG模型

  • model = models.vgg16(pretrained=True)

    # 如果需要微调,可以解冻最后几层
    for param in vgg16_model.features.parameters():
        param.requires_grad = False

    # 修改分类层
    num_features = vgg16_model.classifier[6].in_features
    vgg16_model.classifier[6] = nn.Sequential(
        nn.Linear(num_features, 512),
        nn.ReLU(),
        nn.Dropout(0.5),
        nn.Linear(512, 2)
    )
     



    打印模型
    print(model)

    将模型迁移到GPU上,以提高训练速度
    # 如果有GPU则使用GPU,否则使用CPU
    device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
    model = model.to(device)

    定义优化器和损失函数
    optimizer = Adam(model.parameters(), lr=0.0001)
    loss_fun = F.cross_entropy

    训练模型
    定义训练函数
    def train(model, epoch, train_ds):
        model.train() # 开启训练模式
        total_num = len(train_ds.dataset) # 训练集总个数
        train_loss = 0    # 训练损失
        correct_num = 0 # 分类正确的个数

        for image, label in train_ds:
            image = image.to(device)    # 将图片和标签都迁移到GPU上进行加速
            label = label.to(device)
            label = label.to(torch.long) # 将标签从int32类型转化为long类型,否则计算损失会报错

            output = model(image) # 获取模型输出
            loss = loss_fun(output, label) # 计算交叉熵损失
            train_loss += loss.item() * label.size(0) # 累计总训练损失
            optimizer.zero_grad()    # 清除优化器上一次的梯度
            loss.backward()    # 进行反向传播
            optimizer.step()    # 更新优化器

            predict = torch.argmax(output, dim=-1)    # 获取预测类别
            correct_num += label.eq(predict).sum() # 统计预测正确的个数

        train_loss = train_loss / total_num    # 计算一个轮次的训练损失
        train_acc = correct_num / total_num    # 计算一个轮次的训练准确率
        # 打印相关信息
        print('epoch: {} --> train_loss: {:.6f} - train_acc: {:.6f} - '.format(
            epoch, train_loss, train_acc), end='') 

    定义测试函数
    def evaluate(model, eval_ds, mode='val'):
        model.eval() # 开启测试模式

        total_num = len(eval_ds.dataset)
        eval_loss = 0
        correct_num = 0

        for image, label in eval_ds:
            image = image.to(device)
            label = label.to(device)
            label = label.to(torch.long)
            
            output = model(image)
            loss = loss_fun(output, label)
            eval_loss += loss.item() * label.size(0)

            predict = torch.argmax(output, dim=-1)
            correct_num += label.eq(predict).sum()
        
        eval_loss = eval_loss / total_num
        eval_acc = correct_num / total_num
        
        print('{}_loss: {:.6f} - {}_acc: {:.6f}'.format(
            mode, eval_loss, mode, eval_acc))

  • 开始训练,训练20个epoch
for epoch in range(20):
    train(model, epoch, train_ds)
    evaluate(model, val_ds)
  • 在测试集上测试模型
  • import time
    start_time = time.time()
    # 测试模型
    evaluate(model, test_ds, mode='test')
    end_time = time.time()  # 记录结束时间       
    predict_time = end_time - start_time
    print(f'推理时间为: {predict_time:.3f} seconds')
  • 所以最后所得推理时间和f1值为:
  • 因为用oneapi平台上的pytroch没有GPU,所以在测试集上的时间有点大,但在预测结果上还行
  • 优化模型,调参
  • # 解冻特征提取层
    for name, m in model.named_parameters():
        if name.split('.')[0] != 'fc':
            m.requires_grad_(True)
            
    # 使用一个学习率更小的优化器
    optimizer = Adam(model.parameters(), lr=0.00001)
  • 所得推理时间和f1值为:

  • 但从结果来看优化的并不理想,只是在损失函数和运行时间上缩短了一点点
  • 图片预测:
  • import matplotlib.pyplot as plt
    import numpy as np

    # 选择一张 test_ds 中的图片
    sample_image, true_label = next(iter(test_ds))

    # 将图片传递给模型进行预测
    sample_image = sample_image.to(device)
    with torch.no_grad():
        model_output = model(sample_image)

    # 获取预测结果
    _, predicted_label = torch.max(model_output, 1)

    # 转换为 NumPy 数组
    sample_image = sample_image.cpu().numpy()[0]  # 将数据从 GPU 移回 CPU 并取出第一张图片
    predicted_label = predicted_label[0].item()

    true_label = true_label[0].item()  # 直接获取标量值

    # 获取类别标签
    class_labels = ['cat', 'dog']

    # 显示图像
    plt.imshow(np.transpose(sample_image, (1, 2, 0)))  # 转置图片的维度顺序
    plt.title(f'TRUE LABEL IS: {class_labels[true_label]}, PREDICT LABEL IS: {class_labels[predicted_label]}')
    plt.axis('off')
    plt.show()

  • 预测结果为:

  • 23
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
好的,我来讲一下如何使用PyTorch进行分类。 首先,我们需要准备数据集。可以从Kaggle上下载一个包含的图片的数据集,然后将其分成训练集和验证集。 然后,我们需要定义一个PyTorch数据集类,用于加载和预处理图像数据。这个类需要实现__len__和__getitem__方法,其中__len__返回数据集的大小,__getitem__返回指定索引的数据样本。 接下来,我们需要定义一个PyTorch模型类,用于构建我们的分类模型。可以使用预训练的模型,如ResNet或VGG,作为我们的基础模型,并添加自定义头部来适应我们的数据集。 然后,我们需要定义一个PyTorch训练循环,用于训练我们的模型。这个循环包括数据加载、前向传递、损失计算、反向传播和参数更新等步骤。 最后,我们需要定义一个PyTorch验证循环,用于测试我们的模型的性能。这个循环包括数据加载、前向传递和结果输出等步骤。 以下是一个示例代码,展示了如何使用PyTorch进行分类: ``` import torch import torchvision import torchvision.transforms as transforms import torch.nn as nn import torch.optim as optim # 定义数据集类 class CatDogDataset(torch.utils.data.Dataset): def __init__(self, data_dir, transform=None): self.data = [] self.targets = [] self.transform = transform for img_file in os.listdir(data_dir): img_path = os.path.join(data_dir, img_file) target = 0 if img_file.startswith('cat') else 1 self.data.append(img_path) self.targets.append(target) def __len__(self): return len(self.data) def __getitem__(self, idx): img_path = self.data[idx] target = self.targets[idx] img = Image.open(img_path).convert('RGB') if self.transform: img = self.transform(img) return img, target # 定义模型类 class CatDogModel(nn.Module): def __init__(self, base_model): super().__init__() self.base_model = base_model self.head = nn.Sequential( nn.Linear(1000, 256), nn.ReLU(), nn.Linear(256, 2) ) def forward(self, x): x = self.base_model(x) x = self.head(x) return x # 定义训练循环 def train(model, train_loader, criterion, optimizer): model.train() train_loss = 0 train_acc = 0 for inputs, targets in train_loader: inputs, targets = inputs.to(device), targets.to(device) optimizer.zero_grad() outputs = model(inputs) loss = criterion(outputs, targets) loss.backward() optimizer.step() train_loss += loss.item() * inputs.size(0) train_acc += (outputs.argmax(dim=1) == targets).sum().item() train_loss /=
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值