物体检测-系列教程5:YOLOV3 源码解读2(模型创建函数/darknet模型构造/前向传播计算方式)

🎈🎈🎈YOLO 系列教程 总目录

上篇内容:

YOLOV3项目实战1之 整体介绍与数据处理

YOLOV3提出论文:《Yolov3: An incremental improvement》

4、模型创建

4.1 模型创建函数

模型创建函数 位置:项目 / utils / models.py / def create_modules
这是网络的所有层的构造函数部分

def create_modules(module_defs):
    hyperparams = module_defs.pop(0)
    output_filters = [int(hyperparams["channels"])]
    module_list = nn.ModuleList()
    for module_i, module_def in enumerate(module_defs):
        modules = nn.Sequential()
        if module_def["type"] == "convolutional":(暂时省略)
        elif module_def["type"] == "maxpool":(暂时省略)
        elif module_def["type"] == "upsample":(暂时省略)
        elif module_def["type"] == "route":(暂时省略)
        elif module_def["type"] == "shortcut":(暂时省略)
        elif module_def["type"] == "yolo":(暂时省略)
        module_list.append(modules)
        output_filters.append(filters)
    return hyperparams, module_list
  1. 模型创建函数,读进来配置文件module_defs
  2. 从 module_defs 列表中移除第一个元素,并将其存储在 hyperparams 变量中。这个元素通常包含了一些超参数(例如,输入通道数和其他配置信息),它们用于定义网络的输入和全局参数
  3. 创建了一个名为 output_filters 的列表,其中包含一个整数,表示模型的输出通道数
  4. 创建了一个空的 PyTorch ModuleList 对象,用于存储神经网络的各个模块
  5. 迭代 module_defs 列表,逐个处理模块的定义,module_i 是当前迭代的模块索引,module_def 包含了当前模块的配置信息,例如模块的类型、参数等
  6. 在每次迭代中,创建了一个空的 PyTorch Sequential 容器,用于存储当前模块中的层次。
  7. 接下来是6个多向if语判断句,分别为:
    1. 处理卷积层的情况
    2. 最大池化层
    3. 上采样层
    4. 路由层
    5. 快捷连接层
    6. YOLO 层
  8. module_list.append(modules)这一行代码将当前处理的模块的层次结构 modules 添加到神经网络模型的 module_list 中。module_list 是一个 PyTorch ModuleList 对象,它用于存储神经网络的各个模块(例如卷积层、池化层、激活函数等)
  9. output_filters.append(filters):这一行代码将当前处理的模块的输出通道数 filters 添加到 output_filters 列表中。output_filters 用于跟踪每个模块的输出通道数,以便在构建下一个模块时确定输入通道数

4.2 卷积层的处理

4中介绍了模型创建函数,但是if判断语句中没有给出内容,在5介绍一下卷积层是如何处理的

if module_def["type"] == "convolutional":
    bn = int(module_def["batch_normalize"])
    filters = int(module_def["filters"])
    kernel_size = int(module_def["size"])
    pad = (kernel_size - 1) // 2
    modules.add_module(
        f"conv_{module_i}",
        nn.Conv2d(
            in_channels=output_filters[-1],
            out_channels=filters,
            kernel_size=kernel_size,
            stride=int(module_def["stride"]),
            padding=pad,
            bias=not bn,
        ),
    )
    if bn:
        modules.add_module(f"batch_norm_{module_i}", nn.BatchNorm2d(filters, momentum=0.9, eps=1e-5))
    if module_def["activation"] == "leaky":
        modules.add_module(f"leaky_{module_i}", nn.LeakyReLU(0.1))
  1. 检查当前type值为convolutional,即卷积层:在配置文件中,每个模块开头都有一个中括号括起来的部分,这个是一个type值。
  2. 解析是否应该使用批归一化的信息,将其转换为整数值。如果 batch_normalize 为1,表示应该使用批归一化,否则为0,表示不使用。
  3. 解析卷积层的输出通道数(即卷积核数量)
  4. 解析卷积核的大小。
  5. 计算卷积层的填充大小,以确保输出的特征图尺寸与输入一致。
  6. 使用 PyTorch 的 nn.Conv2d 创建卷积层并添加到 modules 容器中:
    1. 输入通道数等于上一层的输出通道数
    2. 输出通道数等于当前卷积层的配置中指定的通道数
    3. 卷积核大小
    4. 卷积层的步幅
    5. 填充大小
    6. 是否使用偏置项。如果使用批归一化 (bn 为1),则不使用偏置项
  7. 如果 bn 为真(即应该使用批归一化),则添加批归一化层到 modules 容器中:nn.BatchNorm2d(filters, momentum=0.9, eps=1e-5):创建批归一化层,其中 filters 表示通道数,momentum 和 eps 是批归一化的超参数。
  8. 如果当前卷积层的激活函数为 “leaky”,则添加 Leaky ReLU 激活函数层到 modules 容器中:nn.LeakyReLU(0.1):创建 Leaky ReLU 激活函数层,参数 0.1 是斜率,控制着负数部分的输出

