DAB-DETR代码学习记录之模型解析

news2024/11/19 5:31:05

DAB-DETR是吸收了Deformable-DETR,Conditional-DETR,Anchor-DETR等基础上完善而来的。其主要贡献为将query初始化为x,y,w,h思维坐标形式。
这篇博文主要从代码角度来分析DAB-DETR所完成的工作。
DAB-DETR主要是对Decoder模型进行改进。博主也主要是对Decoder模块的模型进行解析。

在这里插入图片描述

在这里插入图片描述

位置编码的温度值调整

首先是position_encoding.py文件,该文件中重新定义了一个PositionEmbeddingSineHW方法,其作用就是将高频位置编码部分的宽高温度值分开,可以让宽高有不同的温度值。该文件中还提高了sincos位置编码方式和可学习的位置编码方式。

class PositionEmbeddingSineHW(nn.Module):
    """
    This is a more standard version of the position embedding, very similar to the one
    used by the Attention is all you need paper, generalized to work on images.
    """
    def __init__(self, num_pos_feats=64, temperatureH=10000, temperatureW=10000, normalize=False, scale=None):
        super().__init__()
        self.num_pos_feats = num_pos_feats
        self.temperatureH = temperatureH
        self.temperatureW = temperatureW
        self.normalize = normalize
        if scale is not None and normalize is False:
            raise ValueError("normalize should be True if scale is passed")
        if scale is None:
            scale = 2 * math.pi
        self.scale = scale
    def forward(self, tensor_list: NestedTensor):
        x = tensor_list.tensors
        mask = tensor_list.mask
        assert mask is not None
        not_mask = ~mask
        y_embed = not_mask.cumsum(1, dtype=torch.float32)
        x_embed = not_mask.cumsum(2, dtype=torch.float32)
        # import ipdb; ipdb.set_trace()
        if self.normalize:
            eps = 1e-6
            y_embed = y_embed / (y_embed[:, -1:, :] + eps) * self.scale
            x_embed = x_embed / (x_embed[:, :, -1:] + eps) * self.scale
        dim_tx = torch.arange(self.num_pos_feats, dtype=torch.float32, device=x.device)
        dim_tx = self.temperatureW ** (2 * (dim_tx // 2) / self.num_pos_feats)
        pos_x = x_embed[:, :, :, None] / dim_tx
        dim_ty = torch.arange(self.num_pos_feats, dtype=torch.float32, device=x.device)
        dim_ty = self.temperatureH ** (2 * (dim_ty // 2) / self.num_pos_feats)
        pos_y = y_embed[:, :, :, None] / dim_ty
        pos_x = torch.stack((pos_x[:, :, :, 0::2].sin(), pos_x[:, :, :, 1::2].cos()), dim=4).flatten(3)
        pos_y = torch.stack((pos_y[:, :, :, 0::2].sin(), pos_y[:, :, :, 1::2].cos()), dim=4).flatten(3)
        pos = torch.cat((pos_y, pos_x), dim=3).permute(0, 3, 1, 2)
        # import ipdb; ipdb.set_trace()
        return pos

Transformer整体架构

我们先来了解Transformer的整体架构:
首先我们来看看forward传入的参数:
src:由backbone提取的特征信息,shape初始为 torch.Size([2, 256,19,24]) 后变为torch.Size([456, 2, 256])
mask:对图像进行补全掩码信息,shape初始为 torch.Size([2, 19, 24]) 后展平为 torch.Size([2, 456])

refpoint_embed:参考点坐标编码,即object_query,torch.Size([300, 4])。在Decoder模块使用,其是在DAB-DETR模块定义初始化的:self.refpoint_embed = nn.Embedding(num_queries, query_dim),初始为torch.Size([300,4]),后经过refpoint_embed = refpoint_embed.unsqueeze(1).repeat(1, bs, 1)变为torch.Size([300, 4])。

pos_embed:位置编码信息,shape初始为 torch.Size([2, 256,19,24]) 后变为torch.Size([456, 2, 256])
上述过程执行代码如下:

    # flatten NxCxHxW to HWxNxC
    bs, c, h, w = src.shape  #初始为2,256,19,24
    src = src.flatten(2).permute(2, 0, 1)#拉平:
    pos_embed = pos_embed.flatten(2).permute(2, 0, 1)
    refpoint_embed = refpoint_embed.unsqueeze(1).repeat(1, bs, 1)
    mask = mask.flatten(1)  

随后将数据送入Encoder模块,输出memory为:torch.Size([456, 2, 256])

 memory = self.encoder(src, src_key_padding_mask=mask, pos=pos_embed)

随后对tgt进行初始化,根据self.num_patterns判断其模式,这里默认为0。tgt初始化为全0,shape为:torch.Size([300, 2, 256]),这里与DETR是相似的,其作为最开始的decoder输入。

 num_queries = refpoint_embed.shape[0]
if self.num_patterns == 0:
    tgt = torch.zeros(num_queries, bs, self.d_model, device=refpoint_embed.device)
else:
    tgt = self.patterns.weight[:, None, None, :].repeat(1, self.num_queries, bs, 1).flatten(0, 1) # n_q*n_pat, bs, d_model
    refpoint_embed = refpoint_embed.repeat(self.num_patterns, 1, 1) # n_q*n_pat, bs, d_model

随后送入Decoder模块:

 hs, references = self.decoder(tgt, memory, memory_key_padding_mask=mask,
                      pos=pos_embed, refpoints_unsigmoid=refpoint_embed)
 return hs, references

Encoder模块构建

DAB-DETR的Encoder模块与DETR并没有太大差别。

EncoderLayer

src_mask=None
src_key_padding_mask:将图片补全shape为【2,456】
src:通过ResNet提取到的特征,由二维转为一维,shape为 torch.Size([456, 2, 256])
pos:位置编码信息,原本为两种,分别为sincos位置编码与可学习的位置编码,此外,DAB-DETR还提出一种可以跳转宽高的位置编码方式。shape为 torch.Size([456, 2, 256])
src2 通过self-attention获得,shape为 torch.Size([456, 2, 256]),随后经过dropout层,norm层。最终的输出结果为src:torch.Size([456, 2, 256]),将该结果送入Decoder。

q = k = self.with_pos_embed(src, pos)
src2 = self.self_attn(q, k, value=src, attn_mask=src_mask,
                              key_padding_mask=src_key_padding_mask)[0]
src = src + self.dropout1(src2)
src = self.norm1(src)
src2 = self.linear2(self.dropout(self.activation(self.linear1(src))))
src = src + self.dropout2(src2)
src = self.norm2(src)
return src

与DETR一样,with_pos_embed是直接相加。

def with_pos_embed(self, tensor, pos: Optional[Tensor]):
        return tensor if pos is None else tensor + pos

Encoder模块

Encoder即有6个EncoderLayer构成。

class TransformerEncoder(nn.Module):
    def __init__(self, encoder_layer, num_layers, norm=None, d_model=256):
        super().__init__()
        self.layers = _get_clones(encoder_layer, num_layers)
        self.num_layers = num_layers
        self.query_scale = MLP(d_model, d_model, d_model, 2)
        self.norm = norm
    def forward(self, src,
                mask: Optional[Tensor] = None,
                src_key_padding_mask: Optional[Tensor] = None,
                pos: Optional[Tensor] = None):
        output = src
        for layer_id, layer in enumerate(self.layers):
            # rescale the content and pos sim
            pos_scales = self.query_scale(output)
            output = layer(output, src_mask=mask,
                           src_key_padding_mask=src_key_padding_mask, pos=pos*pos_scales)
        if self.norm is not None:
            output = self.norm(output)
        return output

Decoder简要概述

在Decoder部分的query ancor(Anchor Boxes)中,其初始化为【2,300,4】会通过Anchor Sine Encoding,x,y,w,h都会进行,都转换为128维度,4个即为512维,随后通过一个MLP转换为256。
位置编码方式如下:总共4个,被编码维128维。

在这里插入图片描述
在这里插入图片描述

下面是其主要的一个创新点,加入了宽高调制的注意力机制,之所以这样做是让注意力能够对宽高也比较敏感。

在这里插入图片描述
在这里插入图片描述

Decoder模块代码实现

首先,将tgt的值给output,这里可以看出,输出结果为output,其shape为torch.Size([300, 2, 256])

output = tgt

reference_points归一化,shape仍为torch.Size([300, 2, 4])

reference_points = refpoints_unsigmoid.sigmoid()

进入Decoder循环中后,首先对reference_points进行高频位置编码,即取出全部值,进入高频位置编码模块,由torch.Size([300, 2, 4])变为torch.Size([300, 2, 512]),每个变为128,如下:
在这里插入图片描述
随后经过一个self.ref_point_head(MLP)变为torch.Size([300, 2, 256])

obj_center = reference_points[..., :self.query_dim]  #torch.Size([300, 2, 4])  
query_sine_embed = gen_sineembed_for_position(obj_center) #torch.Size([300,2,512])
query_pos = self.ref_point_head(query_sine_embed) #torch.Size([300, 2, 256])

gen_sineembed_for_position方法如下:

def gen_sineembed_for_position(pos_tensor):
    # n_query, bs, _ = pos_tensor.size()
    # sineembed_tensor = torch.zeros(n_query, bs, 256)
    scale = 2 * math.pi
    dim_t = torch.arange(128, dtype=torch.float32, device=pos_tensor.device)
    dim_t = 10000 ** (2 * (dim_t // 2) / 128)
    x_embed = pos_tensor[:, :, 0] * scale
    y_embed = pos_tensor[:, :, 1] * scale
    pos_x = x_embed[:, :, None] / dim_t
    pos_y = y_embed[:, :, None] / dim_t
    pos_x = torch.stack((pos_x[:, :, 0::2].sin(), pos_x[:, :, 1::2].cos()), dim=3).flatten(2)
    pos_y = torch.stack((pos_y[:, :, 0::2].sin(), pos_y[:, :, 1::2].cos()), dim=3).flatten(2)
    if pos_tensor.size(-1) == 2:
        pos = torch.cat((pos_y, pos_x), dim=2)
    elif pos_tensor.size(-1) == 4:
        w_embed = pos_tensor[:, :, 2] * scale
        pos_w = w_embed[:, :, None] / dim_t
        pos_w = torch.stack((pos_w[:, :, 0::2].sin(), pos_w[:, :, 1::2].cos()), dim=3).flatten(2)
        h_embed = pos_tensor[:, :, 3] * scale
        pos_h = h_embed[:, :, None] / dim_t
        pos_h = torch.stack((pos_h[:, :, 0::2].sin(), pos_h[:, :, 1::2].cos()), dim=3).flatten(2)
        pos = torch.cat((pos_y, pos_x, pos_w, pos_h), dim=2)
    else:
        raise ValueError("Unknown pos_tensor shape(-1):{}".format(pos_tensor.size(-1)))
    return pos

随后进行一些初始化,self.query_scaleMLP层,output可认为是上一层Decoder的输出结果。
取出 query_sine_embed 的前256维,即x,y与pos_transformation(第一层时为1)相乘。
ref_anchor_head是一个MLP,self.ref_anchor_head = MLP(d_model, d_model, 2, 2)输入维度为256,中间层宽度为256,输出维度为2,隐藏层数为2。
refHW_cond为torch.Size([300, 2, 2])
query_sine_embed 初始为torch.Size([300, 2, 512]),经过下面query_sine_embed = query_sine_embed[...,:self.d_model] * pos_transformation后变为torch.Size([300, 2, 256]),该句代码意思为取前256维

if self.query_scale_type != 'fix_elewise':#执行
      if layer_id == 0:#第一层时执行
          pos_transformation = 1
      else:
          pos_transformation = self.query_scale(output) #query_scale为MLP
else:
     pos_transformation = self.query_scale.weight[layer_id]
#取出  query_sine_embed的前256维,即x,y与pos_transformation相乘 
query_sine_embed = query_sine_embed[...,:self.d_model] * pos_transformation
if self.modulate_hw_attn:
      refHW_cond = self.ref_anchor_head(output).sigmoid() #将其送入MLP后进行归一化 torch.Size([300, 2, 2])
      query_sine_embed[..., self.d_model // 2:] *= (refHW_cond[..., 0] / obj_center[..., 2]).unsqueeze(-1)
      query_sine_embed[..., :self.d_model // 2] *= (refHW_cond[..., 1] / obj_center[..., 3]).unsqueeze(-1)

上述代码执行的其实就是下面这个过程:注意此时并非是没有乘以PE(Xref),PE(Yref),而是由于其设置为1,即pos_transformation = 1,这里我们到了第二层DecoderLayer中可以看到。

在这里插入图片描述

在这里插入图片描述

随后将数据送入DecoderLayer,注意此时DecoderLayer是第一层。

output = layer(output, memory, tgt_mask=tgt_mask,
                           memory_mask=memory_mask,
                           tgt_key_padding_mask=tgt_key_padding_mask,
                           memory_key_padding_mask=memory_key_padding_mask,
                           pos=pos, query_pos=query_pos, query_sine_embed=query_sine_embed,
                           is_first=(layer_id == 0))

第一层DecoderLayer模块

Self_Attention

首先时进行DecoderLayer中自注意力机制的计算。
我们来看看数据是如何变化的:
tgt即上一层DecoderLayer的输出结果,此时全为0,shape为 torch.Size([300, 2, 256])
首先经过一个线性层(sa_qcontent_proj = nn.Linear(d_model, d_model))得到q_content
shape为torch.Size([300, 2, 256])
需要注意的是,tgt在通过线性层完成qkv初始化时,尽管tgt全为0,但q,k,v却不是

在这里插入图片描述

随后将q_pos(Anchor经过高频位置编码与MLP获得的xywh信息)也经过一个线性层sa_qpos_proj维度不变:shape为 torch.Size([300, 2, 256])
同时k,v也采用相同的方式进行初始化。与DETR相同,v是没有位置信息的。

综上所述,在self-attention中,由Anchor Box变换来的query_pos提供位置信息,由初始化为全0或上一层DecoderLayer的输出结果提供内容信息,位置信息与内容信息也是采用相加的方式合并在一起,如: q = q_content + q_pos
至于后面与DETR完全相同,送入q,k,v参与运算即可。

 if not self.rm_self_attn_decoder:
            # Apply projections here
            # shape: num_queries x batch_size x 256
            q_content = self.sa_qcontent_proj(tgt)      # target is the input of the first decoder layer. zero by default.
            q_pos = self.sa_qpos_proj(query_pos)
            k_content = self.sa_kcontent_proj(tgt)
            k_pos = self.sa_kpos_proj(query_pos)
            v = self.sa_v_proj(tgt)
            num_queries, bs, n_model = q_content.shape
            hw, _, _ = k_content.shape
            q = q_content + q_pos
            k = k_content + k_pos
            tgt2 = self.self_attn(q, k, value=v, attn_mask=tgt_mask,
                                key_padding_mask=tgt_key_padding_mask)[0] 
                     #tgt2为Attention计算结果,torch.Size([300, 2, 256])
            # ========== End of Self-Attention =============
            tgt = tgt + self.dropout1(tgt2)
            tgt = self.norm1(tgt)

最终获得self-attention的输出tgt,shape为 torch.Size([300, 2, 256]),上述代码执行的便是下图框出部分。

在这里插入图片描述

随后准备送入cross-attention进行计算

Cross_Attention

首先是q k v的初始化过程,可以看到,q来源于self-attention的输出结果,经过一个线性层,k,v则来自于Encoder的输出结果。memory的维度为torch.Size([456, 2, 256])

q_content = self.ca_qcontent_proj(tgt)#torch.Size([300, 2, 256])
k_content = self.ca_kcontent_proj(memory)#torch.Size([456, 2, 256])
v = self.ca_v_proj(memory)#torch.Size([456, 2, 256])

k_pos = self.ca_kpos_proj(pos)#对K进行位置编码,pos来自于Encoder。torch.Size([456, 2, 256])

由于是第一层,需要执行下面操作,即首先将 query_pos 【torch.Size([300, 2, 256])】通过一个全连接层,维度不发生变化,即生成 q_pos 的过程,

if is_first or self.keep_query_pos:#self.keep_query_pos默认为False
    q_pos = self.ca_qpos_proj(query_pos)# query_pos:torch.Size([300, 2, 256])
    q = q_content + q_pos
    k = k_content + k_pos
else:
    q = q_content
    k = k_content

接下来便是送入Cross_Attention的Q,K,V的初始化过程:需要注意的是,这里将注意力分头操作放到外面了,原本是在注意力内部完成的。

q = q.view(num_queries, bs, self.nhead, n_model//self.nhead)# q分头:torch.Size([300, 2, 8, 32])
query_sine_embed = self.ca_qpos_sine_proj(query_sine_embed)#query_sine_embed即
query_sine_embed = query_sine_embed.view(num_queries, bs, self.nhead, n_model//self.nhead)
q = torch.cat([q, query_sine_embed], dim=3).view(num_queries, bs, n_model * 2)
#q经过拼接变为torch.Size([300, 2, 512])
k = k.view(hw, bs, self.nhead, n_model//self.nhead)#torch.Size([456, 2, 8, 32])
k_pos = k_pos.view(hw, bs, self.nhead, n_model//self.nhead)#torch.Size([456, 2, 8, 32])
k = torch.cat([k, k_pos], dim=3).view(hw, bs, n_model * 2)#torch.Size([456, 2, 512])

随后将Q,K,V送入Cross_Attention进行计算:总结一下,q:torch.Size([300, 2, 512]),k:torch.Size([456, 2, 512]),v:torch.Size([456, 2, 256])

tgt2 = self.cross_attn(query=q, key=k, value=v, attn_mask=memory_mask, key_padding_mask=memory_key_padding_mask)[0]    

具体执行下面过程:Q K V不同维度

return multi_head_attention_forward(
                query, key, value, self.embed_dim, self.num_heads,
                self.in_proj_weight, self.in_proj_bias,
                self.bias_k, self.bias_v, self.add_zero_attn,
                self.dropout, self.out_proj.weight, self.out_proj.bias,
                training=self.training,
                key_padding_mask=key_padding_mask, need_weights=need_weights,
                attn_mask=attn_mask, out_dim=self.vdim)

完成cross_attention计算后,tgt2维度为torch.Size([300, 2, 256]),关于维度变化可以参考Attention计算公式:

在这里插入图片描述
随后便是经过一系列残次连接,批归一化操作输出结果了,最终结果仍为 torch.Size([300, 2, 256])。

锚点更新策略

该模块也是DAB-DETR的一个创新点,即锚点更新策略 Anchor Update

即经过了DecoderLayer的cross_attention计算后出了将输出值传给下一层DecoderLayer外还将其用于锚点更新,使用MLP网络获得x,y,w,h的偏移量,shape为torch.Size([300, 2, 4])。与我们初始化的参考点坐标reference_points(即Anchor box,shape为torch.Size([300, 2, 4]) )相加。此即为锚点更新策略,而先前的DETR模型中的初始化anchor是一直不变的。

在这里插入图片描述

if self.bbox_embed is not None:
    if self.bbox_embed_diff_each_layer:#是否共享参数:false
        tmp = self.bbox_embed[layer_id](output)
    else:
        tmp = self.bbox_embed(output)#经过MLP获得output偏移量x,y,w,h torch.Size([300, 2, 4])
    # import ipdb; ipdb.set_trace()
    tmp[..., :self.query_dim] += inverse_sigmoid(reference_points)
    new_reference_points = tmp[..., :self.query_dim].sigmoid()
    if layer_id != self.num_layers - 1:
        ref_points.append(new_reference_points)
    reference_points = new_reference_points.detach()
if self.return_intermediate:
    intermediate.append(self.norm(output))

由上述代码可知reference_points会不断更新,即Anchor更新策略
为了实现自动微分,PyTorch跟踪所有涉及张量的操作,可能需要为其计算梯度(即require_grad为True)。 这些操作记录为有向图。 detach()方法在张量上构造一个新视图,该张量声明为不需要梯度
上述代码执行的便是下图框出过程:
在这里插入图片描述

第二层DecoderLayer模块

相较于第一层DecoderLayer,第二层的结构上与第一层是没有差别的,只是第一层的Decoder-Embedding的初始化tgt为全0,第二层变为了第一层的输出而已,此外由于锚点更新策略,第二层的Anchor Boxes也变为了第一层Anchor Boxes加上xywh的偏移量。

首先是Anchor Boxes的变化,reference_points(即Anchor Boxes)在经过了上一层Decoderlayer后值得到了更新,再次经过高频位置编码,MLP层将数据维度变为 torch.Size([300, 2, 256])

obj_center = reference_points[..., :self.query_dim]  
query_sine_embed = gen_sineembed_for_position(obj_center)  
query_pos = self.ref_point_head(query_sine_embed) 

紧接着,这里凸显了不同之处,首先,此时的query_scale_type变为 cond_elewise ,且由于到了第二层,output(即上一层的输出结果)通过

self.query_scale = MLP(d_model, d_model, d_model, 2)

进行编码得到pos_transformation维度为 torch.Size([300, 2, 256])

在这里插入图片描述

紧接着query_sine_embed[...,:self.d_model] * pos_transformation,这里的query_sine_embed为 torch.Size([300, 2, 512]),取前面的256维,即对应取得是x,y。与pos_transformation相乘,pos_transformationXref,Yref,这里完成的便是下面的操作:
在这里插入图片描述

由此可见,在第一层中并非是没有乘以PE(Xref),PE(Yref),而是由于其值为1。
之后的过程就与第一层DecodeLayer完全相同了。

Decoder模块

结束DecoderLayer的循环后紧接着执行:intermediate保存每层的结果,是一个List,包含6个值,每个值的shape为 torch.Size([300, 2, 256]),然后将第六层扔掉(pop操作),然后将最终的输出值append。

 if self.norm is not None:
        output = self.norm(output)
        if self.return_intermediate:
            intermediate.pop()
            intermediate.append(output)

在这里插入图片描述
随后判断bbox_embed(MLP box预测头)是否为None,torch.stack是拼接操作。

 if self.return_intermediate:
            if self.bbox_embed is not None:
                return [
                    torch.stack(intermediate).transpose(1, 2),
                    torch.stack(ref_points).transpose(1, 2),
                ]
            else:
                return [
                    torch.stack(intermediate).transpose(1, 2), 
                    reference_points.unsqueeze(0).transpose(1, 2)
                ]

Transformer的decoder模块最终返回结果:

hs, references = self.decoder(tgt, memory, memory_key_padding_mask=mask,
                          pos=pos_embed, refpoints_unsigmoid=refpoint_embed)

其中references torch.Size([6, 2, 300, 4]),hs 为 torch.Size([6, 2, 300, 256]),该结果也是Decoder的返回结果,references即每次更新的完成后的box。hs认为是语义特征信息。

在这里插入图片描述

DAB-DETR整体模块

经过Transformer后,接下来便是分类头与回归头的设计了。
首先是将Decoder模块的reference(Anchor Box)的值进行反归一化,随后对hs(Decoder输出值,相当于DETR的output)进行回归头预测得到tmp ,其shape为 torch.Size([6, 2, 300, 4]),将该值与处理后的reference相加。(self.query_dim为4,即全部加上),最终将tmp在进行归一化。

   if not self.bbox_embed_diff_each_layer:#是否权值共享
        reference_before_sigmoid = inverse_sigmoid(reference)#反归一化
        tmp = self.bbox_embed(hs)#torch.Size([6, 2, 300, 4])
        tmp[..., :self.query_dim] += reference_before_sigmoid
        outputs_coord = tmp.sigmoid()

在这里插入图片描述
outputs_coord值即预测框的xywh
在这里插入图片描述
最终out返回结果为:
pred_logits 为类别预测(这里是91类)torch.Size([2, 300, 91])
pred_boxes为box框预测 torch.Size([2, 300, 4])
aux_outputs为前5层DecoderLayer的结果。为list,有5个值。
在这里插入图片描述

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

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

相关文章

【C++】6. 内联函数

文章目录 前言一、宏函数二、内联函数三、内联函数的易错点 前言 当我们调用函数时,是有很多消耗的。其中最大的销毁就是为函数开辟空间 - 函数栈帧。 如果我们有一个函数,很短,而且要调用很多次,比如Swap()。它所造成消耗就比较…

机器学习笔记Python笔记:HMM(隐马尔科夫模型)

1 引子:猜天气小游戏 一对异地恋的情侣,女朋友想根据男友的心情猜测男友所在城市的天气 1.1 天气和心情一定一一对应 晴天——>高兴雨天——>烦躁 可以根据心情唯一确定天气 1.2 天气和心情没有一一对应 晴天——>80%高兴,20%烦…

有关实现深拷贝的四种方法

深拷贝与浅拷贝: 在开始之前我们需要先了解一下什么是浅拷贝和深拷贝,其实深拷贝和浅拷贝都是针对的引用类型,JS中的变量类型分为值类型(基本类型)和引用类型;对值类型进行复制操作会对值进行一份拷贝,而对…

Logstash学习

一、Logstash基础 1、什么是Logstash logstash是一个数据抽取工具,将数据从一个地方转移到另一个地方。下载地址:https://www.elastic.co/cn/downloads/logstash logstash之所以功能强大和流行,还与其丰富的过滤器插件是分不开的&#xff…

CDGP认证|ChatGPT的出现,对数据治理行业冲击如何?

ChatGPT的出现对数据治理有很多好处,其中最明显的是提供了更高效、更准确和更自动化的数据处理和分析服务,可以帮助企业和组织更好地管理和利用数据资源,提高数据质量和决策效率。此外,ChatGPT还能够发现隐藏在大量数据中的信息和趋势&#x…

OJ练习第82题——填充书架

填充书架 力扣链接:1105. 填充书架 题目描述 给定一个数组 books ,其中 books[i] [thicknessi, heighti] 表示第 i 本书的厚度和高度。你也会得到一个整数 shelfWidth 。 按顺序 将这些书摆放到总宽度为 shelfWidth 的书架上。 先选几本书放在书架…

Nexus 组件发布失败、npm 登录失败 解决过程

目录 参考发布文章进行打包 提示发布成功,但在 Nexus 里没发现组件 测试 yarn 发布 测试 npm 发布(解决登录失败) Nexus 设置 Sonatype Nexus Repository Manager 相关权限 参考发布文章进行打包 整体发布:根目录运行 yarn r…

直播软件app开发:如何保证音视频质量?

随着社交媒体的发展,视频直播已成为越来越流行的社交方式。直播软件app开发也因此成为了一个热门话题。在开发直播软件app时,保证音视频质量是至关重要的。本文将介绍如何确保你的直播软件app在音视频质量方面表现出色。 确定音视频质量标准 首先&…

第七章 建造者模式

文章目录 前言一、传统方式解决盖房子需求完整代码抽象房子类 AbstractHouse实现子类 普通房子实现子类 高楼大厦客户端盖房子 二、引入建造者模式建造者模式的四个角色: 产品、抽象建造者、具体建造者、指挥者完整代码House类 (产品角色)抽象父类(抽象建…

CV 领域的 ChatGPT?MetaAI 推出“最强”大视觉模型 SAM

出品人:Towhee 技术团队 随着 ChatGPT 引起一波又一波的“GPT热潮”,自然语言领域又一次成为了人工智能的讨论焦点。大家不由得思考,计算机视觉领域里是否会出现这样一个堪称划时代的模型?在这种万众瞩目的时候,一直处…

Python3《机器学习实战》学习笔记(七):支持向量机原理篇之手撕线性SVM

文章目录 一、SVM介绍二、线性SVM2.1 数学建模2.1.1决策面方程2.1.2"分类间隔"方程2.1.3约束条件2.1.4线性SVM优化问题基本描述2.1.5求解准备(讲讲凸函数)2.1.6拉格朗日函数2.1.7KKT条件2.1.8对偶问题求解2.1.9最后求解 2.2 SMO算法 三、代码实战3.1准备数据 一、SVM…

M_Map工具箱简介及地理图形绘制

M_Map工具箱简介及地理图形绘制 1 M_Map简介1.1 具体代码说明 2 地理图形绘制案例2.1 M_Map给定案例2.1.1 M_Map Logo2.1.2 Lambert Conformal Conic projection of North American Topography2.1.3 Stereographic projection of North Polar regions2.1.4 Colourmaps 2.2 案例…

vue封装公共组件库并发布到npm库详细教程

vue组件封装的原理:利用vue框架提供的api: Vue.use( plugin ),我们需要把封装好组件的项目打包成vue库,并提供install方法,然后发布到npm中。Vue.use( plugin )的时候会自动执行插件中的install方法。 一、组件库代码目录 目录…

常见的四种排名函数的用法(sql)

四个排名函数: 1.row_number 2.rank 3.dense_rank 4.ntile 1. ROW_NUMBER(排名场景推荐) 1.1 介绍 在 SQL 中,ROW_NUMBER() 是一个窗口函数,它为结果集中的每一行分配一个唯一的序号。该函数的语法如下: …

内网渗透之横向移动PTHPTTPTK

0x00前言 pass the hash(哈希传递攻击,简称pth) pass the ticket(票据传递攻击,简称ptt) pass the key(密钥传递攻击,简称ptk) PTH(pass the hash) #利用的lm或ntlm的值…

【社区图书馆】 Go佬—Go程序开发实战宝典书评

文章目录 前言内容介绍文章大致划分总结 前言 《Go 程序开发实战宝典》是一本非常实用的 Go 语言开发工具书,本书由深入浅出的案例讲解、详细的技术实现、贴近实际的应用开发等组成,非常适合 Go 语言开发爱好者、从事相关行业的工程师、技术负责人以及深…

ThinkPHP视图

ThinkPHP视图 前言视图一、运算符二、模版函数三、循环标签四,volist 循环标签五,if 判断标签六,switch 判断标签七、包含文件八、其他标签1. 条件标签2. 比较标签3. 循环标签4. 杂项标签 总结 前言 ThinkPHP视图基本语法和PHP语法非常的像,所…

React Native 9个好用的开发工具盘点

近几年在大前端的开发领域,选择跨端方案的公司和部门越来越多,曾一何时市面有不下10种跨端框架,但随着“生物进化论”的推动,目前市面上仅剩两种主流方案,就是经常听到的 React Native 和 Flutter。去年终于引来了 Rea…

企业网络安全漏洞分析及其解决_kaic

摘要 为了防范网络安全事故的发生,互联网的每个计算机用户、特别是企业网络用户,必须采取足够的安全防护措施,甚至可以说在利益均衡的情况下不惜一切代价。事实上,许多互联网用户、网管及企业老总都知道网络安全的要性,却不知道网…

《JavaEE初阶》多线程进阶

《JavaEE初阶》多线程进阶 文章目录 《JavaEE初阶》多线程进阶常见锁策略乐观锁与悲观锁普通互斥锁与读写锁轻量级锁与重量级锁自旋锁与挂起等待锁公平锁和非公平锁可重入锁和不可重入锁 CAS什么是CASCAS的应用场景:CAS 的 ABA 问题什么是CAS 的 ABA 问题解决方案: synchronize…