Skip to content

Commit e16977e

Browse files
authored
Add files via upload
1 parent 253ec3b commit e16977e

11 files changed

+1863
-0
lines changed

tools/README.md

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
This directory contains few tools for MaskFormer.
2+
3+
* `convert-torchvision-to-d2.py`
4+
5+
Tool to convert torchvision pre-trained weights for D2.
6+
7+
```
8+
wget https://download.pytorch.org/models/resnet101-63fe2227.pth
9+
python tools/convert-torchvision-to-d2.py resnet101-63fe2227.pth R-101.pkl
10+
```
11+
12+
* `convert-pretrained-swin-model-to-d2.py`
13+
14+
Tool to convert Swin Transformer pre-trained weights for D2.
15+
16+
```
17+
pip install timm
18+
19+
wget https://github.com/SwinTransformer/storage/releases/download/v1.0.0/swin_tiny_patch4_window7_224.pth
20+
python tools/convert-pretrained-swin-model-to-d2.py swin_tiny_patch4_window7_224.pth swin_tiny_patch4_window7_224.pkl
21+
22+
wget https://github.com/SwinTransformer/storage/releases/download/v1.0.0/swin_small_patch4_window7_224.pth
23+
python tools/convert-pretrained-swin-model-to-d2.py swin_small_patch4_window7_224.pth swin_small_patch4_window7_224.pkl
24+
25+
wget https://github.com/SwinTransformer/storage/releases/download/v1.0.0/swin_base_patch4_window12_384_22k.pth
26+
python tools/convert-pretrained-swin-model-to-d2.py swin_base_patch4_window12_384_22k.pth swin_base_patch4_window12_384_22k.pkl
27+
28+
wget https://github.com/SwinTransformer/storage/releases/download/v1.0.0/swin_large_patch4_window12_384_22k.pth
29+
python tools/convert-pretrained-swin-model-to-d2.py swin_large_patch4_window12_384_22k.pth swin_large_patch4_window12_384_22k.pkl
30+
```
31+
32+
* `evaluate_pq_for_semantic_segmentation.py`
33+
34+
Tool to evaluate PQ (PQ-stuff) for semantic segmentation predictions.
35+
36+
Usage:
37+
38+
```
39+
python tools/evaluate_pq_for_semantic_segmentation.py --dataset-name ade20k_sem_seg_val --json-file OUTPUT_DIR/inference/sem_seg_predictions.json
40+
```
41+
42+
where `OUTPUT_DIR` is set in the config file.
43+
44+
* `evaluate_coco_boundary_ap.py`
45+
46+
Tool to evaluate Boundary AP for instance segmentation predictions.
47+
48+
Usage:
49+
50+
```
51+
python tools/coco_instance_evaluation.py --gt-json-file COCO_GT_JSON --dt-json-file COCO_DT_JSON
52+
```
53+
54+
To install Boundary IoU API, run:
55+
56+
```
57+
pip install git+https://github.com/bowenc0221/boundary-iou-api.git
58+
```
59+
60+
* `analyze_model.py`
61+
62+
Tool to analyze model parameters and flops.
63+
64+
Usage for semantic segmentation (ADE20K only, use with caution!):
65+
66+
```
67+
python tools/analyze_model.py --num-inputs 1 --tasks flop --use-fixed-input-size --config-file CONFIG_FILE
68+
```
69+
70+
Note that, for semantic segmentation (ADE20K only), we use a dummy image with fixed size that equals to `cfg.INPUT.CROP.SIZE[0] x cfg.INPUT.CROP.SIZE[0]`.
71+
Please do not use `--use-fixed-input-size` for calculating FLOPs on other datasets like Cityscapes!
72+
73+
Usage for panoptic and instance segmentation:
74+
75+
```
76+
python tools/analyze_model.py --num-inputs 100 --tasks flop --config-file CONFIG_FILE
77+
```
78+
79+
Note that, for panoptic and instance segmentation, we compute the average flops over 100 real validation images.

