Skip to content
Snippets Groups Projects
ErrorFunctions.cpp 17.7 KiB
Newer Older
//
// Created by martin on 7/15/18.
//

#include <vector>
#include <cmath>
#include <boost/random/mersenne_twister.hpp>
#include <boost/random/uniform_int_distribution.hpp>
#include "ErrorFunctions.h"
#include "exceptions.h"
#include "message.h"
    size_t ErrorFunction::get_dimension() {
        return this->dimension;
    }
    NeuralNetwork* ErrorFunction::get_network_instance() {
        return this->net;
    }
    void ErrorFunction::divide_data_train_test(double percent_test) {
        size_t ds_size = this->ds->get_n_elements();
        /* Store the full data set */
        this->ds_full = this->ds;
        /* Choose random subset of the DataSet for training and the remaining part for validation */
        boost::random::mt19937 gen;
        boost::random::uniform_int_distribution<> dist(0, ds_size - 1);
        size_t test_set_size = ceil(ds_size * percent_test);
        std::vector<unsigned int> test_indices;
        test_indices.reserve(test_set_size);
        for (unsigned int i = 0; i < test_set_size; i++) {
            test_indices.emplace_back(dist(gen));
        }
        std::sort(test_indices.begin(), test_indices.end(), std::greater<unsigned int>());
        std::vector<std::pair<std::vector<double>, std::vector<double>>> test_data, train_data;
        /* Copy all the data to train_data */
        for (auto e : *this->ds_full->get_data()) {
            train_data.emplace_back(e);
        }
        /* Move the testing data from train_data to test_data */
            test_data.emplace_back(train_data.at(ind));
            train_data.erase(train_data.begin() + ind);
        }
        /* Re-initialize data set for training */
        this->ds = new DataSet(&train_data, this->ds_full->get_normalization_strategy());
        /* Initialize test data */
        this->ds_test = new DataSet(&test_data, this->ds_full->get_normalization_strategy());
    }

    void ErrorFunction::return_full_data_set_for_training() {
            this->ds = this->ds_full;
    DataSet* ErrorFunction::get_dataset() {
        return this->ds;
    }

    DataSet* ErrorFunction::get_test_dataset() {
        return this->ds_test;
    }

    std::vector<double>* ErrorFunction::get_parameters() {
        std::vector<double>* output = new std::vector<double>(this->net->get_n_weights() + this->net->get_n_biases());

        size_t i = 0;

        for (auto el: *this->net->get_parameter_ptr_weights()) {
            output->at(i) = el;
            ++i;
        }

        for (auto el: *this->net->get_parameter_ptr_biases()) {
            output->at(i) = el;
            ++i;
        }

        return output;
    }

    MSE::MSE(NeuralNetwork* net, DataSet* ds) {
        this->net = net;
        this->ds = ds;
        this->dimension = net->get_n_weights() + net->get_n_biases();
    double MSE::eval_on_data_set(lib4neuro::DataSet* data_set, std::ofstream* results_file_path,
                                 std::vector<double>* weights) {
        //TODO do NOT duplicate code - rewrite the function in a better way

        size_t dim_in = data_set->get_input_dim();
        size_t dim_out = data_set->get_output_dim();
        std::vector<std::pair<std::vector<double>, std::vector<double>>>* data = data_set->get_data();
        //TODO instead use something smarter
        std::vector<std::vector<double>> outputs(data->size());
        COUT_DEBUG("Evaluation of the error function MSE on the given data-set (format 'data-set element index' 'input'"
                   " 'real output' 'predicted output' 'absolute error' 'relative error'):"
                   << std::endl);
        *results_file_path << "[Data-set element index] [Input] [Real output] [Predicted output] [Abs. error] [Rel. error]" << std::endl;

        for (auto i = 0; i < data->size(); i++) {  // Iterate through every element in the test set
            /* Compute the net output and store it into 'output' variable */
            this->net->eval_single(data->at(i).first,
        bool denormalize_output = false;
        if(data_set->is_normalized()) {
            data_set->de_normalize();
        }

        for (auto i = 0; i < data->size(); i++) {
            /* Compute difference for every element of the output vector */
#ifdef L4N_DEBUG
            std::stringstream ss_input;
            for(auto j = 0; j < dim_in-1; j++) {
                ss_input << data->at(i).first.at(j) << ",";
            }
            ss_input << data->at(i).first.back();

            std::stringstream ss_real_output;
            std::stringstream ss_predicted_output;
#endif
            double denormalized_output;
            for (size_t j = 0; j < dim_out; ++j) {
                if(denormalize_output) {
                    denormalized_output = data_set->get_normalization_strategy()->de_normalize(outputs.at(i).at(j));
                } else {
                    denormalized_output = outputs.at(i).at(j);
                }

#ifdef L4N_DEBUG
                ss_real_output << data->at(i).second.at(j);
                ss_predicted_output << denormalized_output;
                val = denormalized_output - data->at(i).second.at(j);
                output_norm += denormalized_output * denormalized_output;
            COUT_DEBUG(i << ": "
                         << ss_input.str() << " ; "
                         << ss_real_output.str() << " ; "
                         << ss_predicted_output.str() << "; "
                         << std::sqrt(error) << "; "
                         << 2 * std::sqrt(error) / (std::sqrt(error) + std::sqrt(output_norm)) <<
                         std::endl);
            *results_file_path << i << ": "
                               << ss_input.str() << " ; "
                               << ss_real_output.str() << " ; "
                               << ss_predicted_output.str() << "; "
                               << std::sqrt(error) << "; "
                               << 2 * std::sqrt(error) / (std::sqrt(error) + std::sqrt(output_norm)) <<
                               std::endl;
#endif
        double result = std::sqrt(error) / n_elements;
        *results_file_path << "MSE = " << result << std::endl;
        return result;
    }

    double MSE::eval_on_data_set(DataSet* data_set, std::string results_file_path, std::vector<double>* weights) {
        //TODO do NOT duplicate code - rewrite the function in a better way

        size_t dim_out = data_set->get_output_dim();
        size_t n_elements = data_set->get_n_elements();
        double error = 0.0, val;

        std::vector<std::pair<std::vector<double>, std::vector<double>>>* data = data_set->get_data();

        //TODO instead use something smarter
        std::vector<std::vector<double>> outputs(data->size());
        std::vector<double> output(dim_out);

        COUT_DEBUG(
                "Evaluation of the error function MSE on the given data-set (format 'input' 'real output' 'predicted output'):"
                        << std::endl);

        std::ofstream ofs(results_file_path);
        if (!ofs.is_open()) {
            THROW_RUNTIME_ERROR("File path: " + results_file_path + " was not successfully opened!");
        }

        ofs << "[Input] [Real output] [Predicted output]" << std::endl;

        for (auto i = 0; i < data->size(); i++) {  // Iterate through every element in the test set

            /* Compute the net output and store it into 'output' variable */
            this->net->eval_single(data->at(i).first,
                                   output,
                                   weights);

        bool denormalize_output = false;
        if(data_set->is_normalized()) {
            data_set->de_normalize();
        }

        for(auto i = 0; i < data->size(); i++) {
            /* Compute difference for every element of the output vector */
            double denormalized_output;
            for (size_t j = 0; j < dim_out; ++j) {
                if(denormalize_output) {
                    denormalized_output = data_set->get_normalization_strategy()->de_normalize(outputs.at(i).at(j));
                } else {
                    denormalized_output = outputs.at(i).at(j);
                }
                COUT_DEBUG("Element " << i << ": "
                                      << data->at(i).first.at(j) << " "
                                      << data->at(i).second.at(j) << " "
                                      << denormalized_output << std::endl);

                ofs << data->at(i).first.at(j) << " "
                    << data->at(i).second.at(j) << " "
                    << denormalized_output << std::endl;
                val = denormalized_output - data->at(i).second.at(j);
    double MSE::eval_on_data_set(DataSet* data_set, std::vector<double>* weights) {
        size_t dim_out = data_set->get_output_dim();
        size_t n_elements = data_set->get_n_elements();
        double error = 0.0, val;

        std::vector<std::pair<std::vector<double>, std::vector<double>>>* data = data_set->get_data();

        //TODO instead use something smarter
        std::vector<std::vector<double>> outputs(data->size());
        std::vector<double> output(dim_out);

        COUT_DEBUG(
                "Evaluation of the error function MSE on the given data-set (format 'input' 'real output' 'predicted output'):"
                        << std::endl);

        /* Compute predicted outputs */
        for (auto i = 0; i < data->size(); i++) {  // Iterate through every element in the test set

            /* Compute the net output and store it into 'output' variable */
            this->net->eval_single(data->at(i).first,
                                   output,
                                   weights);

            outputs.at(i) = output;
        }

        /* De-normalize data-set, if it's normalized */
        bool denormalize_output = false;
        if(data_set->is_normalized()) {
            data_set->de_normalize();
        }

        /* Evaluate the prediction error on de-normalized data */
        for(auto i = 0; i < data->size(); i++) {

            /* Compute difference for every element of the output vector */
            double denormalized_output;
            for (auto j = 0; j < dim_out; ++j) {
                if(denormalize_output) {
                    denormalized_output = data_set->get_normalization_strategy()->de_normalize(outputs.at(i).at(j));
                } else {
                    denormalized_output = outputs.at(i).at(j);
                }
                COUT_DEBUG("Element " << i << ": "
                                      << data->at(i).first.at(j) << " "
                                      << data->at(i).second.at(j) << " "
                                      << denormalized_output << std::endl);
                val = denormalized_output - data->at(i).second.at(j);
        return error / n_elements;
    }

    double MSE::eval(std::vector<double>* weights) {
        size_t dim_out = this->ds->get_output_dim();
        size_t n_elements = this->ds->get_n_elements();
        double error = 0.0, val;
        std::vector<std::pair<std::vector<double>, std::vector<double>>>* data = this->ds->get_data();
Michal Kravcenko's avatar
Michal Kravcenko committed
//    //TODO instead use something smarter
//    this->net->copy_weights(weights);
        std::vector<double> output(dim_out);
        for (auto el: *data) {  // Iterate through every element in the test set
            this->net->eval_single(el.first, output,
                                   weights);  // Compute the net output and store it into 'output' variable
            for (size_t j = 0; j < dim_out; ++j) {  // Compute difference for every element of the output vector
                error += val * val;
            }
        }
        return error / n_elements;
    double MSE::eval_on_test_data(std::vector<double>* weights) {
        return this->eval_on_data_set(this->ds_test, weights);
    double MSE::eval_on_test_data(std::string results_file_path, std::vector<double>* weights) {
        return this->eval_on_data_set(this->ds_test, results_file_path, weights);
    }

    double MSE::eval_on_test_data(std::ofstream* results_file_path, std::vector<double>* weights) {
        return this->eval_on_data_set(this->ds_test, results_file_path, weights);
    }
    void
    MSE::calculate_error_gradient(std::vector<double>& params, std::vector<double>& grad, double alpha, size_t batch) {
        size_t dim_out = this->ds->get_output_dim();
        size_t n_elements = this->ds->get_n_elements();
        std::vector<std::pair<std::vector<double>, std::vector<double>>>* data = this->ds->get_data();
            *data = this->ds->get_random_data_batch(batch);
            n_elements = data->size();
        }
        std::vector<double> error_derivative(dim_out);
        for (auto el: *data) {  // Iterate through every element in the test set
            this->net->eval_single(el.first, error_derivative,
                                   &params);  // Compute the net output and store it into 'output' variable
            for (size_t j = 0; j < dim_out; ++j) {
                error_derivative[j] = 2.0 * (error_derivative[j] - el.second[j]); //real - expected result
            }
            this->net->add_to_gradient_single(el.first, error_derivative, alpha / n_elements, grad);
        }
    ErrorSum::ErrorSum() {
        this->summand = nullptr;
        this->summand_coefficient = nullptr;
        this->dimension = 0;
    }
    ErrorSum::~ErrorSum() {
        if (this->summand) {
            delete this->summand;
        }
        if (this->summand_coefficient) {
            delete this->summand_coefficient;
    double ErrorSum::eval_on_test_data(std::vector<double>* weights) {
        //TODO take care of the case, when there are no test data
        double output = 0.0;
        for (unsigned int i = 0; i < this->summand->size(); ++i) {
            ef = this->summand->at(i);
            if (ef) {
                output += ef->eval_on_test_data(weights) * this->summand_coefficient->at(i);
            }
    double ErrorSum::eval_on_test_data(std::string results_file_path, std::vector<double>* weights) {
        THROW_NOT_IMPLEMENTED_ERROR();

        return -1;
    }

    double ErrorSum::eval_on_test_data(std::ofstream* results_file_path, std::vector<double>* weights) {
        THROW_NOT_IMPLEMENTED_ERROR();
        return -1;
    }

    double ErrorSum::eval_on_data_set(lib4neuro::DataSet* data_set, std::vector<double>* weights) {
        THROW_NOT_IMPLEMENTED_ERROR();

        return -1;
    }

    double ErrorSum::eval_on_data_set(lib4neuro::DataSet* data_set, std::string results_file_path,
                                      std::vector<double>* weights) {
        THROW_NOT_IMPLEMENTED_ERROR();

        return -1;
    }

    double ErrorSum::eval_on_data_set(lib4neuro::DataSet* data_set, std::ofstream* results_file_path,
                                      std::vector<double>* weights) {
        THROW_NOT_IMPLEMENTED_ERROR();
        return -1;
    }

    double ErrorSum::eval(std::vector<double>* weights) {
        for (unsigned int i = 0; i < this->summand->size(); ++i) {
            ef = this->summand->at(i);
            if (ef) {
                output += ef->eval(weights) * this->summand_coefficient->at(i);
            }
    void ErrorSum::calculate_error_gradient(std::vector<double>& params, std::vector<double>& grad, double alpha,
                                            size_t batch) {
        for (size_t i = 0; i < this->summand->size(); ++i) {
            ef = this->summand->at(i);
            if (ef) {
                ef->calculate_error_gradient(params, grad, this->summand_coefficient->at(i) * alpha, batch);
    void ErrorSum::add_error_function(ErrorFunction* F, double alpha) {
            this->summand = new std::vector<ErrorFunction*>(0);
        }
        this->summand->push_back(F);

        if (!this->summand_coefficient) {
            this->summand_coefficient = new std::vector<double>(0);
        }
        this->summand_coefficient->push_back(alpha);

        if (F) {
            if (F->get_dimension() > this->dimension) {
                this->dimension = F->get_dimension();
            }
Michal Kravcenko's avatar
Michal Kravcenko committed
    }
Michal Kravcenko's avatar
Michal Kravcenko committed
//    if(!this->dimension) {
//        size_t max = 0;
//        for(auto e : *this->summand) {
//            if(e->get_dimension() > max) {
//                max = e->get_dimension();
//            }
//        };
//
//        this->dimension = max;
//    }
    std::vector<double>* ErrorSum::get_parameters() {
        return this->summand->at(0)->get_parameters();
    }
    DataSet* ErrorSum::get_dataset() {
        return this->summand->at(0)->get_dataset();
    };