【Intel校企实践】猫狗大战

发布时间:2024年01月14日

作业简介:

问题描述:

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

预期解决方案

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

数据集:

数据集:链接:https://pan.baidu.com/s/1SQe-gKP6yTPpI2zfmbig9A?pwd=esip
提取码:esip

图像展示:

第一张图片显示不出时显示的文字
第二张图片显示不出时显示的文字

数据预处理

数据集结构

本项目数据共由三部分组成,分别包含 test, train , val 文件夹
在这里插入图片描述

train 文件下数据如下:

在这里插入图片描述

图片的名字则为 label 标签,,然后val是从train 文件夹中,剪切100张猫图和狗图。

test 文件里面是一些没有带标签的猫狗图,数据如下:
在这里插入图片描述

下面可以查看数据集大小分别数多少:

import os
dit = 'dataset/train/'
cat=0
dog=0
for file in os.listdir(dit):    # 遍历dir文件夹                  # 数据集增1
    name = file.split(sep='.')              # 分割文件名,"cat.0.jpg"将分割成"cat",".","jpg"3个元素       
    if name[0] == 'cat':
        cat+=1       
    else:
        dog+=1 
print(cat,dog)
12500 12500 #输出

数据标准化

  1. transforms.Resize(IMAGE_SIZE):
    • 将原始图像按比例缩放到指定的 IMAGE_SIZE 大小,确保所有图像具有相同的宽度和高度。
  2. transforms.CenterCrop((IMAGE_SIZE, IMAGE_SIZE)):
    • 在调整大小后的图像上执行中心裁剪操作,裁剪出一个正方形区域,该区域的边长与 IMAGE_SIZE 相同,这样可以确保所有经过此步骤的图像具有统一且固定的尺寸。
  3. transforms.ToTensor():
    • 将 PIL 图像或 numpy 数组转换为 PyTorch Tensor 格式。
    • 这个转换同时会将图像的数据类型从 uint8 转换为 float,并进行归一化处理,即将像素值从 [0, 255] 范围内线性归一化到 [0.0, 1.0] 范围。
    • 另外,它还会将图像的维度顺序从 H×W×C(高度、宽度、通道数)调整为 C×H×W,这是大多数深度学习框架期望的输入格式。
IMAGE_SIZE =200

dataTransform = transforms.Compose([
    transforms.Resize(IMAGE_SIZE),                          # 将图像按比例缩放至合适尺寸
    transforms.CenterCrop((IMAGE_SIZE, IMAGE_SIZE)),        # 从图像中心裁剪合适大小的图像
    transforms.ToTensor()   # 转换成Tensor形式,并且数值归一化到[0.0, 1.0],同时将H×W×C的数据转置成C×H×W,这一点很关键
])

数据加载模块:

该模块为 DVCD 的自定义数据集类,它继承自 PyTorch 中的 data.Dataset 类。这个数据集类是为猫狗分类任务设计的,根据传入的 mode 参数(‘train’ 或 ‘test’/‘eval’)来读取相应目录下的图片文件,并为每张图片分配一个标签(0代表猫,1代表狗)。

  • *init*(self, mode, dir):初始化函数,用于设置数据集模式(训练/测试),以及数据集所在的路径。
  • self.list_imgself.list_label:分别存储图片路径和对应的类别标签信息。
  • self.data_size:记录数据集中样本的数量。
  • self.transform:使用之前定义的数据预处理流水线 dataTransform 对图像进行预处理。

__init__ 函数中:

  • 如果 mode'train',则从指定的训练集目录中遍历所有图片文件,将图片路径添加到 list_img,同时根据文件名判断图片类别并将其对应的标签(0 或 1)添加到 list_label
  • 如果 mode'test''eval',同样遍历测试集目录下的图片文件并将图片路径添加到 list_img,但这里也进行了不必要的标签赋值操作(实际未用到)。对于测试集,在推理阶段通常不需要标签,但在某些情况下可能需要知道每个样本的正确标签来进行评估。
  • mode 不是上述两种情况,则输出错误提示“Undefined Dataset!”。

总结起来,该自定义数据集类的主要作用是组织、加载和提供训练或测试所需的猫狗图片数据及其对应标签,以便后续在深度学习模型训练或评估时调用。

