ECO: red convolucional eficiente para la comprensión de vídeos en línea

Recientemente, es necesario probar los fracasos y los parámetros de esta red. Aquí se presenta brevemente parte de la estructura y el código de la red y cómo probarlo.

El autor del artículo afirmó en la introducción que los algoritmos de comprensión de video han logrado grandes avances con el apoyo del aprendizaje profundo, pero muchos algoritmos solo se centran en la detección de acciones en un período de tiempo más corto. Este contenido de corto plazo es insuficiente si queremos comprender mejor la intención de acciones de mayor escala. Al mismo tiempo, algunos algoritmos de convolución 3D son tan complejos computacionalmente que sólo pueden procesar una parte del vídeo. Estos algoritmos suelen tener una característica: integran información en diferentes momentos "a posteriori".

Por lo tanto, el autor diseñó directamente una arquitectura de un extremo a otro para resolver estos problemas. El autor se dio cuenta de que, dado que una sola imagen ya puede clasificar el contenido de la acción, una gran cantidad de cuadros adyacentes son redundantes, por lo que es suficiente procesar solo un cuadro en un período de tiempo. Además, el autor cree que en lugar de integrar las puntuaciones de cada período de tiempo para obtener la puntuación final, es mejor integrar la información de las características en cada período de tiempo. Podemos tomar un cuadro a cierta distancia y poner estas imágenes en convolución 3D para su procesamiento, lo que no solo puede procesar mejor la información temporal, sino también durante un período de tiempo más largo.

Sin más, hablemos de la estructura de la red:

La imagen de arriba es la estructura de red de ECO Lite: el video se divide en N secciones de la misma longitud y se selecciona un cuadro al azar de cada sección. Estas imágenes se procesan primero a través de una red de convolución 2D convencional para obtener mapas de características y luego, los mapas de características se apilan como entrada a una red convolucional 3D. Las redes 3D clasifican acciones en función de información temporal.

Aquí hay un diseño clave: el muestreo aleatorio. El muestreo aleatorio en cada segmento ayuda a aumentar la diversidad del muestreo, permitiendo que más variaciones en una acción ingresen a la red. Además, aunque la división de secciones de tiempo se puede estudiar más a fondo, en aras de la simplicidad del razonamiento, el autor no adoptó un método de división dinámica más complejo.

Teniendo en cuenta las necesidades de los diferentes usuarios, el autor nombró el diseño anterior como versión liviana (Lite, que es ECO en el código, y Full es ECOfully en el código) y, por lo tanto, actualizó el diseño a la versión de tamaño completo (Full). Las dos versiones son más o menos iguales, la diferencia es que la versión ligera (Lite) se centra principalmente en extraer información a largo plazo a través de la red 3D, por lo que las características a corto plazo pueden ignorarse. La versión de tamaño completo (Full) agrega una red 2D para utilizar aún más la información de la pieza 2D. El nuevo diseño se muestra a continuación:

 Los mapas de características generados por la red 2D recién agregada y la red 3D original se conectarán entre sí antes de ingresar al clasificador.

Red 2D:  se adopta la estructura BN-Inception y se obtiene el corte de Inception-3c. El motivo de la selección es la eficiencia. El resultado es un mapa de características de 96.

Red 3D:  Se utiliza una versión mejorada de 3D-Resnet18. Esta estructura también es muy utilizada.

Redes 2D:  esta es la nueva red 2D agregada a la red de tamaño completo. También se utiliza BN-Inception, comenzando desde la capa Inception-4a y terminando con la última capa de agrupación. La salida tiene 1024 longitudes, N vectores de características en total.

Consulte el documento para obtener detalles sobre cómo entrenar. A continuación, presentaremos algunos códigos de la red ECO:

import torch
from torch import nn
from .layer_factory import get_basic_layer, parse_expr
import torch.utils.model_zoo as model_zoo
import yaml


