深度学习:yolo的使用--建立模型

使用argparse模块来定义和解析命令行参数

创建一个ArgumentParser对象

python 复制代码
parser = argparse.ArgumentParser()

训练的轮数,每批图像的大小,更新模型参数之前累积梯度的次数,模型定义文件的路径。

python 复制代码
parser.add_argument("--epochs", type=int, default=100, help="number of epochs") #训练次数
    parser.add_argument("--batch_size", type=int, default=1, help="size of each image batch")   #batch的大小
    parser.add_argument("--gradient_accumulations", type=int, default=2, help="number of gradient accums before step")#在每一步(更新模型参数)之前累积梯度的次数"
    parser.add_argument("--model_def", type=str, default="config/yolov3.cfg", help="path to model definition file") #模型的配置文件
    

数据配置文件的路径,从预训练的模型权重开始训练,生成批次数据时使用的CPU线程数。

python 复制代码
parser.add_argument("--data_config", type=str, default="config/coco.data", help="path to data config file") #数据的配置文件
    parser.add_argument("--pretrained_weights", type=str, help="if specified starts from checkpoint model") #预训练文件
    parser.add_argument("--n_cpu", type=int, default=0, help="number of cpu threads to use during batch generation")#数据加载过程中应使用的CPU线程数。
    

每张图像的尺寸,每隔多少个epoch保存一次模型权重,每隔多少个epoch在验证集上进行一次评估,每十批计算一次平均精度(mAP),是否允许多尺度训练,

python 复制代码
parser.add_argument("--img_size", type=int, default=416, help="size of each image dimension")
    parser.add_argument("--checkpoint_interval", type=int, default=20, help="interval between saving model weights")#隔多少个epoch保存一次模型权重
    parser.add_argument("--evaluation_interval", type=int, default=20, help="interval evaluations on validation set")#多少个epoch进行一次验证集的验证
    parser.add_argument("--compute_map", default=False, help="if True computes mAP every tenth batch")#
    parser.add_argument("--multiscale_training", default=True, help="allow for multi-scale training")

使用parse_args方法解析命令行参数

python 复制代码
opt = parser.parse_args()

使用TensorFlow 2.0以上版本中的tf.summary模块创建日志记录器(Logger)

python 复制代码
import tensorflow as tf  # 导入TensorFlow库,并简写为tf
# 确保使用的是TensorFlow 2.0或更高版本

class Logger(object):
    def __init__(self, log_dir):
        """
        Create a summary writer logging to log_dir.
        这个类的构造函数接受一个参数log_dir,它表示日志文件将要保存的目录。
        函数的作用是创建一个日志记录器,用于记录TensorFlow的摘要信息(例如训练过程中的损失、准确率等)。
        """
        self.writer = tf.summary.create_file_writer(log_dir)  # 创建一个文件写入器,用于将摘要信息写入到指定的日志目录

调用Logger,创建目录

python 复制代码
logger = Logger("logs")
# 创建Logger类的实例,并将日志目录设置为"logs"。这意味着所有的日志信息将被写入到当前工作目录下的"logs"文件夹中。

device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
# 这行代码使用PyTorch的torch.device来确定运行设备。如果系统有可用的CUDA(即NVIDIA的GPU),则device将被设置为"cuda",否则将使用CPU。

os.makedirs("output", exist_ok=True)
# 使用os模块的makedirs函数创建一个名为"output"的目录。exist_ok=True参数意味着如果"output"目录已经存在,不会抛出错误。

os.makedirs("checkpoints", exist_ok=True)
# 类似地,这行代码创建一个名为"checkpoints"的目录。这个目录通常用于存储模型的检查点或保存的状态,以便后续可以恢复训练或进行模型评估。

定义 parse_data_config 的函数,它用于解析数据配置文件(文件内容分类种类,训练集路径,测试集路径,文件名称路径)

python 复制代码
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

调用函数

python 复制代码
    data_config = parse_data_config(opt.data_config)
    train_path = data_config["train"]
    valid_path = data_config["valid"]

定义了一个名为 load_classes 的函数,它用于从指定路径加载类别标签

python 复制代码
def load_classes(path):
    """
    Loads class labels at 'path'
    """
    fp = open(path, "r")
    names = fp.read().split("\n")[:-1]
    return names

调用函数

python 复制代码
class_names = load_classes(data_config["names"])

定义了一个名为 parse_model_config 的函数,它用于解析 YOLOv3 模型的配置文件

读取文件划分如卷积、池化、上采样、路由、快捷连接和 YOLO 层

python 复制代码
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

这个函数的目的是将 YOLOv3 模型配置文件中的文本描述转换成 PyTorch 可以理解的网络层模块。它首先处理超参数,然后逐个处理每个模块定义,根据模块的类型(如卷积、池化、上采样、路由、快捷连接和 YOLO 层)创建相应的 PyTorch 层,并添加到 module_list 中。

