diff --git a/current_test.cpp b/current_test.cpp new file mode 100644 index 0000000..42b751f --- /dev/null +++ b/current_test.cpp @@ -0,0 +1,34 @@ + +#include "micrograd.hpp" +#include "value.hpp" +#include "mlp.hpp" +using namespace microgradCpp; +/* + --file_name ./data/iris.csv + --encode variety +*/ +int main(int argc, char *argv[]) +{ + // DatasetType dataset = get_iris(); + DataFrame df; + df.from_csv("./data/wine.csv", true, ';'); + // df.normalize( ); + df.encode_column("quality"); + df.print(); + df.shuffle(); + df.print(); + double TRAIN_SIZE{0.8}; + // Create MLP model + // Input: 4 features, hidden layers: [7,7], output: 3 classes + // Define the model and hyperparameters + // MLP model(4, {10, 10, 3}); + MLP model(4, {16, 16, 10}); + auto params = model.parameters(); + double learning_rate = 0.01; + int epochs; // = 100; + std::cout << "Epoch : ?"; + std::cin >> epochs; + AdamOptimizer optimizer(params, learning_rate); + train_eval(df, TRAIN_SIZE, model, optimizer, epochs); + return 0; +} \ No newline at end of file diff --git a/easy.cpp b/easy.cpp index e0cdfca..e1ef277 100644 --- a/easy.cpp +++ b/easy.cpp @@ -1,17 +1,14 @@ -// MIT License +// MIT License // Copyright (c) [2024] Sermet Pekin - // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: - // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. - // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -20,39 +17,27 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. // */ - #include "micrograd.hpp" using namespace microgradCpp; - int main() { - DatasetType dataset = get_iris(); shuffle(dataset); double TRAIN_SIZE{0.8}; - // Create MLP model // Input: 4 features, hidden layers: [7,7], output: 3 classes // Define the model and hyperparameters MLP model(4, {10, 10, 3}); double learning_rate = 0.01; int epochs = 100; - // Train and evaluate the model train_eval(dataset, TRAIN_SIZE, model, learning_rate, epochs); - return 0; } - - /* Notes ----------- - g++ -std=c++17 -Iinclude -O2 -o main main_easy.cpp - // or make run - - -*/ +*/ \ No newline at end of file diff --git a/easy_adam.cpp b/easy_adam.cpp index 699d625..3206452 100644 --- a/easy_adam.cpp +++ b/easy_adam.cpp @@ -1,19 +1,16 @@ + #include "micrograd.hpp" using namespace microgradCpp; // MIT License - // Copyright (c) [2024] Sermet Pekin - // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: - // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. - // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -23,17 +20,13 @@ using namespace microgradCpp; // THE SOFTWARE. // */ /* - g++ -std=c++17 -Iinclude -O2 -o main easy_adam.cpp - */ int main() { - DatasetType dataset = get_iris(); shuffle(dataset); double TRAIN_SIZE{0.8}; - // Create MLP model // Input: 4 features, hidden layers: [7,7], output: 3 classes // Define the model and hyperparameters @@ -42,26 +35,17 @@ int main() auto params = model.parameters(); double learning_rate = 0.001; int epochs = 1000; - - // Initialize Adam optimizer AdamOptimizer optimizer(params, learning_rate); - // Train and evaluate the model // train_eval(dataset, TRAIN_SIZE, model, learning_rate, epochs); train_eval(dataset, TRAIN_SIZE, model, optimizer, epochs); - return 0; } - /* Notes ----------- - g++ -std=c++17 -Iinclude -O2 -o main main_easy.cpp - // or make run - - */ \ No newline at end of file diff --git a/easy_df.cpp b/easy_df.cpp index 77ff02d..8f72a1e 100644 --- a/easy_df.cpp +++ b/easy_df.cpp @@ -1,17 +1,14 @@ -// MIT License +// MIT License // Copyright (c) [2024] Sermet Pekin - // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: - // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. - // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -21,34 +18,23 @@ // THE SOFTWARE. // */ #include "micrograd.hpp" - #include "value.hpp" #include "mlp.hpp" - using namespace microgradCpp; - int main() { - // DatasetType dataset = get_iris(); - DataFrame df; df.from_csv("./data/iris.csv"); df.normalize(); df.encode_column("variety"); - - - df.print(); df.shuffle(); df.print(); - // stop(); - // return 0; // shuffle(dataset); double TRAIN_SIZE{0.8}; - // Create MLP model // Input: 4 features, hidden layers: [7,7], output: 3 classes // Define the model and hyperparameters @@ -58,27 +44,19 @@ int main() int epochs = 300; // Train and evaluate the model train_eval(df, TRAIN_SIZE, model, learning_rate, epochs); - return 0; } /* - // Initialize Adam optimizer AdamOptimizer optimizer(params, learning_rate); - // Train and evaluate the model // train_eval(dataset, TRAIN_SIZE, model, learning_rate, epochs); train_eval(dataset, TRAIN_SIZE, model, optimizer, epochs); - */ /* Notes ----------- - g++ -std=c++17 -Iinclude -O2 -o main easy_df.cpp - // or make run - - -*/ +*/ \ No newline at end of file diff --git a/easy_df_adam.cpp b/easy_df_adam.cpp index c137d2d..18ad99a 100644 --- a/easy_df_adam.cpp +++ b/easy_df_adam.cpp @@ -1,90 +1,48 @@ -// MIT License - -// Copyright (c) [2024] Sermet Pekin - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// */ #include "micrograd.hpp" - #include "value.hpp" #include "mlp.hpp" - using namespace microgradCpp; - int main() { - // DatasetType dataset = get_iris(); - DataFrame df; df.from_csv("./data/iris.csv"); df.normalize(); df.encode_column("variety"); - df.print(); df.shuffle(); df.print(); - + // return 0; // stop(); - // return 0; // shuffle(dataset); double TRAIN_SIZE{0.8}; - // Create MLP model // Input: 4 features, hidden layers: [7,7], output: 3 classes // Define the model and hyperparameters // MLP model(4, {10, 10, 3}); MLP model(4, {16, 16, 3}); - - auto params = model.parameters(); double learning_rate = 0.01; - int epochs = 100; - - + int epochs; // = 100; + std::cout << "Epoch : ?"; + std::cin >> epochs; // Initialize Adam optimizer AdamOptimizer optimizer(params, learning_rate); - // Train and evaluate the model // train_eval(dataset, TRAIN_SIZE, model, learning_rate, epochs); // train_eval(dataset, TRAIN_SIZE, model, optimizer, epochs); - // Train and evaluate the model train_eval(df, TRAIN_SIZE, model, optimizer, epochs); - return 0; } /* - - - */ /* Notes ----------- - -g++ -std=c++17 -Iinclude -O2 -o main easy_df.cpp - +g++ -std=c++17 -Iinclude -O2 -o main easy_df_adam.cpp // or make run - - -*/ +*/ \ No newline at end of file diff --git a/include/adam.hpp b/include/adam.hpp index f224cc9..042e726 100644 --- a/include/adam.hpp +++ b/include/adam.hpp @@ -1,24 +1,20 @@ + #ifndef ADAM_HPP #define ADAM_HPP - #include "value.hpp" #include #include #include // MIT License - // Copyright (c) [2024] Sermet Pekin - // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: - // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. - // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -27,46 +23,34 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. // */ - /* - Adam maintains two moving averages for each parameter: - First Moment Estimate (Mean): - mt=β1mt−1+(1−β1)gtmt​=β1​mt−1​+(1−β1​)gt​ - + mt=β1mt−1+(1−β1)gtmt​=β1​mt−1​+(1−β1​)gt​ Second Moment Estimate (Uncentered Variance): - vt=β2vt−1+(1−β2)gt2vt​=β2​vt−1​+(1−β2​)gt2​ - + vt=β2vt−1+(1−β2)gt2vt​=β2​vt−1​+(1−β2​)gt2​ It then corrects these biases and updates the parameters with: - θt=θt−1−η⋅m^tv^t+ϵ - θt​=θt−1​−v^t​ - ​+ϵη⋅m^t​​ - + θt=θt−1−η⋅m^tv^t+ϵ + θt​=θt−1​−v^t​ + ​+ϵη⋅m^t​​ Where: - - gtgt​: Gradient at time tt - ηη: Learning rate - β1,β2β1​,β2​: Exponential decay rates - ϵϵ: Smoothing term (prevents division by zero) - - + gtgt​: Gradient at time tt + ηη: Learning rate + β1,β2β1​,β2​: Exponential decay rates + ϵϵ: Smoothing term (prevents division by zero) */ class AdamOptimizer { - public: double lr; // Learning rate double beta1; // Exponential decay rate for the first moment double beta2; // Exponential decay rate for the second moment double epsilon; // Small constant for numerical stability int t; // Time step (iteration count) - // For storing moments for each parameter std::vector> params; std::unordered_map m; // First moment estimates std::unordered_map v; // Second moment estimates - // Constructor AdamOptimizer(std::vector> parameters, double lr = 0.001, @@ -81,31 +65,22 @@ class AdamOptimizer v[param.get()] = 0.0; } } - - void step() { t++; // time step for (auto ¶m : params) { double g = param->grad; // Gradient of the parameter - // first moment estimate (mean) m[param.get()] = beta1 * m[param.get()] + (1.0 - beta1) * g; - // second moment estimate (uncentered variance) v[param.get()] = beta2 * v[param.get()] + (1.0 - beta2) * g * g; - // Compute bias-corrected estimates double m_hat = m[param.get()] / (1.0 - std::pow(beta1, t)); double v_hat = v[param.get()] / (1.0 - std::pow(beta2, t)); - - param->data -= lr * m_hat / (std::sqrt(v_hat) + epsilon); } } - - void zero_grad() { for (auto ¶m : params) @@ -114,5 +89,4 @@ class AdamOptimizer } } }; - #endif // ADAM_HPP \ No newline at end of file diff --git a/include/console_utils.hpp b/include/console_utils.hpp index 8d1cb9f..eb6366b 100644 --- a/include/console_utils.hpp +++ b/include/console_utils.hpp @@ -1,19 +1,16 @@ + #ifndef epic_failure_exit_HPP #define epic_failure_exit_HPP // MIT License - // Copyright (c) [2024] Sermet Pekin - // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: - // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. - // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -38,10 +35,8 @@ namespace microgradCpp { throw std::runtime_error(reason); } - std::exit(EXIT_FAILURE); } - inline void epic_out_of_range(const std::string &reason) { std::cout << "\n💥💥💥 BOOM! 💥💥💥" << std::endl; @@ -50,7 +45,6 @@ namespace microgradCpp std::cout << "📉 Better luck next time, brave coder!" << std::endl; std::cout << "🔥🔥🔥 Program terminated. 🔥🔥🔥\n" << std::endl; - throw std::out_of_range(reason); } inline void stop(const std::string &reason = "...") @@ -58,27 +52,22 @@ namespace microgradCpp epic_out_of_range(reason); } // Function to format shapes for display - inline std::string format_shape(size_t rows, size_t cols) { std::ostringstream oss; oss << rows << " x " << cols; return oss.str(); } - inline void display_header(const std::string &message) { std::cout << "\n====================================================\n"; std::cout << "🚨 " << message << "\n"; std::cout << "====================================================\n"; } - inline void display_shapes(const std::string &label, size_t input_size, size_t output_size) { std::cout << "📊 " << label << " Shape: [" << input_size << " features -> " << output_size << " targets]\n"; std::cout << "----------------------------------------------------\n"; } - } - #endif // epic_failure_exit \ No newline at end of file diff --git a/include/csv.hpp b/include/csv.hpp index e89711b..75ff09b 100644 --- a/include/csv.hpp +++ b/include/csv.hpp @@ -1,18 +1,15 @@ + /* MIT License - Copyright (c) [2024] Sermet Pekin - Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -21,11 +18,8 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ - - #ifndef CSV_L_HPP #define CSV_L_HPP - #include #include #include @@ -33,9 +27,7 @@ THE SOFTWARE. #include #include #include "types.hpp" - -using namespace microgradCpp ; - +using namespace microgradCpp ; class CSVLoader { public: @@ -61,64 +53,51 @@ class CSVLoader { throw std::runtime_error("Failed to open CSV file: " + filename); } - std::string line; bool header_skipped = false; vv_string data; - while (std::getline(file, line)) { trim(line); - // Skip empty lines if (line.empty()) { continue; } - // If skipping header, skip the first non-empty line if (skip_header && !header_skipped) { header_skipped = true; continue; } - // Split the line by the delimiter v_string row = split_line(line, delimiter); - // If the row is empty after splitting, skip it if (row.empty()) { continue; } - data.push_back(std::move(row)); } - file.close(); - if (data.empty()) { throw std::runtime_error("No data loaded from CSV. Check file format or path: " + filename); } - return data; } - private: static void trim(std::string &s) { // Remove leading spaces s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](unsigned char ch) { return !std::isspace(ch); })); - // Remove trailing spaces s.erase(std::find_if(s.rbegin(), s.rend(), [](unsigned char ch) { return !std::isspace(ch); }) .base(), s.end()); } - static v_string split_line(const std::string &line, char delimiter) { v_string cells; @@ -132,5 +111,4 @@ class CSVLoader return cells; } }; - -#endif // CSV_L_HPP +#endif // CSV_L_HPP \ No newline at end of file diff --git a/include/data_utils.hpp b/include/data_utils.hpp index 24b3970..4124984 100644 --- a/include/data_utils.hpp +++ b/include/data_utils.hpp @@ -1,21 +1,16 @@ #ifndef DataUtils_HPP - #define DataUtils_HPP // MIT License - // Copyright (c) [2024] Sermet Pekin - // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: - // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. - // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -37,19 +32,14 @@ #include "console_utils.hpp" #include "types.hpp" using namespace microgradCpp; - using vv_string = std::vector>; using vv_double = std::vector>; - - - #include #include #include #include #include "value.hpp" - -inline +inline void display_data(const ColRows& inputs, const ColRows& targets, const std::vector& column_names ) { // column headers for (const auto& col_name : column_names) { @@ -57,67 +47,50 @@ void display_data(const ColRows& inputs, const ColRows& targets, const std::vect } std::cout << std::setw(15) << std::left << "target"; std::cout << "\n"; - // separator line for (size_t i = 0; i < column_names.size() + 1; ++i) { std::cout << std::setw(15) << std::setfill('-') << "" << std::setfill(' '); } std::cout << "\n"; - // rows of data for (size_t i = 0; i < inputs.size(); ++i) { for (const auto& value : inputs[i]) { std::cout << std::setw(15) << std::left << value->data; } - // Print the target for (const auto& target : targets[i]) { std::cout << std::setw(15) << std::left << target->data; } - std::cout << "\n"; } - - std::cout << "========================\n"; } - - static inline v_shared_Value one_hot_encode(int class_index, int num_classes) { - v_shared_Value target(num_classes, std::make_shared(0.0)); - target[class_index] = std::make_shared(1.0); - return target; } - inline void log_model_info(const std::vector &layer_sizes, size_t input_features, size_t output_targets, size_t train_size, size_t test_size, double TRAIN_RATIO); - template inline void write_to_csv(const std::vector> &data, const std::string &filename) { std::ofstream file(filename); - - if (!file.is_open()) { std::cerr << "Error: Could not open file " << filename << " for writing.\n"; return; } - for (const auto &row : data) { for (size_t i = 0; i < row.size(); ++i) { file << row[i]; - if (i < row.size() - 1) { file << ","; @@ -125,7 +98,6 @@ inline void write_to_csv(const std::vector> &data, const std::str } file << "\n"; } - file.close(); std::cout << "Data successfully written to " << filename << "\n"; } @@ -133,7 +105,6 @@ inline std::unordered_map create_target_encoding(const std: { std::unordered_map encoding_map; double index = 0.0; - for (const auto &value : target_column) { if (encoding_map.find(value) == encoding_map.end()) @@ -141,15 +112,12 @@ inline std::unordered_map create_target_encoding(const std: encoding_map[value] = index++; } } - return encoding_map; } - inline vv_double convert_to_double_with_encoding(const vv_string &data, bool has_header = false) { vv_double converted_data; size_t start_index = has_header ? 1 : 0; - // Extract the target column (assuming the last column is the target) std::vector target_column; for (size_t i = start_index; i < data.size(); ++i) @@ -159,18 +127,14 @@ inline vv_double convert_to_double_with_encoding(const vv_string &data, bool has std::cerr << "Warning: Skipping empty row at index " << i << std::endl; continue; } - target_column.push_back(data[i].back()); } - if (target_column.empty()) { throw std::runtime_error("Error: No target column found in the data."); } - // Create target encoding map auto target_encoding_map = create_target_encoding(target_column); - for (size_t i = start_index; i < data.size(); ++i) { if (data[i].empty()) @@ -178,9 +142,7 @@ inline vv_double convert_to_double_with_encoding(const vv_string &data, bool has std::cerr << "Warning: Skipping empty row at index " << i << std::endl; continue; } - std::vector converted_row; - // Convert all but the last column (features) to double for (size_t j = 0; j < data[i].size() - 1; ++j) { @@ -194,21 +156,16 @@ inline vv_double convert_to_double_with_encoding(const vv_string &data, bool has converted_row.push_back(0.0); // Fallback for invalid numeric conversion } } - - std::string target_value = data[i].back(); if (target_encoding_map.find(target_value) == target_encoding_map.end()) { throw std::runtime_error("Error: Target value '" + target_value + "' not found in encoding map."); } converted_row.push_back(target_encoding_map[target_value]); - converted_data.push_back(converted_row); } - return converted_data; } - // Function to validate the dataset and model inline bool validate_dataset_and_model(const DatasetType &dataset, const MLP &model, double TRAIN_RATIO) { @@ -217,21 +174,15 @@ inline bool validate_dataset_and_model(const DatasetType &dataset, const MLP &mo display_header("Error: Dataset is empty!"); return false; } - size_t input_size = model.input_size(); size_t output_size = model.output_size(); - int sz = dataset.size(); - log_model_info(model.get_layer_sizes(), input_size, output_size, sz, sz, TRAIN_RATIO); - display_shapes("Model", input_size, output_size); - for (size_t i = 0; i < dataset.size(); ++i) { const auto &inputs = dataset[i].first; const auto &targets = dataset[i].second; - if (inputs.size() != input_size) { display_header("Input Size Mismatch Detected"); @@ -240,7 +191,6 @@ inline bool validate_dataset_and_model(const DatasetType &dataset, const MLP &mo std::cerr << "🔹 Input Shape: [" << format_shape(inputs.size(), 1) << "]\n"; return false; } - if (targets.size() != output_size) { display_header("Target Size Mismatch Detected"); @@ -249,7 +199,6 @@ inline bool validate_dataset_and_model(const DatasetType &dataset, const MLP &mo std::cerr << "🔸 Target Shape: [" << format_shape(targets.size(), 1) << "]\n"; return false; } - // Check if inputs are valid (not null) for (size_t j = 0; j < inputs.size(); ++j) { @@ -260,7 +209,6 @@ inline bool validate_dataset_and_model(const DatasetType &dataset, const MLP &mo return false; } } - // Check if targets are valid (not null) for (size_t j = 0; j < targets.size(); ++j) { @@ -272,12 +220,10 @@ inline bool validate_dataset_and_model(const DatasetType &dataset, const MLP &mo } } } - std::cout << "✅ All checks passed! Dataset and model are ready for training.\n"; std::cout << "----------------------------------------------------\n"; return true; } - inline void log_model_info(const std::vector &layer_sizes, size_t input_features, size_t output_targets, @@ -287,7 +233,6 @@ inline void log_model_info(const std::vector &layer_sizes, { std::cout << "\n📊 Model and Dataset Info\n"; std::cout << "===========================================\n"; - // Log model shape std::cout << "🧠 Model Shape:\n"; std::cout << " ┌─────────────────────────┐\n"; @@ -298,7 +243,6 @@ inline void log_model_info(const std::vector &layer_sizes, } std::cout << " └─────────────────────────┘\n"; std::cout << " ⮕ Output: " << output_targets << " targets\n"; - // Log dataset info std::cout << "\n📂 Dataset Info:\n"; std::cout << " ┌────────────────────────┐\n"; @@ -308,9 +252,7 @@ inline void log_model_info(const std::vector &layer_sizes, std::cout << " Train ratio " << TRAIN_RATIO << " \n"; std::cout << "===========================================\n\n"; } - // #include // #include // #include - -#endif // DataUtils_HPP +#endif // DataUtils_HPP \ No newline at end of file diff --git a/include/dataframe.hpp b/include/dataframe.hpp index 2fae25d..6d81a62 100644 --- a/include/dataframe.hpp +++ b/include/dataframe.hpp @@ -1,3 +1,4 @@ + /* * evdscpp: An open-source data wrapper for accessing the EVDS API. * Author: Sermet Pekin @@ -24,7 +25,6 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - #pragma once #include #include @@ -40,13 +40,10 @@ #include #include // typeid // #include "../extern/nlohmann/json.hpp" - #include #include #include #include - - #include "series.hpp" #include "header.hpp" #include "range.hpp" @@ -55,62 +52,43 @@ #include // For std::shuffle #include // For std::default_random_engine #include // For seeding the random engine - namespace microgradCpp - { class DataFrame; inline void save_as_csv(const DataFrame &df, const std::string &filename, std::optional delimiter = std::nullopt); - using Cell = std::variant; - using Column = std::vector; - class DataFrame { public: void from_csv(const std::string &filename, bool has_header = true, char delimiter = ','); - std::unordered_map columns; std::unordered_map> column_types; std::unordered_map> encoding_mappings; - std::vector column_order; - static inline bool DEFAULT_INPLACE = true; - int size() const { - return get_all_row_indices().size(); } - DataFrame operator()(const std::initializer_list &row_indices, const std::vector &col_names) { return this->slice(std::vector(row_indices.begin(), row_indices.end()), col_names, DEFAULT_INPLACE); } - DataFrame operator()(const std::initializer_list &row_indices) { return this->slice(std::vector(row_indices.begin(), row_indices.end()), column_order, DEFAULT_INPLACE); } - DataFrame operator()(const Range &range, const std::vector &col_names) { - auto numbers = range.to_vector(); - return this->slice(numbers, col_names, DEFAULT_INPLACE); } - DataFrame rows(const Range &range) { - auto numbers = range.to_vector(); - return this->slice(numbers, column_order, DEFAULT_INPLACE); } - v_string v(const Range &column_range) { v_string items; @@ -123,17 +101,13 @@ namespace microgradCpp } return items; } - vv_double to_vv_double() const { vv_double result; - if (columns.empty()) return result; - // Determine the number of rows based on the first column size_t num_rows = columns.begin()->second.size(); - // Iterate through each row for (size_t i = 0; i < num_rows; ++i) { @@ -156,94 +130,69 @@ namespace microgradCpp } result.push_back(row); } - return result; } - // vv_string v(const Range &colum_range){ - // vv_string items ; // for(int i =0 ; i< column_order.size() ; i++ ){ // if( colum_range.includes( i )) // items.push_back( column_order[ i ]) ; - // } // return items ; - // } DataFrame subset(const Range &range, const Range &colum_range) { - auto numbers = range.to_vector(); - return this->slice(numbers, column_order, DEFAULT_INPLACE); } - DataFrame operator()(const Range &range) { - auto numbers = range.to_vector(); - return this->slice(numbers, column_order, DEFAULT_INPLACE); } - DataFrame operator()(const std::vector &row_indices, const std::vector &col_names) { return this->slice(get_all_row_indices(), column_order, DEFAULT_INPLACE); } - DataFrame operator()() { - return this->slice(get_all_row_indices(), column_order, DEFAULT_INPLACE); } - // DataFrame operator()(const Range &range) // { // return this->slice(range.to_vector(), column_order, DEFAULT_INPLACE); // } - DataFrame operator()(const std::vector &row_indices) { return this->slice(row_indices, column_order, DEFAULT_INPLACE); } - DataFrame operator()(const std::vector &col_names) { - return this->slice(get_all_row_indices(), col_names, DEFAULT_INPLACE); } - DataFrame cols(const std::vector &col_names, bool inplace = DEFAULT_INPLACE) { - return this->slice(get_all_row_indices(), col_names, inplace); } - DataFrame slice(const std::vector &row_indices, const std::vector &col_names, bool inplace = DEFAULT_INPLACE) { - size_t num_rows = columns.empty() ? 0 : columns.begin()->second.size(); for (size_t row_idx : row_indices) { if (row_idx >= num_rows) { - epic_out_of_range("Row index " + std::to_string(row_idx) + " is out of bounds. DataFrame has " + std::to_string(num_rows) + " rows."); } } - std::unordered_map new_columns; std::unordered_map> new_column_types; std::unordered_map> new_encoding_mappings; - for (const auto &col_name : col_names) { if (columns.find(col_name) == columns.end()) { throw std::invalid_argument("Column " + col_name + " not found"); } - Column new_col; for (const auto &row_idx : row_indices) { @@ -253,16 +202,13 @@ namespace microgradCpp } new_col.push_back(columns.at(col_name)[row_idx]); } - new_columns[col_name] = std::move(new_col); new_column_types[col_name] = column_types.at(col_name); - if (encoding_mappings.find(col_name) != encoding_mappings.end()) { new_encoding_mappings[col_name] = encoding_mappings.at(col_name); } } - if (inplace) { columns = std::move(new_columns); @@ -281,7 +227,6 @@ namespace microgradCpp return result; } } - DataFrame() { } @@ -294,11 +239,9 @@ namespace microgradCpp new_df.column_order = column_order; return new_df; } - // ............................................................. get_column_names std::vector get_column_names() const { - return column_order; // std::vector names; // for (const auto &[name, _] : columns) @@ -307,7 +250,6 @@ namespace microgradCpp // } // return names; } - void print_shape() const { size_t num_rows = 0; @@ -316,10 +258,8 @@ namespace microgradCpp num_rows = columns.begin()->second.size(); } size_t num_columns = columns.size(); - std::cout << "📊 DataFrame Shape: [" << num_rows << " rows x " << num_columns << " columns]\n"; } - // ............................................................. get_variant_type_index std::type_index get_variant_type_index(const Cell &value) { @@ -328,20 +268,16 @@ namespace microgradCpp using T = std::decay_t; return std::type_index(typeid(T)); }, value); } - // ............................................................. add_value void add_value(const std::string &column_name, const Cell &value) { if (columns.find(column_name) == columns.end()) { columns[column_name] = Column(); - column_types[column_name] = get_variant_type_index(value); } - columns[column_name].push_back(value); } - void add_value_at(const std::string &column_name, size_t position, const Cell &value) { if (columns.find(column_name) == columns.end()) @@ -349,16 +285,13 @@ namespace microgradCpp columns[column_name] = Column(); column_types[column_name] = get_variant_type_index(value); } - Column &column = columns[column_name]; if (column.size() <= position) { column.resize(position + 1, std::monostate{}); } - column[position] = value; } - std::type_index get_column_type(const std::string &column_name) const { auto it = column_types.find(column_name); @@ -371,7 +304,6 @@ namespace microgradCpp return typeid(void); } } - template std::optional> values(const std::string &column_name) const { @@ -381,7 +313,6 @@ namespace microgradCpp const Column &col = it->second; std::vector result; result.reserve(col.size()); - for (const auto &cell : col) { if (std::holds_alternative(cell)) @@ -405,18 +336,15 @@ namespace microgradCpp } return std::nullopt; } - std::string clean_colname(const std::string &col) const { std::string str = col; std::replace(str.begin(), str.end(), '.', '_'); return str; } - Series operator[](const std::string &column_name) const { std::string c_colname = clean_colname(column_name); - auto it = columns.find(c_colname); if (it == columns.end()) { @@ -424,7 +352,6 @@ namespace microgradCpp } return Series(it->second); } - template void to_csv(const T &file_name, std::optional delimiter = std::nullopt) { @@ -442,53 +369,56 @@ namespace microgradCpp "Unsupported file name type. Must be std::string, const char*, or char array."); } } - void shuffle() { if (columns.empty()) { throw std::runtime_error("Cannot shuffle an empty DataFrame."); } - size_t num_rows = columns.begin()->second.size(); - // a vector of indices representing row positions std::vector indices(num_rows); std::iota(indices.begin(), indices.end(), 0); - // the random number generator unsigned seed = std::chrono::system_clock::now().time_since_epoch().count(); std::shuffle(indices.begin(), indices.end(), std::default_random_engine(seed)); - // new columns with shuffled rows std::unordered_map shuffled_columns; - for (const auto &col_name : column_order) { Column &col = columns[col_name]; Column shuffled_col; - for (size_t i : indices) { shuffled_col.push_back(col[i]); } - shuffled_columns[col_name] = shuffled_col; } - // Replace the original columns with shuffled columns columns = shuffled_columns; } - - void normalize() + void normalize(const v_string &cols_given = {}) { - for (const auto &col_name : column_order) + v_string cols_to_normalize; + if (cols_given.empty()) + cols_to_normalize = column_order; + else + cols_to_normalize = cols_given; + normalize_internal(cols_to_normalize); + } + void normalize(const Range &range) + { + v_string some_cols = range.filter(column_order); + return normalize_internal(some_cols); + } + void normalize_internal(const v_string &cols_given) + { + for (const auto &col_name : cols_given) { - Column &col = columns[col_name]; - + auto &col = columns.at(col_name); // Step 1: Collect numeric values std::vector numeric_values; - for (const auto &cell : col) + for ( auto &cell : col) { if (std::holds_alternative(cell)) { @@ -499,26 +429,22 @@ namespace microgradCpp numeric_values.push_back(static_cast(std::get(cell))); } } - if (numeric_values.empty()) { std::cout << "Skipping non-numeric column: " << col_name << std::endl; continue; // Skip non-numeric columns } - // Step 2: Calculate mean and standard deviation double mean = std::accumulate(numeric_values.begin(), numeric_values.end(), 0.0) / numeric_values.size(); double std_dev = std::sqrt(std::accumulate(numeric_values.begin(), numeric_values.end(), 0.0, [mean](double acc, double val) { return acc + (val - mean) * (val - mean); }) / numeric_values.size()); - if (std_dev == 0) { std::cout << "Standard deviation is zero; skipping normalization for column: " << col_name << std::endl; continue; } - // Step 3: Normalize numeric cells in the column for (auto &cell : col) { @@ -531,23 +457,17 @@ namespace microgradCpp cell = (static_cast(std::get(cell)) - mean) / std_dev; } } - std::cout << "Normalized column: " << col_name << std::endl; } } - - - void show() { auto cols = get_column_names(); - std::cout << divider(); prints("Columns : \n"); std::cout << divider(); print_v(cols); } - void print() const { rocking_star_print(); @@ -558,7 +478,6 @@ namespace microgradCpp { std::cout << column_name << ": \n"; std::cout << divider(); - for (const auto &cell : data) { std::visit([](const auto &v) @@ -573,12 +492,10 @@ namespace microgradCpp std::cout << v << " "; } }, cell); } - std::cout << "\n" << microgradCpp::divider() << "\n"; } } - void encode_column(const std::string &column_name) { auto it = columns.find(column_name); @@ -586,11 +503,9 @@ namespace microgradCpp { throw std::invalid_argument("Column not found: " + column_name); } - Column &column = it->second; std::unordered_map encoding_map; double index = 0.0; - // Create the encoding map for (const auto &cell : column) { @@ -603,7 +518,6 @@ namespace microgradCpp } } } - // Replace the column values with their encoded doubles for (auto &cell : column) { @@ -613,11 +527,9 @@ namespace microgradCpp cell = encoding_map[value]; } } - // Update the column type to double column_types[column_name] = typeid(double); } - void print_encoding_map(const std::string &column_name) const { auto it = encoding_mappings.find(column_name); @@ -626,14 +538,12 @@ namespace microgradCpp std::cout << "No encoding map found for column: " << column_name << "\n"; return; } - std::cout << "Encoding map for column '" << column_name << "':\n"; for (const auto &[key, value] : it->second) { std::cout << key << " -> " << value << "\n"; } } - std::string get_type_string(const std::string &column_name) const { auto it = column_types.find(column_name); @@ -649,12 +559,10 @@ namespace microgradCpp } return "unknown"; } - void rocking_star_print(size_t n = 10) const { std::cout << "\n🚀 DataFrame Overview 🚀\n"; std::cout << "========================\n"; - // Display shape size_t num_rows = 0; if (!columns.empty()) @@ -662,7 +570,6 @@ namespace microgradCpp num_rows = columns.begin()->second.size(); } std::cout << "📝 Shape: (" << num_rows << " rows, " << columns.size() << " columns)\n"; - // Display column names and types std::cout << "\n🧩 Columns and Data Types:\n"; std::cout << "---------------------------\n"; @@ -670,25 +577,21 @@ namespace microgradCpp { std::cout << "🔹 " << std::setw(15) << std::left << name << " | [" << get_type_string(name) << "]\n"; } - // Display first 'n' rows std::cout << "\n🔍 First " << n << " Rows:\n"; std::cout << "---------------------------\n"; - // Print column headers in the correct order for (const auto &name : column_order) { std::cout << std::setw(15) << std::left << name; } std::cout << "\n"; - // Print separator line for (size_t i = 0; i < column_order.size(); ++i) { std::cout << std::setw(15) << std::setfill('-') << "" << std::setfill(' '); } std::cout << "\n"; - // Print rows in the correct order for (size_t row = 0; row < std::min(n, num_rows); ++row) { @@ -713,20 +616,14 @@ namespace microgradCpp } std::cout << "\n"; } - std::cout << "========================\n\n"; } - - - - void add_column(const std::string &name, const Column &col, std::optional type = std::nullopt) { columns[name] = col; column_order.push_back(name); column_types[name] = type; } - private: std::vector get_all_row_indices() const { @@ -735,19 +632,15 @@ namespace microgradCpp std::iota(indices.begin(), indices.end(), 0); return indices; } - void m_save_csv(const std::string &file_name, std::optional delimiter = std::nullopt) { save_as_csv(*this, file_name, delimiter); } - void m_save_csv(const char *file_name, std::optional delimiter = std::nullopt) { const std::string file_name_(file_name); save_as_csv(*this, file_name_, delimiter); } }; // class - // inline void save_as_csv(const DataFrame &df, const std::string &filename, std::optional delimiter = std::nullopt); - } // namespace \ No newline at end of file diff --git a/include/dataframe_utils.hpp b/include/dataframe_utils.hpp index 636f4bb..ebda8d6 100644 --- a/include/dataframe_utils.hpp +++ b/include/dataframe_utils.hpp @@ -25,9 +25,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - #pragma once - #include #include #include @@ -47,31 +45,26 @@ #include #include #include - #include "dataframe.hpp" namespace microgradCpp { - static inline std::string trim(const std::string &str) { auto start = str.find_first_not_of(" \t"); auto end = str.find_last_not_of(" \t"); return (start == std::string::npos) ? "" : str.substr(start, end - start + 1); } - inline bool is_numeric(const std::string &str) { std::string trimmed = trim(str); if (trimmed.empty()) return false; - char *end = nullptr; std::strtod(trimmed.c_str(), &end); return end == trimmed.c_str() + trimmed.size(); } - // // Implementation of the slicing operator() - // DataFrame DataFrame::operator()(const std::vector &row_indices, const std::vector &col_names) + // DataFrame DataFrame::operator()(const std::vector &row_indices, const std::vector &col_names) // { // DataFrame result; // for (const auto &col_name : col_names) @@ -80,7 +73,6 @@ namespace microgradCpp // { // throw std::invalid_argument("Column " + col_name + " not found"); // } - // Column new_col; // for (const auto &row_idx : row_indices) // { @@ -94,30 +86,24 @@ namespace microgradCpp // } // return result; // } - inline void save_as_csv(const DataFrame &df, const std::string &filename, std::optional delimiter) { static std::string NaNstr(""); std::cout << "[saving csv] " << filename << "\n"; std::ofstream file(filename); - if (!file.is_open()) { std::cerr << "Error opening file: " << filename << std::endl; return; } - char actual_delimiter = delimiter.value_or(','); - auto column_names = df.get_column_names(); size_t max_num_rows = 0; - for (const auto &col_name : column_names) { auto col_data = df.columns.at(col_name); max_num_rows = std::max(max_num_rows, col_data.size()); } - // Write headers for (size_t col = 0; col < column_names.size(); ++col) { @@ -126,7 +112,6 @@ namespace microgradCpp file << actual_delimiter; } file << "\n"; - // Write data for (size_t row = 0; row < max_num_rows; ++row) { @@ -134,11 +119,9 @@ namespace microgradCpp { const auto &col_name = column_names[col]; const auto &col_data = df.columns.at(col_name); - if (row < col_data.size()) { const auto &cell = col_data[row]; - std::visit([&](const auto &value) { using T = std::decay_t; @@ -157,17 +140,14 @@ namespace microgradCpp { file << NaNstr; } - if (col < column_names.size() - 1) file << actual_delimiter; } file << "\n"; } - file.close(); std::cout << "CSV file saved as: " << filename << std::endl; } - inline void DataFrame::from_csv(const std::string &filename, bool has_header, char delimiter) // = true, ',' { std::ifstream file(filename); @@ -175,30 +155,24 @@ namespace microgradCpp { throw std::runtime_error("Error opening file: " + filename); } - std::string line; // std::vector column_names; bool is_first_line = true; - while (std::getline(file, line)) { std::stringstream ss(line); std::string cell; std::vector cells; - while (std::getline(ss, cell, delimiter)) { cells.push_back(trim(cell)); } - if (is_first_line && has_header) { column_order = cells; for (auto &col : column_order) { - col = trim(col); // TODO - columns[col] = Column(); column_types[col] = std::nullopt; // Initialize types as unknown } @@ -211,24 +185,20 @@ namespace microgradCpp // If no header, create generic column names for (size_t i = 0; i < cells.size(); ++i) { - std::string col_name = "column_" + std::to_string(i); column_order.push_back(col_name); columns[col_name] = Column(); column_types[col_name] = std::nullopt; - // column_names.push_back("column_" + std::to_string(i)); // columns[column_names[i]] = Column(); // column_types[column_names[i]] = std::nullopt; } is_first_line = false; } - for (size_t i = 0; i < cells.size(); ++i) { const auto &col_name = column_order[i]; const std::string &value = cells[i]; - if (is_numeric(value)) { // Try to convert to double or long long @@ -252,9 +222,7 @@ namespace microgradCpp } } } - file.close(); } - // namespace -} +} \ No newline at end of file diff --git a/include/dataprocessor.hpp b/include/dataprocessor.hpp index cd1729c..ba045c7 100644 --- a/include/dataprocessor.hpp +++ b/include/dataprocessor.hpp @@ -1,8 +1,6 @@ #ifndef DataProcessor_HPP - #define DataProcessor_HPP - #include #include #include @@ -11,11 +9,9 @@ #include "tensor_basic.hpp" #include "schema.hpp" #include "data_utils.hpp" - #include #include #include - #include #include #include @@ -26,19 +22,15 @@ #include #include "types.hpp" // MIT License - // Copyright (c) [2024] Sermet Pekin - // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: - // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. - // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -48,16 +40,13 @@ // THE SOFTWARE. // */ using namespace microgradCpp; - // Simple hash function for strings inline size_t hash_string(const std::string &str) { return std::hash{}(str); } - std::string trim(const std::string &str); std::vector> convert_to_double(const std::vector> &data, const Schema &schema); - // Helper method to trim strings inline std::string trim(const std::string &str) { @@ -65,7 +54,6 @@ inline std::string trim(const std::string &str) size_t last = str.find_last_not_of(" \t\n\r"); return (first == std::string::npos) ? "" : str.substr(first, last - first + 1); } - inline bool is_numeric(const std::string &str) { // Trim quotes and whitespace @@ -76,17 +64,14 @@ inline bool is_numeric(const std::string &str) return false; } std::string trimmed = str.substr(start, end - start + 1); - // Handle empty strings after trimming if (trimmed.empty()) { return false; } - // Check if the trimmed string represents a valid number bool decimal_point = false; bool has_digit = false; - for (size_t i = 0; i < trimmed.size(); ++i) { if (std::isdigit(trimmed[i])) @@ -107,10 +92,8 @@ inline bool is_numeric(const std::string &str) return false; // Any other character makes it non-numeric } } - return has_digit; } - class DataProcessor { public: @@ -118,29 +101,20 @@ class DataProcessor { CSVLoader loader; auto raw_data = loader.load_csv(file_path); - // Convert data to double or perform one-hot encoding std::vector> processed_data = convert_to_double(raw_data, schema); - return Tensor(processed_data); } - vv_string load_and_process(const std::string &file_path, bool skip_header = false, char delimiter = ',') { - return CSVLoader::load_csv(file_path, skip_header = skip_header, delimiter = delimiter); } - - vv_double convert_to_double_with_encoding_(const vv_string &data, bool has_header = false) { vv_double x = convert_to_double_with_encoding(data, has_header); return x; } - - - private: // Hash function to convert strings to numerical values size_t hash_string(const std::string &str) @@ -148,5 +122,4 @@ class DataProcessor return std::hash{}(str); } }; - -#endif // DataProcessor_HPP +#endif // DataProcessor_HPP \ No newline at end of file diff --git a/include/datasetType.hpp b/include/datasetType.hpp index 57ff025..bd50684 100644 --- a/include/datasetType.hpp +++ b/include/datasetType.hpp @@ -1,6 +1,6 @@ + #ifndef DatasetType_hpp #define DatasetType_hpp - #include #include #include @@ -9,19 +9,15 @@ #include "dataframe.hpp" using namespace microgradCpp; // MIT License - // Copyright (c) [2024] Sermet Pekin - // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: - // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. - // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -33,14 +29,12 @@ using namespace microgradCpp; inline DatasetType convert_to_dataset(const vv_double &data, int target_column = -1) { DatasetType dataset; - for (const auto &row : data) { if (row.empty()) { continue; // Skip empty rows } - // target column index int target_idx = (target_column == -1) ? row.size() - 1 : target_column; // inputs and targets @@ -57,30 +51,21 @@ inline DatasetType convert_to_dataset(const vv_double &data, int target_column = inputs.push_back(std::make_shared(row[i])); } } - - dataset.emplace_back(inputs, targets); } - return dataset; } - inline DatasetType convert_to_dataset(const DataFrame &df, int target_column = -1) { DatasetType dataset; vv_double data = df.to_vv_double(); - - for (const auto &row : data) { if (row.empty()) { - continue; + continue; } - - int target_idx = (target_column == -1) ? row.size() - 1 : target_column; - std::vector> inputs; std::vector> targets; for (size_t i = 0; i < row.size(); ++i) @@ -94,30 +79,23 @@ inline DatasetType convert_to_dataset(const DataFrame &df, int target_column = - inputs.push_back(std::make_shared(row[i])); } } - - dataset.emplace_back(inputs, targets); } - return dataset; } - #include #include #include #include #include - inline void write_dataset_to_csv(const DatasetType &dataset, const std::string &filename) { std::ofstream file(filename); - if (!file.is_open()) { std::cerr << "Error: Could not open file " << filename << " for writing." << std::endl; return; } - // the header size_t num_features = dataset[0].first.size(); for (size_t i = 0; i < num_features; ++i) @@ -125,30 +103,24 @@ inline void write_dataset_to_csv(const DatasetType &dataset, const std::string & file << "Feature" << i + 1 << ","; } file << "Target" << std::endl; - // the data for (const auto &sample : dataset) { const auto &features = sample.first; const auto &targets = sample.second; - // features for (const auto &feature : features) { file << feature->data << ","; } - - // target + // target if (!targets.empty()) { file << targets[0]->data; } - file << std::endl; } - file.close(); std::cout << "Dataset successfully written to " << filename << std::endl; } - -#endif // DatasetType_hpp +#endif // DatasetType_hpp \ No newline at end of file diff --git a/include/easy.hpp b/include/easy.hpp index 93f4835..2f940f8 100644 --- a/include/easy.hpp +++ b/include/easy.hpp @@ -1,21 +1,17 @@ + #ifndef EASY_HPP #define EASY_HPP - /* MIT License - Copyright (c) [2024] Sermet Pekin - Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -24,16 +20,13 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ - #include #include #include - #include #include #include #include - // #include "micrograd.hpp" #include "types.hpp" #include "loss.hpp" @@ -44,23 +37,18 @@ THE SOFTWARE. #include "datasetType.hpp" #include "console_utils.hpp" #include "data_utils.hpp" - #include "types.hpp" using namespace microgradCpp; // MIT License - // Copyright (c) [2024] Sermet Pekin - // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: - // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. - // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -72,30 +60,22 @@ using namespace microgradCpp; inline DatasetType get_iris2() // ok { DataProcessor processor; - vv_string data2 = processor.load_and_process("./data/iris.csv", true); - vv_double converted_data2 = processor.convert_to_double_with_encoding_(data2); - DatasetType dataset = convert_to_dataset(converted_data2); - return dataset; } - inline DatasetType get_iris() // ok { return get_iris2(); } - inline DatasetType get_iris1() // problem { // Load Iris dataset std::vector>> inputs; std::vector>> targets; std::string url = "./data/iris.csv"; - IrisLoader::load_iris(url, inputs, targets); - DatasetType dataset; for (size_t i = 0; i < inputs.size(); ++i) { @@ -117,9 +97,7 @@ inline void train_test_split( DatasetType &dataset, ColRows &test_inputs, ColRows &test_targets) { - size_t train_size = static_cast(dataset.size() * TRAIN_SIZE); - for (size_t i = 0; i < train_size; ++i) { train_inputs.push_back(dataset[i].first); @@ -131,47 +109,35 @@ inline void train_test_split( DatasetType &dataset, test_targets.push_back(dataset[i].second); } } - inline void train_eval( DatasetType &dataset, double TRAIN_SIZE, MLP &model, double lr = 0.01, int epochs = 100) { - // Split into train and test sets (80-20 split) ColRows train_inputs, train_targets; ColRows test_inputs, test_targets; - train_test_split(dataset, TRAIN_SIZE, train_inputs, train_targets, test_inputs, test_targets); - // Create SGD optimizer with a learning rate of 0.005 SGD optimizer(lr); - auto start = std::chrono::high_resolution_clock::now(); - // int epochs = 100; for (int epoch = 0; epoch < epochs; ++epoch) { double total_loss = 0.0; - // Training loop for (size_t i = 0; i < train_inputs.size(); ++i) { // Forward pass (training=true to possibly enable dropout or other training-specific behavior in MLP) auto predictions = model.forward(train_inputs[i], true); - // Compute Cross-Entropy Loss auto loss = Loss::cross_entropy(predictions, train_targets[i]); total_loss += loss->data; - // Backpropagation optimizer.zero_grad(model.parameters()); loss->backward(); - // Update weights optimizer.step(model.parameters()); } - std::cout << "Epoch " << epoch + 1 << "/" << epochs << ", Loss: " << total_loss / train_inputs.size() << std::endl; - // Evaluate test accuracy every 10 epochs and on the last epoch if (epoch % 10 == 0 || epoch == epochs - 1) { @@ -180,7 +146,6 @@ void train_eval( DatasetType &dataset, double TRAIN_SIZE, MLP &model, double l { // Forward pass in evaluation mode (e.g., no dropout) auto predictions = model.forward(test_inputs[i], false); - // Find predicted class (the index with max value) int predicted_class = 0; double max_value = predictions[0]->data; @@ -192,7 +157,6 @@ void train_eval( DatasetType &dataset, double TRAIN_SIZE, MLP &model, double l predicted_class = static_cast(j); } } - // Check if prediction matches the target for (size_t j = 0; j < test_targets[i].size(); ++j) { @@ -203,10 +167,8 @@ void train_eval( DatasetType &dataset, double TRAIN_SIZE, MLP &model, double l } } } - double accuracy = static_cast(correct) / test_inputs.size(); std::cout << "Epoch " << epoch + 1 << ": Test Accuracy = " << accuracy * 100.0 << "%" << std::endl; - if (epoch == epochs - 1) { auto end = std::chrono::high_resolution_clock::now(); @@ -216,5 +178,4 @@ void train_eval( DatasetType &dataset, double TRAIN_SIZE, MLP &model, double l } } } - #endif // EASY_HPP \ No newline at end of file diff --git a/include/exceptions.hpp b/include/exceptions.hpp index 3dda817..d15365a 100644 --- a/include/exceptions.hpp +++ b/include/exceptions.hpp @@ -1,18 +1,15 @@ + /* MIT License - Copyright (c) [2024] Sermet Pekin - Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -21,19 +18,14 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ - #ifndef EXC_HPP #define EXC_HPP - #include #include - class NotImplementedException : public std::exception { public: const char* what() const noexcept override { return "Function not implemented yet."; } }; - - -#endif //EXC_HPP +#endif //EXC_HPP \ No newline at end of file diff --git a/include/header.hpp b/include/header.hpp index 81f66fe..b69ab7a 100644 --- a/include/header.hpp +++ b/include/header.hpp @@ -1,3 +1,4 @@ + /* * evdscpp: An open-source data wrapper for accessing the EVDS API. * Author: Sermet Pekin @@ -24,7 +25,6 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - #pragma once #include #include @@ -35,10 +35,8 @@ #include #include #include - namespace microgradCpp { - template void print_v(const T &v) { @@ -46,25 +44,20 @@ namespace microgradCpp std::cout << x << ", "; std::cout << std::endl; } - template void prints(const T &x) { std::cout << x; } - inline bool confirm(const std::string &message, const std::string &url, bool verbose) { std::string input; if (verbose) std::cout << "[" << url << "]\n"; std::cout << message << " (y/n): "; - std::cin >> input; - return input == "y" || input == "Y"; } - inline std::string read_content_from_file(const std::string &filename) { std::ifstream infile(filename, std::ios::in); @@ -73,25 +66,20 @@ namespace microgradCpp std::cerr << "Failed to open file for reading: " << filename << std::endl; return ""; } - std::stringstream buffer; buffer << infile.rdbuf(); infile.close(); - std::cout << "Content read from " << filename << std::endl; return buffer.str(); } - inline std::string getEvds_test() { std::string resp_file = "response.json"; auto resp = read_content_from_file(resp_file); return resp; } - inline std::string divider() { return "\n================================\n"; } - } \ No newline at end of file diff --git a/include/iris.hpp b/include/iris.hpp index 1673ce8..646726e 100644 --- a/include/iris.hpp +++ b/include/iris.hpp @@ -1,18 +1,15 @@ + /* MIT License - Copyright (c) [2024] Sermet Pekin - Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -21,10 +18,8 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ - #ifndef IRIS_LOADER_HPP #define IRIS_LOADER_HPP - #include "value.hpp" #include #include @@ -35,11 +30,8 @@ THE SOFTWARE. #include #include #include "types.hpp" - -using namespace microgradCpp ; - +using namespace microgradCpp ; // using vv_shared_Value = std::vector>>; - class IrisLoader { public: @@ -47,20 +39,16 @@ class IrisLoader vv_shared_Value &inputs, vv_shared_Value &targets) { - std::unordered_map> species_map = { {"Setosa", {1.0, 0.0, 0.0}}, {"Versicolor", {0.0, 1.0, 0.0}}, {"Virginica", {0.0, 0.0, 1.0}}}; - std::ifstream file(url); if (!file.is_open()) { throw std::runtime_error("Failed to open Iris dataset: " + url); } - std::string line; - // Attempt to read the first line and check if it's a header if (std::getline(file, line)) { @@ -75,17 +63,14 @@ class IrisLoader file.seekg(0, std::ios::beg); } } - while (std::getline(file, line)) { if (line.empty()) { continue; } - std::stringstream ss(line); std::string value; - // Parse the four features std::vector> row; for (int i = 0; i < 4; ++i) @@ -94,7 +79,6 @@ class IrisLoader { throw std::runtime_error("Unexpected end of line reading features from: " + line); } - trim(value); double val; try @@ -108,20 +92,16 @@ class IrisLoader row.push_back(std::make_shared(val)); } inputs.push_back(row); - if (!std::getline(ss, value, ',')) { throw std::runtime_error("No species column found in line: " + line); } - trim(value); - auto species_it = species_map.find(value); if (species_it == species_map.end()) { throw std::runtime_error("Unknown species: '" + value + "'"); } - std::vector> target; for (auto v : species_it->second) { @@ -129,17 +109,13 @@ class IrisLoader } targets.push_back(target); } - file.close(); - if (inputs.empty()) { throw std::runtime_error("No data loaded from Iris dataset. Check file format or path."); } - std::cout << "Loaded " << inputs.size() << " samples from " << url << std::endl; } - private: // Helper function to trim whitespace and quotes static void trim(std::string &s) @@ -152,7 +128,6 @@ class IrisLoader return; } s.erase(0, start_pos); - // Remove trailing whitespace and quotes size_t end_pos = s.find_last_not_of(" \t\""); if (end_pos == std::string::npos) @@ -163,5 +138,4 @@ class IrisLoader s.erase(end_pos + 1); } }; - -#endif // IRIS_LOADER_HPP +#endif // IRIS_LOADER_HPP \ No newline at end of file diff --git a/include/linear.hpp b/include/linear.hpp index fbfec19..94144a6 100644 --- a/include/linear.hpp +++ b/include/linear.hpp @@ -1,20 +1,17 @@ + #ifndef LINEAR_HPP #define LINEAR_HPP /* MIT License - Copyright (c) [2024] Sermet Pekin - Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -23,14 +20,12 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ - #include "value.hpp" #include "mlp.hpp" #include #include #include #include - inline std::vector> softmaxLocal(const std::vector> &inputs) { // sum_exp = sum of exp(input) @@ -40,7 +35,6 @@ inline std::vector> softmaxLocal(const std::vectorexp(); // e = exp(inp) sum_exp = sum_exp + e; } - // each output = exp(input) / sum_exp std::vector> outputs; for (auto &inp : inputs) @@ -51,22 +45,18 @@ inline std::vector> softmaxLocal(const std::vector>> weights; std::vector> biases; - Linear(int in_features, int out_features) : in_features(in_features), out_features(out_features) { - std::random_device rd; std::mt19937 gen(rd()); std::uniform_real_distribution<> dist(-1.0, 1.0); - for (int i = 0; i < out_features; ++i) { std::vector> row; @@ -76,22 +66,17 @@ class Linear row.push_back(std::make_shared((rand() % 1000) / 1000.0 - 0.5, "w")); } weights.push_back(row); - biases.push_back(std::make_shared(dist(gen), "b")); } } - // weights[i] = std::make_shared((rand() % 1000) / 1000.0 - 0.5); // Values between -0.5 and 0.5 - // Forward pass std::vector> forward(const std::vector> &inputs) { - if (inputs.empty()) { throw std::runtime_error("Empty input provided to Linear layer forward pass."); } - std::vector> outputs; for (int i = 0; i < out_features; ++i) { @@ -102,7 +87,6 @@ class Linear } outputs.push_back(activation); } - // Debugging: Print outputs /* std::cout << "Linear Layer Activations: "; for (const auto& output : outputs) { @@ -126,5 +110,4 @@ class Linear return params; } }; - -#endif // LINEAR_HPP +#endif // LINEAR_HPP \ No newline at end of file diff --git a/include/loss.hpp b/include/loss.hpp index 8841817..dbfc162 100644 --- a/include/loss.hpp +++ b/include/loss.hpp @@ -1,20 +1,17 @@ + #ifndef LOSS_HPP #define LOSS_HPP /* MIT License - Copyright (c) [2024] Sermet Pekin - Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -23,7 +20,6 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ - #include #include #include @@ -31,21 +27,16 @@ THE SOFTWARE. #include "value.hpp" #include "console_utils.hpp" #include "data_utils.hpp" - using namespace microgradCpp; - inline void display_predictions_and_targets(const std::vector> &predictions, const std::vector> &targets) { const int width = 15; // Width for each column - - std::cout << "\n📊 Predictions vs Targets\n"; + std::cout << "\n📊 Predictions vs Targets\n"; std::cout << "=============================================\n"; std::cout << std::setw(width) << "Predictions" << std::setw(width) << "Targets" << "\n"; std::cout << "---------------------------------------------\n"; - size_t max_size = std::max(predictions.size(), targets.size()); - for (size_t i = 0; i < max_size; ++i) { // Display prediction value or placeholder if out of bounds @@ -57,7 +48,6 @@ inline void display_predictions_and_targets(const std::vector 0)) { - std::cout << predictions.size() << " predictions <== ==> targets " << targets.size(); - // stop( "problem") ; + stop("problem"); } auto loss = std::make_shared(0.0); - for (size_t i = 0; i < predictions.size(); ++i) { // log(p_i) @@ -106,11 +87,9 @@ class Loss // accumulate t_i * log(p_i) loss = loss + (targets[i] * logp); } - // final loss = - (sum t_i * log(p_i)) // The negative sign creates a new node, so backprop will work through it return -loss; } }; - -#endif // LOSS_HPP +#endif // LOSS_HPP \ No newline at end of file diff --git a/include/micrograd.hpp b/include/micrograd.hpp index c5c8052..b15ab5c 100644 --- a/include/micrograd.hpp +++ b/include/micrograd.hpp @@ -1,20 +1,17 @@ + #ifndef MICROGRADCPP_HPP #define MICROGRADCPP_HPP /* MIT License - Copyright (c) [2024] Sermet Pekin - Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -23,7 +20,6 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ - #include "exceptions.hpp" #include "sp_functional.hpp" #include "range.hpp" @@ -34,13 +30,10 @@ THE SOFTWARE. #include "sp_testing_utils.hpp" #include "train_eval.hpp" #include "adam.hpp" - #include "value.hpp" #include "iris.hpp" #include "data_utils.hpp" - #include "easy.hpp" - #include "linear.hpp" // #include "dataset.hpp" #include "loss.hpp" @@ -50,5 +43,4 @@ THE SOFTWARE. #include "mlp.hpp" #include "sgd.hpp" #include "console_utils.hpp" - -#endif // MICROGRADCPP_HPP +#endif // MICROGRADCPP_HPP \ No newline at end of file diff --git a/include/mlp.hpp b/include/mlp.hpp index 24b244f..6ee32cc 100644 --- a/include/mlp.hpp +++ b/include/mlp.hpp @@ -1,20 +1,17 @@ + #ifndef MLP_HPP #define MLP_HPP /* MIT License - Copyright (c) [2024] Sermet Pekin - Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -23,14 +20,12 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ - #include "linear.hpp" #include #include #include #include #include - inline std::vector> leaky_relu(const std::vector> &inputs) { std::vector> outputs; @@ -47,9 +42,7 @@ inline std::vector> leaky_relu(const std::vector> relu(const std::vector> &inputs) { std::vector> outputs; @@ -74,21 +67,16 @@ inline std::vector> relu(const std::vector> dropout(const std::vector> &inputs, double rate, bool training) { if (!training) { - return inputs; } - std::random_device rd; std::mt19937 gen(rd()); std::bernoulli_distribution drop_dist(1.0 - rate); - std::vector> outputs; for (auto &input : inputs) { @@ -112,9 +100,7 @@ inline std::vector> dropout(const std::vector> softmax(const std::vector> &inputs) { // sum_exp = sum of exp(input) @@ -124,7 +110,6 @@ inline std::vector> softmax(const std::vectorexp(); // e = exp(inp) sum_exp = sum_exp + e; } - // each output = exp(input) / sum_exp std::vector> outputs; for (auto &inp : inputs) @@ -135,38 +120,29 @@ inline std::vector> softmax(const std::vector _layer_sizes; public: std::vector> layers; - int input_size() const { - - return _in_features; + return _in_features; // 4 , { 10,10, 3 } => 4 } int output_size() const { - - return _layer_sizes.size(); + return _layer_sizes.back(); // 4 , { 10,10, 3 } => 3 } std::vector get_layer_sizes() const { - return _layer_sizes; } - MLP(int in_features, const std::vector &layer_sizes) { - _in_features = in_features; _layer_sizes = layer_sizes; - int current_in = in_features; for (int size : layer_sizes) { @@ -174,15 +150,12 @@ class MLP current_in = size; } } - std::vector> forward(const std::vector> &inputs, bool training = true) { - if (inputs.empty()) { throw std::runtime_error("Empty input provided to MLP forward pass."); } - auto activations = inputs; for (size_t i = 0; i < layers.size(); ++i) { @@ -199,7 +172,6 @@ class MLP // Apply softmax to the final layer's output return softmax(activations); } - std::vector> parameters() const { std::vector> params; @@ -211,5 +183,4 @@ class MLP return params; } }; - -#endif // MLP_HPP +#endif // MLP_HPP \ No newline at end of file diff --git a/include/range.hpp b/include/range.hpp index 3f1d71f..899d390 100644 --- a/include/range.hpp +++ b/include/range.hpp @@ -1,19 +1,15 @@ #pragma once // MIT License - // Copyright (c) [2024] Sermet Pekin - // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: - // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. - // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -26,20 +22,18 @@ #include #include #include - #include #include - #include #include #include #include // For std::abs - #include #include #include #include - +#include "types.hpp" +using namespace microgradCpp ; class Range { public: @@ -57,12 +51,10 @@ class Range { return step_ > 0 ? current_ < other.current_ : current_ > other.current_; } - private: int current_; int step_; }; - Range(int end) : start_(0), end_(end), step_(1) {} Range(int start, int end) : start_(start), end_(end), step_(1) {} Range(int start, int end, int step) : start_(start), end_(end), step_(step) @@ -70,17 +62,25 @@ class Range if (step == 0) throw std::invalid_argument("Step cannot be zero"); } - Iterator begin() const { return Iterator(start_, step_); } Iterator end() const { return Iterator(end_, step_); } - - - std::vector to_vector() const { return to_vector(); } - + /* template + std::vector filter(const std::vector &all_items) + { + std::vector result_items; + for (int i = 0; i < all_items.size(); i++) + { + if (includes(i)) + { + result_items.emplace_back(all_items[i]) + } + } + return result_items; + } */ template std::vector to_vector() const { @@ -91,17 +91,25 @@ class Range } return result; } - - - + v_string filter(const v_string &all_items) const + { + v_string result_items; + for (int i = 0; i < all_items.size(); i++) + { + if (includes(i)) + { + result_items.emplace_back(all_items[i]); + } + } + return result_items; + } bool includes(int number) const { return step_ > 0 ? number >= start_ && number < end_ && (number - start_) % step_ == 0 : number <= start_ && number > end_ && (start_ - number) % std::abs(step_) == 0; } - private: int start_; int end_; int step_; -}; +}; \ No newline at end of file diff --git a/include/schema.hpp b/include/schema.hpp index e30fe8c..ceee710 100644 --- a/include/schema.hpp +++ b/include/schema.hpp @@ -1,19 +1,16 @@ + #ifndef SCHEMA_HPP #define SCHEMA_HPP // MIT License - // Copyright (c) [2024] Sermet Pekin - // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: - // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. - // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -22,19 +19,15 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. // */ - #include #include #include - class Schema { public: std::vector columns; std::vector types; // e.g., "double", "string", "category" - Schema(const std::vector& columns, const std::vector& types) : columns(columns), types(types) {} - size_t get_column_index(const std::string& name) const { for (size_t i = 0; i < columns.size(); ++i) { if (columns[i] == name) return i; @@ -42,6 +35,4 @@ class Schema { throw std::runtime_error("Column not found: " + name); } }; - - #endif // SCHEMA_HPP \ No newline at end of file diff --git a/include/series.hpp b/include/series.hpp index 7947a2a..eb9b68d 100644 --- a/include/series.hpp +++ b/include/series.hpp @@ -25,7 +25,6 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - #pragma once #include "header.hpp" #include @@ -41,13 +40,10 @@ namespace microgradCpp { using Cell = std::variant; - class Series { - public: Series(std::vector data) : data_(std::move(data)) {} - // .................................................................. size size_t size() const { @@ -56,7 +52,6 @@ namespace microgradCpp void normalize() { std::vector numeric_values; - // Step 1: Collect numeric values for (const auto &cell : data_) { @@ -67,26 +62,22 @@ namespace microgradCpp numeric_values.push_back(static_cast(arg)); } }, cell); } - if (numeric_values.empty()) { std::cerr << "No numeric values to normalize." << std::endl; return; } - // Step 2: Calculate mean and standard deviation double mean = std::accumulate(numeric_values.begin(), numeric_values.end(), 0.0) / numeric_values.size(); double std_dev = std::sqrt(std::accumulate(numeric_values.begin(), numeric_values.end(), 0.0, [mean](double acc, double val) { return acc + (val - mean) * (val - mean); }) / numeric_values.size()); - if (std_dev == 0) { std::cerr << "Standard deviation is zero; cannot normalize." << std::endl; return; } - // Step 3: Normalize numeric cells in data_ for (auto &cell : data_) { @@ -98,30 +89,24 @@ namespace microgradCpp } }, cell); } } - // .................................................................. values_internal template std::vector values_internal() const { std::vector result; result.reserve(data_.size()); - for (const auto &cell : data_) { result.push_back(std::visit([](const auto &value) -> T { return convert(value); }, cell)); } - return result; } - // .................................................................. values - std::vector values() const { return values_internal(); } - // .................................................................. at template T at(size_t index) const @@ -130,11 +115,9 @@ namespace microgradCpp { throw std::out_of_range("Index out of range"); } - return std::visit([](const auto &value) -> T { return convert(value); }, data_[index]); } - // .................................................................. print void print() const { @@ -154,7 +137,6 @@ namespace microgradCpp } std::cout << std::endl; } - private: std::vector data_; // .................................................................. convert @@ -171,22 +153,18 @@ namespace microgradCpp { return std::numeric_limits::quiet_NaN(); } - else if constexpr (std::is_same_v>) { return std::nullopt; } - else if constexpr (std::is_same_v) { return "NaN"; } - else if constexpr (std::is_same_v) { return "NaN"; } - else { throw std::invalid_argument("Unsupported conversion from monostate"); diff --git a/include/sgd.hpp b/include/sgd.hpp index 8185943..bc5c38d 100644 --- a/include/sgd.hpp +++ b/include/sgd.hpp @@ -1,20 +1,17 @@ + #ifndef OPTIMIZER_HPP #define OPTIMIZER_HPP /* MIT License - Copyright (c) [2024] Sermet Pekin - Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -23,7 +20,6 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ - #include "value.hpp" #include #include @@ -31,19 +27,15 @@ THE SOFTWARE. #include #include #include - class BaseOptimizer { public: double lr; double momentum; std::unordered_map velocity; - explicit BaseOptimizer(double learning_rate, double momentum_factor = 0.0) : lr(learning_rate), momentum(momentum_factor) {} - virtual void step(const std::vector> ¶meters) = 0; - void zero_grad(const std::vector> ¶meters) { for (auto ¶m : parameters) @@ -52,12 +44,10 @@ class BaseOptimizer } } }; - class SGD : public BaseOptimizer { public: using BaseOptimizer::BaseOptimizer; - void step(const std::vector> ¶meters) override { for (auto ¶m : parameters) @@ -68,19 +58,16 @@ class SGD : public BaseOptimizer { velocity[param.get()] = 0.0; } - velocity[param.get()] = momentum * velocity[param.get()] - lr * param->grad; param->data += velocity[param.get()]; } } } }; - class NesterovSGD : public BaseOptimizer { public: using BaseOptimizer::BaseOptimizer; - void step(const std::vector> ¶meters) override { for (auto ¶m : parameters) @@ -91,7 +78,6 @@ class NesterovSGD : public BaseOptimizer { velocity[param.get()] = 0.0; } - double prev_velocity = velocity[param.get()]; velocity[param.get()] = momentum * velocity[param.get()] - lr * param->grad; param->data += -momentum * prev_velocity + (1 + momentum) * velocity[param.get()]; @@ -99,7 +85,4 @@ class NesterovSGD : public BaseOptimizer } } }; - - - -#endif // OPTIMIZER_HPP +#endif // OPTIMIZER_HPP \ No newline at end of file diff --git a/include/sp_functional.hpp b/include/sp_functional.hpp index 37ac89d..9e99e08 100644 --- a/include/sp_functional.hpp +++ b/include/sp_functional.hpp @@ -1,18 +1,15 @@ + #pragma once // MIT License - // Copyright (c) [2024] Sermet Pekin - // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: - // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. - // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE diff --git a/include/sp_testing_utils.hpp b/include/sp_testing_utils.hpp index 3d0fa49..d6b66c0 100644 --- a/include/sp_testing_utils.hpp +++ b/include/sp_testing_utils.hpp @@ -1,23 +1,19 @@ + #ifndef SPTEST_HPP #define SPTEST_HPP - #include #include #include // MIT License - // Copyright (c) [2024] Sermet Pekin - // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: - // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. - // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -28,7 +24,6 @@ // */ namespace sptest { - static inline void create_temp_csv(const std::string &filename, const std::string &content) { std::ofstream file(filename); @@ -36,11 +31,9 @@ namespace sptest file << content; file.close(); } - template static inline bool contains(const std::vector &vec, const U &item) { - try { T x = static_cast(item); @@ -48,11 +41,8 @@ namespace sptest } catch (...) { - return false; } } - } - -#endif // SPTEST_HPP +#endif // SPTEST_HPP \ No newline at end of file diff --git a/include/tensor_basic.hpp b/include/tensor_basic.hpp index 3cade40..aa208c1 100644 --- a/include/tensor_basic.hpp +++ b/include/tensor_basic.hpp @@ -1,19 +1,16 @@ + #include #include // MIT License - // Copyright (c) [2024] Sermet Pekin - // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: - // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. - // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -25,9 +22,7 @@ class Tensor { public: std::vector> data; - Tensor(const std::vector>& data) : data(data) {} - void print() const { for (const auto& row : data) { for (double val : row) { @@ -36,5 +31,4 @@ class Tensor { std::cout << "\n"; } } -}; - +}; \ No newline at end of file diff --git a/include/train_eval.hpp b/include/train_eval.hpp index c782fb8..a2de3a1 100644 --- a/include/train_eval.hpp +++ b/include/train_eval.hpp @@ -1,5 +1,5 @@ -#pragma once +#pragma once #include #include #include @@ -15,19 +15,15 @@ #include "adam.hpp" #include "easy.hpp" // MIT License - // Copyright (c) [2024] Sermet Pekin - // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: - // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. - // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -37,7 +33,6 @@ // THE SOFTWARE. // */ using namespace microgradCpp; - // Function to split the DataFrame into train and test sets // inline void train_test_split(DataFrame &df, double train_size, // std::vector> &train_inputs, @@ -47,25 +42,20 @@ using namespace microgradCpp; // { // size_t total_rows = 150; // TODO // size_t train_rows = static_cast(total_rows * train_size); - // DataFrame train_df = df.rows(Range(0, train_rows)); // DataFrame test_df = df.rows(Range(train_rows, total_rows)); - // // Assuming inputs are all columns except the last one, and targets are in the last column // // train_inputs = train_df.get_inputs(); // Extract inputs from train DataFrame // // train_targets = train_df.get_targets(); // Extract targets from train DataFrame - // // test_inputs = test_df.get_inputs(); // Extract inputs from test DataFrame // // test_targets = test_df.get_targets(); // Extract targets from test DataFrame // } - inline std::vector> one_hot_encodeLocal(int class_index, int num_classes) { std::vector> target(num_classes, std::make_shared(0.0)); target[class_index] = std::make_shared(1.0); return target; } - inline void shuffleLocal(DatasetType &dataset) { std::random_device rd; @@ -73,32 +63,26 @@ inline void shuffleLocal(DatasetType &dataset) // gen.seed(42); // A fixed seed for reproducibility std::shuffle(dataset.begin(), dataset.end(), gen); } - // The updated train_eval function -inline void train_test_split( const DataFrame &df, - double TRAIN_SIZE, - ColRows &train_inputs, - ColRows &train_targets, - ColRows &test_inputs, - ColRows &test_targets) +inline void train_test_split(const DataFrame &df, + double TRAIN_SIZE, + ColRows &train_inputs, + ColRows &train_targets, + ColRows &test_inputs, + ColRows &test_targets, + int num_classes) // mlp.output_size() { - // size_t train_size = static_cast(df.size() * TRAIN_SIZE); - DatasetType dataset; - dataset = convert_to_dataset(df); // shuffleLocal(dataset); - size_t train_size = static_cast(dataset.size() * TRAIN_SIZE); - std::cout << "train_size : " << train_size; // stop(); - int num_classes = 3; + // int num_classes = 10; // TODO for (size_t i = 0; i < train_size; ++i) { train_inputs.push_back(dataset[i].first); - // train_targets.push_back(dataset[i].second); int class_index = static_cast(dataset[i].second[0]->data); train_targets.push_back(one_hot_encodeLocal(class_index, num_classes)); @@ -107,7 +91,6 @@ inline void train_test_split( const DataFrame &df, { test_inputs.push_back(dataset[i].first); // test_targets.push_back(dataset[i].second); - int class_index = static_cast(dataset[i].second[0]->data); test_targets.push_back(one_hot_encodeLocal(class_index, num_classes)); } @@ -131,57 +114,41 @@ inline void print(const ColRows &colrows) std::cout << "\n"; } } - // std::vector> one_hot_encode(int class_index, int num_classes) // { - // std::vector> target(num_classes, std::make_shared(0.0)); - // target[class_index] = std::make_shared(1.0); - // return target; // } - inline void train_eval(DatasetType &dataset, double TRAIN_SIZE, MLP &model, AdamOptimizer &optimizer, int epochs = 100) { - // Split into train and test sets (80-20 split) ColRows train_inputs, train_targets; ColRows test_inputs, test_targets; - train_test_split(dataset, TRAIN_SIZE, train_inputs, train_targets, test_inputs, test_targets); - // Create SGD optimizer with a learning rate of 0.005 // SGD optimizer(lr); - double best_loss = std::numeric_limits::infinity(); int patience = 150, patience_counter = 0; - // int epochs = 100; for (int epoch = 0; epoch < epochs; ++epoch) { double total_loss = 0.0; - // Training loop for (size_t i = 0; i < train_inputs.size(); ++i) { // Forward pass (training=true to possibly enable dropout or other training-specific behavior in MLP) auto predictions = model.forward(train_inputs[i], true); - // Compute Cross-Entropy Loss auto loss = Loss::cross_entropy(predictions, train_targets[i]); total_loss += loss->data; - // Backpropagation optimizer.zero_grad(); loss->backward(); - // Update weights optimizer.step(); } - - // std::cout << "Epoch " << epoch + 1 << "/" << epochs << ", Loss: " << total_loss / train_inputs.size() << std::endl; - + // std::cout << "Epoch " << epoch + 1 << "/" << epochs << ", Loss: " << total_loss / train_inputs.size() << std::endl; // Evaluate test accuracy every 10 epochs and on the last epoch if (epoch % 10 == 0 || epoch == epochs - 1) { @@ -190,7 +157,6 @@ inline void train_eval(DatasetType &dataset, double TRAIN_SIZE, MLP &model, Adam { // Forward pass in evaluation mode (e.g., no dropout) auto predictions = model.forward(test_inputs[i], false); - // Find predicted class (the index with max value) int predicted_class = 0; double max_value = predictions[0]->data; @@ -202,7 +168,6 @@ inline void train_eval(DatasetType &dataset, double TRAIN_SIZE, MLP &model, Adam predicted_class = static_cast(j); } } - // Check if prediction matches the target for (size_t j = 0; j < test_targets[i].size(); ++j) { @@ -213,52 +178,39 @@ inline void train_eval(DatasetType &dataset, double TRAIN_SIZE, MLP &model, Adam } } } - double accuracy = static_cast(correct) / test_inputs.size(); std::cout << "Epoch " << epoch + 1 << ": Test Accuracy = " << accuracy * 100.0 << "%" << std::endl; } } } - inline void train_eval(DataFrame &df, double TRAIN_SIZE, MLP &model, AdamOptimizer &optimizer, int epochs = 100) { - ColRows train_inputs, train_targets; ColRows test_inputs, test_targets; - - train_test_split(df, TRAIN_SIZE, train_inputs, train_targets, test_inputs, test_targets); - + train_test_split(df, TRAIN_SIZE, train_inputs, train_targets, test_inputs, test_targets, model.output_size()); // Create SGD optimizer // SGD optimizer(lr); - double best_loss = std::numeric_limits::infinity(); int patience = 150, patience_counter = 0; - // int epochs = 100; for (int epoch = 0; epoch < epochs; ++epoch) { double total_loss = 0.0; - // Training loop for (size_t i = 0; i < train_inputs.size(); ++i) { // Forward pass (training=true to possibly enable dropout or other training-specific behavior in MLP) auto predictions = model.forward(train_inputs[i], true); - // Compute Cross-Entropy Loss auto loss = Loss::cross_entropy(predictions, train_targets[i]); total_loss += loss->data; - // Backpropagation optimizer.zero_grad(); loss->backward(); - // Update weights optimizer.step(); } - - // std::cout << "Epoch " << epoch + 1 << "/" << epochs << ", Loss: " << total_loss / train_inputs.size() << std::endl; - + // std::cout << "Epoch " << epoch + 1 << "/" << epochs << ", Loss: " << total_loss / train_inputs.size() << std::endl; // Evaluate test accuracy every 10 epochs and on the last epoch if (epoch % 10 == 0 || epoch == epochs - 1) { @@ -267,7 +219,6 @@ inline void train_eval(DataFrame &df, double TRAIN_SIZE, MLP &model, AdamOptimiz { // Forward pass in evaluation mode (e.g., no dropout) auto predictions = model.forward(test_inputs[i], false); - // Find predicted class (the index with max value) int predicted_class = 0; double max_value = predictions[0]->data; @@ -279,7 +230,6 @@ inline void train_eval(DataFrame &df, double TRAIN_SIZE, MLP &model, AdamOptimiz predicted_class = static_cast(j); } } - // Check if prediction matches the target for (size_t j = 0; j < test_targets[i].size(); ++j) { @@ -290,86 +240,65 @@ inline void train_eval(DataFrame &df, double TRAIN_SIZE, MLP &model, AdamOptimiz } } } - double accuracy = static_cast(correct) / test_inputs.size(); std::cout << "Epoch " << epoch + 1 << ": Test Accuracy = " << accuracy * 100.0 << "%" << std::endl; } } } - /*======================== train_eval DataFrame =================================================*/ inline void train_eval(const DataFrame &df, double train_size, MLP &model, double lr = 0.01, int epochs = 100) { - ColRows train_inputs, train_targets; ColRows test_inputs, test_targets; - - train_test_split(df, train_size, train_inputs, train_targets, test_inputs, test_targets); - + train_test_split(df, train_size, train_inputs, train_targets, test_inputs, test_targets, model.output_size()); // Create SGD optimizer SGD optimizer(lr); - auto start = std::chrono::high_resolution_clock::now(); - v_string Dummy = {"aa", "bb"}; display_data(train_inputs, train_targets, Dummy); std::cout << "=================================\n"; display_data(test_inputs, test_targets, Dummy); // stop("..."); - // Training loop for (int epoch = 0; epoch < epochs; ++epoch) { double total_loss = 0.0; - size_t NUM_Training = train_inputs.size(); - for (size_t i = 0; i < NUM_Training; ++i) { - // Forward pass (training=true) auto predictions = model.forward(train_inputs[i], true); - // Compute Cross-Entropy Loss auto loss = Loss::cross_entropy(predictions, train_targets[i]); total_loss += loss->data; - // Backpropagation optimizer.zero_grad(model.parameters()); loss->backward(); - // Update weights optimizer.step(model.parameters()); } - // Evaluate test accuracy every 10 epochs and on the last epoch if (epoch % 10 == 0 || epoch == epochs - 1) { int correct = 0; - size_t NUM_Test = test_inputs.size(); - for (size_t i = 0; i < NUM_Test; ++i) { auto predictions = model.forward(test_inputs[i], false); - // Find predicted class int predicted_class = std::distance(predictions.begin(), std::max_element(predictions.begin(), predictions.end())); - // Check if prediction matches the target // if (test_targets[i][predicted_class]->data == 1.0) // if (test_targets[i][predicted_class]->data == 1.0) // { // correct++; // } - if (predicted_class >= 0 && predicted_class < test_targets[i].size()) { // if (test_targets[i][predicted_class]->data == 1.0) // { // correct++; // } - constexpr double EPSILON = 1e-6; if (std::abs(test_targets[i][predicted_class]->data - 1.0) < EPSILON) { @@ -377,10 +306,8 @@ inline void train_eval(const DataFrame &df, double train_size, MLP &model, doubl } } } - double accuracy = static_cast(correct) / test_inputs.size(); std::cout << "Epoch " << epoch + 1 << ": Test Accuracy = " << accuracy * 100.0 << "%" << std::endl; - if (epoch == epochs - 1) { auto end = std::chrono::high_resolution_clock::now(); @@ -389,4 +316,4 @@ inline void train_eval(const DataFrame &df, double train_size, MLP &model, doubl } } } -} +} \ No newline at end of file diff --git a/include/types.hpp b/include/types.hpp index 49774bf..b16774b 100644 --- a/include/types.hpp +++ b/include/types.hpp @@ -1,21 +1,17 @@ + #ifndef TYPES_HPP #define TYPES_HPP - /* MIT License - Copyright (c) [2024] Sermet Pekin - Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -31,27 +27,17 @@ THE SOFTWARE. #include #include #include "value.hpp" - namespace microgradCpp { - using v_string = std::vector; using vv_string = std::vector>; - using v_double = std::vector; using vv_double = std::vector>; - using ColumnData = std::variant, std::vector>; - using vv_shared_Value = std::vector>>; using v_shared_Value = std::vector>; - using DatasetType = std::vector>, std::vector>>>; using ColRows = std::vector>>; - - - // microgradCpp namespace } - -#endif // TYPES_HPP +#endif // TYPES_HPP \ No newline at end of file diff --git a/include/value.hpp b/include/value.hpp index fee5f35..87ec3b7 100644 --- a/include/value.hpp +++ b/include/value.hpp @@ -1,20 +1,17 @@ + #ifndef VALUE_HPP #define VALUE_HPP /* MIT License - Copyright (c) [2024] Sermet Pekin - Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -23,7 +20,6 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ - #include #include #include @@ -31,9 +27,7 @@ THE SOFTWARE. #include #include #include - #include "exceptions.hpp" - class Value : public std::enable_shared_from_this { public: @@ -43,10 +37,8 @@ class Value : public std::enable_shared_from_this bool cache_topology; std::vector> parents; std::function _backward; // Backward function for autograd - mutable std::vector> topo_cache; mutable bool topo_cached = false; - // Constructor Value(double data, const std::string &label = "", bool cache_topology = true) : data(data), @@ -54,13 +46,11 @@ class Value : public std::enable_shared_from_this label(label), _backward([]() {}), cache_topology(cache_topology) {} - // Copy constructor Value(const Value &other) : data(other.data), grad(other.grad), label(other.label), parents(other.parents), _backward(other._backward), cache_topology(other.cache_topology) {} - // Add a parent to the computational graph (ensuring no duplicates) void add_parent(const std::shared_ptr &parent) { @@ -74,13 +64,11 @@ class Value : public std::enable_shared_from_this { return data == val; } - // Friend function for double == Value friend bool operator==(double val, const Value &v) { return val == v.data; } - // Build topological order for backpropagation (with caching) std::vector> build_topological_order() { @@ -88,17 +76,14 @@ class Value : public std::enable_shared_from_this { return topo_cache; } - topo_cache.clear(); std::unordered_set visited; - std::function visit = [&](Value *v) { if (!v) { throw std::runtime_error("Null pointer encountered in build_topological_order"); } - if (v && visited.find(v) == visited.end()) { visited.insert(v); @@ -109,25 +94,21 @@ class Value : public std::enable_shared_from_this topo_cache.push_back(v->shared_from_this()); } }; - visit(this); topo_cached = true; return topo_cache; } - // Method to reset the cache manually (if needed) void reset_topo_cache() { topo_cached = false; topo_cache.clear(); } - // Build topological order for backpropagation // std::vector> build_topological_order() // { // std::vector> topo; // std::unordered_set visited; - // std::function visit = [&](Value *v) // { // if (v && visited.find(v) == visited.end()) @@ -140,11 +121,9 @@ class Value : public std::enable_shared_from_this // topo.push_back(v->shared_from_this()); // } // }; - // visit(this); // return topo; // } - // Backward propagation void backward(double grad_init = 1.0) { @@ -155,9 +134,7 @@ class Value : public std::enable_shared_from_this (*it)->_backward(); } } - // Unary operations - // Logarithm std::shared_ptr log() { @@ -169,13 +146,10 @@ class Value : public std::enable_shared_from_this }; return out; } - // Exponent std::shared_ptr exp() { - // throw NotImplementedException(); - auto out = std::make_shared(std::exp(data)); out->add_parent(shared_from_this()); out->_backward = [self = shared_from_this(), out]() @@ -184,7 +158,6 @@ class Value : public std::enable_shared_from_this }; return out; } - // Power with a double exponent std::shared_ptr pow(double exponent) { @@ -199,7 +172,6 @@ class Value : public std::enable_shared_from_this }; return out; } - // Debug print friend std::ostream &operator<<(std::ostream &os, const Value &v) { @@ -209,10 +181,8 @@ class Value : public std::enable_shared_from_this os << ", parents=" << v.parents.size() << ")"; return os; } - // Value Class constructor }; - // Operator Overloads (assuming both operands are std::shared_ptr) // ======================================================================== // Addition Templated Functions @@ -230,7 +200,6 @@ inline std::shared_ptr operator+(const std::shared_ptr &lhs, const }; return out; } - // +[V , T] template std::shared_ptr operator+(const std::shared_ptr &lhs, const T &rhs) @@ -240,7 +209,6 @@ std::shared_ptr operator+(const std::shared_ptr &lhs, const T &rhs { rhs_data = rhs->data; // If rhs is a std::shared_ptr, extract the data } - auto out = std::make_shared(lhs->data + rhs_data); out->add_parent(lhs); if constexpr (std::is_same_v>) @@ -257,14 +225,12 @@ std::shared_ptr operator+(const std::shared_ptr &lhs, const T &rhs }; return out; } - // +[T , V] template std::shared_ptr operator+(const T &lhs, const std::shared_ptr &rhs) { return rhs + lhs; // Reuse the above operator to avoid duplication } - // ======================================================================== // Subtraction for two std::shared_ptr operands // ======================================================================== @@ -281,7 +247,6 @@ inline std::shared_ptr operator-(const std::shared_ptr &lhs, const }; return out; } - // -[V , T] template std::shared_ptr operator-(const std::shared_ptr &lhs, const T &rhs) @@ -291,7 +256,6 @@ std::shared_ptr operator-(const std::shared_ptr &lhs, const T &rhs { rhs_data = rhs->data; // If rhs is a std::shared_ptr, extract the data } - auto out = std::make_shared(lhs->data - rhs_data); out->add_parent(lhs); if constexpr (std::is_same_v>) @@ -308,26 +272,22 @@ std::shared_ptr operator-(const std::shared_ptr &lhs, const T &rhs }; return out; } - // -[T , V] template std::shared_ptr operator-(const T &lhs, const std::shared_ptr &rhs) { return rhs - lhs; // Reuse the above operator to avoid duplication } - // ======================================================================== // Multiplication // ======================================================================== // *[V , V] inline std::shared_ptr operator*(const std::shared_ptr &lhs, const std::shared_ptr &rhs) { - if (!lhs || !rhs) { throw std::runtime_error("Null pointer in operator*"); } - auto out = std::make_shared(lhs->data * rhs->data); out->add_parent(lhs); out->add_parent(rhs); @@ -338,7 +298,6 @@ inline std::shared_ptr operator*(const std::shared_ptr &lhs, const }; return out; } - // *[V , T] template std::shared_ptr operator*(const std::shared_ptr &lhs, const T &rhs) @@ -348,14 +307,12 @@ std::shared_ptr operator*(const std::shared_ptr &lhs, const T &rhs { rhs_data = rhs->data; // If rhs is a std::shared_ptr, extract the data } - auto out = std::make_shared(lhs->data * rhs_data); out->add_parent(lhs); if constexpr (std::is_same_v>) { out->add_parent(rhs); // If rhs is a std::shared_ptr, add it as a parent } - out->_backward = [lhs, rhs, out]() { // Capture rhs_data explicitly lhs->grad += rhs->data * out->grad; // Use rhs_data here safely if constexpr (std::is_same_v>) @@ -363,10 +320,8 @@ std::shared_ptr operator*(const std::shared_ptr &lhs, const T &rhs rhs->grad += lhs->data * out->grad; } }; - return out; } - // *[T , V] template std::shared_ptr operator*(const T &lhs, const std::shared_ptr &rhs) @@ -384,7 +339,6 @@ std::shared_ptr operator*(const T &lhs, const std::shared_ptr &rhs // }; // return out; // } - // ======================================================================== // Division Templated Functions // ======================================================================== @@ -401,7 +355,6 @@ inline std::shared_ptr operator/(const std::shared_ptr &lhs, const }; return out; } - // /[V , T] template std::shared_ptr operator/(const std::shared_ptr &lhs, const T &rhs) @@ -415,10 +368,8 @@ std::shared_ptr operator/(const std::shared_ptr &lhs, const T &rhs { rhs_value = rhs; } - return lhs / rhs_value; } - // /[T , V] template std::shared_ptr operator/(const T &lhs, const std::shared_ptr &rhs) @@ -426,7 +377,6 @@ std::shared_ptr operator/(const T &lhs, const std::shared_ptr &rhs std::shared_ptr lhs_value = std::make_shared(lhs); return rhs / lhs_value; // Reuse the previous operator } - // Division // std::shared_ptr operator/(const std::shared_ptr& lhs, const std::shared_ptr& rhs) { // auto out = std::make_shared(lhs->data / rhs->data); @@ -438,7 +388,6 @@ std::shared_ptr operator/(const T &lhs, const std::shared_ptr &rhs // }; // return out; // } - // Unary Negation inline std::shared_ptr operator-(const std::shared_ptr &lhs) { @@ -450,5 +399,4 @@ inline std::shared_ptr operator-(const std::shared_ptr &lhs) }; return out; } - #endif // VALUE_HPP \ No newline at end of file