HugoHE commited on
Commit
1215771
1 Parent(s): 504b328

initial commit

Browse files
.gitignore ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ .DS_Store
2
+ *.pyc
README.md CHANGED
@@ -1,13 +1,56 @@
1
- ---
2
- title: MonitoringInterface
3
- emoji: 🐢
4
- colorFrom: yellow
5
- colorTo: red
6
- sdk: gradio
7
- sdk_version: 4.1.1
8
- app_file: app.py
9
- pinned: false
10
- license: mit
11
- ---
12
-
13
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # monitoring-interface
2
+
3
+ ## Requirements
4
+ ```
5
+ pip install -r requiremnets.txt
6
+ ```
7
+ To install Detectron2, please follow [here](https://detectron2.readthedocs.io/tutorials/install.html).
8
+ ## Dataset Preparation
9
+ We use [Fiftyone](https://docs.voxel51.com) library to load and visualize datasets.
10
+
11
+ BDD100k, COCO, KITTI and OpenImage can be loaded directly through [Fiftyone Datasets Zoo](https://docs.voxel51.com/user_guide/dataset_zoo/datasets.html?highlight=zoo).
12
+
13
+ For other datasets, such as NuScene can be loaded manually via the following simple pattern:
14
+ ```python
15
+ import fiftyone as fo
16
+
17
+ # A name for the dataset
18
+ name = "my-dataset"
19
+
20
+ # The directory containing the dataset to import
21
+ dataset_dir = "/path/to/dataset"
22
+
23
+ # The type of the dataset being imported
24
+ dataset_type = fo.types.COCODetectionDataset # for example
25
+
26
+ dataset = fo.Dataset.from_dir(
27
+ dataset_dir=dataset_dir,
28
+ dataset_type=dataset_type,
29
+ name=name,
30
+ )
31
+ ```
32
+ The custom dataset folder should have the following structure:
33
+ ```
34
+ └── /path/to/dataset
35
+ |
36
+ ├── Data
37
+ └── labels.json
38
+ ```
39
+ Notice that the annotation file `labels.json` should be prepared in COCO format.
40
+
41
+ ## Interface demo
42
+
43
+ Three interfaces are provided:
44
+
45
+ - `interface.py`: all-in-1 interface
46
+ - `interface_tabbed.py`: tabbed interface
47
+ - `enlarge.py`: interface for monitor interval enlargement
48
+
49
+ To run any of these interfaces, just execute `python <script name.py>`.
50
+
51
+ Please note that feature extraction for both training data and evaluation data can be a time-consuming process. However, if you are only interested in testing monitor construction, monitor evaluation, or monitoring demo, you can use the following settings to load a pretrained model along with the corresponding extracted features and monitors.
52
+
53
+ | ID | Backbone | Clustering method for Monitors |
54
+ | ----- | -------- | -------------------------------- |
55
+ | KITTI | ResNet | KMeans(nb_clusters=[1, 4, 5, 6]) |
56
+
abstractions/Box.py ADDED
@@ -0,0 +1,104 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from copy import deepcopy
2
+
3
+ class Box:
4
+ def __init__(self):
5
+ self.dimensions = None
6
+ self.ivals = []
7
+ self.element_indexes = [] # record this box is built for what samples
8
+ self.low_bound_indexes = dict() # record which samples visit the low bound for each dimension
9
+ self.high_bound_indexes = dict() # record which samples visit the low bound for each dimension
10
+
11
+ def build(self, dimensions, points):
12
+ # a point is a tuple (index, n-dim numpy)
13
+ # index = point[0]
14
+ # value = point[1]
15
+ piter = iter(points)
16
+ self.dimensions = dimensions
17
+ self.ivals = []
18
+ self.element_indexes = []
19
+ self.low_bound_indexes = dict()
20
+ self.high_bound_indexes = dict()
21
+
22
+ try:
23
+ point = next(piter)
24
+ except StopIteration:
25
+ return
26
+ else:
27
+ self.element_indexes.append(point[0]) # update index list
28
+ i = 0
29
+ for coord in point[1]:
30
+ if(i >= self.dimensions):
31
+ break
32
+ self.ivals.append([coord, coord])
33
+ self.low_bound_indexes["n"+str(i+1)] = [point[0]] # update low bound visiting index list
34
+ self.high_bound_indexes["n"+str(i+1)] = [point[0]] # update upper bound visiting index list
35
+ i += 1
36
+ if(len(self.ivals) != self.dimensions):
37
+ raise "IllegalArgument"
38
+
39
+ while True:
40
+ try:
41
+ point = next(piter)
42
+ except StopIteration:
43
+ break
44
+ else:
45
+ self.element_indexes.append(point[0]) # update index list
46
+ i = 0
47
+ for coord in point[1]:
48
+ if(i >= self.dimensions):
49
+ break
50
+ ival = self.ivals[i]
51
+ if(coord < ival[0]):
52
+ ival[0] = coord
53
+ self.low_bound_indexes["n"+str(i+1)] = [point[0]] # update the bound and its index
54
+ elif(coord == ival[0]):
55
+ low_index_list = self.low_bound_indexes["n"+str(i+1)]
56
+ low_index_list.append(point[0])
57
+
58
+ if(coord > ival[1]):
59
+ ival[1] = coord
60
+ self.high_bound_indexes["n"+str(i+1)] = [point[0]] # update the bound and its index
61
+ elif(coord == ival[1]):
62
+ high_index_list = self.high_bound_indexes["n"+str(i+1)]
63
+ high_index_list.append(point[0])
64
+ i += 1
65
+
66
+ def query(self, point):
67
+ i = 0
68
+ for coord in point:
69
+ if(i >= self.dimensions):
70
+ break
71
+ ival = self.ivals[i]
72
+ if(coord < ival[0] or coord > ival[1]):
73
+ return False
74
+ i += 1
75
+ return True
76
+
77
+ def __str__(self):
78
+ return self.ivals.__str__()
79
+
80
+ def query_delta(self, point, delta):
81
+ i = 0
82
+ for coord in point:
83
+ if(i >= self.dimensions):
84
+ break
85
+ ival = self.ivals[i]
86
+ if(coord < ival[0]*(1+delta) or coord > ival[1]*(1+delta)):
87
+ return False
88
+ i += 1
89
+ return True
90
+
91
+
92
+ def boxes_query(point, boxes):
93
+ for box in boxes:
94
+ if len(box.ivals):
95
+ if box.query(point):
96
+ return True
97
+ return False
98
+
99
+ def boxes_query_delta(point, boxes, delta):
100
+ for box in boxes:
101
+ if len(box.ivals):
102
+ if box.query_delta(point, delta):
103
+ return True
104
+ return False
abstractions/__init__.py ADDED
@@ -0,0 +1 @@
 
 
1
+ from .Box import *
base_cam.py ADDED
@@ -0,0 +1,223 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import numpy as np
2
+ import torch
3
+ import ttach as tta
4
+ from typing import Callable, List, Tuple
5
+ from pytorch_grad_cam.activations_and_gradients import ActivationsAndGradients
6
+ from pytorch_grad_cam.utils.svd_on_activations import get_2d_projection
7
+ from pytorch_grad_cam.utils.image import scale_cam_image
8
+ from pytorch_grad_cam.utils.model_targets import ClassifierOutputTarget
9
+ from pytorch_grad_cam.utils.svd_on_activations import get_2d_projection
10
+
11
+ # https://arxiv.org/abs/2008.00299
12
+
13
+ class BaseCAM:
14
+ def __init__(self,
15
+ model: torch.nn.Module,
16
+ target_layers: List[torch.nn.Module],
17
+ use_cuda: bool = False,
18
+ reshape_transform: Callable = None,
19
+ compute_input_gradient: bool = False,
20
+ uses_gradients: bool = True) -> None:
21
+ self.model = model.eval()
22
+ self.target_layers = target_layers
23
+ self.cuda = use_cuda
24
+ if self.cuda:
25
+ self.model = model.cuda()
26
+ self.reshape_transform = reshape_transform
27
+ self.compute_input_gradient = compute_input_gradient
28
+ self.uses_gradients = uses_gradients
29
+ self.activations_and_grads = ActivationsAndGradients(
30
+ self.model, target_layers, reshape_transform)
31
+
32
+ """ Get a vector of weights for every channel in the target layer.
33
+ Methods that return weights channels,
34
+ will typically need to only implement this function. """
35
+
36
+ def get_cam_weights(self,
37
+ input_tensor: torch.Tensor,
38
+ target_layers: List[torch.nn.Module],
39
+ targets: List[torch.nn.Module],
40
+ activations: torch.Tensor,
41
+ grads: torch.Tensor) -> np.ndarray:
42
+ raise Exception("Not Implemented")
43
+
44
+ def get_cam_image(self,
45
+ input_tensor: torch.Tensor,
46
+ target_layer: torch.nn.Module,
47
+ targets: List[torch.nn.Module],
48
+ activations: torch.Tensor,
49
+ grads: torch.Tensor,
50
+ eigen_smooth: bool = False) -> np.ndarray:
51
+
52
+ weights = self.get_cam_weights(input_tensor,
53
+ target_layer,
54
+ targets,
55
+ activations,
56
+ grads)
57
+ weighted_activations = weights[:, :, None, None] * activations
58
+ if eigen_smooth:
59
+ cam = get_2d_projection(weighted_activations)
60
+ else:
61
+ cam = weighted_activations.sum(axis=1)
62
+ return cam
63
+
64
+ def forward(self,
65
+ input_tensor: torch.Tensor,
66
+ targets: List[torch.nn.Module],
67
+ eigen_smooth: bool = False) -> np.ndarray:
68
+ if self.cuda:
69
+ input_tensor = input_tensor.cuda()
70
+
71
+ if self.compute_input_gradient:
72
+ input_tensor = torch.autograd.Variable(input_tensor,
73
+ requires_grad=True)
74
+
75
+ outputs = self.activations_and_grads(input_tensor)
76
+ if targets is None:
77
+ target_categories = np.argmax(outputs.cpu().data.numpy(), axis=-1)
78
+ targets = [ClassifierOutputTarget(
79
+ category) for category in target_categories]
80
+
81
+ if self.uses_gradients:
82
+ self.model.zero_grad()
83
+ loss = sum([target(output)
84
+ for target, output in zip(targets, outputs)])
85
+ loss.backward(retain_graph=True)
86
+
87
+ # In most of the saliency attribution papers, the saliency is
88
+ # computed with a single target layer.
89
+ # Commonly it is the last convolutional layer.
90
+ # Here we support passing a list with multiple target layers.
91
+ # It will compute the saliency image for every image,
92
+ # and then aggregate them (with a default mean aggregation).
93
+ # This gives you more flexibility in case you just want to
94
+ # use all conv layers for example, all Batchnorm layers,
95
+ # or something else.
96
+ cam_per_layer = self.compute_cam_per_layer(input_tensor,
97
+ targets,
98
+ eigen_smooth)
99
+ return self.aggregate_multi_layers(cam_per_layer)
100
+
101
+ def get_target_width_height(self,
102
+ input_tensor: torch.Tensor) -> Tuple[int, int]:
103
+ width, height = input_tensor.size(-1), input_tensor.size(-2)
104
+ return width, height
105
+
106
+ def compute_cam_per_layer(
107
+ self,
108
+ input_tensor: torch.Tensor,
109
+ targets: List[torch.nn.Module],
110
+ eigen_smooth: bool) -> np.ndarray:
111
+ activations_list = [a.cpu().data.numpy()
112
+ for a in self.activations_and_grads.activations]
113
+ grads_list = [g.cpu().data.numpy()
114
+ for g in self.activations_and_grads.gradients]
115
+ target_size = self.get_target_width_height(input_tensor[0]["image"])
116
+
117
+
118
+ cam_per_target_layer = []
119
+ # Loop over the saliency image from every layer
120
+ for i in range(len(self.target_layers)):
121
+ target_layer = self.target_layers[i]
122
+ layer_activations = None
123
+ layer_grads = None
124
+ if i < len(activations_list):
125
+ layer_activations = activations_list[i]
126
+ if i < len(grads_list):
127
+ layer_grads = grads_list[i]
128
+
129
+ cam = self.get_cam_image(input_tensor,
130
+ target_layer,
131
+ targets,
132
+ layer_activations,
133
+ layer_grads,
134
+ eigen_smooth)
135
+ cam = np.maximum(cam, 0)
136
+ scaled = scale_cam_image(cam, target_size)
137
+ cam_per_target_layer.append(scaled[:, None, :])
138
+
139
+ return cam_per_target_layer
140
+
141
+ def aggregate_multi_layers(
142
+ self,
143
+ cam_per_target_layer: np.ndarray) -> np.ndarray:
144
+ cam_per_target_layer = np.concatenate(cam_per_target_layer, axis=1)
145
+ cam_per_target_layer = np.maximum(cam_per_target_layer, 0)
146
+ result = np.mean(cam_per_target_layer, axis=1)
147
+ return scale_cam_image(result)
148
+
149
+ def forward_augmentation_smoothing(self,
150
+ input_tensor: torch.Tensor,
151
+ targets: List[torch.nn.Module],
152
+ eigen_smooth: bool = False) -> np.ndarray:
153
+ transforms = tta.Compose(
154
+ [
155
+ tta.HorizontalFlip(),
156
+ tta.Multiply(factors=[0.9, 1, 1.1]),
157
+ ]
158
+ )
159
+ cams = []
160
+ for transform in transforms:
161
+ augmented_tensor = transform.augment_image(input_tensor)
162
+ cam = self.forward(augmented_tensor,
163
+ targets,
164
+ eigen_smooth)
165
+
166
+ # The ttach library expects a tensor of size BxCxHxW
167
+ cam = cam[:, None, :, :]
168
+ cam = torch.from_numpy(cam)
169
+ cam = transform.deaugment_mask(cam)
170
+
171
+ # Back to numpy float32, HxW
172
+ cam = cam.numpy()
173
+ cam = cam[:, 0, :, :]
174
+ cams.append(cam)
175
+
176
+ cam = np.mean(np.float32(cams), axis=0)
177
+ return cam
178
+
179
+ def __call__(self,
180
+ input_tensor: torch.Tensor,
181
+ targets: List[torch.nn.Module] = None,
182
+ aug_smooth: bool = False,
183
+ eigen_smooth: bool = False) -> np.ndarray:
184
+
185
+ # Smooth the CAM result with test time augmentation
186
+ if aug_smooth is True:
187
+ return self.forward_augmentation_smoothing(
188
+ input_tensor, targets, eigen_smooth)
189
+
190
+ return self.forward(input_tensor,
191
+ targets, eigen_smooth)
192
+
193
+ def __del__(self):
194
+ self.activations_and_grads.release()
195
+
196
+ def __enter__(self):
197
+ return self
198
+
199
+ def __exit__(self, exc_type, exc_value, exc_tb):
200
+ self.activations_and_grads.release()
201
+ if isinstance(exc_value, IndexError):
202
+ # Handle IndexError here...
203
+ print(
204
+ f"An exception occurred in CAM with block: {exc_type}. Message: {exc_value}")
205
+ return True
206
+
207
+ class EigenCAM(BaseCAM):
208
+ def __init__(self, model, target_layers, use_cuda=False,
209
+ reshape_transform=None):
210
+ super(EigenCAM, self).__init__(model,
211
+ target_layers,
212
+ use_cuda,
213
+ reshape_transform,
214
+ uses_gradients=False)
215
+
216
+ def get_cam_image(self,
217
+ input_tensor,
218
+ target_layer,
219
+ target_category,
220
+ activations,
221
+ grads,
222
+ eigen_smooth):
223
+ return get_2d_projection(activations)
detectron2monitor.py ADDED
@@ -0,0 +1,231 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ from detectron2.utils.logger import setup_logger
3
+ setup_logger()
4
+
5
+ from detectron2.config import get_cfg
6
+ import detectron2.data.transforms as T
7
+ from detectron2.checkpoint import DetectionCheckpointer
8
+ from detectron2.modeling import build_model
9
+ from detectron2.data.detection_utils import read_image
10
+ from detectron2.utils.visualizer import Visualizer
11
+ from detectron2.data import MetadataCatalog
12
+
13
+
14
+ import numpy as np
15
+ import cv2
16
+ import os
17
+ import time
18
+ import pickle
19
+ import gradio as gr
20
+ import tqdm
21
+ import matplotlib.pyplot as plt
22
+ import io
23
+ from PIL import Image
24
+ torch.manual_seed(0)
25
+ np.random.seed(0)
26
+
27
+ torch.backends.cudnn.deterministic = True
28
+ torch.backends.cudnn.benchmark = False
29
+
30
+ from models.regnet import build_regnet_fpn_backbone
31
+ import models.metadata as metadata
32
+
33
+ from utils_clustering import *
34
+
35
+ from base_cam import EigenCAM
36
+ from pytorch_grad_cam.utils.model_targets import FasterRCNNBoxScoreTarget
37
+
38
+ fullName2ab_dict = {'PASCAL-VOC':"voc", 'BDD100K':"bdd", 'KITTI':"kitti", 'Speed signs':"speed", 'NuScenes':"nu"}
39
+ ab2FullName_dict = {'voc':"PASCAL-VOC", 'bdd':"BDD100K", 'kitti':"KITTI", 'speed':"Speed signs", 'nu':"NuScenes"}
40
+ class Detectron2Monitor():
41
+ def __init__(self, id, backbone, confidence_threshold=0.05):
42
+ self.id, self.label_list = self._get_label_list(id)
43
+ self.backbone = backbone
44
+ self.confidence_threshold = confidence_threshold
45
+ self.cfg, self.device, self.model = self._get_model()
46
+ self.label_dict = {i:label for i, label in enumerate(self.label_list)}
47
+ self.eval_list = ["ID-voc-OOD-coco", "OOD-open", "voc-val"] if self.id == "voc" else ["ID-bdd-OOD-coco", "OOD-open", "voc-ood", f"{self.id}-val"]
48
+ MetadataCatalog.get("custom_dataset").set(thing_classes=self.label_list)
49
+
50
+ def _get_label_list(self, id):
51
+ id = fullName2ab_dict[id]
52
+ if id == 'voc':
53
+ label_list = metadata.VOC_THING_CLASSES
54
+ elif id == 'bdd':
55
+ label_list = metadata.BDD_THING_CLASSES
56
+ elif id == 'kitti':
57
+ label_list = metadata.KITTI_THING_CLASSES
58
+ elif id == 'speed' or id == 'prescan':
59
+ label_list = metadata.SPEED_THING_CLASSES
60
+ else:
61
+ label_list = metadata.NU_THING_CLASSES
62
+ return id, label_list
63
+
64
+ def _get_model(self):
65
+ cfg = get_cfg()
66
+ cfg.merge_from_file(f"models/configs/vanilla_{self.backbone}.yaml")
67
+ cfg.MODEL.WEIGHTS = f"models/weights/model_final_{self.backbone}_{self.id}.pth"
68
+ cfg.MODEL.DEVICE='cpu'
69
+ cfg.MODEL.ROI_HEADS.NUM_CLASSES = len(self.label_list)
70
+ cfg.MODEL.ROI_HEADS.SCORE_THRESH_TEST = self.confidence_threshold
71
+ model = build_model(cfg)
72
+ model.eval()
73
+ checkpointer = DetectionCheckpointer(model)
74
+ checkpointer.load(cfg.MODEL.WEIGHTS)
75
+ device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
76
+ model = model.to(device)
77
+ return cfg, device, model
78
+
79
+ def _inference(self, model, inputs):
80
+ with torch.no_grad():
81
+ images = model.preprocess_image(inputs)
82
+ features = model.backbone(images.tensor)
83
+ proposals, _ = model.proposal_generator(images, features, None) # RPN
84
+
85
+ features_ = [features[f] for f in model.roi_heads.box_in_features]
86
+ box_features = model.roi_heads.box_pooler(features_, [x.proposal_boxes for x in proposals])
87
+ box_features = model.roi_heads.box_head(box_features) # features of all 1k candidates
88
+ predictions = model.roi_heads.box_predictor(box_features)
89
+ pred_instances, pred_inds = model.roi_heads.box_predictor.inference(predictions, proposals)
90
+ pred_instances = model.roi_heads.forward_with_given_boxes(features, pred_instances)
91
+
92
+ # output boxes, masks, scores, etc
93
+ pred_instances = model._postprocess(pred_instances, inputs, images.image_sizes) # scale box to orig size
94
+ # features of the proposed boxes
95
+ feats = box_features[pred_inds].cpu().numpy()
96
+ return pred_instances, feats
97
+
98
+ def _load_monitors(self, clustering_algo, nb_clusters, eps=5, min_samples=10):
99
+ if clustering_algo == "dbscan":
100
+ with open(f"monitors/{self.id}/{self.backbone}/{clustering_algo}/eps{eps}_min_samples{min_samples}.pkl", 'rb') as f:
101
+ monitors_dict = pickle.load(f)
102
+ else:
103
+ with open(f"monitors/{self.id}/{self.backbone}/{clustering_algo}/{nb_clusters}.pkl", 'rb') as f:
104
+ monitors_dict = pickle.load(f)
105
+ return monitors_dict
106
+
107
+ def _evaluate(self, clustering_algo, nb_clusters, eps, min_samples):
108
+ dataset_name = f"{self.id}-val"
109
+ with open(f'val_feats/{self.id}/{self.backbone}/{dataset_name}_feats_tp_dict.pickle', 'rb') as f:
110
+ feats_tp_dict = pickle.load(f)
111
+ with open(f'val_feats/{self.id}/{self.backbone}/{dataset_name}_feats_fp_dict.pickle', 'rb') as f:
112
+ feats_fp_dict = pickle.load(f)
113
+ monitors_dict = self._load_monitors(clustering_algo, nb_clusters, eps, min_samples)
114
+ # make verdicts on ID data
115
+ data_tp = []
116
+ data_fp = []
117
+ accept_sum = {"tp": 0, "fp": 0}
118
+ reject_sum = {"tp": 0, "fp": 0}
119
+ for label in tqdm.tqdm(self.label_list, desc="Evaluation on ID data"):
120
+ if label in monitors_dict:
121
+ verdict = monitors_dict[label].make_verdicts(feats_tp_dict[label])
122
+ data_tp.append([label, len(verdict), np.sum(verdict)/len(verdict)])
123
+ accept_sum["tp"] += np.sum(verdict)
124
+ reject_sum["tp"] += len(verdict) - np.sum(verdict)
125
+ verdict = monitors_dict[label].make_verdicts(feats_fp_dict[label])
126
+ data_fp.append([label, len(verdict), (len(verdict)-np.sum(verdict))/len(verdict)])
127
+ accept_sum["fp"] += np.sum(verdict)
128
+ reject_sum["fp"] += len(verdict) - np.sum(verdict)
129
+ TPR = round((accept_sum['tp'] / (reject_sum['tp'] + accept_sum['tp'])*100), 2)
130
+ FPR = round((accept_sum['fp'] / (reject_sum['fp'] + accept_sum['fp'])*100), 2)
131
+ id_name = ab2FullName_dict[self.id]
132
+ df_id = pd.DataFrame([[id_name, f"{TPR}%", f"{FPR}%"]], columns=["Dataset", "TPR", "FPR"])
133
+
134
+ data_ood = []
135
+ i = 0
136
+ self.eval_list.remove(dataset_name)
137
+ for dataset_name in tqdm.tqdm(self.eval_list, desc="Evaluation on OOD data"):
138
+ accept_sum = {"tp": 0, "fp": 0}
139
+ reject_sum = {"tp": 0, "fp": 0}
140
+ with open(f'val_feats/{self.id}/{self.backbone}/{dataset_name}_feats_fp_dict.pickle', 'rb') as f:
141
+ feats_fp_dict = pickle.load(f)
142
+ for label in self.label_list:
143
+ if label in monitors_dict:
144
+ verdict = monitors_dict[label].make_verdicts(feats_fp_dict[label])
145
+ accept_sum["fp"] += np.sum(verdict)
146
+ reject_sum["fp"] += len(verdict) - np.sum(verdict)
147
+ FPR = round((accept_sum['fp'] / (reject_sum['fp'] + accept_sum['fp'])*100), 2)
148
+ data_ood.append([dataset_name, str(FPR)+"%"])
149
+ i += 1
150
+ # prepare dataframes
151
+ df_ood = pd.DataFrame(data_ood, columns=["Dataset", "FPR"])
152
+ df_ood["Dataset"] = ["COCO", "Open Images"] if self.id == "voc" else ["COCO", "Open Images", "VOC-OOD"]
153
+ return df_id, df_ood
154
+
155
+ def _postprocess_cam(self, raw_cam, img_width, img_height):
156
+ cam_orig = np.sum(raw_cam, axis=0) # [H,W]
157
+ cam_orig = np.maximum(cam_orig, 0) # ReLU
158
+ cam_orig -= np.min(cam_orig)
159
+ cam_orig /= np.max(cam_orig)
160
+ cam = cv2.resize(cam_orig, (img_width, img_height))
161
+ return cam
162
+
163
+ def _fasterrcnn_reshape_transform(self, x):
164
+ target_size = x['p6'].size()[-2 : ]
165
+ activations = []
166
+ for key, value in x.items():
167
+ activations.append(torch.nn.functional.interpolate(torch.abs(value), target_size, mode='bilinear'))
168
+ activations = torch.cat(activations, axis=1)
169
+ return activations
170
+
171
+ def _get_input_dict(self, original_image):
172
+ height, width = original_image.shape[:2]
173
+ transform_gen = T.ResizeShortestEdge(
174
+ [self.cfg.INPUT.MIN_SIZE_TEST, self.cfg.INPUT.MIN_SIZE_TEST], self.cfg.INPUT.MAX_SIZE_TEST
175
+ )
176
+ image = transform_gen.get_transform(original_image).apply_image(original_image)
177
+ image = torch.as_tensor(image.astype("float32").transpose(2, 0, 1))
178
+ inputs = {"image": image, "height": height, "width": width}
179
+ return inputs
180
+
181
+ def get_output(self, monitors_dict, img):
182
+ image = read_image(img, format="BGR")
183
+ input_image_dict = [self._get_input_dict(image)]
184
+ pred_instances, feats = self._inference(self.model, input_image_dict)
185
+ detections = pred_instances[0]["instances"].to("cpu")
186
+ cls_idxs = detections.pred_classes.detach().numpy()
187
+ # get labels from class indices
188
+ labels = [self.label_dict[i] for i in cls_idxs]
189
+ # count values in labels, and return a dictionary
190
+ labels_count_dict = dict((i, labels.count(i)) for i in labels)
191
+ v = Visualizer(image[..., ::-1], MetadataCatalog.get("custom_dataset"), scale=1)
192
+ v = v.draw_instance_predictions(detections)
193
+ img_detection = v.get_image()
194
+ df = pd.DataFrame(list(labels_count_dict.items()), columns=['Object', 'Count'])
195
+ verdicts = []
196
+ for label, feat in zip(labels, feats):
197
+ verdict = monitors_dict[label].make_verdicts(feat[np.newaxis,:])[0]
198
+ verdicts.append(verdict)
199
+ detections_ood = detections[[i for i, x in enumerate(verdicts) if not x]]
200
+ detections_ood.pred_classes = torch.tensor([5]*len(detections_ood.pred_classes))
201
+ labels_ood = [label for label, verdict in zip(labels, verdicts) if not verdict]
202
+ verdicts_ood = ["Rejected"]*len(labels_ood)
203
+ df_verdict = pd.DataFrame(list(zip(labels_ood, verdicts_ood)), columns=['Object', 'Verdict'])
204
+ v = Visualizer(image[..., ::-1], MetadataCatalog.get("custom_dataset"), scale=1)
205
+ for box in detections_ood.pred_boxes.to('cpu'):
206
+ v.draw_box(box)
207
+ v.draw_text("OOD", tuple(box[:2].numpy()))
208
+ v = v.get_output()
209
+ img_ood = v.get_image()
210
+ pred_bboxes = detections.pred_boxes.tensor.numpy().astype(np.int32)
211
+ target_layers = [self.model.backbone]
212
+ targets = [FasterRCNNBoxScoreTarget(labels=labels, bounding_boxes=pred_bboxes)]
213
+ cam = EigenCAM(self.model,
214
+ target_layers,
215
+ use_cuda=False,
216
+ reshape_transform=self._fasterrcnn_reshape_transform)
217
+ grayscale_cam = cam(input_image_dict, targets)
218
+ cam = self._postprocess_cam(grayscale_cam, input_image_dict[0]["width"], input_image_dict[0]["height"])
219
+ plt.rcParams["figure.figsize"] = (30,10)
220
+ plt.imshow(img_detection[..., ::-1], interpolation='none')
221
+ plt.imshow(cam, cmap='jet', alpha=0.5)
222
+ plt.axis("off")
223
+ img_buff = io.BytesIO()
224
+ plt.savefig(img_buff, format='png', bbox_inches='tight', pad_inches=0)
225
+ img_cam = Image.open(img_buff)
226
+ image_dict = {}
227
+ image_dict["image"] = image
228
+ image_dict["cam"] = img_cam
229
+ image_dict["detection"] = img_detection
230
+ image_dict["verdict"] = img_ood
231
+ return image_dict, df, df_verdict
enlarge.py ADDED
@@ -0,0 +1,320 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ from detectron2.utils.logger import setup_logger
3
+ setup_logger()
4
+
5
+ from detectron2.config import get_cfg
6
+ import detectron2.data.transforms as T
7
+ from detectron2.checkpoint import DetectionCheckpointer
8
+ from detectron2.modeling import build_model
9
+
10
+
11
+ import numpy as np
12
+ import cv2
13
+ import os
14
+ import argparse
15
+ import time
16
+ import h5py
17
+ import pickle
18
+ import gradio as gr
19
+ import fiftyone as fo
20
+ from fiftyone import ViewField as F
21
+ import tqdm
22
+ torch.manual_seed(0)
23
+ np.random.seed(0)
24
+
25
+ torch.backends.cudnn.deterministic = True
26
+ torch.backends.cudnn.benchmark = False
27
+
28
+ from vos.detection.modeling.regnet import build_regnet_fpn_backbone
29
+ import core.metadata as metadata
30
+
31
+ from utils_clustering import *
32
+
33
+ fullName2ab_dict = {'PASCAL-VOC':"voc", 'BDD100K':"bdd", 'KITTI':"kitti", 'Speed signs':"speed", 'NuScenes':"nu"}
34
+ ab2FullName_dict = {'voc':"PASCAL-VOC", 'bdd':"BDD100K", 'kitti':"KITTI", 'speed':"Speed signs", 'nu':"NuScenes"}
35
+ class Detectron2Monitor():
36
+ def __init__(self, id, backbone):
37
+ self.id, self.label_list = self._get_label_list(id)
38
+ self.backbone = backbone
39
+ self.cfg, self.device, self.model = self._get_model()
40
+ self.label_dict = {i:label for i, label in enumerate(self.label_list)}
41
+ self.eval_list = ["ID-voc-OOD-coco", "OOD-open", "voc-val"] if self.id == "voc" else ["ID-bdd-OOD-coco", "OOD-open", "voc-ood", f"{self.id}-val"]
42
+
43
+ def _get_label_list(self, id):
44
+ id = fullName2ab_dict[id]
45
+ if id == 'voc':
46
+ label_list = metadata.VOC_THING_CLASSES
47
+ elif id == 'bdd':
48
+ label_list = metadata.BDD_THING_CLASSES
49
+ elif id == 'kitti':
50
+ label_list = metadata.KITTI_THING_CLASSES
51
+ elif id == 'speed' or id == 'prescan':
52
+ label_list = metadata.SPEED_THING_CLASSES
53
+ else:
54
+ label_list = metadata.NU_THING_CLASSES
55
+ return id, label_list
56
+
57
+ def _get_model(self):
58
+ cfg = get_cfg()
59
+ cfg.merge_from_file(f"/home/hugo/bdd100k-monitoring/monitoringObjectDetection/vanilla_{self.backbone}.yaml")
60
+ cfg.MODEL.WEIGHTS = f"models/model_final_{self.backbone}_{self.id}.pth"
61
+ cfg.MODEL.DEVICE='cuda'
62
+ cfg.MODEL.ROI_HEADS.NUM_CLASSES = len(self.label_list)
63
+ model = build_model(cfg)
64
+ model.eval()
65
+ checkpointer = DetectionCheckpointer(model)
66
+ checkpointer.load(cfg.MODEL.WEIGHTS)
67
+ device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
68
+ model = model.to(device)
69
+ return cfg, device, model
70
+
71
+ def _inference(self, model, inputs):
72
+ with torch.no_grad():
73
+ images = model.preprocess_image(inputs)
74
+ features = model.backbone(images.tensor)
75
+ proposals, _ = model.proposal_generator(images, features, None) # RPN
76
+
77
+ features_ = [features[f] for f in model.roi_heads.box_in_features]
78
+ box_features = model.roi_heads.box_pooler(features_, [x.proposal_boxes for x in proposals])
79
+ box_features = model.roi_heads.box_head(box_features) # features of all 1k candidates
80
+ predictions = model.roi_heads.box_predictor(box_features)
81
+ pred_instances, pred_inds = model.roi_heads.box_predictor.inference(predictions, proposals)
82
+ pred_instances = model.roi_heads.forward_with_given_boxes(features, pred_instances)
83
+
84
+ # output boxes, masks, scores, etc
85
+ pred_instances = model._postprocess(pred_instances, inputs, images.image_sizes) # scale box to orig size
86
+ # features of the proposed boxes
87
+ feats = box_features[pred_inds].cpu().numpy()
88
+ return pred_instances, feats
89
+
90
+ def _save_features(self, feats_npy, dataset_view, file_path):
91
+ features_idx_dict = {cls:[] for cls in self.label_list}
92
+ for sample in tqdm.tqdm(dataset_view, desc="Saving features"):
93
+ for detection in sample.prediction.detections:
94
+ label_pred = detection.label
95
+ feature_idx = detection.feature_idx
96
+ features_idx_dict[label_pred].append(feature_idx)
97
+ feats_dict = {cls:feats_npy[features_idx_dict[cls]] for cls in self.label_list}
98
+ if not os.path.exists(file_path):
99
+ os.makedirs(os.path.dirname(file_path), exist_ok=True)
100
+ with open(file_path, 'wb') as f:
101
+ pickle.dump(feats_dict, f)
102
+
103
+ def _extract(self, dataset_name):
104
+ dataset = fo.load_dataset(dataset_name)
105
+ aug = T.AugmentationList([T.ResizeShortestEdge(
106
+ [self.cfg.INPUT.MIN_SIZE_TEST, self.cfg.INPUT.MIN_SIZE_TEST], self.cfg.INPUT.MAX_SIZE_TEST),
107
+ ]
108
+ )
109
+ i = 0
110
+ feats_list = []
111
+ for sample in tqdm.tqdm(dataset, desc="Extracting features"):
112
+ image = cv2.imread(sample.filepath)
113
+ height, width = image.shape[:2]
114
+ input = T.AugInput(image)
115
+ transform = aug(input)
116
+ image = input.image
117
+ image = torch.as_tensor(image.astype("float32").transpose(2, 0, 1)).to(self.device)
118
+ inputs = [{"image": image, "height": height, "width": width}]
119
+
120
+ preds, feats = self._inference(self.model, inputs)
121
+ boxes = preds[0]["instances"].pred_boxes.tensor.cpu().detach().numpy()
122
+ classes = preds[0]["instances"].pred_classes.cpu().detach().numpy()
123
+ scores = preds[0]["instances"].scores.cpu().detach().numpy()
124
+
125
+ feats_list.extend(feats)
126
+ if i == 1000:
127
+ np.save('feats.npy', feats_list)
128
+
129
+ detections = []
130
+ for score, label, box in zip(scores, classes, boxes):
131
+ x1, y1, x2, y2 = box
132
+ rel_box = [x1/width, y1/height, (x2 - x1) / width, (y2 - y1) / height]
133
+ label = self.label_dict[label]
134
+ detections.append(
135
+ fo.Detection(
136
+ label=label,
137
+ bounding_box=rel_box,
138
+ confidence=score,
139
+ feature_idx=i
140
+ ),
141
+ )
142
+ i += 1
143
+ sample["prediction"] = fo.Detections(detections=detections)
144
+ sample.save()
145
+ feats_npy = np.array(feats_list)
146
+ with h5py.File(f'feats_{self.id}-train_{self.backbone}.h5', 'w') as f:
147
+ dset = f.create_dataset(f"feats_{self.id}-train_{self.backbone}", data=feats_npy)
148
+ if dataset.name.endswith(("train", "val")):
149
+ results = dataset.evaluate_detections(
150
+ "prediction",
151
+ gt_field="detections",
152
+ eval_key="eval",
153
+ compute_mAP=True)
154
+ # results.print_report()
155
+ # print("mAP: ", results.mAP())
156
+ tp_prediction_view = dataset.filter_labels("prediction", F("eval") == "tp")
157
+ self._save_features(feats_npy, tp_prediction_view, f"train_feats/{self.id}/{self.backbone}/{self.id}-train_feats_tp_dict.pickle")
158
+ if dataset.name.endswith("val"):
159
+ fp_prediction_view = dataset.filter_labels("prediction", F("eval") == "fp")
160
+ self._save_features(feats_npy, fp_prediction_view, f"val_feats/{self.id}/{self.backbone}/{self.dataset_name}_feats_fp_dict.pickle")
161
+ else:
162
+ self._save_features(feats_npy, dataset, f"val_feats/{self.id}/{self.backbone}/{self.dataset_name}_feats_fp_dict.pickle")
163
+
164
+ def _construct(self, clustering_algo, nb_clusters=4, eps=5, min_samples=10):
165
+ with open(f"/home/hugo/bdd100k-monitoring/train_feats/{self.id}/{self.backbone}/{self.id}-train_feats_tp_dict.pickle", 'rb') as f:
166
+ feats_dict = pickle.load(f)
167
+ dir_path = f'monitors/{self.id}/{self.backbone}/{clustering_algo}'
168
+ if not os.path.exists(dir_path):
169
+ os.makedirs(dir_path)
170
+ monitor_dict = {}
171
+ for class_, fts in tqdm.tqdm(feats_dict.items(), desc="Constructing monitors"):
172
+ if clustering_algo == "kmeans":
173
+ clusters = k_means_cluster(fts, nb_clusters)
174
+ elif clustering_algo == "spectral":
175
+ clusters = spectral_cluster(fts, nb_clusters)
176
+ elif clustering_algo == "dbscan":
177
+ clusters = dbscan_cluster(fts, eps, min_samples)
178
+ dims = fts.shape[1]
179
+ box_list = []
180
+ for cl_id, points in clusters.items():
181
+ box = Box()
182
+ box.build(dims, points)
183
+ box_list.append(box)
184
+ monitor = Monitor(good_ref=box_list)
185
+ monitor_dict[class_] = monitor
186
+ if clustering_algo == "dbscan":
187
+ with open(f"monitors/{self.id}/{self.backbone}/{clustering_algo}/eps{eps}_min_samples{min_samples}.pkl" , 'wb') as f:
188
+ pickle.dump(monitor_dict, f)
189
+ else:
190
+ with open(f"monitors/{self.id}/{self.backbone}/{clustering_algo}/{nb_clusters}.pkl" , 'wb') as f:
191
+ pickle.dump(monitor_dict, f)
192
+
193
+ def _load_monitors(self, clustering_algo, nb_clusters, eps=5, min_samples=10):
194
+ if clustering_algo == "dbscan":
195
+ with open(f"monitors/{self.id}/{self.backbone}/{clustering_algo}/eps{eps}_min_samples{min_samples}.pkl", 'rb') as f:
196
+ monitors_dict = pickle.load(f)
197
+ else:
198
+ with open(f"monitors/{self.id}/{self.backbone}/{clustering_algo}/{nb_clusters}.pkl", 'rb') as f:
199
+ monitors_dict = pickle.load(f)
200
+ return monitors_dict
201
+
202
+ def _evaluate(self, monitors_dict):
203
+ dataset_name = f"{self.id}-val"
204
+ with open(f'val_feats/{self.id}/{self.backbone}/{dataset_name}_feats_tp_dict.pickle', 'rb') as f:
205
+ feats_tp_dict = pickle.load(f)
206
+ with open(f'val_feats/{self.id}/{self.backbone}/{dataset_name}_feats_fp_dict.pickle', 'rb') as f:
207
+ feats_fp_dict = pickle.load(f)
208
+ # monitors_dict = self._load_monitors(clustering_algo, nb_clusters, eps, min_samples)
209
+ # make verdicts on ID data
210
+ data_tp = []
211
+ data_fp = []
212
+ accept_sum = {"tp": 0, "fp": 0}
213
+ reject_sum = {"tp": 0, "fp": 0}
214
+ for label in tqdm.tqdm(self.label_list, desc="Evaluation on ID data"):
215
+ if label in monitors_dict:
216
+ verdict = monitors_dict[label].make_verdicts(feats_tp_dict[label])
217
+ data_tp.append([label, len(verdict), np.sum(verdict)/len(verdict)])
218
+ accept_sum["tp"] += np.sum(verdict)
219
+ reject_sum["tp"] += len(verdict) - np.sum(verdict)
220
+ verdict = monitors_dict[label].make_verdicts(feats_fp_dict[label])
221
+ data_fp.append([label, len(verdict), (len(verdict)-np.sum(verdict))/len(verdict)])
222
+ accept_sum["fp"] += np.sum(verdict)
223
+ reject_sum["fp"] += len(verdict) - np.sum(verdict)
224
+ TPR = round((accept_sum['tp'] / (reject_sum['tp'] + accept_sum['tp'])*100), 2)
225
+ FPR = round((accept_sum['fp'] / (reject_sum['fp'] + accept_sum['fp'])*100), 2)
226
+ id_name = ab2FullName_dict[self.id]
227
+ df_id = pd.DataFrame([[id_name, f"{TPR}%", f"{FPR}%"]], columns=["Dataset", "TPR", "FPR"])
228
+
229
+ data_ood = []
230
+ i = 0
231
+ self.eval_list.remove(dataset_name)
232
+ for dataset_name in tqdm.tqdm(self.eval_list, desc="Evaluation on OOD data"):
233
+ accept_sum = {"tp": 0, "fp": 0}
234
+ reject_sum = {"tp": 0, "fp": 0}
235
+ with open(f'val_feats/{self.id}/{self.backbone}/{dataset_name}_feats_fp_dict.pickle', 'rb') as f:
236
+ feats_fp_dict = pickle.load(f)
237
+ for label in self.label_list:
238
+ if label in monitors_dict:
239
+ verdict = monitors_dict[label].make_verdicts(feats_fp_dict[label])
240
+ accept_sum["fp"] += np.sum(verdict)
241
+ reject_sum["fp"] += len(verdict) - np.sum(verdict)
242
+ FPR = round((accept_sum['fp'] / (reject_sum['fp'] + accept_sum['fp'])*100), 2)
243
+ data_ood.append([dataset_name, str(FPR)+"%"])
244
+ i += 1
245
+ # prepare dataframes
246
+ df_ood = pd.DataFrame(data_ood, columns=["Dataset", "FPR"])
247
+ df_ood["Dataset"] = ["COCO", "Open Images"] if self.id == "voc" else ["COCO", "Open Images", "VOC-OOD"]
248
+ return df_id, df_ood
249
+
250
+ def _enlarge(self, monitors_dict, delta):
251
+ for label, monitor in monitors_dict.items():
252
+ for i in range(len(monitor.good_ref)):
253
+ monitor.good_ref[i].ivals = monitor.good_ref[i].ivals*np.array([1-delta, 1+delta])
254
+ monitors_dict[label] = monitor
255
+ return monitors_dict
256
+
257
+ def fx_gradio(id, backbone, progress=gr.Progress(track_tqdm=True)):
258
+ detectron2monitor = Detectron2Monitor(id, backbone)
259
+ t0 = time.time()
260
+ detectron2monitor._extract(f"{detectron2monitor.id}-train")
261
+ minutes, seconds = divmod(time.time()-t0, 60)
262
+ return f"Total feature extraction time: {int(minutes):02d}:{int(seconds):02d}"
263
+
264
+ def construct_gradio(id, backbone, clustering_algo, nb_clusters, eps, min_samples, progress=gr.Progress(track_tqdm=True)):
265
+ detection2monitor = Detectron2Monitor(id, backbone)
266
+ t0 = time.time()
267
+ detection2monitor._construct(clustering_algo, nb_clusters, eps, min_samples)
268
+ minutes, seconds = divmod(time.time()-t0, 60)
269
+ return f"Total monitor construction time: {int(minutes):02d}:{int(seconds):02d}"
270
+
271
+ def fx_eval_gradio(id, backbone, progress=gr.Progress(track_tqdm=True)):
272
+ detectron2monitor = Detectron2Monitor(id, backbone)
273
+ t0 = time.time()
274
+ for dataset_name in tqdm.tqdm(detectron2monitor.eval_list, desc="Evaluation data preparation"):
275
+ detectron2monitor._extract(dataset_name)
276
+ minutes, seconds = divmod(time.time()-t0, 60)
277
+ return f"Total evaluation data preparation time: {int(minutes):02d}:{int(seconds):02d}"
278
+
279
+ def eval_gradio(id, backbone, clustering_algo, nb_clusters, eps, min_samples, progress=gr.Progress(track_tqdm=True)):
280
+ detectron2monitor = Detectron2Monitor(id, backbone)
281
+ monitors_dict = detectron2monitor._load_monitors(clustering_algo, nb_clusters, eps, min_samples)
282
+ df_id, df_ood = detectron2monitor._evaluate(monitors_dict)
283
+ return df_id, df_ood
284
+
285
+ def enlarge_gradio(id, backbone, clustering_algo, nb_clusters, eps, min_samples, delta, progress=gr.Progress(track_tqdm=True)):
286
+ detectron2monitor = Detectron2Monitor(id, backbone)
287
+ monitors_dict = detectron2monitor._load_monitors(clustering_algo, nb_clusters, eps, min_samples)
288
+ monitors_dict_enlarge = detectron2monitor._enlarge(monitors_dict, delta)
289
+ df_id, df_ood = detectron2monitor._evaluate(monitors_dict_enlarge)
290
+ # if clustering_algo == "dbscan":
291
+ # with open(f"monitors/{id}/{backbone}/{clustering_algo}/eps{eps}_min_samples{min_samples}_delta{delta}.pkl" , 'wb') as f:
292
+ # pickle.dump(monitors_dict, f)
293
+ # else:
294
+ # with open(f"monitors/{id}/{backbone}/{clustering_algo}/{nb_clusters}_delta{delta}.pkl" , 'wb') as f:
295
+ # pickle.dump(monitors_dict, f)
296
+ # return f"Monitors enlarged by {delta*100}%"
297
+ return df_id, df_ood
298
+ with gr.Blocks(theme='soft') as demo:
299
+ gr.Markdown("# Monitor enlargment utility")
300
+ id = gr.Radio(['PASCAL-VOC', 'BDD100K', 'KITTI', 'Speed signs', 'NuScenes'], label="Dataset")
301
+ backbone = gr.Radio(['regnet', 'resnet'], label="Backbone")
302
+ clustering_algo = gr.Dropdown(['kmeans', 'spectral', 'dbscan', 'opticals'], label="Clustering algorithm")
303
+ with gr.Row():
304
+ nb_clusters = gr.Number(value=5, label="Number of clusters", precision=0)
305
+ eps = gr.Number(value=5, label="Epsilon", precision=0)
306
+ min_samples = gr.Number(value=10, label="Minimum samples", precision=0)
307
+ delta = gr.Slider(minimum=0, maximum=2.5, step=0.05, label="Delta")
308
+ with gr.Row():
309
+ with gr.Group("Original monitors"):
310
+ eval_id = gr.Dataframe(type="pandas", label="ID performance")
311
+ eavl_ood = gr.Dataframe(type="pandas", label="OOD performance")
312
+ eval_btn = gr.Button("Monitor Evaluation")
313
+ with gr.Column("Enlarged monitors"):
314
+ eval_id2 = gr.Dataframe(type="pandas", label="ID performance")
315
+ eavl_ood2 = gr.Dataframe(type="pandas", label="OOD performance")
316
+ enlarge_btn = gr.Button("Monitor Enlargement")
317
+ eval_btn.click(fn=eval_gradio, inputs=[id, backbone, clustering_algo, nb_clusters, eps, min_samples], outputs=[eval_id, eavl_ood])
318
+ enlarge_btn.click(fn=enlarge_gradio, inputs=[id, backbone, clustering_algo, nb_clusters, eps, min_samples, delta], outputs=[eval_id2, eavl_ood2])
319
+
320
+ demo.queue().launch()
interface.py ADDED
@@ -0,0 +1,95 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ from detectron2.utils.logger import setup_logger
3
+ setup_logger()
4
+
5
+
6
+ import numpy as np
7
+ import time
8
+ import gradio as gr
9
+ import tqdm
10
+ torch.manual_seed(0)
11
+ np.random.seed(0)
12
+
13
+ torch.backends.cudnn.deterministic = True
14
+ torch.backends.cudnn.benchmark = False
15
+
16
+ from detectron2monitor import Detectron2Monitor
17
+
18
+ def eval_gradio(id, backbone, clustering_algo, nb_clusters, eps, min_samples, progress=gr.Progress(track_tqdm=True)):
19
+ detectron2monitor = Detectron2Monitor(id, backbone)
20
+ df_id, df_ood = detectron2monitor._evaluate(clustering_algo, nb_clusters, eps, min_samples)
21
+ return df_id, df_ood
22
+
23
+ def inference_gradio(id, backbone, clustering_algo, nb_clusters, eps, min_samples, file):
24
+ detectron2monitor = Detectron2Monitor(id, backbone, 0.5)
25
+ monitors_dict = detectron2monitor._load_monitors(clustering_algo, nb_clusters, eps, min_samples)
26
+ image_dict, df, df_verdict = detectron2monitor.get_output(monitors_dict, file)
27
+ return image_dict["detection"], image_dict["verdict"], image_dict["cam"], df, df_verdict
28
+
29
+ with gr.Blocks(theme='soft') as demo:
30
+ gr.Markdown("# Runtime Monitoring Computer Vision Models")
31
+ gr.Markdown(
32
+ """
33
+ This interactive demo presents an approach to monitoring neural networks-based computer vision models using box abstraction-based techniques. Our method involves abstracting features extracted from training data to construct monitors. The demo walks users through the entire process, from monitor construction to evaluation.
34
+ <!-- The interface is divided into several basic modules:
35
+
36
+ - **In-distribution dataset and backbone**: This module allows users to select their target model and dataset.
37
+
38
+ - **Feature extraction**: Neuron activation pattern are extracted from the model's intermediate layers using training data. These features represent the good behaviors of the model.
39
+ - **Monitor construction**: Extracted features are grouped using different clustering techniques. These clusters are then abstracted to serve as references for the monitors.
40
+ - **Evaluation preparation**: To facilate the evalution, the features should be extracted from evaluation datasets prior to monitor evalution.
41
+ - **Monitor Evaluation**: The effectiveness of monitors in detecting Out-of-Distribution (OoD) objects are assessed. One of our core metric is FPR 95, which represents the false positive (incorrectly detected objects) rate when the true positive rate for ID is set at 95%. -->
42
+
43
+ """
44
+ )
45
+ with gr.Tab("Image Classification"):
46
+ id = gr.Radio(['MNIST', 'CIFAR-10', 'CIFAR-100', 'ImageNet-100', 'ImageNet-1K'], label="Dataset")
47
+ backbone = gr.Radio(['LeNet-5', 'ResNet-18', 'WideResNet-28', 'ResNet-50'], label="Backbone")
48
+ with gr.Tab("Object Detection"):
49
+ id = gr.Radio(['PASCAL-VOC', 'BDD100K', 'KITTI', 'Speed signs', 'NuScenes'], label="Dataset")
50
+ backbone = gr.Radio(['regnet', 'resnet'], label="Backbone")
51
+ clustering_algo = gr.Dropdown(['kmeans', 'spectral', 'dbscan', 'opticals'], label="Clustering algorithm")
52
+ with gr.Row():
53
+ nb_clusters = gr.Number(value=5, label="Number of clusters", precision=0)
54
+ eps = gr.Number(value=5, label="Epsilon", precision=0)
55
+ min_samples = gr.Number(value=10, label="Minimum samples", precision=0)
56
+ with gr.Column():
57
+ # with gr.Column():
58
+ # with gr.Group():
59
+ # extract_btn = gr.Button("Extract features")
60
+ # output1 = gr.Textbox(label="Output")
61
+ # with gr.Group():
62
+ # construct_btn = gr.Button("Monitor Construction")
63
+ # clustering_algo = gr.Dropdown(['kmeans', 'spectral', 'dbscan', 'opticals'], label="Clustering algorithm")
64
+ # with gr.Row():
65
+ # nb_clusters = gr.Number(value=5, label="Number of clusters", precision=0)
66
+ # eps = gr.Number(value=5, label="Epsilon", precision=0)
67
+ # min_samples = gr.Number(value=10, label="Minimum samples", precision=0)
68
+ # output2 = gr.Textbox(label="Output")
69
+ # with gr.Column():
70
+ # with gr.Group():
71
+ # prep_btn = gr.Button("Evaluation Data Preparation")
72
+ # prep_output = gr.Textbox(label="Output")
73
+ with gr.Group():
74
+ eval_btn = gr.Button("Monitor Evaluation")
75
+ eval_id = gr.Dataframe(type="pandas", label="ID performance")
76
+ eavl_ood = gr.Dataframe(type="pandas", label="OOD performance")
77
+ with gr.Row():
78
+ with gr.Column():
79
+ image = gr.Image(type="filepath", label="Input")
80
+ button = gr.Button("Infer")
81
+ with gr.Column():
82
+ with gr.Tab("Detection"):
83
+ detection = gr.Image(label="Output")
84
+ df = gr.Dataframe(label="Detection summary")
85
+ with gr.Tab("Verdict"):
86
+ verdict = gr.Image(label="Output")
87
+ df_verdict = gr.Dataframe(label="Verdict summary")
88
+ with gr.Tab("Explainable AI"):
89
+ cam = gr.Image(label="Output")
90
+ button.click(fn=inference_gradio, inputs=[id, backbone, clustering_algo, nb_clusters, eps, min_samples, image], outputs=[detection, verdict, cam, df, df_verdict])
91
+ # extract_btn.click(fn=fx_gradio, inputs=[id, backbone], outputs=[output1])
92
+ # construct_btn.click(fn=construct_gradio, inputs=[id, backbone, clustering_algo, nb_clusters, eps, min_samples], outputs=[output2])
93
+ # prep_btn.click(fn=fx_eval_gradio, inputs=[id, backbone], outputs=[prep_output])
94
+ eval_btn.click(fn=eval_gradio, inputs=[id, backbone, clustering_algo, nb_clusters, eps, min_samples], outputs=[eval_id, eavl_ood])
95
+ demo.queue().launch()
interface_tabbed.py ADDED
@@ -0,0 +1,155 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ from detectron2.utils.logger import setup_logger
3
+ setup_logger()
4
+
5
+ from detectron2.config import get_cfg
6
+ import detectron2.data.transforms as T
7
+ from detectron2.checkpoint import DetectionCheckpointer
8
+ from detectron2.modeling import build_model
9
+ from detectron2.data.detection_utils import read_image
10
+ from detectron2.utils.visualizer import Visualizer
11
+ from detectron2.data import MetadataCatalog
12
+
13
+
14
+ import numpy as np
15
+ import time
16
+ import gradio as gr
17
+ import fiftyone as fo
18
+ from fiftyone import ViewField as F
19
+ import tqdm
20
+ import matplotlib.pyplot as plt
21
+ import io
22
+ from PIL import Image
23
+ torch.manual_seed(0)
24
+ np.random.seed(0)
25
+
26
+ torch.backends.cudnn.deterministic = True
27
+ torch.backends.cudnn.benchmark = False
28
+ from detectron2monitor import Detectron2Monitor
29
+
30
+
31
+
32
+ def fx_gradio(id, backbone, progress=gr.Progress(track_tqdm=True)):
33
+ detectron2monitor = Detectron2Monitor(id, backbone)
34
+ t0 = time.time()
35
+ detectron2monitor._extract(f"{detectron2monitor.id}-train")
36
+ minutes, seconds = divmod(time.time()-t0, 60)
37
+ return f"Total feature extraction time: {int(minutes):02d}:{int(seconds):02d}"
38
+
39
+ def construct_gradio(id, backbone, clustering_algo, nb_clusters, eps, min_samples, progress=gr.Progress(track_tqdm=True)):
40
+ detection2monitor = Detectron2Monitor(id, backbone)
41
+ t0 = time.time()
42
+ detection2monitor._construct(clustering_algo, nb_clusters, eps, min_samples)
43
+ minutes, seconds = divmod(time.time()-t0, 60)
44
+ return f"Total monitor construction time: {int(minutes):02d}:{int(seconds):02d}"
45
+
46
+ def fx_eval_gradio(id, backbone, progress=gr.Progress(track_tqdm=True)):
47
+ detectron2monitor = Detectron2Monitor(id, backbone)
48
+ t0 = time.time()
49
+ for dataset_name in tqdm.tqdm(detectron2monitor.eval_list, desc="Evaluation data preparation"):
50
+ detectron2monitor._extract(dataset_name)
51
+ minutes, seconds = divmod(time.time()-t0, 60)
52
+ return f"Total evaluation data preparation time: {int(minutes):02d}:{int(seconds):02d}"
53
+
54
+ def eval_gradio(id, backbone, clustering_algo, nb_clusters, eps, min_samples, progress=gr.Progress(track_tqdm=True)):
55
+ detectron2monitor = Detectron2Monitor(id, backbone)
56
+ df_id, df_ood = detectron2monitor._evaluate(clustering_algo, nb_clusters, eps, min_samples)
57
+ return df_id, df_ood
58
+
59
+ def inference_gradio(id, backbone, clustering_algo, nb_clusters, eps, min_samples, file):
60
+ detectron2monitor = Detectron2Monitor(id, backbone, 0.5)
61
+ monitors_dict = detectron2monitor._load_monitors(clustering_algo, nb_clusters, eps, min_samples)
62
+ image_dict, df, df_verdict = detectron2monitor.get_output(monitors_dict, file)
63
+ return image_dict["detection"], image_dict["verdict"], image_dict["cam"], df, df_verdict
64
+
65
+ with gr.Blocks(theme='soft') as demo:
66
+ gr.Markdown("# Runtime Monitoring Computer Vision Models")
67
+ gr.Markdown(
68
+ """
69
+ This interactive demo presents an approach to monitoring neural networks-based computer vision models using box abstraction-based techniques. Our method involves abstracting features extracted from training data to construct monitors. The demo walks users through the entire process, from monitor construction to evaluation.
70
+ The interface is divided into several basic modules:
71
+
72
+ - **In-distribution dataset and backbone**: This module allows users to select their target model and dataset.
73
+ - **Feature extraction**: Neuron activation pattern are extracted from the model's intermediate layers using training data. These features represent the good behaviors of the model.
74
+ - **Monitor construction**: Extracted features are grouped using different clustering techniques. These clusters are then abstracted to serve as references for the monitors.
75
+ - **Evaluation preparation**: To facilate the evalution, the features should be extracted from evaluation datasets prior to monitor evalution.
76
+ - **Monitor Evaluation**: The effectiveness of monitors in detecting Out-of-Distribution (OoD) objects are assessed. One of our core metric is FPR 95, which represents the false positive (incorrectly detected objects) rate when the true positive rate for ID is set at 95%.
77
+ """
78
+ )
79
+ with gr.Tab("Image Classification"):
80
+ id = gr.Radio(['MNIST', 'CIFAR-10', 'CIFAR-100', 'ImageNet-100', 'ImageNet-1K'], label="Dataset")
81
+ backbone = gr.Radio(['LeNet-5', 'ResNet-18', 'WideResNet-28', 'ResNet-50'], label="Backbone")
82
+ with gr.Tab("Feature extraction"):
83
+ extract_btn = gr.Button("Extract features")
84
+ output1 = gr.Textbox(label="Output")
85
+ with gr.Tab("Monitor construction"):
86
+ construct_btn = gr.Button("Monitor Construction")
87
+ clustering_algo = gr.Dropdown(['kmeans', 'spectral', 'dbscan', 'opticals'], label="Clustering algorithm")
88
+ with gr.Row():
89
+ nb_clusters = gr.Number(value=5, label="Number of clusters", precision=0)
90
+ eps = gr.Number(value=5, label="Epsilon", precision=0)
91
+ min_samples = gr.Number(value=10, label="Minimum samples", precision=0)
92
+ output2 = gr.Textbox(label="Output")
93
+ with gr.Tab("Evaluation"):
94
+ prep_btn = gr.Button("Evaluation Data Preparation")
95
+ prep_output = gr.Textbox(label="Output")
96
+ with gr.Tab("Evaluation results"):
97
+ eval_btn = gr.Button("Monitor Evaluation")
98
+ eval_id = gr.Dataframe(type="pandas", label="ID performance")
99
+ eavl_ood = gr.Dataframe(type="pandas", label="OOD performance")
100
+ with gr.Tab("Inference"):
101
+ with gr.Row().style(equal_height=True):
102
+ with gr.Column():
103
+ image = gr.Image(type="filepath", label="Input")
104
+ button = gr.Button("Infer")
105
+
106
+ with gr.Column():
107
+ with gr.Tab("Detection"):
108
+ detection = gr.Image(label="Output")
109
+ df = gr.Dataframe(label="Detection summary")
110
+ with gr.Tab("Verdict"):
111
+ verdict = gr.Image(label="Output")
112
+ df_verdict = gr.Dataframe(label="Verdict summary")
113
+ with gr.Tab("Explainable AI"):
114
+ cam = gr.Image(label="Output")
115
+ with gr.Tab("Object Detection"):
116
+ id = gr.Radio(['PASCAL-VOC', 'BDD100K', 'KITTI', 'Speed signs', 'NuScenes'], label="Dataset")
117
+ backbone = gr.Radio(['regnet', 'resnet'], label="Backbone")
118
+ with gr.Tab("Feature extraction"):
119
+ extract_btn = gr.Button("Extract features")
120
+ output1 = gr.Textbox(label="Output")
121
+ with gr.Tab("Monitor construction"):
122
+ construct_btn = gr.Button("Monitor Construction")
123
+ clustering_algo = gr.Dropdown(['kmeans', 'spectral', 'dbscan', 'opticals'], label="Clustering algorithm")
124
+ with gr.Row():
125
+ nb_clusters = gr.Number(value=5, label="Number of clusters", precision=0)
126
+ eps = gr.Number(value=5, label="Epsilon", precision=0)
127
+ min_samples = gr.Number(value=10, label="Minimum samples", precision=0)
128
+ output2 = gr.Textbox(label="Output")
129
+ with gr.Tab("Evaluation preparation"):
130
+ prep_btn = gr.Button("Evaluation Data Preparation")
131
+ prep_output = gr.Textbox(label="Output")
132
+ with gr.Tab("Evaluation results"):
133
+ eval_btn = gr.Button("Monitor Evaluation")
134
+ eval_id = gr.Dataframe(type="pandas", label="ID performance")
135
+ eavl_ood = gr.Dataframe(type="pandas", label="OOD performance")
136
+ with gr.Tab("Inference"):
137
+ with gr.Row().style(equal_height=True):
138
+ with gr.Column():
139
+ image = gr.Image(type="filepath", label="Input")
140
+ button = gr.Button("Infer")
141
+ with gr.Column():
142
+ with gr.Tab("Detection"):
143
+ detection = gr.Image(label="Output")
144
+ df = gr.Dataframe(label="Detection summary")
145
+ with gr.Tab("Verdict"):
146
+ verdict = gr.Image(label="Output")
147
+ df_verdict = gr.Dataframe(label="Verdict summary")
148
+ with gr.Tab("Explainable AI"):
149
+ cam = gr.Image(label="Output")
150
+ button.click(fn=inference_gradio, inputs=[id, backbone, clustering_algo, nb_clusters, eps, min_samples, image], outputs=[detection, verdict, cam, df, df_verdict])
151
+ extract_btn.click(fn=fx_gradio, inputs=[id, backbone], outputs=[output1])
152
+ construct_btn.click(fn=construct_gradio, inputs=[id, backbone, clustering_algo, nb_clusters, eps, min_samples], outputs=[output2])
153
+ prep_btn.click(fn=fx_eval_gradio, inputs=[id, backbone], outputs=[prep_output])
154
+ eval_btn.click(fn=eval_gradio, inputs=[id, backbone, clustering_algo, nb_clusters, eps, min_samples], outputs=[eval_id, eavl_ood])
155
+ demo.queue().launch()
models/configs/Base-RCNN-FPN.yaml ADDED
@@ -0,0 +1,50 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ MODEL:
2
+ META_ARCHITECTURE: "GeneralizedRCNN"
3
+
4
+ BACKBONE:
5
+ NAME: "build_resnet_fpn_backbone"
6
+
7
+ RESNETS:
8
+ OUT_FEATURES: ["res2", "res3", "res4", "res5"]
9
+
10
+ FPN:
11
+ IN_FEATURES: ["res2", "res3", "res4", "res5"]
12
+
13
+ ANCHOR_GENERATOR:
14
+ # One size for each in feature map
15
+ SIZES: [[32], [64], [128], [256], [512]]
16
+ # Three aspect ratios (same for all in feature maps)
17
+ ASPECT_RATIOS: [[0.5, 1.0, 2.0]]
18
+
19
+ RPN:
20
+ IN_FEATURES: ["p2", "p3", "p4", "p5", "p6"]
21
+ PRE_NMS_TOPK_TRAIN: 2000 # Per FPN level
22
+ PRE_NMS_TOPK_TEST: 1000 # Per FPN level
23
+ # Detectron1 uses 2000 proposals per-batch,
24
+ # (See "modeling/rpn/rpn_outputs.py" for details of this legacy issue)
25
+ # which is approximately 1000 proposals per-image since the default
26
+ # batch size for FPN is 2.
27
+ POST_NMS_TOPK_TRAIN: 1000
28
+ POST_NMS_TOPK_TEST: 1000
29
+
30
+ ROI_HEADS:
31
+ NAME: "StandardROIHeads"
32
+ IN_FEATURES: ["p2", "p3", "p4", "p5"]
33
+
34
+ ROI_BOX_HEAD:
35
+ NAME: "FastRCNNConvFCHead"
36
+ NUM_FC: 2
37
+ POOLER_RESOLUTION: 7
38
+
39
+ ROI_MASK_HEAD:
40
+ NAME: "MaskRCNNConvUpsampleHead"
41
+ NUM_CONV: 4
42
+ POOLER_RESOLUTION: 14
43
+
44
+ INPUT:
45
+ MIN_SIZE_TRAIN: (640, 672, 704, 736, 768, 800)
46
+
47
+ SOLVER:
48
+ CHECKPOINT_PERIOD: 210000
49
+
50
+ VERSION: 2
models/configs/vanilla_regnet.yaml ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ _BASE_: "Base-RCNN-FPN.yaml"
2
+ MODEL:
3
+ PIXEL_STD: [57.375, 57.120, 58.395]
4
+ BACKBONE:
5
+ NAME: "build_regnetx_fpn_backbone"
6
+ META_ARCHITECTURE: "GeneralizedRCNN"
7
+ WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-50.pkl"
8
+ # WEIGHTS: "./data/VOC-Detection/faster-rcnn/faster_rcnn_R_50_FPN_all_logistic/random_seed_0/model_final.pth"
9
+
10
+ # PROPOSAL_GENERATOR:
11
+ # NAME: "RPNLogistic"
12
+ FPN:
13
+ IN_FEATURES: ["s1", "s2", "s3", "s4"]
14
+ MASK_ON: False
15
+ RESNETS:
16
+ DEPTH: 50
17
+ ROI_HEADS:
18
+ NAME: "StandardROIHeads"
19
+ NUM_CLASSES: 10
20
+ INPUT:
21
+ MIN_SIZE_TRAIN: (480, 512, 544, 576, 608, 640, 672, 704, 736, 768, 800)
22
+ MIN_SIZE_TEST: 800
23
+ DATASETS:
24
+ TRAIN: ('bdd_custom_train',)
25
+ TEST: ('bdd_custom_val',)
26
+ SOLVER:
27
+ IMS_PER_BATCH: 16
28
+ BASE_LR: 0.02
29
+ STEPS: (60000, 80000)
30
+ MAX_ITER: 90000 # 17.4 epochs
31
+ WARMUP_ITERS: 100
32
+ DATALOADER:
33
+ NUM_WORKERS: 8 # Depends on the available memory
models/configs/vanilla_resnet.yaml ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ _BASE_: "Base-RCNN-FPN.yaml"
2
+ MODEL:
3
+ META_ARCHITECTURE: "GeneralizedRCNN"
4
+ WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-50.pkl"
5
+ # WEIGHTS: "./data/VOC-Detection/faster-rcnn/faster_rcnn_R_50_FPN_all_logistic/random_seed_0/model_final.pth"
6
+
7
+ # PROPOSAL_GENERATOR:
8
+ # NAME: "RPNLogistic"
9
+ MASK_ON: False
10
+ RESNETS:
11
+ DEPTH: 50
12
+ ROI_HEADS:
13
+ NAME: "StandardROIHeads"
14
+ NUM_CLASSES: 10
15
+ INPUT:
16
+ MIN_SIZE_TRAIN: (480, 512, 544, 576, 608, 640, 672, 704, 736, 768, 800)
17
+ MIN_SIZE_TEST: 800
18
+ DATASETS:
19
+ TRAIN: ('bdd_custom_train',)
20
+ TEST: ('bdd_custom_val',)
21
+ SOLVER:
22
+ IMS_PER_BATCH: 16
23
+ BASE_LR: 0.02
24
+ STEPS: (60000, 80000)
25
+ MAX_ITER: 90000 # 17.4 epochs
26
+ WARMUP_ITERS: 100
27
+ DATALOADER:
28
+ NUM_WORKERS: 8 # Depends on the available memory
models/metadata.py ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ VOC_THING_CLASSES = ['person',
2
+ 'bird',
3
+ 'cat',
4
+ 'cow',
5
+ 'dog',
6
+ 'horse',
7
+ 'sheep',
8
+ 'airplane',
9
+ 'bicycle',
10
+ 'boat',
11
+ 'bus',
12
+ 'car',
13
+ 'motorcycle',
14
+ 'train',
15
+ 'bottle',
16
+ 'chair',
17
+ 'dining table',
18
+ 'potted plant',
19
+ 'couch',
20
+ 'tv',
21
+ ]
22
+
23
+ BDD_THING_CLASSES = ['pedestrian',
24
+ 'rider',
25
+ 'car',
26
+ 'truck',
27
+ 'bus',
28
+ 'train',
29
+ 'motorcycle',
30
+ 'bicycle',
31
+ 'traffic light',
32
+ 'traffic sign']
33
+ KITTI_THING_CLASSES = ["Car", "Pedestrian", "Cyclist", "Van", "Truck", "Tram"]
34
+ SPEED_THING_CLASSES = ['100kph','120kph','20kph','30kph','40kph','5kph','50kph','60kph','70kph','80kph']
35
+ NU_THING_CLASSES = ['car','truck','trailer','bus','construction_vehicle','bicycle','motorcycle','pedestrian','traffic_cone','barrier']
models/regnet.py ADDED
@@ -0,0 +1,84 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from .regnet_model import RegNet
2
+ from .regnet_model import SimpleStem, ResBottleneckBlock
3
+
4
+ from detectron2.modeling.backbone.build import BACKBONE_REGISTRY
5
+ from detectron2.modeling.backbone.fpn import FPN, LastLevelMaxPool
6
+
7
+ from detectron2.layers import (
8
+ Conv2d,
9
+ DeformConv,
10
+ FrozenBatchNorm2d,
11
+ ModulatedDeformConv,
12
+ ShapeSpec,
13
+ get_norm,
14
+ )
15
+
16
+
17
+ # train.cudnn_benchmark = True
18
+
19
+ @BACKBONE_REGISTRY.register()
20
+ def build_regnet_fpn_backbone(cfg, input_shape: ShapeSpec):
21
+ """
22
+ Args:
23
+ cfg: a detectron2 CfgNode
24
+ Returns:
25
+ backbone (Backbone): backbone module, must be a subclass of :class:`Backbone`.
26
+ """
27
+ bottom_up = RegNet(
28
+ stem_class=SimpleStem,
29
+ stem_width=32,
30
+ block_class=ResBottleneckBlock,
31
+ depth=22,
32
+ w_a=31.41,
33
+ w_0=96,
34
+ w_m=2.24,
35
+ group_width=64,
36
+ se_ratio=0.25,
37
+ freeze_at=2,
38
+ norm="FrozenBN",
39
+ out_features=["s1", "s2", "s3", "s4"],
40
+ )
41
+ in_features = cfg.MODEL.FPN.IN_FEATURES
42
+ out_channels = cfg.MODEL.FPN.OUT_CHANNELS
43
+ backbone = FPN(
44
+ bottom_up=bottom_up,
45
+ in_features=in_features,
46
+ out_channels=out_channels,
47
+ norm=cfg.MODEL.FPN.NORM,
48
+ top_block=LastLevelMaxPool(),
49
+ fuse_type=cfg.MODEL.FPN.FUSE_TYPE,
50
+ )
51
+ return backbone
52
+
53
+ @BACKBONE_REGISTRY.register()
54
+ def build_regnetx_fpn_backbone(cfg, input_shape: ShapeSpec):
55
+ """
56
+ Args:
57
+ cfg: a detectron2 CfgNode
58
+ Returns:
59
+ backbone (Backbone): backbone module, must be a subclass of :class:`Backbone`.
60
+ """
61
+ bottom_up = RegNet(
62
+ stem_class=SimpleStem,
63
+ stem_width=32,
64
+ block_class=ResBottleneckBlock,
65
+ depth=23,
66
+ w_a=38.65,
67
+ w_0=96,
68
+ w_m=2.43,
69
+ group_width=40,
70
+ freeze_at=2,
71
+ norm="FrozenBN",
72
+ out_features=["s1", "s2", "s3", "s4"],
73
+ )
74
+ in_features = cfg.MODEL.FPN.IN_FEATURES
75
+ out_channels = cfg.MODEL.FPN.OUT_CHANNELS
76
+ backbone = FPN(
77
+ bottom_up=bottom_up,
78
+ in_features=in_features,
79
+ out_channels=out_channels,
80
+ norm=cfg.MODEL.FPN.NORM,
81
+ top_block=LastLevelMaxPool(),
82
+ fuse_type=cfg.MODEL.FPN.FUSE_TYPE,
83
+ )
84
+ return backbone
models/regnet_model.py ADDED
@@ -0,0 +1,446 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
2
+ """
3
+ Implementation of RegNet models from :paper:`dds` and :paper:`scaling`.
4
+ This code is adapted from https://github.com/facebookresearch/pycls with minimal modifications.
5
+ Some code duplication exists between RegNet and ResNets (e.g., ResStem) in order to simplify
6
+ model loading.
7
+ """
8
+
9
+ import numpy as np
10
+ from torch import nn
11
+
12
+ from detectron2.layers import CNNBlockBase, ShapeSpec, get_norm
13
+
14
+ from detectron2.modeling.backbone import Backbone
15
+
16
+ __all__ = [
17
+ "AnyNet",
18
+ "RegNet",
19
+ "ResStem",
20
+ "SimpleStem",
21
+ "VanillaBlock",
22
+ "ResBasicBlock",
23
+ "ResBottleneckBlock",
24
+ ]
25
+
26
+
27
+ def conv2d(w_in, w_out, k, *, stride=1, groups=1, bias=False):
28
+ """Helper for building a conv2d layer."""
29
+ assert k % 2 == 1, "Only odd size kernels supported to avoid padding issues."
30
+ s, p, g, b = stride, (k - 1) // 2, groups, bias
31
+ return nn.Conv2d(w_in, w_out, k, stride=s, padding=p, groups=g, bias=b)
32
+
33
+
34
+ def gap2d():
35
+ """Helper for building a global average pooling layer."""
36
+ return nn.AdaptiveAvgPool2d((1, 1))
37
+
38
+
39
+ def pool2d(k, *, stride=1):
40
+ """Helper for building a pool2d layer."""
41
+ assert k % 2 == 1, "Only odd size kernels supported to avoid padding issues."
42
+ return nn.MaxPool2d(k, stride=stride, padding=(k - 1) // 2)
43
+
44
+
45
+ def init_weights(m):
46
+ """Performs ResNet-style weight initialization."""
47
+ if isinstance(m, nn.Conv2d):
48
+ # Note that there is no bias due to BN
49
+ fan_out = m.kernel_size[0] * m.kernel_size[1] * m.out_channels
50
+ m.weight.data.normal_(mean=0.0, std=np.sqrt(2.0 / fan_out))
51
+ elif isinstance(m, nn.BatchNorm2d):
52
+ m.weight.data.fill_(1.0)
53
+ m.bias.data.zero_()
54
+ elif isinstance(m, nn.Linear):
55
+ m.weight.data.normal_(mean=0.0, std=0.01)
56
+ m.bias.data.zero_()
57
+
58
+
59
+ class ResStem(CNNBlockBase):
60
+ """ResNet stem for ImageNet: 7x7, BN, AF, MaxPool."""
61
+
62
+ def __init__(self, w_in, w_out, norm, activation_class):
63
+ super().__init__(w_in, w_out, 4)
64
+ self.conv = conv2d(w_in, w_out, 7, stride=2)
65
+ self.bn = get_norm(norm, w_out)
66
+ self.af = activation_class()
67
+ self.pool = pool2d(3, stride=2)
68
+
69
+ def forward(self, x):
70
+ for layer in self.children():
71
+ x = layer(x)
72
+ return x
73
+
74
+
75
+ class SimpleStem(CNNBlockBase):
76
+ """Simple stem for ImageNet: 3x3, BN, AF."""
77
+
78
+ def __init__(self, w_in, w_out, norm, activation_class):
79
+ super().__init__(w_in, w_out, 2)
80
+ self.conv = conv2d(w_in, w_out, 3, stride=2)
81
+ self.bn = get_norm(norm, w_out)
82
+ self.af = activation_class()
83
+
84
+ def forward(self, x):
85
+ for layer in self.children():
86
+ x = layer(x)
87
+ return x
88
+
89
+
90
+ class SE(nn.Module):
91
+ """Squeeze-and-Excitation (SE) block: AvgPool, FC, Act, FC, Sigmoid."""
92
+
93
+ def __init__(self, w_in, w_se, activation_class):
94
+ super().__init__()
95
+ self.avg_pool = gap2d()
96
+ self.f_ex = nn.Sequential(
97
+ conv2d(w_in, w_se, 1, bias=True),
98
+ activation_class(),
99
+ conv2d(w_se, w_in, 1, bias=True),
100
+ nn.Sigmoid(),
101
+ )
102
+
103
+ def forward(self, x):
104
+ return x * self.f_ex(self.avg_pool(x))
105
+
106
+
107
+ class VanillaBlock(CNNBlockBase):
108
+ """Vanilla block: [3x3 conv, BN, Relu] x2."""
109
+
110
+ def __init__(self, w_in, w_out, stride, norm, activation_class, _params):
111
+ super().__init__(w_in, w_out, stride)
112
+ self.a = conv2d(w_in, w_out, 3, stride=stride)
113
+ self.a_bn = get_norm(norm, w_out)
114
+ self.a_af = activation_class()
115
+ self.b = conv2d(w_out, w_out, 3)
116
+ self.b_bn = get_norm(norm, w_out)
117
+ self.b_af = activation_class()
118
+
119
+ def forward(self, x):
120
+ for layer in self.children():
121
+ x = layer(x)
122
+ return x
123
+
124
+
125
+ class BasicTransform(nn.Module):
126
+ """Basic transformation: [3x3 conv, BN, Relu] x2."""
127
+
128
+ def __init__(self, w_in, w_out, stride, norm, activation_class, _params):
129
+ super().__init__()
130
+ self.a = conv2d(w_in, w_out, 3, stride=stride)
131
+ self.a_bn = get_norm(norm, w_out)
132
+ self.a_af = activation_class()
133
+ self.b = conv2d(w_out, w_out, 3)
134
+ self.b_bn = get_norm(norm, w_out)
135
+ self.b_bn.final_bn = True
136
+
137
+ def forward(self, x):
138
+ for layer in self.children():
139
+ x = layer(x)
140
+ return x
141
+
142
+
143
+ class ResBasicBlock(CNNBlockBase):
144
+ """Residual basic block: x + f(x), f = basic transform."""
145
+
146
+ def __init__(self, w_in, w_out, stride, norm, activation_class, params):
147
+ super().__init__(w_in, w_out, stride)
148
+ self.proj, self.bn = None, None
149
+ if (w_in != w_out) or (stride != 1):
150
+ self.proj = conv2d(w_in, w_out, 1, stride=stride)
151
+ self.bn = get_norm(norm, w_out)
152
+ self.f = BasicTransform(w_in, w_out, stride, norm, activation_class, params)
153
+ self.af = activation_class()
154
+
155
+ def forward(self, x):
156
+ x_p = self.bn(self.proj(x)) if self.proj else x
157
+ return self.af(x_p + self.f(x))
158
+
159
+
160
+ class BottleneckTransform(nn.Module):
161
+ """Bottleneck transformation: 1x1, 3x3 [+SE], 1x1."""
162
+
163
+ def __init__(self, w_in, w_out, stride, norm, activation_class, params):
164
+ super().__init__()
165
+ w_b = int(round(w_out * params["bot_mul"]))
166
+ w_se = int(round(w_in * params["se_r"]))
167
+ groups = w_b // params["group_w"]
168
+ self.a = conv2d(w_in, w_b, 1)
169
+ self.a_bn = get_norm(norm, w_b)
170
+ self.a_af = activation_class()
171
+ self.b = conv2d(w_b, w_b, 3, stride=stride, groups=groups)
172
+ self.b_bn = get_norm(norm, w_b)
173
+ self.b_af = activation_class()
174
+ self.se = SE(w_b, w_se, activation_class) if w_se else None
175
+ self.c = conv2d(w_b, w_out, 1)
176
+ self.c_bn = get_norm(norm, w_out)
177
+ self.c_bn.final_bn = True
178
+
179
+ def forward(self, x):
180
+ for layer in self.children():
181
+ x = layer(x)
182
+ return x
183
+
184
+
185
+ class ResBottleneckBlock(CNNBlockBase):
186
+ """Residual bottleneck block: x + f(x), f = bottleneck transform."""
187
+
188
+ def __init__(self, w_in, w_out, stride, norm, activation_class, params):
189
+ super().__init__(w_in, w_out, stride)
190
+ self.proj, self.bn = None, None
191
+ if (w_in != w_out) or (stride != 1):
192
+ self.proj = conv2d(w_in, w_out, 1, stride=stride)
193
+ self.bn = get_norm(norm, w_out)
194
+ self.f = BottleneckTransform(w_in, w_out, stride, norm, activation_class, params)
195
+ self.af = activation_class()
196
+
197
+ def forward(self, x):
198
+ x_p = self.bn(self.proj(x)) if self.proj else x
199
+ return self.af(x_p + self.f(x))
200
+
201
+
202
+ class AnyStage(nn.Module):
203
+ """AnyNet stage (sequence of blocks w/ the same output shape)."""
204
+
205
+ def __init__(self, w_in, w_out, stride, d, block_class, norm, activation_class, params):
206
+ super().__init__()
207
+ for i in range(d):
208
+ block = block_class(w_in, w_out, stride, norm, activation_class, params)
209
+ self.add_module("b{}".format(i + 1), block)
210
+ stride, w_in = 1, w_out
211
+
212
+ def forward(self, x):
213
+ for block in self.children():
214
+ x = block(x)
215
+ return x
216
+
217
+
218
+ class AnyNet(Backbone):
219
+ """AnyNet model. See :paper:`dds`."""
220
+
221
+ def __init__(
222
+ self,
223
+ *,
224
+ stem_class,
225
+ stem_width,
226
+ block_class,
227
+ depths,
228
+ widths,
229
+ group_widths,
230
+ strides,
231
+ bottleneck_ratios,
232
+ se_ratio,
233
+ activation_class,
234
+ freeze_at=0,
235
+ norm="BN",
236
+ out_features=None,
237
+ ):
238
+ """
239
+ Args:
240
+ stem_class (callable): A callable taking 4 arguments (channels in, channels out,
241
+ normalization, callable returning an activation function) that returns another
242
+ callable implementing the stem module.
243
+ stem_width (int): The number of output channels that the stem produces.
244
+ block_class (callable): A callable taking 6 arguments (channels in, channels out,
245
+ stride, normalization, callable returning an activation function, a dict of
246
+ block-specific parameters) that returns another callable implementing the repeated
247
+ block module.
248
+ depths (list[int]): Number of blocks in each stage.
249
+ widths (list[int]): For each stage, the number of output channels of each block.
250
+ group_widths (list[int]): For each stage, the number of channels per group in group
251
+ convolution, if the block uses group convolution.
252
+ strides (list[int]): The stride that each network stage applies to its input.
253
+ bottleneck_ratios (list[float]): For each stage, the ratio of the number of bottleneck
254
+ channels to the number of block input channels (or, equivalently, output channels),
255
+ if the block uses a bottleneck.
256
+ se_ratio (float): The ratio of the number of channels used inside the squeeze-excitation
257
+ (SE) module to it number of input channels, if SE the block uses SE.
258
+ activation_class (callable): A callable taking no arguments that returns another
259
+ callable implementing an activation function.
260
+ freeze_at (int): The number of stages at the beginning to freeze.
261
+ see :meth:`freeze` for detailed explanation.
262
+ norm (str or callable): normalization for all conv layers.
263
+ See :func:`layers.get_norm` for supported format.
264
+ out_features (list[str]): name of the layers whose outputs should
265
+ be returned in forward. RegNet's use "stem" and "s1", "s2", etc for the stages after
266
+ the stem. If None, will return the output of the last layer.
267
+ """
268
+ super().__init__()
269
+ self.stem = stem_class(3, stem_width, norm, activation_class)
270
+
271
+ current_stride = self.stem.stride
272
+ self._out_feature_strides = {"stem": current_stride}
273
+ self._out_feature_channels = {"stem": self.stem.out_channels}
274
+ self.stages_and_names = []
275
+ prev_w = stem_width
276
+
277
+ for i, (d, w, s, b, g) in enumerate(
278
+ zip(depths, widths, strides, bottleneck_ratios, group_widths)
279
+ ):
280
+ params = {"bot_mul": b, "group_w": g, "se_r": se_ratio}
281
+ stage = AnyStage(prev_w, w, s, d, block_class, norm, activation_class, params)
282
+ name = "s{}".format(i + 1)
283
+ self.add_module(name, stage)
284
+ self.stages_and_names.append((stage, name))
285
+ self._out_feature_strides[name] = current_stride = int(
286
+ current_stride * np.prod([k.stride for k in stage.children()])
287
+ )
288
+ self._out_feature_channels[name] = list(stage.children())[-1].out_channels
289
+ prev_w = w
290
+
291
+ self.apply(init_weights)
292
+
293
+ if out_features is None:
294
+ out_features = [name]
295
+ self._out_features = out_features
296
+ assert len(self._out_features)
297
+ children = [x[0] for x in self.named_children()]
298
+ for out_feature in self._out_features:
299
+ assert out_feature in children, "Available children: {} does not include {}".format(
300
+ ", ".join(children), out_feature
301
+ )
302
+ self.freeze(freeze_at)
303
+
304
+ def forward(self, x):
305
+ """
306
+ Args:
307
+ x: Tensor of shape (N,C,H,W). H, W must be a multiple of ``self.size_divisibility``.
308
+ Returns:
309
+ dict[str->Tensor]: names and the corresponding features
310
+ """
311
+ assert x.dim() == 4, f"Model takes an input of shape (N, C, H, W). Got {x.shape} instead!"
312
+ outputs = {}
313
+ x = self.stem(x)
314
+ if "stem" in self._out_features:
315
+ outputs["stem"] = x
316
+ for stage, name in self.stages_and_names:
317
+ x = stage(x)
318
+ if name in self._out_features:
319
+ outputs[name] = x
320
+ return outputs
321
+
322
+ def output_shape(self):
323
+ return {
324
+ name: ShapeSpec(
325
+ channels=self._out_feature_channels[name], stride=self._out_feature_strides[name]
326
+ )
327
+ for name in self._out_features
328
+ }
329
+
330
+ def freeze(self, freeze_at=0):
331
+ """
332
+ Freeze the first several stages of the model. Commonly used in fine-tuning.
333
+ Layers that produce the same feature map spatial size are defined as one
334
+ "stage" by :paper:`FPN`.
335
+ Args:
336
+ freeze_at (int): number of stages to freeze.
337
+ `1` means freezing the stem. `2` means freezing the stem and
338
+ one residual stage, etc.
339
+ Returns:
340
+ nn.Module: this model itself
341
+ """
342
+ if freeze_at >= 1:
343
+ self.stem.freeze()
344
+ for idx, (stage, _) in enumerate(self.stages_and_names, start=2):
345
+ if freeze_at >= idx:
346
+ for block in stage.children():
347
+ block.freeze()
348
+ return self
349
+
350
+
351
+ def adjust_block_compatibility(ws, bs, gs):
352
+ """Adjusts the compatibility of widths, bottlenecks, and groups."""
353
+ assert len(ws) == len(bs) == len(gs)
354
+ assert all(w > 0 and b > 0 and g > 0 for w, b, g in zip(ws, bs, gs))
355
+ vs = [int(max(1, w * b)) for w, b in zip(ws, bs)]
356
+ gs = [int(min(g, v)) for g, v in zip(gs, vs)]
357
+ ms = [np.lcm(g, b) if b > 1 else g for g, b in zip(gs, bs)]
358
+ vs = [max(m, int(round(v / m) * m)) for v, m in zip(vs, ms)]
359
+ ws = [int(v / b) for v, b in zip(vs, bs)]
360
+ assert all(w * b % g == 0 for w, b, g in zip(ws, bs, gs))
361
+ return ws, bs, gs
362
+
363
+
364
+ def generate_regnet_parameters(w_a, w_0, w_m, d, q=8):
365
+ """Generates per stage widths and depths from RegNet parameters."""
366
+ assert w_a >= 0 and w_0 > 0 and w_m > 1 and w_0 % q == 0
367
+ # Generate continuous per-block ws
368
+ ws_cont = np.arange(d) * w_a + w_0
369
+ # Generate quantized per-block ws
370
+ ks = np.round(np.log(ws_cont / w_0) / np.log(w_m))
371
+ ws_all = w_0 * np.power(w_m, ks)
372
+ ws_all = np.round(np.divide(ws_all, q)).astype(int) * q
373
+ # Generate per stage ws and ds (assumes ws_all are sorted)
374
+ ws, ds = np.unique(ws_all, return_counts=True)
375
+ # Compute number of actual stages and total possible stages
376
+ num_stages, total_stages = len(ws), ks.max() + 1
377
+ # Convert numpy arrays to lists and return
378
+ ws, ds, ws_all, ws_cont = (x.tolist() for x in (ws, ds, ws_all, ws_cont))
379
+ return ws, ds, num_stages, total_stages, ws_all, ws_cont
380
+
381
+
382
+ class RegNet(AnyNet):
383
+ """RegNet model. See :paper:`dds`."""
384
+
385
+ def __init__(
386
+ self,
387
+ *,
388
+ stem_class,
389
+ stem_width,
390
+ block_class,
391
+ depth,
392
+ w_a,
393
+ w_0,
394
+ w_m,
395
+ group_width,
396
+ stride=2,
397
+ bottleneck_ratio=1.0,
398
+ se_ratio=0.0,
399
+ activation_class=None,
400
+ freeze_at=0,
401
+ norm="BN",
402
+ out_features=None,
403
+ ):
404
+ """
405
+ Build a RegNet from the parameterization described in :paper:`dds` Section 3.3.
406
+ Args:
407
+ See :class:`AnyNet` for arguments that are not listed here.
408
+ depth (int): Total number of blocks in the RegNet.
409
+ w_a (float): Factor by which block width would increase prior to quantizing block widths
410
+ by stage. See :paper:`dds` Section 3.3.
411
+ w_0 (int): Initial block width. See :paper:`dds` Section 3.3.
412
+ w_m (float): Parameter controlling block width quantization.
413
+ See :paper:`dds` Section 3.3.
414
+ group_width (int): Number of channels per group in group convolution, if the block uses
415
+ group convolution.
416
+ bottleneck_ratio (float): The ratio of the number of bottleneck channels to the number
417
+ of block input channels (or, equivalently, output channels), if the block uses a
418
+ bottleneck.
419
+ stride (int): The stride that each network stage applies to its input.
420
+ """
421
+ ws, ds = generate_regnet_parameters(w_a, w_0, w_m, depth)[0:2]
422
+ ss = [stride for _ in ws]
423
+ bs = [bottleneck_ratio for _ in ws]
424
+ gs = [group_width for _ in ws]
425
+ ws, bs, gs = adjust_block_compatibility(ws, bs, gs)
426
+
427
+ def default_activation_class():
428
+ return nn.ReLU(inplace=True)
429
+
430
+ super().__init__(
431
+ stem_class=stem_class,
432
+ stem_width=stem_width,
433
+ block_class=block_class,
434
+ depths=ds,
435
+ widths=ws,
436
+ strides=ss,
437
+ group_widths=gs,
438
+ bottleneck_ratios=bs,
439
+ se_ratio=se_ratio,
440
+ activation_class=default_activation_class
441
+ if activation_class is None
442
+ else activation_class,
443
+ freeze_at=freeze_at,
444
+ norm=norm,
445
+ out_features=out_features,
446
+ )
models/weights/model_final_resnet_kitti.pth ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:e577cbf96b80d227eb4959271de68c69f72d653a7ed406cda5c17c4b83bfd388
3
+ size 330233039
monitors/kitti/resnet/kmeans/1.pkl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:fc623c2a3c64b52fc93d099f365352d4d206d8f94298f2cee7c40c94ddba7b8a
3
+ size 136810565
monitors/kitti/resnet/kmeans/4.pkl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:d88075b898e5bcf962ff03babe51150db2e5b8287777c878e2e4854134b47d39
3
+ size 152229171
monitors/kitti/resnet/kmeans/5.pkl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:84b571641d7207d14e31390a465f3bfbe494da90deac82d9e68cd6d3574f376b
3
+ size 157291017
monitors/kitti/resnet/kmeans/6.pkl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:6d85e0d97dccfba7f618e87813db07514d2dacf8ab5655a195da0ad5f2091856
3
+ size 160503921
requirements.txt ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ torch
2
+ detectron2
3
+ numpy
4
+ opencv-python
5
+ h5py
6
+ pickle
7
+ gradio
8
+ fiftyone
9
+ tqdm
10
+ matplotlib
11
+ grad-cam
12
+ scikit-learn
runtime_monitors/Monitor.py ADDED
@@ -0,0 +1,56 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+
4
+ from abstractions import *
5
+ import pickle
6
+ import numpy as np
7
+
8
+ class Monitor(object):
9
+
10
+ def __init__(self, good_ref=None):
11
+ # self.abs_type = abs_type
12
+ self.good_ref = good_ref
13
+
14
+
15
+ def set_reference(self, good_ref):
16
+ self.good_ref = good_ref
17
+
18
+ # def get_identity(self):
19
+ # print("Monitor for network:" + self.netName + "class: " + str(self.classification) + "at layer " + str(self.location))
20
+
21
+
22
+ def make_verdicts(self, features):
23
+ if len(self.good_ref):
24
+ verdicts = ref_query(features, self.good_ref)
25
+ else:
26
+ raise RuntimeError("No reference exists!")
27
+ return verdicts
28
+
29
+ # def make_verdicts_delta(self, features, delta):
30
+ # if len(self.good_ref):
31
+ # verdicts = ref_query_delta(features, self.good_ref, delta)
32
+ # else:
33
+ # raise RuntimeError("No reference exists!")
34
+ # return verdicts
35
+
36
+
37
+ def ref_query(features, reference):
38
+ query_results = [boxes_query(x, reference) for x in features]
39
+ return query_results
40
+
41
+ # def ref_query_delta(features, reference, delta):
42
+ # query_results = [boxes_query_delta(x, reference, delta) for x in features]
43
+ # return query_results
44
+
45
+
46
+ # def query_infusion(in_good_ref, in_bad_ref):
47
+ # if len(in_good_ref) == len(in_bad_ref): #0: acceptance (true, false), 1: rejection (false, true or false), 2: uncertainty (true, true)
48
+ # verdicts = np.zeros(len(in_good_ref), dtype=int)
49
+ # for i in range(len(in_good_ref)):
50
+ # if not in_good_ref[i]:
51
+ # verdicts[i] = 1
52
+ # elif in_bad_ref[i]:
53
+ # verdicts[i] = 2
54
+ # return verdicts
55
+ # else:
56
+ # print("Error: IllegalArgument")
runtime_monitors/__init__.py ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ from .Monitor import *
2
+
train_feats/kitti/resnet/kitti-train_feats_tp_dict.pickle ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:59624dd3527292e3cde0107ecc38f7fc85bb96bbe3b9a3458f6a7dab25b2fe76
3
+ size 126263743
util/Clustering.py ADDED
@@ -0,0 +1,138 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python
2
+ # coding: utf-8
3
+
4
+ import numpy as np
5
+ import pandas as pd
6
+ import time
7
+ import os.path
8
+ # from sklearnex import patch_sklearn, unpatch_sklearn
9
+ # patch_sklearn()
10
+ from sklearn.cluster import KMeans
11
+ from sklearn.cluster import MeanShift
12
+
13
+
14
+ # values: a two-dimensional array, m number of n-dimensional vectors to be clustered;
15
+ def modified_kmeans_cluster(values_to_cluster, threshold, k_start, n_clusters=None):
16
+ if n_clusters is not None:
17
+ kmeans = KMeans(n_clusters=n_clusters, n_init="auto", random_state=0).fit(values_to_cluster)
18
+ return kmeans.labels_
19
+ else:
20
+ n_clusters = k_start
21
+ n_values = len(values_to_cluster)
22
+ assert n_values > 0
23
+ kmeans = KMeans(n_clusters=n_clusters, n_init="auto", random_state=0).fit(values_to_cluster)
24
+ inertias = [kmeans.inertia_]
25
+ while n_values > n_clusters:
26
+ n_clusters_new = n_clusters + 1
27
+ kmeans_new = KMeans(n_clusters=n_clusters_new, n_init="auto", random_state=0).fit(values_to_cluster)
28
+ inertias.append(kmeans_new.inertia_)
29
+ if terminate_clustering(inertias, threshold):
30
+ break
31
+ kmeans = kmeans_new
32
+ n_clusters += 1
33
+ return kmeans.labels_
34
+
35
+
36
+ def terminate_clustering(inertias, threshold):
37
+ # method: compute relative improvement toward previous step
38
+ assert len(inertias) > 1
39
+ improvement = 1 - (inertias[-1] / inertias[-2])
40
+ return improvement < threshold
41
+
42
+
43
+
44
+
45
+ def cluster_existed_features(network_folder_path, classes, layers_indexes, taus):
46
+ appendixes = ["_correctly_classified_features.csv", "_incorrectly_classified_features.csv"]
47
+ product = ((i, y, appendix) for i in layers_indexes for y in classes for appendix in appendixes)
48
+
49
+ for i, y, appendix in product:
50
+ start_time = time.time()
51
+ # load data for class y at layer minus i
52
+ features_file_path = network_folder_path +"Layer_minus_" + str(i) + "/class_" + str(y) + appendix
53
+ df = pd.read_csv(features_file_path)
54
+ index_values = df["index"].to_numpy()
55
+ values_to_cluster = df[df.columns[3:]].to_numpy()
56
+
57
+ if len(values_to_cluster):
58
+ # specify path and then load existing clustering results
59
+ k_and_taus = dict()
60
+ taus_existed = []
61
+ clustering_results = pd.DataFrame(df, columns=["index", "true_label", "pred_label"])
62
+ clustering_results_path = network_folder_path + "Layer_minus_" + str(i) + "/clustering_results_class_" + str(y) + appendix
63
+
64
+ if os.path.exists(clustering_results_path):
65
+ clustering_results = pd.read_csv(clustering_results_path)
66
+ for col in clustering_results.columns[3:]:
67
+ k_and_taus[col] = clustering_results[col].max() + 1
68
+
69
+ # update the existing values of tau
70
+ taus_existed = [float(key) for key in k_and_taus.keys()]
71
+
72
+ # remove existing tau from list existed_taus
73
+ taus_new = [tau for tau in taus if tau not in taus_existed]
74
+
75
+ # iterate every tau to cluster the given data
76
+ for tau in taus_new:
77
+ # fix starting searching point
78
+ k_start = 1
79
+ bigger_taus = [x for x in taus_existed if x > tau]
80
+ if len(bigger_taus):
81
+ tau_closest = min(bigger_taus)
82
+ k_start = k_and_taus[str(tau_closest)]
83
+
84
+ # start to cluster
85
+ cluster_labels = modified_kmeans_cluster(values_to_cluster, tau, k_start)
86
+ clustering_results[str(tau)] = cluster_labels
87
+ taus_existed.append(tau)
88
+ k_and_taus[str(tau)] = max(cluster_labels) + 1
89
+
90
+ clustering_results.to_csv(clustering_results_path, index = False)
91
+ elapsed_time = time.time() - start_time
92
+ print("file:" + "Layer_minus_" + str(i) + "_class_" + str(y) + appendix + ",", "lasting time:", elapsed_time, "seconds")
93
+
94
+
95
+ def features_clustering(features, taus, nb_clusters):
96
+ start_time = time.time()
97
+ values_to_cluster = features
98
+
99
+ if len(values_to_cluster):
100
+ # specify path and then load existing clustering results
101
+ k_and_taus = dict()
102
+ taus_existed = []
103
+
104
+
105
+ # if os.path.exists(clustering_results_path):
106
+ # clustering_results = pd.read_csv(clustering_results_path)
107
+ # for col in clustering_results.columns[3:]:
108
+ # k_and_taus[col] = clustering_results[col].max() + 1
109
+ # else:
110
+ # clustering_results = pd.DataFrame()
111
+
112
+ # update the existing values of tau
113
+ taus_existed = [float(key) for key in k_and_taus.keys()]
114
+
115
+ # remove existing tau from list existed_taus
116
+ taus_new = [tau for tau in taus if tau not in taus_existed]
117
+ clustering_results = dict()
118
+ # iterate every tau to cluster the given data
119
+ for tau in taus_new:
120
+ # fix starting searching point
121
+ k_start = 1
122
+ bigger_taus = [x for x in taus_existed if x > tau]
123
+ if len(bigger_taus):
124
+ tau_closest = min(bigger_taus)
125
+ k_start = k_and_taus[str(tau_closest)]
126
+
127
+ # start to cluster
128
+ cluster_labels = modified_kmeans_cluster(values_to_cluster, tau, k_start, nb_clusters)
129
+ clustering_results[str(tau)] = cluster_labels
130
+ taus_existed.append(tau)
131
+ k_and_taus[str(tau)] = max(cluster_labels) + 1
132
+
133
+ # clustering_results.to_csv(clustering_results_path, index = False)
134
+ elapsed_time = time.time() - start_time
135
+ # print("clustering time:", elapsed_time, "seconds")
136
+ return clustering_results
137
+
138
+
util/Monitor_construction.py ADDED
@@ -0,0 +1,144 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ import numpy as np
3
+ import pandas as pd
4
+ import pickle
5
+ import os
6
+ import sys
7
+ # append the path of the parent directory
8
+ sys.path.append("..")
9
+
10
+ from abstractions import *
11
+ from runtime_monitors import *
12
+
13
+
14
+ # def monitors_offline_construction(network_name, network_folder_path, classes, layers_indexes, taus):
15
+ # appendixes = ["_correctly_classified_features.csv", "_incorrectly_classified_features.csv"]
16
+ # product = ((i, y) for i in layers_indexes for y in classes)
17
+
18
+ # for i, y in product:
19
+
20
+ # # load obtained features to creat reference
21
+ # path_bad_features = network_folder_path +"Layer_minus_" + str(i) + "/class_" + str(y) + appendixes[1]
22
+ # path_good_features = network_folder_path +"Layer_minus_" + str(i) + "/class_" + str(y) + appendixes[0]
23
+
24
+ # bad_feat_clustering_results = []
25
+ # good_feat_clustering_results = []
26
+
27
+
28
+ # if os.path.exists(path_bad_features):
29
+ # df_bad_features = pd.read_csv(path_bad_features)
30
+ # bad_features_to_cluster = df_bad_features[df_bad_features.columns[3:]].to_numpy()
31
+ # bad_features_index = df_bad_features["index"].to_numpy()
32
+
33
+ # # load clustering results to partition the features
34
+ # bad_feat_clustering_results_path = network_folder_path + "Layer_minus_" + str(i) + "/clustering_results_class_" + str(y) + appendixes[1]
35
+ # if os.path.exists(bad_feat_clustering_results_path):
36
+ # bad_feat_clustering_results = pd.read_csv(bad_feat_clustering_results_path)
37
+
38
+ # if os.path.exists(path_good_features):
39
+ # df_good_features = pd.read_csv(path_good_features)
40
+ # good_features_to_cluster = df_good_features[df_good_features.columns[3:]].to_numpy()
41
+ # good_features_index = df_good_features["index"].to_numpy()
42
+
43
+
44
+ # # load clustering results to partition the features
45
+ # good_feat_clustering_results_path = network_folder_path + "Layer_minus_" + str(i) + "/clustering_results_class_" + str(y) + appendixes[0]
46
+ # n_dim = good_features_to_cluster.shape[1]
47
+ # if os.path.exists(good_feat_clustering_results_path):
48
+ # good_feat_clustering_results = pd.read_csv(good_feat_clustering_results_path)
49
+
50
+ # for tau in taus:
51
+ # good_loc_boxes = []
52
+ # bad_loc_boxes = []
53
+
54
+ # if len(bad_feat_clustering_results):
55
+ # # load clustering result related to tau
56
+ # bad_feat_clustering_result = bad_feat_clustering_results[str(tau)]
57
+ # # determine the labels of clusters
58
+ # bad_num_clusters = np.amax(bad_feat_clustering_result) + 1
59
+ # bad_clustering_labels = np.arange(bad_num_clusters)
60
+
61
+ # # extract the indices of vectors in a cluster
62
+ # bad_clusters_indices = []
63
+ # for k in bad_clustering_labels:
64
+ # bad_indices_cluster_k, = np.where(bad_feat_clustering_result == k)
65
+ # bad_clusters_indices.append(bad_indices_cluster_k)
66
+
67
+ # # creat local box for each cluster
68
+ # bad_loc_boxes = [Box() for i in bad_clustering_labels]
69
+ # for j in range(len(bad_loc_boxes)):
70
+ # bad_points_j = [(bad_features_index[i], bad_features_to_cluster[i]) for i in bad_clusters_indices[j]]
71
+ # bad_loc_boxes[j].build(n_dim, bad_points_j)
72
+
73
+
74
+ # if len(good_feat_clustering_results):
75
+ # # load clustering result related to tau
76
+ # good_feat_clustering_result = good_feat_clustering_results[str(tau)]
77
+ # # determine the labels of clusters
78
+ # good_num_clusters = np.amax(good_feat_clustering_result) + 1
79
+ # good_clustering_labels = np.arange(good_num_clusters)
80
+
81
+ # # extract the indices of vectors in a cluster
82
+ # good_clusters_indices = []
83
+ # for k in good_clustering_labels:
84
+ # good_indices_cluster_k, = np.where(good_feat_clustering_result == k)
85
+ # good_clusters_indices.append(good_indices_cluster_k)
86
+
87
+ # # creat local box for each cluster
88
+ # good_loc_boxes = [Box() for i in good_clustering_labels]
89
+ # for j in range(len(good_loc_boxes)):
90
+ # good_points_j = [(good_features_index[i], good_features_to_cluster[i]) for i in good_clusters_indices[j]]
91
+ # good_loc_boxes[j].build(n_dim, good_points_j)
92
+
93
+ # # creat the monitor for class y at layer i
94
+ # monitor_y_i = Monitor("Box", network_name, y, i, good_ref=good_loc_boxes, bad_ref=bad_loc_boxes)
95
+ # # save the created monitor
96
+ # monitor_stored_folder_path = network_folder_path + "Monitors/"
97
+ # if not os.path.exists(monitor_stored_folder_path):
98
+ # os.makedirs(monitor_stored_folder_path)
99
+ # monitor_stored_path = monitor_stored_folder_path + network_name + "_monitor_for_class_" + str(y) + "_at_layer_minus_" + str(i) + "_tau_" + str(tau) + ".pkl"
100
+ # with open(monitor_stored_path, 'wb') as f:
101
+ # pickle.dump(monitor_y_i, f)
102
+
103
+
104
+ def monitor_construction_from_features(features, taus, clustering_results, class_name, monitor_saving_folder):
105
+ # if os.path.exists(clustering_result_path):
106
+ # clustering_results = pd.read_csv(clustering_result_path)
107
+ # else:
108
+ # raise RuntimeError("Please partition your data first!")
109
+
110
+ for tau in taus:
111
+ loc_boxes = []
112
+
113
+ if len(features):
114
+ n_dim = features.shape[1]
115
+
116
+ # load clustering result related to tau
117
+ clustering_result = clustering_results[str(tau)]
118
+ # determine the labels of clusters
119
+ num_clusters = np.amax(clustering_result) + 1
120
+ clustering_labels = np.arange(num_clusters)
121
+
122
+ # extract the indices of vectors in a cluster
123
+ clusters_indices = []
124
+ for k in clustering_labels:
125
+ indices_cluster_k, = np.where(clustering_result == k)
126
+ clusters_indices.append(indices_cluster_k)
127
+
128
+ # creat local box for each cluster
129
+ loc_boxes = [Box() for i in clustering_labels]
130
+ for j in range(len(loc_boxes)):
131
+ points_j = [(i, features[i]) for i in clusters_indices[j]]
132
+ loc_boxes[j].build(n_dim, points_j)
133
+ else:
134
+ raise RuntimeError("There exists no feature for building monitor!!")
135
+
136
+ # creat the monitor for class y at layer i
137
+ monitor = Monitor(good_ref=loc_boxes)
138
+ # save the created monitor
139
+ if not os.path.exists(monitor_saving_folder):
140
+ os.makedirs(monitor_saving_folder)
141
+ monitor_saving_path = monitor_saving_folder + "monitor_for_clustering_parameter" + "_tau_" + str(tau) + ".pkl"
142
+ with open(monitor_saving_path, 'wb') as f:
143
+ pickle.dump(monitor, f)
144
+
util/__init__.py ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ from .Clustering import *
2
+ from .Monitor_construction import *
3
+
utils_clustering.py ADDED
@@ -0,0 +1,61 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from util import *
2
+ from sklearn.cluster import KMeans, SpectralClustering, DBSCAN
3
+ from sklearn import metrics
4
+ import numpy as np
5
+ import warnings
6
+ import os
7
+ from util.Monitor_construction import Box
8
+ from runtime_monitors import *
9
+ def k_means_cluster(data, n_clusters):
10
+ kmeans = KMeans(n_clusters=n_clusters, init='k-means++', random_state=0, n_init="auto")
11
+ kmeans.fit_predict(data)
12
+
13
+ lbs = kmeans.labels_ # cluster labels
14
+ clusters = dict()
15
+ for lb in set(lbs):
16
+ idx = np.where(lbs == lb)[0]
17
+ clusters[lb] = list(zip(idx, data[idx]))
18
+ return clusters
19
+
20
+ def spectral_cluster(data, n_clusters):
21
+ n_neighbors = min(n_clusters, 10)
22
+ spectral = SpectralClustering(n_clusters=n_clusters, affinity='nearest_neighbors', n_neighbors=n_neighbors,
23
+ gamma=1.0, eigen_solver="arpack", random_state=0)
24
+
25
+ # catch warnings related to kneighbors_graph
26
+ with warnings.catch_warnings():
27
+ warnings.filterwarnings(
28
+ "ignore",
29
+ message="the number of connected components of the "
30
+ + "connectivity matrix is [0-9]{1,2}"
31
+ + " > 1. Completing it to avoid stopping the tree early.",
32
+ category=UserWarning,
33
+ )
34
+ warnings.filterwarnings(
35
+ "ignore",
36
+ message="Graph is not fully connected, spectral embedding"
37
+ + " may not work as expected.",
38
+ category=UserWarning,
39
+ )
40
+ spectral = spectral.fit(data)
41
+
42
+ lbs = spectral.labels_ # cluster labels
43
+ clusters = dict()
44
+ for lb in set(lbs):
45
+ idx = np.where(lbs == lb)[0]
46
+ clusters[lb] = list(zip(idx, data[idx]))
47
+ return clusters
48
+
49
+ def dbscan_cluster(data, eps, min_samples):
50
+
51
+ db = DBSCAN(eps=eps, min_samples=min_samples).fit(data)
52
+ lbs = db.labels_ # cluster labels
53
+ n_cls = len(set(lbs)) - (1 if -1 in lbs else 0) # number of clusters
54
+ n_noise = list(lbs).count(-1)
55
+
56
+ clusters = dict()
57
+ for lb in set(lbs):
58
+ idx = np.where(lbs == lb)[0]
59
+ clusters[lb] = list(zip(idx, data[idx]))
60
+
61
+ return clusters
val_feats/kitti/resnet/ID-bdd-OOD-coco_feats_fp_dict.pickle ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:e0a450635f32e07fe9ff5a4a04dbda1c8188984fb5b2443ff117dc53d7df778d
3
+ size 53633470
val_feats/kitti/resnet/OOD-open_feats_fp_dict.pickle ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:0d18d1c5d2f94d059979bce4c1f812707cc3539f798d630b5fc8de2f7e7c80b6
3
+ size 45707710
val_feats/kitti/resnet/kitti-val_feats_fp_dict.pickle ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:636fc37b49df78c5670ff65e0138640cf873985c06df15fc2fa918b256b39901
3
+ size 8860091
val_feats/kitti/resnet/kitti-val_feats_tp_dict.pickle ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:1d7ec3e6034d0c7164f50aab06397cb19176ef5466f766b53d0e3099369ef82a
3
+ size 31691197
val_feats/kitti/resnet/voc-ood_feats_fp_dict.pickle ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:4c6e2edb71aa70fdc42e49db901e7fbf3143d98c060c7d9c8ef8eb543be05b6f
3
+ size 45461950