目标检测—DiffusionDet:用于检测的概率扩散模型笔记—DDPM

news2024/11/17 7:20:16

目录

一、Introduction

二、相关工作

三、方法

1.准备工作

2.架构

3、训练

4.预测过程

四. 实验

1.训练策略

2.main property

3.消融实验

五、代码分析

1.测试 demo.py

2.训练 train-net.py

总结


DiffusionDet:将目标检测问题构建为一个从带噪框到目标框的降噪扩散过程。在训练阶段,目标框从GT框扩散到随机分布,模型学习如何逆转这个噪声过程;在推理阶段,模型将一系列随机的框逐步改善为输出结果。在MS-COCO和LVIS数据集上,DiffusionDet和已有的目标检测器相比具有较好的性能。本文在目标检测方面带来了两个重要发现:1. 随机的框虽然和预设的anchors或者学习好的queries有很大的不同,但也是有效的目标候选者;2. 目标检测这种代表性的感知任务,也可以通过生成方式解决。

一、Introduction

目标检测的目的是预测一幅图像中目标对象的一组边界框和相关的类别标签。作为一项基本的视觉识别任务,它已成为许多相关识别场景的基石,如实例分割、姿态估计、动作识别、目标跟踪和视觉关系检测。

现代对象检测方法的发展随着候选对象的发展而不断发展,即从经验对象先验(faster-Rcnn、SSD等)到可学习的对象查询(ViT,Sparse r-cnn,DETR)。具体来说,大多数检测器通过对经验设计的候选对象定义代理回归和分类来解决检测任务,如滑动窗口、区域建议、锚框和参考点。DETR 提出了可学习的对象查询来消除手工设计的组件,并建立了一个端到端检测管道,这在基于查询的检测范式上引起了极大的关注。它们依赖于一组固定的可学习的查询。是否有一种更简单的方法,甚至不需要可学习查询的代理?我们设计一个新的框架,可以直接检测来自一组纯随机box中的对象,它不包含在训练中进行优化的可学习参数,我们期望逐步细化这些盒子的位置和大小,直到它们完美地覆盖目标对象

动机如下图所示。认为 noise-to-box 范式的哲学类似于去噪扩散模型中的噪声到图像过程,这是一类基于似然的模型,通过学习的去噪模型逐步从图像中去除噪声来生成图像。扩散模型在许多生成任务[3,4,37,63,85]中取得了巨大的成功,并开始在图像分割[1,5,6,12,28,42,89]等感知任务中进行探索

 

DiffusionDet,将检测作为生成任务投射在图像中的位置(中心坐标)和边界框的空间和大小(宽度和高度)上来处理目标检测任务。在训练阶段,将由

  1. 方差 schedual控制的高斯噪声添加到groundtruth 中,获得噪声box。
  2. 然后利用这些噪声box,从backbone encoder(如ResNet Swin)的输出特征图中,裁剪感兴趣区域(RoI)特征。
  3. 最后,将这些RoI特征发送到检测 decoder,来预测无噪声的gt box。有了这个训练目标,扩散网能够从随机盒子中预测地面真实盒子。

推理阶段,扩散det通过反转学习扩散过程生成边界盒,将噪声先验分布调整到边界框上的学习分布。

DiffusionDet的 noise to box 具有 Once-for-All 的优点:我们可以对网络进行一次训练,并在不同的设置下使用相同的网络参数进行推理。

  1. 动态框利用随机个 box 作为候选对象,DiffusionDet 解耦训练和评估。可以用N随机box进行训练,同时用N随机box进行评估(N是任意值)
  2. 渐进式细化:扩散模型的迭代细化,有利于优化扩散模型。可调整去噪采样步骤的数量来提高检测精度或加快推理速度,这种灵活性使其能够适应不同的检测场景。

二、相关工作

1.目标检测

