Notas de lectura DPGN + análisis de código (DPGN: Red de gráficos de propagación de distribución para aprendizaje de pocos disparos)

Papel: https://arxiv.org/pdf/2003.14247.pdf

Código fuente: https://github.com/megvii-research/DPGN

Tabla de contenido

contenido central

relación a nivel de distribución

Estructura de red DPGN y algoritmo de realización

Análisis de estructura de red DPGN

Similitudes de instancia (similitud de instancia)

Similitudes de distribución

P2D y D2P

capa de salida

función de pérdida

Puntos destacados de papel

análisis de código

Preparativos antes de ejecutar el código

Columna vertebral

resultado de la operación


contenido central

En este artículo, proponemos un algoritmo de aprendizaje de pocos disparos basado en una red neuronal de doble gráfico. Basado en la relación tradicional de nivel de instancia, este documento presenta la relación de nivel de distribución para una investigación en profundidad. Para combinar razonablemente las dos relaciones, este documento construye una red neuronal de doble gráfico que cubre un gráfico de puntos (PG, Point Graph) y un gráfico de distribución (DG, Distribution Graph). El marco básico se muestra en la Figura 2 (Figura 2 ). E iterativamente converge a través de la estructura cíclica de PG y DG que interactúan entre sí, y luego obtiene el resultado de la predicción.

relación a nivel de distribución

La relación distribución-nivel es el punto clave de este artículo. La descripción de la relación distribución-nivel en este documento se puede mostrar en la Figura 1 (Figura 1). En primer lugar, todos los conjuntos de soporte y los conjuntos de consultas se incluyen en el cálculo de la similitud de la instancia según las características de la instancia, y el nivel de distribución se construye según los resultados de la similitud. la similitud entre cada muestra y el conjunto completo. Al predecir, compare la relación de nivel de distribución entre la muestra en el conjunto de consulta y el conjunto de soporte, y haga las predicciones correspondientes en función de los resultados de la diferencia. Cuanto menor sea la diferencia, mayor será la posibilidad de pertenecer a esta categoría.

Estructura de red DPGN y algoritmo de realización

Análisis de estructura de red DPGN

Primero, extraiga la característica a nivel de instancia de todas las muestras (soporte+consulta) a través de una capa de red troncal convolucional como la característica del nodo, y calcule las similitudes de instancia como la característica del borde, y luego use estas características del borde para formar un DG a través del algoritmo P2D, donde el nodo La característica (de DG) se inicializa por la característica de borde (de PG) acumulada en el orden de posición de PG, y la característica de borde (de DG) representa la similitud de características del nodo (de DG). Finalmente, la característica de borde obtenida (de DG) se pasa a través del algoritmo D2P y se envía de regreso a GP para actualizar las características del nodo (de GP) y repetir el proceso anterior. Las flechas en la figura indican (conversión de borde a nodo y de nodo a borde)

Similitudes de instancia (similitud de instancia)

Similitud de punto (instancia) (borde en PG): la característica de cada borde en PG representa la similitud de los dos nodos conectados. La fórmula de cálculo es la siguiente.

0 significa que este es el valor inicial, e i, j son nodos, f_{e^{p}_{0}}que convierten Instance Similarities en una red de codificación de cierto tamaño. Específicamente, la red contiene dos etapas Conv-BN-ReLU y la capa de salida usa sigmoide.

En la etapa no inicial,

l representa el número de generaciones, y para integrar razonablemente la información global, se realizará una normalización posterior al cálculo de las Similitudes de Instancia globales.

Similitudes de distribución

 La similitud de distribución es muy similar a las similitudes de instancia. La función de borde en DG se usa para representar las características de distribución de diferentes muestras, la fórmula y la capa de procesamiento de red, y la normalización final son similares a antes. La fórmula de cálculo es la siguiente, y la descripción no se repetirá.

