4. YOLO模型文件解析与配置
任务目标
本案例要求解析YOLO的.cfg配置文件和.weights权重文件,理解网络结构参数,并完成模型加载。通过本案例,你将掌握YOLO模型文件的结构与解析方法,为后续目标检测任务奠定基础。
环境依赖
- OpenCV ≥ 4.4(需包含dnn模块)
- Python ≥ 3.7
- YOLOv4配置文件(yolov4.cfg)与权重文件(yolov4.weights)
核心实现
以下代码完成三个核心任务:解析.cfg文件提取网络层参数、读取.weights文件头部信息、使用OpenCV DNN模块加载模型并验证网络结构。
import cv2
import numpy as np
from collections import OrderedDict
def parse_cfg(cfg_path):
"""解析YOLO .cfg文件,返回层参数字典列表"""
with open(cfg_path, 'r') as f:
lines = f.read().split('\n')
layers = []
current_layer = None
for line in lines:
line = line.strip()
if not line or line.startswith('#'):
continue
if line.startswith('['):
if current_layer is not None:
layers.append(current_layer)
current_layer = OrderedDict()
current_layer['type'] = line.strip('[]')
else:
if current_layer is not None and '=' in line:
key, value = line.split('=')
current_layer[key.strip()] = value.strip()
if current_layer is not None:
layers.append(current_layer)
return layers
def parse_weights_header(weights_path):
"""读取.weights文件头部,返回版本号与网络参数数量"""
with open(weights_path, 'rb') as f:
header = np.fromfile(f, dtype=np.int32, count=4)
if header[0] == 0:
version = 1
num_layers = header[1]
else:
version = header[0]
num_layers = header[1]
return version, num_layers
def load_yolo_model(cfg_path, weights_path):
"""加载YOLO模型并返回网络对象"""
net = cv2.dnn.readNetFromDarknet(cfg_path, weights_path)
return net
if __name__ == "__main__":
cfg_path = "yolov4.cfg"
weights_path = "yolov4.weights"
# 1. 解析配置文件
layers = parse_cfg(cfg_path)
print(f"配置文件共包含 {len(layers)} 层")
# 打印前3层与最后3层结构
for i, layer in enumerate(layers[:3]):
print(f"层 {i}: type={layer['type']}, 参数={dict(layer)}")
print("...")
for i, layer in enumerate(layers[-3:]):
idx = len(layers) - 3 + i
print(f"层 {idx}: type={layer['type']}, 参数={dict(layer)}")
# 2. 解析权重文件头部
version, num_layers = parse_weights_header(weights_path)
print(f"权重文件版本: {version}, 层数: {num_layers}")
# 3. 加载模型
net = load_yolo_model(cfg_path, weights_path)
# 4. 验证网络结构
layer_names = net.getLayerNames()
print(f"OpenCV加载网络层数: {len(layer_names)}")
# 获取输出层
output_layers = [layer_names[i - 1] for i in net.getUnconnectedOutLayers()]
print(f"输出层: {output_layers}")
# 检查网络是否可用
dummy_input = np.zeros((1, 3, 416, 416), dtype=np.float32)
net.setInput(dummy_input)
outputs = net.forward(output_layers)
print(f"前向传播输出特征图数量: {len(outputs)}")
for i, out in enumerate(outputs):
print(f"输出 {i}: 形状={out.shape}")
运行与验证
运行方式:
# 下载YOLOv4模型文件(若未准备)
wget https://github.com/AlexeyAB/darknet/releases/download/darknet_yolo_v3_optimal/yolov4.weights
wget https://raw.githubusercontent.com/AlexeyAB/darknet/master/cfg/yolov4.cfg
# 执行脚本
python yolo_parser.py
验证判定标准:
- 配置文件解析输出应包含约162层(YOLOv4标准结构),且每层包含type字段(如convolutional、route、yolo等)
- 权重文件头部版本号应为0(表示使用旧版格式),层数应与cfg解析结果一致
- OpenCV加载后网络层数应与cfg解析层数一致(或略有差异,因OpenCV会合并部分层)
- 输出层应包含3个yolo层,前向传播输出特征图数量为3,形状分别为(1, 255, 13, 13)、(1, 255, 26, 26)、(1, 255, 52, 52)(输入416×416时)
关键说明
- cfg文件解析逻辑按行处理,以
[type]标记新层开始,key=value格式存储参数 - weights文件头部前4个int32值:第一个为版本号(0表示旧版),第二个为层数,后两个为训练相关参数(可忽略)
- OpenCV的
readNetFromDarknet内部已实现cfg与weights的完整解析,本案例的解析函数用于教学理解文件结构 - 若输出层数量或形状与预期不符,请检查cfg与weights文件是否匹配(版本需一致)
常见问题
- 加载失败:确认OpenCV版本≥4.4,且文件路径正确
- 层数不匹配:cfg与weights必须来自同一模型版本,混用会导致加载错误
- 内存不足:YOLOv4权重约245MB,确保系统有足够内存