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
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ jobs:
sudo apt-get install -y cmake libgtest-dev clang-format libopenblas-dev

- name: Configure CMake
run: cmake -S . -B build -DUSE_THREADING=OFF
run: cmake -S . -B build

- name: Build
run: cmake --build build
Expand Down
33 changes: 18 additions & 15 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,9 @@ set(WARNING_FLAGS -Wall -Wextra -Wpedantic)
set(RELEASE_FLAGS -Ofast -DNDEBUG -march=native -flto -funroll-loops)
set(PROFILE_FLAG -O3 -p -g)

set(INCLUDE_FOLDER ${CMAKE_SOURCE_DIR}/include ${CMAKE_SOURCE_DIR}/external ${CMAKE_SOURCE_DIR}/src)
set(INCLUDE_FOLDER_PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/external ${CMAKE_CURRENT_SOURCE_DIR}/src)

option(USE_THREADING "Enable multithreading support for batch training" ON)
option(USE_THREADING "Enable multithreading support for batch training" OFF)
if(USE_THREADING)
add_compile_definitions(USE_THREADING)
endif()
Expand All @@ -57,30 +57,33 @@ endif()
# libcneuron library
add_library(cneuron ${SRC_FILES})
target_link_libraries(cneuron PRIVATE ${BLAS_LIBRARIES} m)
target_include_directories(cneuron PRIVATE ${INCLUDE_FOLDER})
target_include_directories(cneuron PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include)
target_include_directories(cneuron PRIVATE ${INCLUDE_FOLDER_PRIVATE})

# Main executable
add_executable(cneuron_cli src/main.c)
target_link_libraries(cneuron_cli PRIVATE cneuron)
target_include_directories(cneuron_cli PRIVATE ${CMAKE_SOURCE_DIR}/include ${CMAKE_SOURCE_DIR}/external)
target_include_directories(cneuron_cli PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include ${CMAKE_CURRENT_SOURCE_DIR}/external)

# Test executable
if(ENABLE_TESTING)
add_executable(tests ${TEST_FILES})
target_include_directories(tests PRIVATE ${CMAKE_SOURCE_DIR}/test)
target_include_directories(tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/test)
target_link_libraries(tests PRIVATE cneuron GTest::gtest_main)
add_test(NAME tests COMMAND tests)
target_include_directories(tests PRIVATE ${INCLUDE_FOLDER})
target_include_directories(tests PRIVATE ${INCLUDE_FOLDER_PRIVATE} ${CMAKE_CURRENT_SOURCE_DIR}/include)
endif()


message(STATUS "Copying data and input.py to the build directory...")
file(COPY ${CMAKE_SOURCE_DIR}/data ${CMAKE_SOURCE_DIR}/input.py DESTINATION ${CMAKE_BINARY_DIR})
file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/output)
if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR)
message(STATUS "Copying data and input.py to the build directory...")
file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/data ${CMAKE_CURRENT_SOURCE_DIR}/input.py DESTINATION ${CMAKE_CURRENT_BINARY_DIR})
file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/output)

add_custom_target(run
COMMAND ${CMAKE_COMMAND} -E echo "Using BLAS vendor: ${BLA_VENDOR}"
COMMAND ${CMAKE_COMMAND} -E echo "Building in: ${CMAKE_BUILD_TYPE} mode"
COMMAND ${CMAKE_BINARY_DIR}/cneuron_cli
DEPENDS cneuron_cli $<$<BOOL:${ENABLE_TESTING}>:tests>
)
add_custom_target(run
COMMAND ${CMAKE_COMMAND} -E echo "Using BLAS vendor: ${BLA_VENDOR}"
COMMAND ${CMAKE_COMMAND} -E echo "Building in: ${CMAKE_BUILD_TYPE} mode"
COMMAND ${CMAKE_CURRENT_BINARY_DIR}/cneuron_cli
DEPENDS cneuron_cli $<$<BOOL:${ENABLE_TESTING}>:tests>
)
endif()
31 changes: 16 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,28 +25,28 @@ float sigmoid(float val, bool is_deravative) {
return result;
}

