简介:面向无人机电力巡检与设备运维场景,这套基于RFBNET的电力杆塔和输电线异常检测Python项目,提供了可直接运行的完整源码与配套文档,适合毕业设计、课程设计及项目开发。项目采用轻量级CNN框架,将RFB模块嵌入SSD网络顶部构成RFB检测器,能够对航拍图像中的目标进行定位与分类,识别锈蚀、堆放杂物、绝缘子破损、覆盖异物等典型异常。资源包共109个文件、约8.35MB,涵盖44个Python源码、20张测试图片、14个RST说明文档以及C/CUDA扩展、Shell脚本、XML配置等,代码按数据预处理、模型定义、训练评估等模块组织,便于参考复现。另附演示视频与运行脚本,已有112人学习使用,源码经过严格测试,可放心在其基础上延伸应用,尤其适合具备一定深度学习基础的开发者作为课程设计或工程项目的起点。
1. 为什么用RFBNET做无人机航拍输电线路异常检测
无人机巡检一个架次就能带回几千张航拍图,杆塔和输电线在画面里往往只占几十个像素,背景是田地、山体、云影。直接用通用目标检测模型,要么把绝缘子漏掉,要么把阴影当作输电线。RFBNET的核心思路是在骨干网络的不同层级上挂一组尺寸不同的空洞卷积,让检测头同时拥有小、中、大三种感受野,对小目标的召回率比同体量的SSD高一截,同时推理速度仍能支撑离线批量处理。它的源码结构不复杂,训练也不依赖超大规模数据集,用几百张标注图就能训出一个可演示的模型,正好撑起毕业设计或课程设计的完整工作量。下面从RFB模块原理讲起,逐步落到可运行的Python代码、训练参数和可交付文档。
2. RFBNET原理与检测头结构:从RFB模块到特征融合
RFBNET是在SSD基础上改进的目标检测网络。它保留了SSD的多尺度特征图检测策略,但把原本简单的3×3卷积换成了RFB(Receptive Field Block)模块。RFB模块通过多分支空洞卷积来模拟人类视觉的感受野变化,让模型对电线这类细长小目标有更强的响应。这一章先讲RFB模块的PyTorch实现,再说明锚框、损失函数和输出解码,四个部分连起来就是检测头的完整数据流。
2.1 Receptive Field Block:空洞卷积如何扩大感受野
普通卷积扩大感受野只能靠堆层数或加大卷积核,前者增加网络深度,后者显著增加参数量。RFB的做法是并行使用不同尺寸的卷积核,并在大卷积核分支里插入空洞卷积。空洞卷积在不增加权重数量的前提下,把卷积核的采样点拉开,等效感受野变大。对于无人机航拍图中宽度只有几个像素的输电线,这种多感受野融合可以同时看到线本身和线周围的绝缘子、杆塔横担,上下文信息更完整。
下面是RFB模块的一个常用实现,结构上分为三个分支,每个分支先做1×1降维,再做对应尺寸的卷积和空洞卷积,最后把特征相加:
import torch import torch.nn as nn class BasicRFB(nn.Module): def __init__(self, in_channels, out_channels, stride=1, scale=0.1): super(BasicRFB, self).__init__() self.reduce = nn.Conv2d(in_channels, out_channels, 1) self.branch1 = nn.Conv2d(out_channels, out_channels, 1) self.branch2 = nn.Conv2d(out_channels, out_channels, 3, stride=stride, padding=1) self.branch3 = nn.Conv2d(out_channels, out_channels, 5, stride=stride, padding=2) self.dilation1 = nn.Conv2d(out_channels, out_channels, 3, padding=1, dilation=1) self.dilation2 = nn.Conv2d(out_channels, out_channels, 3, padding=3, dilation=3) self.dilation3 = nn.Conv2d(out_channels, out_channels, 3, padding=5, dilation=5) self.conv_out = nn.Conv2d(out_channels * 3, out_channels, 1) self.scale = scale def forward(self, x): x = self.reduce(x) b1 = self.branch1(x) b2 = self.branch2(x) b3 = self.branch3(x) d1 = self.dilation1(b1) d2 = self.dilation2(b2) d3 = self.dilation3(b3) out = torch.cat([d1, d2, d3], dim=1) out = self.conv_out(out) return x + self.scale * out这段代码里的三个分支分别处理感受野为1、3、5的原始区域,再通过空洞率为1、3、5的卷积进一步拉伸采样点。参数scale通常取0.1,作用是让RFB模块先充当残差补充,避免初始化时破坏主干网络已有的特征分布。实际训练中把输出通道控制在128到256之间,特征图尺寸为38×38时显存占用和精度比较平衡;通道数过大对小数据集反而容易过拟合。
2.2 RFBNET的锚框设计与先验框参数
RFBNET的检测头使用锚框机制,每个特征图位置生成若干个先验框。SSD的默认锚框把宽高比集中在1、2、3,但输电线是典型的细长目标,宽高比经常到1:8甚至1:12。如果锚框宽高比不够,回归分支需要做很大的形状偏移,训练初期loss下降很慢。因此做电力场景时,一般会根据数据集重新统计目标框的长宽分布,再决定锚框参数。
下表是一组针对无人机航拍电力杆塔、输电线的锚框配置,特征图来自主干网络的不同层级:
| 特征图 | 输入尺寸 | 步长 | 锚框缩放比 | 锚框宽高比 | 主要负责目标 |
|---|---|---|---|---|---|
| P3(38×38) | 300×300 | 8 | 0.05, 0.10, 0.15 | 0.3, 0.5, 1.0 | 输电线、绝缘子 |
| P4(19×19) | 300×300 | 16 | 0.15, 0.25, 0.35 | 0.5, 1.0, 2.0 | 杆塔横担、绝缘子串 |
| P5(10×10) | 300×300 | 32 | 0.35, 0.50, 0.65 | 1.0, 2.0, 3.0 | 杆塔整体、大型异响 |
生成锚框时,中心点坐标是特征图像素坐标乘以步长,宽高是缩放比乘以输入尺寸再乘以宽高比。要注意,缩放比必须覆盖航拍图中目标的真实像素占比。拿一张4000×3000的原图举例,如果塔身占整图的6%,缩放到300×300后大约是18像素,那么0.06这个缩放比就需要出现在锚框列表里,否则模型根本看不到这个尺度。
2.3 损失函数与难样本挖掘
RFBNET的损失分为定位损失和分类损失。定位分支输出的是锚框中心点、宽高的偏移量,使用Smooth L1损失;分类分支输出每个锚框对每个类别的置信度,使用交叉熵损失。总损失是两部分加权相加,权重一般为1:1。
在电力杆塔数据集里,背景锚框数量远大于目标锚框,如果不做处理,模型会学成“什么都检测不到”也能把loss压得很低。常用的处理方式是硬负样本挖掘:每轮迭代按分类loss从高到低排序,挑出一定比例的负样本参与反向传播,让背景和前景的比例保持在3:1左右。下面这段伪代码展示了训练循环中的loss组织方式:
for images, targets in dataloader: pred = model(images) conf_loss, loc_loss = build_loss(pred, targets, num_classes=3) # 难样本挖掘:只看分类损失,挑出排名靠前的负样本 neg_loss = conf_loss[targets["is_neg"]] _, neg_idx = torch.topk(neg_loss, k=min(512, len(neg_loss))) hard_neg_mask = torch.zeros_like(conf_loss, dtype=torch.bool) hard_neg_mask[targets["is_neg"]][neg_idx] = True pos_mask = targets["is_pos"] selected = pos_mask | hard_neg_mask final_loss = loc_loss.sum() + conf_loss[selected].mean() final_loss.backward()这里is_pos和is_neg来自锚框与真实框的匹配结果:IoU大于0.5判定为正样本,小于0.3判定为负样本,介于两者之间的锚框不参与分类损失。参数512表示最多回传512个困难负样本的梯度,这个数值可以根据数据集大小调整。小数据集建议降到256,防止难样本对噪声标注过度敏感。
2.4 检测头输出解析
模型输出的是一个形状为[batch, num_anchors, 4 + num_classes]的特征张量。每个锚框占据一行,前4个值是中心点和宽高的偏移量,后面的值是对每个类别的置信度。推理时要把偏移量解码回真实坐标,解码公式与训练时使用的编码方式严格对应,否则检测框会全部漂移。
def decode_boxes(pred, anchors, img_w, img_h): cx = pred[..., 0] * anchors[..., 2] + anchors[..., 0] cy = pred[..., 1] * anchors[..., 3] + anchors[..., 1] w = torch.exp(pred[..., 2]) * anchors[..., 2] h = torch.exp(pred[..., 3]) * anchors[..., 3] xmin = (cx - w / 2) * img_w ymin = (cy - h / 2) * img_h xmax = (cx + w / 2) * img_w ymax = (cy + h / 2) * img_h return torch.stack([xmin, ymin, xmax, ymax], dim=-1)解码后的坐标单位是像素。由于训练时图像会先缩放到300×300,锚框的宽高也在这个尺度下设计,所以解码后要乘以原图的宽高比例才能得到与原图对应的框。这里直接用anchors[..., 2]表示锚框宽度,实际源码里通常存放的是相对于特征图的宽高,需要先乘以步长。多尺度特征图各自对应一个解码分支,代码里写成一个函数时,要注意传入的anchors必须来自同一层特征图。
3. 用Python搭建RFBNET训练环境与数据准备
模型结构能跑通只是第一步,真正让RFBNET学会识别电力杆塔和输电线,需要把Python环境、数据集、增强策略和训练参数串起来。这一章从零开始搭建训练流程,所有命令都在Linux或Windows的conda终端下可执行。
3.1 创建Python虚拟环境与安装依赖
用conda创建独立环境,避免和系统Python环境互相干扰。RFBNET的常见稳定组合是Python 3.8加PyTorch 1.9,这两个版本匹配度最好,网络层定义和后续转ONNX都不会出兼容性问题。
conda create -n rfbenv python=3.8 -y conda activate rfbenv pip install torch==1.9.0+cu111 torchvision==0.10.0+cu111 -f https://download.pytorch.org/whl/torch_stable.html pip install opencv-python pyyaml tqdm tensorboard albumentations安装完成后,用python -c "import torch; print(torch.__version__)"确认CUDA可用。如果显卡驱动支持CUDA 11.3以上,也可以直接安装更新的PyTorch 2.x版本,但代码里nn.Module的接口基本不变,只有个别函数名需要兼容。依赖版本不要盲目升级,RFBNET源码里如果用了torchvision.ops.nms,PyTorch 1.9以上都包含,低于这个版本会报错。
3.2 无人机航拍数据集的VOC格式整理
课程设计和毕业设计通常不会提供现成数据集,需要自己从公开航拍素材或实拍视频中截帧标注。标注工具推荐labelImg,它支持Pascal VOC格式的XML输出,操作成本最低。采集图片时尽量覆盖不同高度、不同天气和不同光照,无人机俯拍角度下杆塔和输电线容易重叠,这种样本要保留,不能因为难标就删掉。
数据集目录按照VOC结构组织:
data/ ├── JPEGImages/ │ ├── frame_0001.jpg │ └── frame_0002.jpg ├── Annotations/ │ ├── frame_0001.xml │ └── frame_0002.xml └── ImageSets/ └── Main/ ├── train.txt └── val.txtXML里最核心的是object节点,每个目标类别的定义需要和训练代码里的类别列表完全一致。习惯上用三个类别:tower表示杆塔,wire表示输电线,defect表示绝缘子破损、异物悬挂等异常。如果数据中某类样本太少,可以先合并成anomaly一个类,再在检测后处理阶段做规则细分。
<annotation> <filename>frame_0001.jpg</filename> <size> <width>4000</width> <height>3000</height> </size> <object> <name>wire</name> <bndbox> <xmin>1862</xmin> <ymin>220</ymin> <xmax>2010</xmax> <ymax>260</ymax> </bndbox> </object> </annotation>标注时有一个容易被忽略的问题:输电线贴着杆塔边缘穿过时,标注框容易被裁断。框的边界最好完整包含输电线的可见部分,不要把阴影和背景标进去。标注完用脚本统计每个类别的框数量和面积分布,如果wire类的框面积普遍小于图像面积的1%,说明小目标样本占比高,需要在下个环节重点做针对性增强。
3.3 面向小目标的增强策略:裁剪、拼接与马赛克
直接resize会把细长的输电线缩成几个像素,性能损失很大。无人机航拍图的增强策略和通用目标检测不一样,不能只靠随机翻转和颜色抖动,要优先保证目标尺度不丢失。常见的做法是大图裁剪,把原图上的一个区域裁成300×300的patch,让输电线在patch中的相对尺度接近训练目标尺度。
import albumentations as A transform = A.Compose([ A.RandomSizedBBoxSafeCrop(height=300, width=300, erosion_rate=0.2), A.HorizontalFlip(p=0.5), A.RandomBrightnessContrast(p=0.3), A.Normalize(mean=(0.485, 0.456, 0.406), std=(0.229, 0.224, 0.225)), ], bbox_params=A.BboxParams(format='pascal_voc', label_fields=['labels']))RandomSizedBBoxSafeCrop会随机选一个包含完整目标框的区域进行裁剪,避免把目标切掉一半。erosion_rate=0.2表示裁剪边界可以向目标框内缩进20%,增加目标位置的多样性。如果显存允许,还可以叠加Mosaic增强,把四张图拼接成一张,让模型在一个batch里看到更多小目标。这里要注意,Mosaic增强会让目标框跨越拼接边界,锚框匹配逻辑需要能处理缩放后的空框,否则训练会报错。
3.4 训练参数表与启动命令
训练参数的选择直接决定模型能不能收敛。下面这组参数是无人机电力场景下调试过的基准值,单卡RTX 3060上跑300轮约需要两小时。
| 参数名 | 推荐值 | 说明 |
|---|---|---|
input_size | 300×300 | 输入分辨率,越高小目标越清晰但越慢 |
batch_size | 16 | batch过大容易显存溢出,8到32之间尝试 |
initial_lr | 0.001 | 使用warmup后逐步恢复 |
milestones | [150, 250] | 在第150轮和第250轮lr乘以0.1 |
num_classes | 3 | tower、wire、defect |
max_epochs | 300 | 小数据集300轮左右过拟合风险可控 |
neg_pos_ratio | 3.0 | 正负样本数量比,难样本挖掘参数 |
启动训练的命令,以常见的源码目录结构为例:
python tools/train.py \ --dataset voc \ --data-path data/ \ --backbone vgg16 \ --input-size 300 \ --batch-size 16 \ --lr 0.001 \ --epochs 300 \ --num-workers 4 \ --cuda命令里的--backbone vgg16是RFBNET论文里的标准配置,也可以用MobileNetV2替换以提升推理速度。注意--num-workers不要超过CPU物理核数,否则数据加载会抢占GPU训练资源,导致每个epoch时间反而变长。
注意:这里的300×300输入尺寸和2.2节的锚框缩放比是配套的。如果为了提升小目标精度把输入改成512×512,锚框宽高和中心化解码公式里的缩放因子也要按比例计算,否则会出现所有检测框偏移到图像边缘的现象。
3.5 训练日志与loss曲线解读
训练时终端会打印每个epoch的loc_loss、conf_loss和total_loss。正常收敛时,conf_loss应该在前50轮快速下降,之后进入平台期;loc_loss下降速度较慢,属于正常现象。如果total_loss出现周期性反弹,先检查学习率是不是用了阶梯式衰减,以及是否启用了warmup。如果从头到尾loss都在0.4附近抖动,很可能锚框缩放比没有覆盖目标尺寸,需要回到2.2节的表格重新配置。
训练结束后,用验证集计算mAP:
python tools/evaluate.py \ --dataset voc \ --data-path data/ \ --weights weights/rfbenet_best.pth \ --iou-threshold 0.5mAP低于0.5时不要急着调模型结构,优先检查标注:框有没有偏移、类别有没有混标、输电线遮挡严重的样本是否被删掉。很多情况下模型表现差不是网络问题,而是数据问题。
4. 异常检测推理:加载权重,对航拍图做目标检测
训练完成后的RFBNET权重文件就是一个可用的检测器。把权重加载到Python脚本中,对新的航拍图做前向推理、解码、NMS后处理,再把检测结果转换成异常告警,这是整个项目最接近“落地”的部分。这一章的代码可以直接抄进自己的detect.py。
4.1 模型加载与图像预处理
推理时模型的输入和训练时保持一致,都要先缩放到训练指定的input_size。加载模型的时候不要直接torch.load整个pth,而是先初始化网络结构,再导入state_dict,这样即使Python环境变了也不会暴露源码路径问题。
import torch import cv2 import numpy as np def load_model(weights_path, num_classes=3): from models.detector import RFBNet model = RFBNet(num_classes=num_classes) state_dict = torch.load(weights_path, map_location='cuda') model.load_state_dict(state_dict) model.eval() return model.cuda() def preprocess(image_path, input_size=300): img = cv2.imread(image_path) img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) img = cv2.resize(img, (input_size, input_size)) img = img.astype(np.float32) / 255.0 img = torch.from_numpy(img).permute(2, 0, 1).unsqueeze(0) return img.cuda()load_model里的RFBNet类要放在models/detector.py中,类名和训练时的完全一致。预处理时注意cv2.imread读出来是BGR顺序,虽然很多代码不转也能跑,但RGB顺序会微调模型响应,用标准ImageNet均值和方差做归一化时最好统一通道顺序。
4.2 后处理:NMS与阈值调节
模型输出可能包含大量重叠的检测框,需要通过置信度阈值和NMS筛选。RFBNET的推理流程是先解码偏移量,再按置信度过滤,最后做非极大值抑制:
def postprocess(pred, anchors, conf_thresh=0.3, nms_thresh=0.45, img_w=300, img_h=300): boxes = decode_boxes(pred[..., :4], anchors, img_w, img_h) scores = pred[..., 4:] class_ids = scores.argmax(dim=-1) max_scores = scores.max(dim=-1).values keep = max_scores > conf_thresh boxes, scores, class_ids = boxes[keep], max_scores[keep], class_ids[keep] keep_idx = torchvision.ops.nms(boxes, scores, iou_threshold=nms_thresh) return boxes[keep_idx], scores[keep_idx], class_ids[keep_idx]conf_thresh和nms_thresh对结果的影响可以用一个表格概括:
| 参数场景 | conf_thresh | nms_thresh | 效果 |
|---|---|---|---|
| 漏检多 | 下调到0.2 | 0.5 | 召回率提升,误报增多 |
| 误检多 | 调到0.4 | 0.4 | 精确率提升,漏检风险增加 |
| 杆塔和输电线重叠 | 0.3 | 0.3 | 减少两个类别互相抑制的情况 |
在航拍图中,输电线经常和杆塔框重叠,如果把nms_thresh设得太大,输电线框会被杆塔框抑制掉。一般会按类别分别做NMS,先处理tower,再处理wire,避免跨类别抑制。
4.3 异常判定:检测框如何变成故障告警
异常检测可以落在两个层面:一个是模型直接检测defect类别,另一个是根据tower和wire的几何关系推导异常。第二种方法更适合数据不足的项目,因为正常样本容易标注,而异常样本稀缺。
def check_anomaly(detections, img_w=300): towers = [d for d in detections if d["class_id"] == 0] wires = [d for d in detections if d["class_id"] == 1] defects = [] for t in towers: t_box = t["box"] has_wire = False for w in wires: if box_iou(t_box, w["box"]) > 0.05: has_wire = True break if not has_wire: defects.append({"type": "missing_wire", "box": t_box}) return defects这个规则的逻辑是:杆塔区域附近应该有输电线穿过,如果检测不到任何wire框,就判定为“缺失或断线”。具体项目中还会加上线长连续性判断:把同一条线路上的wire框按端点距离串联,如果两个框的端点距离超过阈值,说明线可能有断裂。规则简单但非常实用,毕业设计答辩时可以解释成“知识驱动的后处理”,和模型检测形成互补。
4.4 推理阶段的常见问题与排查
推理时最常见的是检测结果全是空框。第一步检查输入图像的尺寸是否和训练一致,第二步打印pred张量的数值范围。如果所有置信度都等于0,说明模型加载了未训练的权重,或者model.eval()被漏掉导致dropout层仍然生效。
显存不足时,可以把输入尺寸从300降到256,同时把batch size设为1。但要注意,decode_boxes函数里的锚框缩放比是按输入尺寸生成的,更换输入尺寸后必须同步缩放锚框参数。还有一种情况是航拍图分辨率太高,超过3000×4000,直接resize会丢失小目标。一般会先用滑窗把大图切成600×600的块,每个块独立推理,最后把所有块的检测结果合并回原图坐标,这样能在不牺牲精度的前提下处理原分辨率。
5. 把RFBNET源码改造成毕业设计可交付的工程
很多课程设计的代码能跑通,但不好好整理也拿不到高分。毕业设计答辩时,评审看到的不只是模型效果,更是代码组织、文档完整度和可复现性。这一章把源码整理和文档写作的关键步骤列出来,按这个顺序改造自己的项目,交付质量会明显上一个台阶。
5.1 工程目录分层与代码规范
不要把 所有Python文件堆在根目录下。一般会按config、models、utils、tools、docs五层来组织源码,训练和推理入口统一放在tools里,模型定义放在models里,数据预处理、增强、可视化这种可复用函数放进utils。
rfbenet-powerline/ ├── config/ │ └── rfbenet.yaml ├── models/ │ ├── __init__.py │ ├── rfb.py │ └── detector.py ├── utils/ │ ├── dataset.py │ ├── augmentation.py │ ├── decode.py │ └── metrics.py ├── tools/ │ ├── train.py │ ├── evaluate.py │ └── detect.py ├── data/ ├── weights/ └── docs/ ├── 需求分析.md ├── 总体设计.md └── 测试报告.mdconfig/rfbenet.yaml里放所有超参数,训练脚本启动时用yaml.safe_load读取。这样做的好处是,答辩现场调整参数时不需要改代码,只需改配置文件,演示起来更专业。代码注释不要求每行都有,但类名、函数名、关键参数含义必须写清楚,尤其是锚框生成和loss计算这种分支多的部分。
5.2 从README到课程设计文档:文档怎么写
“源码+文档”的交付物里,文档不是把注释复制一遍,而是按设计流程组织。下面的表格可以当作课程设计文档的骨架:
| 章节 | 常见内容 | 关联代码 |
|---|---|---|
| 需求分析 | 无人机巡检场景、异常类型定义、性能预期 | README.md |
| 总体设计 | RFBNET结构图、检测流程、模块划分 | models/、config/ |
| 详细设计 | 数据增强策略、锚框生成、loss计算 | utils/、tools/train.py |
| 测试结果 | 训练曲线、mAP、检测样例截图 | weights/、docs/测试报告.md |
| 环境部署 | Python版本、依赖清单、运行命令 | requirements.txt |
写README.md时,开头第一段要说明项目解决什么问题,然后放一张检测效果图,接着是目录结构和快速开始命令。不要只写“本代码实现RFBNET”,要写清楚“实现了基于RFBNET的无人机航拍电力杆塔、输电线异常检测,能识别三类目标,输出带标记的结果图”。
- 需求分析里,把“异常检测”具体化:是检测绝缘子破损、输电线断股、还是异物悬挂。定义得越细,后面的设计越容易写。
- 总体设计配一张数据流图,技术路线确定为“RFBNET检测目标框 + 规则判断异常”,文档逻辑就完整了。
- 详细设计要写锚框参数为什么这样设置,可以把2.2节的表格原样放进去,增加可信度。
5.3 用TensorBoard和PR曲线验证效果
训练时的可视化结果对答辩非常有用。训练脚本里加入TensorBoard的SummaryWriter,记录每个epoch的loss、lr和mAP:
from torch.utils.tensorboard import SummaryWriter writer = SummaryWriter('runs/rfbenet_exp1') for epoch in range(epochs): train_loss = train_one_epoch(...) writer.add_scalar('loss/train', train_loss, epoch) writer.add_scalar('lr', current_lr, epoch) if epoch % 10 == 0: mAP = evaluate(...) writer.add_scalar('metrics/mAP@0.5', mAP, epoch)启动命令是tensorboard --logdir runs,浏览器打开后会看到loss下降曲线和mAP上升曲线。除了曲线,再保存几张检测结果图,把正常样本和异常样本对照排列,能直观展示模型在杆塔、输电线、缺陷三类目标上的表现。PR曲线可以调用sklearn.metrics.average_precision_score生成,但要注意先按类别分别计算,再求均值,这才是mAP的真正含义。
5.4 答辩验收自测清单
最后说一个交付前会走一遍的自测清单,这些项检查完,项目基本就能达到答辩要求:
| 检查项 | 验证方法 | 目标值 |
|---|---|---|
| 环境可复现 | 在干净环境执行pip install -r requirements.txt后训练 | 无报错 |
| 推理速度 | 对单张4000×3000大图连续推理20次取平均 | 单张小于500ms |
| 检测精度 | 运行tools/evaluate.py输出mAP | mAP≥0.70 |
| 文档完整度 | 按README里的命令从数据准备到检测完整走一遍 | 每一步有输出 |
| 代码规范 | 检查models/和utils/中函数是否有注释和类型标注 | 无裸奔函数 |
答辩前用固定随机种子重训一次,把训练命令和输出日志一并放进附录,评审老师能直接复现,比口头解释“我调过参”更有说服力。
本文还有配套的精品资源,点击获取