P2D y D2P

 El objetivo principal de P2D es transferir las características de borde (similitudes de instancia) en PG a los bordes en DG para calcular las características de nodo correspondientes. Las fórmulas de cálculo del valor inicial y cálculo iterativo son las siguientes;

 El operador de concatenación está representado por '||'. Puede conectar columnas y columnas, columnas y caracteres entre sí, para desempeñar el papel de sintetizar una nueva columna personalizada. donde y representa la etiqueta correspondiente. En el cálculo inicial, el resultado del cálculo de la dimensión NK se obtiene directamente. En el cálculo iterativo, use las características de borde NK (similitudes de instancia) en PG y las características de nodo de la dimensión NK DG de la iteración anterior para conectar dos vectores de dimensión NK en la dimensión NK a través de P2D (compuesto de capas completamente conectadas y ReLU) de vectores

 El propósito y el proceso de realización de D2P y P2D también son muy similares. Principalmente transfiere las características de borde (similitudes de distribución) en DG a PG para actualizar las características de nodo en PG. La fórmula de cálculo es la siguiente, y la red D2P también se compone de una capa totalmente conectada y ReLU.

capa de salida

 Después de las iteraciones, de acuerdo con la última característica de PG edge (similitudes de instancia) combinada con one-hot, el resultado de la predicción se genera a través de softmax.

función de pérdida

Esta parte es relativamente simple y no se describirá en detalle.

pérdida de puntos

Pérdida de distribución

Pérdida total

Puntos destacados de papel

1. Tomar la iniciativa en la introducción de la propagación de distribución

2. Red gráfica completa dual

3. Lograr una mejora significativa.

 El uso de DPGN puede facilitar el aprendizaje de muestras pequeñas supervisadas débilmente, especialmente cuando el conjunto de soporte contiene muestras etiquetadas y no etiquetadas para cada clase. En otras palabras, DPGN construye un puente para muestras etiquetadas y no etiquetadas en forma de similitud de distribución. Esta muestra pequeña supervisada débilmente el aprendizaje produce mejores predicciones de etiquetas.

análisis de código

Preparativos antes de ejecutar el código

(1) Conjunto de datos

El código fuente usa el conjunto de datos de MiniImagenet por defecto, pero es diferente del oficial. El autor lo ha procesado y clasificado hasta cierto punto, por lo que es mejor usar el conjunto de datos proporcionado por el autor, de lo contrario, causará algunos problemas. . Al mismo tiempo, el autor también usó otros tres conjuntos de datos, las URL de descarga son las siguientes.