python 复制代码
def create_modules(module_defs):
    """
    Constructs module list of layer blocks from module configuration in module_defs
    """
    # 从模块定义列表中弹出第一个元素,它包含了超参数(hyperparameters),例如输入图像的尺寸等。
    hyperparams = module_defs.pop(0)
    
    # 初始化输出过滤器列表,它将存储每一层的输出通道数(即卷积核的数量)。
    # 这里假设第一个超参数中的 'channels' 键对应的值是网络输入层的通道数。
    output_filters = [int(hyperparams["channels"])]

    # 创建一个 PyTorch 的 ModuleList 对象,用于存储网络层模块。
    module_list = nn.ModuleList()

    # 遍历模块定义列表,module_i 是索引,module_def 是当前模块的定义。
    for module_i, module_def in enumerate(module_defs):
        # 对于每个模块,创建一个 PyTorch 的 Sequential 对象,用于线性堆叠网络层。
        modules = nn.Sequential()

        # 如果模块类型是 "convolutional":
        if module_def["type"] == "convolutional":
            # 获取当前模块是否使用批归一化(batch normalization)。
            bn = int(module_def["batch_normalize"])
            # 获取卷积核的数量(即输出通道数)。
            filters = int(module_def["filters"])
            # 获取卷积核的大小。
            kernel_size = int(module_def["size"])
            # 计算填充值,以保持输出尺寸与输入尺寸相同。
            pad = (kernel_size - 1) // 2
            # 添加一个卷积层到 Sequential 对象中。
            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))
            # 如果激活函数是 "leaky",则添加一个 LeakyReLU 激活层。
            if module_def["activation"] == "leaky":
                modules.add_module(f"leaky_{module_i}", nn.LeakyReLU(0.1))

        # 如果模块类型是 "maxpool":
        elif module_def["type"] == "maxpool":
            # 获取池化层的大小和步长。
            kernel_size = int(module_def["size"])
            stride = int(module_def["stride"])
            # 如果池化核大小为2且步长为1,添加一个填充层以保持尺寸。
            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)

        # 如果模块类型是 "upsample":
        elif module_def["type"] == "upsample":
            # 添加一个上采样层。
            upsample = Upsample(scale_factor=int(module_def["stride"]), mode="nearest")
            modules.add_module(f"upsample_{module_i}", upsample)

        # 如果模块类型是 "route":
        elif module_def["type"] == "route":
            # 获取路由层的层索引。
            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())

        # 如果模块类型是 "shortcut":
        elif module_def["type"] == "shortcut":
            # 获取快捷连接的输入通道数。
            filters = output_filters[1:][int(module_def["from"])]
            # 添加一个空层作为快捷连接层。
            modules.add_module(f"shortcut_{module_i}", EmptyLayer())

        # 如果模块类型是 "yolo":
        elif module_def["type"] == "yolo":
            # 获取 YOLO 层的锚点索引和锚点值。
            anchor_idxs = [int(x) for x in module_def["mask"].split(",")]
            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]
            # 获取 YOLO 层的类别数和图像尺寸。
            num_classes = int(module_def["classes"])
            img_size = int(hyperparams["height"])
            # 创建一个 YOLO 层并添加到模块中。
            yolo_layer = YOLOLayer(anchors, num_classes, img_size)
            modules.add_module(f"yolo_{module_i}", yolo_layer)

        # 将构建好的模块添加到模块列表中。
        module_list.append(modules)
        # 更新输出过滤器列表,添加当前模块的输出通道数。
        output_filters.append(filters)

    # 返回超参数和构建好的模块列表。
    return hyperparams, module_list

定义了一个名为 Darknet 的类,它是用于构建 YOLOv3 目标检测模型的 PyTorch 神经网络类。

python 复制代码
class Darknet(nn.Module):
    """YOLOv3 object detection model"""
    # 这个类继承自 PyTorch 的 nn.Module 类,表示它是一个神经网络模型。
    # YOLOv3 是一个流行的目标检测算法,这个类实现了 YOLOv3 的网络结构。

    def __init__(self, config_path, img_size=416):
        super(Darknet, self).__init__()
        # 类的构造函数接受两个参数:config_path(模型配置文件的路径)和 img_size(输入图像的尺寸,默认为416)。
        # super() 函数用于调用父类的构造函数,即初始化 PyTorch 的 nn.Module。

        self.module_defs = parse_model_config(config_path)
        # 调用 parse_model_config 函数解析配置文件,并存储解析后的模块定义。

        self.hyperparams, self.module_list = create_modules(self.module_defs)
        # 调用 create_modules 函数根据模块定义创建网络层,并存储超参数和模块列表。

        self.yolo_layers = [layer[0] for layer in self.module_list if hasattr(layer[0], "metrics")]
        # 从模块列表中提取出包含 'metrics' 属性的层,这些层通常是 YOLO 层,用于目标检测。
        # hasattr() 函数检查对象是否具有给定的属性。

        self.img_size = img_size
        # 存储输入图像的尺寸。

        self.seen = 0
        # 用于跟踪训练过程中看到的数据量(例如,图像数量)。

        self.header_info = np.array([0, 0, 0, self.seen, 0], dtype=np.int32)
        # 创建一个 NumPy 数组,用于存储与模型相关的头部信息,如版本、修订号、seen、未知字段和 epoch 数。

调用函数

python 复制代码
    model = Darknet(opt.model_def).to(device)
    model.apply(weights_init_normal)#model.apply(fn)表示将fn函数应用到神经网络的各个模块上,包括该神经网络本身。这通常在初始化神经网络的参数时使用,本处用于初始化神经网络的权值
相关推荐
Mintopia12 分钟前
OpenClaw 对软件行业产生的影响
人工智能
陈广亮1 小时前
构建具有长期记忆的 AI Agent:从设计模式到生产实践
人工智能
会写代码的柯基犬1 小时前
DeepSeek vs Kimi vs Qwen —— AI 生成俄罗斯方块代码效果横评
人工智能·llm
Mintopia1 小时前
OpenClaw 是什么?为什么节后热度如此之高?
人工智能
爱可生开源社区2 小时前
DBA 的未来?八位行业先锋的年度圆桌讨论
人工智能·dba
叁两4 小时前
用opencode打造全自动公众号写作流水线,AI 代笔太香了!
前端·人工智能·agent
前端付豪5 小时前
LangChain记忆:通过Memory记住上次的对话细节
人工智能·python·langchain
strayCat232555 小时前
Clawdbot 源码解读 7: 扩展机制
人工智能·开源
程序员打怪兽5 小时前
详解Visual Transformer (ViT)网络模型
深度学习