作业简介:
问题描述:
在这个问题中,你将面临一个经典的机器学习分类挑战——猫狗大战。你的任务是建立一个分类模型,能够准确地区分图像中是猫还是狗。
预期解决方案:
你的目标是通过训练一个机器学习模型,使其在给定一张图像时能够准确地预测图像中是猫还是狗。模型应该能够推广到未见过的图像,并在测试数据上表现良好。我们期待您将其部署到模拟的生产环境中——这里推理时间和二分类准确度(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 #输出
数据标准化
- transforms.Resize(IMAGE_SIZE):
- 将原始图像按比例缩放到指定的
IMAGE_SIZE
大小,确保所有图像具有相同的宽度和高度。
- 将原始图像按比例缩放到指定的
- transforms.CenterCrop((IMAGE_SIZE, IMAGE_SIZE)):
- 在调整大小后的图像上执行中心裁剪操作,裁剪出一个正方形区域,该区域的边长与
IMAGE_SIZE
相同,这样可以确保所有经过此步骤的图像具有统一且固定的尺寸。
- 在调整大小后的图像上执行中心裁剪操作,裁剪出一个正方形区域,该区域的边长与
- 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_img 和 self.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 值的基础上还能够缩小模型的规模。