From cb51e3180853d039b80efea4b02461f8dcad2224 Mon Sep 17 00:00:00 2001 From: Hoonlim Lee Date: Fri, 26 Jun 2026 20:21:42 +0800 Subject: [PATCH 1/6] removed "data" structure and update const correctness --- include/cneuron/cneuron.h | 96 ++++++++++++++------------------------- src/network.c | 20 ++++---- 2 files changed, 45 insertions(+), 71 deletions(-) diff --git a/include/cneuron/cneuron.h b/include/cneuron/cneuron.h index 4398a55c0..1835bd056 100644 --- a/include/cneuron/cneuron.h +++ b/include/cneuron/cneuron.h @@ -9,31 +9,16 @@ #include #include -/** - * @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 @@ -53,15 +38,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. * @@ -74,52 +50,47 @@ 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 data_size Size of the 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, size_t data_size, 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 data_size Size of the 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, size_t data_size, 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 data_size Size of the 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, size_t data_size, 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 data_size Size of the 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 data_size, size_t inputs_length, float noise_factor, float probability); /** * @brief Apply activation to a vector. @@ -195,7 +166,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. @@ -204,14 +175,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. @@ -221,7 +192,7 @@ 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. @@ -229,46 +200,49 @@ float cost(neural_network *nn, const dataset *test_dataset, size_t num_test); * @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_size Size of the data * * @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_size); /** * @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_size Size of the data * * @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_size); /** * @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_size Size of the data * * @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, const size_t data_size); /** - * @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. */ -void mini_batch_gd(neural_network *nn, float learn_rate, const dataset *data_batch); +void mini_batch_gd(const neural_network *nn, float learn_rate, const dataset *data_batch); /** * @brief Saves the neural network to a file. @@ -277,7 +251,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. @@ -295,7 +269,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; diff --git a/src/network.c b/src/network.c index f92cb58c0..6e9979695 100644 --- a/src/network.c +++ b/src/network.c @@ -63,7 +63,7 @@ neural_network *get_neural_network(size_t network_length, const size_t *layers_l return nn; } -void compute_network(neural_network *restrict nn, const float *restrict inputs) { +void compute_network(const neural_network *restrict nn, const float *restrict inputs) { assert(nn && inputs); for (size_t i = 0; i < nn->length; i++) { @@ -80,7 +80,7 @@ void compute_network(neural_network *restrict nn, const float *restrict inputs) } } -float softmax(neural_network *nn, size_t neuron_index) { +float softmax(const neural_network *nn, size_t neuron_index) { assert(nn); float sum = 0.0f; @@ -98,7 +98,7 @@ float softmax(neural_network *nn, size_t neuron_index) { return expf(output_layer->output[neuron_index] - max_output) / sum; } -void print_activation_percentages(neural_network *nn) { +void print_activation_percentages(const neural_network *nn) { assert(nn); layer *output_layer = &nn->layers[nn->length - 1]; @@ -144,7 +144,7 @@ void print_activation_percentages(neural_network *nn) { free(indices); } -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) { assert(nn && test_dataset); float cost = 0.0f; @@ -161,7 +161,7 @@ float cost(neural_network *nn, const dataset *test_dataset, size_t num_test) { return cost / num_test; } -void print_result(neural_network *nn) { +void print_result(const neural_network *nn) { assert(nn); layer *output_layer = &nn->layers[nn->length - 1]; @@ -169,7 +169,7 @@ void print_result(neural_network *nn) { printf("%f ", output_layer->output[i]); } -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 data *data) { assert(nn && data); layer *curr_layer = &nn->layers[layer_index]; @@ -205,7 +205,7 @@ void layer_learn(neural_network *nn, size_t layer_index, float learn_rate, const free(weight_gradient); } -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 data *data) { assert(nn && layer_weights_gradients && layer_bias_gradients && data); layer *curr_layer = &nn->layers[layer_index]; @@ -234,7 +234,7 @@ void layer_learn_collect_gradient(neural_network *nn, float *restrict layer_weig cblas_saxpy(curr_layer->length, 1.0f, curr_layer->delta, 1, layer_bias_gradients, 1); } -void stochastic_gd(neural_network *nn, float learn_rate, const data *data) { +void stochastic_gd(const neural_network *nn, float learn_rate, const data *data) { assert(nn && data); compute_network(nn, data->inputs); @@ -279,7 +279,7 @@ void *thread_worker(void *arg) { return NULL; } -void mini_batch_gd(neural_network *nn, float learn_rate, const dataset *data_batch) { +void mini_batch_gd(const neural_network *nn, float learn_rate, const dataset *data_batch) { assert(nn && data_batch); ThreadArgs args; @@ -402,7 +402,7 @@ bool load_network(const char *restrict filename, neural_network *restrict nn) { return false; } -float test_network_percent(neural_network *nn, const dataset *test_dataset) { +float test_network_percent(const neural_network *nn, const dataset *test_dataset) { assert(nn); assert(test_dataset); From 930bfdde1944a775d60ca3d038c939d0665421d1 Mon Sep 17 00:00:00 2001 From: Hoonlim Lee Date: Fri, 26 Jun 2026 21:23:31 +0800 Subject: [PATCH 2/6] Refactor data.c to not include "data" structure --- include/cneuron/cneuron.h | 14 +++---- src/data.c | 77 ++++++++++++--------------------------- src/network.c | 4 +- 3 files changed, 31 insertions(+), 64 deletions(-) diff --git a/include/cneuron/cneuron.h b/include/cneuron/cneuron.h index 1835bd056..9617cdb1a 100644 --- a/include/cneuron/cneuron.h +++ b/include/cneuron/cneuron.h @@ -51,46 +51,42 @@ dataset *get_random_dataset_sample(const dataset *source_dataset, size_t amount) * @brief Rotates the data by a specified angle. * * @param data Float pointer to the start of data to modify. - * @param data_size Size of the 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(float *data, size_t data_size, 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 Float pointer to the start of data to modify. - * @param data_size Size of the 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(float *data, size_t data_size, 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 Float pointer to the start of data to modify. - * @param data_size Size of the 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(float *data, size_t data_size, 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 Float pointer to the start of data to modify. - * @param data_size Size of the 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(float *data, size_t data_size, size_t inputs_length, float noise_factor, float probability); +void noise_data(float *data, size_t inputs_length, float noise_factor, float probability); /** * @brief Apply activation to a vector. @@ -260,7 +256,7 @@ bool save_network(const char *restrict filename, const neural_network *restrict * @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. diff --git a/src/data.c b/src/data.c index eaf24463b..e42919317 100644 --- a/src/data.c +++ b/src/data.c @@ -8,25 +8,14 @@ #include "cneuron/cneuron.h" -data *alloc_data(size_t inputs_length) { - data *new_data = malloc(sizeof(data) + sizeof(float) * inputs_length); - new_data->inputs = (float *)(new_data + 1); - - return new_data; -} - dataset *alloc_dataset(size_t dataset_length, size_t inputs_length) { - dataset *new_dataset = malloc(sizeof(dataset) + (sizeof(data) + sizeof(float) * inputs_length) * dataset_length); + dataset *new_dataset = malloc(sizeof(dataset) + (sizeof(size_t) + sizeof(float) * inputs_length) * dataset_length); if (!new_dataset) return NULL; - new_dataset->datas = (data *)(new_dataset + 1); + new_dataset->expected_indices = (size_t *)(new_dataset + 1); + new_dataset->all_inputs = (float *)(new_dataset->expected_indices + inputs_length); new_dataset->length = dataset_length; new_dataset->inputs_length = inputs_length; - float *inputs_block = (float *)(new_dataset->datas + dataset_length); - for (size_t i = 0; i < dataset_length; i++) { - new_dataset->datas[i].inputs = inputs_block + i * inputs_length; - } - return new_dataset; } @@ -61,8 +50,8 @@ dataset *get_dataset(const char *filename) { } for (size_t i = 0; i < dataset_length; i++) { - data *read_data = &read_dataset->datas[i]; - size_t read_inputs = fread(read_data->inputs, sizeof(float), read_dataset->inputs_length, file); + float *read_data = &read_dataset->all_inputs[i * inputs_length]; + size_t read_inputs = fread(read_data, sizeof(float), read_dataset->inputs_length, file); if (read_inputs != read_dataset->inputs_length) { fprintf(stderr, "Invalid inputs_length from %s. Expected: %zu. But found: %zu\n", filename, read_dataset->inputs_length, read_inputs); free(read_dataset); @@ -70,7 +59,7 @@ dataset *get_dataset(const char *filename) { return NULL; } - if (fread(&(read_data->expected_index), sizeof(uint64_t), 1, file) != 1) { + if (fread(&(read_dataset->expected_indices[i]), sizeof(uint64_t), 1, file) != 1) { fprintf(stderr, "Failed to read expected_index from %s\n", filename); free(read_dataset); fclose(file); @@ -83,34 +72,24 @@ dataset *get_dataset(const char *filename) { return read_dataset; } -void copy_data(data *restrict target_data, const data *restrict source_data, size_t inputs_length) { - assert(inputs_length > 0); - - size_t inputs_size = sizeof(float) * inputs_length; - - target_data->expected_index = source_data->expected_index; - memcpy(target_data->inputs, source_data->inputs, inputs_size); -} - dataset *get_random_dataset_sample(const dataset *source_dataset, size_t amount) { assert(source_dataset); - dataset *new_dataset = alloc_dataset(amount, source_dataset->inputs_length); + size_t inputs_size = source_dataset->inputs_length; + dataset *new_dataset = alloc_dataset(amount, inputs_size); if (!new_dataset) { return NULL; } - new_dataset->datas = (data *)(new_dataset + 1); - new_dataset->length = amount; - new_dataset->inputs_length = source_dataset->inputs_length; - for (size_t i = 0; i < amount; i++) { - data *random_data = &source_dataset->datas[randnum_u32(source_dataset->length, 0)]; - copy_data(&new_dataset->datas[i], random_data, source_dataset->inputs_length); + uint32_t randnum = randnum_u32(source_dataset->length, 0); + float *random_data = &source_dataset->all_inputs[randnum * inputs_size]; + float *target_data = &new_dataset->all_inputs[i * inputs_size]; + memcpy(target_data, random_data, inputs_size); } return new_dataset; } -void rotate_data(data *data, int width, int height, float angle) { +void rotate_data(float *data, size_t data_size, int width, int height, float angle) { assert(width > 0 && height > 0); float rad = angle * M_PI / 180.0f; @@ -129,16 +108,16 @@ void rotate_data(data *data, int width, int height, float angle) { int src_y = roundf((y - center_y) * cos_angle - (x - center_x) * sin_angle + center_y); if (src_x >= 0 && src_x < width && src_y >= 0 && src_y < height) { - new_inputs[y * width + x] = data->inputs[src_y * width + src_x]; + new_inputs[y * width + x] = data[src_y * width + src_x]; } } } - memcpy(data->inputs, new_inputs, sizeof(float) * width * height); + memcpy(data, new_inputs, sizeof(float) * width * height); free(new_inputs); } -void scale_data(data *data, int width, int height, float scale) { +void scale_data(float *data, int width, int height, float scale) { assert(width > 0 && height > 0); int scale_width = roundf(width * scale); @@ -159,16 +138,16 @@ void scale_data(data *data, int width, int height, float scale) { int src_y = roundf(scaled_y / scale); if (src_x >= 0 && src_x < width && src_y >= 0 && src_y < height) { - new_inputs[y * width + x] = data->inputs[src_y * width + src_x]; + new_inputs[y * width + x] = data[src_y * width + src_x]; } } } - memcpy(data->inputs, new_inputs, sizeof(float) * width * height); + memcpy(data, new_inputs, sizeof(float) * width * height); free(new_inputs); } -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) { assert(width > 0 && height > 0); float *new_inputs = calloc(width * height, sizeof(float)); @@ -184,31 +163,23 @@ void offset_data(data *data, int width, int height, float offset_x, float offset int src_x = roundf(new_x); int src_y = roundf(new_y); if (src_x >= 0 && src_x < width && src_y >= 0 && src_y < height) { - new_inputs[y * width + x] = data->inputs[src_y * width + src_x]; + new_inputs[y * width + x] = data[src_y * width + src_x]; } } } - memcpy(data->inputs, new_inputs, sizeof(float) * width * height); + memcpy(data, new_inputs, sizeof(float) * width * height); free(new_inputs); } -void noise_data(data *data, size_t inputs_length, float noise_factor, float probability) { +void noise_data(float *data, size_t inputs_length, float noise_factor, float probability) { assert(inputs_length > 0); for (size_t i = 0; i < inputs_length; i++) { if (randf(1.0f, 0.0f) <= probability) { float noise = randf(noise_factor, 0); - float new_value = data->inputs[i] + noise; + float new_value = data[i] + noise; - data->inputs[i] = fmin(new_value, 1.0f); + data[i] = fmin(new_value, 1.0f); } } } - -float output_expected(size_t index, const data *data) { - if (index == data->expected_index) { - return 1.0f; - } else { - return 0.0f; - } -} diff --git a/src/network.c b/src/network.c index 6e9979695..f9c4c4b3c 100644 --- a/src/network.c +++ b/src/network.c @@ -317,7 +317,7 @@ void mini_batch_gd(const neural_network *nn, float learn_rate, const dataset *da free(bias_gradients); } -bool save_network(const char *restrict filename, neural_network *restrict nn) { +bool save_network(const char *restrict filename, const neural_network *restrict nn) { assert(filename && nn); FILE *file = fopen(filename, "wb"); @@ -347,7 +347,7 @@ bool save_network(const char *restrict filename, neural_network *restrict nn) { return true; } -bool load_network(const char *restrict filename, neural_network *restrict nn) { +bool load_network(const char *restrict filename, const neural_network *restrict nn) { assert(filename && nn); FILE *file = fopen(filename, "rb"); From 48ba630aa230bced8e1c9f2f6600fcc3fa7438d4 Mon Sep 17 00:00:00 2001 From: Hoonlim Lee Date: Fri, 26 Jun 2026 22:42:42 +0800 Subject: [PATCH 3/6] Rewritten main.c and network.c to work without "data" structure --- include/cneuron/cneuron.h | 14 +++++++------- src/data.c | 2 +- src/main.c | 11 ++++------- src/network.c | 37 +++++++++++++++++++------------------ 4 files changed, 31 insertions(+), 33 deletions(-) diff --git a/include/cneuron/cneuron.h b/include/cneuron/cneuron.h index 9617cdb1a..75de12a37 100644 --- a/include/cneuron/cneuron.h +++ b/include/cneuron/cneuron.h @@ -197,11 +197,11 @@ float cost(const neural_network *nn, const dataset *test_dataset, size_t num_tes * @param layer_index Index of the layer to perform learning on. * @param learn_rate Learning rate for weight updates. * @param data Float pointer to the data element used for learning. - * @param data_size Size of the data + * @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(const neural_network *nn, size_t layer_index, float learn_rate, const float *data, size_t data_size); +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. @@ -211,11 +211,12 @@ void layer_learn(const neural_network *nn, size_t layer_index, float learn_rate, * @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 Float pointer to the data element used for learning. - * @param data_size Size of the data + * @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(const neural_network *nn, float *restrict layer_weights_gradients, float *restrict layer_bias_gradients, size_t layer_index, const float *data, size_t data_size); +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. @@ -223,11 +224,10 @@ void layer_learn_collect_gradient(const neural_network *nn, float *restrict laye * @param nn Pointer to the neural network. * @param learn_rate Learning rate for weight updates. * @param data Float pointer to the data element used for learning. - * @param data_size Size of the data * * @note The network must be computed using 'compute_network' prior to calling this function. */ -void stochastic_gd(const neural_network *nn, float learn_rate, const float *data, const size_t data_size); +void stochastic_gd(const neural_network *nn, float learn_rate, const float *data); /** * @brief Performs mini-batch gradient descent to the network. @@ -238,7 +238,7 @@ void stochastic_gd(const neural_network *nn, float learn_rate, const float *data * * @note The network must be computed using 'compute_network' prior to calling this function. */ -void mini_batch_gd(const neural_network *nn, float learn_rate, const dataset *data_batch); +void mini_batch_gd(neural_network *nn, float learn_rate, const dataset *data_batch); /** * @brief Saves the neural network to a file. diff --git a/src/data.c b/src/data.c index e42919317..3e3c47fdd 100644 --- a/src/data.c +++ b/src/data.c @@ -89,7 +89,7 @@ dataset *get_random_dataset_sample(const dataset *source_dataset, size_t amount) return new_dataset; } -void rotate_data(float *data, size_t data_size, int width, int height, float angle) { +void rotate_data(float *data, int width, int height, float angle) { assert(width > 0 && height > 0); float rad = angle * M_PI / 180.0f; diff --git a/src/main.c b/src/main.c index ac8711f5e..24e0853d9 100644 --- a/src/main.c +++ b/src/main.c @@ -38,7 +38,7 @@ typedef struct { dataset *dataset_generator(generator_args *args) { dataset *batch_dataset = get_random_dataset_sample(args->train_dataset, args->batch_size); for (size_t i = 0; i < batch_dataset->length; i++) { - data *data = &batch_dataset->datas[i]; + float *data = &batch_dataset->all_inputs[i * batch_dataset->inputs_length]; rotate_data(data, IMAGE_SIZE, IMAGE_SIZE, randf(10.0f, -5.0f)); scale_data(data, IMAGE_SIZE, IMAGE_SIZE, randf(1.2f, -0.1f)); offset_data(data, IMAGE_SIZE, IMAGE_SIZE, randf(6.0f, -3.0f), randf(6.0f, -3.0f)); @@ -109,12 +109,9 @@ dataset *get_mnist(bool is_test) { size_t curr_count = 0; for (size_t i = 0; i < 10; i++) { - for (size_t j = 0; j < datasets[i]->length; j++) { - data *curr_data = &mnist_dataset->datas[curr_count]; - copy_data(curr_data, &datasets[i]->datas[j], inputs_length); - curr_count++; - } - + memcpy(&mnist_dataset->all_inputs[curr_count * inputs_length], datasets[i]->all_inputs, inputs_length * datasets[i]->length); + memcpy(&mnist_dataset->expected_indices[curr_count], datasets[i]->expected_indices, datasets[i]->length); + curr_count += datasets[i]->length; free(datasets[i]); } diff --git a/src/network.c b/src/network.c index f9c4c4b3c..fb9a7a9b1 100644 --- a/src/network.c +++ b/src/network.c @@ -151,11 +151,12 @@ float cost(const neural_network *nn, const dataset *test_dataset, size_t num_tes layer *output_layer = &nn->layers[nn->length - 1]; for (size_t i = 0; i < num_test; i++) { - data *test_data = &test_dataset->datas[randnum_u32(test_dataset->length, 0)]; - compute_network(nn, test_data->inputs); + uint32_t randnum = randnum_u32(test_dataset->length, 0); + float *test_data = &test_dataset->all_inputs[randnum * test_dataset->inputs_length]; + compute_network(nn, test_data); for (size_t j = 0; j < output_layer->length; j++) { float output = output_layer->output[j]; - cost += (output - output_expected(j, test_data)) * (output - output_expected(j, test_data)); + cost += (output - (j == test_dataset->expected_indices[randnum])) * (output - (j == test_dataset->expected_indices[randnum])); } } return cost / num_test; @@ -169,7 +170,7 @@ void print_result(const neural_network *nn) { printf("%f ", output_layer->output[i]); } -void layer_learn(const 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, const size_t data_expected_index) { assert(nn && data); layer *curr_layer = &nn->layers[layer_index]; @@ -178,7 +179,7 @@ void layer_learn(const neural_network *nn, size_t layer_index, float learn_rate, vector_apply_activation(curr_layer->weighted_input, curr_layer->weighted_input, curr_layer->length, nn->activation_function, true); if (layer_index == nn->length - 1) { // Error in output - curr_layer->output[data->expected_index] -= 1.0f; + curr_layer->output[data_expected_index] -= 1.0f; } else { // W^T_{i+1}δ_{i+1} in output layer *next_layer = &nn->layers[layer_index + 1]; @@ -190,7 +191,7 @@ void layer_learn(const neural_network *nn, size_t layer_index, float learn_rate, float *weight_gradient; if (layer_index == 0) { weight_gradient = calloc(curr_layer->length * nn->inputs_length, sizeof(float)); - cblas_sger(CblasColMajor, curr_layer->length, nn->inputs_length, 1.0f, curr_layer->delta, 1, data->inputs, 1, weight_gradient, curr_layer->length); + cblas_sger(CblasColMajor, curr_layer->length, nn->inputs_length, 1.0f, curr_layer->delta, 1, data, 1, weight_gradient, curr_layer->length); cblas_saxpy(curr_layer->length * nn->inputs_length, -learn_rate, weight_gradient, 1, curr_layer->weights, 1); } else { layer *prev_layer = &nn->layers[layer_index - 1]; @@ -205,7 +206,7 @@ void layer_learn(const neural_network *nn, size_t layer_index, float learn_rate, free(weight_gradient); } -void layer_learn_collect_gradient(const 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) { assert(nn && layer_weights_gradients && layer_bias_gradients && data); layer *curr_layer = &nn->layers[layer_index]; @@ -214,7 +215,7 @@ void layer_learn_collect_gradient(const neural_network *nn, float *restrict laye vector_apply_activation(curr_layer->weighted_input, curr_layer->weighted_input, curr_layer->length, nn->activation_function, true); if (layer_index == nn->length - 1) { // Error in output - curr_layer->output[data->expected_index] -= 1.0f; + curr_layer->output[data_expected_index] -= 1.0f; } else { // W^T_{i+1}δ_{i+1} in output layer *next_layer = &nn->layers[layer_index + 1]; @@ -224,7 +225,7 @@ void layer_learn_collect_gradient(const neural_network *nn, float *restrict laye hadamard_product(curr_layer->weighted_input, curr_layer->output, curr_layer->delta, curr_layer->length); if (layer_index == 0) { - cblas_sger(CblasColMajor, curr_layer->length, nn->inputs_length, 1.0f, curr_layer->delta, 1, data->inputs, 1, layer_weights_gradients, curr_layer->length); + cblas_sger(CblasColMajor, curr_layer->length, nn->inputs_length, 1.0f, curr_layer->delta, 1, data, 1, layer_weights_gradients, curr_layer->length); } else { layer *prev_layer = &nn->layers[layer_index - 1]; cblas_sger(CblasColMajor, curr_layer->length, prev_layer->length, 1.0f, curr_layer->delta, 1, prev_layer->output, 1, layer_weights_gradients, curr_layer->length); @@ -234,12 +235,12 @@ void layer_learn_collect_gradient(const neural_network *nn, float *restrict laye cblas_saxpy(curr_layer->length, 1.0f, curr_layer->delta, 1, layer_bias_gradients, 1); } -void stochastic_gd(const neural_network *nn, float learn_rate, const data *data) { +void stochastic_gd(const neural_network *nn, float learn_rate, const float *data) { assert(nn && data); - compute_network(nn, data->inputs); + compute_network(nn, data); for (size_t i = 0; i < nn->length; i++) { - layer_learn(nn, nn->length - i - 1, learn_rate, data); + layer_learn(nn, nn->length - i - 1, learn_rate, data, nn->inputs_length); } } @@ -267,19 +268,19 @@ void *thread_worker(void *arg) { } for (size_t i = 0; i < args->data_batch->length; i++) { - data *data = &args->data_batch->datas[i]; - compute_network(nn, data->inputs); + float *data = &args->data_batch->all_inputs[i * args->data_batch->inputs_length]; + compute_network(nn, data); for (size_t j = 0; j < nn->length; j++) { size_t layer_index = nn->length - j - 1; - layer_learn_collect_gradient(nn, weights_gradients[layer_index], bias_gradients[layer_index], layer_index, data); + layer_learn_collect_gradient(nn, weights_gradients[layer_index], bias_gradients[layer_index], layer_index, data, args->data_batch->expected_indices[i]); } } return NULL; } -void mini_batch_gd(const neural_network *nn, float learn_rate, const dataset *data_batch) { +void mini_batch_gd(neural_network *nn, float learn_rate, const dataset *data_batch) { assert(nn && data_batch); ThreadArgs args; @@ -408,13 +409,13 @@ float test_network_percent(const neural_network *nn, const dataset *test_dataset int correct = 0; for (size_t i = 0; i < test_dataset->length; i++) { - compute_network(nn, test_dataset->datas[i].inputs); + compute_network(nn, &test_dataset->all_inputs[i * test_dataset->inputs_length]); size_t max = 0; for (size_t j = 0; j < nn->layers[nn->length - 1].length; j++) { if (softmax(nn, j) > softmax(nn, max)) max = j; } - if (max == test_dataset->datas[i].expected_index) { + if (max == test_dataset->expected_indices[i]) { correct++; } } From afcbee5d126bf97056b224561e6c9fded664467b Mon Sep 17 00:00:00 2001 From: Hoonlim Lee Date: Sat, 27 Jun 2026 10:25:08 +0800 Subject: [PATCH 4/6] Rewrite tests to compile successfully --- src/data.c | 3 +- src/main.c | 4 +-- test/data.cpp | 74 ++++++++++++++------------------------------- test/network.cpp | 6 ++-- test/test_utils.cpp | 48 ++++++++++++++--------------- test/test_utils.h | 1 + 6 files changed, 55 insertions(+), 81 deletions(-) diff --git a/src/data.c b/src/data.c index 3e3c47fdd..a7aa2c92c 100644 --- a/src/data.c +++ b/src/data.c @@ -83,7 +83,8 @@ dataset *get_random_dataset_sample(const dataset *source_dataset, size_t amount) uint32_t randnum = randnum_u32(source_dataset->length, 0); float *random_data = &source_dataset->all_inputs[randnum * inputs_size]; float *target_data = &new_dataset->all_inputs[i * inputs_size]; - memcpy(target_data, random_data, inputs_size); + memcpy(target_data, random_data, sizeof(float) * inputs_size); + new_dataset->expected_indices[i] = source_dataset->expected_indices[randnum]; } return new_dataset; diff --git a/src/main.c b/src/main.c index 24e0853d9..1160198aa 100644 --- a/src/main.c +++ b/src/main.c @@ -109,8 +109,8 @@ dataset *get_mnist(bool is_test) { size_t curr_count = 0; for (size_t i = 0; i < 10; i++) { - memcpy(&mnist_dataset->all_inputs[curr_count * inputs_length], datasets[i]->all_inputs, inputs_length * datasets[i]->length); - memcpy(&mnist_dataset->expected_indices[curr_count], datasets[i]->expected_indices, datasets[i]->length); + memcpy(&mnist_dataset->all_inputs[curr_count * inputs_length], datasets[i]->all_inputs, sizeof(float) * inputs_length * datasets[i]->length); + memcpy(&mnist_dataset->expected_indices[curr_count], datasets[i]->expected_indices, sizeof(size_t) * datasets[i]->length); curr_count += datasets[i]->length; free(datasets[i]); } diff --git a/test/data.cpp b/test/data.cpp index b31e6ec95..77b2001cb 100644 --- a/test/data.cpp +++ b/test/data.cpp @@ -16,7 +16,7 @@ TEST(DataTest, GetDatasetValidFile) { ASSERT_GT(test_dataset->inputs_length, 0); ASSERT_NE(test_dataset, nullptr); - ASSERT_NE(test_dataset->datas[0].inputs, nullptr); + ASSERT_NE(test_dataset->all_inputs, nullptr); free(test_dataset); } @@ -28,30 +28,12 @@ TEST(DataTest, FreeDataset) { // No crash } -TEST(DataTest, CopyData) { - dataset *test_dataset = get_dataset("data/mnist/test/0.dat"); - data *data_copy = alloc_data(test_dataset->inputs_length); - - copy_data(data_copy, &test_dataset->datas[0], test_dataset->inputs_length); - ASSERT_NE(data_copy, nullptr); - ASSERT_NE(data_copy->inputs, nullptr); - - for (size_t i = 0; i < test_dataset->inputs_length; i++) { - ASSERT_FLOAT_EQ(data_copy->inputs[i], test_dataset->datas[0].inputs[i]); - } - - ASSERT_FLOAT_EQ(data_copy->expected_index, test_dataset->datas[0].expected_index); - - free(data_copy); - free(test_dataset); -} - TEST(DataTest, RandomSampleDataset) { dataset *test_dataset = get_dataset("data/mnist/test/0.dat"); dataset *dataset_sample = get_random_dataset_sample(test_dataset, test_dataset->length - 1); ASSERT_NE(dataset_sample, nullptr); - ASSERT_NE(dataset_sample->datas, nullptr); + ASSERT_NE(dataset_sample->all_inputs, nullptr); free(dataset_sample); free(test_dataset); @@ -59,67 +41,67 @@ TEST(DataTest, RandomSampleDataset) { TEST(DataTest, RotateData) { size_t inputs_length = 9; - data *test_data = alloc_data(inputs_length); + float *test_data = (float *)malloc(sizeof(float) * inputs_length); for (size_t i = 0; i < inputs_length; i++) { - test_data->inputs[i] = static_cast(i) + 1.0f; + test_data[i] = static_cast(i) + 1.0f; } rotate_data(test_data, 3, 3, 90); - ASSERT_FLOAT_EQ(test_data->inputs[0], 7.0f); - ASSERT_FLOAT_EQ(test_data->inputs[2], 1.0f); - ASSERT_FLOAT_EQ(test_data->inputs[4], 5.0f); + ASSERT_FLOAT_EQ(test_data[0], 7.0f); + ASSERT_FLOAT_EQ(test_data[2], 1.0f); + ASSERT_FLOAT_EQ(test_data[4], 5.0f); free(test_data); } TEST(DataTest, ScaleData) { size_t inputs_length = 9; - data *test_data = alloc_data(inputs_length); + float *test_data = (float *)malloc(sizeof(float) * inputs_length); for (size_t i = 0; i < inputs_length; i++) { - test_data->inputs[i] = i + 1.0f; + test_data[i] = i + 1.0f; } scale_data(test_data, 3, 3, 2.0f); - ASSERT_FLOAT_EQ(test_data->inputs[0], 5.0f); - ASSERT_FLOAT_EQ(test_data->inputs[2], 6.0f); - ASSERT_FLOAT_EQ(test_data->inputs[8], 9.0f); + ASSERT_FLOAT_EQ(test_data[0], 5.0f); + ASSERT_FLOAT_EQ(test_data[2], 6.0f); + ASSERT_FLOAT_EQ(test_data[8], 9.0f); free(test_data); } TEST(DataTest, OffsetData) { size_t inputs_length = 9; - data *test_data = alloc_data(inputs_length); + float *test_data = (float *)malloc(sizeof(float) * inputs_length); for (size_t i = 0; i < inputs_length; i++) { - test_data->inputs[i] = i + 1.0f; + test_data[i] = i + 1.0f; } offset_data(test_data, 3, 3, 1.0f, 1.0f); - ASSERT_FLOAT_EQ(test_data->inputs[0], 0.0f); - ASSERT_FLOAT_EQ(test_data->inputs[5], 2.0f); - ASSERT_FLOAT_EQ(test_data->inputs[6], 0.0f); - ASSERT_FLOAT_EQ(test_data->inputs[8], 5.0f); + ASSERT_FLOAT_EQ(test_data[0], 0.0f); + ASSERT_FLOAT_EQ(test_data[5], 2.0f); + ASSERT_FLOAT_EQ(test_data[6], 0.0f); + ASSERT_FLOAT_EQ(test_data[8], 5.0f); free(test_data); } TEST(DataTest, NoiseData) { size_t inputs_length = 9; - data *test_data = alloc_data(inputs_length); - data *data_copy = alloc_data(inputs_length); + float *test_data = (float *)malloc(sizeof(float) * inputs_length); + float *data_copy = (float *)malloc(sizeof(float) * inputs_length); for (size_t i = 0; i < inputs_length; i++) { - test_data->inputs[i] = i + 1.0f; + test_data[i] = i + 1.0f; + data_copy[i] = i + 1.0f; } - copy_data(data_copy, test_data, inputs_length); bool same = true; noise_data(test_data, inputs_length, 1.0f, 1.0f); for (size_t i = 0; i < inputs_length; i++) { - if (data_copy->inputs[i] != test_data->inputs[i]) { + if (data_copy[i] != test_data[i]) { same = false; break; } @@ -129,13 +111,3 @@ TEST(DataTest, NoiseData) { free(test_data); free(data_copy); } - -TEST(DataTest, OutputExpected) { - data *test_data = (data *)malloc(sizeof(data)); - test_data->expected_index = 1; - - ASSERT_FLOAT_EQ(output_expected(0, test_data), 0.0f); - ASSERT_FLOAT_EQ(output_expected(1, test_data), 1.0f); - - free(test_data); -} diff --git a/test/network.cpp b/test/network.cpp index ce571f336..b7468b941 100644 --- a/test/network.cpp +++ b/test/network.cpp @@ -106,7 +106,7 @@ TEST(NetworkTest, StochasticGDSingleLayer) { for (size_t i = 0; i < 50000; i++) { for (size_t j = 0; j < test_dataset->length; j++) { - stochastic_gd(nn, 0.03f, &test_dataset->datas[randnum_u32(test_dataset->length, 0)]); + stochastic_gd(nn, 0.03f, &test_dataset->all_inputs[randnum_u32(test_dataset->length, 0) * test_dataset->inputs_length]); } if (i % 10000 == 0) { printf("Single layer learn cost: %f\n", cost(nn, test_dataset, test_dataset->length)); @@ -133,7 +133,7 @@ TEST(NetworkTest, StochasticGDTests) { 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)]); + stochastic_gd(nn, 0.001f, &test_dataset->all_inputs[randnum_u32(test_dataset->length, 0) * test_dataset->inputs_length]); } if (i % 100000 == 0) { printf("Stochastic Multi layer learn cost: %f\n", cost(nn, test_dataset, test_dataset->length)); @@ -154,7 +154,7 @@ TEST(NetworkTest, StochasticGDTests) { for (size_t i = 0; i < 50000; i++) { for (size_t j = 0; j < test_dataset->length; j++) { - stochastic_gd(nn, 0.03f, &test_dataset->datas[randnum_u32(test_dataset->length, 0)]); + stochastic_gd(nn, 0.03f, &test_dataset->all_inputs[randnum_u32(test_dataset->length, 0) * test_dataset->inputs_length]); } if (i % 10000 == 0) { printf("Stochastic Non-linearly separable learn cost: %f\n", cost(nn, test_dataset, test_dataset->length)); diff --git a/test/test_utils.cpp b/test/test_utils.cpp index b99ce6b5c..ea6109aac 100644 --- a/test/test_utils.cpp +++ b/test/test_utils.cpp @@ -17,21 +17,21 @@ dataset *get_xor() { 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; return test_dataset; } @@ -43,21 +43,21 @@ dataset *get_or() { dataset *test_dataset = alloc_dataset(dataset_length, inputs_length); // OR gate - 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[0] = 0.0f; + test_dataset->all_inputs[1] = 0.0f; + test_dataset->expected_indices[0] = 0; - test_dataset->datas[0].inputs[0] = 1.0f; - test_dataset->datas[0].inputs[1] = 1.0f; - test_dataset->datas[0].expected_index = 1; + test_dataset->all_inputs[2] = 1.0f; + test_dataset->all_inputs[3] = 1.0f; + test_dataset->expected_indices[1] = 1; - 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; return test_dataset; } diff --git a/test/test_utils.h b/test/test_utils.h index 6fe5d87c1..dcb064076 100644 --- a/test/test_utils.h +++ b/test/test_utils.h @@ -5,6 +5,7 @@ extern "C" { #include "cneuron/cneuron.h" } + float sigmoid(float val, bool is_deravative); dataset *get_xor(); From 0da1cdc9c5e3a541d28b651d6d5a2dd4d4ea80fe Mon Sep 17 00:00:00 2001 From: Hoonlim Lee Date: Sat, 27 Jun 2026 12:10:10 +0800 Subject: [PATCH 5/6] Fixed bug and format --- include/cneuron/cneuron.h | 4 ++-- src/data.c | 2 +- src/network.c | 4 ++-- test/network.cpp | 9 ++++++--- test/test_utils.h | 1 - 5 files changed, 11 insertions(+), 9 deletions(-) diff --git a/include/cneuron/cneuron.h b/include/cneuron/cneuron.h index 75de12a37..76abe3aea 100644 --- a/include/cneuron/cneuron.h +++ b/include/cneuron/cneuron.h @@ -19,7 +19,6 @@ typedef struct { float *all_inputs; /**< Flat 1D array of size length * inputs_length */ } dataset; - /** * @brief Allocate and setup a dataset * @@ -224,10 +223,11 @@ void layer_learn_collect_gradient(const neural_network *nn, float *restrict laye * @param nn Pointer to the neural network. * @param learn_rate Learning rate for weight updates. * @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(const neural_network *nn, float learn_rate, const float *data); +void stochastic_gd(const neural_network *nn, float learn_rate, const float *data, size_t data_exptected_index); /** * @brief Performs mini-batch gradient descent to the network. diff --git a/src/data.c b/src/data.c index a7aa2c92c..3bed3fc89 100644 --- a/src/data.c +++ b/src/data.c @@ -12,7 +12,7 @@ dataset *alloc_dataset(size_t dataset_length, size_t inputs_length) { dataset *new_dataset = malloc(sizeof(dataset) + (sizeof(size_t) + sizeof(float) * inputs_length) * dataset_length); if (!new_dataset) return NULL; new_dataset->expected_indices = (size_t *)(new_dataset + 1); - new_dataset->all_inputs = (float *)(new_dataset->expected_indices + inputs_length); + new_dataset->all_inputs = (float *)(new_dataset->expected_indices + dataset_length); new_dataset->length = dataset_length; new_dataset->inputs_length = inputs_length; diff --git a/src/network.c b/src/network.c index fb9a7a9b1..826e328cf 100644 --- a/src/network.c +++ b/src/network.c @@ -235,12 +235,12 @@ void layer_learn_collect_gradient(const neural_network *nn, float *restrict laye cblas_saxpy(curr_layer->length, 1.0f, curr_layer->delta, 1, layer_bias_gradients, 1); } -void stochastic_gd(const neural_network *nn, float learn_rate, const float *data) { +void stochastic_gd(const neural_network *nn, float learn_rate, const float *data, size_t data_expected_index) { assert(nn && data); compute_network(nn, data); for (size_t i = 0; i < nn->length; i++) { - layer_learn(nn, nn->length - i - 1, learn_rate, data, nn->inputs_length); + layer_learn(nn, nn->length - i - 1, learn_rate, data, data_expected_index); } } diff --git a/test/network.cpp b/test/network.cpp index b7468b941..c91ea2c0d 100644 --- a/test/network.cpp +++ b/test/network.cpp @@ -106,7 +106,8 @@ TEST(NetworkTest, StochasticGDSingleLayer) { for (size_t i = 0; i < 50000; i++) { for (size_t j = 0; j < test_dataset->length; j++) { - stochastic_gd(nn, 0.03f, &test_dataset->all_inputs[randnum_u32(test_dataset->length, 0) * test_dataset->inputs_length]); + size_t randnum = randnum_u32(test_dataset->length, 0); + stochastic_gd(nn, 0.03f, &test_dataset->all_inputs[randnum * test_dataset->inputs_length], test_dataset->expected_indices[randnum]); } if (i % 10000 == 0) { printf("Single layer learn cost: %f\n", cost(nn, test_dataset, test_dataset->length)); @@ -133,7 +134,8 @@ TEST(NetworkTest, StochasticGDTests) { 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->all_inputs[randnum_u32(test_dataset->length, 0) * test_dataset->inputs_length]); + size_t randnum = randnum_u32(test_dataset->length, 0); + stochastic_gd(nn, 0.001f, &test_dataset->all_inputs[randnum * test_dataset->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)); @@ -154,7 +156,8 @@ TEST(NetworkTest, StochasticGDTests) { for (size_t i = 0; i < 50000; i++) { for (size_t j = 0; j < test_dataset->length; j++) { - stochastic_gd(nn, 0.03f, &test_dataset->all_inputs[randnum_u32(test_dataset->length, 0) * test_dataset->inputs_length]); + size_t randnum = randnum_u32(test_dataset->length, 0); + stochastic_gd(nn, 0.03f, &test_dataset->all_inputs[randnum * test_dataset->inputs_length], test_dataset->expected_indices[randnum]); } if (i % 10000 == 0) { printf("Stochastic Non-linearly separable learn cost: %f\n", cost(nn, test_dataset, test_dataset->length)); diff --git a/test/test_utils.h b/test/test_utils.h index dcb064076..6fe5d87c1 100644 --- a/test/test_utils.h +++ b/test/test_utils.h @@ -5,7 +5,6 @@ extern "C" { #include "cneuron/cneuron.h" } - float sigmoid(float val, bool is_deravative); dataset *get_xor(); From 06c8d95c667b941a80e60418bc9fd36df5452fb2 Mon Sep 17 00:00:00 2001 From: Hoonlim Lee Date: Sat, 27 Jun 2026 14:48:43 +0800 Subject: [PATCH 6/6] Edited the readme --- .github/workflows/ci.yml | 2 +- CMakeLists.txt | 33 ++++++++++++++++++--------------- README.md | 31 ++++++++++++++++--------------- 3 files changed, 35 insertions(+), 31 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9ac1885f3..67f1986c9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -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 diff --git a/CMakeLists.txt b/CMakeLists.txt index f405bfe70..39835754d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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() @@ -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 $<$: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 $<$:tests> + ) +endif() diff --git a/README.md b/README.md index 4e3899a05..2b13b870d 100644 --- a/README.md +++ b/README.md @@ -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; @@ -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)); @@ -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