大多数现代 object detection 方法对经验目标先验进行边框回归和类别分类,如proposal、anchor、point方法。Carion等人提出了DETR ,使用一组固定的可学习查询来检测对象。我们利用 DiffusionDet 进一步推进了目标检测pipline 的开发,如图所示。

2.Diffusion Model。

扩散模型作为一类深度生成模型,从随机分布的样本出发,通过逐步去噪的过程恢复数据样本。扩散模型最近在计算机视觉、自然语言处理、音频处理、跨学科应用等领域取得了显著的成果。

3.扩散模型的感知任务。

虽然扩散模型在图像生成方面取得了巨大的成功,但它们在鉴别任务方面的潜力还有待充分探索。一些先锋工作尝试采用扩散模型进行图像分割任务[1,5,6,12,28,42,89],

例如,Chen等人[4]采用位扩散模型[8]进行图像和视频的全光分割[ Panoptic segmentation.]。因为分割任务是以图像到图像的方式处理的,这在概念上更类似于图像生成任务,而目标检测是一个集合预测问题[10],它需要将候选对象分配给groundtruth。

三、方法

1.准备工作

目标检测
目标检测的学习目标是输入-目标对 (x、b、c) ,其中x是输入图像,b和c分别是图像x中对象的一组边界框和类别标签。即集合中的第i个框表示为b =(c,c,w,h),c 是边界框的中心坐标,w\h分别是该边界框的宽度和高度。

扩散模型
扩散模型是一类基于概率的模型,其灵感来自于非平衡热力学。这些模型通过逐步向样本数据中添加噪声,定义了扩散正向过程的马尔可夫链。正向噪声过程的定义为

 

通过向 z 中添加噪声,将数据样本z 转换为t∈{0,1,…,T}的潜在噪声样本 z。在训练过程中,训练一个神经网络f(zt,t),通过最小化训练目标来从 z 中预测 z: 


在这项工作中,我们的目标是通过扩散模型来解决目标检测任务。在我们的设置中,数据样本是一组边界框z = b,其中b∈R是一组N个框。训练神经网络 f(z,t,x) 从噪声框 z 中预测 z,相应地生成相应的类别标签c

2.架构

由于扩散模型是迭代生成数据样本,因此在推理阶段需要多次运行模型 f。然而,在每一个迭代步骤中,直接将fθ应用于原始图像将是难以计算的。因此,我们建议将整个模型分为两部分,图像编码器检测解码器,前者只运行一次,从原始输入图像 x 提取深度特征,后者把这个深层功能作为条件(而非原始图像输入),利用噪声 z 逐步完善bounding box

2.1 图像 encoder
将原始图像作为输入,并提取其高级特征,可使用卷积神经网络如ResNet 和Swin 模型来实现 DiffusionDet。FPN 用于生成多尺度特征图。

2.2 Detection decoder

借鉴于 Sparse R-CNN, 检测解码器以一组建议框作为输入,从图像编码器生成的特征图中裁剪roi特征,并将这些roi特征发送到检测头,获得回归和分类结果。检测解码器由6个级联阶段组成,与Sparse R-CNN中的解码器的区别在于:(1)扩散det从随机的 box 开始,而Sparse R-CNN使用一组固定的学习盒子进行推理;(2)稀疏R-CNN将建议box及其相应特征作为输入对,而扩散网只需要建议box;(3)扩散网在迭代采样步骤中重复使用探测器头,参数在不同步骤中共享,每个步骤通过时间步嵌入指定到扩散过程中,而稀疏R-CNN在前向传递中只使用检测解码器一次。

3、训练

训练中,首先构造从GT box 到噪声 box 的扩散过程,然后训练模型来逆转这个过程。算法1提供了扩散点训练过程的伪代码。

 

 

3.1 Groundtruth boxes 扩充.

由于感兴趣的实例数量通常因不同的图像而变化。因此,需将额外的box 填充到原始的 Groundtruth 中,则proposal的数量固定为 N。我们探索了几种填充策略,例如,重复现有的地面真实框concat 随机框或图像大小的框。这些策略的比较见第4.4节,连接随机框效果最好。

