From 6c7dd929deb240bfd37c3c107304d2adce88175e Mon Sep 17 00:00:00 2001 From: Fatih Cetinkaya <965295+devfacet@users.noreply.github.com> Date: Sun, 7 Apr 2024 02:21:38 -0400 Subject: [PATCH] Add nn_neuron_set_weights and nn_neuron_set_bias --- examples/arch/arm/cmsis-dsp/neuron/main.c | 4 ++-- examples/arch/arm/neon/neuron/main.c | 4 ++-- examples/arch/generic/neuron/main.c | 4 ++-- include/nn_neuron.h | 10 ++++++++-- src/nn_neuron.c | 23 ++++++++++++++++++---- tests/arch/arm/cmsis-dsp/neuron/main.c | 24 +++++++++++------------ tests/arch/arm/neon/neuron/main.c | 24 +++++++++++------------ tests/arch/generic/dot_product/main.c | 12 ++++++------ tests/arch/generic/neuron/main.c | 24 +++++++++++------------ 9 files changed, 75 insertions(+), 54 deletions(-) diff --git a/examples/arch/arm/cmsis-dsp/neuron/main.c b/examples/arch/arm/cmsis-dsp/neuron/main.c index aa2d231..35487be 100644 --- a/examples/arch/arm/cmsis-dsp/neuron/main.c +++ b/examples/arch/arm/cmsis-dsp/neuron/main.c @@ -15,13 +15,13 @@ int main(int argc, char *argv[]) { // Init vars NNNeuron neuron; - int n_inputs = 3; float inputs[NEURON_MAX_WEIGHTS] = {1, 2, 3}; float weights[NEURON_MAX_WEIGHTS] = {0.2f, 0.8f, -0.5f}; + int n_weights = 3; float bias = 2.0f; // Compute the output - nn_neuron_init(&neuron, weights, n_inputs, bias, nn_activation_func_identity, nn_dot_product_cmsis); + nn_neuron_init(&neuron, weights, n_weights, bias, nn_activation_func_identity, nn_dot_product_cmsis); printf("output (CMSIS): %f\n", nn_neuron_compute(&neuron, inputs)); return 0; diff --git a/examples/arch/arm/neon/neuron/main.c b/examples/arch/arm/neon/neuron/main.c index 70107f4..c77ba18 100644 --- a/examples/arch/arm/neon/neuron/main.c +++ b/examples/arch/arm/neon/neuron/main.c @@ -15,13 +15,13 @@ int main(int argc, char *argv[]) { // Init vars NNNeuron neuron; - int n_inputs = 3; float inputs[NEURON_MAX_WEIGHTS] = {1, 2, 3}; float weights[NEURON_MAX_WEIGHTS] = {0.2f, 0.8f, -0.5f}; + int n_weights = 3; float bias = 2.0f; // Compute the output - nn_neuron_init(&neuron, weights, n_inputs, bias, nn_activation_func_identity, nn_dot_product_neon); + nn_neuron_init(&neuron, weights, n_weights, bias, nn_activation_func_identity, nn_dot_product_neon); printf("output (NEON): %f\n", nn_neuron_compute(&neuron, inputs)); return 0; diff --git a/examples/arch/generic/neuron/main.c b/examples/arch/generic/neuron/main.c index 89c53e0..1f43789 100644 --- a/examples/arch/generic/neuron/main.c +++ b/examples/arch/generic/neuron/main.c @@ -10,13 +10,13 @@ int main(int argc, char *argv[]) { // Init vars NNNeuron neuron; - int n_inputs = 3; float inputs[NEURON_MAX_WEIGHTS] = {1, 2, 3}; float weights[NEURON_MAX_WEIGHTS] = {0.2f, 0.8f, -0.5f}; + int n_weights = 3; float bias = 2.0f; // Compute the output - nn_neuron_init(&neuron, weights, n_inputs, bias, nn_activation_func_identity, nn_dot_product); + nn_neuron_init(&neuron, weights, n_weights, bias, nn_activation_func_identity, nn_dot_product); printf("output (generic): %f\n", nn_neuron_compute(&neuron, inputs)); return 0; diff --git a/include/nn_neuron.h b/include/nn_neuron.h index fb0a2fe..df8bae5 100644 --- a/include/nn_neuron.h +++ b/include/nn_neuron.h @@ -17,14 +17,20 @@ // real-world applications since it's not optimized for performance. typedef struct { float weights[NEURON_MAX_WEIGHTS]; + size_t n_weights; float bias; - size_t n_inputs; NNActivationFunction act_func; NNDotProductFunction dot_product_func; } NNNeuron; // nn_neuron_init initializes a neuron with the given arguments. -void nn_neuron_init(NNNeuron *neuron, const float *weights, size_t n_inputs, float bias, NNActivationFunction act_func, NNDotProductFunction dot_product_func); +void nn_neuron_init(NNNeuron *neuron, const float *weights, size_t n_weights, float bias, NNActivationFunction act_func, NNDotProductFunction dot_product_func); + +// nn_neuron_set_weights sets the weights of the given neuron. +void nn_neuron_set_weights(NNNeuron *neuron, const float *weights); + +// nn_neuron_set_bias sets the bias of the given neuron. +void nn_neuron_set_bias(NNNeuron *neuron, float bias); // nn_neuron_compute computes the given neuron and returns the output. float nn_neuron_compute(const NNNeuron *neuron, const float *inputs); diff --git a/src/nn_neuron.c b/src/nn_neuron.c index 3522994..7e11931 100644 --- a/src/nn_neuron.c +++ b/src/nn_neuron.c @@ -6,12 +6,12 @@ #include // nn_neuron_init initializes a neuron with the given arguments. -void nn_neuron_init(NNNeuron *neuron, const float *weights, size_t n_inputs, float bias, NNActivationFunction act_func, NNDotProductFunction dot_product_func) { +void nn_neuron_init(NNNeuron *neuron, const float *weights, size_t n_weights, float bias, NNActivationFunction act_func, NNDotProductFunction dot_product_func) { if (weights == NULL) { return; } - neuron->n_inputs = n_inputs; - for (size_t i = 0; i < neuron->n_inputs; ++i) { + neuron->n_weights = n_weights; + for (size_t i = 0; i < neuron->n_weights; ++i) { neuron->weights[i] = weights[i]; } neuron->bias = bias; @@ -23,6 +23,21 @@ void nn_neuron_init(NNNeuron *neuron, const float *weights, size_t n_inputs, flo } } +// nn_neuron_set_weights sets the weights of the given neuron. +void nn_neuron_set_weights(NNNeuron *neuron, const float *weights) { + if (weights == NULL) { + return; + } + for (size_t i = 0; i < neuron->n_weights; ++i) { + neuron->weights[i] = weights[i]; + } +} + +// nn_neuron_set_bias sets the bias of the given neuron. +void nn_neuron_set_bias(NNNeuron *neuron, float bias) { + neuron->bias = bias; +} + // nn_neuron_compute computes the given neuron and returns the output. float nn_neuron_compute(const NNNeuron *neuron, const float *inputs) { // Compute the output of the neuron: @@ -32,7 +47,7 @@ float nn_neuron_compute(const NNNeuron *neuron, const float *inputs) { float result = 0.0f; if (neuron->dot_product_func != NULL) { // Sum the weighted inputs - result = neuron->dot_product_func(neuron->weights, inputs, neuron->n_inputs); + result = neuron->dot_product_func(neuron->weights, inputs, neuron->n_weights); } // Add the bias result += neuron->bias; diff --git a/tests/arch/arm/cmsis-dsp/neuron/main.c b/tests/arch/arm/cmsis-dsp/neuron/main.c index 85ebf93..0b8e59a 100644 --- a/tests/arch/arm/cmsis-dsp/neuron/main.c +++ b/tests/arch/arm/cmsis-dsp/neuron/main.c @@ -15,7 +15,7 @@ typedef struct { float inputs[NEURON_MAX_WEIGHTS]; float weights[NEURON_MAX_WEIGHTS]; - int n_inputs; + int n_weights; float bias; NNDotProductFunction dot_product_func; float output_tolerance; @@ -28,7 +28,7 @@ void run_test_cases(TestCase *test_cases, int n_cases, char *info, NNDotProductF TestCase tc = test_cases[i]; NNNeuron neuron; - nn_neuron_init(&neuron, tc.weights, tc.n_inputs, tc.bias, nn_activation_func_identity, dot_product_func); + nn_neuron_init(&neuron, tc.weights, tc.n_weights, tc.bias, nn_activation_func_identity, dot_product_func); const float output = nn_neuron_compute(&neuron, tc.inputs); assert(isnan(output) == false); assert(fabs(output - tc.expected_output) < tc.output_tolerance); @@ -41,7 +41,7 @@ int main() { { .inputs = {0.5f, 1.2f, -0.8f}, .weights = {0.2f, 0.3f, -0.1f}, - .n_inputs = 3, + .n_weights = 3, .bias = 0.5f, .output_tolerance = DEFAULT_OUTPUT_TOLERANCE, .expected_output = 1.04f, @@ -50,7 +50,7 @@ int main() { { .inputs = {-0.6f, -1.1f, 0.9f}, .weights = {-0.2f, 0.5f, 0.3f}, - .n_inputs = 3, + .n_weights = 3, .bias = -0.5f, .output_tolerance = DEFAULT_OUTPUT_TOLERANCE, .expected_output = -0.66f, @@ -59,7 +59,7 @@ int main() { { .inputs = {1.5f, 2.0f, -1.0f}, .weights = {0.4f, 0.4f, -0.2f}, - .n_inputs = 3, + .n_weights = 3, .bias = 2.0f, .output_tolerance = DEFAULT_OUTPUT_TOLERANCE, .expected_output = 3.6f, @@ -68,7 +68,7 @@ int main() { { .inputs = {0.1f, -0.2f, 0.3f}, .weights = {0.3f, -0.2f, 0.1f}, - .n_inputs = 3, + .n_weights = 3, .bias = 0.05f, .output_tolerance = DEFAULT_OUTPUT_TOLERANCE, .expected_output = 0.15f, @@ -77,7 +77,7 @@ int main() { { .inputs = {-2.5f, 3.0f, -1.5f}, .weights = {0.5f, -0.5f, 0.75f}, - .n_inputs = 3, + .n_weights = 3, .bias = 1.0f, .output_tolerance = DEFAULT_OUTPUT_TOLERANCE, .expected_output = -2.875f, @@ -86,7 +86,7 @@ int main() { { .inputs = {0.0f, 0.0f, 0.0f}, .weights = {0.25f, -0.75f, 0.5f}, - .n_inputs = 3, + .n_weights = 3, .bias = 0.5f, .output_tolerance = DEFAULT_OUTPUT_TOLERANCE, .expected_output = 0.5f, @@ -95,7 +95,7 @@ int main() { { .inputs = {1.2f, -1.2f, 0.8f}, .weights = {0.0f, 0.0f, 0.0f}, - .n_inputs = 3, + .n_weights = 3, .bias = 0.25f, .output_tolerance = DEFAULT_OUTPUT_TOLERANCE, .expected_output = 0.25f, @@ -104,7 +104,7 @@ int main() { { .inputs = {1.0f, -1.0f, 1.0f}, .weights = {-1.0f, 1.0f, -1.0f}, - .n_inputs = 3, + .n_weights = 3, .bias = -0.5f, .output_tolerance = DEFAULT_OUTPUT_TOLERANCE, .expected_output = -3.5f, @@ -113,7 +113,7 @@ int main() { { .inputs = {0.123f, 0.456f, -0.789f}, .weights = {0.321f, -0.654f, 0.987f}, - .n_inputs = 3, + .n_weights = 3, .bias = 0.1f, .output_tolerance = DEFAULT_OUTPUT_TOLERANCE, .expected_output = -0.937484, @@ -122,7 +122,7 @@ int main() { { .inputs = {0.001f, -0.002f, 0.003f}, .weights = {0.004f, 0.005f, -0.006f}, - .n_inputs = 3, + .n_weights = 3, .bias = 0.0f, .output_tolerance = DEFAULT_OUTPUT_TOLERANCE, .expected_output = 0.000012f, diff --git a/tests/arch/arm/neon/neuron/main.c b/tests/arch/arm/neon/neuron/main.c index 38a7a3c..4ee31dd 100644 --- a/tests/arch/arm/neon/neuron/main.c +++ b/tests/arch/arm/neon/neuron/main.c @@ -15,7 +15,7 @@ typedef struct { float inputs[NEURON_MAX_WEIGHTS]; float weights[NEURON_MAX_WEIGHTS]; - int n_inputs; + int n_weights; float bias; NNDotProductFunction dot_product_func; float output_tolerance; @@ -28,7 +28,7 @@ void run_test_cases(TestCase *test_cases, int n_cases, char *info, NNDotProductF TestCase tc = test_cases[i]; NNNeuron neuron; - nn_neuron_init(&neuron, tc.weights, tc.n_inputs, tc.bias, nn_activation_func_identity, dot_product_func); + nn_neuron_init(&neuron, tc.weights, tc.n_weights, tc.bias, nn_activation_func_identity, dot_product_func); const float output = nn_neuron_compute(&neuron, tc.inputs); assert(isnan(output) == false); assert(fabs(output - tc.expected_output) < tc.output_tolerance); @@ -41,7 +41,7 @@ int main() { { .inputs = {0.5f, 1.2f, -0.8f}, .weights = {0.2f, 0.3f, -0.1f}, - .n_inputs = 3, + .n_weights = 3, .bias = 0.5f, .output_tolerance = DEFAULT_OUTPUT_TOLERANCE, .expected_output = 1.04f, @@ -50,7 +50,7 @@ int main() { { .inputs = {-0.6f, -1.1f, 0.9f}, .weights = {-0.2f, 0.5f, 0.3f}, - .n_inputs = 3, + .n_weights = 3, .bias = -0.5f, .output_tolerance = DEFAULT_OUTPUT_TOLERANCE, .expected_output = -0.66f, @@ -59,7 +59,7 @@ int main() { { .inputs = {1.5f, 2.0f, -1.0f}, .weights = {0.4f, 0.4f, -0.2f}, - .n_inputs = 3, + .n_weights = 3, .bias = 2.0f, .output_tolerance = DEFAULT_OUTPUT_TOLERANCE, .expected_output = 3.6f, @@ -68,7 +68,7 @@ int main() { { .inputs = {0.1f, -0.2f, 0.3f}, .weights = {0.3f, -0.2f, 0.1f}, - .n_inputs = 3, + .n_weights = 3, .bias = 0.05f, .output_tolerance = DEFAULT_OUTPUT_TOLERANCE, .expected_output = 0.15f, @@ -77,7 +77,7 @@ int main() { { .inputs = {-2.5f, 3.0f, -1.5f}, .weights = {0.5f, -0.5f, 0.75f}, - .n_inputs = 3, + .n_weights = 3, .bias = 1.0f, .output_tolerance = DEFAULT_OUTPUT_TOLERANCE, .expected_output = -2.875f, @@ -86,7 +86,7 @@ int main() { { .inputs = {0.0f, 0.0f, 0.0f}, .weights = {0.25f, -0.75f, 0.5f}, - .n_inputs = 3, + .n_weights = 3, .bias = 0.5f, .output_tolerance = DEFAULT_OUTPUT_TOLERANCE, .expected_output = 0.5f, @@ -95,7 +95,7 @@ int main() { { .inputs = {1.2f, -1.2f, 0.8f}, .weights = {0.0f, 0.0f, 0.0f}, - .n_inputs = 3, + .n_weights = 3, .bias = 0.25f, .output_tolerance = DEFAULT_OUTPUT_TOLERANCE, .expected_output = 0.25f, @@ -104,7 +104,7 @@ int main() { { .inputs = {1.0f, -1.0f, 1.0f}, .weights = {-1.0f, 1.0f, -1.0f}, - .n_inputs = 3, + .n_weights = 3, .bias = -0.5f, .output_tolerance = DEFAULT_OUTPUT_TOLERANCE, .expected_output = -3.5f, @@ -113,7 +113,7 @@ int main() { { .inputs = {0.123f, 0.456f, -0.789f}, .weights = {0.321f, -0.654f, 0.987f}, - .n_inputs = 3, + .n_weights = 3, .bias = 0.1f, .output_tolerance = DEFAULT_OUTPUT_TOLERANCE, .expected_output = -0.937484, @@ -122,7 +122,7 @@ int main() { { .inputs = {0.001f, -0.002f, 0.003f}, .weights = {0.004f, 0.005f, -0.006f}, - .n_inputs = 3, + .n_weights = 3, .bias = 0.0f, .output_tolerance = DEFAULT_OUTPUT_TOLERANCE, .expected_output = 0.000012f, diff --git a/tests/arch/generic/dot_product/main.c b/tests/arch/generic/dot_product/main.c index bee4bf8..f1fcb25 100644 --- a/tests/arch/generic/dot_product/main.c +++ b/tests/arch/generic/dot_product/main.c @@ -14,7 +14,7 @@ typedef struct { float a[4]; float b[4]; - int n_inputs; + int n_weights; float bias; NNDotProductFunction dot_product_func; float output_tolerance; @@ -26,7 +26,7 @@ void run_test_cases(TestCase *test_cases, int n_cases, char *info, NNDotProductF for (int i = 0; i < n_cases; ++i) { TestCase tc = test_cases[i]; - const float output = dot_product_func(tc.a, tc.b, tc.n_inputs); + const float output = dot_product_func(tc.a, tc.b, tc.n_weights); assert(isnan(output) == false); assert(fabs(output - tc.expected_output) < tc.output_tolerance); printf("passed: %s case=%d info=%s\n", __func__, i + 1, info); @@ -38,7 +38,7 @@ int main() { { .a = {0.5f}, .b = {0.2f}, - .n_inputs = 1, + .n_weights = 1, .output_tolerance = DEFAULT_OUTPUT_TOLERANCE, .expected_output = 0.1f, }, @@ -46,7 +46,7 @@ int main() { { .a = {-0.6f, -1.1f}, .b = {-0.2f, 0.5f}, - .n_inputs = 2, + .n_weights = 2, .output_tolerance = DEFAULT_OUTPUT_TOLERANCE, .expected_output = -0.43f, }, @@ -54,7 +54,7 @@ int main() { { .a = {1.5f, 2.0f, -1.0f}, .b = {0.4f, 0.4f, -0.2f}, - .n_inputs = 3, + .n_weights = 3, .output_tolerance = DEFAULT_OUTPUT_TOLERANCE, .expected_output = 1.6f, }, @@ -62,7 +62,7 @@ int main() { { .a = {0.36f, 0.17f, 0.96f, 0.12f}, .b = {0.77f, 0.09f, 0.12f, 0.81f}, - .n_inputs = 4, + .n_weights = 4, .output_tolerance = DEFAULT_OUTPUT_TOLERANCE, .expected_output = 0.5048, }, diff --git a/tests/arch/generic/neuron/main.c b/tests/arch/generic/neuron/main.c index 827b72c..8ff3159 100644 --- a/tests/arch/generic/neuron/main.c +++ b/tests/arch/generic/neuron/main.c @@ -14,7 +14,7 @@ typedef struct { float inputs[NEURON_MAX_WEIGHTS]; float weights[NEURON_MAX_WEIGHTS]; - int n_inputs; + int n_weights; float bias; NNDotProductFunction dot_product_func; float output_tolerance; @@ -27,7 +27,7 @@ void run_test_cases(TestCase *test_cases, int n_cases, char *info, NNDotProductF TestCase tc = test_cases[i]; NNNeuron neuron; - nn_neuron_init(&neuron, tc.weights, tc.n_inputs, tc.bias, nn_activation_func_identity, dot_product_func); + nn_neuron_init(&neuron, tc.weights, tc.n_weights, tc.bias, nn_activation_func_identity, dot_product_func); const float output = nn_neuron_compute(&neuron, tc.inputs); assert(isnan(output) == false); assert(fabs(output - tc.expected_output) < tc.output_tolerance); @@ -40,7 +40,7 @@ int main() { { .inputs = {0.5f, 1.2f, -0.8f}, .weights = {0.2f, 0.3f, -0.1f}, - .n_inputs = 3, + .n_weights = 3, .bias = 0.5f, .output_tolerance = DEFAULT_OUTPUT_TOLERANCE, .expected_output = 1.04f, @@ -49,7 +49,7 @@ int main() { { .inputs = {-0.6f, -1.1f, 0.9f}, .weights = {-0.2f, 0.5f, 0.3f}, - .n_inputs = 3, + .n_weights = 3, .bias = -0.5f, .output_tolerance = DEFAULT_OUTPUT_TOLERANCE, .expected_output = -0.66f, @@ -58,7 +58,7 @@ int main() { { .inputs = {1.5f, 2.0f, -1.0f}, .weights = {0.4f, 0.4f, -0.2f}, - .n_inputs = 3, + .n_weights = 3, .bias = 2.0f, .output_tolerance = DEFAULT_OUTPUT_TOLERANCE, .expected_output = 3.6f, @@ -67,7 +67,7 @@ int main() { { .inputs = {0.1f, -0.2f, 0.3f}, .weights = {0.3f, -0.2f, 0.1f}, - .n_inputs = 3, + .n_weights = 3, .bias = 0.05f, .output_tolerance = DEFAULT_OUTPUT_TOLERANCE, .expected_output = 0.15f, @@ -76,7 +76,7 @@ int main() { { .inputs = {-2.5f, 3.0f, -1.5f}, .weights = {0.5f, -0.5f, 0.75f}, - .n_inputs = 3, + .n_weights = 3, .bias = 1.0f, .output_tolerance = DEFAULT_OUTPUT_TOLERANCE, .expected_output = -2.875f, @@ -85,7 +85,7 @@ int main() { { .inputs = {0.0f, 0.0f, 0.0f}, .weights = {0.25f, -0.75f, 0.5f}, - .n_inputs = 3, + .n_weights = 3, .bias = 0.5f, .output_tolerance = DEFAULT_OUTPUT_TOLERANCE, .expected_output = 0.5f, @@ -94,7 +94,7 @@ int main() { { .inputs = {1.2f, -1.2f, 0.8f}, .weights = {0.0f, 0.0f, 0.0f}, - .n_inputs = 3, + .n_weights = 3, .bias = 0.25f, .output_tolerance = DEFAULT_OUTPUT_TOLERANCE, .expected_output = 0.25f, @@ -103,7 +103,7 @@ int main() { { .inputs = {1.0f, -1.0f, 1.0f}, .weights = {-1.0f, 1.0f, -1.0f}, - .n_inputs = 3, + .n_weights = 3, .bias = -0.5f, .output_tolerance = DEFAULT_OUTPUT_TOLERANCE, .expected_output = -3.5f, @@ -112,7 +112,7 @@ int main() { { .inputs = {0.123f, 0.456f, -0.789f}, .weights = {0.321f, -0.654f, 0.987f}, - .n_inputs = 3, + .n_weights = 3, .bias = 0.1f, .output_tolerance = DEFAULT_OUTPUT_TOLERANCE, .expected_output = -0.937484, @@ -121,7 +121,7 @@ int main() { { .inputs = {0.001f, -0.002f, 0.003f}, .weights = {0.004f, 0.005f, -0.006f}, - .n_inputs = 3, + .n_weights = 3, .bias = 0.0f, .output_tolerance = DEFAULT_OUTPUT_TOLERANCE, .expected_output = 0.000012f,