{"id":18340141,"url":"https://github.com/f33rni/petalflow","last_synced_at":"2025-07-23T19:32:38.772Z","repository":{"id":224417639,"uuid":"762835471","full_name":"F33RNI/PetalFlow","owner":"F33RNI","description":"A pure C light-weight library for machine learning","archived":false,"fork":false,"pushed_at":"2024-04-16T18:57:55.000Z","size":339,"stargazers_count":4,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-04-09T20:48:15.833Z","etag":null,"topics":["ai","alternative","amd","arduino","arm","artificial-intelligence","avr","c","esp32","machine-learning","ml","neural-network","neural-networks","stm32","tensorflow","x86-64"],"latest_commit_sha":null,"homepage":"","language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"agpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/F33RNI.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-02-24T20:25:59.000Z","updated_at":"2025-02-01T02:18:28.000Z","dependencies_parsed_at":"2024-04-15T23:42:41.340Z","dependency_job_id":"fa8b07eb-c44c-4adf-a8a7-e52c64f42400","html_url":"https://github.com/F33RNI/PetalFlow","commit_stats":null,"previous_names":["f33rni/petalflow"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/F33RNI/PetalFlow","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/F33RNI%2FPetalFlow","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/F33RNI%2FPetalFlow/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/F33RNI%2FPetalFlow/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/F33RNI%2FPetalFlow/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/F33RNI","download_url":"https://codeload.github.com/F33RNI/PetalFlow/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/F33RNI%2FPetalFlow/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":266738301,"owners_count":23976415,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-07-23T02:00:09.312Z","response_time":66,"last_error":null,"robots_txt_status":null,"robots_txt_updated_at":null,"robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ai","alternative","amd","arduino","arm","artificial-intelligence","avr","c","esp32","machine-learning","ml","neural-network","neural-networks","stm32","tensorflow","x86-64"],"created_at":"2024-11-05T20:21:07.483Z","updated_at":"2025-07-23T19:32:38.738Z","avatar_url":"https://github.com/F33RNI.png","language":"C","funding_links":[],"categories":[],"sub_categories":[],"readme":"# 🌸 PetalFlow\n\n| \u003cimg src=\"logo.png\" height=\"128\" height=\"128\" alt=\"PetalFlow logo\"\u003e | \u003ch3\u003eA pure C light-weight library for machine learning\u003c/h3\u003e |\n| ------------------------------------------------------------------- | :---------------------------------------------------------: |\n\n----------\n\n## 🚧 PetalFlow is under heavy development\n\n\u003e Please use with caution and wait for the proper release\n\u003e\n\u003e Currently, PetalFlow has only been tested on Linux and Windows with x86_64 architecture\n\u003e\n\u003e In the future, it is planned to extend its usability even to **AVR / ARM / ESP** (such as Arduino / STM / ESP boards)\n\u003e In the future it is planned to use it even on AVR (like Arduino)\n\n## 📝 TODO for the near future\n\n- Add convolution layers\n- Python bindings\n- Test on ARM and AVR\n- More tests and examples\n\n----------\n\n## 📚 Building shared library\n\nIt's possible to build shared library using `cmake`\n\n```shell\ncmake -B build -DTESTS=OFF -DLOGGER_LEVEL=1\ncmake --build build --config Release\n```\n\nShared library will be located inside `build` directory\n\n----------\n\n## 🏗️ Getting started\n\nBelow is an example of an extremely simple classifier capable of just comparing two numbers\n\n![Alt text](demo_classifier_structure.jpg)\n\n1. Include base libraries\n\n    ```c\n    #include \u003cstdint.h\u003e\n    #include \u003cstdio.h\u003e\n    #include \u003cstdlib.h\u003e\n\n    #include \"flower.h\"\n    #include \"random.h\"\n    ```\n\n2. Add functions to print array and create demo dataset\n\n    \u003cdetails\u003e\n    \u003csummary\u003eClick to show code\u003c/summary\u003e\n\n    ```c\n    /**\n     * @brief Prints 1D array as 1D or multidimensional array\n     *\n     * @param array pointer to array\n     * @param rows number of rows (height)\n     * @param cols number of columns (width)\n     * @param depth number of channel (depth)\n     */\n    void print_array(float *array, uint32_t rows, uint32_t cols, uint32_t depth) {\n        for (uint32_t row = 0; row \u003c rows; ++row) {\n            for (uint32_t col = 0; col \u003c cols; ++col) {\n                if (depth \u003e 1)\n                    printf(\"(\");\n                for (uint32_t channel = 0; channel \u003c depth; ++channel) {\n                    printf(\"%.4f\", array[(row * cols + col) * depth + channel]);\n\n                    if (channel \u003c depth - 1)\n                        printf(\", \");\n                }\n                if (depth \u003e 1)\n                    printf(\")\");\n                printf(\"\\t\");\n            }\n            printf(\"\\n\");\n        }\n    }\n\n    /**\n     * @brief Generates 2D array of random floats from -10.0 to 10.0\n     *\n     * @param rows number of rows (outer array length)\n     * @param cols number of elements in each internal array\n     * @return float** 2D array of random floats\n     */\n    float **dense_generate_input_data(uint32_t rows, uint32_t cols) {\n        // Allocate memory for the outer array (rows)\n        float **array = (float **) malloc(rows * sizeof(float *));\n        if (!array) {\n            printf(\"Error allocating array for dense_generate_input_data!\\n\");\n            return NULL;\n        }\n\n        for (uint32_t row = 0; row \u003c rows; ++row) {\n            // Allocate memory for each internal array (columns)\n            array[row] = (float *) malloc(cols * sizeof(float));\n            if (!array[row]) {\n                printf(\"Error allocating array[row] for dense_generate_input_data!\\n\");\n                return NULL;\n            }\n\n            // Populate the internal array with random float values in (-10, 10] interval\n            for (uint32_t col = 0; col \u003c cols; ++col) {\n                array[row][col] = rk_float_() * 20.f - 10.f;\n            }\n        }\n        return array;\n    }\n\n    /**\n     * @brief Generates 2D array of expected outputs by comparing 1st and 2nd\n     * elements of input_data array\n     *\n     * @param input_data 2D array of random floats\n     * @param rows number of rows (outer array length)\n     * @return float** true outputs (cols = 2)\n     */\n    float **dense_generate_output_data(float **input_data, uint32_t rows) {\n        // Allocate memory for the outer array (rows)\n        float **array = (float **) malloc(rows * sizeof(float *));\n        if (!array) {\n            printf(\"Error allocating array for dense_generate_output_data!\\n\");\n            return NULL;\n        }\n\n        for (uint32_t row = 0; row \u003c rows; ++row) {\n            // Allocate memory for each internal array (columns)\n            array[row] = (float *) calloc(2U, sizeof(float));\n            if (!array[row]) {\n                printf(\"Error allocating array[row] for \"\n                        \"dense_generate_output_data!\\n\");\n                return NULL;\n            }\n\n            // 1 \u003e 2\n            if (input_data[row][0] \u003e input_data[row][1])\n                array[row][0] = 1.f;\n\n            // 1 \u003c= 2\n            else\n                array[row][1] = 1.f;\n        }\n        return array;\n    }\n    ```\n\n    \u003c/details\u003e\n\n3. Initialize datasets\n\n    \u003cdetails\u003e\n    \u003csummary\u003eClick to show code\u003c/summary\u003e\n\n    ```c\n    // Set random seed\n    // rk_seed_(time(NULL) \u0026 0xFFFFFFFFUL);\n    rk_seed_(0);\n\n    // 1000 numbers from -10 to 10: 80% train, 20% validation\n    uint32_t train_dataset_length = 800;\n    uint32_t validation_dataset_length = 200;\n\n    // Generate validation datasets\n    float **train_dataset_inputs = dense_generate_input_data(train_dataset_length, 2U);\n    float **validation_dataset_inputs = dense_generate_input_data(validation_dataset_length, 2U);\n    if (!train_dataset_inputs || !validation_dataset_inputs) {\n        printf(\"train_dataset_inputs or validation_dataset_inputs allocation failed\\n\");\n        return 1U;\n    }\n\n    // Generate outputs\n    float **train_dataset_outputs = dense_generate_output_data(train_dataset_inputs, train_dataset_length);\n    float **validation_dataset_outputs =\n        dense_generate_output_data(validation_dataset_inputs, validation_dataset_length);\n    if (!train_dataset_outputs || !validation_dataset_outputs) {\n        printf(\"train_dataset_outputs or est_dataset_outputs allocation failed\\n\");\n        return 1U;\n    }\n    ```\n\n    \u003c/details\u003e\n\n4. Initialize petals\n\n    \u003e Initializes petal's struct and petal's weights if needed Sets petal-\u003eerror_code in case of error.\n    \u003e\n    \u003e **Parameters**\n    \u003e - `petal_type`: type of the petal (PETAL_TYPE_...)\n    \u003e - `first`: true if it's the first petal (output_left is input data) to prevent error_on_input calculation\n    \u003e - `input_shape`: pointer to `petal_shape_s` struct:\n    \u003e   - `rows`: height of input data\n    \u003e   - `cols`: width (or size for 1D) of input data\n    \u003e   - `depth`: number of channels of input data\n    \u003e   - `length`: calculates internally\n    \u003e - `output_shape`: pointer to `petal_shape_s` struct:\n    \u003e   - `rows`: height of output data\n    \u003e   - `cols`: width (or size for 1D) of output data\n    \u003e   - `depth`: number of channels of output data\n    \u003e   - `length`: calculates internally\n    \u003e - `weights`: pointer to `weights_s` struct (for `PETAL_TYPE_DENSE_1D`) or NULL for other types:\n    \u003e   - `trainable`: 1 if weights will be trained or 0 if not\n    \u003e   - `initializer`: weights initializer (`WEIGHTS_INIT_...`)\n    \u003e   - `weights`: pass NULL to initialize weights or pointer to previously initialized weights\n    \u003e   - `center`: constant for `WEIGHTS_INIT_CONSTANT` or center of distribution for other initializers\n    \u003e   - `deviation`: deviation of distribution (ignored for `WEIGHTS_INIT_CONSTANT`)\n    \u003e - `bias_weights`: pointer to `weights_s` struct (for `PETAL_TYPE_DENSE_1D`) or NULL for other types:\n    \u003e   - `trainable`: 1 if bias weights will be trained or 0 if not\n    \u003e   - `initializer`: bias weights initializer (`WEIGHTS_INIT_...`)\n    \u003e   - `weights`: pass NULL to initialize bias weights or pointer to previously initialized bias weights\n    \u003e   - `center`: constant for `WEIGHTS_INIT_CONSTANT` or center of distribution for other initializers\n    \u003e   - `deviation`: deviation of distribution (ignored for `WEIGHTS_INIT_CONSTANT`)\n    \u003e - `activation`: pointer to `activation_s` struct or NULL to disable activation:\n    \u003e   - `type`: activation function (`ACTIVATION_...`)\n    \u003e   - `linear_alpha`: factor for linear activation (ax + c) (for `ACTIVATION_LINEAR` only). Default = 1.0\n    \u003e   - `linear_const`: constant for linear activation (ax + c) (for `ACTIVATION_LINEAR` only). Default = 0.0\n    \u003e   - `relu_leak`: leak amount (for `ACTIVATION_RELU` only). Default = 0.01\n    \u003e   - `elu_alpha`: the value to which an ELU saturates for negative net inputs (for `ACTIVATION_ELU` only). Default = 0.01\n    \u003e   - `swish_beta`: beta for turning Swish into E-Swish (for `ACTIVATION_SWISH` only). Default = 1.0\n    \u003e - `dropout`: ratio of dropped outputs (0 to 1)\n    \u003e - `center`: center of normalization for `PETAL_TYPE_NORMALIZE_...` Default: 0.0\n    \u003e - `deviation`: deviation of normalization for `PETAL_TYPE_NORMALIZE_...` Default: 1.0\n    \u003e\n    \u003e **Returns**\n    \u003e - `petal_s*`: petal's struct\n\n    **Available weights initializers:**\n   - `WEIGHTS_INIT_CONSTANT`\n   - `WEIGHTS_INIT_RANDOM_UNIFORM`\n   - `WEIGHTS_INIT_RANDOM_GAUSSIAN`\n   - `WEIGHTS_INIT_XAVIER_GLOROT_UNIFORM`\n   - `WEIGHTS_INIT_XAVIER_GLOROT_GAUSSIAN`\n   - `WEIGHTS_INIT_KAIMING_HE_UNIFORM`\n   - `WEIGHTS_INIT_KAIMING_HE_GAUSSIAN`\n\n    **Available activation functions:**\n   - `ACTIVATION_LINEAR`\n   - `ACTIVATION_RELU`\n   - `ACTIVATION_ELU`\n   - `ACTIVATION_SOFTSIGN`\n   - `ACTIVATION_SIGMOID`\n   - `ACTIVATION_HARD_SIGMOID`\n   - `ACTIVATION_SWISH`\n   - `ACTIVATION_SOFTMAX`\n   - `ACTIVATION_TANH`\n\n    ```c\n    petal_s *petal_hidden1 =\n        petal_init(PETAL_TYPE_DENSE_1D, true, \u0026(petal_shape_s){1U, 2U, 1U, 0UL}, \u0026(petal_shape_s){1U, 2U, 1U, 0UL},\n                    \u0026(weights_s){true, WEIGHTS_INIT_XAVIER_GLOROT_GAUSSIAN, 4U, NULL, NULL, 0.f, 1.f, NULL, NULL, 0U},\n                    \u0026(weights_s){true, WEIGHTS_INIT_CONSTANT, 2U, NULL, NULL, 0.f, 1.f, NULL, NULL, 0U},\n                    \u0026(activation_s){ACTIVATION_RELU, 1.f, 0.f, 0.0f, 0.00f, 1.f, NULL}, 0.0f, 0.f, 1.f);\n    petal_s *petal_hidden2 =\n        petal_init(PETAL_TYPE_DENSE_1D, false, \u0026(petal_shape_s){1U, 2U, 1U, 0UL}, \u0026(petal_shape_s){1U, 2U, 1U, 0UL},\n                    \u0026(weights_s){true, WEIGHTS_INIT_XAVIER_GLOROT_GAUSSIAN, 4U, NULL, NULL, 0.f, 1.f, NULL, NULL, 0U},\n                    \u0026(weights_s){true, WEIGHTS_INIT_CONSTANT, 2U, NULL, NULL, 0.f, 1.f, NULL, NULL, 0U},\n                    \u0026(activation_s){ACTIVATION_RELU, 1.f, 0.f, 0.0f, 0.00f, 1.f, NULL}, 0.0f, 0.f, 1.f);\n    petal_s *petal_output =\n        petal_init(PETAL_TYPE_DENSE_1D, false, \u0026(petal_shape_s){1U, 2U, 1U, 0UL}, \u0026(petal_shape_s){1U, 2U, 1U, 0UL},\n                    \u0026(weights_s){true, WEIGHTS_INIT_XAVIER_GLOROT_GAUSSIAN, 6U, NULL, NULL, 0.f, 1.f, NULL, NULL, 0U},\n                    \u0026(weights_s){true, WEIGHTS_INIT_CONSTANT, 2U, NULL, NULL, 0.f, 1.f, NULL, NULL, 0U},\n                    \u0026(activation_s){ACTIVATION_SOFTMAX, 1.f, 0.f, 0.0f, 0.01f, 1.f, NULL}, 0.0f, 0.f, 1.f);\n    ```\n\n5. Review generated weights\n\n    ```c\n    printf(\"In -\u003e hidden 1 weights:\\n\");\n    print_array(petal_hidden1-\u003eweights-\u003eweights, 2U, 2U, 1U);\n    printf(\"In -\u003e hidden 1 bias weights:\\n\");\n    print_array(petal_hidden1-\u003ebias_weights-\u003eweights, 1U, 2U, 1U);\n\n    printf(\"hidden 1 -\u003e hidden 2 weights:\\n\");\n    print_array(petal_hidden2-\u003eweights-\u003eweights, 2U, 2U, 1U);\n    printf(\"hidden 1 -\u003e hidden 2 bias weights:\\n\");\n    print_array(petal_hidden2-\u003ebias_weights-\u003eweights, 1U, 2U, 1U);\n\n    printf(\"hidden 2 -\u003e out weights:\\n\");\n    print_array(petal_output-\u003eweights-\u003eweights, 2U, 2U, 1U);\n    printf(\"hidden 2 -\u003e out bias weights:\\n\");\n    print_array(petal_output-\u003ebias_weights-\u003eweights, 1U, 2U, 1U);\n    ```\n\n    ```text\n    In -\u003e hidden 1 weights:\n    0.0464  0.6727\n    0.7127  -1.2468\n    In -\u003e hidden 1 bias weights:\n    0.0000  0.0000\n    hidden 1 -\u003e hidden 2 weights:\n    -0.2981 -0.2883\n    -0.7801 -0.4236\n    hidden 1 -\u003e hidden 2 bias weights:\n    0.0000  0.0000\n    hidden 2 -\u003e out weights:\n    1.5173  -1.2947\n    -0.9410 0.3021\n    hidden 2 -\u003e out bias weights:\n    0.0000  0.0000\n    ```\n\n6. Initialize flower\n\n    \u003e Initializes flower using array of petals.\n    \u003e\n    \u003e **Parameters**\n    \u003e - `petals`: pointer to an array of pointers of petals\n    \u003e - `petals_length`: number of petals\n    \u003e\n    \u003e **Returns**\n    \u003e - `flower_s*`: initialized flower\n\n    ```c\n    petal_s *petals[] = {petal_hidden1, petal_hidden2, petal_output};\n    flower_s *flower = flower_init(petals, 3U);\n    ```\n\n7. Show model output before training\n\n    X1 = 1, X2 = 2\n\n    ```c\n    // Show prediction before training\n    printf(\"Before training [1.0, 2.0] -\u003e [1 \u003e 2, 1 \u003c= 2]:\\t\\t\");\n    print_array(flower_predict(flower, (float[]){1.f, 2.f}), 1U, 2U, 1U);\n    ```\n\n    ```text\n    Before training [1.0, 2.0] -\u003e [1 \u003e 2, 1 \u003c= 2]:          0.5000  0.5000\n    ```\n\n8. Initialize optimizer and metrics\n\n    \u003e **Parameters**\n    \u003e - `type`: optimizer type (`OPTIMIZER_...`)\n    \u003e - `learning_rate`: learning rate (required for all optimizer types) Default: 0.01\n    \u003e - `momentum`: accelerates gradient descent and dampens oscillations (for `OPTIMIZER_SGD_MOMENTUM`)\n    \u003e - `beta_1`: hyperparameter (for `OPTIMIZER_RMS_PROP` and `OPTIMIZER_ADAM`) Default: 0.9\n    \u003e - `beta_2`: hyperparameter (for `OPTIMIZER_ADAM`) Default: 0.999\n\n    **Available metrics:**\n   - `METRICS_TIME_ELAPSED`\n   - `METRICS_LOSS_TRAIN`\n   - `METRICS_ACCURACY_TRAIN`\n   - `METRICS_LOSS_VALIDATION`\n   - `METRICS_ACCURACY_VALIDATION`\n\n    ```c\n    // Initialize optimizer\n    optimizer_s optimizer = (optimizer_s){OPTIMIZER_ADAM, .01f, 0.f, .89f, .99f};\n\n    // Initialize metrics\n    metrics_s *metrics = metrics_init(1);\n    metrics_add(metrics, METRICS_TIME_ELAPSED);\n    metrics_add(metrics, METRICS_LOSS_TRAIN);\n    metrics_add(metrics, METRICS_ACCURACY_TRAIN);\n    metrics_add(metrics, METRICS_LOSS_VALIDATION);\n    metrics_add(metrics, METRICS_ACCURACY_VALIDATION);\n    ```\n\n9. Train model\n\n    \u003e Early implementation of backpropagation learning.\n    \u003e\n    \u003e **Parameters**\n    \u003e - `flower`: pointer to initialized `flower_s` struct\n    \u003e - `loss_type`: loss function (`LOSS_...`)\n    \u003e - `optimizer`: pointer to initialized `optimizer_s` struct with the following parameters:\n    \u003e   - `type`: optimizer type (`OPTIMIZER_...`)\n    \u003e   - `learning_rate`: learning rate (required for all optimizer types) Default: 0.01\n    \u003e   - `momentum`: accelerates gradient descent and dampens oscillations (for `OPTIMIZER_SGD_MOMENTUM`)\n    \u003e   - `beta_1`: hyperparameter (for `OPTIMIZER_RMS_PROP` and `OPTIMIZER_ADAM`) Default: 0.9\n    \u003e   - `beta_2`: hyperparameter (for `OPTIMIZER_ADAM`) Default: 0.999\n    \u003e - `metrics`: pointer to initialized `metrics_s` struct\n    \u003e - `inputs_train`: pointer to array of arrays of training input data (train dataset)\n    \u003e - `outputs_true_train`: pointer to array of arrays of training output data (train dataset)\n    \u003e - `outputs_true_train_sparse`: pointer to array of `label_s` arrays of sparse training output data (1 = [0, 1, ...])\n    \u003e - `train_length`: number of training samples (size of training dataset)\n    \u003e - `inputs_validation`: pointer to array of arrays of validation input data (validation dataset)\n    \u003e - `outputs_true_validation`: pointer to array of arrays of validation output data (train dataset)\n    \u003e - `outputs_true_validation_sparse`: pointer to array of `label_s` arrays of sparse validation output data\n    \u003e - `validation_length`: number of validation samples (size of validation dataset)\n    \u003e - `batch_size`: samples per batch\n    \u003e - `epochs`: total number of training epochs\n\n    **Available loss functions:**\n    - `LOSS_MEAN_SQUARED_ERROR`\n    - `LOSS_MEAN_SQUARED_LOG_ERROR`\n    - `LOSS_ROOT_MEAN_SQUARED_LOG_ERROR`\n    - `LOSS_MEAN_ABS_ERROR`\n    - `LOSS_BINARY_CROSSENTROPY`\n    - `LOSS_CATEGORICAL_CROSSENTROPY`\n\n    ```c\n    uint32_t epochs = 10;\n    uint32_t batch_size = 40;\n    flower_train(flower, LOSS_CATEGORICAL_CROSSENTROPY, \u0026optimizer, metrics, train_dataset_inputs,\n                 train_dataset_outputs, NULL, train_dataset_length, validation_dataset_inputs,\n                 validation_dataset_outputs, NULL, validation_dataset_length, batch_size, epochs);\n    ```\n\n    ```text\n    [2024-04-16 00:28:45] [INFO] [flower_train] Training started\n    [2024-04-16 00:28:45] [INFO] [flower_train] Epoch: 1/10\n    [====================] 20/20 | 00:00:00 | Tloss:   0.2788 | Tacc:  95.00% | Vloss:   0.2481 | Vacc:  93.50%\n    [2024-04-16 00:28:45] [INFO] [flower_train] Epoch: 2/10\n    [====================] 20/20 | 00:00:00 | Tloss:   0.1589 | Tacc:  92.50% | Vloss:   0.1467 | Vacc:  96.00%\n    [2024-04-16 00:28:45] [INFO] [flower_train] Epoch: 3/10\n    [====================] 20/20 | 00:00:00 | Tloss:   0.1022 | Tacc:  95.00% | Vloss:   0.1076 | Vacc:  95.50%\n    [2024-04-16 00:28:45] [INFO] [flower_train] Epoch: 4/10\n    [====================] 20/20 | 00:00:00 | Tloss:   0.0770 | Tacc:  97.50% | Vloss:   0.0761 | Vacc:  96.50%\n    [2024-04-16 00:28:45] [INFO] [flower_train] Epoch: 5/10\n    [====================] 20/20 | 00:00:00 | Tloss:   0.0519 | Tacc: 100.00% | Vloss:   0.0515 | Vacc:  98.50%\n    [2024-04-16 00:28:45] [INFO] [flower_train] Epoch: 6/10\n    [====================] 20/20 | 00:00:00 | Tloss:   0.0199 | Tacc: 100.00% | Vloss:   0.0334 | Vacc:  99.00%\n    [2024-04-16 00:28:45] [INFO] [flower_train] Epoch: 7/10\n    [=============\u003e      ] 20/20 | 00:00:00 | Tloss:   0.0109 | Tacc: 100.00% | Vloss:   0.0185 | Vacc: 100.00%\n    ...\n    ```\n\n10. Test the result\n\n    ```c\n    // Test training result on a new data\n    float *result;\n    printf(\"After training [1.0, 10.0] -\u003e [1 \u003e 2, 1 \u003c= 2]:\\t\\t\");\n    result = flower_predict(flower, (float[]){1.f, 10.f});\n    printf(\"After training [20.0, 10.0] -\u003e [1 \u003e 2, 1 \u003c= 2]:\\t\\t\");\n    result = flower_predict(flower, (float[]){20.f, 10.f});\n    print_array(result, 1U, 2U, 1U);\n    printf(\"After training [-1.0, 10.0] -\u003e [1 \u003e 2, 1 \u003c= 2]:\\t\\t\");\n    result = flower_predict(flower, (float[]){-1.f, 10.f});\n    print_array(result, 1U, 2U, 1U);\n    ```\n\n    ```text\n    After training [1.0, 10.0] -\u003e [1 \u003e 2, 1 \u003c= 2]:          0.0072  0.9928\n    After training [20.0, 10.0] -\u003e [1 \u003e 2, 1 \u003c= 2]:         0.9339  0.0661\n    After training [-1.0, 10.0] -\u003e [1 \u003e 2, 1 \u003c= 2]:         0.0072  0.9928\n    ```\n\n11. Free memory\n    \u003e\n    \u003e ```c\n    \u003e void weights_destroy(weights_s *weights,\n    \u003e                      bool destroy_struct,\n    \u003e                      bool destroy_internal_array)\n    \u003e ```\n    \u003e\n    \u003e Frees memory allocated by weights struct.\n    \u003e\n    \u003e **Parameters**\n    \u003e - `weights`: pointer to `weights_s` struct or NULL\n    \u003e - `destroy_struct`: true to destroy struct itself (set to false if struct was defined manually)\n    \u003e - `destroy_internal_array`: true to also destroy `weights-\u003eweights` array\n\n    \u003e ```c\n    \u003e void flower_destroy(flower_s *flower,\n    \u003e                      bool destroy_petals,\n    \u003e                      bool destroy_weights_array,\n    \u003e                      bool destroy_bias_weights_array)\n    \u003e ```\n    \u003e\n    \u003e Frees memory allocated by flower struct.\n    \u003e\n    \u003e **Parameters**\n    \u003e - `flower`: pointer to `flower_s` struct\n    \u003e - `destroy_petals`: true to also destroy each petal\n    \u003e - `destroy_weights_array`: true to also destroy `weights-\u003eweights` array for each petal, false to not\n    \u003e - `destroy_bias_weights_array`: true to also destroy `bias_weights-\u003eweights` array for each petal, false to not\n\n    ```c\n    // Destroy internal array of weights\n    weights_destroy(petal_hidden1-\u003eweights, false, true);\n    weights_destroy(petal_hidden1-\u003ebias_weights, false, true);\n    weights_destroy(petal_hidden2-\u003eweights, false, true);\n    weights_destroy(petal_hidden2-\u003ebias_weights, false, true);\n    weights_destroy(petal_output-\u003eweights, false, true);\n    weights_destroy(petal_output-\u003ebias_weights, false, true);\n\n    // Destroy flower without destroying petals\n    flower_destroy(flower, false, false, false);\n\n    // Destroy metrics\n    metrics_destroy(metrics);\n\n    // Destroy datasets\n    for (uint16_t i = 0; i \u003c train_dataset_length; ++i) {\n        free(train_dataset_inputs[i]);\n        free(train_dataset_outputs[i]);\n    }\n    for (uint16_t i = 0; i \u003c validation_dataset_length; ++i) {\n        free(validation_dataset_inputs[i]);\n        free(validation_dataset_outputs[i]);\n    }\n    free(train_dataset_inputs);\n    free(train_dataset_outputs);\n    free(validation_dataset_inputs);\n    free(validation_dataset_outputs);\n    ```\n\n----------\n\n## 🧾 Logging\n\nPetalFlow has a simple logging implementation for formatting debug, info, warning and error messages\n\n\u003e **logger()**\n\u003e\n\u003e ```c\n\u003e void logger(uint8_t level,\n\u003e             const char *tag,\n\u003e             const char *message_or_format,\n\u003e             ... )\n\u003e ```\n\u003e\n\u003e Formats and prints logging entry.\n\u003e\n\u003e **Parameters**\n\u003e\n\u003e - `level`: logging level (`LOG_D`, `LOG_I`, `LOG_W`, `LOG_E`, or `LOG_NONE`)\n\u003e - `tag`: logging tag (for example, name of function)\n\u003e - `message_or_format`: message to log or format for other arguments\n\u003e - `...`: other logger arguments, for example: `logger(LOG_I, \"TAG\", \"Hello world %d, %.2f\", 123, 4.5678f);` would result in `[YYYY-MM-DD HH:MM:SS] [INFO] [TAG] Hello world 123, 4.57`\n\nYou can enable logging by defining `LOGGING` (for gcc: `-DLOGGING`). Other available definitions:\n\n- `LOGGER_LEVEL \u003cvalue\u003e` - Minimal allowed logging level. Default: `LOG_I`\n  - `LOG_D` - 0\n  - `LOG_I` - 1\n  - `LOG_W` - 2\n  - `LOG_E` - 3\n  - `LOG_NONE` - 255\n- `LOGGER_DISABLE_TIME` - Define to **disable** printing current time\n- `LOGGER_TIME_FORMAT \u003cvalue\u003e` - Time formatter. Default: `\"[%Y-%m-%d %H:%M:%S]\"`\n- `LOGGER_DISABLE_LEVEL` - Define to **disable** printing current entry level\n- `LOGGER_LEVEL_FIXED` - Define to **enable** fixed-width logging level printing\n- `LOGGER_LEVEL_FIXED_FORMAT \u003cvalue\u003e` - Fixed-width level formatter. Default: `\"[%-7s]\"`\n- `LOGGER_DISABLE_TAG` - Define to **disable** printing tag\n\n----------\n\n## ✅ Tests and examples\n\nYou can find more examples in `test/main.c` file\n\nYou can build `petalflow_tests` target by using `cmake`:\n\n```shell\ncmake -B build -DTESTS=ON -DLOGGER_LEVEL=1\ncmake --build build --config Release\n\nbuild/petalflow_tests\n```\n\nOr by using `gcc`:\n\n```shell\ngcc -o petalflow test/main.c src/*.c -Iinclude -DLOGGING -DLOGGER_LEVEL=1 -lm\n\n./petalflow\n```\n\n----------\n\n## 📄 Documentation\n\nYou can build Doxygen documentation using provided `Doxygen` file\n\nJust clone repo and run:\n\n```shell\ndoxygen\n```\n\nThis will generate HTML and LaTeX with the following structure:\n\n```text\ndocs\n├── html\n│   ├── activation_8c.html\n...\n│   ├── index.html\n...\n│   └── weights_8h_source.html\n└── latex\n    ├── activation_8c.tex\n    ...\n    ├── Makefile\n    ...\n    └── weights_8h.tex\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ff33rni%2Fpetalflow","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ff33rni%2Fpetalflow","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ff33rni%2Fpetalflow/lists"}