Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
# Compiler and flags
CC = gcc
CFLAGS = -I nn/include -I tests -Wall -Wextra -Werror -Wpedantic -Wstrict-prototypes -Wold-style-definition -g $(CU_CFLAGS)
# OpenMP Flag
ifdef USE_OPENMP
OPENMP_FLAG = -fopenmp
else
OPENMP_FLAG =
endif

CFLAGS = -I nn/include -I tests -Wall -Wextra -Werror -Wpedantic -Wstrict-prototypes -Wold-style-definition -g $(CU_CFLAGS) $(OPENMP_FLAG)

# Source files
SRCS = $(shell find nn/src -name '*.c' -not -path 'nn/src/main.c')
Expand Down
26 changes: 26 additions & 0 deletions model_summary.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
==================================
Neural Network Summary
==================================
Number of layers: 2
----------------------------------
Layer 1:
Weights matrix: 2 x 4
Bias matrix: 1 x 4
Activation: RELU
----------------------------------
Layer 2:
Weights matrix: 4 x 1
Bias matrix: 1 x 1
Activation: SIGMOID
==================================

==================================
Model Predictions
==================================
Input -> Expected | Predicted (Rounded)
----------------------------------
Input: (0, 0) -> Expected: 0 | Predicted: 0.5000 (Rounded: 1)
Input: (0, 1) -> Expected: 1 | Predicted: 0.9829 (Rounded: 1)
Input: (1, 0) -> Expected: 1 | Predicted: 0.9924 (Rounded: 1)
Input: (1, 1) -> Expected: 0 | Predicted: 0.9989 (Rounded: 1)
==================================
2 changes: 1 addition & 1 deletion nn/include/feedforward.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,4 @@ void free_network(NeuralNetwork* nn);
* @return Output activation of the last layer (batch_size x output_features).
* Caller owns and must free.
*/
Matrix* feedforward(NeuralNetwork* nn, const Matrix* input);
Matrix* feedforward(const NeuralNetwork* nn, const Matrix* input);
2 changes: 2 additions & 0 deletions nn/include/neural_network.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#pragma once

#include <stdio.h>

#include "activation.h"
#include "cache.h"
#include "linalg.h"
Expand Down
13 changes: 13 additions & 0 deletions nn/include/summary.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#ifndef NN_SUMMARY_H
#define NN_SUMMARY_H

#include <stdio.h>

#include "neural_network.h"

void fprint_network_summary(FILE* stream, const NeuralNetwork* nn);
void flog_training_progress(FILE* stream, int epoch, int epochs, double loss);
void fprint_model_predictions(FILE* stream, const NeuralNetwork* nn,
const Matrix* x_test, const Matrix* y_test);

#endif // NN_SUMMARY_H
24 changes: 22 additions & 2 deletions nn/src/examples/xor.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include "linalg.h"
#include "loss.h"
#include "neural_network.h"
#include "summary.h"
#include "utils.h"