3.2 Box corruption 目标框的破坏
在 pad 后的GT box 中加入高斯噪声。噪声尺度由 α 控制(在等式中(1)),对不同时间步长t下的 α采用单调递减余弦调度。由于信噪比对扩散模型的性能有显著影响,因此GT box坐标也需要进行比例缩放。我们观察到,目标检测倾向于一个比图像生成标准高的的信号缩放值。更多的讨论见第4.4节。

3.3 训练损失
检测检测器以 N 加噪box作为输入,预测类别分类和 N 框坐标的预测。我们将集预测损失应用于 N 预测集。我们通过最优传输分配方法选择成本最小的前k个预测,为每个地面真相分配多个预测。

4.预测过程

DiffusionDet 的推理过程是一个从噪声到目标框的去噪采样过程。从在高斯分布中采样的盒子开始,该模型逐步细化其预测:

 

4.1采样步骤
随机box或最后一个采样步骤中的估计box ,被发送到检测解码器中,以预测类别分类和 box 坐标。在获得当前步骤的box后,采用DDIM对下一步的box 进行估算。我们注意到,将不带DDIM的预测框发送到下一步也是一种可选的渐进细化策略。然而,如第4.4节所述,它会带来严重的恶化。

4.2 方框更新
在每个采样步骤之后,预测的 box 可以粗略地分为两种类型,期望预测非期望预测。期望预测包含正确位于相应对象上的框,而非期望预测是任意分布的。直接将这些非期望box发送到下一个采样迭代不会带来好处,因为它们的分布不是由训练中的box损坏构建的。为了使推理更好地与训练相一致,我们提出了box更新的策略,通过用随机的box来恢复这些非期望的box。具体来说,首先过滤掉分数低于特定阈值的不期望的方框。然后,我们将剩余的box与从高斯分布中抽样的新的随机box连接起来

4.3 一劳永逸
由于随机box的设计,我们可以用任意数量的随机box和采样步数来评估DiffusionDet ,这不需要等于训练阶段。作为比较,以前的方法在训练和评估过程中依赖于相同数量的处理过的box,它们的检测解码器在正向传递中只使用一次。

四. 实验

1.训练策略

ResNet和Swin主干分别在ImageNet-1K和ImageNet-21K上预训练。

新添加的 detect decoder 用Xavier init 进行初始化,用AdamW优化器,初始学习率为2.5×10−5,权重衰减为10−4。模型在8个gpu上,用16的 batchsize 训练。

对于MS-COCO,450K个iter,在350K和420K迭代时,学习率除以10。对于LVIS,训练iter 为210K、250K、270K。数据增强策略包括随机水平翻转,调整输入图像大小的比例抖动,最短边至少480像素,最多800,最长边最多1333。不使用EMA和一些强大的数据增强,如MixUp [98]或Mosaic

2.main property

扩散法的主要性质在于对所有推理情况的一次训练。一旦模型被训练好了,它就可以用于在推理中改变方框的数量和样本步骤的数量,如图所示。扩散det可以通过使用更多的box或更多的细化步骤,以更高延迟为代价,从而实现更高的准确性。因此,我们可以在多个场景中部署一个扩散网络,并在不需要再训练网络的情况下获得一个期望的速度-精度的权衡。

3.消融实验

Signal scaling
信号尺度因子控制着扩散过程的信噪比(SNR)。我们研究了比例因子的影响。结果表明,2.0的比例因子达到了最佳的AP性能,优于图像生成任务的标准值1.0和全景分割的标准值0.1。我们考虑到,这是因为一个盒子只有四个表示参数,即中心坐标(cx,cy)和盒子大小(w,h),这与图像生成中只有四个像素的图像大致相似。盒子表示比密集表示更脆弱,例如,在全光分割中的512×512掩模表示。因此,与图像生成和全光分割相比,扩散det更喜欢一个增加信噪比的更容易的训练目标

