一、项目文件与模型权重下载
1、下载地址
https://gitcode.com/WongKinYiu/yolov9/overview
2、下载项目文件与模型权重
(1)项目文件下载:点击左侧代码,再点击右侧下载zip即可
(2)模型权重下载:同样在该网址里面,先点击首页,往下拉找到Performance,点击模型权重名称进行下载
如果Performance这里点击下载没有反应,也可以找到Evaluation这里进行下载,如下所示:
(3)下载结果如下所示:
二、创建虚拟环境,安装所需要的包/库
1.打开cmd或者Anaconda Prompt(install)创建虚拟环境
方法一:默认情况下虚拟环境创建在Anaconda安装目录下的envs文件夹中
conda create --name Yolov9 #Yolov9是虚拟环境名称(自定义)
方法二:如果想将虚拟环境创建在指定位置,使用–prefix参数即可:
conda create --prefix /home/Zeroad/Envs/Yolov9 python==3.9 #/home/Zeroad/Envs地址(自定义)
查看并激活上面创建的虚拟环境:
#查看虚拟环境
conda env list
#激活虚拟环境
source activate /home/Zeroad/Envs/Yolov9
2、查看当前电脑的cuda版本,再根据版本去安装合适的torch
nvidia-smi
打开网址:https://pytorch.org/get-started/previous-versions/,查找本机对应的torch,并通过命令在线安装,如下图所示;
pip install torch==2.1.0 torchvision==0.16.0 torchaudio==2.1.0 --index-url https://download.pytorch.org/whl/cu118
3、进入项目文件,找到requirements.txt文件,安装所需要包/库
(1)进入yolov9项目文件,找到requirements.txt文件并查看所需要安装的包/库
(2)使用清华镜像源等地址进行快速安装:
清华大学 :https://pypi.tuna.tsinghua.edu.cn/simple/
阿里云:http://mirrors.aliyun.com/pypi/simple/
中国科学技术大学 :http://pypi.mirrors.ustc.edu.cn/simple/
华中科技大学:http://pypi.hustunique.com/
豆瓣源:http://pypi.douban.com/simple/
腾讯源:http://mirrors.cloud.tencent.com/pypi/simple
华为镜像源:https://repo.huaweicloud.com/repository/pypi/simple/
比如:pip install -i https://pypi.tuna.tsinghua.edu.cn/simple/ gitpython
(3)激活虚拟环境并查看下面已安装的库:
(4)如果想要删除yolov5这个虚拟环境:
conda remove --prefix /home/Zeroad/Envs/Yolov9 --all
到这里项目运行环境的设置就完成了!
三、车牌检测数据集的制作(目标检测)
1、数据集下载
url:https://www.cvmart.net/
大家打开网址,进行注册登录,再点击里面的数据集就可以去筛选自己需要的数据进行下载。
2、简介
下载文件解压后,里面有两个文件夹,分别annotations与images;
其中:annotations文件夹存放是标注数据的xml文件433个
其中:images文件夹存放的是原始图片433张
3、由于yolo系列模型不能使用xml形式的数据集进行训练,因此将xml数据转为txt形式
(1)首先对xml文件中的内容进行解读:关键参数在下面标注了
<annotation>
<folder>images</folder>
<filename>Cars399.png</filename> #图片名称
<size> #图片的大小
<width>400</width>
<height>225</height>
<depth>3</depth>
</size>
<segmented>0</segmented>
<object>
<name>licence</name> #标注目标的分类(标签)
<pose>Unspecified</pose>
<truncated>0</truncated>
<occluded>0</occluded>
<difficult>0</difficult>
<bndbox> #标注目标的目标框
<xmin>106</xmin>
<ymin>62</ymin>
<xmax>309</xmax>
<ymax>161</ymax>
</bndbox>
</object>
</annotation>
(2)把 xml 中的目标框在原图上绘制出来 ,并显示标签【注:有需要就做这一步,没需要就可以不管,这步对训练没有什么影响】
import xml.etree.ElementTree as ET # 读取xml。
import os
from PIL import Image,ImageDraw,ImageFont
def parse_rec(filename):
tree = ET.parse(filename) # 解析读取xml函数
objects = []
img_dir =[]
for xml_name in tree.findall('filename'):
img_path = os.path.join(pic_path, xml_name.text)
img_dir.append(img_path)
for obj in tree.findall('object'):
obj_struct = {}
obj_struct['name'] = obj.find('name').text
obj_struct['pose'] = obj.find('pose').text
obj_struct['truncated'] = int(obj.find('truncated').text)
obj_struct['difficult'] = int(obj.find('difficult').text)
bbox = obj.find('bndbox')
obj_struct['bbox'] = [int(bbox.find('xmin').text),
int(bbox.find('ymin').text),
int(bbox.find('xmax').text),
int(bbox.find('ymax').text)]
objects.append(obj_struct)
return objects,img_dir
# 可视化
def visualise_gt(objects,img_dir):
for id,img_path in enumerate(img_dir):
img = Image.open(img_path)
draw = ImageDraw.Draw(img)
for a in objects:
xmin =int(a['bbox'][0])
ymin =int(a['bbox'][1])
xmax =int(a['bbox'][2])
ymax =int(a['bbox'][3])
label = a['name']
draw.rectangle((xmin,ymin,xmax,ymax), fill=None, outline=(0,255,0),width=2)
draw.text((xmin-10,ymin-15), label, fill = (0,255,0),font=font) # 利用ImageDraw的内置函数,在图片上写入文字
img.show()
fontPath = "C:\Windows\Fonts\Consolas\consola.ttf" # 字体路径
root = 'F:/dataset/AQM'
ann_path = os.path.join(root, 'Annotations') # xml文件所在路径
pic_path = os.path.join(root, 'JPEGImages') # 样本图片路径
font = ImageFont.truetype(fontPath, 16)
for filename in os.listdir(ann_path):
xml_path = os.path.join(ann_path,filename)
object,img_dir = parse_rec(xml_path)
visualise_gt(object,img_dir )
(3)将xml批量转化为txt文件
import os
import random
import xml.etree.ElementTree as ET
import numpy as np
def convert_annotation(xml_name, class_list, labels_folder):
xml_path = os.path.join(XML_FOLDER, xml_name)
print("xml_path:", xml_path)
in_file = open(xml_path, encoding='utf-8')
tree = ET.parse(in_file)
root = tree.getroot()
for SIZE in root.iter("size"):
width = float(SIZE.find("width").text)
height = float(SIZE.find("height").text)
label_name = xml_name.split(".")[0]+".txt"
label_path = os.path.join(labels_folder, label_name)
if os.path.exists(label_path):
os.remove(label_path)
with open(label_path, "a", encoding="utf-8") as f:
for obj in root.iter('object'):
difficult = 0
if obj.find('difficult') != None:
difficult = obj.find('difficult').text
cls = obj.find('name').text
if cls not in class_list or int(difficult) == 1:
continue
cls_id = classes_list.index(cls)
xmlbox = obj.find('bndbox')
# x1, y1, x2, y2 = (float(xmlbox.find('xmin').text)/width, float(xmlbox.find('ymin').text)/height,
# float(xmlbox.find('xmax').text)/width, float(xmlbox.find('ymax').text)/height)
x1, y1, x2, y2 = (float(xmlbox.find('xmin').text), float(xmlbox.find('ymin').text),
float(xmlbox.find('xmax').text), float(xmlbox.find('ymax').text))
print("cls:", cls_id)
print("box:", (x1+x2)/2/width, (y1+y2)/2/height, (x2-x1)/width, (y2-y1)/height)
x_c, y_c, w, h = (x1+x2)/2/width, (y1+y2)/2/height, (x2-x1)/width, (y2-y1)/height
# f.write("%s %s %s %s %s\n" % (cls_id, x1, y1, x2, y2))
f.write("%s %s %s %s %s\n"%(cls_id, x_c, y_c, w, h))
if __name__ == "__main__":
classes_list = ["licence"] # 对应类别名称
XML_FOLDER = r"/home/xh/YoLo/del_data/CarLicensePlateDetection/annotations" # xml路径
Labels_FOLDER = r"/home/xh/YoLo/del_data/CarLicensePlateDetection/labels" # 保存路径
random.seed(0)
if " " in os.path.abspath(XML_FOLDER):
raise ValueError("数据集存放的文件夹路径与图片名称中不可以存在空格,否则会影响正常的模型训练,请注意修改。")
# 1.读取xml文件
for xml_name in os.listdir(XML_FOLDER):
if xml_name.endswith(".xml"):
convert_annotation(xml_name, classes_list, Labels_FOLDER)
(4)转化结果查看
分别是【类别、x1、y1、x2、y2】
四、改写代码,进行车牌目标检测训练和验证实战
1、划分数据集:【划分比例为 train :val:test = 8 :1:1】
在项目中新建DataSplit.py文件并放入以下代码,根据自己的需求改写路径和比例(如下图所示)
# 将图片和标注数据按比例切分为 训练集和测试集
import shutil
import random
import os
# 原始路径
image_original_path = "/home/xh/YoLo/del_data/CarLicensePlateDetection/images/"
label_original_path = "/home/xh/YoLo/del_data/CarLicensePlateDetection/labels/"
cur_path = os.getcwd()
# 训练集路径
train_image_path = os.path.join(cur_path, "/home/xh/YoLo/del_data/CarLicensePlateDetection/datasets/images/train/")
train_label_path = os.path.join(cur_path, "/home/xh/YoLo/del_data/CarLicensePlateDetection/datasets/labels/train/")
# 验证集路径
val_image_path = os.path.join(cur_path, "/home/xh/YoLo/del_data/CarLicensePlateDetection/datasets/images/val/")
val_label_path = os.path.join(cur_path, "/home/xh/YoLo/del_data/CarLicensePlateDetection/datasets/labels/val/")
# 测试集路径
test_image_path = os.path.join(cur_path, "/home/xh/YoLo/del_data/CarLicensePlateDetection/datasets/images/test/")
test_label_path = os.path.join(cur_path, "/home/xh/YoLo/del_data/CarLicensePlateDetection/datasets/labels/test/")
# 训练集目录
list_train = os.path.join(cur_path, "/home/xh/YoLo/del_data/CarLicensePlateDetection/datasets/train.txt")
list_val = os.path.join(cur_path, "/home/xh/YoLo/del_data/CarLicensePlateDetection/datasets/val.txt")
list_test = os.path.join(cur_path, "/home/xh/YoLo/del_data/CarLicensePlateDetection/datasets/test.txt")
train_percent = 0.8 # 划分比例
val_percent = 0.1
test_percent = 0.1
def del_file(path):
for i in os.listdir(path):
file_data = path + "\\" + i
os.remove(file_data)
def mkdir():
if not os.path.exists(train_image_path):
os.makedirs(train_image_path)
else:
del_file(train_image_path)
if not os.path.exists(train_label_path):
os.makedirs(train_label_path)
else:
del_file(train_label_path)
if not os.path.exists(val_image_path):
os.makedirs(val_image_path)
else:
del_file(val_image_path)
if not os.path.exists(val_label_path):
os.makedirs(val_label_path)
else:
del_file(val_label_path)
if not os.path.exists(test_image_path):
os.makedirs(test_image_path)
else:
del_file(test_image_path)
if not os.path.exists(test_label_path):
os.makedirs(test_label_path)
else:
del_file(test_label_path)
def clearfile():
if os.path.exists(list_train):
os.remove(list_train)
if os.path.exists(list_val):
os.remove(list_val)
if os.path.exists(list_test):
os.remove(list_test)
def main():
mkdir()
clearfile()
file_train = open(list_train, 'w')
file_val = open(list_val, 'w')
file_test = open(list_test, 'w')
total_txt = os.listdir(label_original_path)
num_txt = len(total_txt)
list_all_txt = range(num_txt)
num_train = int(num_txt * train_percent)
num_val = int(num_txt * val_percent)
num_test = num_txt - num_train - num_val
train = random.sample(list_all_txt, num_train)
# train从list_all_txt取出num_train个元素
# 所以list_all_txt列表只剩下了这些元素
val_test = [i for i in list_all_txt if not i in train]
# 再从val_test取出num_val个元素,val_test剩下的元素就是test
val = random.sample(val_test, num_val)
print("训练集数目:{}, 验证集数目:{}, 测试集数目:{}".format(len(train), len(val), len(val_test) - len(val)))
for i in list_all_txt:
name = total_txt[i][:-4]
srcImage = image_original_path + name + '.png'
srcLabel = label_original_path + name + ".txt"
if i in train:
dst_train_Image = train_image_path + name + '.png'
dst_train_Label = train_label_path + name + '.txt'
shutil.copyfile(srcImage, dst_train_Image)
shutil.copyfile(srcLabel, dst_train_Label)
file_train.write(dst_train_Image + '\n')
elif i in val:
dst_val_Image = val_image_path + name + '.png'
dst_val_Label = val_label_path + name + '.txt'
shutil.copyfile(srcImage, dst_val_Image)
shutil.copyfile(srcLabel, dst_val_Label)
file_val.write(dst_val_Image + '\n')
else:
dst_test_Image = test_image_path + name + '.png'
dst_test_Label = test_label_path + name + '.txt'
shutil.copyfile(srcImage, dst_test_Image)
shutil.copyfile(srcLabel, dst_test_Label)
file_test.write(dst_test_Image + '\n')
file_train.close()
file_val.close()
file_test.close()
if __name__ == "__main__":
main()
结果:
2、制作数据集的yaml文件:
(1)找到项目data文件夹中的coco.yaml文件,复制并重命名为cardata仍放在data文件夹中:
(2)对cardata.yaml文件进行修改:这里根据自己实际情况进行修改
3、找到yolov9-main\models\detect下的yolov9-c.yaml,进行修改
nc表示标签的数量,按照自己实际标签数进行修改!
4、修改项目文件中tran_dual.py文件
注意:项目文件夹中有train.py、tran_dual.py、train_triple.py 三个训练文件,都能用,但是你得对应起来!
train.py:常规的训练,没有辅助分支的
train_dual:一个辅助分支 + 一个主分支
triple_branch:两个辅助分支 + 一个主分支
所以当我们训练 YOLOv9-c 或者 YOLOv9-e 就用 train_dual.py:
必须修改:以下三个参数【–weights、–cfg、–data:分别是模型权重路径、yolov9-c.yaml文件路径、cardata.yaml文件路径】
其它参数解释如下:其它参数根据自己的设备和需求进行调整
def parse_opt(known=False):
parser = argparse.ArgumentParser()
# parser.add_argument('--weights', type=str, default=ROOT / 'yolo.pt', help='initial weights path')
# parser.add_argument('--cfg', type=str, default='', help='model.yaml path')
parser.add_argument('--weights', type=str, default='', help='权重初始化的路径,在这里面设置权重的地址')
parser.add_argument('--cfg', type=str, default='models/detect/gelan.yaml', help='模型的配置文件,我们可以根据自己需求配置yaml文件')
parser.add_argument('--data', type=str, default=ROOT / r'', help='大家的数据集yaml文件的地址')
parser.add_argument('--hyp', type=str, default=ROOT / 'data/hyps/hyp.scratch-high.yaml', help='用于指定超参数文件,大家根据这个地址就能够找到一个yaml文件里面配置了各种超参数')
parser.add_argument('--epochs', type=int, default=150, help='训练的轮次')
parser.add_argument('--batch-size', type=int, default=4, help='训练的批次大小,决定了一次向模型里输入多少张图片')
parser.add_argument('--imgsz', '--img', '--img-size', type=int, default=640, help='输入模型里的图像尺寸,YOLO系列默认都是640')
parser.add_argument('--rect', action='store_true', help='矩形训练或验证。矩形训练或验证是一种数据处理技术,其中在训练或验证过程中,输入数据会被调整为具有相同宽高比的矩形形状')
parser.add_argument('--resume', nargs='?', const=True, default=False, help='这个如果设置为True,那么会根据你最新一次没有训练完成的权重进行继续完成训练')
parser.add_argument('--nosave', action='store_true', help='只保存之后一次训练的权重文件地址,即我们得到的那个权重,默认是这样!')
parser.add_argument('--noval', action='store_true', help='当设置时,只在最后一个周期(epoch)进行验证,而不是在每个周期都进行')
parser.add_argument('--noautoanchor', action='store_true', help='当设置时,禁用自动锚定(AutoAnchor)')
parser.add_argument('--noplots', action='store_true', help='当设置时,不保存任何绘图文件,就是我们runs下面的训练结果')
parser.add_argument('--evolve', type=int, nargs='?', const=300, help='允许超参数的进化优化,参数是进化的代数。')
parser.add_argument('--bucket', type=str, default='', help='指定一个gsutil存储桶的路径')
parser.add_argument('--cache', type=str, nargs='?', const='ram', help='设置图像缓存方式,可以是内存(ram)或磁盘(disk),默认是内存')
parser.add_argument('--image-weights', action='store_true', help='使用加权图像选择进行训练,简单的说就是,处理一些复杂或不平衡的数据集')
parser.add_argument('--device', default='', help='cuda device, i.e. 0 or 0,1,2,3 or cpu, 这个不在多说了,我们的GPU设备号')
parser.add_argument('--multi-scale', action='store_true', help='当设置时,图像大小会在训练过程中变化,上下浮动50%')
parser.add_argument('--single-cls', action='store_true', help='当设置时,将多类别数据作为单类别数据训练,一般都设置为False')
parser.add_argument('--optimizer', type=str, choices=['SGD', 'Adam', 'AdamW', 'LION'], default='SGD', help='优化器的选择,默认为SGD')
parser.add_argument('--sync-bn', action='store_true', help='当设置时,使用同步批处理归一化(SyncBatchNorm)')
parser.add_argument('--workers', type=int, default=0, help='工作的线程,Linux系统可以设置,Windows系统必须设置0否则会导致各种问题')
parser.add_argument('--project', default=ROOT / 'runs/train', help='设置项目保存路径,就是我们训练结果保存的地方')
parser.add_argument('--name', default='exp', help='保存的项目名称,类似于runs/train/exp/一堆我们训练的结果')
parser.add_argument('--exist-ok', action='store_true', help='当设置时,如果项目/名称已存在则不会报错,不会自动增加编号')
parser.add_argument('--quad', action='store_true', help='使用四边形数据加载器, 这些默认都是开启的')
parser.add_argument('--cos-lr', action='store_true', help='使用余弦学习率调度器, 这些默认都是开启的')
parser.add_argument('--flat-cos-lr', action='store_true', help='启用一个平坦余弦学习率调度器(Flat Cosine Learning Rate Scheduler')
parser.add_argument('--fixed-lr', action='store_true', help='启用一个固定学习率调度器(Fixed Learning Rate Scheduler)')
parser.add_argument('--label-smoothing', type=float, default=0.0, help='参数是用来设置标签平滑的 epsilon 值的,用来提高模型的泛化能力')
parser.add_argument('--patience', type=int, default=100, help='早停机制,损失超过多少个回合变化的较小就停止训练')
parser.add_argument('--freeze', nargs='+', type=int, default=[0], help='用于在神经网络训练过程中“冻结”(freeze)一部分层。冻结的意思是在训练过程中保持这些层的权重不变,不对它们进行更新.')
parser.add_argument('--save-period', type=int, default=-1, help='每隔x个周期保存一次检查点,如果小于1则禁用')
parser.add_argument('--seed', type=int, default=0, help='训练的随机数种子,计算机其实是没有随机数的,都是根据随机数种子来演变出来的随机数')
parser.add_argument('--local_rank', type=int, default=-1, help='自动设置用于多GPU DDP训练的局部排名,通常不需要手动修改')
parser.add_argument('--min-items', type=int, default=0, help='目前还不知道这个参数的含义')
parser.add_argument('--close-mosaic', type=int, default=0, help='关闭mosaic数据增强的参数,比如设置0则不关闭mosaic数据增强如果设置10则在训练后10轮关闭mosaic增强')
# Logger arguments
parser.add_argument('--entity', default=None, help='设置实体,用于日志记录')
parser.add_argument('--upload_dataset', nargs='?', const=True, default=False, help='设置是否上传数据集,如果指定“val”,则上传验证集')
parser.add_argument('--bbox_interval', type=int, default=-1, help='设置边界框图像记录间隔')
parser.add_argument('--artifact_alias', type=str, default='latest', help='设置要使用的数据集工件的版本,默认为latest')
return parser.parse_known_args()[0] if known else parser.parse_args()
5、运行tran_dual.py文件,训练模型
(1)运行tran_dual.py文件报的错误1:
我这里就报错了:缺少下面这个库,安装即可。如果遇见类似错误,根据提醒信息进行修改就可以!
(2)运行tran_dual.py文件报的错误2:因为安装了新版本的 Pillow (10)删除了该getsize 功能,降级到 Pillow 9.5 解决该问题
(3)再次运行tran_dual.py文件就正常跑模型了:部分运行结果如下
在目标检测任务中,常用的评估指标包括Precision(P)、Recall(R)、mAP50和mAP50-95。
Precision(精确率)表示检测出的正样本中有多少是真正的正样本。它的计算公式是:P = TP / (TP + FP),其中TP是真正例(正确检测的正样本数),FP是假正例(错误检测的正样本数)。Precision越高,表示检测结果中的正样本越准确。
Recall(召回率)表示真实的正样本中有多少被正确地检测出来。它的计算公式是:R = TP / (TP + FN),其中TP是真正例,FN是假负例(未能检测到的正样本数)。Recall越高,表示模型能够更好地检测出真实的正样本。
mAP50(平均精确率,IoU阈值为0.5)表示在不同类别上的平均精确率。它的计算方法是先计算每个类别的精确率,然后对所有类别的精确率取平均值。mAP50衡量了模型在不同类别上的整体性能,当IoU阈值为0.5时,表示检测框与真实框的重叠程度达到50%以上时才被视为正确检测。
mAP50-95(平均精确率,IoU阈值从0.5到0.95)是在不同IoU阈值范围内的平均精确率。它计算了从0.5到0.95不同IoU阈值下的精确率,并对其取平均值。mAP50-95更全面地评估了模型在不同IoU阈值下的检测性能。
这些评估指标可以帮助我们了解目标检测模型的准确性和召回率,并对模型的性能进行综合评估。
保存的结果存放在 【yolov9-main/runs/train/exp4】文件夹里面,里面有以下主要文件:
里面包含生成的权重文件:weights,精确率与召回率的图像等,如下图所示:
weights:存放模型权重;
在训练深度学习模型时,通常会保存模型的权重文件,以便在需要时进行加载和使用。YOLOv5模型中,best.pt和last.pt是两个常见的权重文件。
best.pt:best.pt是在训练过程中根据验证集表现选择的最佳模型权重文件。它是根据模型在验证集上的性能指标(如精度、损失等)来决定的。通常情况下,best.pt对应于在验证集上获得最佳性能的模型。
last.pt:last.pt是训练过程中最后一次保存的模型权重文件。它保存了模型在训练过程中的最新状态,无论其在验证集上的性能如何。last.pt可以用作继续训练的起点或进行推理和预测。
区别:
best.pt是基于验证集性能选择的最佳模型权重文件,而last.pt是训练过程中最后一次保存的模型权重文件。
best.pt对应于在验证集上获得最佳性能的模型,而last.pt保存了模型在训练过程中的最新状态。
在模型训练完毕后,可以使用best.pt进行性能评估和推理,而last.pt可以用作继续训练的起点。
选择使用哪个权重文件取决于具体的需求。如果需要获得在验证集上最佳性能的模型,可以使用best.pt。如果需要继续训练模型或进行推理和预测,可以使用last.pt。
(1)"labels.jpg"通常是指包含训练数据集中所有类别标签的图像文件。
第一个图是训练集得数据量,每个类别有多少个;
第二个图是框的尺寸和数量;
第三个图是中心点相对于整幅图的位置;
第四个图是图中目标相对于整幅图的高宽比例;
(2)labels_correlogram.jpg是一张颜色矩阵图,它展示了目标检测算法在训练过程中预测标签之间的相关性。
矩阵的行列分别代表了模型训练时使用的标签(classes),而每个单元格则代表了对应标签的预测结果之间的相关性。
矩阵中的颜色越深,表示对应标签之间的相关性越强;颜色越浅,表示相关性越弱。对角线上的颜色表示每个标签的自身相关性,通常都是最深的。
通过这张图,我们可以看出哪些标签之间具有较强的相关性,从而有助于优化模型的训练和预测效果。例如,如果我们发现某些标签之间的相关性过强,可以考虑将它们合并成一个标签,从而简化模型并提高效率。
最上面的图(0,0)表明中心点横坐标x的分布情况;
(1,1)图表明中心点纵坐标y的分布情况;
(2,2)图表明框的宽的分布情况;
(3,3)图表明框的宽的分布情况
(3)P曲线:P_curve.png:准确率precision与置信度confidence的关系图。
【置信度confidence:用来判断边界框内的物体是正样本还是负样本,大于置信度阈值的判定为正样本,小于置信度阈值的判定为负样本即背景。】
(4)R_curve.png(信度阈值 - 召回率曲线图):Recall-Confidence Curve (RCC)图是目标检测中用于评估算法性能的一种方法。它是在不同置信度阈值下,召回率的变化情况的可视化表示。
通常情况下,我们希望算法能够在高召回率的同时保持较高的精度。
当RCC图中的曲线在较高的置信度水平下具有较高的召回率时,说明算法在检测目标时能够较为准确地预测目标的存在,并且在过滤掉低置信度的预测框后,依然可以保持较高的召回率。这说明算法在目标检测任务中具有较好的性能。
需要注意的是,在RCC图中,曲线的斜率越陡峭,说明在过滤掉低置信度的预测框后,能够获得较大的召回率提升,从而提高模型的检测性能。
在该图表中,曲线越靠近右上角,则表示模型的性能越好。当曲线接近图表的右上角时,意味着模型在保持高召回率的同时,也能够保持较高的精确度。因此,R_curve.png可以用于评估模型的整体表现和找到一个合适的阈值,来平衡模型的召回率和精确度。
(5)PR_curve.png —— 精确率和召回率的关系图:PR Curve是Precision-Recall Curve的缩写,表示的是在不同阈值下,精确率与召回率之间的关系曲线。其中精确率(Precision)表示预测为正例的样本中真正为正例的比例,召回率(Recall)表示真正为正例的样本中被预测为正例的比例。
在PR Curve中,横坐标为召回率,纵坐标为精确率。一般而言,当召回率较高时,精确率较低;当精确率较高时,召回率较低。而PR Curve则体现了这种“取舍”关系。当PR Curve越靠近右上角时,表示模型在预测时能够同时保证高的精确率和高的召回率,即预测结果较为准确。相反,当PR Curve越靠近左下角时,表示模型在预测时难以同时保证高的精确率和高的召回率,即预测结果较为不准确。
通常,PR Curve与ROC Curve(受试者工作特征曲线)一同使用,以更全面地评估分类模型的性能。
(6)result.png:
定位损失box_loss:预测框与标定框之间的误差(CIoU),越小定位得越准;
置信度损失obj_loss:计算网络的置信度,越小判定为目标的能力越准;
分类损失cls_loss:计算锚框与对应的标定分类是否正确,越小分类得越准;
mAP@0.5:0.95(mAP@[0.5:0.95]):
表示在不同IoU阈值(从0.5到0.95,步长0.05)(0.5、0.55、0.6、0.65、0.7、0.75、0.8、0.85、0.9、0.95)上的平均mAP;
mAP@0.5:表示阈值大于0.5的平均mAP
6、改写detect.py代码,进行预测,如下图所示:
选择一张图片预测:原图片
发生了报错:
将general.py出错位置进行改正:注释掉原来形式,将替换代码放上去即可
if isinstance(prediction, (list, tuple)):
processed_predictions = [] # 用于存储处理后的张量列表
for pred_tensor in prediction:
# 对每个张量进行处理
processed_tensor = pred_tensor[0] # 假设你只关心张量中的第一个结果
processed_predictions.append(processed_tensor) # 将处理后的张量添加到列表中
# 使用处理后的张量列表中的第一个张量作为预测结果
prediction = processed_predictions[0]
# 在此之后可以继续使用 prediction 变量
device = prediction.device
再次运行detect.py文件,结果如下:结果保存在yolov9-main/runs/detect/exp3里面
再在验证集数据中选择一张图片预测:原图片
预测结果如下:
五、多标签视网膜疾病的分类实战
1、划分数据集:【划分比例为 train :val:test = 8 :1:1】:
(1)下载的数据集分为四类,分别是【normal:cataract:glaucoma:retina_disease=正常:白内障:青光眼:视网膜疾病=300:100:101:100】
(2)数据集划分:
import os
import random
import shutil
def split_dataset(imges_file_list,imges_file,split_save):
for file in imges_file_list:
all_data = os.listdir(imges_file + file) # (图片文件夹)
num_all_data = len(all_data)
print("num_all_data: " + str(num_all_data))
index_list = list(range(num_all_data))
# print(index_list)
random.shuffle(index_list)
num = 0
trainDir = split_save + 'train/' + file # (将训练集放在这个文件夹下)
if not os.path.exists(trainDir):
os.mkdir(trainDir)
validDir = split_save + 'val/' + file # (将验证集放在这个文件夹下)
if not os.path.exists(validDir):
os.mkdir(validDir)
testDir = split_save + 'test/' + file # (将测试集放在这个文件夹下)
if not os.path.exists(testDir):
os.mkdir(testDir)
for i in index_list:
fileName = os.path.join(file, all_data[i])
if num < num_all_data * 0.8:
# print(str(fileName))
print(imges_file+fileName)
shutil.copy(imges_file+fileName, trainDir)
print(imges_file+fileName)
elif num > num_all_data * 0.8 and num < num_all_data * 0.9:
# print(str(fileName))
shutil.copy(imges_file+fileName, validDir)
else:
shutil.copy(imges_file+fileName, testDir)
num += 1
return 'Success'
if __name__ == "__main__":
imges_file = '/home/xh/YoLo/del_data/cataractdataset/'
split_save = "/home/xh/YoLo/del_data/cataractdatasetSplitdata/"
imges_file_list = [i for i in os.listdir(imges_file)]
jg = split_dataset(imges_file_list,imges_file,split_save)
print(jg)
(3)结果如下:
2、修改分类训练代码train.py:
在这里必须修改模型权重路径和数据集路径,其它参数根据自己的需求进行修改;
3、运行train.py文件,训练模型,结果如下:
上图中的【..\runs\train-cls\exp3】路径就是存放训练出的权重路径;
Epoch(迭代轮数):一个 epoch 表示将训练集中的所有样本都用于训练的一次迭代。在每个 epoch 中,模型会依次使用训练集中的每个样本进行训练。
GPU_mem(显存占用):指的是在训练过程中 GPU 使用的显存量。显存占用通常会随着模型的复杂度和批量大小的增加而增加。
train_loss(训练损失):在每个 epoch 结束时,使用训练集计算的损失值。训练损失用于衡量模型在训练集上的拟合程度。
test_loss(测试损失):在每个 epoch 结束时,使用测试集计算的损失值。测试损失用于衡量模型在测试集上的拟合程度。通常用于评估模型的泛化能力。
top1_acc(Top-1 准确率):在每个 epoch 结束时,使用测试集计算的准确率。Top-1 准确率表示模型在预测时,与真实标签完全匹配的样本所占的比例。
top5_acc(Top-5 准确率):在每个 epoch 结束时,使用测试集计算的准确率。Top-5 准确率表示模型在预测时,真实标签在模型预测的前五个最高概率中的样本所占的比例。
这些指标可以用来评估模型的性能和训练过程的进展。通常,我们希望训练损失和测试损失都能够逐渐减小,而准确率则逐渐提高。
4、修改predict.py文件,进行测试验证,结果如下:
测试数据为normal中NL_289.png图片:
测试结果如下:
再测试一张图片,选择cataract(白内障)中的一张图片:
测试结果如下:
六、简单总结
1、以上就是使用yolov9模型进行一个分类与目标检测的介绍和实践,大家可以参考与提出问题,看见了就会回答,一起讨论!项目代码和数据也上传了,大家可以下载!
2、再这个过程中大家肯定会遇见一些错误,但是不要害怕,可以去根据提示进行改正或者上网查找解决方法,还有就是询问chatgpt大模型!
3、不管是分类训练,还是目标检测,已经达到一个不错的效果了!,但是大家如果还需要提高精确度,比如增加数据,我这里的数据量不是太多,数据增强,调整学习率,增加训练轮数等,可以参考以下方法:
要提高YOLOv9模型的精确度和准确度,可以尝试以下方法:
增加训练数据量:增加训练数据可以提供更多的样本和场景,有助于模型学习更多的特征和模式。
数据增强:使用数据增强技术,如随机缩放、翻转、旋转、裁剪等,可以扩充训练数据集,增加模型的鲁棒性和泛化能力。
调整模型架构:可以根据任务需求调整YOLOv5的网络架构,如增加卷积层、调整通道数、调整网络深度等,以提升模型的表达能力。
调整训练参数:可以尝试不同的学习率、批量大小、优化器、学习率衰减策略等训练参数,以找到最优的训练配置。
使用预训练模型:可以使用在大规模数据集上预训练的模型作为初始权重,然后在目标数据集上进行微调,以加快收敛速度和提高模型性能。
多尺度训练和推理:使用多尺度训练和推理可以提升模型对不同尺寸目标的检测能力,可以在训练时随机选择不同的输入尺寸,并在推理时使用多个尺度的图像进行预测。
使用更大的输入图像尺寸:增加输入图像的尺寸可以提高模型对小目标的检测能力,但同时也会增加计算和内存开销。
引入注意力机制:可以在YOLOv9中引入注意力机制,如SENet、CBAM等,以增强模型对重要特征的关注和响应能力。
集成学习:通过模型集成方法,如投票、平均等,结合多个训练好的YOLOv9模型的预测结果,可以提高模型的稳定性和性能。
以上是一些常用的方法,可以根据具体的任务和数据集进行尝试和调整,以获得更好的精确度和准确度。