Construcción del modelo del registro de aprendizaje del código DAB-Deformable-DETR

Sobre la base de Deformable-DETR, el autor de DAB-DETR integró la idea de DAB-DETR en Deformable-DETR y logró buenos resultados. Hoy, el blogger aprende el modelo DAB-Deformable-DETR a través del código fuente.
Primero, veamos las innovaciones de Deformable:

Innovación Deformable-DETR

fusión multiescala

El primero es el problema de fusión multiescala del que se habla a menudo pero que debe mencionarse. El autor toma las últimas tres capas de ResNet50 como salida y las fusiona en el Transformador.

inserte la descripción de la imagen aquí

Mecanismo de atención deformable

Esta es la principal innovación de Deformable-DETR, es decir, atención deformable. Para una introducción detallada, puede ver la publicación del blog del blogger:

Registro de aprendizaje del modelo DETR deformable

inserte la descripción de la imagen aquí
A continuación, partiremos del código fuente para aprender DAB-Deformable-DETR

Modelo general DAB-Deformable-DETR

Primero venga al archivo de construcción del modelo DAB-Deformable-DETR
Datos de muestra preprocesados ​​DAB-Deformable-DETR.py samples:
principalmente observamos dos datos, a saber, tensores, que son los datos de muestra enviados al modelo DAB-Deformable-DETR, es antorcha .Size([2, 3, 608, 760]), que representa tamaño de lote=2, canal=3, ancho=608, alto=760 respectivamente.
La segunda es la máscara, que es la información de la máscara que se obtiene después de unificar el tamaño de la imagen y rellenarla, indica qué partes se rellenan y cuáles son la imagen en sí, es torch.Size([2, 608, 760]).

inserte la descripción de la imagen aquí
Luego, los datos de muestra se envían a la red troncal, que originalmente era resnet50, pero aquí el blogger cambió la red troncal a resnet18 debido a limitaciones de memoria.

features, pos = self.backbone(samples)

Se obtienen tres características, que son los resultados de salida de las últimas 3 capas de Resnet8, correspondientes al primer punto de innovación en el documento: información de características multiescala. Además, existe la correspondiente información de codificación de posición pos. (La información de dimensión de los datos pos es la misma que la de la función)
La información de dimensión de los datos de función es:torch.Size([2, 256, 76, 95]) torch.Size([2, 256, 38, 48]) torch.Size([2, 256, 19, 24])

inserte la descripción de la imagen aquí
Luego procese la función para obtener el src y la máscara enviados al módulo Transformer. El srcs obtenido contiene tres datos, es decir, torch.Size([2, 256, 76, 95]) torch.Size([2, 256, 38, 48]) torch.Size([2, 256, 19, 24])la máscara solo tiene menos dimensiones de canal que la máscara.

srcs = []
masks = []
for l, feat in enumerate(features):
      src, mask = feat.decompose()
      srcs.append(self.input_proj[l](src))
      masks.append(mask)
      assert mask is not None

Luego, la última capa de la función se procesa para generar src, y la máscara correspondiente también se genera por primera vez y se agrega a srcs. Corresponde a la siguiente figura:

inserte la descripción de la imagen aquí

 if self.num_feature_levels > len(srcs):
            _len_srcs = len(srcs)
            for l in range(_len_srcs, self.num_feature_levels):
                if l == _len_srcs:
                    src = self.input_proj[l](features[-1].tensors)
                else:
                    src = self.input_proj[l](srcs[-1])
                m = samples.mask
                mask = F.interpolate(m[None].float(), size=src.shape[-2:]).to(torch.bool)[0]
                pos_l = self.backbone[1](NestedTensor(src, mask)).to(src.dtype)
                srcs.append(src)
                masks.append(mask)
                pos.append(pos_l)

Entre ellos mask = F.interpolate(m[None].float(), size=src.shape[-2:]).to(torch.bool)[0], m es torch.Size([2, 608, 760]), y m se convierte en una máscara del mismo tamaño que src as torch.Size([2, 10, 12]) En este proyecto, dual- etapa no está habilitada,
y Use el método dab, seguido de los siguientes pasos.
Obtener refanchor: torch.Size([300, 4]) tgt_embed: torch.Size([300, 256]), y luego empalmarlo para obtener
query_embeds torch.Size([300, 260]) (coser según dim=1 dimensión)

    if self.num_patterns == 0:
        tgt_embed = self.tgt_embed.weight           # nq, 256
        refanchor = self.refpoint_embed.weight      # nq, 4
        query_embeds = torch.cat((tgt_embed, refanchor), dim=1)

