{"record":{"id":"d295e1756513f0d9","repo":"huggingface/candle","slug":"cross-entropy-expects-an-input-tensor-of-rank-2","errorCode":null,"errorMessage":"cross_entropy expects an input tensor of rank 2","messagePattern":"cross_entropy expects an input tensor of rank 2","errorType":"exception","errorClass":"candle::Error","httpStatus":null,"severity":"error","filePath":"candle-nn/src/loss.rs","lineNumber":43,"sourceCode":"        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///\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 raw logits.\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 cross_entropy(inp: &Tensor, target: &Tensor) -> Result<Tensor> {\n    if inp.rank() != 2 {\n        candle::bail!(\"cross_entropy expects an input tensor of rank 2\")\n    }\n    let inp = crate::ops::log_softmax(inp, 1)?;\n    nll(&inp, target)\n}\n\n/// The mean squared error loss.\npub fn mse(inp: &Tensor, target: &Tensor) -> Result<Tensor> {\n    (inp - target)?.sqr()?.mean_all()\n}\n\n/// The binary cross-entropy with logit 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 raw logits.\n/// * [target]: The ground truth labels as a tensor of u32 of dimension `N, C` where `N` is the batch size and `C` the number\n///   of categories.","sourceCodeStart":25,"sourceCodeEnd":61,"githubUrl":"https://github.com/huggingface/candle/blob/d5fee525bfde3273eb7c9b75fd2bc4937be867ca/candle-nn/src/loss.rs#L25-L61","documentation":"cross_entropy is a thin wrapper that applies log_softmax then nll, and it only supports rank-2 inputs ([batch, classes]). This guard fires before any computation when inp.rank() != 2, giving an earlier and clearer message than the downstream nll shape errors.","triggerScenarios":"Calling candle_nn::loss::cross_entropy with a tensor of rank 1 (unbatched [C]), rank 3 ([B,T,C] sequence logits), or rank 4 ([B,C,H,W] dense prediction logits).","commonSituations":"Segmentation/dense-prediction heads outputting [B,C,H,W]; transformer token logits [B,T,C]; unbatched single sample; porting PyTorch F.cross_entropy which accepts higher-rank inputs channel-first.","solutions":["Reshape higher-rank logits to [N, C]: e.g. [B,C,H,W] -> permute to [B,H,W,C] then reshape ((B*H*W), C), flattening targets likewise.","For [B,T,C], reshape to [B*T, C] and flatten targets to [B*T].","Unsqueeze a batch dim for rank-1 input before calling.","If you regularly need higher-rank cross entropy, write a small wrapper that reshapes, calls cross_entropy, and reshapes back."],"exampleFix":"// before: dense logits [B, C, H, W]\nlet loss = cross_entropy(&logits, &mask)?; // error\n// after\nlet (b, c, h, w) = logits.dims4()?;\nlet logits = logits.permute((0, 2, 3, 1))?.reshape((b * h * w, c))?;\nlet mask = mask.reshape((b * h * w, ()))?;\nlet loss = cross_entropy(&logits, &mask)?;","handlingStrategy":"validation","validationCode":"if logits.rank() != 2 {\n    anyhow::bail!(\"cross_entropy requires rank 2 logits, got {:?}\", logits.dims());\n}","typeGuard":"fn is_cross_entropy_ready(logits: &Tensor, targets: &Tensor) -> bool {\n    logits.rank() == 2 && targets.rank() == 1 && logits.dim(0).map(|d| d == targets.elem_count()).unwrap_or(false)\n}","tryCatchPattern":"fn ce_any_rank(logits: &Tensor, targets: &Tensor) -> candle::Result<Tensor> {\n    let (n, c) = match logits.rank() {\n        2 => logits.dims2()?,\n        _ => {\n            let c = *logits.dims().last().unwrap();\n            let n = logits.elem_count() / c;\n            (n, c)\n        }\n    };\n    candle_nn::loss::cross_entropy(\n        &logits.reshape((n, c))?,\n        &targets.reshape((targets.elem_count(), ()))?,\n    )\n}","preventionTips":["Route all losses through one helper that normalizes logits/target shapes to rank 2/rank 1","For dense outputs, permute channels last before reshaping to [N, C]","Add a debug assert on logits.rank() in training loops","Pin candle version and re-read breaking notes when upgrading"],"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"}