class DVCD(data.Dataset):      # 新建一个数据集类,并且需要继承PyTorch中的data.Dataset父类
    def __init__(self, mode, dir):          # 默认构造函数,传入数据集类别(训练或测试),以及数据集路径
        self.mode = mode
        self.list_img = []                  # 新建一个image list,用于存放图片路径,注意是图片路径
        self.list_label = []                # 新建一个label list,用于存放图片对应猫或狗的标签,其中数值0表示猫,1表示狗
        self.data_size = 0                  # 记录数据集大小
        self.transform = dataTransform      # 转换关系

        if self.mode == 'train':            # 训练集模式下,需要提取图片的路径和标签
            dir = dir + '/val/'           # 训练集路径在"dir"/train/
            for file in os.listdir(dir):    # 遍历dir文件夹
                self.list_img.append(dir + file)        # 将图片路径和文件名添加至image list
                self.data_size += 1                     # 数据集增1
                name = file.split(sep='.')              # 分割文件名,"cat.0.jpg"将分割成"cat",".","jpg"3个元素
                # label采用one-hot编码,"1,0"表示猫,"0,1"表示狗,任何情况只有一个位置为"1",在采用CrossEntropyLoss()计算Loss情况下,label只需要输入"1"的索引,即猫应输入0,狗应输入1
                if name[0] == 'cat':
                    self.list_label.append(0)         # 图片为猫,label为0
                else:
                    self.list_label.append(1)         # 图片为狗,label为1,注意:list_img和list_label中的内容是一一配对的
        elif self.mode == 'test' or self.mode=='eval':           # 测试集模式下,只需要提取图片路径就行
            dir = dir + '/val/'            # 测试集路径为"dir"/test/
            for file in os.listdir(dir):    # 遍历dir文件夹
                self.list_img.append(dir + file)        # 将图片路径和文件名添加至image list
                self.data_size += 1                     # 数据集增1
                name = file.split(sep='.')              # 分割文件名,"cat.0.jpg"将分割成"cat",".","jpg"3个元素
                # label采用one-hot编码,"1,0"表示猫,"0,1"表示狗,任何情况只有一个位置为"1",在采用CrossEntropyLoss()计算Loss情况下,label只需要输入"1"的索引,即猫应输入0,狗应输入1
                if name[0] == 'cat':
                    self.list_label.append(0)         # 图片为猫,label为0
                else:
                    self.list_label.append(1)        # 添加2作为label,实际未用到,也无意义
            
        else:
            print('Undefined Dataset!')

卷积神经网络CNN

CNN简要:

CNN(Convolutional Neural Networks),卷积神经网络,以卷积的基本操作而命名,简单点的主要分3个部分:输入层(Input)卷积层(Conv)池化层(Pool), 和 全连层(FC)

在这里插入图片描述

根据上图:
输入层:根据第一层Conv(Conv1),该层数据一共有3层,故Conc1的输入层是1个3通道的图片,事实也是这样,彩图是3通道(RGB)(3个feature map)的,灰图则1个通道(1 个 feature map);并且没每个像素点的范围为[0,255](像素点)。一般图片的数据形式则是 [h* w* c] ,其中对应的字母分别为 图片的 高,宽,通道数。
卷积层:
用于取特征,由卷积核对输入层图像进行卷积操作以提取图像特征。另外:卷积核(下图移动的部分):1个卷积核生成1个feature map,即卷积输出的图像通道数与卷积核的个数一致,卷积核的尺寸为(S×S×C×N),其中C表示卷积核深度,必须与输入层图像的通道数一致。
卷积的演示:
在这里插入图片描述

池化层
主要用于图像下采样,降低图像分辨率,减少区域内图像的特征数。本文用的池化方法为max pooling,max pooling就是在池化核大小区域内选择最大的数值作为输出结果。
池化的演示:

在这里插入图片描述

全连层
用于分类的操作,若卷积后的图像尺寸为(h×w×c),需分成n类,则全连层的作用为将[h×w×c]的矩阵转换成[n×1]的矩阵。

下面是基于Pytorch框架的CNN net 实现:

