más rápidorcnn_resnet50_fpn comprensión personal del código fuente

Recientemente, estuve mirando algunos códigos sobre la clasificación de imágenes en Tianchi, y descubrí que son básicamente los parámetros de ajuste de Yolo. Quería ver si los modelos anteriores realmente no podían mantenerse al día, y luego pasé por torchvision. Luego seleccioné la red más rápidorcnn_resnet50_fpn, porque solo seguí el tutorial anterior y no lo leí en detalle. Me tomó un tiempo analizar el conjunto de datos de codificación de caracteres de Street View más básico de Tianchi. Miré el código fuente mientras esperaba que se ejecutaran los datos.

1. Primero observe el principio de Fasterrcnn_resnet50_fpn

Para decirlo sin rodeos, las 3 capas de fpn, rpn y roi pooling están cubiertas fuera de resnet50. Capa por capa es lo mismo que envolver wontons.

fpn : Se usa para obtener los datos de cada downsampling de resnet50 , pero para asegurarse de que cada capa obtenga los mejores datos de muestreo, hizo una cosa muy coincidente, de hecho, sigue siendo la idea del módulo residual. , como se muestra en la imagen:

Excepto que la última capa de conv5 es el resultado de la reducción de muestreo directamente, las otras de conv2-conv4 no solo usan el resultado de muestreo original, sino que también agregan el muestreo superior (ampliación) de la capa inferior (el resultado de la imagen reducida ) . los resultados están garantizados para ser los mejores en términos de características.

rpn: después de obtener los datos característicos, rpn solo hace 4 cosas:

        1. Regresión de categoría y regresión de caja

        2.anclajes generación de anclas

        3. filtro de caja

        4. Cálculo de pérdida, calcule la pérdida del tipo y la pérdida de la caja.

Agrupación de ROI: la escala de la característica es fija y de salida.

Después de los pasos anteriores, finalmente realizamos una capa completamente conectada para la clasificación de artículos y la regresión de cajas . Todo el proceso ha terminado.

2. Echa un vistazo al código fuente

 model = torchvision.models.detection.fasterrcnn_resnet50_fpn()

Ingrese directamente desde aquí, y luego podrá ver un montón de cosas que se rascan la cabeza.

def fasterrcnn_resnet50_fpn(pretrained=False, progress=True,
                            num_classes=91, pretrained_backbone=True, trainable_backbone_layers=3, **kwargs):
"""Constructs a Faster R-CNN model with a ResNet-50-FPN backbone.
   ................
"""
    assert trainable_backbone_layers <= 5 and trainable_backbone_layers >= 0
    # dont freeze any layers if pretrained model or backbone is not used
    if not (pretrained or pretrained_backbone):
        trainable_backbone_layers = 5
    if pretrained:
        # no need to download the backbone if pretrained is set
        pretrained_backbone = False
    # 主干网络的设置
    backbone = resnet_fpn_backbone('resnet50', pretrained_backbone, trainable_layers=trainable_backbone_layers)
    # 定义FasterRCNN
    model = FasterRCNN(backbone, num_classes, **kwargs)
    if pretrained:
        state_dict = load_state_dict_from_url(model_urls['fasterrcnn_resnet50_fpn_coco'],
                                              progress=progress)
        model.load_state_dict(state_dict)
    return model

Las dos líneas de código más críticas

# 主干网络的设置
    backbone = resnet_fpn_backbone('resnet50', pretrained_backbone, trainable_layers=trainable_backbone_layers)
    # 定义FasterRCNN
    model = FasterRCNN(backbone, num_classes, **kwargs)

2.1 Primer vistazo a la parte de backbond

Se encuentra que el fpn primero se envuelve fuera de la red resnet y luego se pasa a FasterRCNN.

Haga clic en resnet_fpn_backbone y busque

def resnet_fpn_backbone(
    backbone_name,
    pretrained,
    norm_layer=misc_nn_ops.FrozenBatchNorm2d,
    trainable_layers=3,
    returned_layers=None,
    extra_blocks=None
):
    backbone = resnet.__dict__[backbone_name](
        pretrained=pretrained,
        norm_layer=norm_layer)

    # select layers that wont be frozen
    assert trainable_layers <= 5 and trainable_layers >= 0
    layers_to_train = ['layer4', 'layer3', 'layer2', 'layer1', 'conv1'][:trainable_layers]
    # freeze layers only if pretrained backbone is used
    for name, parameter in backbone.named_parameters():
        if all([not name.startswith(layer) for layer in layers_to_train]):
            parameter.requires_grad_(False)

    if extra_blocks is None:
        extra_blocks = LastLevelMaxPool()

    if returned_layers is None:
        returned_layers = [1, 2, 3, 4]
    assert min(returned_layers) > 0 and max(returned_layers) < 5
    return_layers = {f'layer{k}': str(v) for v, k in enumerate(returned_layers)}

    in_channels_stage2 = backbone.inplanes // 8
    in_channels_list = [in_channels_stage2 * 2 ** (i - 1) for i in returned_layers]
    out_channels = 256
    return BackboneWithFPN(backbone, return_layers, in_channels_list, out_channels, extra_blocks=extra_blocks)

