1.问题简介:猫狗大战(猫狗识别)

1.1问题描述:

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

1.2 预期解决方案

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

1.3数据集

链接:https://pan.baidu.com/s/1kfIuyXuvexREWAJ1ndFs1w
提取码:jc34

1.4图像展示

猫示例图片
狗示例图片

2.数据预处理

2.1数据集结构

本项目数据集共由两部分组成,包括test和train,其中train文件夹下的文件又会被划分为用于训练的train1和用于计算F1分数和准确率的test1。test文件夹里的图片是用于测试最后训练好模型的结果所用图片。
数据集结构

2.2探索性数据分析

先将train的数据集通过2:8划分,分别分成训练数据集合train2和测试数据集test2,然后再在训练数据集中随机选取3张猫的图片和3张狗的图片。

import os
import shutil
import random
from PIL import Image
import matplotlib.pyplot as plt
# 设置数据集路径
dataset_dir = '/kaggle/working/train'  

# 创建训练集和测试集目录
train_dir = '/kaggle/working/train2'  
test_dir = '/kaggle/working/test2'  
os.makedirs(train_dir, exist_ok=True)
os.makedirs(test_dir, exist_ok=True)

# 将猫狗图像分别复制到训练集和测试集目录
for filename in os.listdir(dataset_dir):
    if 'cat' in filename:
        # 复制猫图像到训练集或测试集
        src = os.path.join(dataset_dir, filename)
        if int(filename.split('.')[1]) < 10000:
            dst = os.path.join(train_dir, filename)
        else:
            dst = os.path.join(test_dir, filename)
        shutil.copyfile(src, dst)
    elif 'dog' in filename:
        # 复制狗图像到训练集或测试集
        src = os.path.join(dataset_dir, filename)
        if int(filename.split('.')[1]) < 10000:
            dst = os.path.join(train_dir, filename)
        else:
            dst = os.path.join(test_dir, filename)
        shutil.copyfile(src, dst)
dog_files = [file for file in os.listdir(train_dir) if 'dog' in file]
cat_files = [file for file in os.listdir(train_dir) if 'cat' in file]

# 从狗和猫的文件列表中随机选择3张图片
selected_dog_images = random.sample(dog_files, 3)
selected_cat_images = random.sample(cat_files, 3)

# 打印训练集和测试集的大小
train_set_size = len(os.listdir(train_dir))
test_set_size = len(os.listdir(test_dir))
print("Training Set Size:", train_set_size)
print("Testing Set Size:", test_set_size)

# 展示选择的图片
plt.figure(figsize=(10, 7))

for i, image_file in enumerate(selected_dog_images + selected_cat_images, 1):
    image_path = os.path.join(train_dir, image_file)
    image = Image.open(image_path)
    
    plt.subplot(2, 3, i)
    plt.imshow(image)
    plt.title(image_file)
    plt.axis('off')

plt.show()

运行截图

2.3提取数据集

在本项目中,为了更好地提取出图像,我构建了一个函数构建了一个函数,能够将每个主文件夹下的图片提取出来,并且打好了标签。

import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import Dataset, DataLoader
from torchvision import transforms
from sklearn.metrics import f1_score
class CustomDataset(Dataset):
    def __init__(self, images, labels, transform=None):
        self.images = images
        self.labels = labels
        self.transform = transform
    
    def __len__(self):
        return len(self.images)
    
    def __getitem__(self, index):
        image = self.images[index]
        label = self.labels[index]
        
        if self.transform:
            image = self.transform(image)
        
        return image, label

2.4数据处理

transforms.ToTensor(): 将图像数据转换为PyTorch张量。神经网络通常使用张量作为输入数据类型,因此这一步将图像从其原始格式(例如JPEG或PNG)转换为张量。
transforms.Normalize(mean=[0.5, 0.5, 0.5], std=[0.5, 0.5, 0.5]): 对图像进行标准化。这一步通过减去均值并除以标准差来调整图像的数值范围,将其缩放到约在-1到1之间。标准化有助于提高模型的训练效果,使其更容易收敛。

transform = transforms.Compose([
    transforms.ToTensor(),
    transforms.Normalize(mean=[0.5, 0.5, 0.5], std=[0.5, 0.5, 0.5])
])

2.5构建数据集

