diff --git a/nn/include/feedforward.h b/nn/include/feedforward.h new file mode 100644 index 0000000..fe76aec --- /dev/null +++ b/nn/include/feedforward.h @@ -0,0 +1,14 @@ +#pragma once + +#include "neural_network.h" + +//============================== +// Forward Pass Functions +//============================== + +NeuralNetwork* create_network(int num_layers); + +void free_network(NeuralNetwork* nn); + +// It caches the intermediate results for the backpropagation algorithm. +Matrix* feedforward(NeuralNetwork* nn, const Matrix* input); diff --git a/nn/include/neural_network.h b/nn/include/neural_network.h new file mode 100644 index 0000000..e735a93 --- /dev/null +++ b/nn/include/neural_network.h @@ -0,0 +1,33 @@ +#pragma once + +#include "activation.h" +#include "cache.h" +#include "linalg.h" + +//============================== +// Neural Network Layer Struct +//============================== + +typedef Matrix* (*ActivationFunc)(Matrix*); + +typedef struct { + Matrix* weights; + Matrix* bias; + + ActivationFunc activation; + + // The leak parameter for the Leaky ReLU activation function. + double leak_parameter; +} Layer; + +//============================== +// Neural Network Struct +//============================== + +typedef struct { + Layer** layers; + int num_layers; + + // A caching mechanism to store intermediate values from the forward pass. + Cache* cache; +} NeuralNetwork; diff --git a/nn/include/utils.h b/nn/include/utils.h index 5252130..09572bf 100644 --- a/nn/include/utils.h +++ b/nn/include/utils.h @@ -29,6 +29,12 @@ typedef enum { // For safe malloc allocations #define CHECK_MALLOC(ptr, message) ASSERT((ptr) != NULL, message); +// Macro to handle memory allocation checks. +#define CHECK_ALLOC(ptr) \ + if (ptr == NULL) { \ + return NULL; \ + } + // Function prototypes for logging void log_message(LogLevel level, const char* format, ...); diff --git a/nn/src/neural_network/feedforward.c b/nn/src/neural_network/feedforward.c new file mode 100644 index 0000000..1e90e50 --- /dev/null +++ b/nn/src/neural_network/feedforward.c @@ -0,0 +1,96 @@ +#include "feedforward.h" + +#include +#include +#include + +#include "linalg.h" +#include "neural_network.h" +#include "utils.h" + +NeuralNetwork* create_network(int num_layers) { + NeuralNetwork* nn = (NeuralNetwork*)malloc(sizeof(NeuralNetwork)); + if (nn == NULL) { + LOG_ERROR("Memory allocation failed for Neural Network struct."); + return NULL; + } + + nn->layers = (Layer**)malloc(sizeof(Layer*) * num_layers); + if (nn->layers == NULL) { + LOG_ERROR("Memory allocation failed for layers array."); + free(nn); + return NULL; + } + + nn->num_layers = num_layers; + nn->cache = init_cache(); + if (nn->cache == NULL) { + LOG_ERROR("Failed to initialize cache."); + free(nn->layers); + free(nn); + return NULL; + } + return nn; +} + +void free_network(NeuralNetwork* nn) { + if (nn == NULL) { + return; + } + + if (nn->layers != NULL) { + for (int i = 0; i < nn->num_layers; i++) { + if (nn->layers[i] != NULL) { + if (nn->layers[i]->weights != NULL) { + free_matrix(nn->layers[i]->weights); + } + if (nn->layers[i]->bias != NULL) { + free_matrix(nn->layers[i]->bias); + } + free(nn->layers[i]); + } + } + free(nn->layers); + } + if (nn->cache != NULL) { + clear_cache(nn->cache); + free(nn->cache); + } + free(nn); +} + +Matrix* feedforward(NeuralNetwork* nn, const Matrix* input) { + ASSERT(nn != NULL, "Neural Network pointer cannot be NULL."); + ASSERT(input != NULL, "Input matrix cannot be NULL."); + ASSERT(input->rows == nn->layers[0]->weights->cols, + "Input dimensions must match network dimensions."); + + Matrix* current_output = copy_matrix(input); + + // Cache the input for the backpropagation algorithm. + put_matrix(nn->cache, "input", current_output); + + for (int i = 0; i < nn->num_layers; i++) { + Matrix* z = dot_matrix(current_output, nn->layers[i]->weights); + + add_matrix(z, nn->layers[i]->bias); + + // Cache the intermediate value (z). + char z_key[32]; + sprintf(z_key, "z_%d", i); + put_matrix(nn->cache, z_key, z); + + Matrix* a = apply_onto_matrix(nn->layers[i]->activation, z); + + // Cache the activated output (a). + char a_key[32]; + sprintf(a_key, "a_%d", i); + put_matrix(nn->cache, a_key, a); + + free_matrix(z); + free_matrix(current_output); + current_output = a; + } + + return current_output; +}