From 4b9065e9af6ca68a60c55433b908e49163f84951 Mon Sep 17 00:00:00 2001 From: Az-r-ow Date: Tue, 14 May 2024 16:00:17 +0200 Subject: [PATCH 1/2] feat: Binary cross-entropy loss --- src/NeuralNet/Network.cpp | 3 +++ src/NeuralNet/losses/BCE.hpp | 23 +++++++++++++++++++++++ src/NeuralNet/losses/Loss.hpp | 4 ++-- src/NeuralNet/losses/losses.hpp | 3 ++- src/NeuralNet/utils/Enums.hpp | 3 ++- src/bindings/NeuralNetPy.cpp | 3 ++- 6 files changed, 34 insertions(+), 5 deletions(-) create mode 100644 src/NeuralNet/losses/BCE.hpp diff --git a/src/NeuralNet/Network.cpp b/src/NeuralNet/Network.cpp index 7998a78..ca683f7 100644 --- a/src/NeuralNet/Network.cpp +++ b/src/NeuralNet/Network.cpp @@ -35,6 +35,9 @@ void Network::setLoss(LOSS loss) { this->cmpLoss = MCE::cmpLoss; this->cmpLossGrad = MCE::cmpLossGrad; break; + case LOSS::BCE: + this->cmpLoss = BCE::cmpLoss; + this->cmpLossGrad = BCE::cmpLossGrad; default: assert(false && "Loss not defined"); break; diff --git a/src/NeuralNet/losses/BCE.hpp b/src/NeuralNet/losses/BCE.hpp new file mode 100644 index 0000000..0c43eec --- /dev/null +++ b/src/NeuralNet/losses/BCE.hpp @@ -0,0 +1,23 @@ +#pragma once + +#include "Loss.hpp" + +namespace NeuralNet { +/** + * Binary Cross-Entropy + */ +class BCE : public Loss { + public: + static double cmpLoss(const Eigen::MatrixXd &o, const Eigen::MatrixXd &y) { + Eigen::MatrixXd loss = + -(y.array() * o.array().log() + (1.0 - y.array()).log()); + return loss.sum(); + } + + static Eigen::MatrixXd cmpLossGrad(const Eigen::MatrixXd &yHat, + const Eigen::MatrixXd &y) { + return (yHat.array() - y.array()) / (yHat.array() * (1.0 - y.array())); + } +}; + +} // namespace NeuralNet \ No newline at end of file diff --git a/src/NeuralNet/losses/Loss.hpp b/src/NeuralNet/losses/Loss.hpp index ebff7f7..6c21178 100644 --- a/src/NeuralNet/losses/Loss.hpp +++ b/src/NeuralNet/losses/Loss.hpp @@ -18,12 +18,12 @@ class Loss { /** * @brief This function computes the loss gradient w.r.t the outputs * - * @param o The outputs from the output layer + * @param yHat The outputs from the output layer * @param y The labels (expected vals) * * @return The current iteration's gradient */ - static Eigen::MatrixXd cmpLossGrad(const Eigen::MatrixXd &o, + static Eigen::MatrixXd cmpLossGrad(const Eigen::MatrixXd &yHat, const Eigen::MatrixXd &y); }; } // namespace NeuralNet \ No newline at end of file diff --git a/src/NeuralNet/losses/losses.hpp b/src/NeuralNet/losses/losses.hpp index 87937f6..d0c72d7 100644 --- a/src/NeuralNet/losses/losses.hpp +++ b/src/NeuralNet/losses/losses.hpp @@ -5,5 +5,6 @@ #pragma once -#include "MCE.hpp" +#include "BCE.hpp" // Binary Cross-Entropy +#include "MCE.hpp" // Multiclass Cross-Entropy #include "Quadratic.hpp" diff --git a/src/NeuralNet/utils/Enums.hpp b/src/NeuralNet/utils/Enums.hpp index 7b75341..5a42585 100644 --- a/src/NeuralNet/utils/Enums.hpp +++ b/src/NeuralNet/utils/Enums.hpp @@ -17,6 +17,7 @@ enum class WEIGHT_INIT { enum class LOSS { MCE, // Multi-class Cross Entropy - QUADRATIC + QUADRATIC, + BCE // Binary Cross-Entropy }; } // namespace NeuralNet diff --git a/src/bindings/NeuralNetPy.cpp b/src/bindings/NeuralNetPy.cpp index 90fc9e4..ff6a2f5 100644 --- a/src/bindings/NeuralNetPy.cpp +++ b/src/bindings/NeuralNetPy.cpp @@ -67,7 +67,8 @@ PYBIND11_MODULE(NeuralNetPy, m) { py::enum_(m, "LOSS") .value("QUADRATIC", LOSS::QUADRATIC) - .value("MCE", LOSS::MCE); + .value("MCE", LOSS::MCE) + .value("BCE", LOSS::BCE); py::module optimizers_m = m.def_submodule("optimizers", R"pbdoc( Optimizers From 5bf80d9703e3268e4463935cad1227cd6ffb3891 Mon Sep 17 00:00:00 2001 From: Az-r-ow Date: Tue, 14 May 2024 17:03:55 +0200 Subject: [PATCH 2/2] tests: changed checkpoint file name --- tests/test-callbacks.cpp | 3 ++- tests/test-network.cpp | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/test-callbacks.cpp b/tests/test-callbacks.cpp index ed4c9b4..b17e854 100644 --- a/tests/test-callbacks.cpp +++ b/tests/test-callbacks.cpp @@ -2,6 +2,7 @@ #include #include #include +#include #include #include @@ -11,7 +12,7 @@ TEST_CASE( "EarlyStopping callback throws exception when the metric is not found", "[callback]") { std::shared_ptr earlyStopping = - std::make_shared("LOSS", 0.1); + std::make_shared("NOT_A_METRIC", 0.1); Network network; diff --git a/tests/test-network.cpp b/tests/test-network.cpp index e6c6ac7..a6ef157 100644 --- a/tests/test-network.cpp +++ b/tests/test-network.cpp @@ -214,7 +214,7 @@ SCENARIO("The network updates the weights and biases as pre-calculated") { Network checkpoint; - Model::load_from_file("checkpoint-0.bin", checkpoint); + Model::load_from_file("N9NeuralNet7NetworkE-checkpoint-0.bin", checkpoint); REQUIRE(checkpoint.getNumLayers() == network.getNumLayers());