利用 yolov3.cfg 配置文件搭建网络模型

目录

一、前言

二、yolov3.cfg 配置文件展示

文件信息 

三、配置文件处理

1、parse_model_config 函数

(1)读取并预处理文件内容

(2)解析模块定义

 2、parse_data_config 函数

(1)初始化默认选项

(2)读取并解析文件内容

四、定义模型

1、定义上采样层

2、定义占位类,用于处理路由层和残差层

3、定义Darknet层

4、定义yolo层

五、模型搭建

 


一、前言

在深度学习领域,目标检测一直是一个热门且极具挑战性的任务。而 YOLO(You Only Look Once)系列算法以其高效快速的检测性能备受关注,其中 YOLOv3 更是在诸多应用场景中展现出了强大的实力。今天,我们就来深入了解一下如何通过 yolov3.cfg 配置文件搭建其对应的网络模型。

yolov3.cfg 配置文件就像是搭建网络模型的蓝图,它详细地规定了网络的各个组成部分,包括每一层的类型、参数等信息。通过读取和解析这个配置文件,我们能够准确地复现 YOLOv3 的网络架构,为后续的训练和应用奠定基础。

二、yolov3.cfg 配置文件展示

 

文件信息 

**模型基本信息**:包括输入数据的尺寸、批量大小等。


**卷积层和池化层**:YOLOv3模型主要由卷积层和池化层构成,这些层在配置文件中逐一列出。

**卷积层参数**:包括卷积核数量、大小、步长、填充等。


**池化层参数**:包括池化类型、大小、步长等。


**路由层和归一化层**:用于连接不同层的特征图。


**YOLO层**:负责检测物体的位置和类别。

[net]:定义了网络的基本信息,如输入尺寸(width、height)、通道数(channels)和批量大小(batch)。


[convolutional]:定义了卷积层的参数,包括卷积核大小(size)、步长(stride)、填充(pad)、卷积核数量(filters)和激活函数(activation)。


[maxpool]:定义了池化层的参数,包括池化大小(size)和步长(stride)。


[region]:定义了YOLO层的参数,包括类别数量(classes)、检测框数量(num)、抖动系数(jitter)、锚点(anchors)、logits等。

三、配置文件处理

1、parse_model_config 函数

(1)读取并预处理文件内容

**函数parse_model_config以给定的路径path打开 yolov3.cfg 文件,并将其内容读取为字符串列表lines。在此过程中,它会进行一系列的预处理操作:

**去除空行:通过[x for x in lines if x and not x.startswith('#')]这一语句,过滤掉了空行以及以#开头的注释行,确保我们后续处理的都是有效信息。        

(2)解析模块定义

经过预处理后,函数开始逐行解析配置文件内容以构建模块定义列表module_defs

