From 538a9ab82ec61046c865749d692f91b832012c8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E6=B3=BD=E7=91=9E1?= <9409489+zhang-zerui-1@user.noreply.gitee.com> Date: Sat, 10 Jul 2021 10:43:27 +0000 Subject: [PATCH 1/3] =?UTF-8?q?=E6=96=B0=E5=BB=BA=20=E5=8A=A8=E4=BD=9C?= =?UTF-8?q?=E8=AF=86=E5=88=AB-=E5=BC=A0=E6=B3=BD=E7=91=9E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../.keep" | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 "code/2021_spring/\345\212\250\344\275\234\350\257\206\345\210\253-\345\274\240\346\263\275\347\221\236/.keep" diff --git "a/code/2021_spring/\345\212\250\344\275\234\350\257\206\345\210\253-\345\274\240\346\263\275\347\221\236/.keep" "b/code/2021_spring/\345\212\250\344\275\234\350\257\206\345\210\253-\345\274\240\346\263\275\347\221\236/.keep" new file mode 100644 index 0000000..e69de29 -- Gitee From f3aaa9a2cf3f647cd89c4743e3824b37db63673c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E6=B3=BD=E7=91=9E1?= <9409489+zhang-zerui-1@user.noreply.gitee.com> Date: Sat, 10 Jul 2021 13:34:14 +0000 Subject: [PATCH 2/3] =?UTF-8?q?=E6=96=B0=E5=BB=BA=20slow-fast1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../slow-fast1/.keep" | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 "code/2021_spring/\345\212\250\344\275\234\350\257\206\345\210\253-\345\274\240\346\263\275\347\221\236/slow-fast1/.keep" diff --git "a/code/2021_spring/\345\212\250\344\275\234\350\257\206\345\210\253-\345\274\240\346\263\275\347\221\236/slow-fast1/.keep" "b/code/2021_spring/\345\212\250\344\275\234\350\257\206\345\210\253-\345\274\240\346\263\275\347\221\236/slow-fast1/.keep" new file mode 100644 index 0000000..e69de29 -- Gitee From 358945a604cadb739e5e13b16fc202768fd6fc51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E6=B3=BD=E7=91=9E1?= <9409489+zhang-zerui-1@user.noreply.gitee.com> Date: Sat, 10 Jul 2021 15:08:04 +0000 Subject: [PATCH 3/3] run_model --- .../benchmark.py" | 25 + .../demo_net.py" | 119 ++++ .../run_net.py" | 44 ++ .../test_net.py" | 202 +++++++ .../train_net.py" | 526 ++++++++++++++++++ .../visualization.py" | 347 ++++++++++++ 6 files changed, 1263 insertions(+) create mode 100644 "code/2021_spring/\345\212\250\344\275\234\350\257\206\345\210\253-\345\274\240\346\263\275\347\221\236/benchmark.py" create mode 100644 "code/2021_spring/\345\212\250\344\275\234\350\257\206\345\210\253-\345\274\240\346\263\275\347\221\236/demo_net.py" create mode 100644 "code/2021_spring/\345\212\250\344\275\234\350\257\206\345\210\253-\345\274\240\346\263\275\347\221\236/run_net.py" create mode 100644 "code/2021_spring/\345\212\250\344\275\234\350\257\206\345\210\253-\345\274\240\346\263\275\347\221\236/test_net.py" create mode 100644 "code/2021_spring/\345\212\250\344\275\234\350\257\206\345\210\253-\345\274\240\346\263\275\347\221\236/train_net.py" create mode 100644 "code/2021_spring/\345\212\250\344\275\234\350\257\206\345\210\253-\345\274\240\346\263\275\347\221\236/visualization.py" diff --git "a/code/2021_spring/\345\212\250\344\275\234\350\257\206\345\210\253-\345\274\240\346\263\275\347\221\236/benchmark.py" "b/code/2021_spring/\345\212\250\344\275\234\350\257\206\345\210\253-\345\274\240\346\263\275\347\221\236/benchmark.py" new file mode 100644 index 0000000..acdc12a --- /dev/null +++ "b/code/2021_spring/\345\212\250\344\275\234\350\257\206\345\210\253-\345\274\240\346\263\275\347\221\236/benchmark.py" @@ -0,0 +1,25 @@ +#!/usr/bin/env python3 +# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved +""" +A script to benchmark data loading. +""" + +import slowfast.utils.logging as logging +from slowfast.utils.benchmark import benchmark_data_loading +from slowfast.utils.misc import launch_job +from slowfast.utils.parser import load_config, parse_args + +logger = logging.get_logger(__name__) + + +def main(): + args = parse_args() + cfg = load_config(args) + + launch_job( + cfg=cfg, init_method=args.init_method, func=benchmark_data_loading + ) + + +if __name__ == "__main__": + main() diff --git "a/code/2021_spring/\345\212\250\344\275\234\350\257\206\345\210\253-\345\274\240\346\263\275\347\221\236/demo_net.py" "b/code/2021_spring/\345\212\250\344\275\234\350\257\206\345\210\253-\345\274\240\346\263\275\347\221\236/demo_net.py" new file mode 100644 index 0000000..a7e98eb --- /dev/null +++ "b/code/2021_spring/\345\212\250\344\275\234\350\257\206\345\210\253-\345\274\240\346\263\275\347\221\236/demo_net.py" @@ -0,0 +1,119 @@ +#!/usr/bin/env python3 +# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. + +import numpy as np +import time +import torch +import tqdm + +from slowfast.utils import logging +from slowfast.visualization.async_predictor import AsyncDemo, AsyncVis +from slowfast.visualization.ava_demo_precomputed_boxes import ( + AVAVisualizerWithPrecomputedBox, +) +from slowfast.visualization.demo_loader import ThreadVideoManager, VideoManager +from slowfast.visualization.predictor import ActionPredictor +from slowfast.visualization.video_visualizer import VideoVisualizer + +logger = logging.get_logger(__name__) + + +def run_demo(cfg, frame_provider): + """ + Run demo visualization. + Args: + cfg (CfgNode): configs. Details can be found in + slowfast/config/defaults.py + frame_provider (iterator): Python iterator that return task objects that are filled + with necessary information such as `frames`, `id` and `num_buffer_frames` for the + prediction and visualization pipeline. + """ + # Set random seed from configs. + np.random.seed(cfg.RNG_SEED) + torch.manual_seed(cfg.RNG_SEED) + # Setup logging format. + logging.setup_logging(cfg.OUTPUT_DIR) + # Print config. + logger.info("Run demo with config:") + logger.info(cfg) + common_classes = ( + cfg.DEMO.COMMON_CLASS_NAMES + if len(cfg.DEMO.LABEL_FILE_PATH) != 0 + else None + ) + + video_vis = VideoVisualizer( + num_classes=cfg.MODEL.NUM_CLASSES, + class_names_path=cfg.DEMO.LABEL_FILE_PATH, + top_k=cfg.TENSORBOARD.MODEL_VIS.TOPK_PREDS, + thres=cfg.DEMO.COMMON_CLASS_THRES, + lower_thres=cfg.DEMO.UNCOMMON_CLASS_THRES, + common_class_names=common_classes, + colormap=cfg.TENSORBOARD.MODEL_VIS.COLORMAP, + mode=cfg.DEMO.VIS_MODE, + ) + + async_vis = AsyncVis(video_vis, n_workers=cfg.DEMO.NUM_VIS_INSTANCES) + + if cfg.NUM_GPUS <= 1: + model = ActionPredictor(cfg=cfg, async_vis=async_vis) + else: + model = AsyncDemo(cfg=cfg, async_vis=async_vis) + + seq_len = cfg.DATA.NUM_FRAMES * cfg.DATA.SAMPLING_RATE + + assert ( + cfg.DEMO.BUFFER_SIZE <= seq_len // 2 + ), "Buffer size cannot be greater than half of sequence length." + num_task = 0 + # Start reading frames. + frame_provider.start() + for able_to_read, task in frame_provider: + if not able_to_read: + break + if task is None: + time.sleep(0.02) + continue + num_task += 1 + + model.put(task) + try: + task = model.get() + num_task -= 1 + yield task + except IndexError: + continue + + while num_task != 0: + try: + task = model.get() + num_task -= 1 + yield task + except IndexError: + continue + + +def demo(cfg): + """ + Run inference on an input video or stream from webcam. + Args: + cfg (CfgNode): configs. Details can be found in + slowfast/config/defaults.py + """ + # AVA format-specific visualization with precomputed boxes. + if cfg.DETECTION.ENABLE and cfg.DEMO.PREDS_BOXES != "": + precomputed_box_vis = AVAVisualizerWithPrecomputedBox(cfg) + precomputed_box_vis() + else: + start = time.time() + if cfg.DEMO.THREAD_ENABLE: + frame_provider = ThreadVideoManager(cfg) + else: + frame_provider = VideoManager(cfg) + + for task in tqdm.tqdm(run_demo(cfg, frame_provider)): + frame_provider.display(task) + + frame_provider.join() + frame_provider.clean() + logger.info("Finish demo in: {}".format(time.time() - start)) diff --git "a/code/2021_spring/\345\212\250\344\275\234\350\257\206\345\210\253-\345\274\240\346\263\275\347\221\236/run_net.py" "b/code/2021_spring/\345\212\250\344\275\234\350\257\206\345\210\253-\345\274\240\346\263\275\347\221\236/run_net.py" new file mode 100644 index 0000000..2a78a2c --- /dev/null +++ "b/code/2021_spring/\345\212\250\344\275\234\350\257\206\345\210\253-\345\274\240\346\263\275\347\221\236/run_net.py" @@ -0,0 +1,44 @@ +"""Wrapper to train and test a video classification model.""" +from slowfast.config.defaults import assert_and_infer_cfg +from slowfast.utils.misc import launch_job +from slowfast.utils.parser import load_config, parse_args + +import os +from demo_net import demo +from test_net import test +from train_net import train +from visualization import visualize + +def main(): + """ + Main function to spawn the train and test process. + """ + test_path="/home/slow-fast1/action" + test_set=os.listdir(test_path) + for file in test_set: + args = parse_args() + cfg = load_config(args) + cfg = assert_and_infer_cfg(cfg) + + # Perform training. + if cfg.TRAIN.ENABLE: + launch_job(cfg=cfg, init_method=args.init_method, func=train) + + # Perform multi-clip testing. + if cfg.TEST.ENABLE: + launch_job(cfg=cfg, init_method=args.init_method, func=test) + + # Perform model visualization. + if cfg.TENSORBOARD.ENABLE and ( + cfg.TENSORBOARD.MODEL_VIS.ENABLE + or cfg.TENSORBOARD.WRONG_PRED_VIS.ENABLE + ): + launch_job(cfg=cfg, init_method=args.init_method, func=visualize) + + # Run demo. + if cfg.DEMO.ENABLE: + demo(cfg) + + +if __name__ == "__main__": + main() diff --git "a/code/2021_spring/\345\212\250\344\275\234\350\257\206\345\210\253-\345\274\240\346\263\275\347\221\236/test_net.py" "b/code/2021_spring/\345\212\250\344\275\234\350\257\206\345\210\253-\345\274\240\346\263\275\347\221\236/test_net.py" new file mode 100644 index 0000000..550b1bf --- /dev/null +++ "b/code/2021_spring/\345\212\250\344\275\234\350\257\206\345\210\253-\345\274\240\346\263\275\347\221\236/test_net.py" @@ -0,0 +1,202 @@ +#!/usr/bin/env python3 +# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. + +"""Multi-view test a video classification model.""" + +import numpy as np +import os +import pickle +import torch +from iopath.common.file_io import g_pathmgr + +import slowfast.utils.checkpoint as cu +import slowfast.utils.distributed as du +import slowfast.utils.logging as logging +import slowfast.utils.misc as misc +import slowfast.visualization.tensorboard_vis as tb +from slowfast.datasets import loader +from slowfast.models import build_model +from slowfast.utils.meters import AVAMeter, TestMeter + +logger = logging.get_logger(__name__) + + +@torch.no_grad() +def perform_test(test_loader, model, test_meter, cfg, writer=None): + """ + For classification: + Perform mutli-view testing that uniformly samples N clips from a video along + its temporal axis. For each clip, it takes 3 crops to cover the spatial + dimension, followed by averaging the softmax scores across all Nx3 views to + form a video-level prediction. All video predictions are compared to + ground-truth labels and the final testing performance is logged. + For detection: + Perform fully-convolutional testing on the full frames without crop. + Args: + test_loader (loader): video testing loader. + model (model): the pretrained video model to test. + test_meter (TestMeter): testing meters to log and ensemble the testing + results. + cfg (CfgNode): configs. Details can be found in + slowfast/config/defaults.py + writer (TensorboardWriter object, optional): TensorboardWriter object + to writer Tensorboard log. + """ + # Enable eval mode. + model.eval() + test_meter.iter_tic() + + for cur_iter, (inputs, labels, video_idx, meta) in enumerate(test_loader): + if cfg.NUM_GPUS: + # Transfer the data to the current GPU device. + if isinstance(inputs, (list,)): + for i in range(len(inputs)): + inputs[i] = inputs[i].cuda(non_blocking=True) + else: + inputs = inputs.cuda(non_blocking=True) + + # Transfer the data to the current GPU device. + labels = labels.cuda() + video_idx = video_idx.cuda() + for key, val in meta.items(): + if isinstance(val, (list,)): + for i in range(len(val)): + val[i] = val[i].cuda(non_blocking=True) + else: + meta[key] = val.cuda(non_blocking=True) + test_meter.data_toc() + + if cfg.DETECTION.ENABLE: + # Compute the predictions. + preds = model(inputs, meta["boxes"]) + ori_boxes = meta["ori_boxes"] + metadata = meta["metadata"] + + preds = preds.detach().cpu() if cfg.NUM_GPUS else preds.detach() + ori_boxes = ( + ori_boxes.detach().cpu() if cfg.NUM_GPUS else ori_boxes.detach() + ) + metadata = ( + metadata.detach().cpu() if cfg.NUM_GPUS else metadata.detach() + ) + + if cfg.NUM_GPUS > 1: + preds = torch.cat(du.all_gather_unaligned(preds), dim=0) + ori_boxes = torch.cat(du.all_gather_unaligned(ori_boxes), dim=0) + metadata = torch.cat(du.all_gather_unaligned(metadata), dim=0) + + test_meter.iter_toc() + # Update and log stats. + test_meter.update_stats(preds, ori_boxes, metadata) + test_meter.log_iter_stats(None, cur_iter) + else: + # Perform the forward pass. + preds = model(inputs) + + # Gather all the predictions across all the devices to perform ensemble. + if cfg.NUM_GPUS > 1: + preds, labels, video_idx = du.all_gather( + [preds, labels, video_idx] + ) + if cfg.NUM_GPUS: + preds = preds.cpu() + labels = labels.cpu() + video_idx = video_idx.cpu() + + test_meter.iter_toc() + # Update and log stats. + test_meter.update_stats( + preds.detach(), labels.detach(), video_idx.detach() + ) + test_meter.log_iter_stats(cur_iter) + + test_meter.iter_tic() + + # Log epoch stats and print the final testing results. + if not cfg.DETECTION.ENABLE: + all_preds = test_meter.video_preds.clone().detach() + all_labels = test_meter.video_labels + if cfg.NUM_GPUS: + all_preds = all_preds.cpu() + all_labels = all_labels.cpu() + if writer is not None: + writer.plot_eval(preds=all_preds, labels=all_labels) + + if cfg.TEST.SAVE_RESULTS_PATH != "": + save_path = os.path.join(cfg.OUTPUT_DIR, cfg.TEST.SAVE_RESULTS_PATH) + + if du.is_root_proc(): + with g_pathmgr.open(save_path, "wb") as f: + pickle.dump([all_preds, all_labels], f) + + logger.info( + "Successfully saved prediction results to {}".format(save_path) + ) + + test_meter.finalize_metrics() + return test_meter + + +def test(cfg): + """ + Perform multi-view testing on the pretrained video model. + Args: + cfg (CfgNode): configs. Details can be found in + slowfast/config/defaults.py + """ + # Set up environment. + du.init_distributed_training(cfg) + # Set random seed from configs. + np.random.seed(cfg.RNG_SEED) + torch.manual_seed(cfg.RNG_SEED) + + # Setup logging format. + logging.setup_logging(cfg.OUTPUT_DIR) + + # Print config. + logger.info("Test with config:") + logger.info(cfg) + + # Build the video model and print model statistics. + model = build_model(cfg) + if du.is_master_proc() and cfg.LOG_MODEL_INFO: + misc.log_model_info(model, cfg, use_train_input=False) + + cu.load_test_checkpoint(cfg, model) + + # Create video testing loaders. + test_loader = loader.construct_loader(cfg, "test") + logger.info("Testing model for {} iterations".format(len(test_loader))) + + if cfg.DETECTION.ENABLE: + assert cfg.NUM_GPUS == cfg.TEST.BATCH_SIZE or cfg.NUM_GPUS == 0 + test_meter = AVAMeter(len(test_loader), cfg, mode="test") + else: + assert ( + test_loader.dataset.num_videos + % (cfg.TEST.NUM_ENSEMBLE_VIEWS * cfg.TEST.NUM_SPATIAL_CROPS) + == 0 + ) + # Create meters for multi-view testing. + test_meter = TestMeter( + test_loader.dataset.num_videos + // (cfg.TEST.NUM_ENSEMBLE_VIEWS * cfg.TEST.NUM_SPATIAL_CROPS), + cfg.TEST.NUM_ENSEMBLE_VIEWS * cfg.TEST.NUM_SPATIAL_CROPS, + cfg.MODEL.NUM_CLASSES, + len(test_loader), + cfg.DATA.MULTI_LABEL, + cfg.DATA.ENSEMBLE_METHOD, + ) + + # Set up writer for logging to Tensorboard format. + if cfg.TENSORBOARD.ENABLE and du.is_master_proc( + cfg.NUM_GPUS * cfg.NUM_SHARDS + ): + writer = tb.TensorboardWriter(cfg) + else: + writer = None + + # # Perform multi-view test on the entire dataset. + test_meter = perform_test(test_loader, model, test_meter, cfg, writer) + if writer is not None: + writer.close() diff --git "a/code/2021_spring/\345\212\250\344\275\234\350\257\206\345\210\253-\345\274\240\346\263\275\347\221\236/train_net.py" "b/code/2021_spring/\345\212\250\344\275\234\350\257\206\345\210\253-\345\274\240\346\263\275\347\221\236/train_net.py" new file mode 100644 index 0000000..1f9c267 --- /dev/null +++ "b/code/2021_spring/\345\212\250\344\275\234\350\257\206\345\210\253-\345\274\240\346\263\275\347\221\236/train_net.py" @@ -0,0 +1,526 @@ +"""Train a video classification model.""" + +import numpy as np +import pprint +import torch +from fvcore.nn.precise_bn import get_bn_modules, update_bn_stats + +import slowfast.models.losses as losses +import slowfast.models.optimizer as optim +import slowfast.utils.checkpoint as cu +import slowfast.utils.distributed as du +import slowfast.utils.logging as logging +import slowfast.utils.metrics as metrics +import slowfast.utils.misc as misc +import slowfast.visualization.tensorboard_vis as tb +from slowfast.datasets import loader +from slowfast.datasets.mixup import MixUp +from slowfast.models import build_model +from slowfast.utils.meters import AVAMeter, EpochTimer, TrainMeter, ValMeter +from slowfast.utils.multigrid import MultigridSchedule + +logger = logging.get_logger(__name__) + + +def train_epoch( + train_loader, model, optimizer, train_meter, cur_epoch, cfg, writer=None +): + """ + Perform the video training for one epoch. + Args: + train_loader (loader): video training loader. + model (model): the video model to train. + optimizer (optim): the optimizer to perform optimization on the model's + parameters. + train_meter (TrainMeter): training meters to log the training performance. + cur_epoch (int): current epoch of training. + cfg (CfgNode): configs. Details can be found in + slowfast/config/defaults.py + writer (TensorboardWriter, optional): TensorboardWriter object + to writer Tensorboard log. + """ + # Enable train mode. + model.train() + train_meter.iter_tic() + data_size = len(train_loader) + + if cfg.MIXUP.ENABLE: + mixup_fn = MixUp( + mixup_alpha=cfg.MIXUP.ALPHA, + cutmix_alpha=cfg.MIXUP.CUTMIX_ALPHA, + mix_prob=cfg.MIXUP.PROB, + switch_prob=cfg.MIXUP.SWITCH_PROB, + label_smoothing=cfg.MIXUP.LABEL_SMOOTH_VALUE, + num_classes=cfg.MODEL.NUM_CLASSES, + ) + + for cur_iter, (inputs, labels, _, meta) in enumerate(train_loader): + # Transfer the data to the current GPU device. + if cfg.NUM_GPUS: + if isinstance(inputs, (list,)): + for i in range(len(inputs)): + inputs[i] = inputs[i].cuda(non_blocking=True) + else: + inputs = inputs.cuda(non_blocking=True) + labels = labels.cuda() + for key, val in meta.items(): + if isinstance(val, (list,)): + for i in range(len(val)): + val[i] = val[i].cuda(non_blocking=True) + else: + meta[key] = val.cuda(non_blocking=True) + + # Update the learning rate. + lr = optim.get_epoch_lr(cur_epoch + float(cur_iter) / data_size, cfg) + optim.set_lr(optimizer, lr) + + train_meter.data_toc() + if cfg.MIXUP.ENABLE: + samples, labels = mixup_fn(inputs[0], labels) + inputs[0] = samples + + if cfg.DETECTION.ENABLE: + preds = model(inputs, meta["boxes"]) + else: + preds = model(inputs) + # Explicitly declare reduction to mean. + loss_fun = losses.get_loss_func(cfg.MODEL.LOSS_FUNC)(reduction="mean") + + # Compute the loss. + loss = loss_fun(preds, labels) + + # check Nan Loss. + misc.check_nan_losses(loss) + + # Perform the backward pass. + optimizer.zero_grad() + loss.backward() + if cfg.SOLVER.CLIP_GRAD_VAL: + torch.nn.utils.clip_grad_value_(model.parameters(), cfg.SOLVER.CLIP_GRAD_VAL) + elif cfg.SOLVER.CLIP_GRAD_L2NORM: + torch.nn.utils.clip_grad_norm_(model.parameters(), cfg.SOLVER.CLIP_GRAD_L2NORM) + # Update the parameters. + optimizer.step() + + if cfg.MIXUP.ENABLE: + _top_max_k_vals, top_max_k_inds = torch.topk( + labels, 2, dim=1, largest=True, sorted=True + ) + idx_top1 = torch.arange(labels.shape[0]), top_max_k_inds[:, 0] + idx_top2 = torch.arange(labels.shape[0]), top_max_k_inds[:, 1] + preds = preds.detach() + preds[idx_top1] += preds[idx_top2] + preds[idx_top2] = 0.0 + labels = top_max_k_inds[:, 0] + + if cfg.DETECTION.ENABLE: + if cfg.NUM_GPUS > 1: + loss = du.all_reduce([loss])[0] + loss = loss.item() + + # Update and log stats. + train_meter.update_stats(None, None, None, loss, lr) + # write to tensorboard format if available. + if writer is not None: + writer.add_scalars( + {"Train/loss": loss, "Train/lr": lr}, + global_step=data_size * cur_epoch + cur_iter, + ) + + else: + top1_err, top5_err = None, None + if cfg.DATA.MULTI_LABEL: + # Gather all the predictions across all the devices. + if cfg.NUM_GPUS > 1: + [loss] = du.all_reduce([loss]) + loss = loss.item() + else: + # Compute the errors. + num_topks_correct = metrics.topks_correct(preds, labels, (1, 5)) + top1_err, top5_err = [ + (1.0 - x / preds.size(0)) * 100.0 for x in num_topks_correct + ] + # Gather all the predictions across all the devices. + if cfg.NUM_GPUS > 1: + loss, top1_err, top5_err = du.all_reduce( + [loss, top1_err, top5_err] + ) + + # Copy the stats from GPU to CPU (sync point). + loss, top1_err, top5_err = ( + loss.item(), + top1_err.item(), + top5_err.item(), + ) + + # Update and log stats. + train_meter.update_stats( + top1_err, + top5_err, + loss, + lr, + inputs[0].size(0) + * max( + cfg.NUM_GPUS, 1 + ), # If running on CPU (cfg.NUM_GPUS == 1), use 1 to represent 1 CPU. + ) + # write to tensorboard format if available. + if writer is not None: + writer.add_scalars( + { + "Train/loss": loss, + "Train/lr": lr, + "Train/Top1_err": top1_err, + "Train/Top5_err": top5_err, + }, + global_step=data_size * cur_epoch + cur_iter, + ) + + train_meter.iter_toc() # measure allreduce for this meter + train_meter.log_iter_stats(cur_epoch, cur_iter) + train_meter.iter_tic() + + # Log epoch stats. + train_meter.log_epoch_stats(cur_epoch) + train_meter.reset() + + +@torch.no_grad() +def eval_epoch(val_loader, model, val_meter, cur_epoch, cfg, writer=None): + """ + Evaluate the model on the val set. + Args: + val_loader (loader): data loader to provide validation data. + model (model): model to evaluate the performance. + val_meter (ValMeter): meter instance to record and calculate the metrics. + cur_epoch (int): number of the current epoch of training. + cfg (CfgNode): configs. Details can be found in + slowfast/config/defaults.py + writer (TensorboardWriter, optional): TensorboardWriter object + to writer Tensorboard log. + """ + + # Evaluation mode enabled. The running stats would not be updated. + model.eval() + val_meter.iter_tic() + + for cur_iter, (inputs, labels, _, meta) in enumerate(val_loader): + if cfg.NUM_GPUS: + # Transferthe data to the current GPU device. + if isinstance(inputs, (list,)): + for i in range(len(inputs)): + inputs[i] = inputs[i].cuda(non_blocking=True) + else: + inputs = inputs.cuda(non_blocking=True) + labels = labels.cuda() + for key, val in meta.items(): + if isinstance(val, (list,)): + for i in range(len(val)): + val[i] = val[i].cuda(non_blocking=True) + else: + meta[key] = val.cuda(non_blocking=True) + val_meter.data_toc() + + if cfg.DETECTION.ENABLE: + # Compute the predictions. + preds = model(inputs, meta["boxes"]) + ori_boxes = meta["ori_boxes"] + metadata = meta["metadata"] + + if cfg.NUM_GPUS: + preds = preds.cpu() + ori_boxes = ori_boxes.cpu() + metadata = metadata.cpu() + + if cfg.NUM_GPUS > 1: + preds = torch.cat(du.all_gather_unaligned(preds), dim=0) + ori_boxes = torch.cat(du.all_gather_unaligned(ori_boxes), dim=0) + metadata = torch.cat(du.all_gather_unaligned(metadata), dim=0) + + val_meter.iter_toc() + # Update and log stats. + val_meter.update_stats(preds, ori_boxes, metadata) + + else: + preds = model(inputs) + + if cfg.DATA.MULTI_LABEL: + if cfg.NUM_GPUS > 1: + preds, labels = du.all_gather([preds, labels]) + else: + # Compute the errors. + num_topks_correct = metrics.topks_correct(preds, labels, (1, 5)) + + # Combine the errors across the GPUs. + top1_err, top5_err = [ + (1.0 - x / preds.size(0)) * 100.0 for x in num_topks_correct + ] + if cfg.NUM_GPUS > 1: + top1_err, top5_err = du.all_reduce([top1_err, top5_err]) + + # Copy the errors from GPU to CPU (sync point). + top1_err, top5_err = top1_err.item(), top5_err.item() + + val_meter.iter_toc() + # Update and log stats. + val_meter.update_stats( + top1_err, + top5_err, + inputs[0].size(0) + * max( + cfg.NUM_GPUS, 1 + ), # If running on CPU (cfg.NUM_GPUS == 1), use 1 to represent 1 CPU. + ) + # write to tensorboard format if available. + if writer is not None: + writer.add_scalars( + {"Val/Top1_err": top1_err, "Val/Top5_err": top5_err}, + global_step=len(val_loader) * cur_epoch + cur_iter, + ) + + val_meter.update_predictions(preds, labels) + + val_meter.log_iter_stats(cur_epoch, cur_iter) + val_meter.iter_tic() + + # Log epoch stats. + val_meter.log_epoch_stats(cur_epoch) + # write to tensorboard format if available. + if writer is not None: + if cfg.DETECTION.ENABLE: + writer.add_scalars( + {"Val/mAP": val_meter.full_map}, global_step=cur_epoch + ) + else: + all_preds = [pred.clone().detach() for pred in val_meter.all_preds] + all_labels = [ + label.clone().detach() for label in val_meter.all_labels + ] + if cfg.NUM_GPUS: + all_preds = [pred.cpu() for pred in all_preds] + all_labels = [label.cpu() for label in all_labels] + writer.plot_eval( + preds=all_preds, labels=all_labels, global_step=cur_epoch + ) + + val_meter.reset() + + +def calculate_and_update_precise_bn(loader, model, num_iters=200, use_gpu=True): + """ + Update the stats in bn layers by calculate the precise stats. + Args: + loader (loader): data loader to provide training data. + model (model): model to update the bn stats. + num_iters (int): number of iterations to compute and update the bn stats. + use_gpu (bool): whether to use GPU or not. + """ + + def _gen_loader(): + for inputs, *_ in loader: + if use_gpu: + if isinstance(inputs, (list,)): + for i in range(len(inputs)): + inputs[i] = inputs[i].cuda(non_blocking=True) + else: + inputs = inputs.cuda(non_blocking=True) + yield inputs + + # Update the bn stats. + update_bn_stats(model, _gen_loader(), num_iters) + + +def build_trainer(cfg): + """ + Build training model and its associated tools, including optimizer, + dataloaders and meters. + Args: + cfg (CfgNode): configs. Details can be found in + slowfast/config/defaults.py + Returns: + model (nn.Module): training model. + optimizer (Optimizer): optimizer. + train_loader (DataLoader): training data loader. + val_loader (DataLoader): validatoin data loader. + precise_bn_loader (DataLoader): training data loader for computing + precise BN. + train_meter (TrainMeter): tool for measuring training stats. + val_meter (ValMeter): tool for measuring validation stats. + """ + # Build the video model and print model statistics. + model = build_model(cfg) + if du.is_master_proc() and cfg.LOG_MODEL_INFO: + misc.log_model_info(model, cfg, use_train_input=True) + + # Construct the optimizer. + optimizer = optim.construct_optimizer(model, cfg) + + # Create the video train and val loaders. + train_loader = loader.construct_loader(cfg, "train") + val_loader = loader.construct_loader(cfg, "val") + precise_bn_loader = loader.construct_loader( + cfg, "train", is_precise_bn=True + ) + # Create meters. + train_meter = TrainMeter(len(train_loader), cfg) + val_meter = ValMeter(len(val_loader), cfg) + + return ( + model, + optimizer, + train_loader, + val_loader, + precise_bn_loader, + train_meter, + val_meter, + ) + + +def train(cfg): + """ + Train a video model for many epochs on train set and evaluate it on val set. + Args: + cfg (CfgNode): configs. Details can be found in + slowfast/config/defaults.py + """ + # Set up environment. + du.init_distributed_training(cfg) + # Set random seed from configs. + np.random.seed(cfg.RNG_SEED) + torch.manual_seed(cfg.RNG_SEED) + + # Setup logging format. + logging.setup_logging(cfg.OUTPUT_DIR) + + # Init multigrid. + multigrid = None + if cfg.MULTIGRID.LONG_CYCLE or cfg.MULTIGRID.SHORT_CYCLE: + multigrid = MultigridSchedule() + cfg = multigrid.init_multigrid(cfg) + if cfg.MULTIGRID.LONG_CYCLE: + cfg, _ = multigrid.update_long_cycle(cfg, cur_epoch=0) + # Print config. + logger.info("Train with config:") + logger.info(pprint.pformat(cfg)) + + # Build the video model and print model statistics. + model = build_model(cfg) + if du.is_master_proc() and cfg.LOG_MODEL_INFO: + misc.log_model_info(model, cfg, use_train_input=True) + + # Construct the optimizer. + optimizer = optim.construct_optimizer(model, cfg) + + # Load a checkpoint to resume training if applicable. + start_epoch = cu.load_train_checkpoint(cfg, model, optimizer) + logger.info(start_epoch) + + # Create the video train and val loaders. + train_loader = loader.construct_loader(cfg, "train") + val_loader = loader.construct_loader(cfg, "val") + precise_bn_loader = ( + loader.construct_loader(cfg, "train", is_precise_bn=True) + if cfg.BN.USE_PRECISE_STATS + else None + ) + + # Create meters. + if cfg.DETECTION.ENABLE: + train_meter = AVAMeter(len(train_loader), cfg, mode="train") + val_meter = AVAMeter(len(val_loader), cfg, mode="val") + else: + train_meter = TrainMeter(len(train_loader), cfg) + val_meter = ValMeter(len(val_loader), cfg) + + # set up writer for logging to Tensorboard format. + if cfg.TENSORBOARD.ENABLE and du.is_master_proc( + cfg.NUM_GPUS * cfg.NUM_SHARDS + ): + writer = tb.TensorboardWriter(cfg) + else: + writer = None + + # Perform the training loop. + logger.info("Start epoch: {}".format(start_epoch + 1)) + + epoch_timer = EpochTimer() + for cur_epoch in range(start_epoch, cfg.SOLVER.MAX_EPOCH): + if cfg.MULTIGRID.LONG_CYCLE: + cfg, changed = multigrid.update_long_cycle(cfg, cur_epoch) + logger.info(cur_epoch) + if changed: + ( + model, + optimizer, + train_loader, + val_loader, + precise_bn_loader, + train_meter, + val_meter, + ) = build_trainer(cfg) + + # Load checkpoint. + if cu.has_checkpoint(cfg.OUTPUT_DIR): + last_checkpoint = cu.get_last_checkpoint(cfg.OUTPUT_DIR) + assert "{:05d}.pyth".format(cur_epoch) in last_checkpoint + else: + last_checkpoint = cfg.TRAIN.CHECKPOINT_FILE_PATH + logger.info("Load from {}".format(last_checkpoint)) + cu.load_checkpoint( + last_checkpoint, model, cfg.NUM_GPUS > 1, optimizer + ) + + # Shuffle the dataset. + loader.shuffle_dataset(train_loader, cur_epoch) + + # Train for one epoch. + epoch_timer.epoch_tic() + train_epoch( + train_loader, model, optimizer, train_meter, cur_epoch, cfg, writer + ) + epoch_timer.epoch_toc() + logger.info( + f"Epoch {cur_epoch} takes {epoch_timer.last_epoch_time():.2f}s. Epochs " + f"from {start_epoch} to {cur_epoch} take " + f"{epoch_timer.avg_epoch_time():.2f}s in average and " + f"{epoch_timer.median_epoch_time():.2f}s in median." + ) + logger.info( + f"For epoch {cur_epoch}, each iteraction takes " + f"{epoch_timer.last_epoch_time()/len(train_loader):.2f}s in average. " + f"From epoch {start_epoch} to {cur_epoch}, each iteraction takes " + f"{epoch_timer.avg_epoch_time()/len(train_loader):.2f}s in average." + ) + + is_checkp_epoch = cu.is_checkpoint_epoch( + cfg, + cur_epoch, + None if multigrid is None else multigrid.schedule, + ) + is_eval_epoch = misc.is_eval_epoch( + cfg, cur_epoch, None if multigrid is None else multigrid.schedule + ) + + # Compute precise BN stats. + if ( + (is_checkp_epoch or is_eval_epoch) + and cfg.BN.USE_PRECISE_STATS + and len(get_bn_modules(model)) > 0 + ): + calculate_and_update_precise_bn( + precise_bn_loader, + model, + min(cfg.BN.NUM_BATCHES_PRECISE, len(precise_bn_loader)), + cfg.NUM_GPUS > 0, + ) + _ = misc.aggregate_sub_bn_stats(model) + + # Save a checkpoint. + if is_checkp_epoch: + cu.save_checkpoint(cfg.OUTPUT_DIR, model, optimizer, cur_epoch, cfg) + # Evaluate the model on validation set. + if is_eval_epoch: + eval_epoch(val_loader, model, val_meter, cur_epoch, cfg, writer) + + if writer is not None: + writer.close() diff --git "a/code/2021_spring/\345\212\250\344\275\234\350\257\206\345\210\253-\345\274\240\346\263\275\347\221\236/visualization.py" "b/code/2021_spring/\345\212\250\344\275\234\350\257\206\345\210\253-\345\274\240\346\263\275\347\221\236/visualization.py" new file mode 100644 index 0000000..905863b --- /dev/null +++ "b/code/2021_spring/\345\212\250\344\275\234\350\257\206\345\210\253-\345\274\240\346\263\275\347\221\236/visualization.py" @@ -0,0 +1,347 @@ +#!/usr/bin/env python3 +# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. + +import numpy as np +import pickle +import torch +import tqdm +from iopath.common.file_io import g_pathmgr + +import slowfast.datasets.utils as data_utils +import slowfast.utils.checkpoint as cu +import slowfast.utils.distributed as du +import slowfast.utils.logging as logging +import slowfast.utils.misc as misc +import slowfast.visualization.tensorboard_vis as tb +from slowfast.datasets import loader +from slowfast.models import build_model +from slowfast.visualization.gradcam_utils import GradCAM +from slowfast.visualization.prediction_vis import WrongPredictionVis +from slowfast.visualization.utils import ( + GetWeightAndActivation, + process_layer_index_data, +) +from slowfast.visualization.video_visualizer import VideoVisualizer + +logger = logging.get_logger(__name__) + + +def run_visualization(vis_loader, model, cfg, writer=None): + """ + Run model visualization (weights, activations and model inputs) and visualize + them on Tensorboard. + Args: + vis_loader (loader): video visualization loader. + model (model): the video model to visualize. + cfg (CfgNode): configs. Details can be found in + slowfast/config/defaults.py + writer (TensorboardWriter, optional): TensorboardWriter object + to writer Tensorboard log. + """ + n_devices = cfg.NUM_GPUS * cfg.NUM_SHARDS + prefix = "module/" if n_devices > 1 else "" + # Get a list of selected layer names and indexing. + layer_ls, indexing_dict = process_layer_index_data( + cfg.TENSORBOARD.MODEL_VIS.LAYER_LIST, layer_name_prefix=prefix + ) + logger.info("Start Model Visualization.") + # Register hooks for activations. + model_vis = GetWeightAndActivation(model, layer_ls) + + if writer is not None and cfg.TENSORBOARD.MODEL_VIS.MODEL_WEIGHTS: + layer_weights = model_vis.get_weights() + writer.plot_weights_and_activations( + layer_weights, tag="Layer Weights/", heat_map=False + ) + + video_vis = VideoVisualizer( + cfg.MODEL.NUM_CLASSES, + cfg.TENSORBOARD.CLASS_NAMES_PATH, + cfg.TENSORBOARD.MODEL_VIS.TOPK_PREDS, + cfg.TENSORBOARD.MODEL_VIS.COLORMAP, + ) + if n_devices > 1: + grad_cam_layer_ls = [ + "module/" + layer + for layer in cfg.TENSORBOARD.MODEL_VIS.GRAD_CAM.LAYER_LIST + ] + else: + grad_cam_layer_ls = cfg.TENSORBOARD.MODEL_VIS.GRAD_CAM.LAYER_LIST + + if cfg.TENSORBOARD.MODEL_VIS.GRAD_CAM.ENABLE: + gradcam = GradCAM( + model, + target_layers=grad_cam_layer_ls, + data_mean=cfg.DATA.MEAN, + data_std=cfg.DATA.STD, + colormap=cfg.TENSORBOARD.MODEL_VIS.GRAD_CAM.COLORMAP, + ) + logger.info("Finish drawing weights.") + global_idx = -1 + for inputs, labels, _, meta in tqdm.tqdm(vis_loader): + if cfg.NUM_GPUS: + # Transfer the data to the current GPU device. + if isinstance(inputs, (list,)): + for i in range(len(inputs)): + inputs[i] = inputs[i].cuda(non_blocking=True) + else: + inputs = inputs.cuda(non_blocking=True) + labels = labels.cuda() + for key, val in meta.items(): + if isinstance(val, (list,)): + for i in range(len(val)): + val[i] = val[i].cuda(non_blocking=True) + else: + meta[key] = val.cuda(non_blocking=True) + + if cfg.DETECTION.ENABLE: + activations, preds = model_vis.get_activations( + inputs, meta["boxes"] + ) + else: + activations, preds = model_vis.get_activations(inputs) + if cfg.TENSORBOARD.MODEL_VIS.GRAD_CAM.ENABLE: + if cfg.TENSORBOARD.MODEL_VIS.GRAD_CAM.USE_TRUE_LABEL: + inputs, preds = gradcam(inputs, labels=labels) + else: + inputs, preds = gradcam(inputs) + if cfg.NUM_GPUS: + inputs = du.all_gather_unaligned(inputs) + activations = du.all_gather_unaligned(activations) + preds = du.all_gather_unaligned(preds) + if isinstance(inputs[0], list): + for i in range(len(inputs)): + for j in range(len(inputs[0])): + inputs[i][j] = inputs[i][j].cpu() + else: + inputs = [inp.cpu() for inp in inputs] + preds = [pred.cpu() for pred in preds] + else: + inputs, activations, preds = [inputs], [activations], [preds] + + boxes = [None] * max(n_devices, 1) + if cfg.DETECTION.ENABLE and cfg.NUM_GPUS: + boxes = du.all_gather_unaligned(meta["boxes"]) + boxes = [box.cpu() for box in boxes] + + if writer is not None: + total_vids = 0 + for i in range(max(n_devices, 1)): + cur_input = inputs[i] + cur_activations = activations[i] + cur_batch_size = cur_input[0].shape[0] + cur_preds = preds[i] + cur_boxes = boxes[i] + for cur_batch_idx in range(cur_batch_size): + global_idx += 1 + total_vids += 1 + if ( + cfg.TENSORBOARD.MODEL_VIS.INPUT_VIDEO + or cfg.TENSORBOARD.MODEL_VIS.GRAD_CAM.ENABLE + ): + for path_idx, input_pathway in enumerate(cur_input): + if cfg.TEST.DATASET == "ava" and cfg.AVA.BGR: + video = input_pathway[ + cur_batch_idx, [2, 1, 0], ... + ] + else: + video = input_pathway[cur_batch_idx] + + if not cfg.TENSORBOARD.MODEL_VIS.GRAD_CAM.ENABLE: + # Permute to (T, H, W, C) from (C, T, H, W). + video = video.permute(1, 2, 3, 0) + video = data_utils.revert_tensor_normalize( + video, cfg.DATA.MEAN, cfg.DATA.STD + ) + else: + # Permute from (T, C, H, W) to (T, H, W, C) + video = video.permute(0, 2, 3, 1) + bboxes = ( + None if cur_boxes is None else cur_boxes[:, 1:] + ) + cur_prediction = ( + cur_preds + if cfg.DETECTION.ENABLE + else cur_preds[cur_batch_idx] + ) + video = video_vis.draw_clip( + video, cur_prediction, bboxes=bboxes + ) + video = ( + torch.from_numpy(np.array(video)) + .permute(0, 3, 1, 2) + .unsqueeze(0) + ) + writer.add_video( + video, + tag="Input {}/Pathway {}".format( + global_idx, path_idx + 1 + ), + ) + if cfg.TENSORBOARD.MODEL_VIS.ACTIVATIONS: + writer.plot_weights_and_activations( + cur_activations, + tag="Input {}/Activations: ".format(global_idx), + batch_idx=cur_batch_idx, + indexing_dict=indexing_dict, + ) + + +def perform_wrong_prediction_vis(vis_loader, model, cfg): + """ + Visualize video inputs with wrong predictions on Tensorboard. + Args: + vis_loader (loader): video visualization loader. + model (model): the video model to visualize. + cfg (CfgNode): configs. Details can be found in + slowfast/config/defaults.py + """ + wrong_prediction_visualizer = WrongPredictionVis(cfg=cfg) + for batch_idx, (inputs, labels, _, _) in tqdm.tqdm(enumerate(vis_loader)): + if cfg.NUM_GPUS: + # Transfer the data to the current GPU device. + if isinstance(inputs, (list,)): + for i in range(len(inputs)): + inputs[i] = inputs[i].cuda(non_blocking=True) + else: + inputs = inputs.cuda(non_blocking=True) + labels = labels.cuda() + + # Some model modify the original input. + inputs_clone = [inp.clone() for inp in inputs] + + preds = model(inputs) + + if cfg.NUM_GPUS > 1: + preds, labels = du.all_gather([preds, labels]) + if isinstance(inputs_clone, (list,)): + inputs_clone = du.all_gather(inputs_clone) + else: + inputs_clone = du.all_gather([inputs_clone])[0] + + if cfg.NUM_GPUS: + # Transfer the data to the current CPU device. + labels = labels.cpu() + preds = preds.cpu() + if isinstance(inputs_clone, (list,)): + for i in range(len(inputs_clone)): + inputs_clone[i] = inputs_clone[i].cpu() + else: + inputs_clone = inputs_clone.cpu() + + # If using CPU (NUM_GPUS = 0), 1 represent 1 CPU. + n_devices = max(cfg.NUM_GPUS, 1) + for device_idx in range(1, n_devices + 1): + wrong_prediction_visualizer.visualize_vid( + video_input=inputs_clone, + labels=labels, + preds=preds.detach().clone(), + batch_idx=device_idx * batch_idx, + ) + + logger.info( + "Class indices with wrong predictions: {}".format( + sorted(wrong_prediction_visualizer.wrong_class_prediction) + ) + ) + wrong_prediction_visualizer.clean() + + +def visualize(cfg): + """ + Perform layer weights and activations visualization on the model. + Args: + cfg (CfgNode): configs. Details can be found in + slowfast/config/defaults.py + """ + if cfg.TENSORBOARD.ENABLE and ( + cfg.TENSORBOARD.MODEL_VIS.ENABLE + or cfg.TENSORBOARD.WRONG_PRED_VIS.ENABLE + ): + # Set up environment. + du.init_distributed_training(cfg) + # Set random seed from configs. + np.random.seed(cfg.RNG_SEED) + torch.manual_seed(cfg.RNG_SEED) + + # Setup logging format. + logging.setup_logging(cfg.OUTPUT_DIR) + + # Print config. + logger.info("Model Visualization with config:") + logger.info(cfg) + + # Build the video model and print model statistics. + model = build_model(cfg) + model.eval() + if du.is_master_proc() and cfg.LOG_MODEL_INFO: + misc.log_model_info(model, cfg, use_train_input=False) + + cu.load_test_checkpoint(cfg, model) + + # Create video testing loaders. + vis_loader = loader.construct_loader(cfg, "test") + + if cfg.DETECTION.ENABLE: + assert cfg.NUM_GPUS == cfg.TEST.BATCH_SIZE or cfg.NUM_GPUS == 0 + + # Set up writer for logging to Tensorboard format. + if du.is_master_proc(cfg.NUM_GPUS * cfg.NUM_SHARDS): + writer = tb.TensorboardWriter(cfg) + else: + writer = None + if cfg.TENSORBOARD.PREDICTIONS_PATH != "": + assert not cfg.DETECTION.ENABLE, "Detection is not supported." + logger.info( + "Visualizing class-level performance from saved results..." + ) + if writer is not None: + with g_pathmgr.open( + cfg.TENSORBOARD.PREDICTIONS_PATH, "rb" + ) as f: + preds, labels = pickle.load(f, encoding="latin1") + + writer.plot_eval(preds, labels) + + if cfg.TENSORBOARD.MODEL_VIS.ENABLE: + if cfg.TENSORBOARD.MODEL_VIS.GRAD_CAM.ENABLE: + assert ( + not cfg.DETECTION.ENABLE + ), "Detection task is currently not supported for Grad-CAM visualization." + if cfg.MODEL.ARCH in cfg.MODEL.SINGLE_PATHWAY_ARCH: + assert ( + len(cfg.TENSORBOARD.MODEL_VIS.GRAD_CAM.LAYER_LIST) == 1 + ), "The number of chosen CNN layers must be equal to the number of pathway(s), given {} layer(s).".format( + len(cfg.TENSORBOARD.MODEL_VIS.GRAD_CAM.LAYER_LIST) + ) + elif cfg.MODEL.ARCH in cfg.MODEL.MULTI_PATHWAY_ARCH: + assert ( + len(cfg.TENSORBOARD.MODEL_VIS.GRAD_CAM.LAYER_LIST) == 2 + ), "The number of chosen CNN layers must be equal to the number of pathway(s), given {} layer(s).".format( + len(cfg.TENSORBOARD.MODEL_VIS.GRAD_CAM.LAYER_LIST) + ) + else: + raise NotImplementedError( + "Model arch {} is not in {}".format( + cfg.MODEL.ARCH, + cfg.MODEL.SINGLE_PATHWAY_ARCH + + cfg.MODEL.MULTI_PATHWAY_ARCH, + ) + ) + logger.info( + "Visualize model analysis for {} iterations".format( + len(vis_loader) + ) + ) + # Run visualization on the model + run_visualization(vis_loader, model, cfg, writer) + if cfg.TENSORBOARD.WRONG_PRED_VIS.ENABLE: + logger.info( + "Visualize Wrong Predictions for {} iterations".format( + len(vis_loader) + ) + ) + perform_wrong_prediction_vis(vis_loader, model, cfg) + + if writer is not None: + writer.close() -- Gitee