|
|
""" |
|
|
检查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'): |
|
|
|
|
|
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'): |
|
|
|
|
|
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'): |
|
|
|
|
|
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}") |
|
|
|
|
|
|
|
|
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) |
|
|
|
|
|
|
|
|
yolo_scale = 'n' |
|
|
|
|
|
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)) |
|
|
|
|
|
|
|
|
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() |
|
|
|