五、代码分析

1.测试 demo.py

 

##-------------------1.提取图像特征-----------------------
src = self.backbone(images.tensor)
features =[src['p2'],src['p3'],src['p4'],src['p5']]

results = self.ddim_sample(batched_inputs, features, images_whwh, images)
  # images:(800,1333), normal to(0,1)
  def ddim_sample(self, batch, feats, whwh, images, clip_denoised=True, do_postprocess=True):
        
        shape =(batch, self.num_proposals,4)         # (1,500,4)
        total_timesteps, sampling_timesteps, eta, objective = self.num_timesteps, self.sampling_timesteps, self.ddim_sampling_eta, self.objective
        # T=1000, sampling_timesteps=1, eta=1, objective='pred_x0'
       
        time_pairs =(999,-1)
        img = torch.randn(shape, device)               # (1,500,4)
        
        preds, outputs_class, outputs_coord = self.model_predictions(backbone_feats,
        images_whwh, img, time=999, self_cond=None, clip_x_start=clip_denoised)
        
# ----------------2. 随机生成bbox,循环解码6次----------------
#(随机噪声映射到图像大小,提取对应特征并继续解码)
            for head_idx, rcnn_head inenumerate(self.head_series):            
                class_logits, bboxes, proposal_features =rcnn_head(features, bboxes, proposal_features, self.box_pooler, time)

                roi_features =pooler(features, proposal_boxes)     # (1,500,4)->(500,256,7,7)-->(500,256,49)

                pro_features = roi_features.view(N, nr_boxes, self.d_model,-1).mean(-1)         # (500,1,256)


                # 1.自注意力
                pro_features2 = self.self_attn(pro_features, pro_features, value=pro_features)[0]    # (500,1,256)->(500,1,256) 

                # 2.inst_interact.
                pro_features2 = self.inst_interact(pro_features, roi_features)   # conv+linear+prod ->(500,256)

                # 3.fuse_time_embedding
                scale_shift = self.block_time_mlp(time_emb)          # conv ->(1,512)
                scale_shift = torch.repeat_interleave(scale_shift, nr_boxes, dim=0)    # (500,512)
                scale, shift = scale_shift.chunk(2, dim=1)        # (500,256)(500,256)
                fc_feature = fc_feature *(scale +1)+ shift     # (500,256)

                # 4.分类/回归
                class_logits = self.class_logits(fc_feature)     # conv->(500,80)
                bboxes_deltas = self.bboxes_delta(fc_feature)    # conv->(500,4)
                pred_bboxes = self.apply_deltas(bboxes_deltas, bboxes.view(-1,4))    # decode-> xyxy
                outputs_class.append(class_logits)      # (6,500,80)
                outputs_coord.append(pred_bboxes)       # (6,500,4)
 
 #--------------------3.-----------------------               
           x_start = outputs_coord[-1]/images_whwh      # 取最后一次结果,做归一化
           pred_noise = self.predict_noise_from_start(img, t=999, x_start)                # 输入输出dim相同
           # img(1,500,4)为最开始生成的噪声,X0(即x_start)为最终预测边界框,其具体代码为:(extract(self.sqrt_recip_alphas_cumprod, t, img.shape)* img - x_start)/extract(self.sqrt_recipm1_alphas_cumprod, t, img.shape)
              
#------------------------------4.nms与后处理---------------------------------
# 貌似没用到pred_noise和cls_score, 直接nms得到500个proposal中的230个目标
results = self.inference(box_cls, box_pred, images.image_sizes)           # nms 筛选并得到具体类别
r =detector_postprocess(results_per_image,300,500)                             # resize回原尺寸300,500

#------------------------------5.分数筛选---------------------
new_instances = instances[instances.scores > self.threshold=0.5]        #  (215)-->(9)