int main() {
int main(void) {
// Create XOR dataset
size_t dataset_length = 4;
size_t inputs_length = 2;
dataset *test_dataset = alloc_dataset(dataset_length, inputs_length);

// XOR gate
test_dataset->datas[0].inputs[0] = 1.0f;
test_dataset->datas[0].inputs[1] = 1.0f;
test_dataset->datas[0].expected_index = 0;
test_dataset->all_inputs[0] = 1.0f;
test_dataset->all_inputs[1] = 1.0f;
test_dataset->expected_indices[0] = 0;

test_dataset->datas[1].inputs[0] = 0.0f;
test_dataset->datas[1].inputs[1] = 0.0f;
test_dataset->datas[1].expected_index = 0;
test_dataset->all_inputs[2] = 0.0f;
test_dataset->all_inputs[3] = 0.0f;
test_dataset->expected_indices[1] = 0;

test_dataset->datas[2].inputs[0] = 0.0f;
test_dataset->datas[2].inputs[1] = 1.0f;
test_dataset->datas[2].expected_index = 1;
test_dataset->all_inputs[4] = 0.0f;
test_dataset->all_inputs[5] = 1.0f;
test_dataset->expected_indices[2] = 1;

test_dataset->datas[3].inputs[0] = 1.0f;
test_dataset->datas[3].inputs[1] = 0.0f;
test_dataset->datas[3].expected_index = 1;
test_dataset->all_inputs[6] = 1.0f;
test_dataset->all_inputs[7] = 0.0f;
test_dataset->expected_indices[3] = 1;

// Create network
size_t layer_length = 2;
Expand All @@ -57,7 +57,8 @@ int main() {

for (size_t i = 0; i < 500000; i++) {
for (size_t j = 0; j < test_dataset->length; j++) {
stochastic_gd(nn, 0.001f, &test_dataset->datas[randnum_u32(test_dataset->length, 0)]);
size_t randnum = randnum_u32(test_dataset->length, 0);
stochastic_gd(nn, 0.001f, &test_dataset->all_inputs[randnum * inputs_length], test_dataset->expected_indices[randnum]);
}
if (i % 100000 == 0) {
printf("Stochastic Multi layer learn cost: %f\n", cost(nn, test_dataset, test_dataset->length));
Expand All @@ -70,7 +71,7 @@ int main() {
return 0;
}
```
## Benchmark - Highest average recorded
## Benchmark - Highest score recorded for MNIST dataset
- Intel Core i5 9th Gen: ~150,000 Data/s
- Intel Core Ultra 5: ~250,000 Data/s

Expand Down
94 changes: 32 additions & 62 deletions include/cneuron/cneuron.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,32 +9,16 @@
#include <stddef.h>
#include <stdint.h>

/**
* @brief Represents a single data element with its inputs and expected output index.
*/
typedef struct {
size_t expected_index; /**< Index of the expected output label. */
float *inputs; /**< Pointer to an array of input values. */
} data;

/**
* @brief Represents a dataset that contains multiple data elements.
*/
typedef struct {
size_t length; /**< Number of data elements in the dataset. */
size_t inputs_length; /**< Number of input values per data element. */
data *datas; /**< Array containing the datas */
size_t length; /**< Number of data elements in the dataset. */
size_t inputs_length; /**< Number of input values per data element. */
size_t *expected_indices; /**< Flat array of size length. */
float *all_inputs; /**< Flat 1D array of size length * inputs_length */
} dataset;

/**
* @brief Allocate and setup a data
*
* @param inputs_length Number of input of the data
*
* @return Newly allocated data
*/
data *alloc_data(size_t inputs_length);

/**
* @brief Allocate and setup a dataset
*
Expand All @@ -53,15 +37,6 @@ dataset *alloc_dataset(size_t dataset_length, size_t inputs_length);
*/
dataset *get_dataset(const char *filename);

/**
* @brief Copy a 'data' structure.
*
* @param source_data Pointer to the original data element to copy.
* @param target_data Pointer to the destination data element to perform deep copy.
* @param inputs_length Number of input values in the data element.
*/
void copy_data(data *restrict target_data, const data *restrict source_data, size_t inputs_length);

/**
* @brief Creates allocate new dataset and select random copy of data from a source dataset.
*
Expand All @@ -74,52 +49,43 @@ dataset *get_random_dataset_sample(const dataset *source_dataset, size_t amount)
/**
* @brief Rotates the data by a specified angle.
*
* @param data Pointer to the data element to modify.
* @param data Float pointer to the start of data to modify.
* @param width Width of the data (e.g., for image data).
* @param height Height of the data.
* @param angle Rotation angle in degrees.
*/
void rotate_data(data *data, int width, int height, float angle);
void rotate_data(float *data, int width, int height, float angle);

/**
* @brief Scales the data by a specified factor.
*
* @param data Pointer to the data element to modify.
* @param data Float pointer to the start of data to modify.
* @param width Width of the data (e.g., for image data).
* @param height Height of the data.
* @param scale Scaling factor.
*/
void scale_data(data *data, int width, int height, float scale);
void scale_data(float *data, int width, int height, float scale);

/**
* @brief Applies an offset to the data in both x and y directions.
*
* @param data Pointer to the data element to modify.
* @param data Float pointer to the start of data to modify.
* @param width Width of the data (e.g., for image data).
* @param height Height of the data.
* @param offset_x Offset value in the x-direction.
* @param offset_y Offset value in the y-direction.
*/
void offset_data(data *data, int width, int height, float offset_x, float offset_y);
void offset_data(float *data, int width, int height, float offset_x, float offset_y);

/**
* @brief Adds noise to the data with a given intensity and probability.
*
* @param data Pointer to the data element to modify.
* @param data Float pointer to the start of data to modify.
* @param inputs_length Number of input values in the data element.
* @param noise_factor Intensity of the noise to be added.
* @param probability Probability of adding noise to each input value.
*/
void noise_data(data *data, size_t inputs_length, float noise_factor, float probability);

/**
* @brief Computes the expected output value for the data element.
*
* @param index Index of the output neuron to check.
* @param data Pointer to the data element.
* @return Floating-point value representing the expected output.
*/
float output_expected(size_t index, const data *data);
void noise_data(float *data, size_t inputs_length, float noise_factor, float probability);

/**
* @brief Apply activation to a vector.
Expand Down Expand Up @@ -195,7 +161,7 @@ neural_network *get_neural_network(size_t network_length, const size_t *layers_l
*
* @note The weights and biases are automatically initialized when the network is created using 'get_neural_network'. Ensure the network is created properly before calling this function.
*/
void compute_network(neural_network *restrict nn, const float *restrict inputs);
void compute_network(const neural_network *restrict nn, const float *restrict inputs);

/**
* @brief Applies the softmax function for a specific neuron in the output layer.
Expand All @@ -204,14 +170,14 @@ void compute_network(neural_network *restrict nn, const float *restrict inputs);
* @param neuron_index Index of the neuron to compute the softmax value for.
* @return The softmax value for the specified neuron.
*/
float softmax(neural_network *nn, size_t neuron_index);
float softmax(const neural_network *nn, size_t neuron_index);

/**
* @brief Prints the activation percentages of neurons in the network.
*
* @param nn Pointer to the neural network.
*/
void print_activation_percentages(neural_network *nn);
void print_activation_percentages(const neural_network *nn);

/**
* @brief Computes the cost (loss) of the neural network on a test dataset.
Expand All @@ -221,50 +187,54 @@ void print_activation_percentages(neural_network *nn);
* @param num_test Number of test samples to evaluate.
* @return The computed cost value.
*/
float cost(neural_network *nn, const dataset *test_dataset, size_t num_test);
float cost(const neural_network *nn, const dataset *test_dataset, size_t num_test);

/**
* @brief Performs learning (backpropagation) for a specific layer.
*
* @param nn Pointer to the neural network.
* @param layer_index Index of the layer to perform learning on.
* @param learn_rate Learning rate for weight updates.
* @param data Pointer to the data element used for learning.
* @param data Float pointer to the data element used for learning.
* @param data_expected_index Expected index of the correct output
*
* @note The network must be computed using 'compute_network' prior to calling this function.
*/
void layer_learn(neural_network *nn, size_t layer_index, float learn_rate, const data *data);
void layer_learn(const neural_network *nn, size_t layer_index, float learn_rate, const float *data, size_t data_expected_index);

/**
* @brief Performs backpropagation for a specific layer but add the change in gradient to a array.
*
* @param nn Pointer to the neural network.
* @param layer_weights_gradients Pointer to an array of weights to be added to.
* @param layer_weights_bias Pointer to an array of bias to be added to.
* @param layer_bias_gradients Pointer to an array of bias to be added to.
* @param layer_index Index of the layer to perform learning on.
* @param data Pointer to the data element used for learning.
* @param data Float pointer to the data element used for learning.
* @param data_expected_index Expected index of the correct output

*
* @note The network must be computed using 'compute_network' prior to calling this function.
*/
void layer_learn_collect_gradient(neural_network *nn, float *restrict layer_weights_gradients, float *restrict layer_bias_gradients, size_t layer_index, const data *data);
void layer_learn_collect_gradient(const neural_network *nn, float *restrict layer_weights_gradients, float *restrict layer_bias_gradients, size_t layer_index, const float *data, size_t data_expected_index);

/**
* @brief Performs stochastic gradient descent to the network.
*
* @param nn Pointer to the neural network.
* @param learn_rate Learning rate for weight updates.
* @param data Pointer to the data element used for learning.
* @param data Float pointer to the data element used for learning.
* @param data_expected_index Expected index of the correct output
*
* @note The network must be computed using 'compute_network' prior to calling this function.
*/
void stochastic_gd(neural_network *nn, float learn_rate, const data *data);
void stochastic_gd(const neural_network *nn, float learn_rate, const float *data, size_t data_exptected_index);

/**
* @brief Performs mini-batch gradient decent to the network.
* @brief Performs mini-batch gradient descent to the network.
*
* @param nn Pointer to the neural network.
* @param learn_rate Learning rate for weight updates.
* @param data_batch Pointer to the dataset used for gradient decent.
* @param data_batch Pointer to the dataset used for gradient descent.
*
* @note The network must be computed using 'compute_network' prior to calling this function.
*/
Expand All @@ -277,7 +247,7 @@ void mini_batch_gd(neural_network *nn, float learn_rate, const dataset *data_bat
* @param nn Pointer to the neural network to save.
* @return True if the network was successfully saved, false otherwise.
*/
bool save_network(const char *restrict filename, neural_network *restrict nn);
bool save_network(const char *restrict filename, const neural_network *restrict nn);

/**
* @brief Loads a neural network from a file.
Expand All @@ -286,7 +256,7 @@ bool save_network(const char *restrict filename, neural_network *restrict nn);
* @param nn Pointer to the neural network structure to load into.
* @return True if the network was successfully loaded, false otherwise.
*/
bool load_network(const char *restrict filename, neural_network *restrict nn);
bool load_network(const char *restrict filename, const neural_network *restrict nn);

/**
* @brief Tests the accuracy of the neural network on a test dataset.
Expand All @@ -295,7 +265,7 @@ bool load_network(const char *restrict filename, neural_network *restrict nn);
* @param test_dataset Pointer to the test dataset.
* @return The percentage of correct predictions.
*/
float test_network_percent(neural_network *nn, const dataset *test_dataset);
float test_network_percent(const neural_network *nn, const dataset *test_dataset);

struct rand_chunk {
size_t count;
Expand Down
Loading
Loading