**识别新模块块的开始:当遇到以[开头的行时,意味着一个新的模块块开始了。此时,函数会在module_defs列表中添加一个新的空字典,用于存储该模块的定义信息,并将该行中去除[]后的字符串作为模块的类型type存入字典。例如,若该行是[convolutional],则会在新添加的字典中设置typeconvolutional。同时,对于convolutional类型的模块,会默认设置batch_normalize0

**解析模块参数:对于非以[开头的行,函数会将其按照=进行拆分,得到参数名key和参数值value。然后去除参数名和参数值两端的空白字符,并将其存入当前正在构建的模块字典中。这样,通过逐行解析,就能完整地构建出每个模块的定义信息,最终module_defs列表就包含了 yolov3.cfg 文件中所有模块的详细定义。

def parse_model_config(path):
    """Parses the yolo-v3 layer configuration file and returns module definitions"""
    file = open(path, 'r')
    lines = file.read().split('\n')
    lines = [x for x in lines if x and not x.startswith('#')]   #x.startswith('#')用于检查字符串变量x是否以#前缀开始。如果x以该前缀开头,该方法将返回一个布尔值,通常是True,否则返回False。
    lines = [x.rstrip().lstrip() for x in lines] # get rid of fringe whitespaces
    module_defs = []
    for line in lines:
        if line.startswith('['): # This marks the start of a new block
            module_defs.append({})
            module_defs[-1]['type'] = line[1:-1].rstrip()
            if module_defs[-1]['type'] == 'convolutional':
                module_defs[-1]['batch_normalize'] = 0
        else:
            key, value = line.split("=")
            value = value.strip()
            module_defs[-1][key.rstrip()] = value.strip()

    return module_defs

 2、parse_data_config 函数

(1)初始化默认选项

函数parse_data_config首先初始化了一个字典options,并设置了一些默认值,比如gpus设置为'0,1,2,3',表示默认使用的 GPU 编号;num_workers设置为'10',这通常涉及到数据加载时的工作线程数量。

(2)读取并解析文件内容

接着,函数以给定的路径打开数据配置文件,并逐行读取其内容到lines列表中。对于每一行:

**跳过空行和注释行:通过if line == '' or line.startswith('#'):判断,如果行是空行或者以#开头的注释行,就直接跳过,不做处理。

**解析有效行:对于非空且非注释的行,函数会按照‘='将其拆分得到参数名key和参数值value,然后去除参数名和参数值两端的空白字符,并将参数值存入options字典中对应的参数名下。这样,经过对整个数据配置文件的逐行解析,options字典就完整地记录了数据配置文件中的各项参数设置。

def parse_data_config(path):
    """Parses the data configuration file"""
    options = dict()
    options['gpus'] = '0,1,2,3'
    options['num_workers'] = '10'
    with open(path, 'r') as fp:
        lines = fp.readlines()
    for line in lines:
        line = line.strip()
        if line == '' or line.startswith('#'):#startswith()用于检查字符串是否以特定的子字符串开始。如果是,它将返回True,否则返回False。
            continue
        key, value = line.split('=')
        options[key.strip()] = value.strip()
    return options

四、定义模型

1、定义上采样层

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):
        # torch.nn.functional中的interpolate用于对输入的张量进行插值操作。它可以用于缩放、放大或者重新定义张量的空间尺寸。
        # interpolate(input, size=None, scale_factor=None, mode='nearest', align_corners=False)
        # 参数解释:
        # input: 输入的张量。它应该是一个多维的张量,通常至少是二维的。
        # size: 输出张量的大小。如果未指定,则默认使用scale_factor参数进行缩放。
        # scale_factor: 缩放因子。它可以是单个数字,表示所有维度都按照相同的比例缩放;也可以是一个与输入张量维度相同的向量,表示每个维度分别按照不同的比例缩放。
        # mode: 插值模式。它决定了在缩放过程中如何插值。不同的插值模式会得到不同的结果,常见的插值模式有'nearest'(最近邻插值)、'bilinear'(双线性插值)等。
        # 函数返回一个与输入张量具有相同数据类型的输出张量,其空间尺寸通过指定的参数进行了缩放。
        x = F.interpolate(x, scale_factor=self.scale_factor, mode=self.mode)
        return x

2、定义占位类,用于处理路由层和残差层

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

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

3、定义Darknet层

class Darknet(nn.Module):
    """YOLOv3 object detection model"""

    def __init__(self, config_path, img_size=416):
        super(Darknet, self).__init__()
        self.module_defs = parse_model_config(config_path)
        self.hyperparams, self.module_list = create_modules(self.module_defs)   #创建出网络中需要的所有网络层,每一层需要哪些参数
        self.yolo_layers = [layer[0] for layer in self.module_list if hasattr(layer[0], "metrics")] #hasattr()是Python的一个内置函数,它用于检查对象是否具有给定(metrics)的属性。
        self.img_size = img_size
        self.seen = 0
        self.header_info = np.array([0, 0, 0, self.seen, 0], dtype=np.int32)

    def forward(self, x, targets=None):
        img_dim = x.shape[2]
        loss = 0
        layer_outputs, yolo_outputs = [], []
        for i, (module_def, module) in enumerate(zip(self.module_defs, self.module_list)):
            if module_def["type"] in ["convolutional", "upsample", "maxpool"]:
                x = module(x)
            elif module_def["type"] == "route":     #为concat的操作,是数据的拼接
                x = torch.cat([layer_outputs[int(layer_i)] for layer_i in module_def["layers"].split(",")], 1)
            elif module_def["type"] == "shortcut":  #残差的链接,也就是进行数据的加法+运算,
                layer_i = int(module_def["from"])
                x = layer_outputs[-1] + layer_outputs[layer_i]
            elif module_def["type"] == "yolo":      #包含3个yolo层,分别对应52*52,26*26,13*13层
                x, layer_loss = module[0](x, targets, img_dim)
                loss += layer_loss
                yolo_outputs.append(x)
            layer_outputs.append(x)     #包含了按顺序执行后的每一层结果。后期route和shortcut需要使用
        yolo_outputs = to_cpu(torch.cat(yolo_outputs, 1))
        return yolo_outputs if targets is None else (loss, yolo_outputs)

    def load_darknet_weights(self, weights_path):
        """Parses and loads the weights stored in 'weights_path'"""

        # Open the weights file
        with open(weights_path, "rb") as f:
            header = np.fromfile(f, dtype=np.int32, count=5)  # 前5个是开头值First five are header values
            self.header_info = header  # 保存权重时需要写入header
            self.seen = header[3]  # 训练期间看到的图像数量。number of images seen during training
            weights = np.fromfile(f, dtype=np.float32)  #读取剩余的权重数据 The rest are weights

        # Establish cutoff for loading backbone weights
        cutoff = None
        if "darknet53.conv.74" in weights_path:
            cutoff = 75

        ptr = 0
        for i, (module_def, module) in enumerate(zip(self.module_defs, self.module_list)):
            if i == cutoff:
                break
            if module_def["type"] == "convolutional":
                conv_layer = module[0]
                if module_def["batch_normalize"]:
                    # Load BN bias, weights, running mean and running variance
                    bn_layer = module[1]
                    num_b = bn_layer.bias.numel()  # Number of biases
                    # Bias
                    bn_b = torch.from_numpy(weights[ptr : ptr + num_b]).view_as(bn_layer.bias)
                    bn_layer.bias.data.copy_(bn_b)
                    ptr += num_b
                    # Weight
                    bn_w = torch.from_numpy(weights[ptr : ptr + num_b]).view_as(bn_layer.weight)
                    bn_layer.weight.data.copy_(bn_w)
                    ptr += num_b
                    # Running Mean
                    bn_rm = torch.from_numpy(weights[ptr : ptr + num_b]).view_as(bn_layer.running_mean)
                    bn_layer.running_mean.data.copy_(bn_rm)
                    ptr += num_b
                    # Running Var
                    bn_rv = torch.from_numpy(weights[ptr : ptr + num_b]).view_as(bn_layer.running_var)
                    bn_layer.running_var.data.copy_(bn_rv)
                    ptr += num_b
                else:
                    # Load conv. bias
                    num_b = conv_layer.bias.numel()
                    conv_b = torch.from_numpy(weights[ptr : ptr + num_b]).view_as(conv_layer.bias)
                    conv_layer.bias.data.copy_(conv_b)
                    ptr += num_b
                # Load conv. weights
                num_w = conv_layer.weight.numel()
                conv_w = torch.from_numpy(weights[ptr : ptr + num_w]).view_as(conv_layer.weight)
                conv_layer.weight.data.copy_(conv_w)
                ptr += num_w

    def save_darknet_weights(self, path, cutoff=-1):
        """
            @:param path    - path of the new weights file
            @:param cutoff  - save layers between 0 and cutoff (cutoff = -1 -> all are saved)
        """
        fp = open(path, "wb")
        self.header_info[3] = self.seen
        self.header_info.tofile(fp)

        # Iterate through layers
        for i, (module_def, module) in enumerate(zip(self.module_defs[:cutoff], self.module_list[:cutoff])):
            if module_def["type"] == "convolutional":
                conv_layer = module[0]
                # If batch norm, load bn first
                if module_def["batch_normalize"]:
                    bn_layer = module[1]
                    bn_layer.bias.data.cpu().numpy().tofile(fp)
                    bn_layer.weight.data.cpu().numpy().tofile(fp)
                    bn_layer.running_mean.data.cpu().numpy().tofile(fp)
                    bn_layer.running_var.data.cpu().numpy().tofile(fp)
                # Load conv bias
                else:
                    conv_layer.bias.data.cpu().numpy().tofile(fp)
                # Load conv weights
                conv_layer.weight.data.cpu().numpy().tofile(fp)

        fp.close()

4、定义yolo层

class YOLOLayer(nn.Module):
    """Detection layer
    YOLO
    实现的,该卷积核对每个网格单元的先验框进行回归,以确定其大小和偏移。
    关联:这个步骤将每个先验框与前面的卷积层输出的特征图进行关联。具体来说,对于每个先验框,它将特征图中的特征与该框进行匹配,并计算一个匹配得分。这个得分被用来确定该框是否应该保留下来作为检测框,还是应该被抑制掉。
    非极大值抑制:这个步骤的目的是进一步优化最终的检测结果。它通过比较每个检测框与其相邻框的置信度得分,将低得分的框抑制掉,保留高得分的框。
    YOLO层的操作都是在高层次的特征图上进行的,这使得YOLO算法能够在保持高准确性的同时,实现较高的运行速度。
    """

    def __init__(self, anchors, num_classes, img_dim=416):
        super(YOLOLayer, self).__init__()
        self.anchors = anchors
        self.num_anchors = len(anchors)
        self.num_classes = num_classes
        self.ignore_thres = 0.5     #阈值
        self.mse_loss = nn.MSELoss()    #nn.MSELoss()是用于计算均方误差(Mean Squared Error)的损失函数。均方误差常用于衡量预测值与真实值之间的差距,适用于回归问题。
        self.bce_loss = nn.BCELoss()    #nn.BCELoss()是用于二元分类问题的损失函数。全称是Binary Cross Entropy Loss,也就是二元交叉熵损失。它通常用于处理二分类问题。
        self.obj_scale = 1
        self.noobj_scale = 100
        self.metrics = {}

        self.img_dim = img_dim
        self.grid_size = 0  # grid size

    def compute_grid_offsets(self, grid_size, cuda=True):
        self.grid_size = grid_size
        g = self.grid_size
        FloatTensor = torch.cuda.FloatTensor if cuda else torch.FloatTensor
        self.stride = self.img_dim / self.grid_size #每一个网格占用的像素大小
        # Calculate offsets for each grid
        self.grid_x = torch.arange(g).repeat(g, 1).view([1, 1, g, g]).type(FloatTensor)
        self.grid_y = torch.arange(g).repeat(g, 1).t().view([1, 1, g, g]).type(FloatTensor)
        self.scaled_anchors = FloatTensor([(a_w / self.stride, a_h / self.stride) for a_w, a_h in self.anchors])
        self.anchor_w = self.scaled_anchors[:, 0:1].view((1, self.num_anchors, 1, 1))
        self.anchor_h = self.scaled_anchors[:, 1:2].view((1, self.num_anchors, 1, 1))

    def forward(self, x, targets=None, img_dim=None):
        # Tensors for cuda support
        print (x.shape)
        FloatTensor = torch.cuda.FloatTensor if x.is_cuda else torch.FloatTensor#根据是否支持GPU来选择不同的tensor处理方法
        LongTensor = torch.cuda.LongTensor if x.is_cuda else torch.LongTensor
        ByteTensor = torch.cuda.ByteTensor if x.is_cuda else torch.ByteTensor

        self.img_dim = img_dim
        num_samples = x.size(0)
        grid_size = x.size(2)

        prediction = (
            x.view(num_samples, self.num_anchors, self.num_classes + 5, grid_size, grid_size)#改变一个张量(Tensor)的形状或者维度。这个方法并不会改变张量内部元素的排列顺序或者数据类型,只是改变了我们看待这个张量的方式。
            .permute(0, 1, 3, 4, 2)#将张量的维度重新排列的函数。原始张量的维度顺序是(0, 1, 2, 3, 4),而重排后的维度顺序是(0, 1, 3, 4, 2)。这意味着原始张量的第一个维度(索引为0)保持不变,第二个维度(索引为1)保持不变,第三个维度(索引为3)移动到第四个位置,第四个维度(索引为4)移动到第五个位置,而原始张量的第五个维度(索引为2)移动到第三个位置。
            .contiguous()       #用于将张量在内存中重新排序,以确保在执行操作时没有发生内存错误。
        )   #排序后的结果:【1个batch数据的数量,每个框中候选框的个数,网格尺寸、网格尺寸、输出结果:分类的个数+(x,y,w,h,confidence)】
        print (prediction.shape)
        # Get outputs
        x = torch.sigmoid(prediction[..., 0])  # Center x,...通常表示"展开"操作,特别是在多维数组或张量中。当你在一个数组或张量后面使用...,它表示将数组或张量的所有维度都展开。
        y = torch.sigmoid(prediction[..., 1])  # Center y,是相对于每一个小的方格 的位置。
        w = prediction[..., 2]  # Width
        h = prediction[..., 3]  # Height
        pred_conf = torch.sigmoid(prediction[..., 4])  # Conf
        pred_cls = torch.sigmoid(prediction[..., 5:])  # Cls pred.

        # If grid size does not match current we compute new offsets
        if grid_size != self.grid_size:
            self.compute_grid_offsets(grid_size, cuda=x.is_cuda) #相对位置得到对应的绝对位置比如之前的位置是0.5,0.5变为 11.5,11.5这样的

        # Add offset and scale with anchors #特征图中的实际位置
        pred_boxes = FloatTensor(prediction[..., :4].shape)
        pred_boxes[..., 0] = x.data + self.grid_x
        pred_boxes[..., 1] = y.data + self.grid_y
        pred_boxes[..., 2] = torch.exp(w.data) * self.anchor_w  #exp的原因:看ppt中bw和bh的计算方法
        pred_boxes[..., 3] = torch.exp(h.data) * self.anchor_h

        output = torch.cat(         #用于将多个张量沿着指定维度进行拼接。
            (
                pred_boxes.view(num_samples, -1, 4) * self.stride, #还原到原始图中,
                pred_conf.view(num_samples, -1, 1),
                pred_cls.view(num_samples, -1, self.num_classes),
            ),
            -1,
        )   #包含预测框,置信度、类别概率

        if targets is None:
            return output, 0
        else:
            iou_scores, class_mask, obj_mask, noobj_mask, tx, ty, tw, th, tcls, tconf = build_targets(
                pred_boxes=pred_boxes,
                pred_cls=pred_cls,
                target=targets,
                anchors=self.scaled_anchors,
                ignore_thres=self.ignore_thres,
            )
            # iou_scores:真实值与最匹配的anchor的IOU得分值 class_mask:分类正确的索引  obj_mask:目标框所在位置的最好anchor置为1 noobj_mask obj_mask那里置0,还有计算的iou大于阈值的也置0,其他都为1 tx, ty, tw, th, 对应的对于该大小的特征图的xywh目标值也就是我们需要拟合的值 tconf 目标置信度
            # Loss : Mask outputs to ignore non-existing objects (except with conf. loss)
            loss_x = self.mse_loss(x[obj_mask], tx[obj_mask]) # 只计算有目标的
            loss_y = self.mse_loss(y[obj_mask], ty[obj_mask])
            loss_w = self.mse_loss(w[obj_mask], tw[obj_mask])
            loss_h = self.mse_loss(h[obj_mask], th[obj_mask])
            loss_conf_obj = self.bce_loss(pred_conf[obj_mask], tconf[obj_mask]) 
            loss_conf_noobj = self.bce_loss(pred_conf[noobj_mask], tconf[noobj_mask])
            loss_conf = self.obj_scale * loss_conf_obj + self.noobj_scale * loss_conf_noobj #有物体越接近1越好 没物体的越接近0越好
            loss_cls = self.bce_loss(pred_cls[obj_mask], tcls[obj_mask]) #分类损失
            total_loss = loss_x + loss_y + loss_w + loss_h + loss_conf + loss_cls #总损失

            # Metrics
            cls_acc = 100 * class_mask[obj_mask].mean()
            conf_obj = pred_conf[obj_mask].mean()
            conf_noobj = pred_conf[noobj_mask].mean()
            conf50 = (pred_conf > 0.5).float()
            iou50 = (iou_scores > 0.5).float()
            iou75 = (iou_scores > 0.75).float()
            detected_mask = conf50 * class_mask * tconf
            precision = torch.sum(iou50 * detected_mask) / (conf50.sum() + 1e-16)
            recall50 = torch.sum(iou50 * detected_mask) / (obj_mask.sum() + 1e-16)
            recall75 = torch.sum(iou75 * detected_mask) / (obj_mask.sum() + 1e-16)

            self.metrics = {
                "loss": to_cpu(total_loss).item(),
                "x": to_cpu(loss_x).item(),
                "y": to_cpu(loss_y).item(),
                "w": to_cpu(loss_w).item(),
                "h": to_cpu(loss_h).item(),
                "conf": to_cpu(loss_conf).item(),
                "cls": to_cpu(loss_cls).item(),
                "cls_acc": to_cpu(cls_acc).item(),
                "recall50": to_cpu(recall50).item(),
                "recall75": to_cpu(recall75).item(),
                "precision": to_cpu(precision).item(),
                "conf_obj": to_cpu(conf_obj).item(),
                "conf_noobj": to_cpu(conf_noobj).item(),
                "grid_size": grid_size,
            }

            return output, total_loss

五、模型搭建

结合parse_data_config函数返回的options字典中的参数,设置网络的输入输出属性、确定 GPU 的使用方式以及数据加载的工作线程数量等,确保网络模型能够在合适的环境下高效运行。

def create_modules(module_defs):
    """
    Constructs module list of layer blocks from module configuration in module_defs
    """
    hyperparams = module_defs.pop(0)
    output_filters = [int(hyperparams["channels"])]     #输出特征图的个数,也是卷积核的个数
    module_list = nn.ModuleList()
    #nn.ModuleList()是PyTorch中的一个类,用于将一个或多个nn.Module的实例包装到一个可迭代对象中。这个类在定义神经网络模型时非常有用,因为它允许你将多个网络层(每个都是nn.Module的实例)组合在一起,并以列表的形式管理和使用。
    #具体来说,nn.ModuleList()的工作方式类似于Python的列表。你可以像向普通列表添加元素一样向ModuleList添加层,然后可以通过索引访问它们,就像访问普通的列表一样。然而,与普通列表不同的是,ModuleList中的元素是nn.Module的实例,即神经网络层。
    for module_i, module_def in enumerate(module_defs):
        modules = nn.Sequential()#nn.Sequential()主要用于实现线性堆叠,内部实现了前向传播函数;而nn.ModuleList()则用于存储多个网络层,以便在需要时进行调用,并不实现任何前向传播功能。

        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}", #'conv_0
                nn.Conv2d(
                    in_channels=output_filters[-1],#输入特征图的数量,即输入的维度。
                    out_channels=filters,           #输出特征图的数量,即输出的维度。
                    kernel_size=kernel_size,        #卷积核的大小
                    stride=int(module_def["stride"]),   #卷积核滑动的步长大小
                    padding=pad,        #在输入数据边界上填充0的层数
                    bias=not bn,        #是否在卷积后添加偏置项。做了batch normalize,很多情况就不用偏置项。
                ),
            )
            if bn:
                # nn.BatchNorm2d的输入参数包括:
                # num_features:输入图像的通道数量。
                # eps:稳定系数,防止分母出现0。默认值为1e - 05。
                # momentum:一个用于运行过程中均值和方差的一个估计参数。默认值为0.1。可用于控制批量归一化中移动平均和指数加权的因子。这意味着,随着时间的推移,momentum将推动均值和方差向新的数据进行调整。较小的momentum会导致较慢的调整速度,而较大的momentum则会加快调整速度。
                            # 这个参数的设置是有实际意义的。因为在深度神经网络训练过程中,数据分布会随时间变化。通过设置momentum参数,我们可以使批量归一化层的均值和方差能够跟随数据分布的变化而动态调整,从而更好地适应新的数据分布。
                            # 例如,如果我们有一个网络,其中第一个层使用了批量归一化,而第二个层没有使用批量归一化。在这种情况下,如果我们提高了第一个层的momentum值,那么随着时间的推移,第一个层的均值和方差将逐渐调整以适应新的数据分布。这将使得第二个层能够更好地处理经过归一化的输入数据,从而提高整个网络的性能。
                            # 总之,momentum参数在nn.BatchNorm2d中起到了推动批量归一化层的均值和方差向新数据进行调整的作用,有助于提高网络对数据分布变化的适应能力。
                modules.add_module(f"batch_norm_{module_i}", nn.BatchNorm2d(filters, momentum=0.9))
            if module_def["activation"] == "leaky":
                # nn.LeakyReLU是PyTorch中的一个非线性激活函数,它是一种带有泄漏的线性整流激活函数(LeakyReLU)。与ReLU不同,Leaky
                # ReLU在输入为负数时不是完全的截断,而是有一个小的负斜率。这意味着,对于负输入,LeakyReLU将不会输出0,而是逐渐增加的输出值。
                # 这种设计有助于解决ReLU在负数区域的问题,特别是在深度神经网络中,ReLU的截断可能导致一些神经元无法激活,而LeakyReLU可以缓解这个问题。
                modules.add_module(f"leaky_{module_i}", nn.LeakyReLU(0.1))

        elif module_def["type"] == "maxpool":       #darknet网络中没有池化层,因此本段代码不会执行
            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)

        elif module_def["type"] == "upsample": #上采样
            upsample = Upsample(scale_factor=int(module_def["stride"]), mode="nearest")#yolov3.cfg文件中保存的参数为stride=2
            modules.add_module(f"upsample_{module_i}", upsample)

        elif module_def["type"] == "route": #为concat的操作,是数据的拼接。
            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())#初始化的空网络

        elif module_def["type"] == "shortcut":      # #残差的链接,也就是进行数据的加法+运算
            filters = output_filters[1:][int(module_def["from"])]
            modules.add_module(f"shortcut_{module_i}", EmptyLayer())

        elif module_def["type"] == "yolo":
            anchor_idxs = [int(x) for x in module_def["mask"].split(",")]#预选框的id
            # 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)
        # Register module list and number of output filters
        module_list.append(modules)
        output_filters.append(filters)#保存的是每一层的卷积核的个数

    return hyperparams, module_list

 

 

 

 

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/903158.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