2.训练 train-net.py

0.提取特征
features = self.backbone(images.tensor)1.targets, x_boxes, noises, t = self.prepare_targets(gt_instances)
     def prepare_targets(self, targets):
         # 循环所有图像 batch
         gt_boxes =xyxy_to_cxcywh(gt_boxes/ image_size_xyxy)    # 归一化
         d_boxes, d_noise, d_t = self.prepare_diffusion_concat(gt_boxes)
              d_t = torch.randint(0,T=1000).long()         # 随机选 t
              d_noise = torch.randn( num_proposals=500,4)

              # 01.真实目标不够500,补充到500if num_gt < self.num_proposals:
                 box_placeholder = torch.randn(500- num_gt,4)/6.+0.5                # 3sigma=1/2-> sigma:1/6
                 x_start = torch.cat((gt_boxes, box_placeholder), dim=0)                  # (500,4) 
              
              # 02.预测分布 q(x_t-1|x_t, x_0)
                 d_boxes = self.q_sample(x_start=x_start, t=d_t, noise=d_noise)   # 即公式:
                     qrt_alphas_cumprod_t =extract(self.sqrt_alphas_cumprod, t, x_start.shape)
                     sqrt_one_minus_alphas_cumprod_t =extract(self.sqrt_one_minus_alphas_cumprod, t, x_start.shape)
                     x = sqrt_alphas_cumprod_t * x_start + sqrt_one_minus_alphas_cumprod_t * noise
                     d_boxes =box_cxcywh_to_xyxy(x)

      x_boxes = cat(d_boxes)
      target["labels"]= gt_classes # [29,0,1]       target["boxes"]= gt_boxes  # 归一化坐标
      target["boxes_xyxy"]= targets_per_image.gt_boxes    # (3,4)
      target["image_size_xyxy"]= image_size_xyxy
      image_size_xyxy_tgt = image_size_xyxy.unsqueeze(0).repeat(len(gt_boxes),1)
      target["image_size_xyxy_tgt"]= image_size_xyxy_tgt
      target["area"]= targets_per_image.gt_boxes.area()

x_boxes = x_boxes * images_whwh[:, None,:]         # (bs,500,4)
2.计算网络输出
outputs_class, outputs_coord = self.head(features, x_boxes, t, None)   # t:(8)
# outputs_class:(6,bs,500,80)  outputs_coord:(6,bs,500,4)

output ={'pred_logits': outputs_class[-1],'pred_boxes': outputs_coord[-1]}if self.deep_supervision:                                           # True
      output['aux_outputs']=[{'pred_logits': outputs_class[:-1],'pred_boxes': outputs_coord[:-1]}]3.计算损失
