PyTorch训练Faster R-CNN异常:技术图纸检测中整图被识别为目标
技术图纸目标检测Faster R-CNN训练问题排查
我正在用PyTorch从零训练Faster R-CNN做技术图纸目标检测,不使用预训练权重,自有数据由Label Studio标注,包含9类目标加背景共10类。目前训练时损失能下降,但预测时仅输出1-2个框,甚至整图被识别成目标。以下是我的训练代码和数据处理流水线,求排查问题:
训练代码
start_time = time.time() model = fasterrcnn_resnet50_fpn(weights=None, num_classes=10) # Construct optimizer and learning rate scheduler params = [p for p in model.parameters() if p.requires_grad] optimizer = torch.optim.SGD(params, lr=1e-6, momentum=0, weight_decay=0.0005) lr_scheduler = torch.optim.lr_scheduler.StepLR(optimizer, step_size=3, gamma=0.1) NUM_EPOCHS = 6 # Store losses to plot loss in train and validation loss_progress = [] for epoch in range(NUM_EPOCHS): print(f"Beginning epoch: {epoch}") optimizer.zero_grad() total_loss = 0.0 loss_dict = model(images, targets) losses = sum(loss for loss in loss_dict.values()) losses.backward() test_image = images[15] model.eval() with torch.no_grad(): results = model(torch.unsqueeze(test_image, 0)) optimizer.step() lr_scheduler.step() total_loss += losses.item() print(f"Epoch [{epoch+1}/{NUM_EPOCHS}], Loss: {losses.item():.4f}") loss_progress.append(losses.item())
数据处理流水线
images = [] images_dir = os.path.join(root_data, "images") for item in os.listdir(images_dir): item_path = os.path.join(images_dir, item) if os.path.isfile(item_path) and str.endswith(item_path, ".png"): image = Image.open(item_path) image = image.convert("L") image = datapoints.Image(image, dtype=torch.float32) image = F.normalize(image, mean=0.9619, std=0.1660) images.append(image) # Load and process annotations targets = [] annotations_dir = os.path.join(root_data, "annotations") for xml_file in os.listdir(annotations_dir): xml_path = os.path.join(annotations_dir, xml_file) tree = ET.parse(xml_path) root = tree.getroot() boxes = [] labels = [] for object_element in root.findall("object"): label = object_element.find("name").text xmin = int(object_element.find(".//xmin").text) ymin = int(object_element.find(".//ymin").text) xmax = int(object_element.find(".//xmax").text) ymax = int(object_element.find(".//ymax").text) label_encoded = symbols_metadata.get_attribute(label, "value") # Boxes in XYHW format # box = [xmin, ymin, xmin + xmax, ymin + ymax] box = [xmin, ymin, xmax, ymax] boxes.append(box) labels.append(label_encoded) boxes = torch.tensor(data=boxes, dtype=torch.int32) labels = torch.tensor(data=labels, dtype=torch.int64) targets.append({"boxes": boxes, "labels": labels})
问题排查与修复建议
训练代码的致命逻辑错误
- 训练循环结构完全混乱:当前代码把测试、优化器更新、loss统计都放到了循环外面,实际只执行了1次训练步骤,根本没跑完设定的6个epoch。正确结构应将训练流程(forward→backward→step)全部放在
for epoch in range(NUM_EPOCHS):循环体内,测试代码放在训练完成后或epoch间隔执行。 - 学习率与优化器参数不合理:
- 从零训练用
1e-6的学习率过低,Faster R-CNN从零训练建议初始LR设为1e-3或5e-4,同时SGD的momentum需设为0.9才能有效更新参数,当前momentum=0会严重减慢收敛速度。
- 从零训练用
- 缺少训练模式切换:训练时未设置
model.train(),默认处于eval模式,会关闭batch norm、dropout等训练必需模块,导致训练无效。
修正后的训练代码框架:
start_time = time.time() model = fasterrcnn_resnet50_fpn(weights=None, num_classes=10) model.train() # 切换到训练模式 params = [p for p in model.parameters() if p.requires_grad] # 调整优化器参数 optimizer = torch.optim.SGD(params, lr=1e-3, momentum=0.9, weight_decay=0.0005) lr_scheduler = torch.optim.lr_scheduler.StepLR(optimizer, step_size=3, gamma=0.1) NUM_EPOCHS = 6 loss_progress = [] for epoch in range(NUM_EPOCHS): print(f"Beginning epoch: {epoch}") optimizer.zero_grad() total_loss = 0.0 # 训练步骤全部放在循环内 loss_dict = model(images, targets) losses = sum(loss for loss in loss_dict.values()) losses.backward() optimizer.step() lr_scheduler.step() total_loss += losses.item() print(f"Epoch [{epoch+1}/{NUM_EPOCHS}], Loss: {total_loss:.4f}") loss_progress.append(total_loss) # 训练完成后再做测试 model.eval() test_image = images[15] with torch.no_grad(): results = model(torch.unsqueeze(test_image, 0))
数据处理的关键问题
- 单通道图像不兼容Faster R-CNN输入:Faster R-CNN的ResNet-FPN backbone默认期望3通道RGB图像,你将图像转为单通道(
image.convert("L"))会导致特征提取模块工作异常,特征表达能力不足。解决方式是将单通道图像复制3次转为3通道。 - Box数据类型错误:Faster R-CNN要求boxes的dtype为
torch.float32,当前用torch.int32会导致IOU计算等环节出现精度问题或报错。 - 数据顺序不匹配风险:
os.listdir返回的文件顺序不确定,可能导致图像与标注文件不匹配,需确保图像与xml文件名一一对应(如xxx.png对应xxx.xml),并按文件名排序后加载。
修正后的数据处理关键部分:
# 图像加载部分修改为3通道 for item in os.listdir(images_dir): item_path = os.path.join(images_dir, item) if os.path.isfile(item_path) and str.endswith(item_path, ".png"): image = Image.open(item_path) image = image.convert("L") # 复制单通道为3通道 image = Image.merge("RGB", (image, image, image)) image = datapoints.Image(image, dtype=torch.float32) # 对应3通道的归一化参数 image = F.normalize(image, mean=[0.9619, 0.9619, 0.9619], std=[0.1660, 0.1660, 0.1660]) images.append(image) # Boxes数据类型修改为float32 boxes = torch.tensor(data=boxes, dtype=torch.float32)
其他建议
- 增加训练轮数:从零训练Faster R-CNN6个epoch远远不够,建议至少训练30-50个epoch,观察loss和验证集指标变化。
- 添加验证环节:每几个epoch用验证集测试模型性能,避免过拟合或训练无效。
- 调整预测阈值:预测时默认置信度阈值为0.05,会输出大量低置信度框,可手动过滤置信度低于0.5的框:
# 过滤预测结果 pred_boxes = results[0]['boxes'][results[0]['scores'] > 0.5] pred_labels = results[0]['labels'][results[0]['scores'] > 0.5]
内容的提问来源于stack exchange,提问作者compvision-eng
相关产品推荐
相关产品推荐

