{"record":{"id":"1241dd8f6d49a939","repo":"Lightning-AI/pytorch-lightning","slug":"trying-to-infer-the-batch-size-from-an-ambiguous","errorCode":null,"errorMessage":"Trying to infer the `batch_size` from an ambiguous collection. The batch size we found is {batch_size}. To avoid any miscalculations, use `self.log(..., batch_size=batch_size)`.","messagePattern":"Trying to infer the `batch_size` from an ambiguous collection\\. The batch size we found is (.+?)\\. To avoid any miscalculations, use `self\\.log\\(\\.\\.\\., batch_size=batch_size\\)`\\.","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"src/lightning/pytorch/utilities/data.py","lineNumber":79,"sourceCode":"\ndef extract_batch_size(batch: BType) -> int:\n    \"\"\"Unpack a batch to find a ``torch.Tensor``.\n\n    Returns:\n        ``len(tensor)`` when found, or ``1`` when it hits an empty or non iterable.\n\n    \"\"\"\n    error_msg = (\n        \"We could not infer the batch_size from the batch. Either simplify its structure\"\n        \" or provide the batch_size as `self.log(..., batch_size=batch_size)`.\"\n    )\n    batch_size = None\n    try:\n        for bs in _extract_batch_size(batch):\n            if batch_size is None:\n                batch_size = bs\n            elif batch_size != bs:\n                warning_cache.warn(\n                    \"Trying to infer the `batch_size` from an ambiguous collection. The batch size we\"\n                    f\" found is {batch_size}. To avoid any miscalculations, use `self.log(..., batch_size=batch_size)`.\"\n                )\n                break\n    except RecursionError:\n        raise RecursionError(error_msg)\n\n    if batch_size is None:\n        raise MisconfigurationException(error_msg)\n\n    return batch_size\n\n\ndef has_len_all_ranks(\n    dataloader: object,\n    strategy: \"pl.strategies.Strategy\",\n    allow_zero_length_dataloader_with_multiple_devices: bool = False,\n) -> TypeGuard[Sized]:","sourceCodeStart":61,"sourceCodeEnd":97,"githubUrl":"https://github.com/Lightning-AI/pytorch-lightning/blob/9fed5c27d2a62ff0efd6c3573599921d6ff67c14/src/lightning/pytorch/utilities/data.py#L61-L97","documentation":"PyTorch Lightning tries to automatically infer the batch size from the batch object when self.log(...) is called without an explicit batch_size argument. This warning fires when the batch is an ambiguous nested collection (dict/list/tuple of tensors) whose elements disagree about the batch size — e.g. a per-sample tensor alongside a [batch, ...] tensor — so the inferred number may be wrong. Metrics like accuracy or any normalization by batch size can then be miscalculated.","triggerScenarios":"Calling self.log('name', value) (without batch_size=...) with a batch containing heterogeneous leading dimensions: e.g. {'x': tensor(B,3,224,224), 'idx': tensor(B)} mixed with a scalar/1-element structure, or nested dicts where one branch yields bs=N and another yields bs=1 or bs=N*seq_len. _extract_batch_size walks the collection, finds conflicting sizes, and warns (breaking after the first conflict).","commonSituations":"Custom batches with extra metadata tensors (indices, lengths, masks of shape [B] vs [1]); seq2seq batches where sequence tensors are reshaped to [B*T, ...]; token-level NLP batches mixing [B] input_ids with [B, T] flattened tensors; datasets returning tuples with a scalar label wrapped in a list.","solutions":["Pass the batch size explicitly at the log site: self.log('loss', loss, batch_size=batch['x'].size(0)).","Restructure the batch so all per-sample tensors share the same leading dimension and wrap non-per-sample metadata in a non-iterable object or keep it out of the batch dict.","Audit your batch composition by printing [_extract_batch_size(b) for b in one batch] in a sanity check to find which element disagrees.","If the inferred size is actually acceptable, pass batch_size explicitly anyway to make logging deterministic and silence the warning."],"exampleFix":"# before\nself.log(\"train_loss\", loss, prog_bar=True)\n\n# after\nself.log(\"train_loss\", loss, prog_bar=True, batch_size=x.size(0))","handlingStrategy":"validation","validationCode":"from lightning.pytorch.utilities.data import extract_batch_size\n\nbs_values = set(extract_batch_size(batch))\nassert len(bs_values) == 1, f\"Ambiguous batch sizes in batch: {bs_values}\"\nself.log(\"loss\", loss, batch_size=next(iter(bs_values)))","typeGuard":"def has_uniform_batch_size(batch) -> bool:\n    sizes = set(extract_batch_size(batch))\n    return len(sizes) == 1","tryCatchPattern":null,"preventionTips":["Always pass batch_size=... to self.log instead of relying on inference.","Keep per-sample tensors first-dim-aligned; move scalars/metadata out of the batch dict or wrap them so traversal is unambiguous.","Add a one-batch sanity check in setup() that asserts extract_batch_size returns a single value."],"tags":["pytorch-lightning","batch-size","logging","data-loading"],"backgroundTag":"batch-size-inference-ambiguity","analyzedSha":"9fed5c27d2a62ff0efd6c3573599921d6ff67c14","analyzedAt":"2026-08-28T11:52:41.083Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}