YOLO11训练自己的数据集
环境搭建
Anaconda3安装
Anaconda
我下载的版本是Anaconda3-2024.10-1-Windows-x86_64
Pycharm安装
安装Cuda
Cuda
我下载的版本是cuda_12.9.0_576.02_windows
Cudnn安装
Cudnn
我下载的版本是cudnn-windows-x86_64-8.9.7.29_cuda12-archive
安装完后进入Anaconda prompt创建虚拟环境

默认进入的是base环境,base环境的版本与下载的anaconda3版本有关,因此不建议直接使用,需要新建环境,在新建环境之前建议更改默认的pip源和conda源可加速下载速度
conda config --remove-key channels
conda config --add channels https://mirrors.ustc.edu.cn/anaconda/pkgs/main/
conda config --add channels https://mirrors.ustc.edu.cn/anaconda/pkgs/free/
conda config --add channels https://mirrors.bfsu.edu.cn/anaconda/cloud/pytorch/
conda config --set show_channel_urls yes
pip config set global.index-url https://mirrors.ustc.edu.cn/pypi/web/simple
创建一个名为yolov11,python版本为3.10的虚拟环境,回车后出现新建环境提醒输入y继续,耐心等待全部下载完成后自动安装
conda create -n yolov11 python=3.10
创建完之后输入下面命令进入yolov11环境
conda activate yolov11
输入下方命令即可安装pytorch,耐心等待安装完成
pip install torch==2.0.0+cu118 torchvision==0.15.1+cu118 --extra-index-url https://download.pytorch.org/whl/cu118
输入下面的指令通过pip安装其余库
pip install torch==2.0.0+cu118 torchvision==0.15.1+cu118 --extra-index-url https://download.pytorch.org/whl/cu118
安装ultralytics库
pip install ultralytics
源码下载
搭建好环境后,下载yolo11源码,源码仓库现在是Yolo26
可以在网盘下载
下载完后使用Pycharm打开
修改解释器

项目有这些文件

先验证环境是否配好
yolo predict model=yolo11n.pt source='cat.jpg'
如下输出说明正常

训练自己的模型
数据集收集
因为这个检测我是要在RK3588上跑的,所以数据集直接用RK3588进行收集

数据集标注
数据集标注使用labelme软件进行标注

数据集转换
标注完数据集后,文件格式没有转换的话是LabelMe输出的.json文件,需要转换成yolo格式的文件,即.txt文件

在项目目录下创建一个json2yolo.py的脚本,脚本内容如下
import json
import os
import glob
# ================= 配置区域 =================
# 1. 你的 json 文件所在的文件夹路径 (注意路径中的斜杠)
json_dir = 'D:/your_path/json_labels'
# 2. 转换后的 txt 文件保存路径 (建议新建一个文件夹)
txt_save_dir = 'D:/your_path/yolo_txt_labels'
# 3. 类别映射字典 (务必与你之前设定的严格对应!)
class_mapping = {
'solder_bridge': 0,
'missing_comp': 1,
'missing_solder': 2
}
# ============================================
if not os.path.exists(txt_save_dir):
os.makedirs(txt_save_dir)
json_files = glob.glob(os.path.join(json_dir, '*.json'))
for json_file in json_files:
with open(json_file, 'r', encoding='utf-8') as f:
data = json.load(f)
# 获取图像的绝对宽和高
img_width = data['imageWidth']
img_height = data['imageHeight']
# 获取文件名(不含后缀)
file_name = os.path.splitext(os.path.basename(json_file))[0]
txt_file_path = os.path.join(txt_save_dir, file_name + '.txt')
with open(txt_file_path, 'w') as txt_f:
for shape in data['shapes']:
label_name = shape['label']
# 如果标注的名字不在我们设定的字典里,跳过或者报错
if label_name not in class_mapping:
print(f"警告: 文件 {file_name}.json 中发现未知类别 '{label_name}',已跳过。")
continue
class_id = class_mapping[label_name]
# 解析 LabelMe 的两点坐标 (左上角和右下角)
points = shape['points']
x1, y1 = points[0]
x2, y2 = points[1]
# 确保坐标顺序正确
xmin = min(x1, x2)
xmax = max(x1, x2)
ymin = min(y1, y2)
ymax = max(y1, y2)
# YOLO 格式数学转换:计算中心点和宽高,并归一化 (除以图片总宽高)
x_center = ((xmin + xmax) / 2) / img_width
y_center = ((ymin + ymax) / 2) / img_height
width = (xmax - xmin) / img_width
height = (ymax - ymin) / img_height
# 写入 txt 文件,保留 6 位小数
txt_f.write(f"{class_id} {x_center:.6f} {y_center:.6f} {width:.6f} {height:.6f}\n")
print("转换完成!请前往输出文件夹检查 .txt 文件。")
然后点击右上角的运行