class Net(nn.Module):                                       # 新建一个网络类,就是需要搭建的网络,必须继承PyTorch的nn.Module父类
    def __init__(self):                                     # 构造函数,用于设定网络层
        super(Net, self).__init__()                         # 标准语句
        self.conv1 = torch.nn.Conv2d(3, 16, 3, padding=1)   # 第一个卷积层,输入通道数3,输出通道数16,卷积核大小3×3,padding大小1,其他参数默认
        self.conv2 = torch.nn.Conv2d(16, 16, 3, padding=1)  # 第二个卷积层,输入通道数16,输出通道数16,卷积核大小3×3,padding大小1,其他参数默认

        self.fc1 = nn.Linear(50*50*16, 128)                 # 第一个全连层,线性连接,输入节点数50×50×16,输出节点数128
        self.fc2 = nn.Linear(128, 64)                       # 第二个全连层,线性连接,输入节点数128,输出节点数64
        self.fc3 = nn.Linear(64, 2)                         # 第三个全连层,线性连接,输入节点数64,输出节点数2

    def forward(self, x):                   # 重写父类forward方法,即前向计算,通过该方法获取网络输入数据后的输出值
        x = self.conv1(x)                   # 第一次卷积
        x = F.relu(x)                       # 第一次卷积结果经过ReLU激活函数处理
        x = F.max_pool2d(x, 2)              # 第一次池化,池化大小2×2,方式Max pooling

        x = self.conv2(x)                   # 第二次卷积
        x = F.relu(x)                       # 第二次卷积结果经过ReLU激活函数处理
        x = F.max_pool2d(x, 2)              # 第二次池化,池化大小2×2,方式Max pooling

        x = x.view(x.size()[0], -1)         # 由于全连层输入的是一维张量,因此需要对输入的[50×50×16]格式数据排列成[40000×1]形式
        x = F.relu(self.fc1(x))             # 第一次全连,ReLU激活
        x = F.relu(self.fc2(x))             # 第二次全连,ReLU激活
        y = self.fc3(x)                     # 第三次激活,ReLU激活

        return y

本地GPU训练

参数设置:

  • 交叉熵损失函数 (nn.CrossEntropyLoss())。交叉熵损失对于分类任务是一种常见的损失函数,它在训练期间衡量模型的预测和真实标签之间的差异。

  • Adam 优化器 (optim.Adam)。是一种基于梯度的优化算法,通常在深度学习中表现较好。

  • nepoch:50

  • lr:0.0001

  • batch_size=32

model = Net()                       # 实例化一个网络
model = model.to(device).cuda()              # 网络送入GPU,即采用GPU计算,如果没有GPU加速,可以去掉".cuda()"
model.train()                       # 网络设定为训练模式,有两种模式可选,.train()和.eval(),训练模式和评估模式,区别就是训练模式采用了dropout策略,可以放置网络过拟合
optimizer = torch.optim.Adam(model.parameters(), lr=lr)         # 实例化一个优化器,即调整网络参数,优化方式为adam方法
criterion = torch.nn.CrossEntropyLoss()   

GPU训练

    for epoch in range(nepoch):
        # 读取数据集中数据进行训练,因为dataloader的batch_size设置为16,所以每次读取的数据量为16,即img包含了16个图像,label有16个
        for img, label in dataloader:                                           # 循环读取封装后的数据集,其实就是调用了数据集中的__getitem__()方法,只是返回数据格式进行了一次封装
            img, label = Variable(img).to(device), Variable(label).to(device)           # 将数据放置在PyTorch的Variable节点中,并送入GPU中作为网络计算起点
            out = model(img)                                                    # 计算网络输出值,就是输入网络一个图像数据,输出猫和狗的概率,调用了网络中的forward()方法
            loss = criterion(out, label.squeeze())      # 计算损失,也就是网络输出值和实际label的差异,显然差异越小说明网络拟合效果越好,此处需要注意的是第二个参数,必须是一个1维Tensor

            loss.backward()                             # 误差反向传播,采用求导的方式,计算网络中每个节点参数的梯度,显然梯度越大说明参数设置不合理,需要调整
            optimizer.step()                            # 优化采用设定的优化方法对网络中的各个参数进行调整
            optimizer.zero_grad()                       # 清除优化器中的梯度以便下一次计算,因为优化器默认会保留,不清除的话,每次计算梯度都回累加
            cnt += 1

            print('Epoch:{0},Frame:{1}, train_loss {2}'.format(epoch, cnt*batch_size, loss/batch_size))          # 打印一个batch size的训练结果
    torch.save(model.state_dict(), '{0}/model.pth'.format(model_cp))  

查看test数据集F1分数及时间

在这里插入图片描述

下面是对应的代码实现:

