# -*- coding: utf-8 -*- # Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved import logging import numpy as np from collections import Counter import tqdm from detectron2.checkpoint import DetectionCheckpointer from detectron2.config import get_cfg from detectron2.data import build_detection_test_loader from detectron2.engine import default_argument_parser from detectron2.modeling import build_model from detectron2.utils.analysis import ( activation_count_operators, flop_count_operators, parameter_count_table, ) from detectron2.utils.logger import setup_logger logger = logging.getLogger("detectron2") def setup(args): cfg = get_cfg() cfg.merge_from_file(args.config_file) cfg.DATALOADER.NUM_WORKERS = 0 cfg.merge_from_list(args.opts) cfg.freeze() setup_logger() return cfg def do_flop(cfg): data_loader = build_detection_test_loader(cfg, cfg.DATASETS.TEST[0]) model = build_model(cfg) DetectionCheckpointer(model).load(cfg.MODEL.WEIGHTS) model.eval() counts = Counter() total_flops = [] for idx, data in zip(tqdm.trange(args.num_inputs), data_loader): # noqa count = flop_count_operators(model, data) counts += count total_flops.append(sum(count.values())) logger.info( "(G)Flops for Each Type of Operators:\n" + str([(k, v / idx) for k, v in counts.items()]) ) logger.info("Total (G)Flops: {}±{}".format(np.mean(total_flops), np.std(total_flops))) def do_activation(cfg): data_loader = build_detection_test_loader(cfg, cfg.DATASETS.TEST[0]) model = build_model(cfg) DetectionCheckpointer(model).load(cfg.MODEL.WEIGHTS) model.eval() counts = Counter() total_activations = [] for idx, data in zip(tqdm.trange(args.num_inputs), data_loader): # noqa count = activation_count_operators(model, data) counts += count total_activations.append(sum(count.values())) logger.info( "(Million) Activations for Each Type of Operators:\n" + str([(k, v / idx) for k, v in counts.items()]) ) logger.info( "Total (Million) Activations: {}±{}".format( np.mean(total_activations), np.std(total_activations) ) ) def do_parameter(cfg): model = build_model(cfg) logger.info("Parameter Count:\n" + parameter_count_table(model, max_depth=5)) def do_structure(cfg): model = build_model(cfg) logger.info("Model Structure:\n" + str(model)) if __name__ == "__main__": parser = default_argument_parser( epilog=""" Examples: To show parameters of a model: $ ./analyze_model.py --tasks parameter \\ --config-file ../configs/COCO-InstanceSegmentation/mask_rcnn_R_50_FPN_1x.yaml Flops and activations are data-dependent, therefore inputs and model weights are needed to count them: $ ./analyze_model.py --num-inputs 100 --tasks flop \\ --config-file ../configs/COCO-InstanceSegmentation/mask_rcnn_R_50_FPN_1x.yaml \\ MODEL.WEIGHTS /path/to/model.pkl """ ) parser.add_argument( "--tasks", choices=["flop", "activation", "parameter", "structure"], required=True, nargs="+", ) parser.add_argument( "--num-inputs", default=100, type=int, help="number of inputs used to compute statistics for flops/activations, " "both are data dependent.", ) args = parser.parse_args() assert not args.eval_only assert args.num_gpus == 1 cfg = setup(args) for task in args.tasks: { "flop": do_flop, "activation": do_activation, "parameter": do_parameter, "structure": do_structure, }[task](cfg)