ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

自动标注-AI自主学习功能步骤

自动标注-AI自主学习功能步骤

yolov8的val.py的结果默认只在控制台显示,其中主要包括

Class Images Instances Box(P R mAP50 mAP50-95)

本文就是要将这些个信息都输出到指定文件(***.log文件)中保存,就不用每次要看数据都运行val.py了

原理:
就是将官方用logging模块输出到控制台的所有信息全部再输出到.log文件中,步骤一解决的是输出问题,步骤二是往log文件添加提示信息,步骤三解决的是.log文件的文件名字问题

设置:ultralytics/utils/__init__.py>set_logging

添加筛选log类

#*****************定义日志过滤功能类 start class KeywordFilter(logging.Filter): """ 自定义日志过滤器,只记录包含特定关键字的日志行 """ def __init__(self, *keywords): self.keywords = keywords def filter(self, record): # 仅当日志消息包含任意一个关键字时,返回True return any(keyword in record.msg for keyword in self.keywords) class MultiFileFilter(logging.Filter): """ 自定义日志过滤器,只记录来自指定源文件的日志信息。 """ def __init__(self, filenames): super().__init__() self.filenames = set(filenames) # 将文件名集合化,方便查找 def filter(self, record): # 仅当日志记录来自指定的源文件时,返回 True return record.filename in self.filenames #*****************定义日志过滤功能类 end

在set_logging方法中添加

位置:

# ---------------将日志结果输出到文件中-----------------# log_path = '/home/luodehuan/workspace/test/runs/log/' #指定文件夹路径 os.makedirs(log_path, exist_ok=True) # from val import result_name # from auto_batch import result_name logfile = log_path + 'Val_Results.log' #“result_name”是和val.py运行生成的“文件夹的名称”一致方便一一对应 print_handler = logging.FileHandler(filename=logfile, encoding='utf-8') # 定义输出的文件名字 formatter = logging.Formatter("%(asctime)s - %(filename)s[line:%(lineno)d] - %(levelname)s: %(message)s") # 设置日志格式 print_handler.setFormatter(formatter) # 设置处理器的日志格式 # 创建自定义过滤器,只写入来自 'my_script.py' 的日志信息 allowed_files = ["validator.py", "val.py"] # 指定多个文件名 file_filter = MultiFileFilter(allowed_files) print_handler.addFilter(file_filter) # 添加过滤器到日志处理器 # 获取Logger实例并设置日志级别和处理器 logger = logging.getLogger() logger.setLevel(logging.INFO) # 设置日志级别为INFO或更高级别 logger.addHandler(print_handler) # 往logger对象中添加处理器 #*************筛选日志目录

1.把相关实现函数,写入tools.py