CV项目整理

1. 爬取+展示的实时项目 1.1 核心技术 myqls + maxwell + redis+django 实现读写分离,实时项目,主从复制,读写分离,顺写日志。 maxwell将自己伪装成为slave,就可以从Mysql的集群中获取顺写日志Binlog maxwell取得的数据格式json 1.2 流程 1.3优化查询 下面的查询,笛卡尔…

【Spring】Spring Boot 日志(8)

本系列共涉及4个框架:Sping,SpringBoot,Spring MVC,Mybatis。 博客涉及框架的重要知识点,根据序号学习即可。 1、日志概述 1.1学习日志的必要性 在第一次学习编程语言的时候,我们就在使用printf或者System.out.println等打印语句打印日志了…

CSS常用标签笔记

1 字体样式属性 对于font-family属性&#xff0c;如果字体类型只有一个英文单词&#xff0c;则不需要加上双引号&#xff1b;如果字体类型是多个英文单词或者是中文&#xff0c;则需要加上双引号。 <!DOCTYPE html> <html> <head><meta charset"utf…

Qt实现播放器顶部、底部悬浮工具栏

未实现时的播放器界面 下面是实现了雏形的悬浮栏&#xff1a; 设计一个播放器的悬浮工具栏旨在提升用户的交互体验&#xff0c;使得播放器在不影响观感的情况下提供常用功能。为此&#xff0c;我们可以新建一个QWidget窗口作为悬浮工具栏&#xff0c;将其作为播放器窗口的子控…

