# YOLOv3:基于 PyTorch 的目标检测模型实现

news2025/5/14 15:56:55

YOLOv3:基于 PyTorch 的目标检测模型实现

引言

YOLOv3(You Only Look Once)是一种流行的单阶段目标检测算法,它能够直接在输入图像上预测边界框和类别概率。YOLOv3 的优势在于其高效性和准确性,使其在实时目标检测任务中表现出色。本文将详细介绍如何使用 PyTorch 实现 YOLOv3 模型,并提供完整的代码实现。

1. YOLOv3 简介

YOLOv3 是 YOLO 系列算法的第三个版本,它在前两个版本的基础上进行了改进,提高了检测的准确性和速度。YOLOv3 的主要特点包括:

  • 单阶段检测:YOLOv3 直接在输入图像上预测边界框和类别概率,无需生成候选框。
  • 多尺度检测:YOLOv3 使用三个不同尺度的特征图进行检测,能够检测不同大小的目标。
  • 高效率:YOLOv3 的设计使其能够在实时应用中高效运行。

2. 环境准备

在开始实现之前,确保你已经安装了以下必要的依赖库:

pip install torch numpy matplotlib

3. 代码实现

3.1 导入必要的库

from __future__ import division

import torch
import torch.nn as nn
import torch.nn.functional as F
from torch.autograd import Variable
import numpy as np

from utils.parse_config import *  # 用于解析配置文件
from utils.utils import build_targets, to_cpu, non_max_suppression  # 用于目标构建和后处理

import matplotlib.pyplot as plt
import matplotlib.patches as patches

3.2 构建模块列表

create_modules 函数根据配置文件构建网络层:

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()  # 用于存储网络层的 ModuleList

    for module_i, module_def in enumerate(module_defs):
        modules = nn.Sequential()  # 用于线性堆叠网络层

        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))
            if module_def["activation"] == "leaky":
                # 添加 LeakyReLU 激活函数
                modules.add_module(f"leaky_{module_i}", nn.LeakyReLU(0.1))

        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)

        elif module_def["type"] == "upsample":
            # 获取上采样层的参数
            upsample = Upsample(scale_factor=int(module_def["stride"]), mode="nearest")
            modules.add_module(f"upsample_{module_i}", upsample)

        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())  # 添加空层

        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":
            # 获取 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]
            num_classes = int(module_def["classes"])
            img_size = int(hyperparams["height"])
            # 定义检测层
            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

3.3 上采样层

Upsample 类实现上采样操作:

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):
        # 使用 PyTorch 的 interpolate 函数进行上采样
        x = F.interpolate(x, scale_factor=self.scale_factor, mode=self.mode)
        return x

3.4 空层

EmptyLayer 类用于占位,例如在 routeshortcut 层中:

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

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

3.5 YOLO 检测层

YOLOLayer 类负责预测边界框、置信度和类别概率:

class YOLOLayer(nn.Module):
    """Detection layer"""

    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()  # 均方误差损失
        self.bce_loss = nn.BCELoss()  # 二元交叉熵损失
        self.obj_scale = 1  # 有目标的损失权重
        self.noobj_scale = 100  # 无目标的损失权重
        self.metrics = {}  # 用于存储评估指标

        self.img_dim = img_dim  # 输入图像尺寸
        self.grid_size = 0  # 网格大小

    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  # 每个网格的像素大小
        # 计算每个网格的偏移量
        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):
        """前向传播"""
        FloatTensor = torch.cuda.FloatTensor if x.is_cuda else torch.FloatTensor
        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)
            .permute(0, 1, 3, 4, 2)
            .contiguous()
        )
        # 提取预测结果
        x = torch.sigmoid(prediction[..., 0])  # 中心点 x
        y = torch.sigmoid(prediction[..., 1])  # 中心点 y
        w = prediction[..., 2]  # 宽度
        h = prediction[..., 3]  # 高度
        pred_conf = torch.sigmoid(prediction[..., 4])  # 置信度
        pred_cls = torch.sigmoid(prediction[..., 5:])  # 类别预测

        # 如果网格大小不匹配,重新计算偏移量
        if grid_size != self.grid_size:
            self.compute_grid_offsets(grid_size, cuda=x.is_cuda)

        # 添加偏移量并缩放锚框
        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
        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,
            )
            # 计算损失
            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
            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

            # 计算评估指标
            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

3.6 YOLOv3 模型