class ECO(nn.Module):
    def __init__(self, model_path='tf_model_zoo/ECO/ECO.yaml', num_classes=101,
                       num_segments=4, pretrained_parts='both'):

        super(ECO, self).__init__()

        self.num_segments = num_segments

        self.pretrained_parts = pretrained_parts

        manifest = yaml.safe_load(open(model_path))

        layers = manifest['layers']

        self._channel_dict = dict()

        self._op_list = list()
        for l in layers:
            out_var, op, in_var = parse_expr(l['expr'])
            if op != 'Concat' and op != 'Eltwise':
                id, out_name, module, out_channel, in_name = get_basic_layer(l,
                                                                3 if len(self._channel_dict) == 0 else self._channel_dict[in_var[0]],
                                                                             conv_bias=True if op == 'Conv3d' else True, num_segments=num_segments)

                self._channel_dict[out_name] = out_channel
                setattr(self, id, module)
                self._op_list.append((id, op, out_name, in_name))
            elif op == 'Concat':
                self._op_list.append((id, op, out_var[0], in_var))
                channel = sum([self._channel_dict[x] for x in in_var])
                self._channel_dict[out_var[0]] = channel
            else:
                self._op_list.append((id, op, out_var[0], in_var))
                channel = self._channel_dict[in_var[0]]
                self._channel_dict[out_var[0]] = channel


    def forward(self, input):
        data_dict = dict()
        data_dict[self._op_list[0][-1]] = input

        def get_hook(name):

            def hook(m, grad_in, grad_out):
                print(name, grad_out[0].data.abs().mean())

            return hook
        for op in self._op_list:
            if op[1] != 'Concat' and op[1] != 'InnerProduct' and op[1] != 'Eltwise':
                # first 3d conv layer judge, the last 2d conv layer's output must be transpose from 4d to 5d
                if op[0] == 'res3a_2':
                    inception_3c_output = data_dict['inception_3c_double_3x3_1_bn']
                    inception_3c_transpose_output = torch.transpose(inception_3c_output.view((-1, self.num_segments) + inception_3c_output.size()[1:]), 1, 2)
                    data_dict[op[2]] = getattr(self, op[0])(inception_3c_transpose_output)
                else:
                    data_dict[op[2]] = getattr(self, op[0])(data_dict[op[-1]])
                    # getattr(self, op[0]).register_backward_hook(get_hook(op[0]))
            elif op[1] == 'InnerProduct':
                x = data_dict[op[-1]]
                data_dict[op[2]] = getattr(self, op[0])(x.view(x.size(0), -1))
            elif op[1] == 'Eltwise':
                try:
                    data_dict[op[2]] = torch.add(data_dict[op[-1][0]], 1, data_dict[op[-1][1]])
                except:
                    for x in op[-1]:
                        print(x,data_dict[x].size())
                    raise
                # x = data_dict[op[-1]]
                # data_dict[op[2]] = getattr(self, op[0])(x.view(x.size(0), -1))
            else:
                try:
                    data_dict[op[2]] = torch.cat(tuple(data_dict[x] for x in op[-1]), 1)
                except:
                    for x in op[-1]:
                        print(x,data_dict[x].size())
                    raise
        # print output data size in each layers
        # for k in data_dict.keys():
        #     print(k,": ",data_dict[k].size())
        # exit()

        # "self._op_list[-1][2]" represents: last layer's name(e.g. fc_action)
        return data_dict[self._op_list[-1][2]]

El código anterior utiliza algunas funciones en Layer_factory.py para cargar el archivo de definición del modelo ECO ECO.yaml, establecer la propagación hacia adelante y devolver los datos finales obtenidos mediante la propagación hacia adelante.

ECO implementa la definición e inicialización del modelo de red cambiando base_model según TSN. Para TSN, consulte el código de red TSN o el código al final de este artículo.

Los fracasos y parámetros de la red de prueba son los siguientes. Además, ECO_en representa un conjunto con el número de cuadro de entrada de 16, 20, 24 y 32. Modifique el archivo de configuración:

import argparse
parser = argparse.ArgumentParser(description="PyTorch implementation of ECO")
parser.add_argument('--dataset', type=str, default="kinetics", choices=['ucf101', 'hmdb51', 'kinetics', 'something','jhmdb'])
parser.add_argument('--modality', type=str, default="RGB", choices=['RGB', 'Flow', 'RGBDiff'])
parser.add_argument('--train_list', default="", type=str)
parser.add_argument('--val_list', default="", type=str)
parser.add_argument('--net_model', type=str, default=None)
parser.add_argument('--net_model2D', type=str, default=None)
parser.add_argument('--net_modelECO', type=str, default=None)
parser.add_argument('--net_model3D', type=str, default=None)
# ========================= Model Configs ==========================
parser.add_argument('--arch', type=str, default="ECO")
parser.add_argument('--num_segments', type=int, default=16)
parser.add_argument('--consensus_type', type=str, default='avg',
                    choices=['avg', 'max', 'topk', 'identity', 'rnn', 'cnn'])