Luego, los datos se envían al modelo Transformer y los resultados de salida son los siguientes:

hs, init_reference, inter_references, enc_outputs_class, enc_outputs_coord_unact = self.transformer(srcs, masks, pos, query_embeds)

hs: torch.Size([6, 2, 300, 256]) información de características semánticas
init_reference: torch.Size([300, 4])
inter_references: torch.Size([6, 2, 300, 4]) cuadro de punto de referencia
enc_outputs_class: Ninguno
enc_outputs_coord_unact: Ninguno

inserte la descripción de la imagen aquí

Al final, hs tiene que pasar por el encabezado de clasificación y el encabezado de regresión, y el cuadro obtenido por el encabezado de regresión se agrega a las inter_referencias.

        outputs_classes = []#存放分类结果
        outputs_coords = []#存放box结果
        for lvl in range(hs.shape[0]):#6层
            if lvl == 0:
                reference = init_reference#初始化的,没变
            else:
                reference = inter_references[lvl - 1]
            reference = inverse_sigmoid(reference)#反归一化
            outputs_class = self.class_embed[lvl](hs[lvl])#6个分类头,各自预测各自的 torch.Size([2, 300, 91])
            tmp = self.bbox_embed[lvl](hs[lvl])#6个回归头 torch.Size([2, 300, 4])
            if reference.shape[-1] == 4:
                tmp += reference#预测出box结果加上reference,默认为4
            else:
                assert reference.shape[-1] == 2
                tmp[..., :2] += reference
            outputs_coord = tmp.sigmoid()#获取输出box结果并进行归一化
            outputs_classes.append(outputs_class)
            outputs_coords.append(outputs_coord)
        outputs_class = torch.stack(outputs_classes)#凭借输出结果
        outputs_coord = torch.stack(outputs_coords)

reference = inverse_sigmoid(reference)Después del resultado:

inserte la descripción de la imagen aquí
outputs_coord = tmp.sigmoid()Obtenga el resultado del cuadro de salida y normalícelo

inserte la descripción de la imagen aquí

Finalmente, hay 6 salidas_clases:valores de lista, cada uno de los cuales es torch.Size([2, 300, 91])

inserte la descripción de la imagen aquí
Después de outputs_class = torch.stack(outputs_classes): torch.Size([6, 2, 300, 91])

inserte la descripción de la imagen aquí

outputs_coord también desea lo mismo, cambie a torch.Size([6, 2, 300, 4])
y finalmente devuelva el resultado. Se puede ver que la lógica externa de DAB-Deformable-DETR no ha cambiado mucho, centrémonos en cómo cambia internamente el Transformador.

Módulo transformador

En primer lugar, debido a la introducción de funciones de escala múltiple en el módulo de red troncal, el procesamiento antes del codificador también ha cambiado.
El ingreso al módulo Transformador comienza aquí.

hs, init_reference, inter_references, enc_outputs_class, enc_outputs_coord_unact = self.transformer(srcs, masks, pos, query_embeds)