Darknet 类是 YOLOv3 模型的主体,负责加载配置文件、构建网络、前向传播、加载和保存权重:

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")]  # 提取 YOLO 层
        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":
                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":
                x, layer_loss = module[0](x, targets, img_dim)
                loss += layer_loss
                yolo_outputs.append(x)
            layer_outputs.append(x)
        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):
        """加载 Darknet 权重"""
        with open(weights_path, "rb") as f:
            header = np.fromfile(f, dtype=np.int32, count=5)  # 读取头部信息
            self.header_info = header
            self.seen = header[3]
            weights = np.fromfile(f, dtype=np.float32)  # 读取权重

        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"]:
                    bn_layer = module[1]
                    num_b = bn_layer.bias.numel()
                    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
                    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
                    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
                    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:
                    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
                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):
        """保存 Darknet 权重"""
        fp = open(path, "wb")
        self.header_info[3] = self.seen
        self.header_info.tofile(fp)
        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 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)
                else:
                    conv_layer.bias.data.cpu().numpy().tofile(fp)
                conv_layer.weight.data.cpu().numpy().tofile(fp)
        fp.close()

4. 使用示例

以下是一个简单的示例代码,用于加载 YOLOv3 模型并进行推理:

import torch
from models import *  # 确保你的模型定义在 models 模块中

# 加载配置文件和权重
config_path = "path/to/your/yolov3.cfg"
weights_path = "path/to/your/yolov3.weights"
img_size = 416

model = Darknet(config_path, img_size=img_size)
model.load_darknet_weights(weights_path)

# 设置为评估模式
model.eval()

# 加载输入图像
# 假设你有一个输入图像 tensor,尺寸为 (1, 3, 416, 416)
input_image = torch.randn(1, 3, img_size, img_size)

# 前向传播
with torch.no_grad():
    detections = model(input_image)

# 打印检测结果
print(detections)

5. 结论

本文详细介绍了如何使用 PyTorch 实现 YOLOv3 模型。通过加载配置文件和权重,我们可以轻松地构建和使用 YOLOv3 模型进行目标检测。YOLOv3 的高效性和准确性使其在实时应用中表现出色,适用于多种目标检测任务。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

MySQL的Docker版本,部署在ubantu系统

前言 MySQL的Docker版本,部署在ubantu系统,出现问题: 1.执行一个SQL,只有错误编码,没有错误提示信息,主要影响排查SQL运行问题; 2.这个问题,并不影响实际的MySQL运行,如…

Mac QT水平布局和垂直布局

首先上代码 #include "mainwindow.h" #include "ui_mainwindow.h" #include <QPushButton> #include<QVBoxLayout>//垂直布局 #include<QHBoxLayout>//水平布局头文件 MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), …

回答 | 图形数据库neo4j社区版可以应用小型企业嘛?

刚在知乎上看到了一个提问&#xff0c;挺有意思&#xff0c;于是乎&#xff0c;贴到这里再简聊一二。 转自知乎提问 当然可以&#xff0c;不过成本问题不容小觑。另外还有性能上的考量。 就在最近&#xff0c;米国国家航空航天局——NASA因为人力成本问题&#xff0c;摒弃了使…

Linux操作系统从入门到实战(二)手把手教你安装VMware17pro与CentOS 9 stream,实现Vim配置,并配置C++环境

Linux操作系统从入门到实战&#xff08;二&#xff09;手把手教你安装VMware17pro与CentOS 9.0 stream&#xff0c;实现Vim配置&#xff0c;并编译C文件 前言一、安装VMware17pro二、安装CentOS9.02.1 为什么选择CentOS9&#xff0c;与CentOS7对比2.1 官网下载CentOS9.02.2 国内…

软考架构师考试-UML图总结

考点 选择题 2-4分 案例分析0~1题和面向对象结合考察&#xff0c;前几年固定一题。近3次考试没有出现。但还是有可能考。 UML图概述 1.用例图&#xff1a;描述系统功能需求和用户&#xff08;参与者&#xff09;与系统之间的交互关系&#xff0c;聚焦于“做什么”。 2.类图&…

论文学习_Trex: Learning Execution Semantics from Micro-Traces for Binary Similarity

摘要&#xff1a;检测语义相似的函数在漏洞发现、恶意软件分析及取证等安全领域至关重要&#xff0c;但该任务面临实现差异大、跨架构、多编译优化及混淆等挑战。现有方法多依赖语法特征&#xff0c;难以捕捉函数的执行语义。对此&#xff0c;TREX 提出了一种基于迁移学习的框架…

在VirtualBox中安装虚拟机后不能全屏显示的问题及解决办法

在VirtualBox中安装Windows或Linux虚拟机后&#xff0c;将遇到启动虚拟机后&#xff0c;只能在屏幕中的一块区域里显示虚拟机桌面&#xff0c;却不能全屏显示的问题。要解决此问题&#xff0c;需要在虚拟机中安装与VirtualBox版本相对应的VBox GuestAdditons软件。 这里…

element-ui分页的使用及修改样式

1.安装 npm install element-ui -S 2.在main.js中引入,这里是全部引入&#xff0c;也可以按需引入 import ElementUI from element-ui import element-ui/lib/theme-chalk/index.css Vue.use(ElementUI) 3.使用 layout"prev, pager, next, jumper" &#xff1a;jumpe…

