{"record":{"id":"cd8ff29194fe571c","repo":"huggingface/candle","slug":"the-target-tensor-should-have-a-single-dimension","errorCode":null,"errorMessage":"the target tensor should have a single dimension ({dims:?})","messagePattern":"the target tensor should have a single dimension \\((.+?)\\)","errorType":"exception","errorClass":"candle::Error","httpStatus":null,"severity":"error","filePath":"candle-nn/src/loss.rs","lineNumber":17,"sourceCode":"//! Loss Calculations\n//!\nuse candle::{Result, Tensor};\n\n/// The negative log likelihood loss.\n///\n/// Arguments\n///\n/// * [inp]: The input tensor of dimensions `N, C` where `N` is the batch size and `C` the number\n///   of categories. This is expected to contain log probabilities.\n/// * [target]: The ground truth labels as a tensor of u32 of dimension `N`.\n///\n/// The resulting tensor is a scalar containing the average value over the batch.\npub fn nll(inp: &Tensor, target: &Tensor) -> Result<Tensor> {\n    let b_sz = match target.dims() {\n        &[b_sz] => b_sz,\n        dims => candle::bail!(\"the target tensor should have a single dimension ({dims:?})\"),\n    };\n    match inp.dims() {\n        &[inp_b_sz, _] => {\n            if inp_b_sz != b_sz {\n                candle::bail!(\"batch size mismatch between inp ({inp_b_sz}) and target ({b_sz})\")\n            }\n        }\n        dims => candle::bail!(\"the target tensor should have two dimensions ({dims:?})\"),\n    }\n    inp.gather(&target.unsqueeze(1)?, 1)?\n        .sum_all()?\n        .affine(-1f64 / b_sz as f64, 0.)\n}\n\n/// The cross-entropy loss.\n///\n/// Arguments\n///","sourceCodeStart":1,"sourceCodeEnd":35,"githubUrl":"https://github.com/huggingface/candle/blob/d5fee525bfde3273eb7c9b75fd2bc4937be867ca/candle-nn/src/loss.rs#L1-L35","documentation":"candle_nn::loss::nll computes negative log likelihood and expects the target tensor to be exactly 1-D with shape [batch_size], holding per-sample class indices. This bail fires when target.dims() is not a single-element slice (e.g. scalar, 2-D one-hot, or higher rank).","triggerScenarios":"Calling nll(&inp, &target) (directly or via cross_entropy) with a target of rank 0 (scalar), rank 2 (e.g. [B,1] or one-hot [B,C]), or higher.","commonSituations":"One-hot encoded labels passed instead of class indices; target loaded with an extra trailing dimension of size 1 from a dataloader; softmax cross-entropy ported from PyTorch where targets of shape [B] vs [B,1] both work — candle is stricter.","solutions":["Squeeze the target to 1-D before calling: let target = target.squeeze(1)?; if it is [B,1].","Convert one-hot targets back to indices via argmax (target.argmax(1)?) instead of passing the [B,C] tensor.","If target is scalar, reshape to [1]: target.reshape((1,))?.","Assert target.rank() == 1 in your training loop and log target.dims() when it fails."],"exampleFix":"// before: target dims [B, 1]\nlet loss = cross_entropy(&logits, &target)?;\n// after\nlet target = target.squeeze(1)?; // dims [B]\nlet loss = cross_entropy(&logits, &target)?;","handlingStrategy":"validation","validationCode":"if target.rank() != 1 {\n    return Err(anyhow!(\"nll target must be 1-D [B], got dims {:?}\", target.dims()));\n}","typeGuard":"fn is_class_index_target(t: &Tensor) -> bool { t.rank() == 1 }","tryCatchPattern":"let target = if target.rank() == 2 && target.dim(1)? == 1 {\n    target.squeeze(1)?\n} else { target };\nlet loss = cross_entropy(&logits, &target)?;","preventionTips":["Keep targets as class-index tensors, never one-hot, for nll/cross_entropy","Squeeze dataloader-produced [B,1] targets immediately after loading","Write one shared loss wrapper that normalizes target shape before any loss fn","Convert one-hot to indices with argmax if upstream code emits one-hot"],"tags":["loss","shape","tensor","rust"],"backgroundTag":"tensor-rank-mismatch","analyzedSha":"d5fee525bfde3273eb7c9b75fd2bc4937be867ca","analyzedAt":"2026-09-02T00:15:47.023Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-09T06:17:21.866Z"}