loss_dict = self.criterion(output, targets)      # Hungarian匹配损失
    indices, _ = self.matcher(outputs_class[-1], outputs_coord[-1], targets)
        fg_mask, is_in_boxes_and_center = self.get_in_boxes_info(box_xyxy_to_cxcywh(bz_boxes),            # absolute (3,4):x, y, w, h
                      box_xyxy_to_cxcywh(bz_gtboxs_abs_xyxy),  # absolute (500,4):x, y, w, h
                      expanded_strides=32)
        # 筛选出500个pro是否在3个gt的 (框内|中心点2.5范围内),返回两个值都是(500)×[True,False]
        # 01.IOU损失
        pair_wise_ious = ops.box_iou(bz_boxes, bz_gtboxs_abs_xyxy)    # 之间的iou(500,3)

        # 02.类别损失
        if self.use_focal:                      # True
            alpha = self.focal_loss_alpha       # 0.25
            gamma = self.focal_loss_gamma       # 2
            neg_cost_class =(1- alpha)*(bz_out_prob ** gamma)*(-(1- bz_out_prob +1e-8).log())   # (500,80)
            pos_cost_class = alpha *((1- bz_out_prob)** gamma)*(-(bz_out_prob +1e-8).log())
            cost_class = pos_cost_class[:, bz_tgt_ids]- neg_cost_class[:, bz_tgt_ids]                  # (500,3)

        # 03.距离损失
        cost_bbox = torch.cdist(bz_out_bbox_, bz_tgt_bbox_, p=1)    # (500,3)
        
        # 04.giou
        cost_giou =-generalized_box_iou(bz_boxes, bz_gtboxs_abs_xyxy)    # (500,3)

        # 05.总损失
        cost =5*cost_bbox+2*cost_class +2*cost_giou +100.0*(~is_in_boxes_and_center)
        cost[~fg_mask]= cost[~fg_mask]+10000.0                    # (500,3)
        
        indices_batchi, matched_qidx = self.dynamic_k_matching(cost, pair_wise_ious, bz_gtboxs.shape[0])
        # k=5 动态匹配:
              def dynamic_k_matching(self, cost, pair_wise_ious, num_gt):
                  matching_matrix = torch.zeros_like(cost)  # [300,num_gt]
                  ious_in_boxes_matrix = pair_wise_ious
                  n_candidate_k = self.ota_k

                  # Take the sum of the predicted value and the top 10 iou of gt with the largest iou as dynamic_k
                  topk_ious, _ = torch.topk(ious_in_boxes_matrix, n_candidate_k, dim=0)   # (500,3)->(5,3)
                  dynamic_ks = torch.clamp(topk_ious.sum(0).int(), min=1)                 # sum(5,3)->(3)for gt_idx inrange(num_gt):
                  _, pos_idx = torch.topk(cost[:, gt_idx], k=dynamic_ks[gt_idx].item(), largest=False)     # [376]500中分数最大的那一个索引
                  matching_matrix[:, gt_idx][pos_idx]=1.0                                                # (500,3)中只有3个1
                  anchor_matching_gt = matching_matrix.sum(1)
                  selected_query = matching_matrix.sum(1)>0            # (500):[F,F,F,F...T,F]
                  gt_indices = matching_matrix[selected_query].max(1)[1]   # [1,0,2]
                  assert selected_query.sum()==len(gt_indices)

                  cost[matching_matrix ==0]= cost[matching_matrix ==0]+float('inf')
                  matched_query_id = torch.min(cost, dim=0)[1]             # [376,32,403]return(selected_query, gt_indices), matched_query_id

4.更新损失
losses ={}
or loss in self.losses:
      losses.update(self.get_loss(loss, outputs, targets, indices, num_boxes))

# 循环6次,把6次解码损失,按同样方法计算,并更新损失
if'aux_outputs'inoutputs:for i, aux_outputs inenumerate(outputs['aux_outputs']):
                indices, _ = self.matcher(aux_outputs, targets)

其实代码更像是Cascade-RCNN,只是初始anchor是随机生成,并利用DDPM公式做了解码。

总结

在这项工作中,我们提出了一种新的检测范式,扩散det,通过将目标检测视为一个从噪声box到物体box的去噪扩散过程。我们的 noise-> box 管道有几个吸引人的特性,包括动态box 和逐步细化,使我们能够使用相同的网络参数来获得所需的速度-精度的权衡,而不需要重新训练模型。在标准检测基准上的实验表明,与成熟的探测器相比,扩散器取得了良好的性能。为了进一步探索扩散模型在解决对象级识别任务中的潜力,未来的一些工作是有益的。一种尝试将扩散数据应用于视频层任务,例如物体跟踪和动作识别。另一种方法是将扩散网络从封闭世界扩展到开放世界或开放词汇表对象检测。

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

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

相关文章

安卓APP源码和设计报告——麻雀笔记

目录 一 安卓应用程序开发背景3 1.1开发背景3 1.2开发环境4 二 安卓应用程序开发理论与方法4 三 记事本应用程序的设计与实现5 3.1 拟解决的问题及目标5 3.2 总体设计6 3.3 详细设计与编码实现6 四 总结23 一 安卓应用程序开发背景 1.1开发背景 1.智能手机的市场 …

