这篇文章主要讲述的是如何通过自己建立的图片数据集训练一个模型。即从0开始到最终建立一个模型。(我举的例子是我将模型部署到openmv开发板上训练模型的过程)
数据集建立
数据收集
我做的是一个六分类问题的数据集,数据集中包含六种不同的食物,收集方式就是通过手机拍摄。拍摄完成后,将图片全部导入电脑,并将每个图片重命名一下(emmm,我觉得这样更好看,自己也能看的更明白),然后分不同的文件夹存储(这个是为了观察数据集,然后后面也能知道数据集数量,为了省事也可以就存在同一个文件夹下)。然后将整个文件夹压缩成压缩包(我的父目录是training,所以就压缩training就好了)
到这里,数据收集就结束了。顺便一提,这里图片的数量根据你自己的需求来定。
数据集标注
数据集导入
当建立完一个图片数据集后,就可以开始图像标注了。这里我选择的是
https://console.bce.baidu.com/easydata/app
百度智能云easydata来进行图像标注。
然后登录,进入主界面。
即这个界面,点击创建数据集,填写数据集名称,选择物体检测,然后点击创建并导入。
然后点击从本地导入,选择上传压缩包,然后点击上传压缩包,点击已阅读并上传,然后将你的压缩包上传上去即可。
等待文件上传好后,点击确认,然后会回到主界面。在主界面会显示正在导入。等进度条走完,数据集就导入好了。
数据集标注
点击标注,进入数据标注界面,先添加标签,在界面右方,点击添加标签然后自定义标签名,我这里选择的hamburger,cake,hotdog,sandwich,popcorn,chips作为标签名。
创建好后,即可开始标注,先将每个类别的数据标注十个,即画框然后确认标签。因为需要开启智能标注的前提是每个类别都标注了十个以上。然后点击智能标注。
点击提交。
等待自动标注。
看右上角的提示,继续接下来的步骤。
上图是第一轮结束后的,我们可以进入下一轮难例筛选,也可以直接自动标注未标图片。这里根据个人需求来选择,我认为还是轮次越多准确度越高,后期微调也越少。
结束后,点击左边目录的在线标注标签浏览一遍所有标注的标签是否正确。如果正确,即可回到左边目录中数据管理下的数据集图标,然后导出即可。
数据集导出
点击导出,选择xml格式,导出到本地,点击开始导出即可。
可以在导出记录查看导出状态,最后的下载下来即可。下载下来会是一个压缩包,解压后的文件夹目录如图
其中,images放的是我们所有的图片,annotations放的是我们的标签。
到这里,数据集建立就基本结束。
模型训练
模型训练是在anaconda下进行的。
环境配置
conda create -n tf python=3.7
conda activate tf
pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple
pip install tensorflow==2.2.0
pip install opencv-contrib-python -i Simple Indexconda install ipykernel
python -m ipykernel install --user --name=tf
打开anaconda prompt 然后输入上述语句创建一个虚拟环境。
conda create -n tf python=3.7:即创建一个python版本为3.7的名为tf的虚拟环境
conda activate tf:激活tf 环境
pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple:配置pip使用清华大学的镜像源,方便加速下载。
pip install tensorflow==2.2.0:下载tensorflow2.2.0版本
pip install opencv-python -i Simple Index
conda install ipykernel:使用conda安装ipykernel库。ipykernel允许在Jupyter Notebook或Jupyter Lab中使用不同的Python环境作为kernel运行。
python -m ipykernel install --user --name=tf:将当前conda环境(tf
)添加到Jupyter Notebook或Jupyter Lab中,使得在这些环境中可以选择并使用这个特定的环境运行代码。
下载opencv库及其扩展功能(opencv-contrib)。
代码实现
库的导入
import os
import cv2
import xml.etree.ElementTree as ET
import numpy as np
加载一个数据集,数据集包含图像文件(.jpg格式)和与之对应的XML格式的注解文件,提取图像数据并解析注解文件中的标签信息,最终返回图像数组和标签数组。
def load_data(dataset_path):
# 构建图像和注解文件路径
images_path = os.path.join(dataset_path, 'images') # 图像文件夹路径
annotations_path = os.path.join(dataset_path, 'annotations') # 注解文件夹路径
images = [] # 存储图像的列表
labels = [] # 存储标签的列表
# 遍历图像文件夹中的每个文件
for filename in os.listdir(images_path):
if filename.endswith('.jpg'): # 仅处理.jpg格式的图像文件
image_path = os.path.join(images_path, filename) # 构建图像文件的完整路径
annotation_path = os.path.join(annotations_path, f'{os.path.splitext(filename)[0]}.xml') # 构建对应的XML注解文件路径
# 读取图像并调整大小为模型需要的尺寸
image = cv2.imread(image_path) # 使用OpenCV读取图像
image = cv2.resize(image, (224, 224)) # 调整图像大小为224x224像素
images.append(image) # 将处理后的图像添加到列表中
# 解析XML文件以获取标签信息
tree = ET.parse(annotation_path) # 使用ElementTree解析XML文件
root = tree.getroot() # 获取XML文件的根元素
label = root.find('object/name') # 查找XML文件中第一个对象的名称标签
if label is not None:
label = label.text # 获取标签的文本内容
labels.append(label) # 将标签添加到列表中
images = np.array(images) # 将图像列表转换为NumPy数组
labels = np.array(labels) # 将标签列表转换为NumPy数组
return images, labels
# 加载数据集
dataset_path = 'study' # 数据集所在的根文件夹路径
images, labels = load_data(dataset_path) # 调用加载数据的函数
# 打印加载的数据集大小信息
print(f'Loaded {len(images)} images and {len(labels)} labels.')
标签转换
label_map = {'hamburger': 0, 'sandwich': 1, 'hotdog': 2, 'chips': 3, 'popcorn': 4, 'cake': 5} # 定义标签映射,将标签字符串映射到数字类别
pop = [label_map[label_str] for label_str in labels] # 使用列表推导式将每个标签字符串映射为其对应的数字类别
pop = np.array(pop) # 将列表转换为NumPy数组,得到最终的标签数组
模型导入
from tensorflow.keras.models import Sequential # 导入Sequential模型,用于按顺序堆叠神经网络层
from tensorflow.keras.layers import Conv2D, MaxPooling2D, Flatten, Dense # 导入各种神经网络层类
model = Sequential([ # 创建Sequential模型,按顺序添加各层
Conv2D(32, (3, 3), activation='relu', input_shape=(224, 224, 3)), # 添加第一个卷积层,32个3x3的卷积核,relu激活函数,输入形状为(224, 224, 3)
MaxPooling2D((2, 2)), # 添加最大池化层,池化窗口大小为2x2
Conv2D(64, (3, 3), activation='relu'), # 添加第二个卷积层,64个3x3的卷积核,relu激活函数
MaxPooling2D((2, 2)), # 添加最大池化层,池化窗口大小为2x2
Conv2D(128, (3, 3), activation='relu'), # 添加第三个卷积层,128个3x3的卷积核,relu激活函数
MaxPooling2D((2, 2)), # 添加最大池化层,池化窗口大小为2x2
Flatten(), # 添加Flatten层,将多维输入展平为一维
Dense(128, activation='relu'), # 添加全连接层,128个神经元,relu激活函数
Dense(6, activation='softmax') # 添加输出层,6个神经元(对应6个类别),softmax激活函数用于多分类
])
model.compile(optimizer='adam', # 编译模型,选择优化器为Adam
loss='sparse_categorical_crossentropy', # 损失函数为稀疏分类交叉熵
metrics=['accuracy']) # 评估指标为准确率
定义了一个卷积神经网络模型,用于处理224x224的彩色图像(输入通道为3)。模型包括三个卷积层,每个后跟一个最大池化层来逐渐减少空间尺寸。Flatten层用于将三维特征图展平为一维向量,然后是两个全连接层,最后一个输出层使用softmax激活函数,适用于多分类任务。
训练模型
model.fit(images, pop, epochs=3, batch_size=5, validation_split=0.2)
images
: 这是训练用的输入数据,通常是一个包含多个图像的张量(如NumPy数组),每个图像的形状为(224, 224, 3)
,这与模型中的input_shape
一致。
pop
: 这是训练用的目标标签数据,是与输入图像对应的标签数组,用于指导模型学习正确的分类。
epochs=3
: 这指定了整个数据集训练的轮数(epochs),即模型将从头到尾多次遍历整个数据集(images
和pop
)进行学习和优化。
batch_size=5
: 这是批量大小,即每次模型更新参数时,使用的样本数。
validation_split=0.2
: 这表示从训练数据中分割出20%作为验证集。训练过程中,模型将不会看到验证集的数据,而是使用它来评估模型在未见过的数据上的表现。
模型保存
import tensorflow as tf
tf.saved_model.save(model,'ttt')
其中,'model'
为之前定义且训练好的模型, 'ttt'
为保存模型的目标路径或文件夹。在这里,模型将被保存在名为 'ttt'
的文件夹中。
模型转换
# 保存 Keras 模型为 HDF5 文件
model.save('my_model.h5')
# 从 HDF5 文件加载 Keras 模型
model = tf.keras.models.load_model('my_model.h5')
# 创建一个 TensorFlow Lite 转换器,并加载 Keras 模型
converter = tf.lite.TFLiteConverter.from_keras_model(model)
# 将 Keras 模型转换为 TensorFlow Lite 模型
tflite_model = converter.convert()
# 将 TensorFlow Lite 模型保存到文件
with open('model.tflite', 'wb') as f:
f.write(tflite_model)