From ed82bb141db9191ac657f3685da14c9e732178d5 Mon Sep 17 00:00:00 2001 From: Shreyash Date: Sat, 30 Aug 2025 23:39:49 +0530 Subject: [PATCH 1/3] feat: add loss header file --- nn/include/loss.h | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 nn/include/loss.h diff --git a/nn/include/loss.h b/nn/include/loss.h new file mode 100644 index 0000000..3eca080 --- /dev/null +++ b/nn/include/loss.h @@ -0,0 +1,27 @@ +#pragma once + +#include "linalg.h" +#include "utils.h" + +//============================== +// My first loss function library +// I'm going to define common loss functions and their gradients here +//============================== + +//===================== +// Loss Functions +//===================== + +double mean_squared_error(const Matrix* y_hat, const Matrix* y); +double categorical_cross_entropy(const Matrix* y_hat, const Matrix* y); +double mean_absolute_error(const Matrix* y_hat, const Matrix* y); +double binary_cross_entropy(const Matrix* y_hat, const Matrix* y); + +//============================== +// Loss Function Gradients +//============================== + +Matrix* mean_squared_error_gradient(const Matrix* y_hat, const Matrix* y); +Matrix* categorical_cross_entropy_gradient(const Matrix* y_hat, const Matrix* y); +Matrix* mean_absolute_error_gradient(const Matrix* y_hat, const Matrix* y); +Matrix* binary_cross_entropy_gradient(const Matrix* y_hat, const Matrix* y); From d8de6aa03e7bfc764d795c735d456c45e9572e21 Mon Sep 17 00:00:00 2001 From: Shreyash Date: Sat, 30 Aug 2025 23:41:38 +0530 Subject: [PATCH 2/3] feat: add loss function definitions --- nn/src/loss/loss.c | 132 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 132 insertions(+) create mode 100644 nn/src/loss/loss.c diff --git a/nn/src/loss/loss.c b/nn/src/loss/loss.c new file mode 100644 index 0000000..c81241d --- /dev/null +++ b/nn/src/loss/loss.c @@ -0,0 +1,132 @@ +#include +#include +#include +#include + +#include "linalg.h" +#include "loss.h" +#include "utils.h" + +// A small value to prevent log(0) errors. +#define EPSILON 1e-15 + +double mean_squared_error(const Matrix* y_hat, const Matrix* y) { + ASSERT(y_hat->rows == y->rows && y_hat->cols == y->cols, + "MSE: Matrices must have matching dimensions."); + + double loss = 0.0; + int total_elements = y_hat->rows * y_hat->cols; + + for (int i = 0; i < total_elements; i++) { + double diff = y_hat->matrix_data[i] - y->matrix_data[i]; + loss += pow(diff, 2); + } + + return loss / total_elements; +} + +double categorical_cross_entropy(const Matrix* y_hat, const Matrix* y) { + ASSERT(y_hat->rows == y->rows && y_hat->cols == y->cols, + "Categorical Cross-Entropy: Matrices must have matching dimensions."); + + double loss = 0.0; + int total_elements = y_hat->rows * y_hat->cols; + + for (int i = 0; i < total_elements; i++) { + loss -= y->matrix_data[i] * log(y_hat->matrix_data[i] + EPSILON); + } + + return loss / y_hat->rows; +} + +double mean_absolute_error(const Matrix* y_hat, const Matrix* y) { + ASSERT(y_hat->rows == y->rows && y_hat->cols == y->cols, + "MAE: Matrices must have matching dimensions."); + + double loss = 0.0; + int total_elements = y_hat->rows * y_hat->cols; + + for (int i = 0; i < total_elements; i++) { + loss += fabs(y_hat->matrix_data[i] - y->matrix_data[i]); + } + + return loss / total_elements; +} + +double binary_cross_entropy(const Matrix* y_hat, const Matrix* y) { + ASSERT(y_hat->rows == y->rows && y_hat->cols == y->cols, + "Binary Cross-Entropy: Matrices must have matching dimensions."); + + double loss = 0.0; + int total_elements = y_hat->rows * y_hat->cols; + + for (int i = 0; i < total_elements; i++) { + loss -= y->matrix_data[i] * log(y_hat->matrix_data[i] + EPSILON) + + (1 - y->matrix_data[i]) * log(1 - y_hat->matrix_data[i] + EPSILON); + } + + return loss / total_elements; +} + +Matrix* mean_squared_error_gradient(const Matrix* y_hat, const Matrix* y) { + ASSERT(y_hat->rows == y->rows && y_hat->cols == y->cols, + "MSE Gradient: Matrices must have matching dimensions."); + + Matrix* gradient = create_matrix(y_hat->rows, y_hat->cols); + int total_elements = y_hat->rows * y_hat->cols; + + for (int i = 0; i < total_elements; i++) { + gradient->matrix_data[i] = 2.0 * (y_hat->matrix_data[i] - y->matrix_data[i]); + } + + return gradient; +} + +Matrix* categorical_cross_entropy_gradient(const Matrix* y_hat, const Matrix* y) { + ASSERT(y_hat->rows == y->rows && y_hat->cols == y->cols, + "Categorical Cross-Entropy Gradient: Matrices must have matching dimensions."); + + Matrix* gradient = create_matrix(y_hat->rows, y_hat->cols); + int total_elements = y_hat->rows * y_hat->cols; + + for (int i = 0; i < total_elements; i++) { + gradient->matrix_data[i] = -y->matrix_data[i] / (y_hat->matrix_data[i] + EPSILON); + } + + return gradient; +} + +Matrix* mean_absolute_error_gradient(const Matrix* y_hat, const Matrix* y) { + ASSERT(y_hat->rows == y->rows && y_hat->cols == y->cols, + "MAE Gradient: Matrices must have matching dimensions."); + + Matrix* gradient = create_matrix(y_hat->rows, y_hat->cols); + int total_elements = y_hat->rows * y_hat->cols; + + for (int i = 0; i < total_elements; i++) { + if (y_hat->matrix_data[i] > y->matrix_data[i]) { + gradient->matrix_data[i] = 1.0; + } else if (y_hat->matrix_data[i] < y->matrix_data[i]) { + gradient->matrix_data[i] = -1.0; + } else { + gradient->matrix_data[i] = 0.0; + } + } + + return gradient; +} + +Matrix* binary_cross_entropy_gradient(const Matrix* y_hat, const Matrix* y) { + ASSERT(y_hat->rows == y->rows && y_hat->cols == y->cols, + "Binary Cross-Entropy Gradient: Matrices must have matching dimensions."); + + Matrix* gradient = create_matrix(y_hat->rows, y_hat->cols); + int total_elements = y_hat->rows * y_hat->cols; + + for (int i = 0; i < total_elements; i++) { + gradient->matrix_data[i] = (y_hat->matrix_data[i] - y->matrix_data[i]) / + (y_hat->matrix_data[i] * (1 - y_hat->matrix_data[i]) + EPSILON); + } + + return gradient; +} From 59b74b9a7160d6e0732c4010c1a1101d7fd27695 Mon Sep 17 00:00:00 2001 From: Shreyash Date: Sat, 30 Aug 2025 23:42:35 +0530 Subject: [PATCH 3/3] style: format and lint recent code --- nn/include/loss.h | 3 ++- nn/src/loss/loss.c | 25 ++++++++++++++++--------- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/nn/include/loss.h b/nn/include/loss.h index 3eca080..7c3a2fa 100644 --- a/nn/include/loss.h +++ b/nn/include/loss.h @@ -22,6 +22,7 @@ double binary_cross_entropy(const Matrix* y_hat, const Matrix* y); //============================== Matrix* mean_squared_error_gradient(const Matrix* y_hat, const Matrix* y); -Matrix* categorical_cross_entropy_gradient(const Matrix* y_hat, const Matrix* y); +Matrix* categorical_cross_entropy_gradient(const Matrix* y_hat, + const Matrix* y); Matrix* mean_absolute_error_gradient(const Matrix* y_hat, const Matrix* y); Matrix* binary_cross_entropy_gradient(const Matrix* y_hat, const Matrix* y); diff --git a/nn/src/loss/loss.c b/nn/src/loss/loss.c index c81241d..bb25bf3 100644 --- a/nn/src/loss/loss.c +++ b/nn/src/loss/loss.c @@ -1,10 +1,11 @@ +#include "loss.h" + #include #include #include #include #include "linalg.h" -#include "loss.h" #include "utils.h" // A small value to prevent log(0) errors. @@ -76,21 +77,25 @@ Matrix* mean_squared_error_gradient(const Matrix* y_hat, const Matrix* y) { int total_elements = y_hat->rows * y_hat->cols; for (int i = 0; i < total_elements; i++) { - gradient->matrix_data[i] = 2.0 * (y_hat->matrix_data[i] - y->matrix_data[i]); + gradient->matrix_data[i] = + 2.0 * (y_hat->matrix_data[i] - y->matrix_data[i]); } return gradient; } -Matrix* categorical_cross_entropy_gradient(const Matrix* y_hat, const Matrix* y) { +Matrix* categorical_cross_entropy_gradient(const Matrix* y_hat, + const Matrix* y) { ASSERT(y_hat->rows == y->rows && y_hat->cols == y->cols, - "Categorical Cross-Entropy Gradient: Matrices must have matching dimensions."); + "Categorical Cross-Entropy Gradient: Matrices must have matching " + "dimensions."); Matrix* gradient = create_matrix(y_hat->rows, y_hat->cols); int total_elements = y_hat->rows * y_hat->cols; for (int i = 0; i < total_elements; i++) { - gradient->matrix_data[i] = -y->matrix_data[i] / (y_hat->matrix_data[i] + EPSILON); + gradient->matrix_data[i] = + -y->matrix_data[i] / (y_hat->matrix_data[i] + EPSILON); } return gradient; @@ -117,15 +122,17 @@ Matrix* mean_absolute_error_gradient(const Matrix* y_hat, const Matrix* y) { } Matrix* binary_cross_entropy_gradient(const Matrix* y_hat, const Matrix* y) { - ASSERT(y_hat->rows == y->rows && y_hat->cols == y->cols, - "Binary Cross-Entropy Gradient: Matrices must have matching dimensions."); + ASSERT( + y_hat->rows == y->rows && y_hat->cols == y->cols, + "Binary Cross-Entropy Gradient: Matrices must have matching dimensions."); Matrix* gradient = create_matrix(y_hat->rows, y_hat->cols); int total_elements = y_hat->rows * y_hat->cols; for (int i = 0; i < total_elements; i++) { - gradient->matrix_data[i] = (y_hat->matrix_data[i] - y->matrix_data[i]) / - (y_hat->matrix_data[i] * (1 - y_hat->matrix_data[i]) + EPSILON); + gradient->matrix_data[i] = + (y_hat->matrix_data[i] - y->matrix_data[i]) / + (y_hat->matrix_data[i] * (1 - y_hat->matrix_data[i]) + EPSILON); } return gradient;