Something went wrong on our end
-
Martin Beseda authoredMartin Beseda authored
ErrorFunctions.cpp 15.10 KiB
//
// Created by martin on 7/15/18.
//
#include <vector>
#include <cmath>
#include <sstream>
#include <boost/random/mersenne_twister.hpp>
#include <boost/random/uniform_int_distribution.hpp>
#include "ErrorFunctions.h"
#include "exceptions.h"
#include "message.h"
namespace lib4neuro {
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 */
for (auto ind : test_indices) {
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() {
if (this->ds_test) {
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();
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<double> output(dim_out);
COUT_DEBUG("Evaluation of the error function MSE on the given data-set (format 'data-set element index' 'input'"
" 'real output' 'predicted output'):"
<< std::endl);
*results_file_path << "[Data-set element index] [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);
/* 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
for (size_t j = 0; j < dim_out; ++j) {
#ifdef L4N_DEBUG
ss_real_output << data->at(i).second.at(j);
ss_predicted_output << output.at(j);
#endif
val = output.at(j) - data->at(i).second.at(j);
error += val * val;
}
COUT_DEBUG(i << ": "
<< ss_input.str() << " ; "
<< ss_real_output.str() << " ; "
<< ss_predicted_output.str() << std::endl);
*results_file_path << i << ": "
<< ss_input.str() << " ; "
<< ss_real_output.str() << " ; "
<< ss_predicted_output.str() << std::endl;
}
double result = 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<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);
/* Compute difference for every element of the output vector */
for (size_t j = 0; j < dim_out; ++j) {
COUT_DEBUG("Element " << i << ": "
<< data->at(i).first.at(j) << " "
<< data->at(i).second.at(j) << " "
<< output.at(j) << std::endl);
ofs << data->at(i).first.at(j) << " "
<< data->at(i).second.at(j) << " "
<< output.at(j) << std::endl;
val = output.at(j) - data->at(i).second.at(j);
error += val * val;
}
ofs << std::endl;
}
ofs.close();
return error / n_elements;
}
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<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);
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);
/* Compute difference for every element of the output vector */
for (size_t j = 0; j < dim_out; ++j) {
COUT_DEBUG("Element " << i << ": "
<< data->at(i).first.at(j) << " "
<< data->at(i).second.at(j) << " "
<< output.at(j) << std::endl);
val = output.at(j) - data->at(i).second.at(j);
error += val * val;
}
}
return error / n_elements;
}
double MSE::eval(std::vector<double>* weights) {
size_t dim_out = this->ds->get_output_dim();
// unsigned int dim_in = this->ds->get_input_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();
// //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
val = output.at(j) - el.second.at(j);
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();
if (batch) {
*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,
¶ms); // 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;
ErrorFunction* ef = nullptr;
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);
}
}
return output;
}
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) {
double output = 0.0;
ErrorFunction* ef = nullptr;
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);
}
}
return output;
}
void ErrorSum::calculate_error_gradient(std::vector<double>& params, std::vector<double>& grad, double alpha,
size_t batch) {
ErrorFunction* ef = nullptr;
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) {
if (!this->summand) {
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();
}
}
}
size_t ErrorSum::get_dimension() {
// if(!this->dimension) {
// size_t max = 0;
// for(auto e : *this->summand) {
// if(e->get_dimension() > max) {
// max = e->get_dimension();
// }
// };
//
// this->dimension = max;
// }
return this->dimension;
}
std::vector<double>* ErrorSum::get_parameters() {
return this->summand->at(0)->get_parameters();
}
DataSet* ErrorSum::get_dataset() {
return this->summand->at(0)->get_dataset();
};
}