4.3 最大池化层的处理

elif module_def["type"] == "maxpool":
    kernel_size = int(module_def["size"])
    stride = int(module_def["stride"])
    if kernel_size == 2 and stride == 1:
        modules.add_module(f"_debug_padding_{module_i}", nn.ZeroPad2d((0, 1, 0, 1)))
    maxpool = nn.MaxPool2d(kernel_size=kernel_size, stride=stride, padding=int((kernel_size - 1) // 2))
    modules.add_module(f"maxpool_{module_i}", maxpool)
  1. elif module_def["type"] == "maxpool"::这个条件语句检查当前模块是否为最大池化层
  2. kernel_size = int(module_def["size"]):解析模块配置中的池化核大小,将其转换为整数
  3. stride = int(module_def["stride"]):解析模块配置中的池化层步幅,将其转换为整数
  4. if kernel_size == 2 and stride == 1::这一行代码检查是否满足一个特定条件,即池化核大小为2且步幅为1。如果满足这个条件,就执行下面的操作
    • modules.add_module(f"_debug_padding_{module_i}", nn.ZeroPad2d((0, 1, 0, 1))):这里添加了一个用于调试的零填充层。具体来说,它使用 nn.ZeroPad2d 在输入图像的底部和右侧各添加一列零填充。这个填充的目的可能是为了处理特定情况下的池化层,以确保输出大小与输入大小一致。这个步骤通常是为了处理某些特殊情况而添加的
  5. 创建最大池化层并添加到 modules 容器中:
    • maxpool = nn.MaxPool2d(kernel_size=kernel_size, stride=stride, padding=int((kernel_size - 1) // 2)):创建最大池化层,其中包括池化核大小、步幅和填充大小的配置
    • modules.add_module(f"maxpool_{module_i}", maxpool):将最大池化层添加到 modules 容器中,命名为 maxpool_{module_i},其中 module_i 是当前模块的索引

通过上述操作,当前最大池化层的配置被解析,并相应地创建了相应的 PyTorch 模块,这些模块将在神经网络模型中用于后续的前向传播计算。最大池化层用于减小特征图的尺寸,以帮助网络提取更高级别的特征。

4.4 上采样层的处理

elif module_def["type"] == "upsample":
    upsample = Upsample(scale_factor=int(module_def["stride"]), mode="nearest")
    modules.add_module(f"upsample_{module_i}", upsample)

这段代码处理上采样层(upsample)的情况。让我逐步解释这段代码的主要步骤:

  1. elif module_def["type"] == "upsample"::这个条件语句检查当前模块是否为上采样层
  2. 创建上采样层并添加到 modules 容器中:
    • upsample = Upsample(scale_factor=int(module_def["stride"]), mode="nearest"):创建一个上采样层,其中包括上采样的尺度因子(由模块配置中的 “stride” 参数指定)和上采样的模式。在这里,使用了 “nearest” 模式,表示最近邻插值。
    • modules.add_module(f"upsample_{module_i}", upsample):将上采样层添加到 modules 容器中,命名为 upsample_{module_i},其中 module_i 是当前模块的索引
      上采样层用于将特征图的尺寸放大,通常在网络的某些层级中使用,以便实现更高级别的特征提取。上采样可以帮助网络在不同尺度上检测目标,并提高检测精度。在这里,上采样层根据模块配置中的参数进行操作,以满足网络的需要。

上采样层:

class Upsample(nn.Module):
    """ nn.Upsample is deprecated """

    def __init__(self, scale_factor, mode="nearest"):
        super(Upsample, self).__init__()
        self.scale_factor = scale_factor
        self.mode = mode

    def forward(self, x):
        x = F.interpolate(x, scale_factor=self.scale_factor, mode=self.mode)
        return x

4.5 路由层的处理

elif module_def["type"] == "route": # 输入1:26*26*256 输入2:26*26*128  输出:26*26*(256+128)
    layers = [int(x) for x in module_def["layers"].split(",")]
    filters = sum([output_filters[1:][i] for i in layers])
    modules.add_module(f"route_{module_i}", EmptyLayer())

这段代码处理路由层(route)的情况。路由层通常用于连接前面的某些层的输出。让我逐步解释这段代码的主要步骤:

  1. elif module_def["type"] == "route"::这个条件语句检查当前模块是否为路由层
  2. 解析模块配置中的 layers 参数,该参数指定了要连接的前一层的索引列表:
    • layers = [int(x) for x in module_def["layers"].split(",")]:将模块配置中的 layers 参数按逗号分隔并解析为整数列表。这些整数表示要连接的前一层的索引
  3. 计算路由层的输出通道数(filters):
    • filters = sum([output_filters[1:][i] for i in layers]):根据前一层的输出通道数列表 output_filters,通过索引列表 layers 计算路由层的输出通道数。通常,路由层的输出通道数等于要连接的前一层的输出通道数之和
  4. 添加路由层到 modules 容器中:
    • modules.add_module(f"route_{module_i}", EmptyLayer()):添加路由层到 modules 容器中,命名为 route_{module_i},其中 module_i 是当前模块的索引。EmptyLayer() 可能是一个占位符层,用于表示路由层的存在,但不执行具体的计算。这种情况下,通常会在后续的代码中处理路由层的连接操作

路由层通常用于将不同尺度或分辨率的特征图连接在一起,以提供网络更丰富的信息,用于目标检测等任务。在这里,路由层的具体操作可能在后续的代码中进行,以确保正确地连接前一层的输出。

4.6 快捷连接层的处理

在介绍yolov3的时候,有讲过为了适应不同需要检测的物体的尺寸,在网络的最后几层将不同尺寸的输出拼接在一起,shortcut就是完成这个过程。

elif module_def["type"] == "shortcut":
    filters = output_filters[1:][int(module_def["from"])]
    modules.add_module(f"shortcut_{module_i}", EmptyLayer())

这段代码处理快捷连接层(shortcut)的情况。快捷连接层通常用于跨层级连接输出。让我逐步解释这段代码的主要步骤:

  1. elif module_def["type"] == "shortcut"::这个条件语句检查当前模块是否为快捷连接层
  2. 解析模块配置中的 from 参数,该参数指定了要连接的前一层的索引:
    • filters = output_filters[1:][int(module_def["from"])]:通过索引参数 from,获取要连接的前一层的输出通道数。通常,快捷连接层的输出通道数等于要连接的前一层的输出通道数
  3. 添加快捷连接层到 modules 容器中:
    • modules.add_module(f"shortcut_{module_i}", EmptyLayer()):添加快捷连接层到 modules 容器中,命名为 shortcut_{module_i},其中 module_i 是当前模块的索引。EmptyLayer() 可能是一个占位符层,用于表示快捷连接层的存在,但不执行具体的计算。这种情况下,通常会在后续的代码中处理快捷连接层的连接操作

快捷连接层通常用于实现跳跃连接,以便在网络的不同层之间传递信息。这有助于模型学习更丰富的特征表示,并有助于提高性能,尤其是在深层神经网络中。在这里,快捷连接层的具体操作可能在后续的代码中进行,以确保正确地连接前一层的输出。

空层:

class EmptyLayer(nn.Module):
    """Placeholder for 'route' and 'shortcut' layers"""

    def __init__(self):
        super(EmptyLayer, self).__init__()

4.7 yolo层的处理

elif module_def["type"] == "yolo":
    anchor_idxs = [int(x) for x in module_def["mask"].split(",")]
    # Extract anchors
    anchors = [int(x) for x in module_def["anchors"].split(",")]
    anchors = [(anchors[i], anchors[i + 1]) for i in range(0, len(anchors), 2)]
    anchors = [anchors[i] for i in anchor_idxs]
    num_classes = int(module_def["classes"])
    img_size = int(hyperparams["height"])
    # Define detection layer
    yolo_layer = YOLOLayer(anchors, num_classes, img_size)
    modules.add_module(f"yolo_{module_i}", yolo_layer)

这段代码处理YOLO层(yolo)的情况,YOLO层通常是目标检测模型中的输出层,用于生成检测框。让我逐步解释这段代码的主要步骤:

  1. elif module_def["type"] == "yolo"::这个条件语句检查当前模块是否为YOLO层
  2. 解析模块配置中的参数:
    • anchor_idxs = [int(x) for x in module_def["mask"].split(",")]:解析YOLO层的掩码(mask)参数,它指定了要使用哪些锚框来生成检测框。
    • anchors = [int(x) for x in module_def["anchors"].split(",")]:解析YOLO层的锚框(anchors)参数,它包含一组用于检测的锚框的尺寸。
    • 对锚框的解析和处理:首先将锚框的参数分成一对一对的元组,然后根据掩码参数 anchor_idxs 选择要使用的锚框
  3. 解析YOLO层的其他参数:
    • num_classes = int(module_def["classes"]):解析YOLO层的类别数。
    • img_size = int(hyperparams["height"]):解析图像的尺寸(高度),这通常用于检测框坐标的归一化
  4. 创建YOLO层并添加到 modules 容器中:
    • yolo_layer = YOLOLayer(anchors, num_classes, img_size):创建YOLO层,该层接受锚框、类别数和图像尺寸作为参数。
    • modules.add_module(f"yolo_{module_i}", yolo_layer):将YOLO层添加到 modules 容器中,命名为 yolo_{module_i},其中 module_i 是当前模块的索引

YOLO层是目标检测模型的关键组成部分,它负责生成检测框并预测每个框的类别和置信度。具体的操作在 YOLOLayer 类中实现,通常包括锚框处理、边界框回归、类别预测等。这个层级的输出包含了目标检测任务的预测结果。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
你遇到的问题是在使用https方式进行git clone时出现了错误。根据引用\[1\]和引用\[3\]的描述,这个错误可能是由于gnutls_handshake()失败导致的。这个错误可能是由于TLS连接没有正确终止引起的。另外,引用\[2\]中描述的错误是由于连接超时导致的。 为了解决这个问题,你可以尝试以下几个步骤: 1. 确保你的网络连接正常。如果你的网络连接不稳定或者有防火墙限制,可能会导致连接超时的错误。你可以尝试使用其他网络或者关闭防火墙来解决这个问题。 2. 检查你的git配置。有时候,错误可能是由于git配置的问题引起的。你可以使用以下命令检查你的git配置: ``` git config --list ``` 确保你的配置中没有错误的设置。 3. 尝试使用git协议进行clone。根据引用\[1\]的描述,使用git协议进行clone可能会成功。你可以尝试使用以下命令进行clone: ``` git clone git://github.com/pjreddie/darknet.git ``` 这将使用git协议进行clone,而不是https协议。 希望以上步骤能够帮助你成功进行git clone操作。如果问题仍然存在,请提供更多的错误信息以便我们进一步帮助你解决问题。 #### 引用[.reference_title] - *1* [fatal: unable to access ‘https://github.com/.git/‘: gnutls_handshake() failed: Error](https://blog.csdn.net/wang2008start/article/details/118967723)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* *3* [【Ubuntu git clone命令报错】fatal: unable to access ‘https://github.com/XXX‘: gnutls_handshake() ...](https://blog.csdn.net/xvrixingkong/article/details/129605071)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

机器学习杨卓越

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值