/**
Expand Down Expand Up @@ -76,7 +77,16 @@ int main() {
}

// 3. Print Network Summary
print_network_summary(nn);
FILE* summary_file = fopen("model_summary.txt", "w");
if (summary_file == NULL) {
LOG_ERROR("Failed to open model_summary.txt for writing.");
// Handle error, but continue for now to avoid stopping the whole process
} else {
fprint_network_summary(summary_file, nn);
fprint_model_predictions(summary_file, nn, x_train,
y_train); // Add predictions
fclose(summary_file);
}

// 4. Training Parameters
double learning_rate = 0.1;
Expand All @@ -85,6 +95,11 @@ int main() {
printf("Training XOR network with %d epochs, learning rate %.2f\n", epochs,
learning_rate);

FILE* log_file = fopen("training_log.txt", "w");
if (log_file == NULL) {
LOG_ERROR("Failed to open training_log.txt for writing.");
}

// 5. Training Loop
for (int epoch = 0; epoch < epochs; epoch++) {
double total_loss = 0;
Expand Down Expand Up @@ -124,7 +139,9 @@ int main() {
}
free_matrix(y_hat);

log_training_progress(epoch, epochs, total_loss);
if (log_file != NULL) {
flog_training_progress(log_file, epoch, epochs, total_loss);
}
}

printf("\nTraining complete. Testing network...\n");
Expand Down Expand Up @@ -153,6 +170,9 @@ int main() {
free_matrix(x_train);
free_matrix(y_train);
free_matrix(predictions);

if (log_file != NULL) fclose(log_file);

free_network(nn);

printf("\nMemory freed. XOR example finished.\n");
Expand Down
72 changes: 53 additions & 19 deletions nn/src/linalg/operations.c
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@
#include "linalg.h"
#include "utils.h"

#ifdef USE_OPENMP
#include <omp.h>
#endif

//============================
// Functions for Matrix Operations
//============================
Expand Down Expand Up @@ -46,6 +50,9 @@ Matrix* add_matrix(Matrix* a, Matrix* b) {
Matrix* result = create_matrix(a->rows, a->cols);
size_t total_elements = a->rows * a->cols;

#ifdef USE_OPENMP
#pragma omp parallel for
#endif
for (size_t i = 0; i < total_elements; i++) {
result->matrix_data[i] = a->matrix_data[i] + b->matrix_data[i];
}
Expand All @@ -69,6 +76,9 @@ Matrix* subtract_matrix(Matrix* a, Matrix* b) {
Matrix* result = create_matrix(a->rows, a->cols);
size_t total_elements = a->rows * a->cols;

#ifdef USE_OPENMP
#pragma omp parallel for
#endif
for (size_t i = 0; i < total_elements; i++) {
result->matrix_data[i] = a->matrix_data[i] - b->matrix_data[i];
}
Expand All @@ -94,6 +104,9 @@ Matrix* multiply_matrix(Matrix* a, Matrix* b) {
Matrix* result = create_matrix(a->rows, a->cols);
size_t total_elements = a->rows * a->cols;

#ifdef USE_OPENMP
#pragma omp parallel for
#endif
for (size_t i = 0; i < total_elements; i++) {
result->matrix_data[i] = a->matrix_data[i] * b->matrix_data[i];
}
Expand All @@ -115,6 +128,9 @@ Matrix* apply_onto_matrix(double (*func)(double), Matrix* m) {
Matrix* result = create_matrix(m->rows, m->cols);
size_t total_elements = m->rows * m->cols;

#ifdef USE_OPENMP
#pragma omp parallel for
#endif
for (size_t i = 0; i < total_elements; i++) {
result->matrix_data[i] = func(m->matrix_data[i]);
}
Expand All @@ -135,6 +151,9 @@ Matrix* add_scalar_to_matrix(Matrix* m, double n) {
Matrix* result = create_matrix(m->rows, m->cols);
size_t total_elements = m->rows * m->cols;

#ifdef USE_OPENMP
#pragma omp parallel for
#endif
for (size_t i = 0; i < total_elements; i++) {
result->matrix_data[i] = m->matrix_data[i] + n;
}
Expand All @@ -149,29 +168,32 @@ Matrix* add_scalar_to_matrix(Matrix* m, double n) {
* @param b The second matrix.
* @return A new matrix containing the result of the dot product a * b.
*/
Matrix* dot_matrix(Matrix* a, Matrix* b) {
ASSERT(a != NULL && b != NULL, "Input matrices cannot be NULL.");
ASSERT(a->cols == b->rows,
"The number of columns in the first matrix must equal the number of "
"rows in the second matrix for dot product.");

LOG_INFO("Performing dot product on a %zux%zu and a %zux%zu matrix.", a->rows,
a->cols, b->rows, b->cols);
Matrix* result = create_matrix(a->rows, b->cols);

for (size_t i = 0; i < a->rows; i++) {
for (size_t j = 0; j < b->cols; j++) {
double sum = 0;
for (size_t k = 0; k < a->cols; k++) {
sum +=
a->matrix_data[i * a->cols + k] * b->matrix_data[k * b->cols + j];
Matrix* dot_matrix(Matrix* m1, Matrix* m2) {
ASSERT(m1 != NULL && m2 != NULL, "Input matrices cannot be NULL.");
ASSERT(m1->cols == m2->rows,
"Matrices dimensions are incompatible for dot product.");

LOG_INFO("Performing dot product of %zux%zu and %zux%zu matrices.", m1->rows,
m1->cols, m2->rows, m2->cols);
Matrix* result = create_matrix(m1->rows, m2->cols);
// Initialize result matrix with zeros
memset(result->matrix_data, 0, m1->rows * m2->cols * sizeof(double));

#ifdef USE_OPENMP
#pragma omp parallel for collapse(2)
#endif
for (size_t i = 0; i < m1->rows; i++) {
for (size_t j = 0; j < m2->cols; j++) {
for (size_t k = 0; k < m1->cols; k++) {
result->matrix_data[i * result->cols + j] +=
m1->matrix_data[i * m1->cols + k] *
m2->matrix_data[k * m2->cols + j];
}
result->matrix_data[i * result->cols + j] = sum;
}
}

LOG_INFO("Matrix dot product complete. Resulting matrix is %zux%zu.",
result->rows, result->cols);
LOG_INFO("Dot product complete. Resulting matrix is %zux%zu.", result->rows,
result->cols);
return result;
}

Expand All @@ -185,6 +207,9 @@ Matrix* transpose_matrix(Matrix* m) {
LOG_INFO("Transposing a %zux%zu matrix.", m->rows, m->cols);
Matrix* result = create_matrix(m->cols, m->rows);

#ifdef USE_OPENMP
#pragma omp parallel for collapse(2)
#endif
for (size_t i = 0; i < m->rows; i++) {
for (size_t j = 0; j < m->cols; j++) {
result->matrix_data[j * result->cols + i] =
Expand All @@ -209,6 +234,9 @@ Matrix* scale_matrix(double n, Matrix* m) {
Matrix* result = create_matrix(m->rows, m->cols);
size_t total_elements = m->rows * m->cols;

#ifdef USE_OPENMP
#pragma omp parallel for
#endif
for (size_t i = 0; i < total_elements; i++) {
result->matrix_data[i] = m->matrix_data[i] * n;
}
Expand All @@ -233,6 +261,9 @@ Matrix* add_bias_to_matrix(Matrix* m, Matrix* bias) {
Matrix* result = create_matrix(m->rows, m->cols);
ASSERT(result != NULL, "Failed to create matrix for bias addition.");

#ifdef USE_OPENMP
#pragma omp parallel for collapse(2)
#endif
for (size_t i = 0; i < m->rows; i++) {
for (size_t j = 0; j < m->cols; j++) {
result->matrix_data[i * m->cols + j] =
Expand All @@ -255,6 +286,9 @@ Matrix* sum_matrix_columns(Matrix* m) {
Matrix* result = create_matrix(1, m->cols);
ASSERT(result != NULL, "Failed to create matrix for column summation.");

#ifdef USE_OPENMP
#pragma omp parallel for
#endif
for (size_t j = 0; j < m->cols; j++) {
double sum = 0;
for (size_t i = 0; i < m->rows; i++) {
Expand Down
2 changes: 1 addition & 1 deletion nn/src/neural_network/feedforward.c
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ void free_network(NeuralNetwork* nn) {
* @return A new matrix containing the output of the last layer of the network.
* The caller is responsible for freeing this matrix.
*/
Matrix* feedforward(NeuralNetwork* nn, const Matrix* input) {
Matrix* feedforward(const NeuralNetwork* nn, const Matrix* input) {
ASSERT(nn != NULL, "Neural Network pointer cannot be NULL.");
ASSERT(input != NULL, "Input matrix cannot be NULL.");
ASSERT(input->cols == nn->layers[0]->weights->rows,
Expand Down
38 changes: 38 additions & 0 deletions nn/src/neural_network/summary.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#include <math.h>
#include <stdio.h>

#include "feedforward.h"
#include "neural_network.h"
#include "utils.h"

Expand Down Expand Up @@ -32,4 +34,40 @@ void flog_training_progress(FILE* stream, int epoch, int epochs, double loss) {
if (epoch % 100 == 0 || epoch == epochs - 1) {
fprintf(stream, "Epoch %d/%d, Loss: %f\n", epoch, epochs, loss);
}
}

void fprint_model_predictions(FILE* stream, const NeuralNetwork* nn,
const Matrix* x_test, const Matrix* y_test) {
if (nn == NULL || x_test == NULL || y_test == NULL) {
fprintf(stream,
"Cannot print predictions: NeuralNetwork, x_test, or y_test is "
"NULL.\n");
return;
}

Matrix* predictions = feedforward(nn, x_test);
if (predictions == NULL) {
fprintf(stream, "Failed to generate predictions.\n");
return;
}

fprintf(stream, "\n==================================\n");
fprintf(stream, " Model Predictions \n");
fprintf(stream, "==================================\n");
fprintf(stream, "Input -> Expected | Predicted (Rounded)\n");
fprintf(stream, "----------------------------------\n");

for (size_t i = 0; i < x_test->rows; i++) {
fprintf(stream, "Input: (");
for (size_t j = 0; j < x_test->cols; j++) {
fprintf(stream, "%.0f%s", x_test->matrix_data[i * x_test->cols + j],
(j == x_test->cols - 1) ? "" : ", ");
}
fprintf(stream, ") -> Expected: %.0f | Predicted: %.4f (Rounded: %.0f)\n",
y_test->matrix_data[i], predictions->matrix_data[i],
round(predictions->matrix_data[i]));
}
fprintf(stream, "==================================\n");

free_matrix(predictions);
}
21 changes: 21 additions & 0 deletions training_log.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
Epoch 0/2000, Loss: 0.312058
Epoch 100/2000, Loss: 0.257256
Epoch 200/2000, Loss: 0.019960
Epoch 300/2000, Loss: 0.006621
Epoch 400/2000, Loss: 0.003768
Epoch 500/2000, Loss: 0.002665
Epoch 600/2000, Loss: 0.002038
Epoch 700/2000, Loss: 0.001627
Epoch 800/2000, Loss: 0.001347
Epoch 900/2000, Loss: 0.001140
Epoch 1000/2000, Loss: 0.000984
Epoch 1100/2000, Loss: 0.000863
Epoch 1200/2000, Loss: 0.000772
Epoch 1300/2000, Loss: 0.000702
Epoch 1400/2000, Loss: 0.000642
Epoch 1500/2000, Loss: 0.000591
Epoch 1600/2000, Loss: 0.000547
Epoch 1700/2000, Loss: 0.000509
Epoch 1800/2000, Loss: 0.000475
Epoch 1900/2000, Loss: 0.000445
Epoch 1999/2000, Loss: 0.000419
Loading