@@ -865,6 +865,39 @@ def _average_precision_from_matches(
865865 return float (((recall [changing + 1 ] - recall [changing ]) * precision [changing + 1 ]).sum ())
866866
867867
868+ def verify_loader_cardinality (
869+ loader : DataLoader ,
870+ processed_batches : int ,
871+ distributed : DistributedContext | None ,
872+ * ,
873+ phase : str ,
874+ ) -> None :
875+ """Refuse silently truncated iterable streams on any DDP rank."""
876+
877+ expected_batches = len (loader )
878+ local = {
879+ "rank" : 0 if distributed is None else distributed .rank ,
880+ "processed" : processed_batches ,
881+ "expected" : expected_batches ,
882+ }
883+ reports = (
884+ [local ]
885+ if distributed is None
886+ else distributed .all_gather_objects (local )
887+ )
888+ invalid = [
889+ report
890+ for report in reports
891+ if report ["processed" ] != report ["expected" ]
892+ ]
893+ if invalid :
894+ details = ", " .join (
895+ f"rank { report ['rank' ]} : { report ['processed' ]} /{ report ['expected' ]} "
896+ for report in invalid
897+ )
898+ raise RuntimeError (f"{ phase } loader ended before its declared cardinality ({ details } )" )
899+
900+
868901@torch .inference_mode ()
869902def evaluate_detector (
870903 model : TRHashObjectDetector ,
@@ -892,7 +925,9 @@ def evaluate_detector(
892925 leave = False ,
893926 disable = False if show_progress else True ,
894927 )
928+ processed_batches = 0
895929 for pixel_values , targets in progress :
930+ processed_batches += 1
896931 autocast = torch .autocast ("cuda" , dtype = torch .bfloat16 ) if use_amp else nullcontext ()
897932 with autocast :
898933 model_inputs = pixel_values .to (
@@ -921,6 +956,12 @@ def evaluate_detector(
921956 detection ["labels" ],
922957 image_targets ,
923958 )
959+ verify_loader_cardinality (
960+ loader ,
961+ processed_batches ,
962+ distributed ,
963+ phase = "validation" ,
964+ )
924965 if distributed is not None and distributed .enabled :
925966 states = distributed .all_gather_objects (metrics .state_dict ())
926967 metrics = DetectionMetricsAccumulator (
@@ -1655,7 +1696,9 @@ def write_checkpoint(
16551696 leave = False ,
16561697 disable = not distributed .is_main ,
16571698 )
1699+ processed_batches = batches_to_skip
16581700 for batch_index , (pixel_values , targets ) in enumerate (progress , start = batches_to_skip ):
1701+ processed_batches = batch_index + 1
16591702 pixel_values = pixel_values .to (device , non_blocking = device .type == "cuda" )
16601703 if args .multi_scale_min :
16611704 choices = range (
@@ -1742,6 +1785,13 @@ def write_checkpoint(
17421785 if args .save_steps and step % args .save_steps == 0 :
17431786 write_checkpoint (epoch = epoch , batch_in_epoch = batch_index + 1 )
17441787
1788+ verify_loader_cardinality (
1789+ loader ,
1790+ processed_batches ,
1791+ distributed ,
1792+ phase = f"training epoch { epoch + 1 } " ,
1793+ )
1794+
17451795 should_validate = validation_loader is not None and should_validate_epoch (
17461796 epoch ,
17471797 args .epochs ,
0 commit comments