目录
5.1 迁移学习 Transfer Learning with oneAPI AI Analytics Toolkit
5.2 Intel Extension for PyTorch 优化并保存优化后的模型
一.实验介绍
1.1 课题方向
在这个问题中,你将面临一个经典的机器学习分类挑战——猫狗大战。你的任务是建立一个分类模型,能够准确地区分图像中是猫还是狗。
1.2 目标任务
你的目标是通过训练一个机器学习模型,使其在给定一张图像时能够准确地预测图像中是猫还是狗。模型应该能够推广到未见过的图像,并在测试数据上表现良好。我们期待您将其部署到模拟的生产环境中——这里推理时间和二分类准确度(F1分数)将作为评分的主要依据。
1.3 数据集
数据集:
链接:百度网盘 请输入提取码
提取码:jc34
1.4 图像展示
1.cat
2.dog
二.数据集划分处理
2.1 数据集目录
初始数据集猫狗.zip解压获得train和猫狗大战_test文件夹,其中train中含cat类12500张,含dog类12500张,猫狗_test含有两个子目录,分别是cats和dogs,各自含有cat类500张和dog类500张。
2.2 train数据集划分
a.从训练集train中,按类别cat和dog,选取10000张图片,6000张用于训练,2000张用于验证,2000张用于测试,并创建train_dataset及其子目录
import os
import shutil
# 初始训练集train
original_dataset_dir = 'train'
# 创建目录结构
#创建train_dataset,其子目录train
base_dir = 'train_dataset/train'
os.makedirs(base_dir, exist_ok=True)
#创建train_dataset/train/train作为模型训练
train_dir = os.path.join(base_dir, 'train')
os.makedirs(train_dir, exist_ok=True)
#创建train_dataset/train/validation作为模型验证
validation_dir = os.path.join(base_dir, 'validation')
os.makedirs(validation_dir, exist_ok=True)
#创建train_dataset/train/validation作为模型测试
test_dir = os.path.join(base_dir, 'test')
os.makedirs(test_dir, exist_ok=True)
b.在子目录train,test和validation中,分别创建cats和dogs子目录
# 训练集文件
train_cats_dir = os.path.join(train_dir, 'cats')
os.makedirs(train_cats_dir, exist_ok=True)
train_dogs_dir = os.path.join(train_dir, 'dogs')
os.makedirs(train_dogs_dir, exist_ok=True)
# 验证集文件
validation_cats_dir = os.path.join(validation_dir, 'cats')
os.makedirs(validation_cats_dir, exist_ok=True)
validation_dogs_dir = os.path.join(validation_dir, 'dogs')
os.makedirs(validation_dogs_dir, exist_ok=True)
# 测试集文件
test_cats_dir = os.path.join(test_dir, 'cats')
os.makedirs(test_cats_dir, exist_ok=True)
test_dogs_dir = os.path.join(test_dir, 'dogs')
os.makedirs(test_dogs_dir, exist_ok=True)
c.像其中放入抽取的图片
#分别向集合中放入初始训练集的图片
# 训练集 - 猫和狗
fnames = ['cat.{}.jpg'.format(i) for i in range(3000)]
for fname in fnames:
src = os.path.join(original_dataset_dir, fname)
dst = os.path.join(train_cats_dir, fname)
shutil.copyfile(src, dst)
fnames = ['dog.{}.jpg'.format(i) for i in range(3000)]
for fname in fnames:
src = os.path.join(original_dataset_dir, fname)
dst = os.path.join(train_dogs_dir, fname)
shutil.copyfile(src, dst)
# 验证集 - 猫和狗
fnames = ['cat.{}.jpg'.format(i) for i in range(3000, 4000)]
for fname in fnames:
src = os.path.join(original_dataset_dir, fname)
dst = os.path.join(validation_cats_dir, fname)
shutil.copyfile(src, dst)
fnames = ['dog.{}.jpg'.format(i) for i in range(3000, 4000)]
for fname in fnames:
src = os.path.join(original_dataset_dir, fname)
dst = os.path.join(validation_dogs_dir, fname)
shutil.copyfile(src, dst)
# 测试集 - 猫和狗
fnames = ['cat.{}.jpg'.format(i) for i in range(4000, 5000)]
for fname in fnames:
src = os.path.join(original_dataset_dir, fname)
dst = os.path.join(test_cats_dir, fname)
shutil.copyfile(src, dst)
fnames = ['dog.{}.jpg'.format(i) for i in range(4000, 5000)]
for fname in fnames:
src = os.path.join(original_dataset_dir, fname)
dst = os.path.join(test_dogs_dir, fname)
shutil.copyfile(src, dst)
d.检验图片放入是否数量正确
#数据划分的结果检验
import os
from IPython.display import display, HTML
def count_images_in_directory(directory):
count = sum([len(files) for _, _, files in os.walk(directory)])
return count
# 打印初始train图片数量
target_directory = 'train'
total_images = count_images_in_directory(target_directory)
display(HTML(f"<p>目录 '{target_directory}' 中的总图片数量为: <strong>{total_images}</strong></p>"))
# 打印初始test图片数量
target_directory = 'test'
total_images = count_images_in_directory(target_directory)
display(HTML(f"<p>目录 '{target_directory}' 中的总图片数量为: <strong>{total_images}</strong></p>"))
# 打印/train_dataset/train/train/cats图片数量
target_directory = './train_dataset/train/train/cats'
total_images = count_images_in_directory(target_directory)
display(HTML(f"<p>目录 '{target_directory}' 中的总图片数量为: <strong>{total_images}</strong></p>"))
# 打印/train_dataset/train/train/dogs图片数量
target_directory = './train_dataset/train/train/dogs'
total_images = count_images_in_directory(target_directory)
display(HTML(f"<p>目录 '{target_directory}' 中的总图片数量为: <strong>{total_images}</strong></p>"))
# 打印/train_dataset/train/validation/cats图片数量
target_directory = './train_dataset/train/validation/cats'
total_images = count_images_in_directory(target_directory)
display(HTML(f"<p>目录 '{target_directory}' 中的总图片数量为: <strong>{total_images}</strong></p>"))
# 打印/train_dataset/train/validation/dogs图片数量
target_directory = './train_dataset/train/validation/dogs'
total_images = count_images_in_directory(target_directory)
display(HTML(f"<p>目录 '{target_directory}' 中的总图片数量为: <strong>{total_images}</strong></p>"))
# 打印/train_dataset/train/test/cats图片数量
target_directory = './train_dataset/train/test/cats'
total_images = count_images_in_directory(target_directory)
display(HTML(f"<p>目录 '{target_directory}' 中的总图片数量为: <strong>{total_images}</strong></p>"))
# 打印/train_dataset/train/test/dogs图片数量
target_directory = './train_dataset/train/test/dogs'
total_images = count_images_in_directory(target_directory)
display(HTML(f"<p>目录 '{target_directory}' 中的总图片数量为: <strong>{total_images}</strong></p>"))
2.3 数据处理
#导包和样本目录
from keras import models, layers
from tensorflow.keras.applications import VGG16
from tensorflow import optimizers
from keras.preprocessing.image import ImageDataGenerator
import matplotlib.pyplot as plt
#训练样本的目录
train_dir='./train_dataset/train/train'
#验证样本的目录
validation_dir='./train_dataset/train/validation'
#测试样本目录
test_dir='./train_dataset/train/test'
#训练集生成器,数据加强
train_datagen=ImageDataGenerator(
rescale=1./255,
rotation_range=40,
width_shift_range=0.2,
height_shift_range=0.2,
shear_range=0.2,
zoom_range=0.2,
horizontal_flip=True,
fill_mode='nearest')
train_generator=train_datagen.flow_from_directory(
directory=train_dir,
target_size=(150,150),
class_mode='binary',
batch_size=20
)
#cats和dogs的分类标签
print(train_generator.class_indices)
print(test_generator.class_indices)
print(validation_generator.class_indices)
3. 使用卷积神经网络识别猫狗图像
3.1 vgg16
VGG16是由Karen Simonyan和Andrew Zisserman于2014年在论文“VERY DEEP CONVOLUTIONAL NETWORKS FOR LARGE SCALE IMAGE RECOGNITION”中提出的一种处理多分类、大范围图像识别问题的卷积神经网络架构, 本项目将分类层的最后一层修改为(1x1x2)即可将分类结果从1000类 修改为二分类。
3.2 CNN
卷积神经网络(Convolutional Neural Network,CNN)是一种专门用于处理具有网格结构数据(如图像和视频)的深度学习模型。CNN 在计算机视觉任务中取得了巨大成功,因为它能够有效地捕获图像中的空间结构信息。
3.3 模型修改
对传统的VGG-16网络进行了调整,将其输出大小改为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)
)
4 在cpu上训练模型
4.1 训练的参数和方法
在jupyter上默认使用cpu进行模型训练,完成vgg16实例化,冻结卷积层,构建基于vgg16的网络结构,使用adam优化函数,使用代价函数,并在过程中记录准确率
#VGG 16实例化---使用imagenet数据集训练,全连接层
conv_base = VGG16(weights='imagenet',
include_top=False, #是否指定模型最后是否包含密集连接分类器
input_shape=(150, 150, 3))
#冻结卷积基----保证其权重在训练过程中不变
conv_base.trainable = False
#构建网络模型----基于VGG16建立模型
model = models.Sequential()
model.add(conv_base)
model.add(layers.Flatten(input_shape=conv_base.output_shape[1:])) #图片输出四维,1代表数量
model.add(layers.Dense(256, activation='relu'))
model.add(layers.Dropout(0.5))
model.add(layers.Dense(1, activation='sigmoid')) #二分类
#定义优化器、代价函数、训练过程中计算准确率
model.compile(optimizer=optimizers.Adam(learning_rate=0.001),
loss='binary_crossentropy',
metrics=['acc'])
model.summary()
4.2 在cpu上完成训练20个Epoch
history=model.fit_generator(
train_generator,
steps_per_epoch=100,
epochs=20,
validation_data=validation_generator,
validation_steps=50
)
4.3 检验并保存模型
#保存模型
model.save('model/model4_2_VGG 16_cats_vs_dogs_1.pth')
#评估测试集的准确率[los,Accuracy]
test_eval=model.evaluate(test_generator)
print("测试集准确率:",test_eval)
train_eval=model.evaluate(train_generator)
print("训练集准确率:",train_eval)
val_eval=model.evaluate(validation_generator)
print("验证集准确率:",val_eval)
推理时间和f1的值
from keras.models import load_model
from keras.preprocessing.image import ImageDataGenerator
import numpy as np
import time
from sklearn.metrics import f1_score
# 加载模型
model = load_model('model/model4_2_VGG 16_cats_vs_dogs_1.h5')
model.summary()
test_datagen = ImageDataGenerator(rescale=1./255)
test_generator = test_datagen.flow_from_directory(
directory="猫狗大战_test",
target_size=(150,150),
shuffle=False,
class_mode='binary',
batch_size=20
)
# 预测
start_time = time.time()
result = model.predict(test_generator)
end_time = time.time()
# 转换为整数
result = [(int((result[i][0] + 0.5) / 1.0)) for i in range(len(result))]
# 计算 F1 值
f1 = f1_score(test_label, result)
print("推理时间: {} 秒".format(end_time - start_time))
print("F1 值: ", f1)
4.4 抽样检测模型
冲测试目录的cats和dogs各抽取两张,调用保存好的vgg16模型进行测试
#随机抽样测试模型
import os
import random
from keras.models import load_model
from keras.preprocessing import image
import numpy as np
from PIL import Image
from IPython.display import display
# 加载模型
model = load_model('model/model4_2_VGG 16_cats_vs_dogs_1.h5')
model.summary()
# 定义图像预处理函数
def preprocess_image(img_path, target_size=(150, 150)):
img = image.load_img(img_path, target_size=target_size)
img_array = image.img_to_array(img)
img_array = np.expand_dims(img_array, axis=0)
img_array /= 255.0 # 归一化
return img_array
# 获取 'cats' 文件夹中的所有图像文件
test_images_path = "train_dataset/test2/cats"
all_test_images = [os.path.join(test_images_path, img) for img in os.listdir(test_images_path)]
# 从 'cats' 文件夹中随机选择 2 张图片
selected_test_images = random.sample(all_test_images, 2)
# 预测并输出结果
for img_path in selected_test_images:
# 预处理图像
img_array = preprocess_image(img_path)
# 预测
prediction = model.predict(img_array)
prediction = int((prediction[0][0] + 0.5) / 1.0) # 转为整数
# 打印预测结果
print(f"图像路径: {img_path}, 预测结果: {'猫' if prediction == 0 else '狗'}")
img = Image.open(img_path)
display(img)
# 获取 'dogs' 文件夹中的所有图像文件
test_images_path = "train_dataset/test2/dogs"
all_test_images = [os.path.join(test_images_path, img) for img in os.listdir(test_images_path)]
# 从 'cats' 文件夹中随机选择 2 张图片
selected_test_images = random.sample(all_test_images, 2)
# 预测并输出结果
for img_path in selected_test_images:
# 预处理图像
img_array = preprocess_image(img_path)
# 预测
prediction = model.predict(img_array)
prediction = int((prediction[0][0] + 0.5) / 1.0) # 转为整数
# 打印预测结果
print(f"图像路径: {img_path}, 预测结果: {'猫' if prediction == 0 else '狗'}")
img = Image.open(img_path)
display(img)
测试结果均正确
5. 运用one api提升模型性能
5.1 迁移学习 Transfer Learning with oneAPI AI Analytics Toolkit
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()
# 创建 CustomVGG16 模型实例
# 加载权重
vgg16_model.vgg16_model.load_state_dict(torch.load('vgg16.pth', map_location=torch.device('cpu')))
5.2 Intel Extension for PyTorch 优化并保存优化后的模型
# 将模型移动到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)
优化后,在次执行cpu上调用保存的模型,得到了推理时间变短的结果如下
保存模型
# 保存模型参数
torch.save(vgg16_model.state_dict(), 'vgg16_optimized.pth')
# 加载模型参数
loaded_model = CustomVGG16()
loaded_model.load_state_dict(torch.load('vgg16_optimized.pth'))
5.3加载模型,并以准确度为评估函数进行量化
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)
量化结果
量化后保存
5.4 使用量化后的模型进行推理
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)
明显在推理速度上得到了提升
6.实验总结
完成基础的模型训练后,再使用oneAPI优化组件,推理的速度有明显提升,如果在使用量化工具,推理的时间还能可以进一步下降,并且在整个过程中F1分数的值一直能稳定在0.89以上,这是一个非常好的现象。证明了oneAPI优秀的模型压缩能力,在保证模型精确度,F1值的基础上还能够缩小模型的规模。