data_img / script /check_yolov11_model_architecture.py
sft2005's picture
Upload folder using huggingface_hub
9545fea verified
"""
检查YOLOv11 backbone的YOLOP模型架构
验证backbone和neck的结构是否与预期一致
"""
import sys
import os
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.append(BASE_DIR)
import torch
import torch.nn as nn
from lib.models.YOLOP_YOLOv11 import YOLOPWithYOLOv11
from lib.config import cfg
from lib.config import update_config
def count_parameters(model):
"""统计模型参数数量"""
total_params = sum(p.numel() for p in model.parameters())
trainable_params = sum(p.numel() for p in model.parameters() if p.requires_grad)
return total_params, trainable_params
def check_module_structure(module, name, depth=0):
"""递归检查模块结构"""
indent = " " * depth
print(f"{indent}{name}:")
if isinstance(module, nn.ModuleList):
for i, sub_module in enumerate(module):
sub_name = f"[{i}] {type(sub_module).__name__}"
if depth < 2: # 限制递归深度
check_module_structure(sub_module, sub_name, depth + 1)
else:
print(f"{indent} {sub_name}")
elif isinstance(module, nn.Sequential):
for i, sub_module in enumerate(module):
sub_name = f"[{i}] {type(sub_module).__name__}"
print(f"{indent} {sub_name}")
else:
# 打印模块的子模块
children = list(module.named_children())
if children and depth < 2:
for child_name, child_module in children:
print(f"{indent} {child_name}: {type(child_module).__name__}")
def test_forward_pass(model, input_size=(1, 3, 640, 640)):
"""测试前向传播"""
print("\n" + "="*80)
print("Testing forward pass...")
print("="*80)
model.eval()
with torch.no_grad():
dummy_input = torch.randn(input_size)
try:
det_out, da_out, ll_out = model(dummy_input)
print(f"\n✓ Forward pass successful!")
print(f"\nInput shape: {dummy_input.shape}")
# 检测输出
if isinstance(det_out, tuple):
print(f"\nDetection output (tuple with {len(det_out)} elements):")
for i, out in enumerate(det_out):
if isinstance(out, list):
print(f" Element {i} (list with {len(out)} items):")
for j, item in enumerate(out):
print(f" [{j}] shape: {item.shape}")
else:
print(f" Element {i} shape: {out.shape}")
else:
print(f"\nDetection output shape: {det_out.shape}")
# 分割输出
print(f"\nDrivable area segmentation output shape: {da_out.shape}")
print(f"Lane line segmentation output shape: {ll_out.shape}")
return True
except Exception as e:
print(f"\n✗ Forward pass failed with error:")
print(f" {type(e).__name__}: {e}")
import traceback
traceback.print_exc()
return False
def check_backbone_structure(model):
"""检查backbone结构"""
print("\n" + "="*80)
print("Checking Backbone Structure (YOLOv11)")
print("="*80)
backbone = model.backbone
print(f"\nBackbone type: {type(backbone).__name__}")
print(f"Number of layers: {len(backbone.layers)}")
print(f"Output indices (P3, P4, P5): {backbone.out_indices}")
print("\nBackbone layers:")
for i, layer in enumerate(backbone.layers):
layer_type = type(layer).__name__
# 获取输入输出通道数
if hasattr(layer, 'conv') and hasattr(layer.conv, 'in_channels'):
# Conv 层
in_ch = layer.conv.in_channels
out_ch = layer.conv.out_channels
print(f" [{i:2d}] {layer_type:15s} - {in_ch:4d} -> {out_ch:4d} channels")
elif hasattr(layer, 'cv1') and hasattr(layer, 'cv2'):
# C3k2, C2PSA 等层
in_ch = layer.cv1.conv.in_channels
out_ch = layer.cv2.conv.out_channels
print(f" [{i:2d}] {layer_type:15s} - {in_ch:4d} -> {out_ch:4d} channels")
elif hasattr(layer, 'cv1'):
# SPPF 层
in_ch = layer.cv1.conv.in_channels
out_ch = layer.cv2.conv.out_channels if hasattr(layer, 'cv2') else 'N/A'
if isinstance(out_ch, int):
print(f" [{i:2d}] {layer_type:15s} - {in_ch:4d} -> {out_ch:4d} channels")
else:
print(f" [{i:2d}] {layer_type:15s} - {in_ch:4d} -> {out_ch} channels")
else:
print(f" [{i:2d}] {layer_type}")
# 测试backbone输出
print("\nTesting backbone forward pass...")
test_input = torch.randn(1, 3, 384, 640)
with torch.no_grad():
features = backbone(test_input)
print(f"Backbone outputs {len(features)} feature maps:")
for i, feat in enumerate(features):
print(f" P{i+3} shape: {feat.shape}")
def check_adapters(model):
"""检查通道适配器"""
print("\n" + "="*80)
print("Checking Channel Adapters")
print("="*80)
for i, adapter in enumerate(model.adapters):
in_ch = adapter.conv.in_channels
out_ch = adapter.conv.out_channels
print(f" Adapter {i}: {in_ch} -> {out_ch} channels")
def check_neck_structure(model):
"""检查neck结构"""
print("\n" + "="*80)
print("Checking Neck Structure (YOLOP FPN+PAN)")
print("="*80)
print(f"\nNeck has {len(model.neck)} layers:")
for i, layer in enumerate(model.neck):
layer_type = type(layer).__name__
if hasattr(layer, 'conv'):
if hasattr(layer.conv, 'in_channels'):
in_ch = layer.conv.in_channels
out_ch = layer.conv.out_channels
print(f" [{i:2d}] {layer_type:15s} - {in_ch:4d} -> {out_ch:4d} channels")
elif hasattr(layer, 'Upsample'):
in_ch = layer.Upsample.in_channels
out_ch = layer.Upsample.out_channels
print(f" [{i:2d}] {layer_type:15s} - {in_ch:4d} -> {out_ch:4d} channels")
else:
print(f" [{i:2d}] {layer_type}")
else:
print(f" [{i:2d}] {layer_type}")
def check_heads(model):
"""检查检测和分割头"""
print("\n" + "="*80)
print("Checking Detection and Segmentation Heads")
print("="*80)
# 检测头
print("\nDetection Head:")
print(f" Type: {type(model.detect_head).__name__}")
print(f" Number of classes: {model.detect_head.nc}")
print(f" Number of detection layers: {model.detect_head.nl}")
print(f" Number of anchors per layer: {model.detect_head.na}")
print(f" Anchors shape: {model.detect_head.anchors.shape}")
print(f" Strides: {model.detect_head.stride}")
# 可驾驶区域分割头
print(f"\nDrivable Area Segmentation Head:")
print(f" Number of layers: {len(model.drivable_seg_head)}")
for i, layer in enumerate(model.drivable_seg_head):
layer_type = type(layer).__name__
if hasattr(layer, 'conv'):
if hasattr(layer.conv, 'in_channels'):
in_ch = layer.conv.in_channels
out_ch = layer.conv.out_channels
print(f" [{i}] {layer_type:15s} - {in_ch:4d} -> {out_ch:4d} channels")
else:
print(f" [{i}] {layer_type}")
else:
print(f" [{i}] {layer_type}")
# 车道线分割头
print(f"\nLane Line Segmentation Head:")
print(f" Number of layers: {len(model.lane_seg_head)}")
for i, layer in enumerate(model.lane_seg_head):
layer_type = type(layer).__name__
if hasattr(layer, 'conv'):
if hasattr(layer.conv, 'in_channels'):
in_ch = layer.conv.in_channels
out_ch = layer.conv.out_channels
print(f" [{i}] {layer_type:15s} - {in_ch:4d} -> {out_ch:4d} channels")
else:
print(f" [{i}] {layer_type}")
else:
print(f" [{i}] {layer_type}")
def check_frozen_parameters(model):
"""检查哪些参数被冻结"""
print("\n" + "="*80)
print("Checking Frozen Parameters")
print("="*80)
frozen_params = sum(p.numel() for p in model.parameters() if not p.requires_grad)
trainable_params = sum(p.numel() for p in model.parameters() if p.requires_grad)
total_params = frozen_params + trainable_params
print(f"\nTotal parameters: {total_params:,}")
print(f"Trainable parameters: {trainable_params:,}")
print(f"Frozen parameters: {frozen_params:,}")
print(f"Frozen percentage: {frozen_params/total_params*100:.2f}%")
# 检查各部分的冻结状态
parts = {
'Backbone': model.backbone,
'Adapters': model.adapters,
'Neck': model.neck,
'Detection Head': model.detect_head,
'Drivable Seg Head': model.drivable_seg_head,
'Lane Seg Head': model.lane_seg_head
}
print("\nParameter status by component:")
for name, module in parts.items():
total = sum(p.numel() for p in module.parameters())
frozen = sum(p.numel() for p in module.parameters() if not p.requires_grad)
trainable = total - frozen
status = "FROZEN" if frozen == total else ("TRAINABLE" if frozen == 0 else "PARTIAL")
print(f" {name:20s}: {trainable:>10,} trainable, {frozen:>10,} frozen [{status}]")
def main():
print("="*80)
print("YOLOP with YOLOv11 Backbone - Architecture Check")
print("="*80)
# 创建模型 - 使用 nano 版本
yolo_scale = 'n' # 可以改为 'n', 's', 'm', 'l', 'x'
# yolov11_weights = '' # f'weights/yolo11{yolo_scale}.pt'
yolov11_weights = f'weights/yolo11{yolo_scale}.pt'
print(f"\n1. Creating model with YOLOv11{yolo_scale} backbone...")
if os.path.exists(yolov11_weights):
print(f" Loading pretrained weights from: {yolov11_weights}")
model = YOLOPWithYOLOv11(num_seg_class=2, yolo_scale=yolo_scale, yolo_weights_path=yolov11_weights)
print("✓ Model created with pretrained backbone")
else:
print(f" ⚠ Weights not found at: {yolov11_weights}")
print(" Creating model from scratch...")
model = YOLOPWithYOLOv11(num_seg_class=2, yolo_scale=yolo_scale, yolo_weights_path=None)
print("✓ Model created from scratch")
# 统计参数
print("\n2. Counting parameters...")
total_params, trainable_params = count_parameters(model)
print(f" Total parameters: {total_params:,}")
print(f" Trainable parameters: {trainable_params:,}")
# 检查各部分结构
check_backbone_structure(model)
check_adapters(model)
check_neck_structure(model)
check_heads(model)
# 测试前向传播
success = test_forward_pass(model, input_size=(1, 3, 384, 640))
# 测试冻结backbone功能
if os.path.exists(yolov11_weights):
print("\n" + "="*80)
print(f"Testing Backbone Freezing")
print("="*80)
print("\nFreezing backbone parameters...")
model.freeze_backbone()
check_frozen_parameters(model)
else:
print("\n" + "="*80)
print("Checking Parameters (without pretrained weights)")
print("="*80)
check_frozen_parameters(model)
# 总结
print("\n" + "="*80)
print("Summary")
print("="*80)
if success:
print("✓ All checks passed!")
print("✓ Model architecture is correct and ready for training")
else:
print("✗ Some checks failed. Please review the errors above.")
print("\nModel attributes:")
print(f" model.nc = {model.nc}")
print(f" model.detector_index = {model.detector_index}")
print(f" model.names = {model.names}")
if __name__ == '__main__':
main()