parser.add_argument('--pretrained_parts', type=str, default='both',
                    choices=['scratch', '2D', '3D', 'both','finetune'])
parser.add_argument('--k', type=int, default=3)

parser.add_argument('--dropout', '--do', default=0.5, type=float,
                    metavar='DO', help='dropout ratio (default: 0.5)')
parser.add_argument('--loss_type', type=str, default="nll",
                    choices=['nll'])

# ========================= Learning Configs ==========================
parser.add_argument('--epochs', default=45, type=int, metavar='N',
                    help='number of total epochs to run')
parser.add_argument('-b', '--batch-size', default=1, type=int,
                    metavar='N', help='mini-batch size (default: 256)')
parser.add_argument('-i', '--iter-size', default=1, type=int,
                    metavar='N', help='number of iterations before on update')
parser.add_argument('--lr', '--learning-rate', default=0.001, type=float,
                    metavar='LR', help='initial learning rate')
parser.add_argument('--lr_steps', default=[20, 40], type=float, nargs="+",
                    metavar='LRSteps', help='epochs to decay learning rate by 10')
parser.add_argument('--momentum', default=0.9, type=float, metavar='M',
                    help='momentum')
parser.add_argument('--weight-decay', '--wd', default=5e-4, type=float,
                    metavar='W', help='weight decay (default: 5e-4)')
parser.add_argument('--clip-gradient', '--gd', default=None, type=float,
                    metavar='W', help='gradient norm clipping (default: disabled)')
parser.add_argument('--no_partialbn', '--npb', default=False, action="store_true")
parser.add_argument('--nesterov',  default=False)
parser.add_argument('--num_saturate', type=int, default=5,
                    help='if number of epochs that validation Prec@1 saturates, then decrease lr by 10 (default: 5)')

# ========================= Monitor Configs ==========================
parser.add_argument('--print-freq', '-p', default=20, type=int,
                    metavar='N', help='print frequency (default: 10)')
parser.add_argument('--eval-freq', '-ef', default=5, type=int,
                    metavar='N', help='evaluation frequency (default: 5)')


# ========================= Runtime Configs ==========================
parser.add_argument('-j', '--workers', default=4, type=int, metavar='N',
                    help='number of data loading workers (default: 4)')
parser.add_argument('--resume', default='', type=str, metavar='PATH',
                    help='path to latest checkpoint (default: none)')
parser.add_argument('-e', '--evaluate', dest='evaluate', action='store_true',
                    help='evaluate model on validation set')
parser.add_argument('--snapshot_pref', type=str, default="")
parser.add_argument('--start-epoch', default=0, type=int, metavar='N',
                    help='manual epoch number (useful on restarts)')
parser.add_argument('--gpus', nargs='+', type=int, default=None)
parser.add_argument('--flow_prefix', default="", type=str)
parser.add_argument('--rgb_prefix', default="", type=str)

Aquí el tamaño de lote se establece en 1 y se extrae un cuadro de cada segmento durante la entrada. El código de prueba es el siguiente:


import torch
from models import TSN

from opts import parser


args = parser.parse_args()

model = TSN(400, args.num_segments, args.pretrained_parts, args.modality,
                base_model="ECO",
                consensus_type=args.consensus_type, dropout=args.dropout, partial_bn=not args.no_partialbn)

from thop import profile
#input:(t, c, h, w), 注意:此处t = batch_size*segment_num, 根据TSN而定
input = torch.randn(16,3,224,224)
flops, params = profile(model, inputs=(input,))
print("FLOPs = %f G"%(flops/1e9))
print("params = %f M"%(params/1e6))
# print(model)
#FLOPs = 46.681547 G
#params = 37.503856 M

El modelo se inicializa como se indica arriba. Al probar otros números de fotograma, es necesario cambiar segment_nums. Al mismo tiempo, las estadísticas de parámetros también pueden utilizar el resumen en torchsummary.

No leí este documento en detalle, simplemente inicialicé la red y probé los fracasos y los parámetros. Para obtener más detalles, consulte el documento y el código fuente.

El documento y el código fuente son los siguientes:

1804.09066.pdf (arxiv.org) https://arxiv.org/pdf/1804.09066.pdf mzolfaghari/ECO-pytorch: Implementación de PyTorch para "ECO: Red convolucional eficiente para la comprensión de videos en línea", ECCV 2018 (github.com) https //github.com/mzolfaghari/ECO-pytorch

Supongo que te gusta

Origin blog.csdn.net/Mr___WQ/article/details/127302371
Recomendado
Clasificación