[Intel校企合作课程]基于VGG-16的猫狗大战

1.项目准备

1.1问题描述

猫狗大战是一个经典的计算机视觉问题,旨在通过图像识别算法来区分猫和狗的图像。该问题的数据集通常包含大量的猫和狗的图像,用于训练和测试机器学习模型。在这个问题中,我们需要使用机器学习或深度学习算法来训练一个模型,使其能够准确地识别给定图像中的猫和狗。

1.2任务描述

通过训练一个机器学习(分类)模型,使其在给定一张图像时能够准确地预测图像中是猫还是狗。模型应该能够推广到未见过的图像,并在测试数据上表现良好。

1.3数据集

根据课程组给的数据集压缩包,里面包含了train.zip和test.zip,解压后train文件内有25000张猫狗图片(12500张猫+12500张狗),test文件内有1000张猫狗图片,且两个文件中照片名均含有cat/dog标签。

2.开始项目

2.1加载库

以下是机器学习以及深度学习过程中所要用到的库

import os
import pandas
import matplotlib.pyplot as plt
import matplotlib.image as img
import tensorflow.compat.v1 as tf
from tensorflow.keras.preprocessing.image import ImageDataGenerator
from tensorflow.keras.applications.vgg16 import VGG16
from tensorflow.keras.layers import Flatten, Dense, Dropout
from tensorflow.keras.models import Sequential
from sklearn.model_selection import train_test_split
from tensorflow.keras.callbacks import ModelCheckpoint

2.2查看数据集情况

获取图片路径同时打印数据集大小

# 设置训练集和测试集文件路径
train_path = './train'
test_path = './test'

# 获取训练集和测试集文件名列表
train_file_names = os.listdir(train_path)
test_file_names = os.listdir(test_path)

# 打印训练集和测试集文件数量
print('The number of training sets is:{}'.format(len(train_file_names)))
print('The number of test sets is:{}'.format(len(test_file_names)))

2.3处理数据集

将训练集和测试集中的图像文件路径和标签提取出来,并存储到一个 DataFrame 对象中,并打印DataFrame的前几行。

# 创建空列表用于存储图像文件路径和标签
image_paths = []
labels = []

# 遍历训练文件名列表
for train_file in train_file_names:
    # 从文件名中提取标签(假设文件名为 label.jpg)
    label = train_file.split('.')[0]
    labels.append(label)
    
    # 构建图像文件的完整路径
    image_path = os.path.join(train_path, train_file)
    image_paths.append(image_path)

# 创建一个空的 DataFrame 对象来存储图像文件路径和标签
train_df = pandas.DataFrame()

# 将图像文件路径列表赋值给 DataFrame 的 'train_image_path' 列
train_df['train_image_path'] = image_paths 

# 将标签列表赋值给 DataFrame 的 'label' 列
train_df['label'] = labels

# 打印 DataFrame 的前几行
train_df.head()

# 创建空列表用于存储测试集图像文件路径和索引
image_paths = []
test_idx = []

# 遍历测试集文件名列表
for test_file in test_file_names:
    # 从文件名中提取索引(假设文件名为 idx.jpg)
    idx = test_file.split('.')[0]
    test_idx.append(idx)
    
    # 构建测试集图像文件的完整路径
    image_path = os.path.join(test_path, test_file)
    image_paths.append(image_path)

# 创建一个空的 DataFrame 对象来存储测试集图像文件路径和索引
test_df = pandas.DataFrame()

# 将测试集图像文件路径列表赋值给 DataFrame 的 'test_image_path' 列
test_df['test_image_path'] = image_paths 

# 打印 DataFrame 的前几行
test_df.head()

2.4查看部分图片

读取训练集中前16个样本的图像文件,并使用 matplotlib 库中的子图功能将它们展示在一个 4x4 的网格中。

import matplotlib.pyplot as plt
import matplotlib.image as img

# 创建一个4x4的子图布局,设置图像大小为10x10英寸
fig, axes = plt.subplots(4, 4, figsize=(10, 10))

# 将子图展平为一维数组
axes = axes.ravel()

# 循环遍历前16个样本,并在每个子图中展示图像
for i in range(16):
    # 使用`img.imread`函数读取图像文件
    axes[i].imshow(img.imread(train_set.iloc[i, 0]))
    
    # 关闭子图的坐标轴显示
    axes[i].axis('off')
    
# 自动调整子图之间的间距和位置,使它们更好地适应图表的大小
fig.tight_layout()

# 显示图表
plt.show()

2.5拆分训练集

使用分层抽样将训练集划分为训练集和验证集,并绘制它们的标签分布直方图以检查分层抽样效果。

from sklearn.model_selection import train_test_split

# 使用分层抽样将训练集划分为训练集和验证集
train_set, val_set = train_test_split(train_df, random_state=42, stratify=train_df['label'])

print("Training set size: {}".format(len(train_set)))
print("Validation set size: {}".format(len(val_set)))

# 绘制训练集和验证集的标签分布直方图
train_set['label'].hist() # 样本无偏
val_set['label'].hist()