四年一次的世界杯来了,看看还剩下哪些赛程呢?

足球世界杯的由来2022年的世界杯目前世界杯的赛程看看排在前十的球员几个疑问&#xff1f;世界杯的由来 足球大家应该知道&#xff0c;是一项体育运动&#xff0c;而且是一项多人的体育运动。 世界杯的诞生&#xff0c;源于1950年的南非公开赛&#xff0c;在当时只有5支队伍参…

微信视频播放点播小程序毕业设计,在线教育视频学习小程序系统设计与实现,微信小程序毕业设计论文怎么写毕设源码开题报告需求分析怎么做

基于微信小程序的毕业设计题目&#xff08;12&#xff09;php在线教育视频点播学习小程序(含开题报告、任务书、中期报告、答辩PPT、论文模板) 项目背景和意义 目的&#xff1a;本课题主要目标是设计并能够实现一个基于微信小程序视频点播系统&#xff0c;前台用户使用小程序&a…

理解Nodejs中的进程间通信

前置知识 文件描述符 在 Linux 系统中&#xff0c;一切都看成文件&#xff0c;当进程打开现有文件时&#xff0c;会返回一个文件描述符。 文件描述符是操作系统为了管理已经被进程打开的文件所创建的索引&#xff0c;用来指向被打开的文件。 当我们的进程启动之后&#xff0c…

Python学习-8.1.2 标准库(random库的基础与实例)

2.2 random库 使用random库的主要目的是生成随机数 2.2.1 产生随机数 random()函数&#xff1a;生成一个[0.0,1.0)之间的随机小数&#xff0c;左开右闭。 import random print(random.random())#生成一个[0.0,1.0)之间的随机小数 #每次运行random()函数都会产生不同的数据 …

Layer2代币经济学:除了治理 还应该具备什么价值?

为什么我们需要L2&#xff1f; 任何在2021年期间使用以太坊的人都知道&#xff0c;该区块链可能会变得非常拥堵。这是一个典型的问题——需求太多而供应不足。因此&#xff0c;gas费用&#xff08;交易费&#xff09;变得相当昂贵。在牛市高峰期&#xff0c;使用以太坊区块链发…

基于Jenkins的开发测试全流程持续集成实践

今年一直在公司实践CI&#xff0c;本文将近半年来的一些实践总结一下&#xff0c;可能不太完善或优美&#xff0c;但的确初步解决了我目前所在项目组的一些痛点。当然这仅是一家之言也不够完整&#xff0c;后续还会深入实践和引入Kubernetes进行容器编排&#xff0c;以及通过阿…

从js中加载图片和Cannot read property ‘appendChild‘ of null 错误

先写一段代码如下&#xff1b; <!DOCTYPE html> <html> <head> <meta charset"utf-8"> <title> </title><script>var imgnew Image();img.addEventListener("load",loadHandler);img.src"img/1.jpg"…

【JMeter】命令参数说明

jmeter -n -t xxx.jmx -l result.jtl 非GUI运行xxx.jml脚本写入xxx.jtl结果文件 jmeter -n -t xxx.jmx -l result.jtl -j run.log 非GUI运行xxx.jmx脚本写入xxx.jtl结果文件&#xff0c;日志记录到run.log jmeter -n -t xxx.jmx -R ip1:port1,ip2:port2 -l result.jtl 使用远…

详解 Redis 持久化之掌握 RDB ⽂件的格式,学习如何制作数据库镜像

本文带大家了解一下 Redis 数据一种持久化方式 RDB 的实现。包括 Redis 内存快照 RDB ⽂件的创建时机以及⽣成⽅法。可以让你掌握 RDB ⽂件的格式&#xff0c;学习如何制作数据库镜像。 RDB 创建的入口函数 Redis 创建 RDB 文件的函数有三个&#xff0c;分别是 rdbSave, rdbSa…

