Done with activation softmax backwards

This commit is contained in:
2026-07-31 19:08:57 +02:00
parent d7b74ab40f
commit 11da534fd6
30 changed files with 3029 additions and 184 deletions
@@ -40,8 +40,13 @@
#include <tensor/matrix.hpp> // panic::tensor::real_matrix (uint_matrix, int_matrix)
#include <tensor/vector.hpp>
#include <math/mean.hpp>
#include <math/clip.hpp>
#include <math/log.hpp>
#include <math/mul.hpp>
#include <tensor/generators/one_hot.hpp>
#include <math/div.hpp>
#include <math/sum.hpp>
//---------------------------------------------------------------------------------------------------------------------------
// PRIVATE CONSTANTS
@@ -53,6 +58,11 @@
* worker threads can be larger than the work itself.
*/
static const panic::types::uint_t loss_categorical_crossentropy_omp_min_size = 500;
static const panic::types::real_t clip_min = 1e-7;
static const panic::types::real_t clip_max = 1 - 1e-7;
static const panic::types::real_t neg = -1;
//---------------------------------------------------------------------------------------------------------------------------
@@ -63,13 +73,47 @@ namespace panic{
// Function Name : panic::neural_network::loss_categorical_crossentropy::forward
//
// Description:
// Default implementation. Derived classes can override it.
// Default implementation for catecorical labels. Derived classes can override it.
//--------------------------------------------------------------------------------------------------------------------------
bool loss_categorical_crossentropy::forward(
const panic::tensor::real_matrix& y_pred,
const panic::tensor::uint_vector& y_true){
return false;
// Number of samples in a batch
const panic::types::uint_t samples = y_pred.rows();
if (samples != y_true.size()){
return false;
}
// clip data to prevent log by 0
// Clip both sides to not drag the mean towards any value
panic::tensor::real_matrix y_pred_cliped = panic::math::clip(y_pred, clip_min, clip_max);
// Vector to hold the correct confidences
panic::tensor::real_vector correct_confidences(samples);
PANIC_OMP_PARALLEL_FOR_IF(samples > loss_categorical_crossentropy_omp_min_size)
for (panic::types::uint_t i = 0; i < samples; ++i){
const panic::types::uint_t idx = y_true[i];
//if (idx >= y_pred.cols()){
// return false;
//}
correct_confidences[i] = y_pred_cliped(i, idx);
}
// Calculate losses
panic::tensor::real_vector negative_log_likelihoos(samples);
negative_log_likelihoos = panic::math::mul(panic::math::log(correct_confidences), neg);
sample_losses = negative_log_likelihoos;
return true;
}
@@ -77,16 +121,120 @@ bool loss_categorical_crossentropy::forward(
// Function Name : panic::neural_network::loss_categorical_crossentropy::forward
//
// Description:
// Default matrix-target implementation. Derived classes can override it.
// Default one-hot encoded labels implementation. Derived classes can override it.
//--------------------------------------------------------------------------------------------------------------------------
bool loss_categorical_crossentropy::forward(
const panic::tensor::real_matrix& y_pred,
const panic::tensor::real_matrix& y_true){
return false;
if (y_pred.rows() != y_true.rows() || y_pred.cols() != y_true.cols()){
return false;
}
// Number of samples in a batch
const panic::types::uint_t samples = y_pred.rows();
// clip data to prevent log by 0
// Clip both sides to not drag the mean towards any value
panic::tensor::real_matrix y_pred_cliped = panic::math::clip(y_pred, clip_min, clip_max);
// Vector to hold the correct confidences
panic::tensor::real_vector correct_confidences(samples);
correct_confidences = panic::math::sum_rowwise(panic::math::mul(y_pred_cliped, y_true));
// Calculate losses
panic::tensor::real_vector negative_log_likelihoos(samples);
negative_log_likelihoos = panic::math::mul(panic::math::log(correct_confidences), neg);
sample_losses = negative_log_likelihoos;
return true;
}
//--------------------------------------------------------------------------------------------------------------------------
// Function Name : panic::neural_network::loss_categorical_crossentropy::backward
//
// Description:
// Default implementation for catecorical labels. Derived classes can override it.
//--------------------------------------------------------------------------------------------------------------------------
bool loss_categorical_crossentropy::backward(
const panic::tensor::real_matrix& dvalues,
const panic::tensor::uint_vector& y_true){
if (dvalues.rows() == 0 || dvalues.cols() == 0 || y_true.size() != dvalues.rows()){
return false;
}
panic::tensor::real_matrix y_true_one_hot;
// Transforms it to one_hot
if (! panic::tensor::one_hot(dvalues.cols(), y_true, y_true_one_hot)){
return false;
}
// Uses other backward overloaded function to handle the rest.
return backward(dvalues, y_true_one_hot);
}
//--------------------------------------------------------------------------------------------------------------------------
// Function Name : panic::neural_network::loss_categorical_crossentropy::backward
//
// Description:
// Default one-hot encoded labels implementation. Derived classes can override it.
//--------------------------------------------------------------------------------------------------------------------------
bool loss_categorical_crossentropy::backward(
const panic::tensor::real_matrix& dvalues,
const panic::tensor::real_matrix& y_true){
// Number of samples
const panic::types::uint_t samples = dvalues.rows();
if (samples == 0 || dvalues.cols() == 0 || y_true.rows() != dvalues.rows() || y_true.cols() != dvalues.cols()){
return false;
}
// dinputs = y_true / dvalues
if (!panic::math::div(y_true, dvalues, dinputs)){
return false;
}
// scale = -1 / samples
const panic::types::real_t scale = -static_cast<panic::types::real_t>(1) / static_cast<panic::types::real_t>(samples);
// dinputs *= scale
if (!panic::math::mul(dinputs, scale, dinputs)){
return false;
}
return true;
}
} // namespace tensor
} // namespace panic