2.6增强数据

导入ImageDataGenerator类用于train、test和val的数据增强,通过数据增强生成更多的训练样本,提高模型的泛化能力。数据增强可以通过对原始图像应用一系列随机变换,生成多个经过变换后的图像,从而扩充训练集的规模。这有助于减少过拟合现象,提高模型的性能和稳定性。

from keras.preprocessing.image import ImageDataGenerator

# 创建一个 ImageDataGenerator 对象,用于数据增强
train_gen = ImageDataGenerator(
    zoom_range=0.1, # 随机缩放图像的大小范围
    rotation_range=10, # 随机旋转图像的角度范围
    rescale=1./255, # 缩放像素值到 [0,1] 区间
    shear_range=0.1, # 随机扭曲图像的剪切强度
    horizontal_flip=True, # 随机水平翻转图像
    width_shift_range=0.1, # 随机水平平移图像的宽度比例
    height_shift_range=0.1 # 随机垂直平移图像的高度比例
)

# 从 DataFrame 中读取训练集数据,并使用 train_gen 进行数据增强
train_generator = train_gen.flow_from_dataframe(
    dataframe=train_set, # 训练集 DataFrame
    x_col='train_image_path', # 图像文件路径列名
    y_col='label', # 标签列名
    target_size=(200,200), # 图像尺寸
    class_mode='binary', # 二分类问题
    batch_size=128, # 每个批次的图像数量
    shuffle=False # 不打乱数据顺序
)

print(len(train_generator)) # 输出训练集生成器的长度

 

val_gen = ImageDataGenerator(
    rescale=1./255
)

# 使用 flow_from_dataframe 函数从 DataFrame 中创建验证集的图像生成器
val_generator = val_gen.flow_from_dataframe(
    dataframe=val_set,  # 验证集的 DataFrame
    x_col='train_image_path',  # 图像文件路径列名
    y_col='label',  # 标签列名
    target_size=(200, 200),  # 目标图像尺寸
    class_mode='binary',  # 类别模式(二分类)
    batch_size=128,  # 批量大小
    shuffle=False  # 不对数据进行洗牌
)

print(len(val_generator))

# 创建一个 ImageDataGenerator 对象,用于对测试集进行数据预处理
test_datagen = ImageDataGenerator(
    rescale=1./255 # 缩放像素值到 [0,1] 区间
)

# 从 DataFrame 中读取测试集数据,并使用 test_datagen 进行数据预处理
test_generator = test_datagen.flow_from_dataframe(
    dataframe=test_df, # 测试集 DataFrame
    x_col='test_image_path', # 图像文件路径列名
    y_col=None, # 没有标签列
    target_size=(200,200), # 图像尺寸
    class_mode=None, # 没有标签,不需要返回类别
    batch_size=128, # 每个批次的图像数量
    shuffle=False # 不打乱数据顺序
)

print(len(test_generator)) # 输出测试集生成器的长度

 

 2.7加载模型

VGG16是由Karen Simonyan和Andrew Zisserman于2014年在论文“VERY DEEP CONVOLUTIONAL NETWORKS FOR LARGE SCALE IMAGE RECOGNITION”中提出的一种处理多分类、大范围图像识别问题的卷积神经网络架构,成功对ImageNet数据集的14万张图片进行了1000个类别的归类并有92.7%的准确率。

构建一个基于 VGG16 的分类模型,通过迁移学习使用预训练的 VGG16 模型提取图像特征,并在顶部添加全连接层进行分类。冻结卷积层的权重可以保持预训练模型的特征提取能力,同时减少需要训练的参数数量,加快模型的训练速度。最后,通过编译模型并打印摘要信息,对模型进行配置和检查。

import tensorflow.compat.v1 as tf
from tensorflow.keras.applications.vgg16 import VGG16
from tensorflow.keras.layers import Flatten, Dense, Dropout
from tensorflow.keras.models import Sequential

# 加载预训练的VGG16模型,不包含顶部的全连接层
vgg16_model = VGG16(weights='imagenet', include_top=False, input_shape=(200, 200, 3))

# 冻结卷积层的权重
for layer in vgg16_model.layers:
    layer.trainable = False

# 新建顶部的全连接层
model = Sequential()
model.add(vgg16_model)
model.add(Flatten())
model.add(Dense(128, activation='relu'))
model.add(Dropout(0.2))
model.add(Dense(1, activation='sigmoid'))

# 编译模型,选择的是Adam优化器
model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])

# 打印模型信息
model.summary()

 vgg16模型信息如下:

 

 2.8模型训练

 训练时我定义一个 F1ScoreCallback 类,该类继承自 tf.keras.callbacks.Callback,并在每个训练周期结束时计算和打印 F1 值。然后,使用 ModelCheckpoint 回调函数保存训练过程中表现最好的模型,并使用训练数据集、验证数据集和定义的回调函数来训练模型,最后保存模型。

import tensorflow as tf
from tensorflow.keras.callbacks import ModelCheckpoint