知识图谱-KGE-语义匹配-双线性模型-2017:ANALOGY

【paper】 Analogical Inference for Multi-relational Embeddings【简介】 本文是卡耐基梅隆大学的中国学者发表在 ICML 2017 上的工作&#xff0c;提出了 ANALOGY 模型&#xff0c;用于建模实体和关系的推理属性。这个模型应当也算是双线性模型中比较经典的一个了&#xff0c…

Erueka基本使用

SpringCloud Erueka基本使用 Erueka是微服务架构中&#xff0c;可以作为注册中心的技术实现&#xff0c;如下图所示 服务提供者&#xff1a;一次业务中&#xff0c;暴露接口给其它微服务调用&#xff0c;被其它微服务调用的服务。&#xff08;提供接口给其它微服务&#xff09…

一个超好看的音乐网站设计与实现(HTML+CSS)

&#x1f389;精彩专栏推荐 &#x1f4ad;文末获取联系 ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 &#x1f482; 作者主页: 【主页——&#x1f680;获取更多优质源码】 &#x1f393; web前端期末大作业&#xff1a; 【&#x1f4da;毕设项目精品实战案例 (10…

vue+vite的创建

1、创建vue3项目 yarn create vite效果&#xff1a; yarn create v1.22.19 [1/4] Resolving packages... [2/4] Fetching packages... [3/4] Linking dependencies... [4/4] Building fresh packages...success Installed "create-vite3.2.1" with binaries:- crea…

接口管理测试繁琐复杂?何不试试这个神器

一、前言 作为一名测试从业者&#xff0c;深刻的明白接口测试在项目过程中是多么重要的一个环节。通过页面进行的UI测试会因为界面不稳定而导致用例维护非常困难。另外&#xff0c;在检查系统的安全性、稳定性上面也是尤为重要的环节&#xff0c;这些也是无法通过前端测试的&a…

Redis - Windows下载与安装

1.获取Redis在windows下的安装包 Windows版下载地址&#xff1a;https://github.com/microsoftarchive/redis/releases 选择Redis-x64-*.zip 2.解压zip文件与配置 2.1 选取目录 选取一个目录作为解压目录&#xff0c;这个目录就是你Redis程序所在位置&#xff0c;尽量找一…

【软件测试】师傅给我的测试新手“真理“宝典......

目录&#xff1a;导读前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09;前言 熟悉你所测试的软件…

java学习day59(乐友商城)Vue入门

0.前言 前几天我们已经对后端的技术栈有了初步的了解、并且已经搭建了整个后端微服务的平台。接下来要做的事情就是功能开发了。但是没有前端页面&#xff0c;我们肯定无从下手&#xff0c;因此今天我们就要来了解一下前端的一些技术&#xff0c;完成前端页面搭建。 先聊一下…

【Redis】Redis实现分布式锁解析与应用(Redis专栏启动)

&#x1f4eb;作者简介&#xff1a;小明java问道之路&#xff0c;专注于研究 Java/ Liunx内核/ C及汇编/计算机底层原理/源码&#xff0c;就职于大型金融公司后端高级工程师&#xff0c;擅长交易领域的高安全/可用/并发/性能的架构设计与演进、系统优化与稳定性建设。 &#x1…

java版商城+Spring Cloud+SpringBoot+mybatis+uniapp b2b2c o2o 多商家入驻商城 直播带货商城 电子商务

一个好的SpringCloudSpringBoot b2b2c 电子商务平台涉及哪些技术、运营方案&#xff1f;以下是我结合公司的产品做的总结&#xff0c;希望可以帮助到大家&#xff01; 搜索体验小程序&#xff1a;海哇 1. 涉及平台 平台管理、商家端&#xff08;PC端、手机端&#xff09;、买…