[MiniImageNet](https://drive.google.com/open?id=15WuREBvhEbSWo4fTr1r-vMY0C_6QWv4w)

[ImageNet en niveles] (https://drive.google.com/file/d/1nVGCTd9ttULRXFezh4xILQ9lUkg0WZCG)

[CIFAR-FS](https://drive.google.com/file/d/1GjGMI0q3bgcpcB_CjI40fX54WgLPuTpS)

[CUB-200-2011](https://github.com/wyharveychen/CloserLookFewShot/tree/master/filelists/CUB)

(2) Entorno operativo

Si desea ejecutarlo localmente, primero debe descargar el conjunto de datos, pero debido a que el conjunto de datos es demasiado grande y está en Google Drive, es difícil descargarlo con éxito incluso si salta el muro, por lo que debe cooperar con IDM para descargarlo.

Si usa la colaboración de Google, solo necesita guardar el conjunto de datos en la unidad de Google en su propia unidad de Google y luego modificar la ruta correspondiente en el código, lo que evitará muchos problemas.

(3) Un pequeño problema con el código

Puede consultar este blog, https://blog.csdn.net/jsk_learner/article/details/103833034

 El código modificado es:

Columna vertebral

El código de este artículo se divide aproximadamente en 5 partes: principal, cargador de datos, dpgn, backbone, utils Este artículo presenta principalmente dpgn y backbone. La red troncal se utiliza principalmente para la red de extracción de las características de la instancia inicial. En esta parte, se diseñan principalmente la estructura básica de la red ResNet12 y la red Conv4.

El diseño de red de Resnet12 no ha cambiado mucho. Básicamente, el módulo de red residual básico (que incluye la red convolucional de 3 capas + la estructura de salida residual) se diseña primero, y luego se diseñan la capa básica de entrada y la capa de salida.

La capa de salida y la capa oculta de Conv4 también son la estructura básica de la red convolucional, pero la capa de salida final es diferente. La estructura de salida final es la tercera capa de la red convolucional. Los resultados de salida pasan por la cuarta capa de la red convolucional y la más grande. piscina respectivamente La combinación de las dos estructuras de la capa química.

import torch.nn as nn


class ResNet12Block(nn.Module):
    """
    ResNet Block
    """
    def __init__(self, inplanes, planes):
        super(ResNet12Block, self).__init__()

        self.conv1 = nn.Conv2d(inplanes, planes, kernel_size=1, bias=False)
        self.bn1 = nn.BatchNorm2d(planes)
        self.conv2 = nn.Conv2d(planes, planes, kernel_size=3, stride=1,
                               padding=1, bias=False)
        self.bn2 = nn.BatchNorm2d(planes)
        self.conv3 = nn.Conv2d(planes, planes, kernel_size=1, bias=False)
        self.bn3 = nn.BatchNorm2d(planes)
        self.relu = nn.LeakyReLU(negative_slope=0.2, inplace=True)

        self.conv = nn.Conv2d(inplanes, planes, kernel_size=1, bias=False)
        self.bn = nn.BatchNorm2d(planes)
        self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1)

    def forward(self, x):
        residual = x
        residual = self.conv(residual)
        residual = self.bn(residual)

        out = self.conv1(x)
        out = self.bn1(out)
        out = self.relu(out)

        out = self.conv2(out)
        out = self.bn2(out)
        out = self.relu(out)

        out = self.conv3(out)
        out = self.bn3(out)

        out += residual
        out = self.relu(out)
        out = self.maxpool(out)
        return out


class ResNet12(nn.Module):
    """
    ResNet12 Backbone
    """
    def __init__(self, emb_size, block=ResNet12Block, cifar_flag=False):
        super(ResNet12, self).__init__()
        cfg = [64, 128, 256, 512]
        # layers = [1, 1, 1, 1]
        iChannels = int(cfg[0])
        self.conv1 = nn.Conv2d(3, iChannels, kernel_size=3, stride=1, padding=1, bias=False)
        self.bn1 = nn.BatchNorm2d(iChannels)
        self.relu = nn.LeakyReLU()
        self.emb_size = emb_size
        self.layer1 = self._make_layer(block, cfg[0], cfg[0])
        self.layer2 = self._make_layer(block, cfg[0], cfg[1])
        self.layer3 = self._make_layer(block, cfg[1], cfg[2])
        self.layer4 = self._make_layer(block, cfg[2], cfg[3])
        self.avgpool = nn.AvgPool2d(7)
        self.maxpool = nn.MaxPool2d(kernel_size=2)
        layer_second_in_feat = cfg[2] * 5 * 5 if not cifar_flag else cfg[2] * 2 * 2
        self.layer_second = nn.Sequential(nn.Linear(in_features=layer_second_in_feat,
                                                    out_features=self.emb_size,
                                                    bias=True),
                                          nn.BatchNorm1d(self.emb_size))

        self.layer_last = nn.Sequential(nn.Linear(in_features=cfg[3],
                                                  out_features=self.emb_size,
                                                  bias=True),
                                        nn.BatchNorm1d(self.emb_size))

        for m in self.modules():
            if isinstance(m, nn.Conv2d):
                nn.init.kaiming_normal_(m.weight, mode='fan_out', nonlinearity='leaky_relu')
            elif isinstance(m, nn.BatchNorm2d):
                nn.init.constant_(m.weight, 1)
                nn.init.constant_(m.bias, 0)

    def _make_layer(self, block, inplanes, planes):
        layers = []
        layers.append(block(inplanes, planes))
        return nn.Sequential(*layers)

    def forward(self, x):
        # 3 -> 64
        x = self.conv1(x)
        x = self.bn1(x)
        x = self.relu(x)
        # 64 -> 64
        x = self.layer1(x)
        # 64 -> 128
        x = self.layer2(x)
        # 128 -> 256
        inter = self.layer3(x)
        # 256 -> 512
        x = self.layer4(inter)
        
        AdaptiveAvgPool2d = nn.AdaptiveAvgPool2d((1,1))
        x = AdaptiveAvgPool2d(x)
        
        #x = self.avgpool(x)
        
        x = x.view(x.size(0), -1)
        # 512 -> 128
        x = self.layer_last(x)
        inter = self.maxpool(inter)
        # 256 * 5 * 5
        inter = inter.view(inter.size(0), -1)
        # 256 * 5 * 5 -> 128
        inter = self.layer_second(inter)
        out = []
        out.append(x)
        out.append(inter)
        # no FC here
        return out


class ConvNet(nn.Module):
    """
    Conv4 Backbone
    """
    def __init__(self, emb_size, cifar_flag=False):
        super(ConvNet, self).__init__()
        # set size
        self.hidden = 128
        self.last_hidden = self.hidden * 25 if not cifar_flag else self.hidden
        self.emb_size = emb_size

        # set layers
        self.conv_1 = nn.Sequential(nn.Conv2d(in_channels=3,
                                              out_channels=self.hidden,
                                              kernel_size=3,
                                              padding=1,
                                              bias=False),
                                    nn.BatchNorm2d(num_features=self.hidden),
                                    nn.MaxPool2d(kernel_size=2),
                                    nn.LeakyReLU(negative_slope=0.2, inplace=True))
        self.conv_2 = nn.Sequential(nn.Conv2d(in_channels=self.hidden,
                                              out_channels=int(self.hidden*1.5),
                                              kernel_size=3,
                                              bias=False),
                                    nn.BatchNorm2d(num_features=int(self.hidden*1.5)),
                                    nn.MaxPool2d(kernel_size=2),
                                    nn.LeakyReLU(negative_slope=0.2, inplace=True))
        self.conv_3 = nn.Sequential(nn.Conv2d(in_channels=int(self.hidden*1.5),
                                              out_channels=self.hidden*2,
                                              kernel_size=3,
                                              padding=1,
                                              bias=False),
                                    nn.BatchNorm2d(num_features=self.hidden * 2),
                                    nn.MaxPool2d(kernel_size=2),
                                    nn.LeakyReLU(negative_slope=0.2, inplace=True),
                                    nn.Dropout2d(0.4))
        self.max = nn.MaxPool2d(kernel_size=2)
        self.layer_second = nn.Sequential(nn.Linear(in_features=self.last_hidden * 2,
                                          out_features=self.emb_size, bias=True),
                                          nn.BatchNorm1d(self.emb_size))
        self.conv_4 = nn.Sequential(nn.Conv2d(in_channels=self.hidden*2,
                                              out_channels=self.hidden*4,
                                              kernel_size=3,
                                              padding=1,
                                              bias=False),
                                    nn.BatchNorm2d(num_features=self.hidden * 4),
                                    nn.MaxPool2d(kernel_size=2),
                                    nn.LeakyReLU(negative_slope=0.2, inplace=True),
                                    nn.Dropout2d(0.5))
        self.layer_last = nn.Sequential(nn.Linear(in_features=self.last_hidden * 4,
                                                  out_features=self.emb_size, bias=True),
                                        nn.BatchNorm1d(self.emb_size))

    def forward(self, input_data):
        out_1 = self.conv_1(input_data)
        out_2 = self.conv_2(out_1)
        out_3 = self.conv_3(out_2)
        output_data = self.conv_4(out_3)
        output_data0 = self.max(out_3)
        out = []
        out.append(self.layer_last(output_data.view(output_data.size(0), -1)))
        out.append(self.layer_second(output_data0.view(output_data0.size(0), -1)))
        return out

resultado de la operación

El conjunto de datos de miniimagen se probó en colab. Los resultados del paso 5000 y el paso 9000 son los siguientes. La tasa de precisión no ha mejorado mucho. Puede deberse a tiempos de entrenamiento insuficientes o al tamaño del lote (el tamaño del lote del tren en este ambiente es 10 ).

Supongo que te gusta

Origin blog.csdn.net/nwpufreshman/article/details/118659416
Recomendado
Clasificación