El último retorno es el método directo de la clase BackboneWithFPN, lo que significa que tantos códigos son todos preparativos de parámetros.

La red troncal debe realizar la capa de pirámide de características que incluye {'capa1': '0', 'capa2': '1', 'capa3': '2', 'capa4': '3'}

in_channels_list Accede a in_channels_list de la capa FPN

out_channels El canal de la capa de salida FPN, que no cambia el ancho y el alto de la entidad.

Busque una capa de BackboneWithFPN 

class BackboneWithFPN(nn.Module):
    def __init__(self, backbone, return_layers, in_channels_list, out_channels, extra_blocks=None):
        super(BackboneWithFPN, self).__init__()

        if extra_blocks is None:
            extra_blocks = LastLevelMaxPool()
         # 根据需返回层的名字return_layers返回特征
        self.body = IntermediateLayerGetter(backbone, return_layers=return_layers)
         # 特征金字塔,以输出的特征做特征金字塔
        self.fpn = FeaturePyramidNetwork(
            in_channels_list=in_channels_list,
            out_channels=out_channels,
            extra_blocks=extra_blocks,
        )
        self.out_channels = out_channels

    def forward(self, x):
        x = self.body(x)
        x = self.fpn(x)
        return x

Este módulo es para generar la pirámide de características.

Hasta ahora, todas las funciones de la red troncal están completas, que es la parte Resnet50_fpn.

A continuación, mire la parte FasterRCNN

2.2 FastRCNN

 model = FasterRCNN(backbone, num_classes, **kwargs)

haga clic aquí

class FasterRCNN(GeneralizedRCNN):

Puede ver que FasterRCNN hereda de GeneralizedRCNN y GeneralizedRCNN hereda de la clase base nn.Module GeneralizedRCNN se usa como una excusa para que las clases abstractas controlen la lógica general. Mirando de cerca el código de GeneralizedRCNN, lo más importante es el método forward(), pero mirando hacia atrás en el código de FasterRCNN, no reescribió forward(), todo se hereda de GeneralizedRCNN forward()

super(FasterRCNN, self).__init__(backbone, rpn, roi_heads, transform)

Para la clase GeneralizedRCNN, hay 4 interfaces importantes:

1. transformar

2. columna vertebral

3. rpn

4. cabezas_roi

La transformación aquí hace dos cosas.

1. Estandarice la entrada, reste la media de la imagen de entrada y divídala por la varianza

2. Escale el cuadro de destino en la imagen y el destino.

Luego ingrese los tensores de imágenes transformados en la red principal principal e introdúzcalos en la red principal para extraer el mapa de características.

features = self.backbone(images.tensors)

La red troncal es generalmente VGG, ResNet, MobileNet y otras redes. Hay ejemplos relacionados en el tutorial oficial de pytorch. El ejemplo oficial se reemplaza por MobileNet.

Después de obtener el mapa de funciones, genere propuestas y propuestas_pérdidas a través del módulo rpn, luego ingrese el módulo roi_heads (roi_pooling + clasificación) y finalmente pase el módulo de posprocesamiento (realice NMS y, al mismo tiempo, vuelva a asignar el cuadro a la imagen original a través de tamaño_imágenes_originales).

2.2 El papel del módulo RPN