CustomDataset(train_images, train_labels, transform=transform): 创建训练数据集对象,其中train_images是训练图像的集合,train_labels是相应的标签。
CustomDataset(test_images, test_labels, transform=transform): 类似地,创建测试数据集对象,其中test_images是测试图像的集合,test_labels是相应的标签。同样,应用了相同的图像转换。
DataLoader(train_dataset, batch_size=32, shuffle=True): 创建用于训练的数据加载器。将训练数据集传递给DataLoader,并设置批次大小为32,以及启用洗牌(shuffle=True)。这样可以在每个训练周期中以随机顺序提供数据,有助于模型更好地学习。
DataLoader(test_dataset, batch_size=32, shuffle=False): 类似地,创建用于测试的数据加载器,但在这里关闭了洗牌(shuffle=False),以确保在测试时按顺序提供数据。

train_dataset = CustomDataset(train_images, train_labels, transform=transform)
test_dataset = CustomDataset(test_images, test_labels, transform=transform)

train_loader = DataLoader(train_dataset, batch_size=32, shuffle=True)
test_loader = DataLoader(test_dataset, batch_size=32, shuffle=False)

3.使用卷积神经网识别猫狗图片

3.1卷积神经网络

卷积神经网络(Convolutional Neural Network,CNN)是一种专门用于处理具有网格结构数据(如图像和视频)的深度学习模型。CNN 在计算机视觉任务中取得了巨大成功,因为它能够有效地捕获图像中的空间结构信息。

3.2自定义卷积神经网络架构

import torch
import torch.nn as nn

class CNN(nn.Module):
    def __init__(self):
        super(CNN, self).__init__()
        self.conv1 = nn.Conv2d(3, 64, kernel_size=3, stride=1, padding=1)
        self.bn1 = nn.BatchNorm2d(64)
        self.pool1 = nn.MaxPool2d(kernel_size=2, stride=2)
        self.conv2 = nn.Conv2d(64, 64, kernel_size=3, stride=1, padding=1)
        self.bn2 = nn.BatchNorm2d(64)
        self.pool2 = nn.MaxPool2d(kernel_size=2, stride=2)
        self.conv3 = nn.Conv2d(64, 32, kernel_size=3, stride=1, padding=1)
        self.bn3 = nn.BatchNorm2d(32)
        self.pool3 = nn.MaxPool2d(kernel_size=2, stride=2)
        self.conv4 = nn.Conv2d(32, 32, kernel_size=3, stride=1, padding=1)
        self.bn4 = nn.BatchNorm2d(32)
        self.pool4 = nn.MaxPool2d(kernel_size=2, stride=2)
        self.fc1 = nn.Linear(32 * 14 * 14, 2)

    def forward(self, x):
        x = self.pool1(nn.functional.relu(self.bn1(self.conv1(x))))
        x = self.pool2(nn.functional.relu(self.bn2(self.conv2(x))))
        x = self.pool3(nn.functional.relu(self.bn3(self.conv3(x))))
        x = self.pool4(nn.functional.relu(self.bn4(self.conv4(x))))
        x = x.view(x.size(0), -1)
        x = self.fc1(x)
        return x

4.在GPU环境下训练模型

4.1参数设置

在这里我使用了以下几个部分来提高训练的精度:
(1):交叉熵损失函数 (nn.CrossEntropyLoss())。交叉熵损失对于分类任务是一种常见的损失函数,它在训练期间衡量模型的预测和真实标签之间的差异。
(2):Adam 优化器 (optim.Adam)。是一种基于梯度的优化算法,通常在深度学习中表现较好。

model = CNN()
model.to(device)
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=0.001)

4.2在GPU上训练10次

import torch
import torch.nn as nn
import torch.optim as optim

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

model = CNN().to(device)
model.train()

lr_scheduler = optim.lr_scheduler.ExponentialLR(optimizer, gamma=0.8, last_epoch=-1)
optimizer = optim.Adam(model.parameters(), lr=0.001)

for epoch_num in range(EPOCH):
    for batch_id, data in enumerate(train_loader):
        img_data, label = data
        img_data = img_data.to(device)
        label = label.to(device)

        optimizer.zero_grad()

        output = model(img_data)
        loss = nn.CrossEntropyLoss()(output, label)
        loss.backward()
        optimizer.step()

        all_train_iter = all_train_iter + 32
        all_train_iters.append(all_train_iter)
        all_train_costs.append(loss.item())
        _, predicted = torch.max(output, 1)
        acc = (predicted == label).sum().item() / len(label)
        all_train_accs.append(acc)

        if batch_id % 20 == 0:
            print('epoch:{}, batch:{}, cost:{}, acc:{}'.format(epoch_num, batch_id, loss.item(), acc))

    lr_scheduler.step()