Redis-概念、安装、基本配置

文章目录 一、Redis及Redis集群概念、分布式系统概念一-1 Redis是什么&#xff1f;一-2 什么是分布式系统及其特性&#xff1f;一-3 什么是Redis集群以及实现的方法介绍&#xff1f;哨兵模式(sentinel)&#xff1f;cluster&#xff1f;&#xff1f; 一-4 Redis的库&#xff1f;…

大语言模型驱动的跨域属性级情感分析——论文阅读笔记

前言 论文PDF下载地址&#xff1a;7156 最近想搜一下基于大语言模型的情感分析论文&#xff0c;搜到了这篇在今年发表的论文&#xff0c;于是简单阅读之后在这里记一下笔记。 如图1所示&#xff0c;在餐厅领域中的"快"是上菜快&#xff0c;属于正面情感&#xff0c;但…

2022 icpc南京(I,G,A,D,M,B)

文章目录 [I. Perfect Palindrome](https://codeforces.com/gym/104128/problem/I)[G. Inscryption](https://codeforces.com/gym/104128/problem/G)[A.Stop, Yesterday Please No More](https://codeforces.com/gym/104128/problem/A)[D. Chat Program](https://codeforces.co…

来电显示单位名称怎么设置?

在现代商务沟通中&#xff0c;来电显示单位名称已成为提升企业形象、增强客户信任的重要工具。想象一下&#xff0c;当拨打或接听电话时&#xff0c;如果对方的手机屏幕上能够显示出企业的单位名称和品牌标识&#xff0c;会有什么样的效果呢&#xff1f;毋庸置疑&#xff0c;这…

图为大模型一体机新探索,赋能智能家居行业

在21世纪的今天&#xff0c;科技的飞速进步正以前所未有的速度重塑着我们的生活方式。从智能手机到物联网&#xff0c;从大数据到人工智能&#xff0c;每一项技术创新都在为人类带来前所未有的便利与效率。其中&#xff0c;图为AI大模型一体机作为人工智能领域的最新成果&#…

【Linux学习】(9)调试器gdb

前言 Linux基础工具&#xff1a;安装软件我们用的是yum&#xff0c;写代码用的是vim&#xff0c;编译代码用gcc/g&#xff0c;调试代码用gdb&#xff0c;自动化构建用make/Makefile&#xff0c;多人协作上传代码到远端用的是git。 在前面我们把yum、vim、gcc、make、git都已经学…

大数据新视界 -- 大数据大厂之大数据重塑影视娱乐产业的未来(4 - 2)

&#x1f496;&#x1f496;&#x1f496;亲爱的朋友们&#xff0c;热烈欢迎你们来到 青云交的博客&#xff01;能与你们在此邂逅&#xff0c;我满心欢喜&#xff0c;深感无比荣幸。在这个瞬息万变的时代&#xff0c;我们每个人都在苦苦追寻一处能让心灵安然栖息的港湾。而 我的…

Leecode热题100-226.反转二叉树

给你一棵二叉树的根节点 root &#xff0c;翻转这棵二叉树&#xff0c;并返回其根节点。 示例 1&#xff1a; 输入&#xff1a;root [4,2,7,1,3,6,9] 输出&#xff1a;[4,7,2,9,6,3,1]示例 2&#xff1a; 输入&#xff1a;root [2,1,3] 输出&#xff1a;[2,3,1]示例 3&#x…

探讨Facebook的AI研究:未来社交平台的技术前瞻

在数字时代&#xff0c;社交媒体已成为人们日常生活的重要组成部分。作为全球最大的社交网络之一&#xff0c;Facebook不断致力于人工智能&#xff08;AI&#xff09;的研究与应用&#xff0c;以提升用户体验、增强平台功能并推动技术创新。本文将探讨Facebook在AI领域的研究方…

PHP计件工资平台系统小程序源码

&#x1f4bc;提升效率&#xff0c;精准计薪 ——「计件工资系统」全解析&#x1f4bc; &#x1f4ca;【告别繁琐&#xff0c;一键统计】&#x1f4ca; 你是否还在为每个月的计件工资统计而烦恼&#xff1f;繁琐的数据录入、核对&#xff0c;不仅耗时耗力&#xff0c;还容易出…

ClickHouse 3节点集群安装

ClickHouse 简介 ClickHouse是一个用于联机分析(OLAP)的列式数据库管理系统(DBMS)。 官方网站&#xff1a;https://clickhouse.com/ 项目地址&#xff1a;https://github.com/ClickHouse/ClickHouse 横向扩展集群介绍 此示例架构旨在提供可扩展性。它包括三个节点&#xff…

机器学习课程学习周报十八

机器学习课程学习周报十八 文章目录 机器学习课程学习周报十八摘要Abstract一、机器学习部分1. 再探Diffusion的数学原理1.1 回顾生成模型的任务1.2 DDPM (Denoising Diffusion Probabilistic Models)1.3 DDPM的证据下界 总结 摘要 本周周报深入探讨了Diffusion模型的数学原理…

JsonCpp库学习记录

使用源码的方式 到JsonCpp的开源库仓库下载最新的发行版本 解压压缩包 使用Python生成源码文件 在本路径下cmd打开控制台&#xff0c;使用python编译&#xff08;前提是python环境已安装&#xff09; python amalgamate.py 生成dist文件夹 jsoncpp为整合在一起的源码&#…

数通自学——VLAN虚拟局域网,eNSP实验讲解

VLAN虚拟局域网&#xff0c;eNSP实验讲解 一、概念二、eNSP仿真实验1、实验一&#xff1a;vlan演示&#xff08;交换机端口access模式&#xff09;2、实验二&#xff1a;vlan演示&#xff08;交换机端口trunk模式&#xff09; 一、概念 VLAN&#xff08;Virtual Local Area Ne…

vmware运维技巧总结

vmware使用实践总结 技巧一、在线添加硬盘技巧二、lvm脚本挂载硬盘 技巧一、在线添加硬盘 适用于不重启vmware虚拟机添加硬盘 首先控制台新加一块100G的硬盘 此时虚拟机内部是识别不到的&#xff0c;lsblk查看如下 不重启的情况下&#xff0c;采用如下方案 步骤一、查找主机…

智慧港口大屏可视化产品原型设计

全球贸易的快速发展和技术的不断进步&#xff0c;港口作为国际贸易的重要枢纽&#xff0c;其运营效率和管理水平直接影响到全球供应链的顺畅。智慧港口的概念应运而生&#xff0c;旨在通过新一代信息技术&#xff0c;将港口相关业务和管理创新深度融合&#xff0c;实现港口的高…