转换成功

随便打开一个,显示的是坐标文件,说明没有问题

数据集划分
训练自己的yolov11检测模型,数据集需要划分为训练集、验证集和测试集,这里提供一个参考代码,划分比例为8:1:1,也可以按照自己的比例划分,获取的数据集划分过了则不用重复划分,即在项目目录创建一个dataset.py脚本,在代码最后替换成自己的路径
import os
import shutil
import random
# random.seed(0) #随机种子,可自选开启
def split_data(file_path, label_path, new_file_path, train_rate, val_rate, test_rate):
images = os.listdir(file_path)
labels = os.listdir(label_path)
images_no_ext = {os.path.splitext(image)[0]: image for image in images}
labels_no_ext = {os.path.splitext(label)[0]: label for label in labels}
matched_data = [(img, images_no_ext[img], labels_no_ext[img]) for img in images_no_ext if img in labels_no_ext]
unmatched_images = [img for img in images_no_ext if img not in labels_no_ext]
unmatched_labels = [label for label in labels_no_ext if label not in images_no_ext]
if unmatched_images:
print("未匹配的图片文件:")
for img in unmatched_images:
print(images_no_ext[img])
if unmatched_labels:
print("未匹配的标签文件:")
for label in unmatched_labels:
print(labels_no_ext[label])
random.shuffle(matched_data)
total = len(matched_data)
train_data = matched_data[:int(train_rate * total)]
val_data = matched_data[int(train_rate * total):int((train_rate + val_rate) * total)]
test_data = matched_data[int((train_rate + val_rate) * total):]
# 处理训练集
for img_name, img_file, label_file in train_data:
old_img_path = os.path.join(file_path, img_file)
old_label_path = os.path.join(label_path, label_file)
new_img_dir = os.path.join(new_file_path, 'train', 'images')
new_label_dir = os.path.join(new_file_path, 'train', 'labels')
os.makedirs(new_img_dir, exist_ok=True)
os.makedirs(new_label_dir, exist_ok=True)
shutil.copy(old_img_path, os.path.join(new_img_dir, img_file))
shutil.copy(old_label_path, os.path.join(new_label_dir, label_file))
# 处理验证集
for img_name, img_file, label_file in val_data:
old_img_path = os.path.join(file_path, img_file)
old_label_path = os.path.join(label_path, label_file)
new_img_dir = os.path.join(new_file_path, 'val', 'images')
new_label_dir = os.path.join(new_file_path, 'val', 'labels')
os.makedirs(new_img_dir, exist_ok=True)
os.makedirs(new_label_dir, exist_ok=True)
shutil.copy(old_img_path, os.path.join(new_img_dir, img_file))
shutil.copy(old_label_path, os.path.join(new_label_dir, label_file))
# 处理测试集
for img_name, img_file, label_file in test_data:
old_img_path = os.path.join(file_path, img_file)
old_label_path = os.path.join(label_path, label_file)
new_img_dir = os.path.join(new_file_path, 'test', 'images')
new_label_dir = os.path.join(new_file_path, 'test', 'labels')
os.makedirs(new_img_dir, exist_ok=True)
os.makedirs(new_label_dir, exist_ok=True)
shutil.copy(old_img_path, os.path.join(new_img_dir, img_file))
shutil.copy(old_label_path, os.path.join(new_label_dir, label_file))
print("数据集已划分完成")
if __name__ == '__main__':
file_path = r"f:\data\JPEGImages" # 图片文件夹
label_path = r'f:\data\labels' # 标签文件夹
new_file_path = r"f:\VOCdevkit" # 新数据存放位置
split_data(file_path, label_path, new_file_path, train_rate=0.8, val_rate=0.1, test_rate=0.1)
点击运行

分好了,把分好后的文件夹复制到项目目录

8:1:1是标准的做法,但是会浪费一部分数据给测试,更推荐做8:2划分,即训练:验证=8:2,只需要把脚本最后的比例改一下就行了

训练模型
在项目路径创建一个.yaml文件,这是专门写给 YOLO 系统的数据集导航地图