# 保存模型参数
torch.save(model.state_dict(), 'CNN.pth')
print('Final loss: {}, Final acc: {}'.format(loss.item(), acc))

在这里插入图片描述

4.3查看test2数据集F1分数以及运行时间

在这里插入图片描述
这是我在GPU环境下的运行时间。

4.4保存模型以及验证模型

torch.save(model, 'model.pth')
import os
import random
import numpy as np
import matplotlib.pyplot as plt
from PIL import Image
import torch
import torchvision.transforms as T


model.eval()  # 将模型设置为评估模式

# 指定图片文件夹路径
image_folder_path = '/kaggle/input/test'

# 列出文件夹中所有图片文件
image_files = [os.path.join(image_folder_path, file) for file in os.listdir(image_folder_path) if file.endswith('.jpg')]

# 从文件列表中随机选择一张图片
random_img_path = random.choice(image_files)

# 定义图像预处理函数
transform = T.Compose([
    T.Resize((224, 224)),
    T.ToTensor(),
    T.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
])

# 读取并预处理图片
random_img = Image.open(random_img_path)
random_img = transform(random_img)

# 增加 batch 维度
random_img = random_img.unsqueeze(0)

# 预处理输入并进行推理
results = model(random_img)  # 进行推理

# 获取预测结果标签
predicted_label = torch.argmax(results)

# 输出模型识别结果和图片文件名
if predicted_label == 0:
    print(f"模型识别为猫,图片文件名:{os.path.basename(random_img_path)}")
else:
    print(f"模型识别为狗,图片文件名:{os.path.basename(random_img_path)}")
    # 展示图片
plt.imshow(Image.open(random_img_path))
plt.axis('off')
plt.show()

在这里插入图片描述

5.将模型转移到CPU环境上

5.1创建模型到CPU

import torch.nn as nn
class CNN(nn.Module):
    def __init__(self):
        super(CNN, self).__init__()
        self.conv1 = nn.Conv2d(3, 64, kernel_size=3, stride=1, padding=1)
        self.bn1 = nn.BatchNorm2d(64)
        self.pool1 = nn.MaxPool2d(kernel_size=2, stride=2)
        self.conv2 = nn.Conv2d(64, 64, kernel_size=3, stride=1, padding=1)
        self.bn2 = nn.BatchNorm2d(64)
        self.pool2 = nn.MaxPool2d(kernel_size=2, stride=2)
        self.conv3 = nn.Conv2d(64, 32, kernel_size=3, stride=1, padding=1)
        self.bn3 = nn.BatchNorm2d(32)
        self.pool3 = nn.MaxPool2d(kernel_size=2, stride=2)
        self.conv4 = nn.Conv2d(32, 32, kernel_size=3, stride=1, padding=1)
        self.bn4 = nn.BatchNorm2d(32)
        self.pool4 = nn.MaxPool2d(kernel_size=2, stride=2)
        self.fc1 = nn.Linear(32 * 14 * 14, 2)

    def forward(self, x):
        x = self.pool1(nn.functional.relu(self.bn1(self.conv1(x))))
        x = self.pool2(nn.functional.relu(self.bn2(self.conv2(x))))
        x = self.pool3(nn.functional.relu(self.bn3(self.conv3(x))))
        x = self.pool4(nn.functional.relu(self.bn4(self.conv4(x))))
        x = x.reshape(x.size(0), -1)
        x = self.fc1(x)
        return x
        # 加载模型
state_dict = torch.load('CNN.pth', map_location=torch.device('cpu'))
model = CNN()  
model.load_state_dict(state_dict)
model.eval()  # 将模型设置为评估模式

5.2在CPU上运行模型

import time

predicted_labels = []
true_labels = []

correct = 0
total = 0

# 记录整个推理开始时间
inference_start_time = time.time()

with torch.no_grad():
    for images, labels in test_loader:
        outputs = model(images)
        _, predicted = torch.max(outputs, 1)

        total += labels.size(0)
        correct += (predicted == labels).sum().item()

        predicted_labels.extend(predicted.numpy())
        true_labels.extend(labels.numpy())

# 记录整个推理结束时间
inference_end_time = time.time()

# 计算整个测试集的推理时间
total_inference_time = inference_end_time - inference_start_time
print(f"Total Inference Time: {total_inference_time} seconds")

