pytorch猫狗大战VGG16

1.简介 

1.1 问题描述:

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

1.2预期解决方案:

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

1.3数据集

百度网盘 请输入提取码

提取码:jc34  

1.4图像展示

文件包含训练集train和测试集test ,训练集前12500张图片为cat,后12500张图片为dog。

 

2.数据预处理 

1.1训练集分类

训练集train中前12500张图片为cat,后12500张图片为dog,所以需将train文件中的图片分别转移到cat文件和dog文件中,方便对其分类。假设我们下载好的train文件是放在data目录下的,则我们需要专门建一个文件夹用于保存用于训练与测试的文件。在运行以下代码之前需要先新建一个文件catdog,再在catdog中新建train文件,再在train文件中新建cat和dog文件。

import os
import shutil

# 定义路径
data_dir = "data/train"
cat_dir = "catdog/train/cat"
dog_dir = "catdog/train/dog"

# 创建目标目录
os.makedirs(cat_dir, exist_ok=True)
os.makedirs(dog_dir, exist_ok=True)

# 移动猫的图片
for i in range(1, 12501):
    src_path = os.path.join(data_dir, f"{i}.jpg")
    dest_path = os.path.join(cat_dir, f"{i}.jpg")
    shutil.move(src_path, dest_path)

# 移动狗的图片
for i in range(12501, 25001):
    src_path = os.path.join(data_dir, f"{i}.jpg")
    dest_path = os.path.join(dog_dir, f"{i}.jpg")
    shutil.move(src_path, dest_path)

运行完成后catdog文件夹因如图所示 ,运行完成后请删除代码以免再次运行报错

 1.2构建测试集

 猫狗识别数据集的test文件无标签,所以我们需要将train中的图片分出一些做测试集,从catdog/train/cat和catdog/train/dog分别分出2500张图片用作测试集。在catdog目录下新建文件test,在test文件下新建cat和dog文件。运行以下代码:

import os
import shutil

def move_images(src_folder, dest_folder, category, num_images, img_format='jpg'):
    # 创建目标文件夹
    os.makedirs(os.path.join(dest_folder, category), exist_ok=True)

    # 获取源文件夹中的所有图片文件
    src_path = os.path.join(src_folder, category)
    image_files = [f for f in os.listdir(src_path) if f.endswith(f".{img_format}")]

    # 移动指定数量的图片到目标文件夹
    for i in range(num_images):
        src_file = os.path.join(src_path, image_files[i])
        dest_file = os.path.join(dest_folder, category, image_files[i])
        shutil.move(src_file, dest_file)

# 指定源文件夹和目标文件夹以及需要移动的图片数量
src_folder = 'catdog/train'
dest_folder = 'catdog/test'
num_images_to_move = 2500

# 移动cat类别的.jpg格式图片
move_images(src_folder, dest_folder, 'cat', num_images_to_move)

# 移动dog类别的.jpg格式图片
move_images(src_folder, dest_folder, 'dog', num_images_to_move)

运行完成后文件如图所示,运行完成请删除上面代码,以免再次运行报错

 3.训练

3.1导入相关包

我的环境为PyTorch (Intel® oneAPI 2024)

import os
import sys
import time
import argparse
import itertools
import torch
import torch.nn.functional as F
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
import intel_extension_for_pytorch as ipex
import pandas as pd 

from torch import nn
from torch import optim
from torch.autograd import Variable
from torchvision import models
from matplotlib.patches import Rectangle
from sklearn.metrics import confusion_matrix, accuracy_score, balanced_accuracy_score
from torchvision import transforms
from PIL import Image
from torch.utils.data import Dataset, DataLoader, SubsetRandomSampler
from torchvision import transforms
from sklearn.model_selection import train_test_split, StratifiedKFold
from torch.utils.data import DataLoader
from sklearn.metrics import f1_score

3.2 数据检查

查看train/cat和dog图片的数量,查看test/cat和dog的数量

