You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 06:45:03