# 计算 F1 Score for PyTorch
f1 = f1_score(true_labels, predicted_labels)
print(f"F1 Score: {f1}")

# 计算 Test Accuracy for PyTorch
accuracy = 100 * correct / total
print(f"Test Accuracy: {accuracy}%")

跑的时间比较长。
CPU上无任何加速

6.使用oneAPI组件

6.1使用Intel Extension for PyTorch进行优化

import intel_extension_for_pytorch as ipex
import torch
# 加载模型
state_dict = torch.load('CNN.pth', map_location=torch.device('cpu'))
model = CNN()  
model.load_state_dict(state_dict)
model.eval()  # 将模型设置为评估模式
# 移动模型和优化器到IPEX设备(CPU)
model= ipex.optimize(model=model, dtype=torch.float32)
import time

predicted_labels = []
true_labels = []

correct = 0
total = 0

# 记录整个推理开始时间
inference_start_time = time.time()

with torch.no_grad():
    for images, labels in test_loader:
        outputs = model(images)
        _, predicted = torch.max(outputs, 1)

        total += labels.size(0)
        correct += (predicted == labels).sum().item()

        predicted_labels.extend(predicted.numpy())
        true_labels.extend(labels.numpy())

# 记录整个推理结束时间
inference_end_time = time.time()

# 计算整个测试集的推理时间
total_inference_time = inference_end_time - inference_start_time
print(f"Total Inference Time: {total_inference_time} seconds")

# 计算 F1 Score for PyTorch
f1 = f1_score(true_labels, predicted_labels)
print(f"F1 Score: {f1}")

# 计算 Test Accuracy for PyTorch
accuracy = 100 * correct / total
print(f"Test Accuracy: {accuracy}%")

使用Intel Extension for PyTorch进行优化

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

torch.save(model.state_dict(), 'optimized_model.pth')

6.3使用 Intel® Neural Compressor 量化模型

import os
import torch
from neural_compressor.config import PostTrainingQuantConfig, AccuracyCriterion
from neural_compressor import quantization
from sklearn.metrics import confusion_matrix, accuracy_score, balanced_accuracy_score

# 定义评估函数
import time

# 定义时间评估函数
def eval_func(model):
    with torch.no_grad():
        y_true = []
        y_pred = []

        for inputs, labels in test_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=test_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)

在这里插入图片描述

6.5使用量化后的模型

import time

predicted_labels = []
true_labels = []

correct = 0
total = 0

# 记录整个推理开始时间
inference_start_time = time.time()

with torch.no_grad():
    for images, labels in test_loader:
        outputs = quantized_model(images)
        _, predicted = torch.max(outputs, 1)

        total += labels.size(0)
        correct += (predicted == labels).sum().item()

        predicted_labels.extend(predicted.numpy())
        true_labels.extend(labels.numpy())

# 记录整个推理结束时间
inference_end_time = time.time()

# 计算整个测试集的推理时间
total_inference_time = inference_end_time - inference_start_time
print(f"Total Inference Time: {total_inference_time} seconds")

# 计算 F1 Score for PyTorch
f1 = f1_score(true_labels, predicted_labels)
print(f"F1 Score: {f1}")

# 计算 Test Accuracy for PyTorch
accuracy = 100 * correct / total
print(f"Test Accuracy: {accuracy}%")

量化后模型在CPU上的运行
可以看出优化并量化后的模型运行时间得到了大大的提升,得到了接近7倍的提升,然而精度的损失却很小,更符合我们在实际生活场景的运用。甚至比我之前在GPU上未加速的模型速度更快。
GPU未优化
这是GPU环境跑出来的,因为所用测试集不一样,所以F1分数有一定的区别。

7.总结

oneAPI是Intel提供的一个综合的、跨多种设备的软件开发工具集,用于高性能计算。在深度学习领域,oneAPI可以用于优化和加速模型的训练和推理。
性能提升: 实验结果表明,在使用DPC++和优化库的情况下,深度学习模型的训练和推理性能得到了显著的提升。
可扩展性: oneAPI的跨平台性使得模型能够在不同的Intel硬件上运行,实现了更好的可扩展性和灵活性。
资源利用效率: 通过使用oneAPI进行性能分析和调优,我们优化了模型的资源利用效率,确保了更好的计算资源利用。
总体而言,使用Intel的oneAPI工具集为深度学习模型带来了显著的性能提升和更好的可扩展性。通过充分利用Intel的硬件和工具,我们成功地优化了模型,使其在不同硬件配置下都能够高效运行。

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