import cv2
# 正常和肺炎的路径
cat_imgs = [fn for fn in os.listdir('catdog/train/cat') if fn.endswith('.jpg') and fn.startswith('cat')]
dog_imgs = [fn for fn in os.listdir('catdog/train/dog') if fn.endswith('.jpg') and fn.startswith('dog')]
test_cat= [fn for fn in os.listdir('catdog/test/cat') if fn.endswith('.jpg') ]
test_dog = [fn for fn in os.listdir('catdog/test/dog') if fn.endswith('.jpg') ]
print(f'cat的数量为: {len(cat_imgs)}')
print(f'dog的数量为: {len(dog_imgs)}')
print(len(test_cat))
print(len(test_dog))


运行结果应为:

3.3 创建数据集函数

创建自定义数据集,用于将读取train和test中的图像与标签 

# 创建自定义数据集
class SelfDataset(Dataset):
    def __init__(self, root_dir, transform=None):
        self.root_dir = root_dir
        self.transform = transform
        self.classes = ['cat', 'dog']
        self.data = self.load_data()

    def load_data(self):
        data = []
        for class_idx, class_name in enumerate(self.classes):
            print(class_name)
            class_path = os.path.join(self.root_dir,class_name)
            for file_name in os.listdir(class_path):
                file_path = os.path.join(class_path, file_name)
                if os.path.isfile(file_path) and file_name.lower().endswith('.jpg'):
                    data.append((file_path, class_idx))
        return data

    def __len__(self):
        return len(self.data)

    def __getitem__(self, idx):
        img_path, label = self.data[idx]
        img = Image.open(img_path).convert('RGB')
        if self.transform:
            img = self.transform(img)
        return img, label

3.4创建数据集

使用PyTorch中的`transforms`模块创建了一个数据增强的操作序列。数据增强是在训练深度学习模型时常用的一种技术,通过对原始数据进行随机变换,增加数据的多样性,有助于提升模型的泛化性能。

  1. transforms.RandomResizedCrop(64):随机裁剪输入图像,同时进行缩放,生成64x64大小的新图像。这有助于模型学习对不同尺寸和位置的物体进行有效识别。
  2. transforms.RandomHorizontalFlip():以一定的概率(默认为0.5)对图像进行水平翻转。这种变换有助于模型学习不同方向的物体特征,增加数据的多样性。
  3. transforms.ToTensor():将图像转换为PyTorch张量。深度学习模型通常使用张量作为输入,因此这一步是将图像数据格式从PIL Image转换为PyTorch可处理的形式。

其次是为train和test中的数据创建数据集,用于后续的模型训练。

训练集train: 用于训练机器学习模型。模型通过学习训练集中的样本来调整参数,使其能够捕捉输入数据的模式和特征。

测试集test:用于评估训练好的模型的性能。测试集中的样本是模型在训练和验证过程中未曾见过的数据,因此测试集上的性能评估更接近模型在真实场景中的表现。

# 数据集路径
train_dataset_path = 'catdog/train'
test_dataset_path = 'catdog/test'

# 数据增强
transform = transforms.Compose([
    transforms.RandomResizedCrop(64),
    transforms.RandomHorizontalFlip(),
    transforms.ToTensor(),
])


# 创建数据集实例
train_dataset = SelfDataset(root_dir=train_dataset_path, transform=transform)
test_dataset = SelfDataset(root_dir=test_dataset_path, transform=transform)

# 创建 DataLoader
batch_size = 64
train_loader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True)
test_loader = DataLoader(test_dataset, batch_size=batch_size, shuffle=False)

3.5定义VGG16模型

        使用PyTorch中的VGG16模型,并对其进行微调以适应特定的图像分类任务。在修改分类层中,创建一个新的分类器,替换VGG16模型的原始分类器。新的分类器包括一个全连接层(线性层)和一些非线性激活和正则化操作。过这一步骤,模型的最后一层从原始的1000类别(ImageNet分类数)修改为适应特定任务的2类别(在这里是猫和狗的分类)

vgg16_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)
)

3.6设置参数

