跳转至

代码

tiny_loss.hpp

/**
 * @file tiny_loss.hpp
 * @brief Loss functions for tiny_ai — MSE, MAE, Cross-Entropy, Binary-CE.
 */

#pragma once

#include "tiny_tensor.hpp"

#ifdef __cplusplus

namespace tiny
{

enum class LossType
{
    MSE = 0,
    MAE,
    CROSS_ENTROPY,
    BINARY_CE
};

float mse_forward(const Tensor &pred, const Tensor &target);
#if TINY_AI_TRAINING_ENABLED
Tensor mse_backward(const Tensor &pred, const Tensor &target);
#endif

float mae_forward(const Tensor &pred, const Tensor &target);
#if TINY_AI_TRAINING_ENABLED
Tensor mae_backward(const Tensor &pred, const Tensor &target);
#endif

float cross_entropy_forward(const Tensor &logits, const int *labels);
#if TINY_AI_TRAINING_ENABLED
Tensor cross_entropy_backward(const Tensor &logits, const int *labels);
#endif

float binary_ce_forward(const Tensor &pred, const Tensor &target);
#if TINY_AI_TRAINING_ENABLED
Tensor binary_ce_backward(const Tensor &pred, const Tensor &target);
#endif

float loss_forward(const Tensor &pred, const Tensor &target,
                   LossType type, const int *labels = nullptr);

#if TINY_AI_TRAINING_ENABLED
Tensor loss_backward(const Tensor &pred, const Tensor &target,
                     LossType type, const int *labels = nullptr);
#endif

} // namespace tiny

#endif // __cplusplus

tiny_loss.cpp

/**
 * @file tiny_loss.cpp
 * @brief Loss function implementations for tiny_ai.
 */

#include "tiny_loss.hpp"
#include "tiny_activation.hpp"
#include <cmath>

#ifdef __cplusplus

namespace tiny
{

float mse_forward(const Tensor &pred, const Tensor &target)
{
    float sum = 0.0f;
    for (int i = 0; i < pred.size; i++)
    {
        float d = pred.data[i] - target.data[i];
        sum += d * d;
    }
    return sum / (float)pred.size;
}

#if TINY_AI_TRAINING_ENABLED
Tensor mse_backward(const Tensor &pred, const Tensor &target)
{
    Tensor g = Tensor::zeros_like(pred);
    float scale = 2.0f / (float)pred.size;
    for (int i = 0; i < pred.size; i++)
        g.data[i] = scale * (pred.data[i] - target.data[i]);
    return g;
}
#endif

float mae_forward(const Tensor &pred, const Tensor &target)
{
    float sum = 0.0f;
    for (int i = 0; i < pred.size; i++)
        sum += fabsf(pred.data[i] - target.data[i]);
    return sum / (float)pred.size;
}

#if TINY_AI_TRAINING_ENABLED
Tensor mae_backward(const Tensor &pred, const Tensor &target)
{
    Tensor g = Tensor::zeros_like(pred);
    float scale = 1.0f / (float)pred.size;
    for (int i = 0; i < pred.size; i++)
    {
        float d = pred.data[i] - target.data[i];
        g.data[i] = (d > 0.0f ? scale : (d < 0.0f ? -scale : 0.0f));
    }
    return g;
}
#endif

float cross_entropy_forward(const Tensor &logits, const int *labels)
{
    int batch = logits.rows();
    int cls   = logits.cols();
    float loss = 0.0f;

    for (int b = 0; b < batch; b++)
    {
        const float *row = logits.data + b * cls;
        int lbl = labels[b];

        // log-sum-exp trick
        float mx = row[0];
        for (int c = 1; c < cls; c++) if (row[c] > mx) mx = row[c];

        float sum_exp = 0.0f;
        for (int c = 0; c < cls; c++) sum_exp += expf(row[c] - mx);

        loss += -(row[lbl] - mx) + logf(sum_exp + TINY_MATH_MIN_DENOMINATOR);
    }
    return loss / (float)batch;
}

#if TINY_AI_TRAINING_ENABLED
Tensor cross_entropy_backward(const Tensor &logits, const int *labels)
{
    int batch = logits.rows();
    int cls   = logits.cols();

    Tensor g = softmax_forward(logits);
    float inv_batch = 1.0f / (float)batch;
    for (int b = 0; b < batch; b++)
    {
        g.data[b * cls + labels[b]] -= 1.0f;
        for (int c = 0; c < cls; c++)
            g.data[b * cls + c] *= inv_batch;
    }
    return g;
}
#endif

float binary_ce_forward(const Tensor &pred, const Tensor &target)
{
    float loss = 0.0f;
    float eps = TINY_MATH_MIN_POSITIVE_INPUT_F32;
    for (int i = 0; i < pred.size; i++)
    {
        float p = pred.data[i];
        float t = target.data[i];
        loss -= t * logf(p + eps) + (1.0f - t) * logf(1.0f - p + eps);
    }
    return loss / (float)pred.size;
}

#if TINY_AI_TRAINING_ENABLED
Tensor binary_ce_backward(const Tensor &pred, const Tensor &target)
{
    Tensor g(pred.clone());
    float eps   = TINY_MATH_MIN_POSITIVE_INPUT_F32;
    float scale = 1.0f / (float)pred.size;
    for (int i = 0; i < pred.size; i++)
    {
        float p = pred.data[i];
        float t = target.data[i];
        g.data[i] = scale * (-(t / (p + eps)) + (1.0f - t) / (1.0f - p + eps));
    }
    return g;
}
#endif

float loss_forward(const Tensor &pred, const Tensor &target,
                   LossType type, const int *labels)
{
    switch (type)
    {
        case LossType::MSE:           return mse_forward(pred, target);
        case LossType::MAE:           return mae_forward(pred, target);
        case LossType::CROSS_ENTROPY: return cross_entropy_forward(pred, labels);
        case LossType::BINARY_CE:     return binary_ce_forward(pred, target);
        default: return 0.0f;
    }
}

#if TINY_AI_TRAINING_ENABLED
Tensor loss_backward(const Tensor &pred, const Tensor &target,
                     LossType type, const int *labels)
{
    switch (type)
    {
        case LossType::MSE:           return mse_backward(pred, target);
        case LossType::MAE:           return mae_backward(pred, target);
        case LossType::CROSS_ENTROPY: return cross_entropy_backward(pred, labels);
        case LossType::BINARY_CE:     return binary_ce_backward(pred, target);
        default:                      return pred.clone();
    }
}
#endif

} // namespace tiny

#endif // __cplusplus