[ultralytics/yolov5]在yolov5中添加了自定义的模块,训练结果始终为0

2023-12-14 912 views
5
问题

我在yolov5l.yaml中添加了自定义模块

# YOLOv5 🚀 by Ultralytics, GPL-3.0 license

# Parameters
nc: 80  # number of classes
depth_multiple: 1.0  # model depth multiple
width_multiple: 1.0  # layer channel multiple
anchors:
  - [ 10,13, 16,30, 33,23 ]  # P3/8
  - [ 30,61, 62,45, 59,119 ]  # P4/16
  - [ 116,90, 156,198, 373,326 ]  # P5/32

# YOLOv5 v6.0 backbone
backbone:
  # [from, number, module, args]
  [ [ -1, 1, Conv, [ 64, 6, 2, 2 ] ],  # 0-P1/2
    [ -1, 1, SplitModule,[ 'test' ] ],
    [ -1, 1, Conv, [ 128, 3, 2 ] ],  # 2-P2/4
    [ -1, 3, C3, [ 128 ] ],
    [ -1, 1, Conv, [ 256, 3, 2 ] ],  # 4-P3/8
    [ -1, 6, C3, [ 256 ] ],
    [ -1, 1, Conv, [ 512, 3, 2 ] ],  # 6-P4/16
    [ -1, 9, C3, [ 512 ] ],
    [ -1, 1, Conv, [ 1024, 3, 2 ] ],  # 8-P5/32
    [ -1, 3, C3, [ 1024 ] ],
    [ -1, 1, MergeModule,[ 'test' ] ],
    [ -1, 1, SPPF, [ 1024, 5 ] ],  # 11
  ]

# YOLOv5 v6.0 head
head:
  [ [ -1, 1, Conv, [ 512, 1, 1 ] ],
    [ -1, 1, nn.Upsample, [ None, 2, 'nearest' ] ],
    [ [ -1, 7 ], 1, Concat, [ 1 ] ],  # cat backbone P4
    [ -1, 3, C3, [ 512, False ] ],  # 15

    [ -1, 1, Conv, [ 256, 1, 1 ] ],
    [ -1, 1, nn.Upsample, [ None, 2, 'nearest' ] ],
    [ [ -1, 5 ], 1, Concat, [ 1 ] ],  # cat backbone P3
    [ -1, 3, C3, [ 256, False ] ],  # 19 (P3/8-small)

    [ -1, 1, Conv, [ 256, 3, 2 ] ],
    [ [ -1, 16 ], 1, Concat, [ 1 ] ],  # cat head P4
    [ -1, 3, C3, [ 512, False ] ],  # 22 (P4/16-medium)

    [ -1, 1, Conv, [ 512, 3, 2 ] ],
    [ [ -1, 12 ], 1, Concat, [ 1 ] ],  # cat head P5
    [ -1, 3, C3, [ 1024, False ] ],  # 25 (P5/32-large)

    [ [ 19, 22, 25 ], 1, Detect, [ nc, anchors ] ],  # Detect(P3, P4, P5)
]

为了验证吸力,这两个模块中都没有做操作,在forward函数中直接返回输入的Tensor

class SplitModule(nn.Module):
    # Split a module into a list of modules
    def __init__(self, arg1):
        super(SplitModule, self).__init__()
        print("SplitModule: ", arg1)

    def forward(self, x):
        # print("call SplitModule forward")
        return x

class MergeModule(nn.Module):
    def __init__(self, arg1):
        super(MergeModule, self).__init__()
        print("MergeModule: ", arg1)

    def forward(self, x):
        # print("call MergeModule forward")
        return x

相应的我在yolo.py的parser_model中也做了修改,将这两个模块的通道数和他们的上层保持了一致

elif m is SplitModule:
            c2 = ch[-1]
        elif m is MergeModule:
            c2 = ch[-1]
        else:
            c2 = ch[f]

但是,当我使用过的框架训练coco128数据集时我发现,同样使用yolov5l.pt的预训练修改权重,修改后的训练结果惨不忍睹。 all 128 929 0.00368 0.0421 0.0142 0.00567请问在yolo中正确的添加模块的方法是什么?为什么我添加了这个“透明”的模块,对结果的影响这么大?

回答

8

你是从头开始训练的吗?你说你使用了预训练模型,但是如果你修改了配置,密钥将不匹配?

1

我也在学习YOLO源代码,希望能扩展成3D的功能。根据我对YOLO的一知半解的理解,你中间插入2个MODULE以后,把直接权重读进去,权重对于各层的匹配就全错了。这是我对这句话的理解“但是如果你修改了配置,密钥将不匹配”。估计你需要做一个LAYER的循环,一层的把权重弄对。

8

我也在研究YOLO的源码,希望将其功能拓展到3D领域。基于我对YOLO的有限理解,如果插入两个模块并直接加载权重,每层权重的匹配将是完全错误的。这就是我从“但是如果修改配置,键将不匹配”这句话中理解的。我认为您可能需要进行层循环以逐层匹配权重

6

我不确定每一层是否有自己的名称或ID,我认为他们仍然可以正确分配权重。

4

我仔细检查了。我认为目前您需要匹配的钥匙来进行重量转移。如果您修改并传递了一个 yaml,该 yaml 意味着与 .pt 相比不同数量的块,则作为键一部分的块索引在第一个插入的块之后根本不匹配。

我认为要实现您想要的目标,您需要将默认的传输机制替换为能够正确分配权重的机制。

2

我也想知道“from”[from, number, module, args]是什么意思,大部分是-1,有的是[-1, 16],什么意思?

9

它指示该块的输入来自哪里。“-1”是前一个块。[] 将多个块输出作为输入。

1

[-1 5] [-1 16] 是什么意思?5 表示第 5 层,16 表示第 16 层,[-1 5] 让我很困惑。

8

我发现了这个问题,所以我尝试在没有预训练模型的情况下训练新模型,然后得到了正确的结果。

8

@tapio Huang 很高兴听到从头开始的训练带来了正确的结果!使用修改后的配置进行训练可能会导致权重匹配不正确,因此在不使用预训练权重的情况下训练新模型是正确的方法。如果您还有任何其他问题或需要帮助,请随时询问。保持良好的工作!