用于配置深度学习模型的训练相关参数,包括损失函数、优化器、学习率调度器以及设备选择。criterion = nn.CrossEntropyLoss()定义交叉熵损失函数,用于衡量模型输出与真实标签之间的差异。适用于多类别分类任务,如本例中的猫狗分类。

criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(vgg16_model.parameters(), lr=0.001, weight_decay=1e-4)
# 添加学习率调度器
# 使用 ReduceLROnPlateau 调度器
scheduler = optim.lr_scheduler.ReduceLROnPlateau(optimizer, mode='max', factor=0.1, patience=3, verbose=True)

# 训练参数
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
vgg16_model.to(device)

3.7训练

代码实现了一个简单的训练循环,对VGG16模型进行训练和测试,并在每轮训练后输出训练损失和测试F1分数。num_epochs为训练次数,以下代码为测试代码所以num_epochs较小,在正式运行时可根据需要将num_epochs增大。训练过程为vgg16_model.train()将模型设置为训练模式。循环遍历训练数据集,计算训练损失,并进行反向传播和优化。输出每轮训练的平均训练损失。

# 训练循环
num_epochs = 0
consecutive_f1_count = 0

while num_epochs < 2:
    print(f'第{num_epochs+1}次训练开始了')
    vgg16_model.train()  # 设置模型为训练模式
    train_loss = 0.0
    for inputs, labels in train_loader:
       
        inputs, labels = inputs.to(device), labels.to(device)

        # 将数据传递给模型
        outputs = vgg16_model(inputs)

        # 计算损失
        loss = criterion(outputs, labels)

        # 反向传播和优化
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()

        train_loss += loss.item()

    # 计算平均训练损失
    avg_train_loss = train_loss / len(train_loader)

    # 计算平均验证损失


    # 打印训练过程中的损失和验证损失
    print(f'Epoch [{num_epochs+1}], 第{num_epochs+1}轮:训练集损失: {avg_train_loss:.4f}')

    # 在模型训练完后,使用测试集进行最终评估
    vgg16_model.eval()
    all_predictions = []
    all_labels = []
    start_time = time.time()  # 记录开始时间
    with torch.no_grad():
        for inputs, labels in test_loader:
            inputs, labels = inputs.to(device), labels.to(device)

            # 在测试集上进行推理
            outputs = vgg16_model(inputs)

            # 将预测结果和真实标签保存
            _, predicted = torch.max(outputs, 1)
            all_predictions.extend(predicted.cpu().numpy())
            all_labels.extend(labels.cpu().numpy())

    end_time = time.time()  # 记录结束时间
    elapsed_time = end_time - start_time
    print(f'测试集用的时间为: {elapsed_time:.2f} seconds')

    # 计算F1分数
    f1 = f1_score(all_labels, all_predictions, average='binary')  # 适用于二分类问题

    # 打印每轮的测试F1分数
    print(f'第{num_epochs+1}轮的测试F1分数: {f1:.4f}')


    # 调整学习率
    scheduler.step(f1)

    # 增加训练次数
    num_epochs += 1

训练结果如下:

3.8使用模型进行测试

模型训练完成后,可以使用数据集中的test无标签的图片进行测试(训练集下载文件包中的test文件)。test中的图片无标签,猫狗图片混杂。以下为test中图片展示,扩展名为.jpg

import matplotlib.pyplot as plt
import numpy as np
from PIL import Image  # Ensure you have the PIL library installed

# Assuming you have defined data_transform earlier
data_transform = transforms.Compose([
    transforms.Resize(224),
    transforms.CenterCrop(224),
    transforms.ToTensor(),
    transforms.Normalize(mean=[0.5, 0.5, 0.5], std=[0.5, 0.5, 0.5])
])

# 选择一张指定图像进行训练
image_path = 'data/test/10030.jpg'  # 替换为你的图像文件路径
sample_image = Image.open(image_path).convert('RGB')
sample_image = data_transform(sample_image)
sample_image = sample_image.unsqueeze(0)  # 添加批次维度

