写在前面
最近要参加一个比赛,是做图像识别的,简单说下数据集,这是一些交通图片,jpg格式,像素为1280*720,类似这样:
每个图片对应的标签数据是一个txt文件,内容长这样:
每行是5个数字,第一个数字是物体类别的编号,第二第三个数字是物体在图片中左上角点的位置,第四第五是物体在图片中右下角点的位置,这样不方便看,于是用ChatGPT写了一个小工具用来看这个训练数据和标签数据的情况,工具不放出来了,就是根据标签数据在图片上画框框:
这里心疼标注人员一秒钟
训练数据集分为白天的和晚上的,我目前只看了白天的数据,3万多张,先拿这个训练,其它的再说吧
我准备使用Tensorflow Object Detection Api中的预训练模型进行训练,一是自己没有用过,二是想看看效果,所以跟着各路教程搭建一下这个环境
目前已有的环境是:Win11,Anaconda Python 3.8.19, tensorflow=2.10, CUDA Toolkit=11.2, cuDNN=8.1, 显卡GeForce RTX 3080
来吧,开干
第一步 安装Tensorflow Object Detection API
这里先参照这篇文章进行安装,这篇文章前面还介绍了Anaconda和Tensorflow的安装过程,我没看,因为我装好了,所以也不知道写得好不好
1. 下载Tensorflow Object Detection API代码
首先第一步,下载Tensorflow Object Detection API代码,有git用git,没git从项目页面直接下载zip解压也可以
我在我的Python项目里建了一个TensorFlow文件夹,在里面克隆代码git clone https://github.com/tensorflow/models
目录结构如下:
2. 安装Protobuf
这个Protobuf是个编译工具,用来编译上一步里下载的代码,还是从git链接上下载zip包就可以,按照文档安装3.12.3版本,在Assets里选择protoc-3.12.3-win64.zip
下载之后解压,里面会有一个bin目录,这个目录添加到系统环境变量Path中即可
打开cmd,运行protoc --version
出现版本号信息就表示安装好了
3. 编译代码
然后就用protoc去编译Tensorflow Object Detection Api里的代码
cmd进入Tensorflow Object Detection Api的models/research
目录,文档上有这么一个提示框:
大概的意思是如果在Windows上使用3.5以上版本的Protobuf,需要执行下面这个命令,那说的不就是我吗,复制,粘贴,运行
也没个SUCCESS之类的提示,心慌,但愿没有消息就是好消息,继续
4. 安装COCO API
有关coco api的说明,可以参考(一) COCO Python API - 使用篇
大概是使用COCO数据集的API,COCO数据集是一种图片标注数据集,既然模型封装了,所以数据集也得处理成模型认识的格式,我们先装,后面再处理
注意这个东西依赖Visual C++ 2015以上版本进行编译,所以需要把Visual C++ 2015添加到环境变量里,文档给了安装包链接
进入conda,在对应的Python环境里安装cython和pycocotools两个包
pip install cython
pip install pycocotools
,文档里从git上下载安装,我遇到了报错问题,就参考这个直接安装了
5. 安装Object Detection API依赖
cmd进入Tensorflow Object Detection Api代码目录,进入models/research
文件夹,执行:
copy object_detection\packages\tf2\setup.py .
python -m pip install .
因为我用的是Windows,所以路径斜杠与Linux不同,使用的也是copy命令而不是cp命令
这个过程有点漫长,可以来一把轻松愉快的Warcraft III
吐槽一下人工智能这些个工具包,版本依赖问题太恶心了,装完这个装那个,各路大佬整理的资料都不一样,太难了
6. 测试安装效果
确保在models/research
目录中,执行python object_detection/builders/model_builder_tf2_test.py
妈的果然就出错了
参考装一下protobuf库pip install protobuf==3.20.3
,之前用的3.19版本有问题
测试的过程中GPU也是能跑起来的
好了,至此这个环境就算搭建好了,再顺便骂一遍这些个开发者,BUG满天飞!
第二步 转换数据集
这一步我没有成功!可以直接跳到第三步去
因为这是一个预训练模型,因此数据集的输入格式跟我的训练数据格式是不一样的,所以需要进行转换,这个地方我用了ChatGPT,以下步骤基本是在ChatGPT-4o指导下进行的
1. 编写代码把图片和标签数据转换为TFRecord格式
这里先说一下我的目录结构
白天的训练集在dataset/trainA
目录下,images是图片数据,labels里面是标签数据,和文章最开头展示的一样
TFODAPI/Tensorflow
目录是第一步里面我们搭建环境的那个目录
main.py
是我的代码,内容是:
import os
import random
import tensorflow as tf
from PIL import Image
from sklearn.model_selection import train_test_split
# 设置随机种子以便结果可重复
random.seed(42)
def create_tf_example(image_path, label_path):
with tf.io.gfile.GFile(image_path, 'rb') as fid:
encoded_image = fid.read()
image = Image.open(image_path)
width, height = image.size
with open(label_path, 'r') as file:
lines = file.readlines()
xmins = []
xmaxs = []
ymins = []
ymaxs = []
classes_text = []
classes = []
for line in lines:
parts = line.strip().split()
class_id = int(parts[0])
xmin = float(parts[1]) / width
ymin = float(parts[2]) / height
xmax = float(parts[3]) / width
ymax = float(parts[4]) / height
xmins.append(xmin)
xmaxs.append(xmax)
ymins.append(ymin)
ymaxs.append(ymax)
classes_text.append(str(class_id).encode('utf8'))
classes.append(class_id)
tf_example = tf.train.Example(features=tf.train.Features(feature={
'image/height': tf.train.Feature(int64_list=tf.train.Int64List(value=[height])),
'image/width': tf.train.Feature(int64_list=tf.train.Int64List(value=[width])),
'image/filename': tf.train.Feature(bytes_list=tf.train.BytesList(value=[image_path.encode('utf8')])),
'image/source_id': tf.train.Feature(bytes_list=tf.train.BytesList(value=[image_path.encode('utf8')])),
'image/encoded': tf.train.Feature(bytes_list=tf.train.BytesList(value=[encoded_image])),
'image/format': tf.train.Feature(bytes_list=tf.train.BytesList(value=['jpg'.encode('utf8')])),
'image/object/bbox/xmin': tf.train.Feature(float_list=tf.train.FloatList(value=xmins)),
'image/object/bbox/xmax': tf.train.Feature(float_list=tf.train.FloatList(value=xmaxs)),
'image/object/bbox/ymin': tf.train.Feature(float_list=tf.train.FloatList(value=ymins)),
'image/object/bbox/ymax': tf.train.Feature(float_list=tf.train.FloatList(value=ymaxs)),
'image/object/class/text': tf.train.Feature(bytes_list=tf.train.BytesList(value=classes_text)),
'image/object/class/label': tf.train.Feature(int64_list=tf.train.Int64List(value=classes)),
}))
return tf_example
def create_tfrecord(output_path, image_label_pairs):
writer = tf.io.TFRecordWriter(output_path)
i = 0
for image_path, label_path in image_label_pairs:
tf_example = create_tf_example(image_path, label_path)
writer.write(tf_example.SerializeToString())
i += 1
print(i)
writer.close()
if __name__ == '__main__':
# 获取所有图片和标签文件路径
image_dir = '../dataset/trainA/train_dataset/images'
label_dir = '../dataset/trainA/train_dataset/labels'
image_files = [os.path.join(image_dir, f) for f in os.listdir(image_dir) if f.endswith('.jpg')]
label_files = [os.path.join(label_dir, f.replace('.jpg', '.txt')) for f in os.listdir(image_dir) if f.endswith('.jpg')]
# 分割训练集和验证集
train_images, val_images, train_labels, val_labels = train_test_split(image_files, label_files, test_size=0.2,
random_state=42)
# 创建TFRecord文件
create_tfrecord('train.tfrecord', zip(train_images, train_labels))
# 创建验证集文件
create_tfrecord('val.tfrecord', zip(val_images, val_labels))
这段代码实际上是读取images文件夹和labels文件夹里的文件,然后生成两个文件,分别是train.trfecord
和val.tfrecord
,第一个是训练集数据,第二个是验证集数据
接下来写一个类别映射文件label_map.pbtxt
,因为这个比赛图片中有9中类别,所以文件内容写的是
item {
id: 0
name: 'bus'
}
item {
id: 1
name: 'traffic_light'
}
item {
id: 2
name: 'traffic_sign'
}
item {
id: 3
name: 'person'
}
item {
id: 4
name: 'bike'
}
item {
id: 5
name: 'truck'
}
item {
id: 6
name: 'motor'
}
item {
id: 7
name: 'car'
}
item {
id: 8
name: 'rider'
}
2. 下载模型并修改配置文件
然后下载TensorFlow Object Detection API提供的与训练模型,因为我用的windows,所以直接从链接下载,下载完以后用7-Zip解压
把这个efficientdet_d0_coco17_tpu-32
文件夹整个放在我的main.py
同级目录下
然后修改pipeline.config
中的内容
这里需要修改的都是路径,路径我是相对于pipeline.config
文件来写的
3. 训练模型
这里遇到了非常多的他妈的坑,我猜都是GPT给我挖的
在conda里进入TFODAPI
目录,这个目录里efficientdet_d0_coco17_tpu-32
是模型,Tensorflow
是第一步安装的Tensorflow Object Detection API
执行命令python TensorFlow\models\research\object_detection\model_main_tf2.py --pipeline_config_path=efficientdet_d0_coco17_tpu-32\pipeline.config --model_dir=efficientdet_d0_coco17_tpu-32\checkpoint --alsologtostderr
然后报错
这里看着是编码问题,我检查了pipeline.config
、label_map.pbtxt
、标签txt数据,都是utf-8呀
看报错是label_map_util.py报错,所以我猜是label_map.pbtxt
文件有问题,但是能有什么问题呢?
于是我做了两步修改,首先把label_map.pbtxt
里面的id从1开始,而不再从0开始
item {
id: 1
name: 'bus'
}
item {
id: 2
name: 'traffic_light'
}
item {
id: 3
name: 'traffic_sign'
}
item {
id: 4
name: 'person'
}
item {
id: 5
name: 'bike'
}
item {
id: 6
name: 'truck'
}
item {
id: 7
name: 'motor'
}
item {
id: 8
name: 'car'
}
item {
id: 9
name: 'rider'
}
第二步修改pipeline.config
中的路径为绝对路径,因为我实在不知道是相对的谁,如果是相对的model_main_tf2.py
那真的是恶心死,所以先写绝对路径,随后再去尝试相对路径吧
再次运行python TensorFlow\models\research\object_detection\model_main_tf2.py --pipeline_config_path=efficientdet_d0_coco17_tpu-32\pipeline.config --model_dir=efficientdet_d0_coco17_tpu-32\checkpoint --alsologtostderr
报错更新
这是说检查点路径Checkpoint dir和模型路径model_dir不能相同,需要修改,但是这个至少说明pipeline.config
文件是读出来了,所以之前的问题可能是读错文件了吧
解决上面的报错,在efficientdet_d0_coco17_tpu-32
新建一个文件夹train_output
,用做model_dir
修改指令为:python TensorFlow\models\research\object_detection\model_main_tf2.py --pipeline_config_path=efficientdet_d0_coco17_tpu-32\pipeline.config --model_dir=efficientdet_d0_coco17_tpu-32\train_output --alsologtostderr
训练过程中提示
毕竟他妈3w多张图片,光train.tfrecord
文件就1.8个G
于是问ChatGPT咋办,它说是GPU内存不足导致的,先修改批处理大小,在pipeling.config
中找到train_config
的batch_size
配置项,原先默认是128,按照GPT说的改成4先试试
重新运行前,需要先删掉train_output
目录下自动生成的train
文件夹
遇到新问题
问GPT,他不知道,在stackoverflow上找到一个帖子,有人回复说修改pineline.config
里面的一个配置项可以解决
我也修改
删掉train_output
目录下自动生成的train
文件夹,重新运行训练命令
然后继续出错
我裂开了,我心好累,我决定不跟着GPT玩了,继续看文档去吧
第三步 跟着文档训练模型
人工智障GPT,跟着文档Train Custom Object Detector
来玩吧,老老实实撸英文
文档里说,这并不像看上去那样简单
我们需要做这么6个步骤
1. 准备工作空间
首先,在第一步的TensorFlow
里新建一个文件夹,名为addons
,这个后面要放labelImg
,然后新建一个workspace
文件夹,在workspace
目录下面新建一个training_demo
文件夹,如图:
文档里说这个training_demo
是一个训练文件夹,每个不同的训练任务(应当是指在不同数据集上)都应当有独立的目录,每个训练目录下面基本包含这么些个文件:
文档里给每个目录有较为详细的说明,我们也跟着创建
2. 准备数据集
2.1. 注释数据集
2.1.1. 安装LabelImg
直接在conda上用pip安装即可pip isntall labelImg
,这是一个做图像标注的工具
直接在conda中敲命令labelImg
查看安装效果
哇哦居然会打开一个小窗口
在TensorFlow
的addons
目录下新建文件夹labelImg
然后文档让我们把图片放到images
目录里,运行lableImg对图片进行标注,嗷!原来你是在教我标注啊,对不起我的数据是标注好的,我得看看怎么导入,我选择直接去问GPT
GPT说lableImg不支持导入txt文件,但是可以写一个脚本把txt文件转换成xml,以下是脚本
import os
import glob
from xml.etree.ElementTree import Element, SubElement, tostring
from xml.dom.minidom import parseString
def create_voc_xml(image_path, txt_path, output_path, img_width=1280, img_height=720):
image_name = os.path.basename(image_path)
txt_name = os.path.basename(txt_path).split('.')[0]
root = Element('annotation')
folder = SubElement(root, 'folder')
folder.text = 'images'
filename = SubElement(root, 'filename')
filename.text = image_name
path = SubElement(root, 'path')
path.text = image_path
source = SubElement(root, 'source')
database = SubElement(source, 'database')
database.text = 'Unknown'
size = SubElement(root, 'size')
width = SubElement(size, 'width')
width.text = str(img_width)
height = SubElement(size, 'height')
height.text = str(img_height)
depth = SubElement(size, 'depth')
depth.text = '3'
segmented = SubElement(root, 'segmented')
segmented.text = '0'
with open(txt_path, 'r') as f:
for line in f:
values = line.strip().split()
class_id, xmin, ymin, xmax, ymax = map(int, values)
obj = SubElement(root, 'object')
name = SubElement(obj, 'name')
name.text = str(class_id) # Assuming class_id is used as class name
pose = SubElement(obj, 'pose')
pose.text = 'Unspecified'
truncated = SubElement(obj, 'truncated')
truncated.text = '0'
difficult = SubElement(obj, 'difficult')
difficult.text = '0'
bndbox = SubElement(obj, 'bndbox')
x1 = SubElement(bndbox, 'xmin')
x1.text = str(xmin)
y1 = SubElement(bndbox, 'ymin')
y1.text = str(ymin)
x2 = SubElement(bndbox, 'xmax')
x2.text = str(xmax)
y2 = SubElement(bndbox, 'ymax')
y2.text = str(ymax)
dom = parseString(tostring(root))
with open(os.path.join(output_path, txt_name + '.xml'), 'w') as f:
f.write(dom.toprettyxml(indent='\t'))
def convert_all(txt_dir, img_dir, output_dir):
if not os.path.exists(output_dir):
os.makedirs(output_dir)
txt_files = glob.glob(os.path.join(txt_dir, '*.txt'))
for txt_file in txt_files:
img_file = os.path.join(img_dir, os.path.basename(txt_file).replace('.txt', '.jpg'))
if os.path.exists(img_file):
create_voc_xml(img_file, txt_file, output_dir)
if __name__ == "__main__":
txt_directory = 'dataset/trainA/train_dataset/labels' # 原txt格式标签路径
img_directory = 'dataset/trainA/train_dataset/images' # 训练图片文件夹路径
output_directory = 'TFODAPI/TensorFlow/workspace/training_demo/images/train/' # xml标签文件输出路径
convert_all(txt_directory, img_directory, output_directory)
只需要去修改txt_directory
、img_directory
、output_directory
这三个路径即可,路径是相对于这个py脚本的
脚本可以用,打开一个生成的xml文件,发现并不难看懂
由于这个Object Detection模型还能做动作识别,所以会有一些其它的标签,比如pose,应该是指动作,但是咱不管,咱们的标签数据没那么多信息,一个xml文件里会有很多个object标签,对应着原txt标签的一行数据
不过看文档的意思,他的图片文件和xml标签文件都是在同一个目录下的,所以我需要把数据集都放到training_demo/images/train
目录下
复制完了,目录里面的文件长这样
在labelImg窗口中点击Open Dir
,选择training_demo/images/train
目录,就可以查看到标注好的数据了
可以点击Next Image
查看下一张图,鼠标移动到图片上,可以看到被标注的物体有高亮显示,右侧会显示物体类别信息,还是很容易理解的
这个脚本GPT写的不错
2.2. 数据集分区
这里实际上就是划分训练集和测试集,防止过拟合,也可以在测试集上查看模型的效果,一般来说训练集和测试集的比例是9:1,由于我们的训练数据目前都在training_demo/images/train
目录下,因此从里面随机挑选10%的数据放到training_demo/images/test
目录即可,这里我们也可以写一个脚本来实现,还是让GPT来写脚本,这点小事他还是能干的
我在training_demo
文件夹里新建了一个脚本
import os
import random
import shutil
def move_files_for_test(train_dir, test_dir, test_ratio=0.1):
if not os.path.exists(test_dir):
os.makedirs(test_dir)
# 获取所有jpg文件和对应的xml文件
images = [f for f in os.listdir(train_dir) if f.endswith('.jpg')]
xmls = [f for f in os.listdir(train_dir) if f.endswith('.xml')]
# 确保每张图片都有对应的xml文件
paired_files = [(img, img.replace('.jpg', '.xml')) for img in images if img.replace('.jpg', '.xml') in xmls]
# 打乱顺序并选择10%的文件
random.shuffle(paired_files)
num_test = int(len(paired_files) * test_ratio)
test_files = paired_files[:num_test]
# 移动文件到test目录
for img, xml in test_files:
shutil.move(os.path.join(train_dir, img), os.path.join(test_dir, img))
shutil.move(os.path.join(train_dir, xml), os.path.join(test_dir, xml))
print(f'Moved {num_test} images and their XML files to {test_dir}')
if __name__ == "__main__":
train_directory = 'images/train' # 替换为你的train文件夹路径
test_directory = 'images/test' # 替换为你的test文件夹路径
move_files_for_test(train_directory, test_directory)
可以运行
这样我们就有了训练集和测试集
2.3. 创建标签映射
这里就是写一个.pbtxt
文件,里面写着物体的类别,这个在第二步里实际上做过,这里的坑是id需要从1开始而不是从0开始,但是我的标签数据里的物体编号是从0开始,不晓得这里会不会遇到问题
在training_demo/annotations
目录下新建label_map.pbtxt
文件,写入:
item {
id: 1
name: 'bus'
}
item {
id: 2
name: 'traffic_light'
}
item {
id: 3
name: 'traffic_sign'
}
item {
id: 4
name: 'person'
}
item {
id: 5
name: 'bike'
}
item {
id: 6
name: 'truck'
}
item {
id: 7
name: 'motor'
}
item {
id: 8
name: 'car'
}
item {
id: 9
name: 'rider'
}
2.4. 创建tfrecord数据集
这个tfrecord实际上就是这个Object Detection模型接受的输入数据格式,上面的labelImg标注的xml格式还是太大,这个tfrecord格式是个二进制文件,在第二步里也有涉及,当时使用GPT写的脚本把txt标签转换成了tfrecord格式,所以我觉得直接拿过来用就可以了,因为文档是写了个脚本把xml标签文件转换成了额tfrecord,这不就是中间商赚差价吗
但是因为我不清楚第二步里到底哪里有问题,所以决定还是跟着文档走,在TensorFlow
文件夹中新建scripts/preprocessing
文件夹,再新建一个generate_tfrecord.py
文件,写入文档中的脚本,文档中也提供了脚本的下载,行数有点多我就不贴在这里了,脚本路径如下:
在conda中进入TensorFlow/scripts/preprocessing
文件夹,执行脚本的格式为python generate_tfrecord.py -x [A]\train -l [B]\label_map.pbtxt -o [B]\train.record
其中[A]是图片路径,也就是images
那个文件夹,[B]是annotations
那个文件夹
首先创建训练集的tfrecord
python generate_tfrecord.py -x ..\..\workspace\training_demo\images\train -l ..\..\workspace\training_demo\annotations\label_map.pbtxt -o ..\..\workspace\training_demo\annotations\train.record
执行居然报错了
看了一下文档中的脚本,大致了解了问题所在
label_map.pbtxt的本意,是id是类别编号,而name是标签名,也就说我原本的txt标签数据中,第一个数字值的就是物体类别编号,而文档的意思是第一个应当不是一个数字,而是物体的类别,例如bike 120 230 560 460
,而我的是8 120 230 560 460
,所以,这样的话我的label_map.pbtxt中的类别英文就是凭空出现的,所以找不到Key
修改label_map.pbtxt内容为
item {
id: 1
name: '0'
}
item {
id: 2
name: '1'
}
item {
id: 3
name: '2'
}
item {
id: 4
name: '3'
}
item {
id: 5
name: '4'
}
item {
id: 6
name: '5'
}
item {
id: 7
name: '6'
}
item {
id: 8
name: '7'
}
item {
id: 9
name: '8'
}
这里id是label_map的编号,从1开始,name是我标签数据中的标签名,从0开始,按字符串写,如果想知道每个标签编号对应的物体类别,参照下面这个字典吧
LABEL_ID_OBJECT_MAPPING = {
"0": "bus",
"1": "traffic_light",
"2": "traffic_sign",
"3": "person",
"4": "bike",
"5": "truck",
"6": "motor",
"7": "car",
"8": "rider"
}
再次运行生成tfrecord的脚本python generate_tfrecord.py -x ..\..\workspace\training_demo\images\train -l ..\..\workspace\training_demo\annotations\label_map.pbtxt -o ..\..\workspace\training_demo\annotations\train.record
,就可以跑起来了,如果心慌的话可以适当在脚本中层架一些打印,告诉自己程序再跑,不然30000多文件真的跑好慢
生成完训练集的tfrecord文件后,再生成测试集的,执行python generate_tfrecord.py -x ..\..\workspace\training_demo\images\test -l ..\..\workspace\training_demo\annotations\label_map.pbtxt -o ..\..\workspace\training_demo\annotations\test.record
成功生成了train.record
和test.record
两个文件,目录架构如图:
3. 训练准备工作
3.1. 下载预训练模型
从这里开始与文档稍有分歧,文档提到不会从头训练模型,而是采用预训练模型,文档采用的是SSD ResNet50 V1 FPN 640x640
模型,而我准备继续用第二步中失败了的efficientdet_d0_coco17_tpu-32
模型
首先从tensorflow的git上下载预训练模型,放到pre-trained-models
目录中,那我就从链接中下载efficientdet_d0_coco17_tpu-32
模型的tar包,用7-zip解压,复制到对应目录中,如图:
文档中说如果想搞多个预训练模型,都可以放在pre-trained-models
目录中,一个模型一个文件夹即可
3.2. 修改pipeline.config
首先,在workspace
目录中的models
文件夹下面,新建一个和预训练模型同名的文件夹,并把pipeline.config
复制过去,这个models
目录存放的是我们自己训练的模型,如图
然后按照文档修改pipeline.config
文件
跟着文档改完,明白了这里的相对路径都是相对于training_demo
文件夹的
4. 训练模型
好激动,好激动
首先把TensorFlow/models/research/object_detection/model_main_tf2.py
这个文件复制到training_demo
文件夹
在training_demo
路径中执行命令python model_main_tf2.py --model_dir=models\efficientdet_d0_coco17_tpu-32 --pipeline_config_path=models\efficientdet_d0_coco17_tpu-32\pipeline.config
,这里就是指定了模型路径和pipeline配置文件路径
好了,又报错了,这次是跟第二步中最后让我崩溃的报错一样
环境变量是有的,所以找不到文件的报错纯是在跟我扯淡
GPT说用CPU训练试试,于是在model_main_tf2.py的引用后面加入两行代码
再运行python model_main_tf2.py --model_dir=models\efficientdet_d0_coco17_tpu-32 --pipeline_config_path=models\efficientdet_d0_coco17_tpu-32\pipeline.config
,嘿,跑起来了
只不过拿CPU跑的,GPU闲置严重,就这样训练了一晚上7个小时了还没有训练完
GPU的问题之后再解决吧,今天六一,陪娃陪一天,就让我的破i5继续超负荷吧
【六一晚上更新】
这样下去发现不是办法,用cpu训练从上面的信息里可以看到每步的耗时为2.7秒左右,然后我查看pipeline.config
里面,总步数有30万步
那我得训练300000*2.7/60/60/24=9.375天,完蛋去吧!!这个问题必须解决
首先是问GPT,GPT说要么禁用JIT编译,要么修改GPU缓存策略,然而这两个解决办法都没卵用,我就步记录过程了
然后问谷歌,幸运的是在github上有人遇到了这个问题,这人遇到的问题跟我一模一样,环境变量都配置了,就是不行,回答问题的人也不知道问题原理是什么,但是在训练前执行export XLA_FLAGS=--xla_gpu_cuda_data_dir=<PATH_TO_CUDA>
就好了,这里的<PATH_TO_CUDA>
是CUDA的安装路径,实际上就是CUDA_DIR或者CUDA_PATH的路径,这个应当是安装完CUDA就自动配好的,如果是windows系统,把export
命令改为set
命令,需要注意的是这个指令并不会永久保存,所以每次重开conda窗口都需要重新输入,否则就是把这个配置写在环境变量中
不要忘记注释掉之前禁用GPU的那两行代码
执行了这个命令之后,再次运行训练指令python model_main_tf2.py --model_dir=models\efficientdet_d0_coco17_tpu-32 --pipeline_config_path=models\efficientdet_d0_coco17_tpu-32\pipeline.config
他妈的成功了成功了成功了!!!!!
我的3080动起来了!!
每步的耗时骤降至0.3秒左右,这样总步数30万的话,我需要训练300000*0.3/60/60/24≈1.04天
虽然还是很长的,不过总比10天强
那就先训练吧,等训练完了再看看模型效果好了!
【六月三日早上更新】
训练了一整天,终于训练完成了!
models/efficientdet_d0_coco17_tpu-32
目录里多了不少文件
写到这里突然意识到,第二步里我完全跟着ChatGPT走,最终遇到的问题就是找不到GPU的问题,当时没有搜索到github上的issue,这么看来GPT还是很牛的,虽然第二步不了了之,但我还是保留,以记录我踩的坑
5. 评估模型
训练过程中会有一些指标被随时记录(默认每300秒一次),这些ckpt-*
开头的文件都是
执行评估脚本来查看效果,在training_demo
目录中执行python model_main_tf2. py --model_dir=models\efficientdet_d0_coco17_tpu-32 --pipeline_config_path=models\efficientdet_d0_coco17_tpu-32\pipeline.config --checkpoint_dir=models\efficientdet_d0_coco17_tpu-32
这个命令跟训练命令很像,但是这次models
文件夹中有训练的过程文件了,因此执行起来就是在测试集上验证了
这个过程也是吃GPU的,
验证的速度还算快,几分钟,验证完成后会在模型文件夹中自动创建一个eval
文件夹
然后用TensorBoard来查看训练过程
进入conda中的环境,在training_demo
目录中执行tensorboard --logdir=models\efficientdet_d0_coco17_tpu-32
,会显示一个地址
在浏览器输入这个地址就可以查看一个可视化的界面
导航到scalars
标签页,可以查看模型在测试集上的各项评估指标
这个界面可能会因tensorflow版本不同而不同,至于怎么看,请查阅其它资料
6. 导出模型
首先把TensorFlow/models/research/object_detection/exporter_main_v2.py
中的脚本复制到training_demo
文件夹中
然后在training_demo
目录中执行python exporter_main_v2.py --input_type image_tensor --pipeline_config_path models\efficientdet_d0_coco17_tpu-32\pipeline.config --trained_checkpoint_dir models\efficientdet_d0_coco17_tpu-32 --output_directory exported-models\my_model
这个指令实际上就是设置了这么几个参数
- input_type,输入类型
- pipeline_config_path,pipeline.config文件路径
- trained_checkpoint_dir,训练模型检查点路径
- output_directory,输出路径,这里最后的my_model是可以自己任意命名的
输出完成后目录结构如下:
7. 调用模型
最后就是调用模型去对其它图片做识别了,我这里直接问GPT,让它给写了个脚本
import tensorflow as tf
import numpy as np
import cv2
import matplotlib.pyplot as plt
from object_detection.utils import visualization_utils as viz_utils
from object_detection.utils import label_map_util
# 配置路径
saved_model_dir = 'exported-models/my_model/saved_model'
label_map_path = 'annotations/label_map.pbtxt'
image_test_id = 1504
image_path = f'images/test/train_A_{image_test_id}.jpg'
# 加载导出的模型
detect_fn = tf.saved_model.load(saved_model_dir)
# 加载标签映射
category_index = label_map_util.create_category_index_from_labelmap(label_map_path, use_display_name=True)
# 读取并处理输入图片
image_np = cv2.imread(image_path)
image_np = cv2.cvtColor(image_np, cv2.COLOR_BGR2RGB)
image_np = cv2.resize(image_np, (1280, 720)) # 确保图片大小为1280x720
image_np_expanded = np.expand_dims(image_np, axis=0)
# 将图片转换为TensorFlow张量
input_tensor = tf.convert_to_tensor(image_np_expanded, dtype=tf.uint8)
# 进行物体检测
detections = detect_fn(input_tensor)
# 提取检测结果
num_detections = int(detections.pop('num_detections'))
detections = {key: value[0, :num_detections].numpy() for key, value in detections.items()}
detections['num_detections'] = num_detections
# 检测到的类别ID和分数
detections['detection_classes'] = detections['detection_classes'].astype(np.int64)
# 可视化结果
label_id_offset = 1
image_np_with_detections = image_np.copy()
viz_utils.visualize_boxes_and_labels_on_image_array(
image_np_with_detections,
detections['detection_boxes'],
detections['detection_classes'] + label_id_offset,
detections['detection_scores'],
category_index,
use_normalized_coordinates=True,
max_boxes_to_draw=200,
min_score_thresh=.30,
agnostic_mode=False)
# 保存检测结果图像
output_image = cv2.cvtColor(image_np_with_detections, cv2.COLOR_RGB2BGR)
output_image_path = f"my_test/train_A_{image_test_id}_mytest.jpg"
cv2.imwrite(output_image_path, output_image)
# 显示图片
# plt.figure(figsize=(12, 8))
# plt.imshow(image_np_with_detections)
# plt.show()
首先是在training_demo
里建了一个名为my_test
的文件夹,用来存放预测后的结果,只需修改image_test_id
这个参数为图片编号即可运行,我先是拿着一个测试集的数据查看效果
上面的图片是模型预测的结果,下面的图片是测试集和标注信息叠加的图片,可以看出来这个准确率还是可以的,至少该有的东西就都有了
剩下的工作就是写脚本,去按照比赛要求的格式输出物体了
实际上我这里只训练了白天的数据,还需要再去训练夜晚的数据,参照上面的步骤执行就可以了,至于如何融合两个模型,我等问问GPT,再写一篇博客吧
很欣慰,搞了一个周末终于是训练出来了,上班去了呜呜呜