def update_stats(self): """ Update the model with precise statistics. Users can manually call this method. """ if self._disabled: return def data_loader(): for num_iter in itertools.count(1): if num_iter % 100 == 0: self._logger.info( "Running precise-BN ... {}/{} iterations.".format( num_iter, self._num_iter)) # This way we can reuse the same iterator try: item = next(self._data_iter) except StopIteration: self._data_iter = iter(self._data_loader) item = next(self._data_iter) yield item with EventStorage(): # capture events in a new storage to discard them self._logger.info( "Running precise-BN for {} iterations... ".format( self._num_iter) + "Note that this could produce different statistics every time." ) update_bn_stats(self._model, data_loader(), self._num_iter)
def testScalarMismatchedPeriod(self): with tempfile.TemporaryDirectory( prefix="cvpods_tests") as dir, EventStorage() as storage: json_file = os.path.join(dir, "test.json") writer = JSONWriter(json_file) for k in range(60): if k % 17 == 0: # write in a differnt period storage.put_scalar("key2", k, smoothing_hint=False) storage.put_scalar("key", k, smoothing_hint=False) if (k + 1) % 20 == 0: writer.write() storage.step() writer.close() with open(json_file) as f: data = [json.loads(line) for line in f] print([int(k.get("key2", 0)) for k in data]) print([int(k.get("key", 0)) for k in data]) print([int(k.get("iteration", 0)) for k in data]) self.assertTrue([int(k.get("key2", 0)) for k in data] == [17, 0, 34, 0, 51, 0]) self.assertTrue([int(k.get("key", 0)) for k in data] == [0, 19, 0, 39, 0, 59]) self.assertTrue([int(k["iteration"]) for k in data] == [17, 19, 34, 39, 51, 59])
def test_rroi_heads(self): torch.manual_seed(121) cfg = RCNNConfig() cfg.MODEL.ANCHOR_GENERATOR.NAME = "RotatedAnchorGenerator" # PROPOSAL_GENERATOR: "RRPN" # ROI_HEADS: "RROIHeads" # ROI_BOX_HEAD.NAME: "FastRCNNConvFCHead" def build_box_head(cfg, input_shape): return FastRCNNConvFCHead(cfg, input_shape) cfg.build_box_head = build_box_head cfg.MODEL.RESNETS.DEPTH = 50 cfg.MODEL.ROI_BOX_HEAD.NUM_FC = 2 cfg.MODEL.RPN.BBOX_REG_WEIGHTS = (1, 1, 1, 1, 1) cfg.MODEL.RPN.HEAD_NAME = "StandardRPNHead" cfg.MODEL.ROI_BOX_HEAD.POOLER_TYPE = "ROIAlignRotated" cfg.MODEL.ROI_BOX_HEAD.BBOX_REG_WEIGHTS = (10, 10, 5, 5, 1) backbone = build_backbone(cfg) num_images = 2 images_tensor = torch.rand(num_images, 20, 30) image_sizes = [(10, 10), (20, 30)] images = ImageList(images_tensor, image_sizes) num_channels = 1024 features = {"res4": torch.rand(num_images, num_channels, 1, 2)} image_shape = (15, 15) gt_boxes0 = torch.tensor([[2, 2, 2, 2, 30], [4, 4, 4, 4, 0]], dtype=torch.float32) gt_instance0 = Instances(image_shape) gt_instance0.gt_boxes = RotatedBoxes(gt_boxes0) gt_instance0.gt_classes = torch.tensor([2, 1]) gt_boxes1 = torch.tensor([[1.5, 5.5, 1, 3, 0], [8.5, 4, 3, 2, -50]], dtype=torch.float32) gt_instance1 = Instances(image_shape) gt_instance1.gt_boxes = RotatedBoxes(gt_boxes1) gt_instance1.gt_classes = torch.tensor([1, 2]) gt_instances = [gt_instance0, gt_instance1] # currently using DefaultAnchorGenerator in RRPN proposal_generator = RRPN(cfg, backbone.output_shape()) roi_heads = RROIHeads(cfg, backbone.output_shape()) with EventStorage(): # capture events in a new storage to discard them proposals, proposal_losses = proposal_generator(images, features, gt_instances) _, detector_losses = roi_heads(images, features, proposals, gt_instances) expected_losses = { "loss_cls": torch.tensor(4.381618499755859), "loss_box_reg": torch.tensor(0.0011829272843897343), } for name in expected_losses.keys(): err_msg = "detector_losses[{}] = {}, expected losses = {}".format( name, detector_losses[name], expected_losses[name] ) self.assertTrue(torch.allclose(detector_losses[name], expected_losses[name]), err_msg)
def testScalar(self): with tempfile.TemporaryDirectory( prefix="cvpods_tests") as dir, EventStorage() as storage: json_file = os.path.join(dir, "test.json") writer = JSONWriter(json_file) for k in range(60): storage.put_scalar("key", k, smoothing_hint=False) if (k + 1) % 20 == 0: writer.write() storage.step() writer.close() with open(json_file) as f: data = [json.loads(line) for line in f] self.assertTrue([int(k["key"]) for k in data] == [19, 39, 59])
def test_roi_heads(self): torch.manual_seed(121) cfg = RCNNConfig() # PROPOSAL_GENERATOR: "RPN" # ROI_HEADS: "StandardROIHeads" # ROI_BOX_HEAD: "FastRCNNConvFCHead" cfg.MODEL.RESNETS.DEPTH = 50 cfg.MODEL.ROI_BOX_HEAD.NUM_FC = 2 cfg.MODEL.ROI_BOX_HEAD.POOLER_TYPE = "ROIAlignV2" cfg.MODEL.ROI_BOX_HEAD.BBOX_REG_WEIGHTS = (10, 10, 5, 5) def build_box_head(cfg, input_shape): return FastRCNNConvFCHead(cfg, input_shape) cfg.build_box_head = build_box_head backbone = build_backbone(cfg) num_images = 2 images_tensor = torch.rand(num_images, 20, 30) image_sizes = [(10, 10), (20, 30)] images = ImageList(images_tensor, image_sizes) num_channels = 1024 features = {"res4": torch.rand(num_images, num_channels, 1, 2)} image_shape = (15, 15) gt_boxes0 = torch.tensor([[1, 1, 3, 3], [2, 2, 6, 6]], dtype=torch.float32) gt_instance0 = Instances(image_shape) gt_instance0.gt_boxes = Boxes(gt_boxes0) gt_instance0.gt_classes = torch.tensor([2, 1]) gt_boxes1 = torch.tensor([[1, 5, 2, 8], [7, 3, 10, 5]], dtype=torch.float32) gt_instance1 = Instances(image_shape) gt_instance1.gt_boxes = Boxes(gt_boxes1) gt_instance1.gt_classes = torch.tensor([1, 2]) gt_instances = [gt_instance0, gt_instance1] proposal_generator = RPN(cfg, backbone.output_shape()) roi_heads = StandardROIHeads(cfg, backbone.output_shape()) with EventStorage(): # capture events in a new storage to discard them proposals, proposal_losses = proposal_generator(images, features, gt_instances) _, detector_losses = roi_heads(images, features, proposals, gt_instances) expected_losses = { "loss_cls": torch.tensor(4.4236516953), "loss_box_reg": torch.tensor(0.0091214813), } for name in expected_losses.keys(): self.assertTrue(torch.allclose(detector_losses[name], expected_losses[name]))
def test_fast_rcnn_empty_batch(self, device="cpu"): cfg = RCNNConfig() cfg.MODEL.ROI_BOX_HEAD.BBOX_REG_WEIGHTS = (10, 10, 5, 5) box2box_transform = Box2BoxTransform(weights=cfg.MODEL.ROI_BOX_HEAD.BBOX_REG_WEIGHTS) logits = torch.randn(0, 100, requires_grad=True, device=device) deltas = torch.randn(0, 4, requires_grad=True, device=device) smooth_l1_beta = cfg.MODEL.ROI_BOX_HEAD.SMOOTH_L1_BETA outputs = FastRCNNOutputs( box2box_transform, logits, deltas, [], smooth_l1_beta ) with EventStorage(): # capture events in a new storage to discard them losses = outputs.losses() for value in losses.values(): self.assertTrue(torch.allclose(value, torch.zeros_like(value)))
def test_fast_rcnn_rotated(self): torch.manual_seed(132) cfg = RCNNConfig() cfg.MODEL.ROI_BOX_HEAD.BBOX_REG_WEIGHTS = (10, 10, 5, 5, 1) box2box_transform = Box2BoxTransformRotated(weights=cfg.MODEL.ROI_BOX_HEAD.BBOX_REG_WEIGHTS) box_head_output_size = 8 num_classes = 5 cls_agnostic_bbox_reg = False box_predictor = FastRCNNOutputLayers( box_head_output_size, num_classes, cls_agnostic_bbox_reg, box_dim=5 ) feature_pooled = torch.rand(2, box_head_output_size) pred_class_logits, pred_proposal_deltas = box_predictor(feature_pooled) image_shape = (10, 10) proposal_boxes = torch.tensor( [[2, 1.95, 2.4, 1.7, 0], [4.65, 5.25, 4.7, 5.5, 0]], dtype=torch.float32 ) gt_boxes = torch.tensor([[2, 2, 2, 2, 0], [4, 4, 4, 4, 0]], dtype=torch.float32) result = Instances(image_shape) result.proposal_boxes = RotatedBoxes(proposal_boxes) result.gt_boxes = RotatedBoxes(gt_boxes) result.gt_classes = torch.tensor([1, 2]) proposals = [] proposals.append(result) smooth_l1_beta = cfg.MODEL.ROI_BOX_HEAD.SMOOTH_L1_BETA outputs = RotatedFastRCNNOutputs( box2box_transform, pred_class_logits, pred_proposal_deltas, proposals, smooth_l1_beta ) with EventStorage(): # capture events in a new storage to discard them losses = outputs.losses() # Note: the expected losses are slightly different even if # the boxes are essentially the same as in the FastRCNNOutput test, because # bbox_pred in FastRCNNOutputLayers have different Linear layers/initialization # between the two cases. expected_losses = { "loss_cls": torch.tensor(1.7920907736), "loss_box_reg": torch.tensor(4.0410838127), } for name in expected_losses.keys(): assert torch.allclose(losses[name], expected_losses[name])
def test_fast_rcnn(self): torch.manual_seed(132) cfg = RCNNConfig() cfg.MODEL.ROI_BOX_HEAD.BBOX_REG_WEIGHTS = (10, 10, 5, 5) box2box_transform = Box2BoxTransform(weights=cfg.MODEL.ROI_BOX_HEAD.BBOX_REG_WEIGHTS) box_head_output_size = 8 num_classes = 5 cls_agnostic_bbox_reg = False box_predictor = FastRCNNOutputLayers( box_head_output_size, num_classes, cls_agnostic_bbox_reg, box_dim=4 ) feature_pooled = torch.rand(2, box_head_output_size) pred_class_logits, pred_proposal_deltas = box_predictor(feature_pooled) image_shape = (10, 10) proposal_boxes = torch.tensor([[0.8, 1.1, 3.2, 2.8], [2.3, 2.5, 7, 8]], dtype=torch.float32) gt_boxes = torch.tensor([[1, 1, 3, 3], [2, 2, 6, 6]], dtype=torch.float32) result = Instances(image_shape) result.proposal_boxes = Boxes(proposal_boxes) result.gt_boxes = Boxes(gt_boxes) result.gt_classes = torch.tensor([1, 2]) proposals = [] proposals.append(result) smooth_l1_beta = cfg.MODEL.ROI_BOX_HEAD.SMOOTH_L1_BETA outputs = FastRCNNOutputs( box2box_transform, pred_class_logits, pred_proposal_deltas, proposals, smooth_l1_beta ) with EventStorage(): # capture events in a new storage to discard them losses = outputs.losses() expected_losses = { "loss_cls": torch.tensor(1.7951188087), "loss_box_reg": torch.tensor(4.0357131958), } for name in expected_losses.keys(): assert torch.allclose(losses[name], expected_losses[name])
def train(self, start_iter: int, max_iter: int, max_epoch): """ Args: start_iter, max_iter (int): See docs above """ logger = logging.getLogger(__name__) logger.info("Starting training from iteration {}".format(start_iter)) self.iter = self.start_iter = start_iter self.max_iter = max_iter self.max_epoch = max_epoch with EventStorage(start_iter) as self.storage: try: self.before_train() for self.iter in range(start_iter, self.max_iter): self.before_step() self.run_step() self.after_step() except Exception: logger.exception("Exception during training:") finally: self.after_train()
def do_train(cfg, model, resume=False): model.train() optimizer = build_optimizer(cfg, model) scheduler = build_lr_scheduler(cfg, optimizer) checkpointer = DefaultCheckpointer( model, cfg.OUTPUT_DIR, optimizer=optimizer, scheduler=scheduler ) start_iter = ( checkpointer.resume_or_load(cfg.MODEL.WEIGHTS, resume=resume).get("iteration", -1) + 1 ) max_iter = cfg.SOLVER.MAX_ITER periodic_checkpointer = PeriodicCheckpointer( checkpointer, cfg.SOLVER.CHECKPOINT_PERIOD, max_iter=max_iter ) writers = ( [ CommonMetricPrinter(max_iter), JSONWriter(os.path.join(cfg.OUTPUT_DIR, "metrics.json")), TensorboardXWriter(cfg.OUTPUT_DIR), ] if comm.is_main_process() else [] ) # compared to "train_net.py", we do not support accurate timing and # precise BN here, because they are not trivial to implement data_loader = build_train_loader(cfg) logger.info("Starting training from iteration {}".format(start_iter)) with EventStorage(start_iter) as storage: for data, iteration in zip(data_loader, range(start_iter, max_iter)): iteration = iteration + 1 storage.step() loss_dict = model(data) losses = sum(loss for loss in loss_dict.values()) assert torch.isfinite(losses).all(), loss_dict loss_dict_reduced = {k: v.item() for k, v in comm.reduce_dict(loss_dict).items()} losses_reduced = sum(loss for loss in loss_dict_reduced.values()) if comm.is_main_process(): storage.put_scalars(total_loss=losses_reduced, **loss_dict_reduced) optimizer.zero_grad() losses.backward() optimizer.step() storage.put_scalar("lr", optimizer.param_groups[0]["lr"], smoothing_hint=False) scheduler.step() if ( cfg.TEST.EVAL_PERIOD > 0 and iteration % cfg.TEST.EVAL_PERIOD == 0 and iteration != max_iter ): do_test(cfg, model) # Compared to "train_net.py", the test results are not dumped to EventStorage comm.synchronize() if iteration - start_iter > 5 and (iteration % 20 == 0 or iteration == max_iter): for writer in writers: writer.write() periodic_checkpointer.step(iteration)
def test_rpn(self): torch.manual_seed(121) cfg = RCNNConfig() # PROPOSAL_GENERATOR: "RPN" # ANCHOR_GENERATOR: "DefaultAnchorGenerator" cfg.MODEL.RESNETS.DEPTH = 50 cfg.MODEL.RPN.BBOX_REG_WEIGHTS = (1, 1, 1, 1) backbone = build_backbone(cfg) proposal_generator = RPN(cfg, backbone.output_shape()) num_images = 2 images_tensor = torch.rand(num_images, 20, 30) image_sizes = [(10, 10), (20, 30)] images = ImageList(images_tensor, image_sizes) image_shape = (15, 15) num_channels = 1024 features = {"res4": torch.rand(num_images, num_channels, 1, 2)} gt_boxes = torch.tensor([[1, 1, 3, 3], [2, 2, 6, 6]], dtype=torch.float32) gt_instances = Instances(image_shape) gt_instances.gt_boxes = Boxes(gt_boxes) with EventStorage(): # capture events in a new storage to discard them proposals, proposal_losses = proposal_generator( images, features, [gt_instances[0], gt_instances[1]] ) expected_losses = { "loss_rpn_cls": torch.tensor(0.0804563984), "loss_rpn_loc": torch.tensor(0.0990132466), } for name in expected_losses.keys(): err_msg = "proposal_losses[{}] = {}, expected losses = {}".format( name, proposal_losses[name], expected_losses[name] ) self.assertTrue(torch.allclose(proposal_losses[name], expected_losses[name]), err_msg) expected_proposal_boxes = [ Boxes(torch.tensor([[0, 0, 10, 10], [7.3365392685, 0, 10, 10]])), Boxes( torch.tensor( [ [0, 0, 30, 20], [0, 0, 16.7862777710, 13.1362524033], [0, 0, 30, 13.3173446655], [0, 0, 10.8602609634, 20], [7.7165775299, 0, 27.3875980377, 20], ] ) ), ] expected_objectness_logits = [ torch.tensor([0.1225359365, -0.0133192837]), torch.tensor([0.1415634006, 0.0989848152, 0.0565387346, -0.0072308783, -0.0428492837]), ] for proposal, expected_proposal_box, im_size, expected_objectness_logit in zip( proposals, expected_proposal_boxes, image_sizes, expected_objectness_logits ): self.assertEqual(len(proposal), len(expected_proposal_box)) self.assertEqual(proposal.image_size, im_size) self.assertTrue( torch.allclose(proposal.proposal_boxes.tensor, expected_proposal_box.tensor) ) self.assertTrue(torch.allclose(proposal.objectness_logits, expected_objectness_logit))
def test_rrpn(self): torch.manual_seed(121) cfg = RCNNConfig() # PROPOSAL_GENERATOR: "RRPN" # ANCHOR_GENERATOR: "RotatedAnchorGenerator" cfg.MODEL.RESNETS.DEPTH = 50 cfg.MODEL.ANCHOR_GENERATOR.SIZES = [[32, 64]] cfg.MODEL.ANCHOR_GENERATOR.ASPECT_RATIOS = [[0.25, 1]] cfg.MODEL.ANCHOR_GENERATOR.ANGLES = [[0, 60]] cfg.MODEL.RPN.BBOX_REG_WEIGHTS = (1, 1, 1, 1, 1) # cfg.MODEL.RPN.HEAD_NAME = "StandardRPNHead" backbone = build_backbone(cfg) # currently using DefaultAnchorGenerator in RRPN proposal_generator = RRPN(cfg, backbone.output_shape()) num_images = 2 images_tensor = torch.rand(num_images, 20, 30) image_sizes = [(10, 10), (20, 30)] images = ImageList(images_tensor, image_sizes) image_shape = (15, 15) num_channels = 1024 features = {"res4": torch.rand(num_images, num_channels, 1, 2)} gt_boxes = torch.tensor([[2, 2, 2, 2, 0], [4, 4, 4, 4, 0]], dtype=torch.float32) gt_instances = Instances(image_shape) gt_instances.gt_boxes = RotatedBoxes(gt_boxes) with EventStorage(): # capture events in a new storage to discard them proposals, proposal_losses = proposal_generator( images, features, [gt_instances[0], gt_instances[1]] ) expected_losses = { "loss_rpn_cls": torch.tensor(0.043263837695121765), "loss_rpn_loc": torch.tensor(0.14432406425476074), } for name in expected_losses.keys(): err_msg = "proposal_losses[{}] = {}, expected losses = {}".format( name, proposal_losses[name], expected_losses[name] ) self.assertTrue(torch.allclose(proposal_losses[name], expected_losses[name]), err_msg) expected_proposal_boxes = [ RotatedBoxes( torch.tensor( [ [0.60189795, 1.24095452, 61.98131943, 18.03621292, -4.07244873], [15.64940453, 1.69624567, 59.59749603, 16.34339333, 2.62692475], [-3.02982378, -2.69752932, 67.90952301, 59.62455750, 59.97010040], [16.71863365, 1.98309708, 35.61507797, 32.81484985, 62.92267227], [0.49432933, -7.92979717, 67.77606201, 62.93098450, -1.85656738], [8.00880814, 1.36017394, 121.81007385, 32.74150467, 50.44297409], [16.44299889, -4.82221127, 63.39775848, 61.22503662, 54.12270737], [5.00000000, 5.00000000, 10.00000000, 10.00000000, -0.76943970], [17.64130402, -0.98095351, 61.40377808, 16.28918839, 55.53118134], [0.13016054, 4.60568953, 35.80157471, 32.30180359, 62.52872086], [-4.26460743, 0.39604485, 124.30079651, 31.84611320, -1.58203125], [7.52815342, -0.91636634, 62.39784622, 15.45565224, 60.79549789], ] ) ), RotatedBoxes( torch.tensor( [ [0.07734215, 0.81635046, 65.33510590, 17.34688377, -1.51821899], [-3.41833067, -3.11320257, 64.17595673, 60.55617905, 58.27033234], [20.67383385, -6.16561556, 63.60531998, 62.52315903, 54.85546494], [15.00000000, 10.00000000, 30.00000000, 20.00000000, -0.18218994], [9.22646523, -6.84775209, 62.09895706, 65.46472931, -2.74307251], [15.00000000, 4.93451595, 30.00000000, 9.86903191, -0.60272217], [8.88342094, 2.65560246, 120.95362854, 32.45022202, 55.75970078], [16.39088631, 2.33887148, 34.78761292, 35.61492920, 60.81977463], [9.78298569, 10.00000000, 19.56597137, 20.00000000, -0.86660767], [1.28576660, 5.49873352, 34.93610382, 33.22600174, 60.51599884], [17.58912468, -1.63270092, 62.96052551, 16.45713997, 52.91245270], [5.64749718, -1.90428460, 62.37649155, 16.19474792, 61.09543991], [0.82255805, 2.34931135, 118.83985901, 32.83671188, 56.50753784], [-5.33874989, 1.64404404, 125.28501892, 33.35424042, -2.80731201], ] ) ), ] expected_objectness_logits = [ torch.tensor( [ 0.10111768, 0.09112845, 0.08466332, 0.07589971, 0.06650183, 0.06350251, 0.04299347, 0.01864817, 0.00986163, 0.00078543, -0.04573630, -0.04799230, ] ), torch.tensor( [ 0.11373727, 0.09377633, 0.05281663, 0.05143715, 0.04040275, 0.03250912, 0.01307789, 0.01177734, 0.00038105, -0.00540255, -0.01194804, -0.01461012, -0.03061717, -0.03599222, ] ), ] torch.set_printoptions(precision=8, sci_mode=False) for proposal, expected_proposal_box, im_size, expected_objectness_logit in zip( proposals, expected_proposal_boxes, image_sizes, expected_objectness_logits ): self.assertEqual(len(proposal), len(expected_proposal_box)) self.assertEqual(proposal.image_size, im_size) # It seems that there's some randomness in the result across different machines: # This test can be run on a local machine for 100 times with exactly the same result, # However, a different machine might produce slightly different results, # thus the atol here. err_msg = "computed proposal boxes = {}, expected {}".format( proposal.proposal_boxes.tensor, expected_proposal_box.tensor ) self.assertTrue( torch.allclose( proposal.proposal_boxes.tensor, expected_proposal_box.tensor, atol=1e-5 ), err_msg, ) err_msg = "computed objectness logits = {}, expected {}".format( proposal.objectness_logits, expected_objectness_logit ) self.assertTrue( torch.allclose(proposal.objectness_logits, expected_objectness_logit, atol=1e-5), err_msg, )
if xylabels[1]: plt.ylabel(xylabels[1]) ax.set_title(prefix) if not label: label = range(len(tensor_list)) ax.hist(array_list, bincount, histtype='bar', label=label) ax.legend() if __name__ == "__main__": _dict = dict(VISDOM=dict( HOST="192.168.1.1", PORT="8082", TURN_ON=True, ENV_PREFIX='test', ), ) import torch from easydict import EasyDict cfg = EasyDict(_dict) from cvpods.utils import (CommonMetricPrinter, JSONWriter, PathManager, TensorboardXWriter, collect_env_info, comm, seed_all_rng, setup_logger, VisdomWriter) from cvpods.utils import EventStorage with EventStorage() as storage: hist = PltHistogram() hist.mode = 'data' visdom_writer = VisdomWriter(cfg.VISDOM.HOST, cfg.VISDOM.PORT, 1, [], cfg.VISDOM.ENV_PREFIX) storage.put_image('histo', hist(torch.rand((1000, )))) visdom_writer.write()