# 将图片传递给模型进行预测
sample_image = sample_image.to(device)
with torch.no_grad():
    model_output = vgg16_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()
print(predicted_label)

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

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

选用数据集中的无标签的三张猫狗图片进行测试 ,更改图片途径来选择图片,得到的结果如下:

4.保存模型与使用oneAPI组件

4.1保存模型

保存模型为vgg16_model.path,保存的位置与代码文件目录一致。

# 保存模型
torch.save(vgg16_model.state_dict(), 'vgg16_model.pth')

# 打印保存成功的消息
print("模型已保存为 vgg16_model.pth")

运行结果如下,模型保存位置与代码文件位置一致 

4.2使用oneAPI组件

Transfer Learning with oneAPI AI Analytics Toolkit进行迁移学习,这段代码定义了一个名为CustomVGG16的自定义模型类,该类继承自nn.Module。冻结了卷积层的权重,并修改了最后一层以适应二分类任务。然后,加载了预训练的权重,使得模型具备在大规模图像数据上学到的特征。

class CustomVGG16(nn.Module):
    def __init__(self):
        super(CustomVGG16, self).__init__()
        self.vgg16_model = models.vgg16(pretrained=True)
        for param in self.vgg16_model.features.parameters():
            param.requires_grad = False
        num_features = self.vgg16_model.classifier[6].in_features
        self.vgg16_model.classifier[6] = nn.Sequential(
            nn.Linear(num_features, 512),
            nn.ReLU(),
            nn.Dropout(0.5),
            nn.Linear(512, 2)
        )

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

# 创建 CustomVGG16 模型实例
vgg16_model = CustomVGG16()



# 加载权重
vgg16_model.vgg16_model.load_state_dict(torch.load('vgg16_model.pth', map_location=torch.device('cpu')))


4.2使用Intel Extension for PyTorch进行优化

这段代码涉及将PyTorch模型迁移到CPU上并使用Intel Extension for PyTorch(Intel oneAPI工具包的一部分)进行优化。使用Intel Extension for PyTorch能明显提高速度。大概缩短了一倍的时间,并且F1的值并没有改变。


# 将模型移动到CPU
device = torch.device('cpu')
vgg16_model.to(device)

# 重新构建优化器
optimizer = optim.Adam(vgg16_model.parameters(), lr=0.001, weight_decay=1e-4)

# 使用Intel Extension for PyTorch进行优化
vgg16_model, optimizer = ipex.optimize(model=vgg16_model, optimizer=optimizer, dtype=torch.float32)

4.3对Intel Extension for PyTorch进行优化的模型进行F1值计算

vgg16_model.eval()

# Assuming you have a DataLoader for the test dataset (test_loader)
all_predictions = []
all_labels = []
start_time = time.time()

with torch.no_grad():
    for inputs, labels in test_loader:
        inputs, labels = inputs.to(device), labels.to(device)
        outputs = vgg16_model(inputs)
        _, predicted = torch.max(outputs, 1)
        all_predictions.extend(predicted.cpu().numpy())
        all_labels.extend(labels.cpu().numpy())

end_time = time.time()  # 记录结束时间
elapsed_time = end_time - start_time
print(f'测试集用的时间为: {elapsed_time:.2f} seconds')
f1 = f1_score(all_labels, all_predictions, average='binary')  # 适用于二分类问题
print(f'F1分数为: {f1:.4f}')

使用 Intel Extension for PyTorch进行优化的模型F1为:

4.4保存使用Intel Extension for PyTorch进行优化的模型

# 保存模型参数
torch.save(vgg16_model.state_dict(), 'vgg16_optimized.pth')
 
# 加载模型参数
loaded_model = CustomVGG16()
loaded_model.load_state_dict(torch.load('vgg16_optimized.pth'))

4.5使用 Intel® Neural Compressor 量化模型

 对优化后的模型vgg16_optimized.pth进行加载

import os
import torch
 
