hankcs/HanLP · error · ValueError
emissions must have dimension of 3, got {emissions.dim()}
Error message
emissions must have dimension of 3, got {emissions.dim()} What it means
CRF._validate checks that emissions is a rank-3 tensor of shape (batch, seq_len, num_tags) (or (seq_len, batch, num_tags) if batch_first=False). A rank-2 or rank-4 emissions tensor (e.g. a flat (batch*seq, tags) score matrix) fails this check in both forward (loss) and decode (prediction).
Source
Thrown at hanlp/layers/crf/crf.py:166
List of list containing the best tag sequence for each batch.
"""
self._validate(emissions, mask=mask)
if mask is None:
mask = emissions.new_ones(emissions.shape[:2], dtype=torch.uint8)
if self.batch_first:
emissions = emissions.transpose(0, 1)
mask = mask.transpose(0, 1)
return self._viterbi_decode(emissions, mask)
def _validate(
self,
emissions: torch.Tensor,
tags: Optional[torch.LongTensor] = None,
mask: Optional[torch.ByteTensor] = None) -> None:
if emissions.dim() != 3:
raise ValueError(f'emissions must have dimension of 3, got {emissions.dim()}')
if emissions.size(2) != self.num_tags:
raise ValueError(
f'expected last dimension of emissions is {self.num_tags}, '
f'got {emissions.size(2)}')
if tags is not None:
if emissions.shape[:2] != tags.shape:
raise ValueError(
'the first two dimensions of emissions and tags must match, '
f'got {tuple(emissions.shape[:2])} and {tuple(tags.shape)}')
if mask is not None:
if emissions.shape[:2] != mask.shape:
raise ValueError(
'the first two dimensions of emissions and mask must match, '
f'got {tuple(emissions.shape[:2])} and {tuple(mask.shape)}')
no_empty_seq = not self.batch_first and mask[0].all()
no_empty_seq_bf = self.batch_first and mask[:, 0].all()View on GitHub (pinned to ddb1299bdd)
Solutions
- Keep emissions 3D: use (batch, seq_len, num_tags); avoid .view(-1, num_tags) before the CRF
- For a single sentence, keep the batch dim: emissions.unsqueeze(0)
- Check batch_first consistency with your tensor layout (transpose if needed)
Example fix
# before loss = crf(logits.view(-1, n_tags), tags.view(-1), mask.view(-1)) # after loss = crf(logits, tags, mask) # logits: (batch, seq_len, n_tags)
Defensive patterns
Strategy: type-guard
Validate before calling
assert emissions.dim() == 3, f'emissions must be (batch, seq, tags), got {tuple(emissions.shape)}' Type guard
def is_valid_crf_emissions(t: torch.Tensor) -> bool:
return t.dim() == 3 and t.dtype.is_floating_point Prevention
- Never flatten to 2D before CRF; keep (batch, seq_len, num_tags)
- Add shape asserts at model boundaries
When it happens
Trigger: Passing reshaped logits like emissions.view(-1, num_tags) into crf(...); feeding a 2D per-token score matrix from a softmax tagger; forgetting the batch dimension when scoring a single sentence.
Common situations: Adapting softmax-tagger code (which flattens to 2D for CrossEntropyLoss) to a CRF; single-sample inference where the batch dim was squeezed.
Related errors
- expected last dimension of emissions is {self.num_tags}, got
- DataParallel not supported when CRF is used
- invalid number of tags: {num_tags}
- invalid reduction: {reduction}
- Got average f{average}, expected one of None, 'token', or 'b
AI-assisted analysis of hankcs/HanLP@ddb1299bdd (2026-08-27).
Data as JSON: /api/errors/17e5a0b49d315790.
Report an issue: GitHub.