from a1 import *
dataset_dir = 'D:/Dataset/猫狗大战数据集/val/'                    # 数据集路径
model_file = './model/model.pth'                # 模型保存路径
N = 10
device = torch.device("cuda:0" if (torch.cuda.is_available()) else "cpu")
cat=0
dog=0
# new version
def test():
    # setting model
    model = Net()                                       # 实例化一个网络
    model.to(device)                                       # 送入GPU,利用GPU计算
    model.load_state_dict(torch.load(model_file))       # 加载训练好的模型参数
    model.eval()
    # 设定为评估模式,即计算过程中不要dropout
    l1=0
    l2=0
    cat=0
    dog=0
    # get data
    label =[]
    imgs = []           # img
    imgs_data = []      # img data
    for file in os.listdir(dataset_dir):
        name = file.split('.')
        if name[0] == 'cat':
            label.append(0)
            l1+=1
        elif name[0] == 'dog':
            label.append(1)
            l2+=1
        img = Image.open(dataset_dir + file)            # 打开图像
        img_data =dataTransform(img).to(device)           # 转换成torch tensor数据   ++

        imgs.append(img)                                # 图像list
        imgs_data.append(img_data)                      # tensor list
    imgs_data = torch.stack(imgs_data)                  # tensor list合成一个4D tensor
    print(label)
    # calculation
    out = model(imgs_data)                              # 对每个图像进行网络计算
    out = F.softmax(out, dim=1)                         # 输出概率化
    out = out.data.cpu().numpy()                        # 转成numpy数据

    # pring results         显示结果
    for idx in range(len( out )):
        if out[idx, 0] > out[idx, 1] and label[idx] == 0:
            cat+=1
            #plt.suptitle('cat:{:.1%},dog:{:.1%}'.format(out[idx, 0], out[idx, 1]))
        elif out[idx, 0] < out[idx, 1] and label[idx] == 1:
            dog+=1
            #plt.suptitle('cat:{:.1%},dog:{:.1%}'.format(out[idx, 0], out[idx, 1]))

            #plt.suptitle('dog:{:.1%},cat:{:.1%}'.format(out[idx, 1], out[idx, 0]))
        #plt.imshow(imgs[idx])
    #plt.show()
    pre1 =cat/(l1)
    pre2 =dog/(l2)
    print("pre1:{:.1%},pre2:{:.1%}".format(pre1,pre2))
    r1 = cat/(cat+(l2-dog))
    r2 = dog/(dog+(l1-cat))
    print("recall1:{:.1%},recall2:{:.1%}".format(r1,r2))
    f1_cat = 2*pre1*r1/(pre1+r1)
    f1_dog = 2*pre2*r2/(pre2+r2)
    f1  =(f1_cat+f1_dog)/2
    print("f1_cat:{:.3},f1_dog:{:.3},f1:{:.3}".format(f1_cat,f1_dog,f1))

保存为CNN模型并使用模型进行推理测试

torch.save(model.state_dict(), '{0}/model.pth'.format(model_cp)) 
import matplotlib.pyplot as plt
import numpy as np

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

# 将图片传递给模型进行预测
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()

在这里插入图片描述

转移到CPU上面

创建CNN 模型

这里将GPU训练的模型保存到了model.pth中,在CPU上进行加载。

class Net(nn.Module):                                       # 新建一个网络类,就是需要搭建的网络,必须继承PyTorch的nn.Module父类
    def __init__(self):                                     # 构造函数,用于设定网络层
        super(Net, self).__init__()                         # 标准语句
        self.conv1 = torch.nn.Conv2d(3, 16, 3, padding=1)   # 第一个卷积层,输入通道数3,输出通道数16,卷积核大小3×3,padding大小1,其他参数默认
        self.conv2 = torch.nn.Conv2d(16, 16, 3, padding=1)  # 第二个卷积层,输入通道数16,输出通道数16,卷积核大小3×3,padding大小1,其他参数默认

        self.fc1 = nn.Linear(50*50*16, 128)                 # 第一个全连层,线性连接,输入节点数50×50×16,输出节点数128
        self.fc2 = nn.Linear(128, 64)                       # 第二个全连层,线性连接,输入节点数128,输出节点数64
        self.fc3 = nn.Linear(64, 2)                         # 第三个全连层,线性连接,输入节点数64,输出节点数2

    def forward(self, x):                   # 重写父类forward方法,即前向计算,通过该方法获取网络输入数据后的输出值
        x = self.conv1(x)                   # 第一次卷积
        x = F.relu(x)                       # 第一次卷积结果经过ReLU激活函数处理
        x = F.max_pool2d(x, 2)              # 第一次池化,池化大小2×2,方式Max pooling

        x = self.conv2(x)                   # 第二次卷积
        x = F.relu(x)                       # 第二次卷积结果经过ReLU激活函数处理
        x = F.max_pool2d(x, 2)              # 第二次池化,池化大小2×2,方式Max pooling
        x = x.reshape(1, 40000)         # 由于全连层输入的是一维张量,因此需要对输入的[50×50×16]格式数据排列成[40000×1]形式
        x = F.relu(self.fc1(x))             # 第一次全连,ReLU激活
        x = F.relu(self.fc2(x))             # 第二次全连,ReLU激活
        y = self.fc3(x)      
        
