C++标准库无交叉熵损失函数,需手写或用第三方库;手写时须用log-sum-exp稳定技巧避免溢出,如先减最大值再计算;libtorch中可用torch::nn::functional::cross_entropy,要求targets为int64_t类型且索引不越界。

交叉熵损失在 C++ 里没有标准库函数
标准 C++ 库(<cmath>、<algorithm> 等)不提供 cross_entropy_loss 这类函数。你得自己写,或依赖第三方库(如 Eigen、xtensor、libtorch)。直接手写最可控,也最容易调试。
常见错误是把 logits 当作概率直接喂给 -log(p) ——这会导致数值不稳定甚至 nan,因为未归一化的 logits 可能极大或极小,exp(logits) 溢出,log(0) 崩溃。
- 必须先对 logits 做 softmax(或更稳的 log-softmax),再计算 loss
- 分类任务中,label 通常是 one-hot 或 class index,要对应处理
- 多分类单标签场景最常用:输入 shape 是
[batch_size, num_classes],label 是[batch_size]的整数索引
手写稳定版 cross_entropy_loss(单样本 + 批量)
核心是避免 exp 溢出:用 softmax 的 log-space 形式,即 log_softmax,再取负对数似然。公式等价于:
loss = -log(softmax(logits)[target]) = -logits[target] + log(sum(exp(logits)))
但直接算 sum(exp(logits)) 危险,所以改用带减法平移的 trick:
立即学习“C++免费学习笔记(深入)”;
- 先求
max_logit = max(logits) - 再算
log(sum(exp(logits - max_logit))) + max_logit—— 这就是稳定版的log(sum(exp(logits))) - 最终 loss =
-logits[target] + log(sum(exp(logits - max_logit))) + max_logit
示例(单样本,logits = {2.0, 1.0, 0.1}, target = 0):
max_logit = 2.0<br>shifted = {0.0, -1.0, -1.9}<br>log_sum_exp = log(exp(0.0) + exp(-1.0) + exp(-1.9)) + 2.0 ≈ log(1 + 0.3679 + 0.1496) + 2.0 ≈ 2.405<br>loss = -2.0 + 2.405 = 0.405用 libtorch(C++ API)时怎么调用
如果你已在用 PyTorch 训练流程,C++ 部署时推荐直接用 torch::nn::functional::cross_entropy,它默认启用 log-sum-exp 稳定实现,且支持 label 为 int64_t 索引(无需 one-hot)。
- 输入
logits是torch::Tensor,shape[N, C] -
targets是torch::Tensor,dtypetorch::kLong,shape[N] - 别漏掉
reduction="mean"(默认)或设为"none"得到 per-sample loss - 错误示例:
targets传float类型 → 报错expected scalar type Long but found Float
代码片段:
auto logits = torch::randn({32, 10});<br>auto targets = torch::randint(0, 10, {32}, torch::kLong);<br>auto loss = torch::nn::functional::cross_entropy(logits, targets);为什么不用 std::log(std::exp(x)) 这类链式调用
因为 std::exp(89) 就已溢出为 inf,而典型 logits 值域常达 ±30~±100。一旦出现 inf,后续 std::log(inf) 是 inf,std::log(0) 是 -inf,loss 失控。
- log-sum-exp 的平移技巧让所有
exp输入 ≤ 0,彻底规避溢出 - 即使全用
double,原始写法在 batch 较大时仍易崩;稳定版可安全跑满 float32 范围 - 某些嵌入式或纯 C++ 环境无法用 libtorch,这时手写稳定版是唯一可靠选择
真正容易被忽略的是:label 索引越界检查(比如 target >= num_classes)和梯度回传路径——如果只是前向计算 loss,不涉及 autograd,那没问题;但若需反向,必须确保 tensor 在计算图中正确连接。


