tools/analyze_model.py

Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
# -*- coding: utf-8 -*-
2+
# Copyright (c) Facebook, Inc. and its affiliates.
3+
# Modified by Bowen Cheng from https://github.com/facebookresearch/detectron2/blob/main/tools/analyze_model.py
4+
5+
import logging
6+
import numpy as np
7+
from collections import Counter
8+
import tqdm
9+
from fvcore.nn import flop_count_table # can also try flop_count_str
10+
11+
from detectron2.checkpoint import DetectionCheckpointer
12+
from detectron2.config import CfgNode, LazyConfig, get_cfg, instantiate
13+
from detectron2.data import build_detection_test_loader
14+
from detectron2.engine import default_argument_parser
15+
from detectron2.modeling import build_model
16+
from detectron2.projects.deeplab import add_deeplab_config
17+
from detectron2.utils.analysis import (
18+
FlopCountAnalysis,
19+
activation_count_operators,
20+
parameter_count_table,
21+
)
22+
from detectron2.utils.logger import setup_logger
23+
24+
# fmt: off
25+
import os
26+
import sys
27+
sys.path.insert(1, os.path.join(sys.path[0], '..'))
28+
# fmt: on
29+
30+
from mask2former import add_maskformer2_config
31+
32+
logger = logging.getLogger("detectron2")
33+
34+
35+
def setup(args):
36+
if args.config_file.endswith(".yaml"):
37+
cfg = get_cfg()
38+
add_deeplab_config(cfg)
39+
add_maskformer2_config(cfg)
40+
cfg.merge_from_file(args.config_file)
41+
cfg.DATALOADER.NUM_WORKERS = 0
42+
cfg.merge_from_list(args.opts)
43+
cfg.freeze()
44+
else:
45+
cfg = LazyConfig.load(args.config_file)
46+
cfg = LazyConfig.apply_overrides(cfg, args.opts)
47+
setup_logger(name="fvcore")
48+
setup_logger()
49+
return cfg
50+
51+
52+
def do_flop(cfg):
53+
if isinstance(cfg, CfgNode):
54+
data_loader = build_detection_test_loader(cfg, cfg.DATASETS.TEST[0])
55+
model = build_model(cfg)
56+
DetectionCheckpointer(model).load(cfg.MODEL.WEIGHTS)
57+
else:
58+
data_loader = instantiate(cfg.dataloader.test)
59+
model = instantiate(cfg.model)
60+
model.to(cfg.train.device)
61+
DetectionCheckpointer(model).load(cfg.train.init_checkpoint)
62+
model.eval()
63+
64+
counts = Counter()
65+
total_flops = []
66+
for idx, data in zip(tqdm.trange(args.num_inputs), data_loader): # noqa
67+
if args.use_fixed_input_size and isinstance(cfg, CfgNode):
68+
import torch
69+
crop_size = cfg.INPUT.CROP.SIZE[0]
70+
data[0]["image"] = torch.zeros((3, crop_size, crop_size))
71+
flops = FlopCountAnalysis(model, data)
72+
if idx > 0:
73+
flops.unsupported_ops_warnings(False).uncalled_modules_warnings(False)
74+
counts += flops.by_operator()
75+
total_flops.append(flops.total())
76+
77+
logger.info("Flops table computed from only one input sample:\n" + flop_count_table(flops))
78+
logger.info(
79+
"Average GFlops for each type of operators:\n"
80+
+ str([(k, v / (idx + 1) / 1e9) for k, v in counts.items()])
81+
)
82+
logger.info(
83+
"Total GFlops: {:.1f}±{:.1f}".format(np.mean(total_flops) / 1e9, np.std(total_flops) / 1e9)
84+
)
85+
86+
87+
def do_activation(cfg):
88+
if isinstance(cfg, CfgNode):
89+
data_loader = build_detection_test_loader(cfg, cfg.DATASETS.TEST[0])
90+
model = build_model(cfg)
91+
DetectionCheckpointer(model).load(cfg.MODEL.WEIGHTS)
92+
else:
93+
data_loader = instantiate(cfg.dataloader.test)
94+
model = instantiate(cfg.model)
95+
model.to(cfg.train.device)
96+
DetectionCheckpointer(model).load(cfg.train.init_checkpoint)
97+
model.eval()
98+
99+
counts = Counter()
100+
total_activations = []
101+
for idx, data in zip(tqdm.trange(args.num_inputs), data_loader): # noqa
102+
count = activation_count_operators(model, data)
103+
counts += count
104+
total_activations.append(sum(count.values()))
105+
logger.info(
106+
"(Million) Activations for Each Type of Operators:\n"
107+
+ str([(k, v / idx) for k, v in counts.items()])
108+
)
109+
logger.info(
110+
"Total (Million) Activations: {}±{}".format(
111+
np.mean(total_activations), np.std(total_activations)
112+
)
113+
)
114+
115+
116+
def do_parameter(cfg):
117+
if isinstance(cfg, CfgNode):
118+
model = build_model(cfg)
119+
else:
120+
model = instantiate(cfg.model)
121+
logger.info("Parameter Count:\n" + parameter_count_table(model, max_depth=5))
122+
123+
124+
def do_structure(cfg):
125+
if isinstance(cfg, CfgNode):
126+
model = build_model(cfg)
127+
else:
128+
model = instantiate(cfg.model)
129+
logger.info("Model Structure:\n" + str(model))
130+
131+
132+
if __name__ == "__main__":
133+
parser = default_argument_parser(
134+
epilog="""
135+
Examples:
136+
To show parameters of a model:
137+
$ ./analyze_model.py --tasks parameter \\
138+
--config-file ../configs/COCO-InstanceSegmentation/mask_rcnn_R_50_FPN_1x.yaml
139+
Flops and activations are data-dependent, therefore inputs and model weights
140+
are needed to count them:
141+
$ ./analyze_model.py --num-inputs 100 --tasks flop \\
142+
--config-file ../configs/COCO-InstanceSegmentation/mask_rcnn_R_50_FPN_1x.yaml \\
143+
MODEL.WEIGHTS /path/to/model.pkl
144+
"""
145+
)
146+
parser.add_argument(
147+
"--tasks",
148+
choices=["flop", "activation", "parameter", "structure"],
149+
required=True,
150+
nargs="+",
151+
)
152+
parser.add_argument(
153+
"-n",
154+
"--num-inputs",
155+
default=100,
156+
type=int,
157+
help="number of inputs used to compute statistics for flops/activations, "
158+
"both are data dependent.",
159+
)
160+
parser.add_argument(
161+
"--use-fixed-input-size",
162+
action="store_true",
163+
help="use fixed input size when calculating flops",
164+
)
165+
args = parser.parse_args()
166+
assert not args.eval_only
167+
assert args.num_gpus == 1
168+
169+
cfg = setup(args)
170+
171+
for task in args.tasks:
172+
{
173+
"flop": do_flop,
174+
"activation": do_activation,
175+
"parameter": do_parameter,
176+
"structure": do_structure,
177+
}[task](cfg)
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
#!/usr/bin/env python
2+
# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
3+
4+
import pickle as pkl
5+
import sys
6+
7+
import torch
8+
9+
"""
10+
Usage:
11+
# download pretrained swin model:
12+
wget https://github.com/SwinTransformer/storage/releases/download/v1.0.0/swin_tiny_patch4_window7_224.pth
13+
# run the conversion
14+
./convert-pretrained-model-to-d2.py swin_tiny_patch4_window7_224.pth swin_tiny_patch4_window7_224.pkl
15+
# Then, use swin_tiny_patch4_window7_224.pkl with the following changes in config:
16+
MODEL:
17+
WEIGHTS: "/path/to/swin_tiny_patch4_window7_224.pkl"
18+
INPUT:
19+
FORMAT: "RGB"
20+
"""
21+
22+
if __name__ == "__main__":
23+
input = sys.argv[1]
24+
25+
obj = torch.load(input, map_location="cpu")["model"]
26+
27+
res = {"model": obj, "__author__": "third_party", "matching_heuristics": True}
28+
29+
with open(sys.argv[2], "wb") as f:
30+
pkl.dump(res, f)