import os from ultralytics import YOLO import shutil import random import numpy as np from sklearn.model_selection import train_test_split import yaml import warnings warnings.filterwarnings('ignore') class ImagePredictor: def __init__(self, img_path, model_path): """ 初始化图像预测器。 :param img_path: 要预测的图像文件夹路径 :param model_path: YOLO 模型的权重路径 """ self.img_path = img_path self.model_path = model_path self.model = YOLO(model_path) # 加载模型 def process_images(self): """ 遍历文件夹中的图片文件并进行预测。 """ # 遍历文件夹中的文件 for filename in os.listdir(self.img_path): # 判断文件是否为图片文件(支持 .jpg 和 .png 格式) if filename.endswith(".jpg") or filename.endswith(".png"): # 拼接文件的完整路径 file_path = os.path.join(self.img_path, filename) # 打印正在处理的文件 print(f"正在处理: {file_path}") # 使用模型进行预测并保存结果 results = self.model(file_path, save=True,save_txt=True) print(f"预测结果保存完成: {file_path}") # 打印保存标签的路径 labels_save_path = self.model.predictor.save_dir / "labels" print(f"labels_save_path: {labels_save_path}") # 返回保存标签的路径 return labels_save_path def run(self): """ 运行图像预测流程。 """ return self.process_images() class DatasetSplitter: def __init__(self, img_path, txt_path, output_path, val_size=0.1, test_size=0.1, postfix='png'): """ 初始化数据集划分器。 :param img_path: 图片文件路径 :param txt_path: 标签文件路径 :param output_path: 数据集输出文件夹路径 :param val_size: 验证集比例 :param test_size: 测试集比例 :param postfix: 图片文件后缀 """ self.img_path = img_path self.txt_path = txt_path self.output_path = output_path self.val_size = val_size self.test_size = test_size self.postfix = postfix # 创建数据集文件夹 os.makedirs(os.path.join(self.output_path, 'images/train'), exist_ok=True) os.makedirs(os.path.join(self.output_path, 'images/val'), exist_ok=True) os.makedirs(os.path.join(self.output_path, 'images/test'), exist_ok=True) os.makedirs(os.path.join(self.output_path, 'labels/train'), exist_ok=True) os.makedirs(os.path.join(self.output_path, 'labels/val'), exist_ok=True) os.makedirs(os.path.join(self.output_path, 'labels/test'), exist_ok=True) def check_and_create_txt_files(self): """ 检查图片文件是否有对应的txt标签文件,如果没有则创建空的txt文件。 """ # 获取所有的图片文件 image_files = [f for f in os.listdir(self.img_path) if f.endswith(self.postfix)] # 检查并创建对应的txt文件 for image in image_files: txt_filename = f'{image[:-4]}.txt' # 对应的txt文件名 txt_filepath = os.path.join(self.txt_path, txt_filename) # 完整txt文件路径 if not os.path.exists(txt_filepath): # 如果txt文件不存在,创建一个空文件 print(f'{txt_filename} 不存在,创建空文件...') with open(txt_filepath, 'w') as f: pass # 创建一个空文件 def split_dataset(self): """ 使用train_test_split划分数据集为训练集、验证集和测试集。 :return: 训练集、验证集、测试集的文件列表 """ # 获取所有的标签文件(txt文件) listdir = np.array([f for f in os.listdir(self.txt_path) if f.endswith('.txt')]) random.shuffle(listdir) # 随机打乱 # 使用train_test_split进行数据集划分 train_val, test = train_test_split(listdir, test_size=self.test_size, random_state=0) train, val = train_test_split(train_val, test_size=self.val_size / (1 - self.test_size), random_state=0) print(f'train set size: {len(train)}') print(f'val set size: {len(val)}') print(f'test set size: {len(test)}') return train, val, test def copy_files(self, file_list, split_name): """ 将图片和标签文件复制到指定的目录中。 :param file_list: 要复制的文件列表 :param split_name: 数据集的分组名称(train/val/test) """ for txt_file in file_list: try: # 拼接源图片文件和目标文件路径 img_file = f'{txt_file[:-4]}.{self.postfix}' # 图片文件名 src_img_path = os.path.join(self.img_path, img_file) # 源图片路径 src_txt_path = os.path.join(self.txt_path, txt_file) # 源标签路径 # 目标文件夹 dst_img_dir = os.path.join(self.output_path, f'images/{split_name}') # 目标图片目录 dst_txt_dir = os.path.join(self.output_path, f'labels/{split_name}') # 目标标签目录 # 复制图片和标签文件到目标目录 shutil.copy(src_img_path, os.path.join(dst_img_dir, img_file)) shutil.copy(src_txt_path, os.path.join(dst_txt_dir, txt_file)) except FileNotFoundError as e: print(f"文件未找到: {e}") def run(self): """ 运行数据集划分流程,依次进行标签文件检查、数据集划分和文件复制。 """ # 1. 检查并创建缺失的标签文件 self.check_and_create_txt_files() # 2. 划分数据集 train, val, test = self.split_dataset() # 3. 复制训练集、验证集和测试集文件 self.copy_files(train, 'train') self.copy_files(val, 'val') self.copy_files(test, 'test') class YAMLPathUpdater: def __init__(self, yaml_file): """ 初始化 YAMLPathUpdater 类 :param yaml_file: YAML 文件路径 """ self.yaml_file = yaml_file def update_path(self, new_path): """ 更新 YAML 文件中的 'path' 字段为新的路径,只修改 path 字段 :param new_path: 要更新的路径 """ try: # 逐行读取文件 with open(self.yaml_file, 'r') as file: lines = file.readlines() # 遍历文件的每一行,寻找 path 字段 with open(self.yaml_file, 'w') as file: for line in lines: if line.strip().startswith('path:'): # 找到 path 字段并更新 file.write(f"path: {new_path}\n") print(f"旧的 path 字段已更新为: {new_path}") else: # 其他行保持不变 file.write(line) print(f"YAML 文件保存成功: {self.yaml_file}") except FileNotFoundError: print(f"文件 {self.yaml_file} 未找到!") except Exception as e: print(f"更新 YAML 文件时发生错误: {e}") def run(self, new_path): """ 执行更新 'path' 字段并保存文件的流程 :param new_path: 新的路径 """ self.update_path(new_path)

2.主函数auto_project.py

