1use nuvec::{Array, DType, fast, ops};
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq)]
7pub enum Reduction {
8 None,
10 Mean,
12 Sum,
14}
15
16#[track_caller]
24pub fn cross_entropy(logits: &Array, targets: &Array, reduction: Reduction) -> Array {
25 let valid = logits.ndim() > 0
26 && targets.shape() == &logits.shape()[..logits.ndim() - 1]
27 && is_index_dtype(targets.dtype());
28 let loss = if !valid || nuvec::default_device().kind == nuvec::DeviceKind::Gpu {
29 fast::cross_entropy(logits, targets).astype(logits.dtype())
32 } else {
33 let classes = logits.shape()[logits.ndim() - 1];
34 let z = logits.subtract(logits.max_keepdims(-1).stop_gradient());
35 let positions = ops::arange(0, classes, 1).astype(targets.dtype());
36 let hit = positions.equal(targets.expand_dims(-1));
37 let score = hit.select(&z, 0.0).sum(-1);
38 z.logsumexp(-1).subtract(&score)
39 };
40 match reduction {
41 Reduction::None => loss,
42 Reduction::Mean => loss.mean(..),
43 Reduction::Sum => loss.sum(..),
44 }
45}
46
47fn is_index_dtype(d: DType) -> bool {
49 !matches!(
50 d,
51 DType::F16 | DType::BF16 | DType::F32 | DType::F64 | DType::Complex64
52 )
53}