# 定义模型保存路径
checkpoint_save_path = "./checkpoint/mnist.ckpt"

# 检查之前保存的模型文件是否存在
if os.path.exists(checkpoint_save_path + '.index'):
    print('开始训练...')
    model.load_weights(checkpoint_save_path)  # 加载模型权重

# 创建模型保存的回调函数
cp_callback = ModelCheckpoint(
    filepath=checkpoint_save_path,
    save_weights_only=True,  # 只保存权重
    save_best_only=True  # 只保存在验证集上表现最好的模型
)

# 训练模型
history = model.fit(
    train_generator,  # 训练数据集
    epochs=1,  # 训练轮数
    batch_size=128,  # 批次大小
    validation_data=val_generator,  # 验证数据集
    validation_freq=1,  # 验证频率
    callbacks=[cp_callback],  # 回调函数列表
    verbose=1  # 打印训练过程中的详细信息
)

# 保存模型
model.save('my_model.h5')

生成的模型保存如下 

 

 2.9预测测试集

加载已保存的模型并使用该模型对测试数据进行推理。通过计算 F1 值评估模型的性能,并打印出推理时间。

from tensorflow.keras.models import load_model
from sklearn.metrics import f1_score
import time

# 加载模型
loaded_model = load_model('my_model.h5')

# 计算推理时间
start_time = time.time()

# 使用加载的模型进行预测
predictions = loaded_model.predict(test_generator)

end_time = time.time()
inference_time = end_time - start_time

# 获取真实标签
y_test = test_generator.labels

# 将预测结果转换为二进制形式
y_pred_binary = tf.round(predictions)

# 计算 F1 值
f1 = f1_score(y_test, y_pred_binary)

# 打印 F1 值
print(f'F1 Score: {f1}')

# 打印推理时间
print(f'Inference Time: {inference_time:.2f}s')

 

2.10使用OneAPI组件加速

首先将 Keras 模型转换为 TensorFlow 序列化 protobuf 格式,并保存为 .pb 文件。然后,使用 OpenVINO 的 Model Optimizer 工具将模型转换为 OpenVINO IR 格式(.xml.bin 文件)。接着,使用 Inference Engine Core (IECore) 对象创建推理执行器 (exec_net),并使用该执行器进行推理。最后,计算推理时间和 F1 值,并输出结果。

import tensorflow as tf
from tensorflow.keras.models import load_model
from sklearn.metrics import f1_score
import time

# 加载模型
loaded_model = load_model('my_model.h5')

# 转换模型为 TensorFlow 序列化 protobuf 格式
model_path = './my_model.pb'
tf.saved_model.save(loaded_model, model_path)

# 加载 TensorFlow 模型
loaded_graph_def = tf.compat.v1.GraphDef()
with tf.io.gfile.GFile(model_path, 'rb') as f:
    loaded_graph_def.ParseFromString(f.read())

# 将 TensorFlow 模型转换为 OpenVINO IR 模型
ir_model_path = 'my_model.xml'
mo_tf_path = '/opt/intel/openvino/deployment_tools/model_optimizer/mo_tf.py'
!python {mo_tf_path} --input_model {model_path} --output_dir . --model_name my_model --input_shape [1,224,224,3] --data_type FP32

# 加载 OpenVINO 推理引擎
ie = IECore()
net = ie.read_network(model='my_model.xml', weights='my_model.bin')

# 获取输入输出节点名称
input_blob = next(iter(net.inputs))
output_blob = next(iter(net.outputs))

# 创建执行器
exec_net = ie.load_network(network=net, device_name="CPU")

# 从测试生成器中获取一个批次的数据进行推理
batch_size = 32
x_test, y_test = test_generator.next()
x_test = x_test.astype('float32')

# 计算推理时间
start_time = time.time()

# 使用加载的模型进行预测
res = exec_net.infer(inputs={input_blob: x_test})

end_time = time.time()
inference_time = end_time - start_time

# 获取预测结果
predictions = res[output_blob]

# 将预测结果转换为二进制形式
y_pred_binary = tf.round(predictions)

# 计算 F1 值
f1 = f1_score(y_test, y_pred_binary)

# 打印 F1 值
print(f'F1 Score: {f1}')

# 打印推理时间
print(f'Inference Time: {inference_time:.2f}s')

使用OneAPI加速组件后推理时间明显缩短,并且f1并没有下降 

 

3.结论

  • 使用 VGG16 模型可以取得较好的图像分类效果。
  • 将模型转换为 TensorFlow 序列化 protobuf 格式可以方便地保存和加载模型。
  • OpenVINO 提供了强大的硬件加速能力,可以进一步提高模型的推理性能。
  • 在实际应用中,可以根据硬件环境选择合适的推理加速方式,以获得最佳的性能和效率。

使用 VGG16 模型进行图像分类,在模型转换和推理加速方面,可以借助 TensorFlow 和 OpenVINO 提供的工具和库来优化和加速模型的训练和推理过程,从而提高模型的性能和效率。 

  • 37
    点赞
  • 51
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值