tools/convert-torchvision-to-d2.py

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
#!/usr/bin/env python
2+
# Copyright (c) Facebook, Inc. and its affiliates.
3+
4+
import pickle as pkl
5+
import sys
6+
7+
import torch
8+
9+
"""
10+
Usage:
11+
# download one of the ResNet{18,34,50,101,152} models from torchvision:
12+
wget https://download.pytorch.org/models/resnet50-19c8e357.pth -O r50.pth
13+
# run the conversion
14+
./convert-torchvision-to-d2.py r50.pth r50.pkl
15+
# Then, use r50.pkl with the following changes in config:
16+
MODEL:
17+
WEIGHTS: "/path/to/r50.pkl"
18+
PIXEL_MEAN: [123.675, 116.280, 103.530]
19+
PIXEL_STD: [58.395, 57.120, 57.375]
20+
RESNETS:
21+
DEPTH: 50
22+
STRIDE_IN_1X1: False
23+
INPUT:
24+
FORMAT: "RGB"
25+
"""
26+
27+
if __name__ == "__main__":
28+
input = sys.argv[1]
29+
30+
obj = torch.load(input, map_location="cpu")
31+
32+
newmodel = {}
33+
for k in list(obj.keys()):
34+
old_k = k
35+
if "layer" not in k:
36+
k = "stem." + k
37+
for t in [1, 2, 3, 4]:
38+
k = k.replace("layer{}".format(t), "res{}".format(t + 1))
39+
for t in [1, 2, 3]:
40+
k = k.replace("bn{}".format(t), "conv{}.norm".format(t))
41+
k = k.replace("downsample.0", "shortcut")
42+
k = k.replace("downsample.1", "shortcut.norm")
43+
print(old_k, "->", k)
44+
newmodel[k] = obj.pop(old_k).detach().numpy()
45+
46+
res = {"model": newmodel, "__author__": "torchvision", "matching_heuristics": True}
47+
48+
with open(sys.argv[2], "wb") as f:
49+
pkl.dump(res, f)
50+
if obj:
51+
print("Unconverted keys:", obj.keys())