从数据中台到数据飞轮:数字化转型的演进之路

从数据中台到数据飞轮&#xff1a;数字化转型的演进之路 数据中台 数据中台是企业为整合内部和外部数据资源而构建的中介层&#xff0c;实现数据的统一管理、共享和高效利用&#xff0c;目标是打破信息孤岛&#xff0c;提高数据使用效率&#xff0c;支持业务决策和创新 实施成本…

2025年5月-信息系统项目管理师高级-软考高项一般计算题

决策树和期望货币值 加权算法 自制和外购分析 沟通渠道 三点估算PERT 当其他条件一样时&#xff0c;npv越大越好

zst-2001 上午题-历年真题 算法(5个内容)

回溯 算法 - 第1题 找合适的位置&#xff0c;如果没有位置就按B回家 d 分治 算法 - 第2题 b 算法 - 第3题 a 算法 - 第4题 划分一般就是分治 a 算法 - 第5题 分治 a 0-1背包 算法 - 第6题 c 算法 - 第7题 最小的为c 3100 c 算法 - 第8题 …

udp多点通信和心跳包

刷题 # UDP多点通信核心要点## 基础通信模式### 单播通信- 一对一通信方式- UDP默认通信模式- 地址指向具体目标主机### 广播通信- 一对多通信机制- 地址范围&#xff1a;xxx.xxx.xxx.255- 仅限局域网传输- 需设置SO_BROADCAST标志### 组播通信- 多对多群组通信- 地址范围&…

音视频学习:使用NDK编译FFmpeg动态库

1. 环境 1.1 基础配置 NDK 22b (r22b)FFmpeg 4.4Ubuntu 22.04 1.2 下载ffmpeg 官网提供了 .tar.xz 包&#xff0c;可以直接下载解压&#xff1a; wget https://ffmpeg.org/releases/ffmpeg-4.4.tar.xz tar -xvf ffmpeg-4.4.tar.xz cd ffmpeg-4.41.3 安装基础工具链 sudo …

如何使用 Qwen3 实现 Agentic RAG?

今天&#xff0c;我们将学习如何部署由阿里巴巴最新Qwen 3驱动的Agentic RAG。 这里是我们的工具栈&#xff1a; CrewAI用于代理编排。 Firecrawl用于网络搜索。 LightningAI的LitServe用于部署。 顶部的视频展示了这一过程。 图表显示了我们的Agentic RAG流程&#xff1…

相机、雷达标定工具,以及雷达自动标定的思路

本篇我们来看一下自动驾驶传感器配置一个非常重要的模块&#xff0c;也就是传感器的标定。这里主要是对我之前修改的功能包的使用进行一个介绍. 对应的资源也已经上传了&#xff0c;0积分下载 安装 首先整个项目是使用ros1来进行启动的,但是要想正常编译,需要先安装三个对应的…

vsomeip环境搭建保姆级教程

vsomeip环境搭建保姆级教程 ubuntu环境搭建 {% links %} site: VMware搭建ubuntu保姆级教程 url: https://zhuanlan.zhihu.com/p/1903219373906327339 desc: flechazo image: https://q1.qlogo.cn/g?b=qq&nk=2861099&s=5 color: “#9d5b8b” {% endlinks %} vsomei…

我的MCP相关配置记录

1.VSCode的Cline中的MCP {"mcpServers": {"github.com/modelcontextprotocol/servers/tree/main/src/github": {"autoApprove": [],"disabled": false,"timeout": 60,"command": "cmd","args&quo…

我们来学nacos -- 集群nacos2.5.1mysql8.4

2.5.1集群搭建 架构下载解压到3个文件夹初始化数据库&数据迁移检查端口可用配置cluster.confapplication.properties 使用mysql8.4的jar启动db.num is null报错datasource错误成功 nginx反向代理集群查看 架构 其中包含3个nacos节点&#xff0c;然后一个负载均衡器代理3个…

Rollup入门与进阶:为现代Web应用构建超小的打包文件

我们常常面临Webpack复杂配置或是Babel转译后的冗余代码&#xff0c;结果导致最终的包体积居高不下加载速度也变得异常缓慢&#xff0c;而在众多打包工具中Rollup作为一个轻量且高效的选择&#xff0c;正悄然改变着这一切&#xff0c;本文将带你深入了解这个令人惊艳的打包工具…

专题四:综合练习( 找出所有子集的异或总和再求和)

以leetcode1863题为例 题目分析&#xff1a; 找到每个子集&#xff0c;然后子集中的元素异或之后全部相加 算法原理分析&#xff1a; 画决策树&#xff1a;第一层为这个子集有一个元素 第二层这个子集有两个元素 从上往下罗列&#xff0c;把所有子集都罗列出来&#xf…