from tools import * # 使用示例 if __name__ == '__main__': # 初始化设置 # 文件夹路径和模型路径 img_path = "/home/luodehuan/workspace/test/test_picture" # 给定客户数据路径 model_path = "/home/luodehuan/workspace/test/runs/train/yolov8s/New11C/WIOU2/weights/best.pt" #yolov8x模型路径 dataset_path = "/home/luodehuan/workspace/test/output/datasets" # 指定生成数据集文件夹路径 yaml_file_path = '/home/luodehuan/workspace/test/ultralytics-main/auto_project/data.yaml' # 替换为你的 yaml 文件路径 # 1.对输入客户数据进行 预打标 # 在指定 dataset_path 生成数据集 # 实例化 ImagePredictor 类 predictor = ImagePredictor(img_path, model_path) # 运行预测 labels_save_path = predictor.run() # 实例化 DatasetSplitter 并运行数据集划分 dataset_splitter = DatasetSplitter(img_path, labels_save_path, dataset_path) dataset_splitter.run() # 实例化 YAMLPathUpdater 类并执行更新操作 # 生成的数据集路径更新到微调模型训练的 data.yaml中 new_dataset_path = dataset_path # 替换为你想要的新的输出路径 yaml_updater = YAMLPathUpdater(yaml_file_path) yaml_updater.run(new_dataset_path) # 2.定制YOLOv8n finetune # 设置data.yaml # 开始微调训练 # 输出FineTune_Base模型 model1 = YOLO('/home/luodehuan/workspace/test/ultralytics-main/ultralytics/cfg/models/v8/yolov8n.yaml') model1.load('/home/luodehuan/workspace/ultralytics-main/runs/train/yolov8n/New11C/640/weights/best.pt') # loading pretrain weights model1.train(data=yaml_file_path, cache=False, imgsz=640, epochs=100, batch=63, close_mosaic=0, workers=40, device='0,1,2', optimizer='SGD', # using SGD # patience=0, # close earlystop # resume=True, # 断点续训,YOLO初始化时选择last.pt # amp=False, # close amp # fraction=0.2, project='runs/train/yolov8n', #MPCA name='New11C', ) # 获取FineTune_Base模型 保存路径 FineTune_weight_path = model1.trainer.wdir / 'best.pt' # 3.用img_path (客户提供)数据分别输入预设的大模型、Base模型、FineTune_Base模型进行验证 # 验证1 大模型(yolov8x) model2 = YOLO(model_path) model2.val(data=yaml_file_path, split='train', imgsz=640, batch=16, # iou=0.7, # rect=False, # save_json=True, # if you need to cal coco metrice project='runs/val', name='yolov8x', ) # 验证2 大模型(yolov8n) model3 = YOLO('/home/luodehuan/workspace/ultralytics-main/runs/train/yolov8n/New11C/640/weights/best.pt') model3.val(data=yaml_file_path, split='train', imgsz=640, batch=16, # iou=0.7, # rect=False, # save_json=True, # if you need to cal coco metrice project='runs/val', name='yolov8n', ) #验证3 FineTune模型 #对客户数据用FineTune模型进行验证,得出验证指标 model4 = YOLO(FineTune_weight_path) model4.val(data=yaml_file_path, split='train', imgsz=640, batch=16, # iou=0.7, # rect=False, # save_json=True, # if you need to cal coco metrice project='runs/val', name='FineTune', )

3.读取log文件并画柱形图

import re import matplotlib.pyplot as plt def extract_values_for_plot(log_file_path): """提取日志文件中所有包含 'all' 的行的第十四个数值,并返回以供绘图""" indices = [] # 用于存储读取次数 values14 = [] # 用于存储第十四个值 try: with open(log_file_path, 'r', encoding='utf-8') as file: count = 0 # 计数器初始化 for line in file: if 'all' in line: # 查找包含 'all' 的行 count += 1 # 每次找到 'all' 时计数加一 # 使用正则表达式提取数值 values = re.findall(r'[\d.]+', line) if len(values) >= 14: # 确保有足够的数值 indices.append(count) values14.append(float(values[13])*100) # 第十四个值 except FileNotFoundError: print(f"文件 {log_file_path} 未找到。") except Exception as e: print(f"发生错误: {e}") return indices, values14 def plot_and_save_values(indices, values14, output_path, custom_labels): """绘制柱形图并保存""" width = 0.35 # 柱子宽度 x = range(len(indices)) bars = plt.bar(x, values14, width, label='', color='m', alpha=0.7) plt.xlabel('') plt.ylabel('Values') plt.title('Comparison') plt.xticks(x, custom_labels) # 使用自定义的横坐标标签 plt.legend() # 在每个柱形条右下角显示对应的数值 for bar in bars: yval = bar.get_height() # 获取柱子的高度 plt.text(bar.get_x() + bar.get_width() / 2, yval , f'{yval:.3f}%', ha='center', va='bottom') # 显示数值 plt.tight_layout() # 保存柱形图 plt.savefig(output_path) print(f"柱形图已保存到 {output_path}") plt.close() # 关闭图像以释放内存 # 示例用法 log_file_path = '/home/luodehuan/workspace/test/runs/log/Val_Results.log' # 替换为你的日志文件路径 output_image_path = '/home/luodehuan/workspace/test/runs/log/bar_chart.png' # 替换为你想保存的图像路径 custom_labels = ['', '', ''] # 自定义横坐标标签 indices, values14 = extract_values_for_plot(log_file_path) plot_and_save_values(indices, values14, output_image_path, custom_labels)
返回列表