Ve a comprobar el método forward() de rpn.py

    def forward(self,
                images,       # type: ImageList
                features,     # type: Dict[str, Tensor]
                targets=None  # type: Optional[List[Dict[str, Tensor]]]
                ):
        # type: (...) -> Tuple[List[Tensor], Dict[str, Tensor]]
  
        # RPN uses all feature maps that are available
        features = list(features.values())
        objectness, pred_bbox_deltas = self.head(features)
        anchors = self.anchor_generator(images, features)

        num_images = len(anchors)
        num_anchors_per_level_shape_tensors = [o[0].shape for o in objectness]
        num_anchors_per_level = [s[0] * s[1] * s[2] for s in num_anchors_per_level_shape_tensors]
        objectness, pred_bbox_deltas = \
            concat_box_prediction_layers(objectness, pred_bbox_deltas)
        # apply pred_bbox_deltas to anchors to obtain the decoded proposals
        # note that we detach the deltas because Faster R-CNN do not backprop through
        # the proposals
        proposals = self.box_coder.decode(pred_bbox_deltas.detach(), anchors)
        proposals = proposals.view(num_images, -1, 4)
        boxes, scores = self.filter_proposals(proposals, objectness, images.image_sizes, num_anchors_per_level)

        losses = {}
        if self.training:
            assert targets is not None
            labels, matched_gt_boxes = self.assign_targets_to_anchors(anchors, targets)
            regression_targets = self.box_coder.encode(matched_gt_boxes, anchors)
            loss_objectness, loss_rpn_box_reg = self.compute_loss(
                objectness, pred_bbox_deltas, labels, regression_targets)
            losses = {
                "loss_objectness": loss_objectness,
                "loss_rpn_box_reg": loss_rpn_box_reg,
            }
        return boxes, losses

1. Predicción de categorías y predicción de fotogramas

objectness, pred_bbox_deltas = self.head(features)
class RPNHead(nn.Module):

    def __init__(self, in_channels, num_anchors):
        super(RPNHead, self).__init__()
        self.conv = nn.Conv2d(
            in_channels, in_channels, kernel_size=3, stride=1, padding=1
        )
        self.cls_logits = nn.Conv2d(in_channels, num_anchors, kernel_size=1, stride=1)
        self.bbox_pred = nn.Conv2d(
            in_channels, num_anchors * 4, kernel_size=1, stride=1
        )

        for layer in self.children():
            torch.nn.init.normal_(layer.weight, std=0.01)
            torch.nn.init.constant_(layer.bias, 0)

    def forward(self, x):
        # type: (List[Tensor]) -> Tuple[List[Tensor], List[Tensor]]
        logits = []
        bbox_reg = []
        for feature in x:
            t = F.relu(self.conv(feature))
            logits.append(self.cls_logits(t))
            bbox_reg.append(self.bbox_pred(t))
        return logits, bbox_reg

El principio de RPNHead aquí es realmente muy simple, es decir, después de una convolución de 3*3, se divide en dos flujos, uno para clasificación y otro para predecir el marco.

2.anclajes generación de anclas

anchors = self.anchor_generator(images, features)
if rpn_anchor_generator is None:
    anchor_sizes = ((32,), (64,), (128,), (256,), (512,))
    aspect_ratios = ((0.5, 1.0, 2.0),) * len(anchor_sizes)
    rpn_anchor_generator = AnchorGenerator(
        anchor_sizes, aspect_ratios
    )

El principio de ancla_generador es en realidad generar un cuadro candidato de un tamaño específico basado en el mapa de características. Los parámetros predeterminados aquí son 32, 64, 128, 256 y 512. Este parámetro se establece para min_size = 800. Esta configuración es para tamaños de imagen más grandes El efecto de detección es mejor. La detección y el reconocimiento de objetivos pequeños en imágenes de tamaño pequeño deben ajustarse de acuerdo con la situación real. Cada tamaño de ancla corresponde a un mapa de características para generar anclas, y cada punto de característica del mapa de características deforma anclas con diferentes relaciones de aspecto según las relaciones de aspecto.

Para múltiples mapas de características de diferentes tamaños generados por la red FPN, se procesará cada mapa de características y se establecerán anclas. Después del procesamiento, obtenga varios anclajes densamente empaquetados.

3. Filtrado enmarcado

 self.filter_proposals(proposals, objectness, images.image_sizes, num_anchors_per_level)

4. Cálculo de pérdidas por categorías y marcos

loss_objectness, loss_rpn_box_reg = self.compute_loss(objectness, pred_bbox_deltas, labels, regression_targets)

Entropía cruzada de tiempo de pérdida de categoría

La pérdida de cuadro es la pérdida de regresión bajo la premisa de considerar solo muestras positivas.

2.3 Construcción del ROI

Los componentes principales de roi son: box_roi_pool, box_head, box_predictor

box_roi_pool: roialign alinea mapas de características de diferentes tamaños al mismo tamaño

box_head: Realice dos procesamientos de secuencias de las características de salida de box_roi_pool, una para la clasificación de destino y otra para la regresión de caja

box_predictor: la clasificación y la regresión predicen las características de box_head two stream, la dimensión de salida final de box_predictor es el número de categorías de clasificación

Después del proceso transform -- rpn -- roi, todo el proceso de formación de llamadas rcnn más rápido se completa básicamente y, finalmente, el resultado de salida se restaura al tamaño de imagen original.

Supongo que te gusta

Origin blog.csdn.net/qq_35326529/article/details/127908486
Recomendado
Clasificación