YOLOX-Darknet代码阅读

class Darknet(nn.Module):
    # number of blocks from dark2 to dark5.
    depth2blocks = {21: [1, 2, 2, 1], 53: [2, 8, 8, 4]}
    # ========================  darknet stem ========================
    # Q: stem 是什么意思
    # A: stem 是 darknet 的第一层,它是一个 Focus 层。
    # Q: 什么是 Focus 层
    # A: Focus 层是一个卷积层,它的作用是将输入的图片从 3 通道变成 64 通道。
    # Q: stem_out_channels 为32,那Focus层输出的通道数应该是多少
    # A: 32*4=128
    # Q: 为什么要需要*4
    # Q: stem_out_channels 是什么意思
    # A: stem_out_channels 是 darknet 的输出通道数,它决定了 darknet layer2 到 layer5 的通道数。
    # Q: layer2 和 layer5 是什么意思
    # A: layer2 和 layer5 是 darknet 的层数,layer2 是 darknet 的第二层,layer5 是 darknet 的第五层。
    # Q: 他们的结构是什么样的
    # A: layer2 是一个卷积层,layer5 是一个 SPPBottleneck 层。
    # ========================  darknet out_features ========================
    # Q: out_features 是什么意思
    # A: out_features 是 darknet 的输出层名字。
    # Q: 为什么要有 out_features
    # A: 因为 darknet 有多个输出层,我们可以选择需要的输出层。
    # Q: 这几个输出层的作用是什么
    # A: dark2 的输出层用于检测小目标,dark3 的输出层用于检测中目标,dark4 的输出层用于检测大目标。
    # Q: 这三个层会融合吗
    # A: 会,darknet 的输出层会融合,然后再进行后处理。
    # ========================  darknet 结构 ========================
    # Q: 请帮我梳理一下 darknet 的结构,请详细描述每一层的作用
    # A: darknet 的结构如下:
    # A: darknet stem (Focus)
    # A: darknet layer2 (BaseConv)
    # A: darknet layer3 (BaseConv)
    # A: darknet layer4 (BaseConv)
    # A: darknet layer5 (SPPBottleneck)
    # A: darknet out_features (Tuple[str])

    
    def __init__(
        self,
        depth,
        in_channels=3,
        stem_out_channels=32,
        out_features=("dark3", "dark4", "dark5"),
    ):
        """
        Args:
            depth (int): depth of darknet used in model, usually use [21, 53] for this param.
            in_channels (int): number of input channels, for example, use 3 for RGB image.
            stem_out_channels (int): number of output channels of darknet stem.
                It decides channels of darknet layer2 to layer5.
            out_features (Tuple[str]): desired output layer name.
        """
        super().__init__()
        # Q: 为什么要 assert out_features
        # A: 因为 out_features 是 darknet 的输出层名字,我们需要它。
        # Q: 这里的 assert 是什么意思
        # A: assert 是断言,它的作用是判断 out_features 是否为 True,如果不为 True,就会报错。
        # Q: assert的内容为空,会发生什么
        # A: 会报错,报错内容是 "please provide output features of Darknet"。
        assert out_features, "please provide output features of Darknet"
        self.out_features = out_features
        # ========================  nn.Sequential ========================
        # Q: nn.Sequential 是什么意思
        # A: nn.Sequential 是一个容器,它可以将多个层组合在一起。
        # Q: 第二个参数是什么意思,为什么要加 *,为什么要加 self.make_group_layer,make_group_layer 是什么意思
        # A: 第二个参数是 darknet 的第一层,它是一个 Focus 层。
        # A: * 是解包操作,它的作用是将 self.make_group_layer 的返回值解包,然后传给 nn.Sequential。
        # Q: 我认为第一个参数BaseConv是第一层,为什么你说是Focus层
        # A: Focus 层是一个卷积层,它的作用是将输入的图片从 3 通道变成 64 通道。
        # Q: 所以到底是哪一层是Focus层,是nn.Sequential的第一层还是第二层
        # A: Focus 层是 nn.Sequential 的第一层。
        # Q: 为什么要加 self.make_group_layer
        # A: 因为 darknet 的第二层到第五层都是由多个卷积层组成的,self.make_group_layer 的作用是创建一个 darknet 的层。
        # Q: 可不可以理解成nn.Sequential中的第一个参数是Focus层,第二个参数是darknet的第二层到第五层
        # A: 可以,nn.Sequential 中的第一个参数是 Focus 层,第二个参数是 darknet 的第二层到第五层。
        # Q: 但是nn.Sequential中的第二个参数只有一个num_blocks,为什么
        # A: 因为 Focus 层只有一个卷积层,所以只需要一个 num_blocks。
        # ========================  self.make_group_layer ========================
        # A: self.make_group_layer 是一个函数,它的作用是创建一个 darknet 的层。
        # A: darknet 的层是由多个卷积层组成的,self.make_group_layer 的作用是创建一个 darknet 的层。
        # Q: 关于 self.make_group_layer 的问题,我还有一些疑问,num_blocks 是什么意思,stride 是什么意思
        # A: num_blocks 是 darknet 的层数,stride 是 darknet 的步长。
        # Q: 在make_group_layer中,我没有看到卷积核的大小,为什么
        # A: 卷积核的大小在 BaseConv 中定义了,这里不需要再定义。
        # Q: 可不可以理解make_group_layer自动调用了BaseConv
        # A: 可以,make_group_layer 自动调用了 BaseConv。
        # Q: 详细解释BaseConv这个方法的作用与参数
        # A: BaseConv 是一个卷积层,它的作用是将输入的特征图通过卷积层进行处理,然后输出处理后的特征图。
        # A: BaseConv 的参数有:
        # A: in_channels (int): number of input channels.
        # A: out_channels (int): number of output channels.
        # A: ksize (int): size of kernel.
        # A: stride (int): stride of kernel.
        # A: act (str): activation function, use "lrelu" for leaky relu.
        # Q: 为什么要用 leaky relu
        # A: leaky relu 的作用是增加梯度,使得模型训练更快。
        # Q: leaky relu 的公式是什么,它与 relu 的区别是什么
        # A: leaky relu 的公式是:max(0.1x, x),它与 relu 的区别类的初始化函数中被赋值的。是当 x < 0 时,leaky relu 的值不为 0。 
        
        
        self.stem = nn.Sequential(
            BaseConv(in_channels, stem_out_channels, ksize=3, stride=1, act="lrelu"),
            *self.make_group_layer(stem_out_channels, num_blocks=1, stride=2),
        )
        # Q: 为什么要乘以 2,经过 stem 后,特征图的通道数是多少
        # A: 乘以 2 是因为 darknet 的第二层到第五层的通道数都是前一层的两倍。
        # Q: 其实不是的,是因为make_group_layer调用了BaseConv,BaseConv中的out_channels是前一层的两倍
        # A: 对,是因为 make_group_layer 调用了 BaseConv,BaseConv 中的 out_channels 是前一层的两倍。
        
        
        in_channels = stem_out_channels * 2  # 64

        # ========================  num_blocks ========================
        # Q: 下面的语法中,Darknet是否就是self
        # A: 是的,Darknet 就是 self。
        # Q: depth2blocks是个字典吗?
        # A: 是的,depth2blocks 是一个字典。
        # Q: depth这个参数是不是没有被赋值
        # A: depth 这个参数是在 Darknet 类的初始化函数中被赋值的。
        # Q: 请尝试写一个调用这个类的函数
        # A: 请看下面的代码。
        # A: def test():
        # A:     model = Darknet(depth=53)
        # A:     print(model)
        num_blocks = Darknet.depth2blocks[depth]
        # create darknet with `stem_out_channels` and `num_blocks` layers.
        # to make model structure more clear, we don't use `for` statement in python.
        self.dark2 = nn.Sequential(
            *self.make_group_layer(in_channels, num_blocks[0], stride=2)
        )
        in_channels *= 2  # 128
        self.dark3 = nn.Sequential(
            *self.make_group_layer(in_channels, num_blocks[1], stride=2)
        )
        in_channels *= 2  # 256
        self.dark4 = nn.Sequential(
            *self.make_group_layer(in_channels, num_blocks[2], stride=2)
        )
        in_channels *= 2  # 512

        self.dark5 = nn.Sequential(
            *self.make_group_layer(in_channels, num_blocks[3], stride=2),
            *self.make_spp_block([in_channels, in_channels * 2], in_channels * 2),
        )
    # ========================  self.make_group_layer ========================
    # Q: ResLayer的方法是什么,它的参数是什么,它的网络结构是什么样子的
    # A: ResLayer 是一个残差层,它的参数是 in_channels,它的网络结构是:
    # A: 1. 卷积层,卷积核大小为 1,步长为 1,激活函数为 leaky relu。
    # A: 2. 卷积层,卷积核大小为 3,步长为 1,激活函数为 leaky relu。
    # A: 3. 卷积层,卷积核大小为 1,步长为 1,激活函数为 leaky relu。
    # A: 4. 残差连接。
    # Q: 残差连接了那两个层
    # A: 残差连接了第 1 个卷积层和第 3 个卷积层。
    def make_group_layer(self, in_channels: int, num_blocks: int, stride: int = 1):
        "starts with conv layer then has `num_blocks` `ResLayer`"
        return [
            BaseConv(in_channels, in_channels * 2, ksize=3, stride=stride, act="lrelu"),
            *[(ResLayer(in_channels * 2)) for _ in range(num_blocks)],
        ]

    # ========================  SPPBottleneck ========================
    # Q: SPPBottleneck的方法是什么,它的参数是什么,它的网络结构是什么样子的
    # A: SPPBottleneck 是一个 SPP 层,它的参数是 in_channels,它的网络结构是:
    # A: 1. 卷积层,卷积核大小为 1,步长为 1,激活函数为 leaky relu。
    # A: 2. 卷积层,卷积核大小为 3,步长为 1,激活函数为 leaky relu。
    # A: 3. SPP 层。
    # A: 4. 卷积层,卷积核大小为 3,步长为 1,激活函数为 leaky relu。
    # A: 5. 卷积层,卷积核大小为 1,步长为 1,激活函数为 leaky relu。
    # Q: 请详细阐述一下 SPP 层的作用,它的网络结构是什么样子的
    # A: SPP 层是 Spatial Pyramid Pooling 层,它的作用是将特征图的高和宽降低,同时增加通道数。

    def make_spp_block(self, filters_list, in_filters):
        m = nn.Sequential(
            *[
                BaseConv(in_filters, filters_list[0], 1, stride=1, act="lrelu"),
                BaseConv(filters_list[0], filters_list[1], 3, stride=1, act="lrelu"),
                SPPBottleneck(
                    in_channels=filters_list[1],
                    out_channels=filters_list[0],
                    activation="lrelu",
                ),
                BaseConv(filters_list[0], filters_list[1], 3, stride=1, act="lrelu"),
                BaseConv(filters_list[1], filters_list[0], 1, stride=1, act="lrelu"),
            ]
        )
        return m

    def forward(self, x):
        outputs = {}
        x = self.stem(x)
        outputs["stem"] = x
        x = self.dark2(x)
        outputs["dark2"] = x
        x = self.dark3(x)
        outputs["dark3"] = x
        x = self.dark4(x)
        outputs["dark4"] = x
        x = self.dark5(x)
        outputs["dark5"] = x
        return {k: v for k, v in outputs.items() if k in self.out_features}


已发布

分类

来自

标签:

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注