model = Net() 

接着加载gpu训练好的model:

model.load_state_dict(torch.load('model.pth', map_location=torch.device('cpu')))

尝试在cpu上面跑:

import torch
from sklearn.metrics import f1_score
dataset_dir = 'dataset'                                                  # 数据集路径
import time
model_cp = './model/'               # 网络参数保存位置
workers = 10                        # PyTorch读取数据线程数量
batch_size = 32                     # batch_size大小
lr = 0.0001                         # 学习率
nepoch = 1
device = torch.device('cpu')
print(device)
# .to(device)
loss_list=[]

datafile = DVCD('eval', dataset_dir)     
    # 实例化一个数据集
test_loader = DataLoader(datafile, batch_size=1, shuffle=True, num_workers=workers, drop_last=True)     # 用PyTorch的DataLoader类封装,实现数据集顺序打乱,多线程读取,一次取多个数据等效果
print('Dataset loaded! length of train set is {0}'.format(len(datafile)))

model = Net()                       # 实例化一个网络
model.load_state_dict(torch.load('model.pth', map_location=torch.device('cpu')))

model = model.to(device)             # 网络送入GPU,即采用GPU计算,如果没有GPU加速,可以去掉".cuda()"
print(device)    
                    # 网络设定为训练模式,有两种模式可选,.train()和.eval(),训练模式和评估模式,区别就是训练模式采用了dropout策略,可以放置网络过拟合

optimizer = torch.optim.Adam(model.parameters(), lr=lr)         # 实例化一个优化器,即调整网络参数,优化方式为adam方法

                     # 定义loss计算方法,cross entropy,交叉熵,可以理解为两者数值越接近其值越小
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 = 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}')

在这里插入图片描述

使用oneAPI 组件

Transfer Learning with oneAPI AI Analytics Toolkit进行迁移学习

使用Intel Extension for PyTorch进行优化

在上一章中,我发现使用CPU直接进行训练的话会相当慢,在这里使用Intel Extension for PyTorch大大提高了速度。大概缩短了一倍的时间,并且F1的值并没有改变。

model = Net()                       # 实例化一个网络
model.load_state_dict(torch.load('model.pth', map_location=torch.device('cpu')))


model = model.to(device)             # 网络送入GPU,即采用GPU计算,如果没有GPU加速,可以去掉".cuda()"
    
                    # 网络设定为训练模式,有两种模式可选,.train()和.eval(),训练模式和评估模式,区别就是训练模式采用了dropout策略,可以放置网络过拟合

optimizer = torch.optim.Adam(model.parameters(), lr=lr)         # 实例化一个优化器,即调整网络参数,优化方式为adam方法
model,optimizer = ipex.optimize(model=model,optimizer=optimizer,dtype=torch.float32)
criterion = torch.nn.CrossEntropyLoss()                         # 定义loss计算方法,cross entropy,交叉熵,可以理解为两者数值越接近其值越小
model.eval()  

在这里插入图片描述

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

# 保存模型参数
torch.save(model.state_dict(), 'new_model.pth')
 
# 加载模型参数
loaded_model = Net()
loaded_model.load_state_dict(torch.load('new_model.pth'))

使用 Intel? Neural Compressor 量化模型

这里对优化后的模型new_model.pth进行加载

model =Net()
model.load_state_dict(torch.load('new_model.pth',map_location=torch.device('cpu')))
model.to('cpu')  # 将模型移动到 CPU
model.eval()

加载完成以后以准确度为评估函数进行量化