tools/evaluate_coco_boundary_ap.py

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
#!/usr/bin/env python
2+
# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
3+
# Modified by Bowen Cheng from: https://github.com/bowenc0221/boundary-iou-api/blob/master/tools/coco_instance_evaluation.py
4+
5+
"""
6+
Evaluation for COCO val2017:
7+
python ./tools/coco_instance_evaluation.py \
8+
--gt-json-file COCO_GT_JSON \
9+
--dt-json-file COCO_DT_JSON
10+
"""
11+
import argparse
12+
import json
13+
14+
from boundary_iou.coco_instance_api.coco import COCO
15+
from boundary_iou.coco_instance_api.cocoeval import COCOeval
16+
17+
18+
def main():
19+
parser = argparse.ArgumentParser()
20+
parser.add_argument("--gt-json-file", default="")
21+
parser.add_argument("--dt-json-file", default="")
22+
parser.add_argument("--iou-type", default="boundary")
23+
parser.add_argument("--dilation-ratio", default="0.020", type=float)
24+
args = parser.parse_args()
25+
print(args)
26+
27+
annFile = args.gt_json_file
28+
resFile = args.dt_json_file
29+
dilation_ratio = args.dilation_ratio
30+
if args.iou_type == "boundary":
31+
get_boundary = True
32+
else:
33+
get_boundary = False
34+
cocoGt = COCO(annFile, get_boundary=get_boundary, dilation_ratio=dilation_ratio)
35+
36+
# remove box predictions
37+
resFile = json.load(open(resFile))
38+
for c in resFile:
39+
c.pop("bbox", None)
40+
41+
cocoDt = cocoGt.loadRes(resFile)
42+
cocoEval = COCOeval(cocoGt, cocoDt, iouType=args.iou_type, dilation_ratio=dilation_ratio)
43+
cocoEval.evaluate()
44+
cocoEval.accumulate()
45+
cocoEval.summarize()
46+
47+
48+
if __name__ == '__main__':
49+
main()

0 commit comments

Comments
 (0)