YOLO11训练后的模型无法正常推理解决办法
前面的文章,已将YOLO11从0到1的完整的运动一遍:从环境搭建、到模型评估、再到模型推理。
但实际结果前面的文章中已有表述,推理结果也不明显。可能导致此问题的点在于:
- 基座模型选的不合适
- 标注的数据过拟合,导致无法准备推理
- 标注数据过少,毕竟只选择了10张左右的数据,这也是过拟合的一种表现
继续优化,训练出的模型可以正常推理才算完成目标。
比较一下基座模型与训练的差异
创建 compare_models.py进行比对:
from ultralytics import YOLO
import torchdef compare_models_detailed():"""详细对比原始模型和训练后模型"""print("🔍 模型对比分析")print("=" * 60)# 加载两个模型try:original_model = YOLO('yolo11n.pt')trained_model = YOLO('runs/train/my_yolo11_model/weights/best.pt')print("✅ 两个模型都加载成功")except Exception as e:print(f"❌ 模型加载失败: {e}")return# 对比模型结构print("\n📊 模型结构对比:")print(f"{'特性':<20} {'原始模型':<15} {'训练后模型':<15}")print("-" * 50)# 类别数量orig_nc = original_model.model.model[-1].nctrained_nc = trained_model.model.model[-1].ncprint(f"{'类别数量':<20} {orig_nc:<15} {trained_nc:<15}")# 类别名称orig_names = original_model.namestrained_names = trained_model.namesprint(f"{'类别名称':<20} {str(orig_names):<15} {str(trained_names):<15}")# 参数量orig_params = sum(p.numel() for p in original_model.model.parameters())trained_params = sum(p.numel() for p in trained_model.model.parameters())print(f"{'参数量':<20} {orig_params:,}<15} {trained_params:,}<15")return original_model, trained_modeldef analyze_class_mapping(original_model, trained_model):"""分析类别映射问题"""print("\n🎯 类别映射分析:")print("=" * 60)orig_names = original_model.namestrained_names = trained_model.namesprint("原始模型类别:", orig_names)print("训练后模型类别:", trained_names)# 检查类别一致性if set(orig_names.values()) != set(trained_names.values()):print("❌ 类别名称不一致!")print("原始模型有但训练模型没有:", set(orig_names.values()) - set(trained_names.values()))print("训练模型有但原始模型没有:", set(trained_names.values()) - set(orig_names.values()))else:print("✅ 类别名称一致")# 检查类别ID映射if len(orig_names) != len(trained_names):print("⚠️ 类别数量不同,可能存在映射问题")def test_same_image_both_models(original_model, trained_model, test_image_path):"""用同一张图片测试两个模型"""print(f"\n🧪 测试图片: {test_image_path}")print("=" * 60)# 原始模型预测print("1. 原始模型 (yolo11n.pt):")orig_results = original_model.predict(source=test_image_path,conf=0.25,device='mps',save=False,verbose=False)orig_boxes = orig_results[0].boxesprint(f" 检测到 {len(orig_boxes)} 个目标")if len(orig_boxes) > 0:for i, box in enumerate(orig_boxes):class_name = original_model.names[int(box.cls)]confidence = float(box.conf)print(f" - {class_name}: {confidence:.3f}")# 训练后模型预测print("\n2. 训练后模型 (best.pt):")trained_results = trained_model.predict(source=test_image_path, conf=0.01, # 使用极低置信度看是否有任何输出device='mps',save=False,verbose=False)trained_boxes = trained_results[0].boxesprint(f" 检测到 {len(trained_boxes)} 个目标")if len(trained_boxes) > 0:for i, box in enumerate(trained_boxes):class_name = trained_model.names[int(box.cls)]confidence = float(box.conf)print(f" - {class_name}: {confidence:.3f}")else:# 尝试更低的置信度print(" 尝试置信度 0.001...")trained_results = trained_model.predict(source=test_image_path,conf=0.001,device='mps', save=False,verbose=False)trained_boxes = trained_results[0].boxesprint(f" 检测到 {len(trained_boxes)} 个目标")if len(trained_boxes) > 0:for i, box in enumerate(trained_boxes):class_name = trained_model.names[int(box.cls)]confidence = float(box.conf)print(f" - {class_name}: {confidence:.3f}")if __name__ == '__main__':original_model, trained_model = compare_models_detailed()analyze_class_mapping(original_model, trained_model)# 测试一张图片test_image = '../test/your_test_image.jpg' # 替换为你的测试图片test_same_image_both_models(original_model, trained_model, test_image)
结果显示如下:
/Users/hogworts/miniconda3/envs/yolo11/bin/python /Users/hogworts/PycharmProjects/my-yolo-proj/scripts/compare_models.py
🔍 模型对比分析
============================================================
✅ 两个模型都加载成功模型结构对比:
特性 原始模型 训练后模型
--------------------------------------------------
类别数量 80 3
类别名称 {0: 'person', 1: 'bicycle', 2: 'car', 3: 'motorcycle', 4: 'airplane', 5: 'bus', 6: 'train', 7: 'truck', 8: 'boat', 9: 'traffic light', 10: 'fire hydrant', 11: 'stop sign', 12: 'parking meter', 13: 'bench', 14: 'bird', 15: 'cat', 16: 'dog', 17: 'horse', 18: 'sheep', 19: 'cow', 20: 'elephant', 21: 'bear', 22: 'zebra', 23: 'giraffe', 24: 'backpack', 25: 'umbrella', 26: 'handbag', 27: 'tie', 28: 'suitcase', 29: 'frisbee', 30: 'skis', 31: 'snowboard', 32: 'sports ball', 33: 'kite', 34: 'baseball bat', 35: 'baseball glove', 36: 'skateboard', 37: 'surfboard', 38: 'tennis racket', 39: 'bottle', 40: 'wine glass', 41: 'cup', 42: 'fork', 43: 'knife', 44: 'spoon', 45: 'bowl', 46: 'banana', 47: 'apple', 48: 'sandwich', 49: 'orange', 50: 'broccoli', 51: 'carrot', 52: 'hot dog', 53: 'pizza', 54: 'donut', 55: 'cake', 56: 'chair', 57: 'couch', 58: 'potted plant', 59: 'bed', 60: 'dining table', 61: 'toilet', 62: 'tv', 63: 'laptop', 64: 'mouse', 65: 'remote', 66: 'keyboard', 67: 'cell phone', 68: 'microwave', 69: 'oven', 70: 'toaster', 71: 'sink', 72: 'refrigerator', 73: 'book', 74: 'clock', 75: 'vase', 76: 'scissors', 77: 'teddy bear', 78: 'hair drier', 79: 'toothbrush'} {0: 'cat', 1: 'sequirrel', 2: 'bird'}
参数量 2624080,,,,,,,, 2,590,425<15🎯 类别映射分析:
============================================================
原始模型类别: {0: 'person', 1: 'bicycle', 2: 'car', 3: 'motorcycle', 4: 'airplane', 5: 'bus', 6: 'train', 7: 'truck', 8: 'boat', 9: 'traffic light', 10: 'fire hydrant', 11: 'stop sign', 12: 'parking meter', 13: 'bench', 14: 'bird', 15: 'cat', 16: 'dog', 17: 'horse', 18: 'sheep', 19: 'cow', 20: 'elephant', 21: 'bear', 22: 'zebra', 23: 'giraffe', 24: 'backpack', 25: 'umbrella', 26: 'handbag', 27: 'tie', 28: 'suitcase', 29: 'frisbee', 30: 'skis', 31: 'snowboard', 32: 'sports ball', 33: 'kite', 34: 'baseball bat', 35: 'baseball glove', 36: 'skateboard', 37: 'surfboard', 38: 'tennis racket', 39: 'bottle', 40: 'wine glass', 41: 'cup', 42: 'fork', 43: 'knife', 44: 'spoon', 45: 'bowl', 46: 'banana', 47: 'apple', 48: 'sandwich', 49: 'orange', 50: 'broccoli', 51: 'carrot', 52: 'hot dog', 53: 'pizza', 54: 'donut', 55: 'cake', 56: 'chair', 57: 'couch', 58: 'potted plant', 59: 'bed', 60: 'dining table', 61: 'toilet', 62: 'tv', 63: 'laptop', 64: 'mouse', 65: 'remote', 66: 'keyboard', 67: 'cell phone', 68: 'microwave', 69: 'oven', 70: 'toaster', 71: 'sink', 72: 'refrigerator', 73: 'book', 74: 'clock', 75: 'vase', 76: 'scissors', 77: 'teddy bear', 78: 'hair drier', 79: 'toothbrush'}
训练后模型类别: {0: 'cat', 1: 'sequirrel', 2: 'bird'}
❌ 类别名称不一致!
原始模型有但训练模型没有: {'snowboard', 'frisbee', 'refrigerator', 'carrot', 'cup', 'hair drier', 'train', 'fork', 'scissors', 'laptop', 'bottle', 'zebra', 'handbag', 'apple', 'sink', 'cow', 'suitcase', 'motorcycle', 'sheep', 'traffic light', 'bicycle', 'toaster', 'pizza', 'book', 'giraffe', 'toilet', 'dining table', 'fire hydrant', 'vase', 'couch', 'broccoli', 'bed', 'skis', 'parking meter', 'tie', 'knife', 'umbrella', 'banana', 'skateboard', 'chair', 'donut', 'bear', 'horse', 'spoon', 'bench', 'cake', 'potted plant', 'stop sign', 'oven', 'bus', 'remote', 'teddy bear', 'baseball glove', 'kite', 'dog', 'sandwich', 'elephant', 'car', 'backpack', 'hot dog', 'clock', 'baseball bat', 'cell phone', 'person', 'surfboard', 'truck', 'sports ball', 'wine glass', 'boat', 'toothbrush', 'orange', 'tennis racket', 'tv', 'mouse', 'microwave', 'keyboard', 'airplane', 'bowl'}
训练模型有但原始模型没有: {'sequirrel'}
⚠️ 类别数量不同,可能存在映射问题🧪 测试图片: ../test/0.jpeg
============================================================
1. 原始模型 (yolo11n.pt):检测到 1 个目标- cat: 0.8232. 训练后模型 (best.pt):检测到 0 个目标尝试置信度 0.001...
WARNING ⚠️ NMS time limit 2.050s exceeded检测到 300 个目标- cat: 0.004- cat: 0.004- sequirrel: 0.004- sequirrel: 0.004- cat: 0.004- sequirrel: 0.004- cat: 0.004- bird: 0.004- sequirrel: 0.004- sequirrel: 0.004- cat: 0.004- sequirrel: 0.004- cat: 0.004- bird: 0.004- cat: 0.004- cat: 0.004- sequirrel: 0.004- sequirrel: 0.004- cat: 0.004- cat: 0.004- sequirrel: 0.004- sequirrel: 0.004- sequirrel: 0.004- cat: 0.004- sequirrel: 0.004- sequirrel: 0.004- sequirrel: 0.004- cat: 0.004- sequirrel: 0.004- cat: 0.004- cat: 0.004- cat: 0.004- cat: 0.004- sequirrel: 0.004- cat: 0.004- sequirrel: 0.004- cat: 0.004- cat: 0.004- cat: 0.004- sequirrel: 0.004- sequirrel: 0.004- sequirrel: 0.004- cat: 0.004- cat: 0.004- cat: 0.004- bird: 0.004- sequirrel: 0.004- bird: 0.004- cat: 0.004- sequirrel: 0.004- bird: 0.004- sequirrel: 0.004- sequirrel: 0.004- sequirrel: 0.004- cat: 0.004- bird: 0.004- sequirrel: 0.004- bird: 0.004- sequirrel: 0.004- cat: 0.004- cat: 0.004- cat: 0.004- bird: 0.004- cat: 0.004- cat: 0.004- bird: 0.004- sequirrel: 0.004- cat: 0.004- bird: 0.004- sequirrel: 0.004- cat: 0.004- cat: 0.004- bird: 0.004- sequirrel: 0.004- cat: 0.004- bird: 0.004- cat: 0.004- bird: 0.004- cat: 0.004- sequirrel: 0.004- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001- cat: 0.001进程已结束,退出代码为 0
训练后的模型识别种类,只有3种,但这也不是不能进行推理的理由。
检查训练数据集是否正常
创建 check_training_config.py,看看源头是不是错了?
import yaml
import osdef analyze_training_config():"""分析训练配置和数据"""print("📋 训练配置分析")print("=" * 60)# 检查数据集配置dataset_config = '../configs/my_dataset.yaml'if os.path.exists(dataset_config):with open(dataset_config, 'r') as f:data = yaml.safe_load(f)print("数据集配置:")print(f" 路径: {data.get('path', '未设置')}")print(f" 训练集: {data.get('train', '未设置')}") print(f" 验证集: {data.get('val', '未设置')}")print(f" 类别数: {data.get('nc', '未设置')}")print(f" 类别名称: {data.get('names', '未设置')}")# 检查训练数据train_path = os.path.join(data.get('path', ''), data.get('train', ''))if os.path.exists(train_path):image_files = [f for f in os.listdir(train_path) if f.lower().endswith(('.jpg', '.png', '.jpeg'))]print(f" 训练图片数量: {len(image_files)}")else:print(f" ❌ 训练路径不存在: {train_path}")else:print(f"❌ 数据集配置文件不存在: {dataset_config}")# 检查训练参数train_args = 'runs/train/my_yolo11_model/args.yaml'if os.path.exists(train_args):with open(train_args, 'r') as f:args = yaml.safe_load(f)print("\n训练参数:")print(f" 训练轮数: {args.get('epochs', '未设置')}")print(f" 批次大小: {args.get('batch', '未设置')}")print(f" 学习率: {args.get('lr0', '未设置')}")print(f" 数据增强: {args.get('augment', '未设置')}")else:print(f"❌ 训练参数文件不存在: {train_args}")def check_label_files():"""检查标签文件"""print("\n🏷️ 标签文件分析")print("=" * 60)labels_dir = '../datasets/my_dataset/labels/train/'if os.path.exists(labels_dir):label_files = [f for f in os.listdir(labels_dir) if f.endswith('.txt')]print(f"标签文件数量: {len(label_files)}")# 分析标签内容class_distribution = {}empty_files = 0for label_file in label_files[:10]: # 检查前10个文件file_path = os.path.join(labels_dir, label_file)with open(file_path, 'r') as f:lines = f.readlines()if len(lines) == 0:empty_files += 1continuefor line in lines:parts = line.strip().split()if len(parts) >= 5:class_id = int(parts[0])class_distribution[class_id] = class_distribution.get(class_id, 0) + 1print(f"类别分布: {class_distribution}")print(f"空标签文件: {empty_files}")if empty_files > 0:print("⚠️ 存在空标签文件,可能影响训练")else:print(f"❌ 标签目录不存在: {labels_dir}")if __name__ == '__main__':analyze_training_config()check_label_files()
结果显示也没有不妥之处。
/Users/hogworts/miniconda3/envs/yolo11/bin/python /Users/hogworts/PycharmProjects/my-yolo-proj/scripts/check_training_config.py
📋 训练配置分析
============================================================
数据集配置:路径: /Users/hogworts/PycharmProjects/my-yolo-proj/datasets/my_dataset训练集: images/train验证集: images/val类别数: 3类别名称: {0: 'cat', 1: 'sequirrel', 2: 'bird'}训练图片数量: 8训练参数:训练轮数: 10批次大小: 8学习率: 0.01数据增强: False🏷️ 标签文件分析
============================================================
标签文件数量: 8
类别分布: {1: 3, 2: 2, 0: 5}
空标签文件: 0进程已结束,退出代码为 0
在各种尝试之后,决定换一个基座模型再进行训练,看看效果能不能好一些。事实证明,选用yolo11m模型之后,基本上就可以正常推理了(训练数据过少,无法导出规律,需要选用更大一些的模型才可以)。
/Users/hogworts/miniconda3/envs/yolo11/bin/python /Users/hogworts/PycharmProjects/my-yolo-proj/scripts/predict.py image 1/10 /Users/hogworts/PycharmProjects/my-yolo-proj/scripts/../test/0.jpeg: 640x448 1 cat, 226.7ms
image 2/10 /Users/hogworts/PycharmProjects/my-yolo-proj/scripts/../test/10.jpeg: 640x480 1 cat, 330.6ms
image 3/10 /Users/hogworts/PycharmProjects/my-yolo-proj/scripts/../test/2.jpeg: 448x640 1 cat, 256.6ms
image 4/10 /Users/hogworts/PycharmProjects/my-yolo-proj/scripts/../test/22.jpeg: 640x448 1 sequirrel, 1 bird, 71.3ms
image 5/10 /Users/hogworts/PycharmProjects/my-yolo-proj/scripts/../test/23d.jpeg: 640x448 2 birds, 33.5ms
image 6/10 /Users/hogworts/PycharmProjects/my-yolo-proj/scripts/../test/32.jpeg: 640x640 1 sequirrel, 1 bird, 69.7ms
image 7/10 /Users/hogworts/PycharmProjects/my-yolo-proj/scripts/../test/catanddog.jpeg: 480x640 1 cat, 373.1ms
image 8/10 /Users/hogworts/PycharmProjects/my-yolo-proj/scripts/../test/e3.jpeg: 448x640 1 cat, 1 sequirrel, 1 bird, 75.9ms
image 9/10 /Users/hogworts/PycharmProjects/my-yolo-proj/scripts/../test/f1.jpeg: 448x640 1 sequirrel, 1 bird, 37.0ms
image 10/10 /Users/hogworts/PycharmProjects/my-yolo-proj/scripts/../test/v2.jpeg: 640x448 1 sequirrel, 1 bird, 82.6ms
Speed: 2.9ms preprocess, 155.7ms inference, 69.4ms postprocess per image at shape (1, 3, 640, 448)
Results saved to /Users/hogworts/PycharmProjects/my-yolo-proj/scripts/runs/detect/predict13
检测到 1 个目标
检测到 1 个目标
检测到 1 个目标
检测到 2 个目标
检测到 2 个目标
检测到 2 个目标
检测到 1 个目标
检测到 3 个目标
检测到 2 个目标
检测到 2 个目标进程已结束,退出代码为 0

基座模型的差异

高容量模型 + 大数据集 = 最佳效果 ✅
高容量模型 + 小数据集 = 可能过拟合 ⚠️
低容量模型 + 大数据集 = 可能欠拟合 ⚠️
低容量模型 + 小数据集 = 严重过拟合 ❌
这也是前面使用yolo11n进行数据训练时存在的问题,严重过拟合,无法正常推理新数据。