Los parámetros que se pasan son:
srcs: la información de la característica semántica obtenida a través del backbone, en forma de lista, con un total de 4 valores, respectivamente:
torch.Size([2, 256, 76, 95]) torch.Size ([2, 256, 38, 48])
antorcha.Tamaño([2, 256, 19, 24]) antorcha.Tamaño([2, 256, 10, 12])`

inserte la descripción de la imagen aquí

máscaras: valor de máscara, hay 4, cada tamaño es el mismo que el src de la capa correspondiente, pero no hay dimensión de canal, como mask0:torch.Size([2, 76, 95]), mask1: torch. Tamaño ([2, 38, 48])

inserte la descripción de la imagen aquí

pos: código de posición, la dimensión es la misma que srcs.
query_embeds: torch.Size([300, 260]), esto se obtiene empalmando el siguiente código.

tgt_embed = self.tgt_embed.weight           # nq, 256
refanchor = self.refpoint_embed.weight      # nq, 4
query_embeds = torch.cat((tgt_embed, refanchor), dim=1)

Luego llevamos a cabo el módulo Transformador.Primero, preprocesar los datos de la red troncal de entrada y definir las variables primero:

src_flatten = []
mask_flatten = []
lvl_pos_embed_flatten = []
spatial_shapes = []

Convertir srcs, máscaras, pos:
Nota: lvl_pos_embed = pos_embed + self.level_embed[lvl].view(1, 1, -1)(parámetros de aprendizaje)

self.level_embed = nn.Parameter(torch.Tensor(num_feature_levels, d_model))

self.level_embed[lvl] es un parámetro personalizado del módulo Transformer, que es [4, 256], uno de los cuales es 256, y luego se cambia a 1X1X256 a través de la vista, luego se puede comparar con pos_embed (torch.Size( [2, 7220, 256 ])), es decir, en el primer 2X7220, cada 256 se suma. Después de agregar la dimensión lvl_pos_embed es lo mismo que pos_embed, que es torch.Size([2, 7220, 256])

for lvl, (src, mask, pos_embed) in enumerate(zip(srcs, masks, pos_embeds)):
            bs, c, h, w = src.shape
            spatial_shape = (h, w)
            spatial_shapes.append(spatial_shape)
            src = src.flatten(2).transpose(1, 2)# 由bs,c,h,w变为bs, hw, c
            mask = mask.flatten(1)#由bs,h,w变为 bs, hw
            pos_embed = pos_embed.flatten(2).transpose(1, 2)#由bs,c,h,w变为bs, hw, c
            lvl_pos_embed = pos_embed + self.level_embed[lvl].view(1, 1, -1)
            lvl_pos_embed_flatten.append(lvl_pos_embed)
            src_flatten.append(src)
            mask_flatten.append(mask)

Finalmente, se obtiene la posición de la máscara src convertida (de hecho, es una conversión de dimensión). como sigue:

inserte la descripción de la imagen aquí

Luego, src se empalma en la segunda dimensión, es decir, todos los src (cuatro capas en total, forma de lista) se cambian a torch.Size([2, 9620, 256]) De manera similar, mask y pos también se transforman en
el mismo camino.

src_flatten = torch.cat(src_flatten, 1)     # bs, \sum{hxw}, c 
mask_flatten = torch.cat(mask_flatten, 1)   # bs, \sum{hxw}
lvl_pos_embed_flatten = torch.cat(lvl_pos_embed_flatten, 1)

formas_espaciales: [(76, 95), (38, 48), (19, 24), (10, 12)], se convierte el formato triple:

spatial_shapes = torch.as_tensor(spatial_shapes, dtype=torch.long, device=src_flatten.device)

Obtener formato Tensor

tensor([[76, 95],
[38, 48],
[19, 24],
[10, 12]], dispositivo='cuda:0')

Obtenga el índice inicial de cada nivel (empalmamos los valores de los 4 niveles juntos)

level_start_index = torch.cat((spatial_shapes.new_zeros((1, )), spatial_shapes.prod(1).cumsum(0)[:-1]))

Obtener: level_start_index es tensor ([ 0, 7220, 9044, 9500], dispositivo = 'cuda: 0')

valid_ratios = torch.stack([self.get_valid_ratio(m) for m in masks], 1)

El método get_valid_ratio se define de la siguiente manera: la función de este método es en realidad devolver un valor de relación de expansión (es decir, la relación entre largo y ancho) y la dimensión es torch.Size([2, 4, 2]), es decir, lote = 2, 4 para cada capa, 2 es la relación de aspecto

def get_valid_ratio(self, mask):
    _, H, W = mask.shape
    valid_H = torch.sum(~mask[:, :, 0], 1)
    valid_W = torch.sum(~mask[:, 0, :], 1)
    valid_ratio_h = valid_H.float() / H
    valid_ratio_w = valid_W.float() / W
    valid_ratio = torch.stack([valid_ratio_w, valid_ratio_h], -1)
    return valid_ratio

inserte la descripción de la imagen aquí

El código completo es el siguiente:

		src_flatten = []
        mask_flatten = []
        lvl_pos_embed_flatten = []
        spatial_shapes = []
        for lvl, (src, mask, pos_embed) in enumerate(zip(srcs, masks, pos_embeds)):
            bs, c, h, w = src.shape
            spatial_shape = (h, w)
            spatial_shapes.append(spatial_shape)
            src = src.flatten(2).transpose(1, 2)                # bs, hw, c
            mask = mask.flatten(1)                              # bs, hw
            pos_embed = pos_embed.flatten(2).transpose(1, 2)    # bs, hw, c
            lvl_pos_embed = pos_embed + self.level_embed[lvl].view(1, 1, -1)
            lvl_pos_embed_flatten.append(lvl_pos_embed)
            src_flatten.append(src)
            mask_flatten.append(mask)
        src_flatten = torch.cat(src_flatten, 1)     # bs, \sum{hxw}, c 
        mask_flatten = torch.cat(mask_flatten, 1)   # bs, \sum{hxw}
        lvl_pos_embed_flatten = torch.cat(lvl_pos_embed_flatten, 1)
        spatial_shapes = torch.as_tensor(spatial_shapes, dtype=torch.long, device=src_flatten.device)
        level_start_index = torch.cat((spatial_shapes.new_zeros((1, )), spatial_shapes.prod(1).cumsum(0)[:-1]))
        valid_ratios = torch.stack([self.get_valid_ratio(m) for m in masks], 1)

Módulo codificador

Luego envíe los datos al módulo Encoder

memory = self.encoder(src_flatten, spatial_shapes, level_start_index, valid_ratios, lvl_pos_embed_flatten, mask_flatten)

Primero observe los valores de los parámetros de entrada:
src_flatten: torch.Size([2, 9620, 256]) El src aplanado (resultado de la extracción de la columna vertebral)
formas_espaciales: el tamaño de cada capa.

tensor([[76, 95],
        [38, 48],
        [19, 24],
        [10, 12]], device='cuda:0')

level_start_index: tensor([ 0, 7220, 9044, 9500], device='cuda:0')
valid_ratios: torch.Size([2, 4, 2]) La relación de escala de cada capa:

tensor([[[1.0000, 1.0000],
         [1.0000, 1.0000],
         [1.0000, 1.0000],
         [1.0000, 1.0000]],
        [[0.8421, 0.8421],
         [0.8542, 0.8421],
         [0.8750, 0.8421],
         [0.9167, 0.9000]]], device='cuda:0')

El primero en la proporción anterior es todo 1, lo que indica que es relativamente grande, y el segundo tiene un relleno del mismo tamaño que el primero.

lvl_pos_embed_flatten: torch.Size([2, 9620, 256]) información de codificación de posición aplanada
mask_flatten: torch.Size([2, 9620]) información de máscara aplanada

Al ingresar al codificador, el módulo no tiene cambios en la estructura general:

Generar coordenadas de puntos de referencia.

reference_points = self.get_reference_points(spatial_shapes, valid_ratios, device=src.device)

El resultado reference_pointses torch.Size([2, 9620, 4, 2]) El significado de cada dimensión es: lote=2 4
WH se suman para ser 9620, 4 son 4 capas y 2 es el valor x, y.
Al generar, comience con 0.5, tome 1 como intervalo y termine restando 0.5 para generar puntos de cuadrícula (coordenadas de puntos de referencia).Para evitar que los puntos de cuadrícula no existan, deben multiplicarse por su relación de escala.

meshgridLa función se utiliza para generar una matriz de cuadrícula, que puede ser una función de matriz de cuadrícula bidimensional
linspace: Cree una secuencia de valores definiendo intervalos uniformes. Especifique el punto inicial del intervalo, el punto final y especifique 分隔值总数(tanto el punto inicial como el final); la función final devuelve una secuencia de valores numéricos que se distribuyen uniformemente para la clase de intervalo. Ver ejemplo:

np.linspace(start = 0, stop = 100, num = 5)
   def get_reference_points(spatial_shapes, valid_ratios, device):
        reference_points_list = []
        for lvl, (H_, W_) in enumerate(spatial_shapes):
            ref_y, ref_x = torch.meshgrid(torch.linspace(0.5, H_ - 0.5, H_, dtype=torch.float32, device=device),
                                          torch.linspace(0.5, W_ - 0.5, W_, dtype=torch.float32, device=device))
            ref_y = ref_y.reshape(-1)[None] / (valid_ratios[:, None, lvl, 1] * H_)
            ref_x = ref_x.reshape(-1)[None] / (valid_ratios[:, None, lvl, 0] * W_)
            ref = torch.stack((ref_x, ref_y), -1)
            reference_points_list.append(ref)
        reference_points = torch.cat(reference_points_list, 1)
        reference_points = reference_points[:, :, None] * valid_ratios[:, None]
        return reference_points

Las coordenadas de referencia del punto de cuadrícula correspondientes a la pieza.
inserte la descripción de la imagen aquí

Módulo EncoderLayer

Abra el ciclo y envíelo al módulo EncoderLayer. Primero, verifique la información de parámetros enviada:
Cuando se realiza un ciclo en una capa:
la salida es torch.Size ([2, 9620, 256]), que es la información de características extraída por la red troncal .
pos: información de codificación de posición antorcha.Tamaño([2, 9620, 256])
puntos_de_referencia: punto de referencia antorcha.Tamaño([2, 9620, 4, 2])
formas_espaciales: antorcha.Tamaño([4, 2]) su valor es :

tensor([[76, 95],
        [38, 48],
        [19, 24],
        [10, 12]], device='cuda:0')

level_start_index:tensor([ 0, 7220, 9044, 9500], device='cuda:0')
padding_mask:torch.Size([2, 9620])

inserte la descripción de la imagen aquí

output = layer(output, pos, reference_points, spatial_shapes, level_start_index, padding_mask)

Después de ingresar a EncoderLayer, se realiza el cálculo de autoatención, ya que aquí se usa Deformable-Attention (atención deformable), es decir, se self.self_attnreescribe el cálculo de atención de esta parte.

		src2 = self.self_attn(self.with_pos_embed(src, pos), reference_points, src, spatial_shapes, level_start_index, padding_mask)
        src = src + self.dropout1(src2)
        src = self.norm1(src)
        src = self.forward_ffn(src)
        return src

Luego, el resultado src2 será torch.Size([2, 9620, 256]), y luego será exactamente igual que el EncoderLayer normal. El retorno final srces torch.Size([2, 9620, 256])

Módulo de atención deformable

Finalmente viene la parte más crítica: la atención deformable.
La idea de la atención deformable es simple, es decir, cada consulta muestra K posiciones en cada cabeza y solo necesita interactuar con las características de estas posiciones, a diferencia de detr, cada consulta necesita interactuar con la posición global. Cabe señalar que el desplazamiento de posición Δp mqx se obtiene mediante la consulta a través de la capa totalmente conectada, y el peso de atención también se obtiene mediante la consulta a través de la capa totalmente conectada, y el peso se normaliza entre los K puntos de muestreo
inserte la descripción de la imagen aquí

src2 = self.self_attn(self.with_pos_embed(src, pos), reference_points, src, spatial_shapes, level_start_index, padding_mask)

Echemos un vistazo a la información de parámetros enviada a self_attn:

def forward(self, query, reference_points, input_flatten, input_spatial_shapes, input_level_start_index, input_padding_mask=None): 

Coordenadas del punto de referencia

Primero, la información de codificación de posición se agrega a la información de características extraída por la red troncal.
with_pos_embed(src, pos)
reference_points: torch.Size([2, 9620, 4, 2]) Aquí recordamos que reference_points es solo un valor de coordenadas, sin información semántica real, o una cuadrícula, que se establecerá en la función mapa utilizado en.

inserte la descripción de la imagen aquí

formas_espaciales:antorcha.Tamaño([4, 2])

tensor([[76, 95],
        [38, 48],
        [19, 24],
        [10, 12]], device='cuda:0')

src:antorcha.Tamaño([2, 9620, 256])
padding_mask:antorcha.Tamaño([2, 9620])

def with_pos_embed(tensor, pos):
    return tensor if pos is None else tensor + pos

Valor del mapa de características

Ingrese ms_deform_attn.pypara calcular:
la primera consulta es torch.Size ([2, 9620, 256])
input_flatten es src, y la dimensión es torch.Size ([2, 9620, 256])

value = self.value_proj(input_flatten)

Corresponde a la siguiente operación:
inserte la descripción de la imagen aquí

masked_fillEl método tiene dos parámetros, máscara y valor. La máscara es un tensor de pytorch (Tensor), el elemento es un valor booleano y el valor es el valor que se va a llenar. La regla de llenado es que el valor del valor en la máscara corresponde a la posición correspondiente de sí mismo con relleno de valor.

if input_padding_mask is not None:
    value = value.masked_fill(input_padding_mask[..., None], float(0))

Transforme la dimensión del valor en antorcha. Tamaño ([2, 9620, 8, 32])

value = value.view(N, Len_in, self.n_heads, self.d_model // self.n_heads)

Valores de compensación y peso

sampling_offsets = self.sampling_offsets(query).view(N, Len_q, self.n_heads, self.n_levels, self.n_points, 2)
attention_weights = self.attention_weights(query).view(N, Len_q, self.n_heads, self.n_levels * self.n_points)
attention_weights = F.softmax(attention_weights, -1).view(N, Len_q, self.n_heads, self.n_levels, self.n_points)

El código anterior corresponde a la siguiente parte, es decir, la consulta se obtiene a través de linear:
offset sampling_offsets (el offset es el offset en el mapa de características): torch.Size([2, 9620, 8, 4, 4, 2] ), lote = 2, la suma de 4 WH, 8 cabezas, 4 capas de características, 4 puntos de muestreo (se dibujan 3 en la figura), 2 es la coordenada de desplazamiento (x, y)

Obtener el valor del peso a través de lineal.
atención_pesos, al principio es torch.Size([2, 9620, 8, 16]), 16 representa la multiplicación de 4 capas de características y 4 puntos de muestreo, y luego se convierte en torch.Size después de softmax ([2, 9620, 8, 4, 4])

Se puede ver desde aquí: el atten deformable no necesita multiplicar K por el punto Q para obtener el peso de la atención, y su peso de la atención se aprende a través de la consulta de objetos.

inserte la descripción de la imagen aquí
La capa completamente conectada correspondiente:

self.sampling_offsets = nn.Linear(d_model, n_heads * n_levels * n_points * 2)

Luego juzgue si la última dimensión del punto de muestreo es 2, donde puntos_de_referencia es antorcha.Tamaño([2, 9620, 4, 2]), desplazamientos_de_muestreo es antorcha.Tamaño([2, 9620, 8, 4, 4, 2] ), offset_normalizer es torch.Size([4, 2])
, es decir, las coordenadas del punto de referencia más el desplazamiento (el desplazamiento aquí también se divide por el tamaño del mapa de características), para obtener la posición de el verdadero punto de muestreo.

  if reference_points.shape[-1] == 2:
        offset_normalizer = torch.stack([input_spatial_shapes[..., 1], input_spatial_shapes[..., 0]], -1)
        sampling_locations = reference_points[:, :, None, :, None, :] \
                             + sampling_offsets / offset_normalizer[None, None, None, :, None, :]

La primera oración del código es cambiar la posición del ancho y alto de input_spatial_shapes a alto y ancho, como en el original:

tensor([[76, 95],
        [38, 48],
        [19, 24],
        [10, 12]], device='cuda:0')

se convierte en offset_normalizer:

tensor([[95, 76],
        [48, 38],
        [24, 19],
        [12, 10]], device='cuda:0')

La segunda frase es para averiguar las coordenadas reales del punto de referencia. Entre ellos, el papel de Ninguno en [:, :, Ninguno, :, Ninguno, :] es aumentar la dimensión, porque debe agregarse con sampling_offsets, y es torch.Size([2, 9620, 4, 2]), y sampling_offsets es torch.Size([2, 9620, 8, 4, 4, 2]), el cabezal de atención no se considera al agregar (se agregan todos los cabezales), y se deben agregar los 4 puntos de muestreo , así que use [:, :, Ninguno, :, Ninguno, :] al expandir. De la misma manera, dado que su valor de desplazamiento es (src) en el mapa de características, debe dividirse por sus valores de ancho y alto transformados (el número de capas corresponde al número de capas, x, y corresponde al ancho y altura)

Finalmente, enviaremos los valores anteriores obtenidos a la implementación de CUDA (que puede considerarse como el cálculo del valor de atención)

output = MSDeformAttnFunction.apply(
            value, input_spatial_shapes, input_level_start_index, sampling_locations, attention_weights, self.im2col_step)
     

La salida de salida es antorcha. Tamaño ([2, 9620, 256])

inserte la descripción de la imagen aquí

resultado de salida

Finalmente, el resultado del cálculo se emite a través de una capa completamente conectada:

output = self.output_proj(output)

El resultado convertido sigue siendo: torch.Size([2, 9620, 256])

Definición de una capa completamente conectada:

self.output_proj = nn.Linear(d_model, d_model)

Lo que se hace es el siguiente proceso:

inserte la descripción de la imagen aquí
Después de la serie de procesos anterior, se completa el cálculo de la siguiente fórmula:
cálculo de atención prolongada:

inserte la descripción de la imagen aquí
Cálculo de atención multicabezal multiescala:
inserte la descripción de la imagen aquí

Doble etapa de dos etapas

Después de una serie de operaciones, la memoria es el resultado de salida de la antorcha del codificador. Tamaño ([2, 9620, 256])

memory = self.encoder(src_flatten, spatial_shapes, level_start_index, valid_ratios, lvl_pos_embed_flatten, mask_flatten)

Luego juzgue si abre la doble etapa, que es el segundo punto de innovación de Deformable-DETR.
la memoria es antorcha. Tamaño ([2, 9620, 256])
memory_padding_mask es antorcha. Tamaño ([2, 9620])
formas_espaciales es

tensor([[76, 95],
        [38, 48],
        [19, 24],
        [10, 12]], device='cuda:0')

gen_encoder_output_proposalsEste método consiste en realizar una serie de procesamientos en la memoria de valores de salida del codificador, que finalmente se utilizará para la inicialización del punto de referencia en el decodificador.

def gen_encoder_output_proposals(self, memory, memory_padding_mask, spatial_shapes):
    N_, S_, C_ = memory.shape#batch_size ,长度,通道数
    base_scale = 4.0  #多尺度数为4
    proposals = []
    _cur = 0
    for lvl, (H_, W_) in enumerate(spatial_shapes):
        mask_flatten_ = memory_padding_mask[:, _cur:(_cur + H_ * W_)].view(N_, H_, W_, 1)  #按照尺度大小得出mask值 并转换维度为:torch.Size([2, 76, 95, 1])
        valid_H = torch.sum(~mask_flatten_[:, :, 0, 0], 1)  #计算高有多少为真  tensor([76, 64], device='cuda:0')  第一张图片最大,全部为真,第二张图片64为真
        valid_W = torch.sum(~mask_flatten_[:, 0, :, 0], 1)  #计算宽多少为真tensor([95, 80], device='cuda:0')

        grid_y, grid_x = torch.meshgrid(torch.linspace(0, H_ - 1, H_, dtype=torch.float32, device=memory.device),
                                        torch.linspace(0, W_ - 1, W_, dtype=torch.float32, device=memory.device))#生成矩阵网格点坐标
        grid = torch.cat([grid_x.unsqueeze(-1), grid_y.unsqueeze(-1)], -1)
#unsqueeze(-1)  再加一层维度
        scale = torch.cat([valid_W.unsqueeze(-1), valid_H.unsqueeze(-1)], 1).view(N_, 1, 1, 2)
        grid = (grid.unsqueeze(0).expand(N_, -1, -1, -1) + 0.5) / scale
        wh = torch.ones_like(grid) * 0.05 * (2.0 ** lvl)
        proposal = torch.cat((grid, wh), -1).view(N_, -1, 4)
        proposals.append(proposal)
        _cur += (H_ * W_)
    output_proposals = torch.cat(proposals, 1)
    output_proposals_valid = ((output_proposals > 0.01) & (output_proposals < 0.99)).all(-1, keepdim=True)
    output_proposals = torch.log(output_proposals / (1 - output_proposals))
    output_proposals = output_proposals.masked_fill(memory_padding_mask.unsqueeze(-1), float('inf'))
    output_proposals = output_proposals.masked_fill(~output_proposals_valid, float('inf'))

    output_memory = memory
    output_memory = output_memory.masked_fill(memory_padding_mask.unsqueeze(-1), float(0))
    output_memory = output_memory.masked_fill(~output_proposals_valid, float(0))
    output_memory = self.enc_output_norm(self.enc_output(output_memory))
    return output_memory, output_proposals

El valor de salida final
output_memory: torch.Size([2, 9620, 256])

inserte la descripción de la imagen aquí

output_proposals:antorcha.Tamaño([2, 9620, 4])

inserte la descripción de la imagen aquí

Inicie el cálculo de la segunda etapa, llame al encabezado de clasificación y al encabezado de regresión e inicialice 7 durante la inicialización, class_embed[self.decoder.num_layers] es solo el 7.

enc_outputs_class = self.decoder.class_embed[self.decoder.num_layers](output_memory)#对encoder的结果进行分类预测 torch.Size([2, 9620, 91])
enc_outputs_coord_unact = self.decoder.bbox_embed[self.decoder.num_layers](output_memory) + output_proposals#对encoder进行回归并加上output_proposals
torch.Size([2, 9620, 4])

Módulo decodificador

Estoy cansado, lo agregaré más tarde.

Supongo que te gusta

Origin blog.csdn.net/pengxiang1998/article/details/130295796
Recomendado
Clasificación