# 检查文件是否存在
assert os.path.exists("./vgg16_optimized.pth"), "文件不存在"
 
# 尝试加载模型
model = torch.load("./vgg16_optimized.pth")
print("模型加载成功")

 4.6量化

代码的作用是加载一个预训练的VGG16模型,将其移动到CPU上,然后进行后训练量化。该量化过程使用了Intel PyTorch Extension,并配置了量化参数,包括准确度评估标准、量化误差等。将量化后的模型保存在指定路径下。

from neural_compressor.config import PostTrainingQuantConfig, AccuracyCriterion
from neural_compressor import quantization
import os
 
# 加载模型
model = CustomVGG16()
model.load_state_dict(torch.load('vgg16_optimized.pth'))
model.to('cpu')  # 将模型移动到 CPU
model.eval()
 
# 定义评估函数
def eval_func(model):
    with torch.no_grad():
        y_true = []
        y_pred = []
 
        for inputs, labels in train_loader:
            inputs = inputs.to('cpu')
            labels = labels.to('cpu')
            preds_probs = model(inputs)
            preds_class = torch.argmax(preds_probs, dim=-1)
            y_true.extend(labels.numpy())
            y_pred.extend(preds_class.numpy())
 
        return accuracy_score(y_true, y_pred)
 
# 配置量化参数
conf = PostTrainingQuantConfig(backend='ipex',  # 使用 Intel PyTorch Extension
                               accuracy_criterion=AccuracyCriterion(higher_is_better=True, 
                                                                   criterion='relative',  
                                                                   tolerable_loss=0.01))
 
# 执行量化
q_model = quantization.fit(model,
                           conf,
                           calib_dataloader=train_loader,
                           eval_func=eval_func)
 
# 保存量化模型
quantized_model_path = './quantized_models'
if not os.path.exists(quantized_model_path):
    os.makedirs(quantized_model_path)
 
q_model.save(quantized_model_path)

4.7使用量化模型

import torch
import json
 
from neural_compressor import quantization
 
# 指定量化模型的路径
quantized_model_path = './quantized_models'
 
# 加载 Qt 模型和 JSON 配置
vgg16_model_path = f'{quantized_model_path}/best_model.pt'
json_config_path = f'{quantized_model_path}/best_configure.json'
 
# 加载 Qt 模型
vgg16_model = torch.jit.load(vgg16_model_path, map_location='cpu')
 
# 加载 JSON 配置
with open(json_config_path, 'r') as json_file:
    json_config = json.load(json_file)
 
# 打印 JSON 配置(可选)
print(json_config)

4.8使用模型计算f1值

import torch
from sklearn.metrics import f1_score
import time
 
# 假设 test_loader 是你的测试数据加载器
# 请确保它返回 (inputs, labels) 的形式
 
 
# 将模型设置为评估模式
vgg16_model.eval()
 
# 初始化变量用于存储真实标签和预测标签
y_true = []
y_pred = []
 
# 开始推理
start_time = time.time()
 
# 设置 batch_size
batch_size = 64
 
# 使用 DataLoader 时设置 batch_size
test_loader = DataLoader(test_dataset, batch_size=batch_size, shuffle=False)
 
# 在推理时处理每个批次
 
 
with torch.no_grad():
    for inputs, labels in test_loader:
        # 将输入数据移动到 CPU(如果尚未在 CPU 上)
        inputs = inputs.to('cpu')
        labels = labels.to('cpu')
 
        # 获取模型预测
        preds_probs = vgg16_model(inputs)
        preds_class = torch.argmax(preds_probs, dim=-1)
 
        # 扩展真实标签和预测标签列表
        y_true.extend(labels.numpy())
        y_pred.extend(preds_class.numpy())
 
# 计算 F1 分数
f1 = f1_score(y_true, y_pred, average='weighted')
 
# 计算推理时间
inference_time = time.time() - start_time
 
# 打印结果
print(f"测试集用的时间为: {inference_time} seconds")
print(f"F1分数: {f1}")
 

最后的运行结果:

  • 21
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值