项目目录创建一个训练脚本yolo11_train.py
epochs是训练轮数,可以由少变多看训练效果,workers和batch根据电脑性能进行调整,如果运行吃力则相应降低,最好为2的n次方。没有显卡则把device=0修改为device=‘cpu’
import warnings
warnings.filterwarnings('ignore')
from ultralytics import YOLO
if __name__ == '__main__':
model = YOLO('D:/OKay/Yolo/Yolo11/YOLOv11/ultralytics-8.3.23/ultralytics/cfg/models/11/yolo11.yaml')
model.load('yolo11n.pt') #注释则不加载
results = model.train(
data='D:/OKay/Yolo/Yolo11/YOLOv11/ultralytics-8.3.23/pcba_data.yaml', #数据集配置文件的路径
epochs=200, #训练轮次总数
batch=16, #批量大小,即单次输入多少图片训练
imgsz=640, #训练图像尺寸
workers=8, #加载数据的工作线程数
device= 0, #指定训练的计算设备,无nvidia显卡则改为 'cpu'
optimizer='SGD', #训练使用优化器,可选 auto,SGD,Adam,AdamW 等
amp= True, #True 或者 False, 解释为:自动混合精度(AMP) 训练
cache=False # True 在内存中缓存数据集图像,服务器推荐开启
)
也可以使用命令行执行训练
yolo task=detect mode=train model=yolo11n.yaml pretrained=yolo11n.pt data=data.yaml epochs=200 imgsz=640 device=0 optimizer='SGD' workers=8 batch=64 amp=False cache=False
训练开始

内存炸了,训练过程中pycharm闪退了,改用终端训练,总共要训练200轮

训练完成

模型测试
项目目录创建一个测试脚本yolov11_predict.py
from ultralytics import YOLO
# 加载训练好的模型,改为自己的路径
model = YOLO('runs/detect/train5/weights/best.pt') #修改为训练好的路径
source = 'D:/OKay/Yolo/test.png' #修改为自己的图片路径及文件名
# 运行推理,并附加参数
model.predict(source, save=True)

测试测试集上各类别的精度及总体精度,创建一个yolo11_val.py脚本
import warnings
warnings.filterwarnings('ignore')
from ultralytics import YOLO
if __name__ == '__main__':
model = YOLO('runs/detect/train5/weights/best.pt')
model.val(data='D:/OKay/Yolo/Yolo11/YOLOv11/ultralytics-8.3.23/pcba_data.yaml',
split='val', # <--- 关键修改在这里
imgsz=640,
batch=16,
iou=0.6,
conf=0.001,
workers=8,
)
由于前面使用的是8:2的划分,所以没有单独的测试集,直接用验证验证集的数据


数据分析
混淆矩阵
混淆矩阵数据表明,模型在当前验证集上实现了零误检(False Positive)与零漏检(False Negative),呈现出极高的分类与定位精度。从绝对值混淆矩阵的统计结果来看,验证集内共包含76个连锡(solder_bridge)、38个漏件(missing_comp)以及40个漏焊(missing_solder)真实样本。模型对上述三个类别的预测数量与真实标签数量完全对应,所有预测结果均集中于矩阵对角线。相应的归一化混淆矩阵(Normalized Confusion Matrix)显示各类别对角线数值均为 1.00,背景(background)类别与其他缺陷类别交叉项均为0。该组数据证实模型对各类PCBA缺陷特征具有优异的提取与判别能力,未发生类别混淆现象。


精确率与召回率曲线
精确率-召回率曲线进一步量化了模型的综合检测性能。在当前验证集下,连锡、漏件与漏焊三个单独类别的 P-R 曲线以及全类别平均曲线均呈现出理想的直角形态,紧贴坐标平面右上方(坐标为 1.0, 1.0 的区域)。在交并比(IoU)阈值设为 0.5 的标准下,模型全类别的平均精度均值(mAP@0.5)高达 0.995。此项指标反映出模型在维持极高预测精确率的同时,也达到了满分的召回率,验证了模型在复杂区域特征定位上的高可靠性。

置信度相关曲线
置信度综合曲线(包含 F1-Confidence, Precision-Confidence, Recall-Confidence)的结果显示,模型对其输出的预测框具有极高的确定性。其中,F1-置信度曲线表明,当推理阶段的置信度阈值(Confidence Threshold)设定为 0.878 时,所有类别的 F1 综合得分达到最高值 1.00。此外,精确率和召回率在极宽的置信度区间(约 0.05 至 0.90)内均保持在 1.0 满分状态,直至置信度超过 0.95 之后才出现召回率的断崖式下降。该分布特性说明模型预测得分普遍且稳定地处于高位,抗背景噪声干扰的能力极强,非常适合在实际工程中采用较高的阈值进行严格筛选。



综合上述多维度评估指标,本次训练的 YOLOv11 模型在 PCBA 缺陷检测的验证集上表现卓越,各项性能指标均触及理论上限。鉴于验证集数据表现出 100% 的准确率,模型具备了坚实的落地基础。针对后续工程实践,提出两点建议:首先,需警惕当前数据集分布可能带来的过拟合(Overfitting)风险,建议引入未参与模型迭代的全新批次产线实拍数据进行交叉验证,以评估其实际泛化能力。

评论