from neural_compressor.config import PostTrainingQuantConfig, AccuracyCriterion
from neural_compressor import quantization
import os
from sklearn.metrics import accuracy_score
# 加载模型
model =Net()
model.load_state_dict(torch.load('new_model.pth',map_location=torch.device('cpu')))
model.to('cpu')  # 将模型移动到 CPU
model.eval()

eval_loader=DataLoader(DVCD('eval',dataset_dir),batch_size=1, shuffle=True, num_workers=workers, drop_last=True )

# 定义评估函数
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))


conf = PostTrainingQuantConfig(backend='default',  # or 'qnnpack'
                               accuracy_criterion=AccuracyCriterion(higher_is_better=True,
                                                                   criterion='relative',
                                                                   tolerable_loss=0.01))



# 执行量化
q_model = quantization.fit(model,
                           conf,
                           calib_dataloader=eval_loader,
                           eval_func=eval_func)
print(q_model)

# 保存量化模型
quantized_model_path = './quantized_models'
if not os.path.exists(quantized_model_path):
    os.makedirs(quantized_model_path)

q_model.save(quantized_model_path)

量化成功以后会出现如下代码 :

在这里插入图片描述

生成对应的文件

在这里插入图片描述

使用量化后的模型在 CPU上进行推理

加载模型

import torch
import json
 
from neural_compressor import quantization
 
# 指定量化模型的路径
quantized_model_path = './quantized_models'
 
# 加载 Qt 模型和 JSON 配置
new_model_path = f'{quantized_model_path}/best_model.pt'
json_config_path = f'{quantized_model_path}/best_configure.json'
 
# 加载 Qt 模型
model = torch.jit.load(new_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)

进行推理

import torch
from sklearn.metrics import f1_score

dataset_dir = 'dataset'                                                  # 数据集路径
import time
model_cp = './model/'               # 网络参数保存位置
workers = 10                        # PyTorch读取数据线程数量
batch_size = 32                     # batch_size大小
lr = 0.0001                         # 学习率
nepoch = 1



device = torch.device('cpu')
print(device)

datafile = DVCD('eval', dataset_dir)     
    # 实例化一个数据集
test_loader = DataLoader(datafile, batch_size=1, shuffle=True, num_workers=workers, drop_last=True)     # 用PyTorch的DataLoader类封装,实现数据集顺序打乱,多线程读取,一次取多个数据等效果
print('Dataset loaded! length of train set is {0}'.format(len(datafile)))

    
model.load_state_dict(torch.load(new_model_path, map_location=torch.device('cpu')))

          # 网络送入GPU,即采用GPU计算,如果没有GPU加速,可以去掉".cuda()"
print(device)    
                    # 网络设定为训练模式,有两种模式可选,.train()和.eval(),训练模式和评估模式,区别就是训练模式采用了dropout策略,可以放置网络过拟合

optimizer = torch.optim.Adam(model.parameters(), lr=lr)         # 实例化一个优化器,即调整网络参数,优化方式为adam方法

                     # 定义loss计算方法,cross entropy,交叉熵,可以理解为两者数值越接近其值越小
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 = 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}')

F1分数及推理时间

在这里插入图片描述

补充

这里补充一点,由于test里面没有标签,是从train 里面剪切200张图片(猫狗各100张),这样可能得到的F1分数具有偶然性,下面使用另一个猫狗大战_test作为测试
然后将前面的路径做调整,云端环境的话就直接打包重新上传,
首先这里对其改名为new_test,然后解压
在这里插入图片描述

下面是用新的test集对应的不同版本的F1分数:
本地gpu版本
在这里插入图片描述
intel 云端 cpu(无优化)(一共1000张图):

在这里插入图片描述
在这里插入图片描述

Intel Extension for PyTorch 版:
只用了4.19s ,大概是优化前的2
在这里插入图片描述

量化优化版
用了2.34s ,大概是优化前(初始cpu版)的4.2
在这里插入图片描述

总结

在使用oneAPI的优化组件以后,推理的时间大幅度下降,从原来的7.91s到目前的4.19s (1000张图),其次,在使用量化工具以后,推理的时间从4.19s又下降到了2.34s并且在整个过程中F1分数的值一直稳定在0.95左右,这是一个非常好的现象。证明了oneAPI优秀的模型压缩能力,在保证模型精确度,F1 值的基础上还能够缩小模型的规模。

文章来源:https://blog.csdn.net/qq_57862276/article/details/135507255
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。