mirror of
https://github.com/Relintai/pmlpp.git
synced 2024-11-09 13:22:09 +01:00
1069 lines
33 KiB
C++
1069 lines
33 KiB
C++
//
|
|
// Data.cpp
|
|
// MLP
|
|
//
|
|
// Created by Marc Melikyan on 11/4/20.
|
|
//
|
|
|
|
#include "data.h"
|
|
|
|
#include "core/os/file_access.h"
|
|
|
|
#include "../lin_alg/lin_alg.h"
|
|
#include "../softmax_net/softmax_net.h"
|
|
#include "../stat/stat.h"
|
|
|
|
#include <algorithm>
|
|
#include <cmath>
|
|
#include <fstream>
|
|
#include <iostream>
|
|
#include <random>
|
|
#include <sstream>
|
|
|
|
void MLPPDataESimple::_bind_methods() {
|
|
}
|
|
|
|
void MLPPDataSimple::_bind_methods() {
|
|
}
|
|
|
|
void MLPPDataComplex::_bind_methods() {
|
|
}
|
|
|
|
// Loading Datasets
|
|
Ref<MLPPDataSimple> MLPPData::load_breast_cancer(const String &path) {
|
|
const int BREAST_CANCER_SIZE = 30; // k = 30
|
|
|
|
Ref<MLPPDataSimple> data;
|
|
data.instance();
|
|
|
|
set_data_supervised(BREAST_CANCER_SIZE, path, data->input, data->output);
|
|
|
|
return data;
|
|
}
|
|
|
|
Ref<MLPPDataSimple> MLPPData::load_breast_cancer_svc(const String &path) {
|
|
const int BREAST_CANCER_SIZE = 30; // k = 30
|
|
|
|
Ref<MLPPDataSimple> data;
|
|
data.instance();
|
|
|
|
set_data_supervised(BREAST_CANCER_SIZE, path, data->input, data->output);
|
|
|
|
return data;
|
|
}
|
|
|
|
Ref<MLPPDataComplex> MLPPData::load_iris(const String &path) {
|
|
const int IRIS_SIZE = 4;
|
|
const int ONE_HOT_NUM = 3;
|
|
|
|
std::vector<double> tempOutputSet;
|
|
|
|
Ref<MLPPDataComplex> data;
|
|
data.instance();
|
|
|
|
set_data_supervised(IRIS_SIZE, path, data->input, tempOutputSet);
|
|
data->output = oneHotRep(tempOutputSet, ONE_HOT_NUM);
|
|
|
|
return data;
|
|
}
|
|
|
|
Ref<MLPPDataComplex> MLPPData::load_wine(const String &path) {
|
|
const int WINE_SIZE = 4;
|
|
const int ONE_HOT_NUM = 3;
|
|
|
|
std::vector<double> tempOutputSet;
|
|
|
|
Ref<MLPPDataComplex> data;
|
|
data.instance();
|
|
|
|
set_data_supervised(WINE_SIZE, path, data->input, tempOutputSet);
|
|
data->output = oneHotRep(tempOutputSet, ONE_HOT_NUM);
|
|
|
|
return data;
|
|
}
|
|
|
|
Ref<MLPPDataComplex> MLPPData::load_mnist_train(const String &path) {
|
|
const int MNIST_SIZE = 784;
|
|
const int ONE_HOT_NUM = 10;
|
|
|
|
std::vector<std::vector<double>> inputSet;
|
|
std::vector<double> tempOutputSet;
|
|
|
|
Ref<MLPPDataComplex> data;
|
|
data.instance();
|
|
|
|
set_data_supervised(MNIST_SIZE, path, data->input, tempOutputSet);
|
|
data->output = oneHotRep(tempOutputSet, ONE_HOT_NUM);
|
|
|
|
return data;
|
|
}
|
|
|
|
Ref<MLPPDataComplex> MLPPData::load_mnist_test(const String &path) {
|
|
const int MNIST_SIZE = 784;
|
|
const int ONE_HOT_NUM = 10;
|
|
std::vector<std::vector<double>> inputSet;
|
|
std::vector<double> tempOutputSet;
|
|
|
|
Ref<MLPPDataComplex> data;
|
|
data.instance();
|
|
|
|
set_data_supervised(MNIST_SIZE, path, data->input, tempOutputSet);
|
|
data->output = oneHotRep(tempOutputSet, ONE_HOT_NUM);
|
|
|
|
return data;
|
|
}
|
|
|
|
Ref<MLPPDataSimple> MLPPData::load_california_housing(const String &path) {
|
|
const int CALIFORNIA_HOUSING_SIZE = 13; // k = 30
|
|
|
|
Ref<MLPPDataSimple> data;
|
|
data.instance();
|
|
|
|
set_data_supervised(CALIFORNIA_HOUSING_SIZE, path, data->input, data->output);
|
|
|
|
return data;
|
|
}
|
|
|
|
Ref<MLPPDataESimple> MLPPData::load_fires_and_crime(const String &path) {
|
|
// k is implicitly 1.
|
|
|
|
Ref<MLPPDataESimple> data;
|
|
data.instance();
|
|
|
|
set_data_simple(path, data->input, data->output);
|
|
|
|
return data;
|
|
}
|
|
|
|
// MULTIVARIATE SUPERVISED
|
|
|
|
void MLPPData::set_data_supervised(int k, const String &file_name, std::vector<std::vector<double>> &inputSet, std::vector<double> &outputSet) {
|
|
MLPPLinAlg alg;
|
|
|
|
inputSet.resize(k);
|
|
|
|
FileAccess *file = FileAccess::open(file_name, FileAccess::READ);
|
|
|
|
ERR_FAIL_COND(!file);
|
|
|
|
while (!file->eof_reached()) {
|
|
Vector<String> ll = file->get_csv_line();
|
|
|
|
for (int i = 0; i < k; ++i) {
|
|
inputSet[i].push_back(ll[i].to_double());
|
|
}
|
|
|
|
outputSet.push_back(ll[k].to_double());
|
|
}
|
|
|
|
inputSet = alg.transpose(inputSet);
|
|
|
|
memdelete(file);
|
|
}
|
|
|
|
void MLPPData::set_data_unsupervised(int k, const String &file_name, std::vector<std::vector<double>> &inputSet) {
|
|
MLPPLinAlg alg;
|
|
|
|
inputSet.resize(k);
|
|
|
|
FileAccess *file = FileAccess::open(file_name, FileAccess::READ);
|
|
|
|
ERR_FAIL_COND(!file);
|
|
|
|
while (!file->eof_reached()) {
|
|
Vector<String> ll = file->get_csv_line();
|
|
|
|
for (int i = 0; i < k; ++i) {
|
|
inputSet[i].push_back(ll[i].to_double());
|
|
}
|
|
}
|
|
|
|
inputSet = alg.transpose(inputSet);
|
|
|
|
memdelete(file);
|
|
}
|
|
|
|
void MLPPData::set_data_simple(const String &file_name, std::vector<double> &inputSet, std::vector<double> &outputSet) {
|
|
FileAccess *file = FileAccess::open(file_name, FileAccess::READ);
|
|
|
|
ERR_FAIL_COND(!file);
|
|
|
|
while (!file->eof_reached()) {
|
|
Vector<String> ll = file->get_csv_line();
|
|
|
|
for (int i = 0; i < ll.size(); i += 2) {
|
|
inputSet.push_back(ll[i].to_double());
|
|
outputSet.push_back(ll[i + 1].to_double());
|
|
}
|
|
}
|
|
|
|
memdelete(file);
|
|
}
|
|
|
|
MLPPData::SplitComplexData MLPPData::train_test_split(const Ref<MLPPDataComplex> &data, double test_size) {
|
|
SplitComplexData res;
|
|
|
|
res.train.instance();
|
|
res.test.instance();
|
|
|
|
ERR_FAIL_COND_V(!data.is_valid(), res);
|
|
|
|
int is = MIN(data->input.size(), data->output.size());
|
|
|
|
Array indices;
|
|
indices.resize(is);
|
|
|
|
for (int i = 0; i < is; ++i) {
|
|
indices[i] = i;
|
|
}
|
|
|
|
indices.shuffle();
|
|
|
|
int test_input_number = test_size * is; // implicit usage of floor
|
|
|
|
for (int i = 0; i < test_input_number; ++i) {
|
|
int index = indices[i];
|
|
|
|
res.test->input.push_back(data->input[i]);
|
|
res.test->output.push_back(data->output[i]);
|
|
}
|
|
|
|
for (int i = test_input_number; i < is; ++i) {
|
|
int index = indices[i];
|
|
|
|
res.train->input.push_back(data->input[i]);
|
|
res.train->output.push_back(data->output[i]);
|
|
}
|
|
|
|
return res;
|
|
}
|
|
Array MLPPData::train_test_split_bind(const Ref<MLPPDataComplex> &data, double test_size) {
|
|
SplitComplexData res = train_test_split(data, test_size);
|
|
|
|
Array arr;
|
|
arr.push_back(res.train);
|
|
arr.push_back(res.test);
|
|
|
|
return arr;
|
|
}
|
|
|
|
// Loading Datasets
|
|
std::tuple<std::vector<std::vector<double>>, std::vector<double>> MLPPData::loadBreastCancer() {
|
|
const int BREAST_CANCER_SIZE = 30; // k = 30
|
|
std::vector<std::vector<double>> inputSet;
|
|
std::vector<double> outputSet;
|
|
|
|
setData(BREAST_CANCER_SIZE, "MLPP/Data/Datasets/BreastCancer.csv", inputSet, outputSet);
|
|
return { inputSet, outputSet };
|
|
}
|
|
|
|
std::tuple<std::vector<std::vector<double>>, std::vector<double>> MLPPData::loadBreastCancerSVC() {
|
|
const int BREAST_CANCER_SIZE = 30; // k = 30
|
|
std::vector<std::vector<double>> inputSet;
|
|
std::vector<double> outputSet;
|
|
|
|
setData(BREAST_CANCER_SIZE, "MLPP/Data/Datasets/BreastCancerSVM.csv", inputSet, outputSet);
|
|
return { inputSet, outputSet };
|
|
}
|
|
|
|
std::tuple<std::vector<std::vector<double>>, std::vector<std::vector<double>>> MLPPData::loadIris() {
|
|
const int IRIS_SIZE = 4;
|
|
const int ONE_HOT_NUM = 3;
|
|
std::vector<std::vector<double>> inputSet;
|
|
std::vector<double> tempOutputSet;
|
|
|
|
setData(IRIS_SIZE, "/Users/marcmelikyan/Desktop/Data/Iris.csv", inputSet, tempOutputSet);
|
|
std::vector<std::vector<double>> outputSet = oneHotRep(tempOutputSet, ONE_HOT_NUM);
|
|
return { inputSet, outputSet };
|
|
}
|
|
|
|
std::tuple<std::vector<std::vector<double>>, std::vector<std::vector<double>>> MLPPData::loadWine() {
|
|
const int WINE_SIZE = 4;
|
|
const int ONE_HOT_NUM = 3;
|
|
std::vector<std::vector<double>> inputSet;
|
|
std::vector<double> tempOutputSet;
|
|
|
|
setData(WINE_SIZE, "MLPP/Data/Datasets/Iris.csv", inputSet, tempOutputSet);
|
|
std::vector<std::vector<double>> outputSet = oneHotRep(tempOutputSet, ONE_HOT_NUM);
|
|
return { inputSet, outputSet };
|
|
}
|
|
|
|
std::tuple<std::vector<std::vector<double>>, std::vector<std::vector<double>>> MLPPData::loadMnistTrain() {
|
|
const int MNIST_SIZE = 784;
|
|
const int ONE_HOT_NUM = 10;
|
|
std::vector<std::vector<double>> inputSet;
|
|
std::vector<double> tempOutputSet;
|
|
|
|
setData(MNIST_SIZE, "MLPP/Data/Datasets/MnistTrain.csv", inputSet, tempOutputSet);
|
|
std::vector<std::vector<double>> outputSet = oneHotRep(tempOutputSet, ONE_HOT_NUM);
|
|
return { inputSet, outputSet };
|
|
}
|
|
|
|
std::tuple<std::vector<std::vector<double>>, std::vector<std::vector<double>>> MLPPData::loadMnistTest() {
|
|
const int MNIST_SIZE = 784;
|
|
const int ONE_HOT_NUM = 10;
|
|
std::vector<std::vector<double>> inputSet;
|
|
std::vector<double> tempOutputSet;
|
|
|
|
setData(MNIST_SIZE, "MLPP/Data/Datasets/MnistTest.csv", inputSet, tempOutputSet);
|
|
std::vector<std::vector<double>> outputSet = oneHotRep(tempOutputSet, ONE_HOT_NUM);
|
|
return { inputSet, outputSet };
|
|
}
|
|
|
|
std::tuple<std::vector<std::vector<double>>, std::vector<double>> MLPPData::loadCaliforniaHousing() {
|
|
const int CALIFORNIA_HOUSING_SIZE = 13; // k = 30
|
|
std::vector<std::vector<double>> inputSet;
|
|
std::vector<double> outputSet;
|
|
|
|
setData(CALIFORNIA_HOUSING_SIZE, "MLPP/Data/Datasets/CaliforniaHousing.csv", inputSet, outputSet);
|
|
return { inputSet, outputSet };
|
|
}
|
|
|
|
std::tuple<std::vector<double>, std::vector<double>> MLPPData::loadFiresAndCrime() {
|
|
std::vector<double> inputSet; // k is implicitly 1.
|
|
std::vector<double> outputSet;
|
|
|
|
setData("MLPP/Data/Datasets/FiresAndCrime.csv", inputSet, outputSet);
|
|
return { inputSet, outputSet };
|
|
}
|
|
|
|
// Note that inputs and outputs should be pairs (technically), but this
|
|
// implementation will separate them. (My implementation keeps them tied together.)
|
|
// Not yet sure whether this is intentional or not (or it's something like a compiler specific difference)
|
|
std::tuple<std::vector<std::vector<double>>, std::vector<std::vector<double>>, std::vector<std::vector<double>>, std::vector<std::vector<double>>> MLPPData::trainTestSplit(std::vector<std::vector<double>> inputSet, std::vector<std::vector<double>> outputSet, double testSize) {
|
|
std::random_device rd;
|
|
std::default_random_engine generator(rd());
|
|
|
|
std::shuffle(inputSet.begin(), inputSet.end(), generator); // inputSet random shuffle
|
|
std::shuffle(outputSet.begin(), outputSet.end(), generator); // outputSet random shuffle)
|
|
|
|
std::vector<std::vector<double>> inputTestSet;
|
|
std::vector<std::vector<double>> outputTestSet;
|
|
|
|
int testInputNumber = testSize * inputSet.size(); // implicit usage of floor
|
|
int testOutputNumber = testSize * outputSet.size(); // implicit usage of floor
|
|
|
|
for (int i = 0; i < testInputNumber; i++) {
|
|
inputTestSet.push_back(inputSet[i]);
|
|
inputSet.erase(inputSet.begin());
|
|
}
|
|
|
|
for (int i = 0; i < testOutputNumber; i++) {
|
|
outputTestSet.push_back(outputSet[i]);
|
|
outputSet.erase(outputSet.begin());
|
|
}
|
|
|
|
return { inputSet, outputSet, inputTestSet, outputTestSet };
|
|
}
|
|
|
|
// MULTIVARIATE SUPERVISED
|
|
|
|
void MLPPData::setData(int k, std::string fileName, std::vector<std::vector<double>> &inputSet, std::vector<double> &outputSet) {
|
|
MLPPLinAlg alg;
|
|
std::string inputTemp;
|
|
std::string outputTemp;
|
|
|
|
inputSet.resize(k);
|
|
|
|
std::ifstream dataFile(fileName);
|
|
if (!dataFile.is_open()) {
|
|
std::cout << fileName << " failed to open." << std::endl;
|
|
}
|
|
|
|
std::string line;
|
|
while (std::getline(dataFile, line)) {
|
|
std::stringstream ss(line);
|
|
|
|
for (int i = 0; i < k; i++) {
|
|
std::getline(ss, inputTemp, ',');
|
|
inputSet[i].push_back(std::stod(inputTemp));
|
|
}
|
|
|
|
std::getline(ss, outputTemp, ',');
|
|
outputSet.push_back(std::stod(outputTemp));
|
|
}
|
|
inputSet = alg.transpose(inputSet);
|
|
dataFile.close();
|
|
}
|
|
|
|
void MLPPData::printData(std::vector<std::string> inputName, std::string outputName, std::vector<std::vector<double>> inputSet, std::vector<double> outputSet) {
|
|
MLPPLinAlg alg;
|
|
inputSet = alg.transpose(inputSet);
|
|
for (int i = 0; i < inputSet.size(); i++) {
|
|
std::cout << inputName[i] << std::endl;
|
|
for (int j = 0; j < inputSet[i].size(); j++) {
|
|
std::cout << inputSet[i][j] << std::endl;
|
|
}
|
|
}
|
|
|
|
std::cout << outputName << std::endl;
|
|
for (int i = 0; i < outputSet.size(); i++) {
|
|
std::cout << outputSet[i] << std::endl;
|
|
}
|
|
}
|
|
|
|
// UNSUPERVISED
|
|
|
|
void MLPPData::setData(int k, std::string fileName, std::vector<std::vector<double>> &inputSet) {
|
|
MLPPLinAlg alg;
|
|
std::string inputTemp;
|
|
|
|
inputSet.resize(k);
|
|
|
|
std::ifstream dataFile(fileName);
|
|
if (!dataFile.is_open()) {
|
|
std::cout << fileName << " failed to open." << std::endl;
|
|
}
|
|
|
|
std::string line;
|
|
while (std::getline(dataFile, line)) {
|
|
std::stringstream ss(line);
|
|
|
|
for (int i = 0; i < k; i++) {
|
|
std::getline(ss, inputTemp, ',');
|
|
inputSet[i].push_back(std::stod(inputTemp));
|
|
}
|
|
}
|
|
inputSet = alg.transpose(inputSet);
|
|
dataFile.close();
|
|
}
|
|
|
|
void MLPPData::printData(std::vector<std::string> inputName, std::vector<std::vector<double>> inputSet) {
|
|
MLPPLinAlg alg;
|
|
inputSet = alg.transpose(inputSet);
|
|
for (int i = 0; i < inputSet.size(); i++) {
|
|
std::cout << inputName[i] << std::endl;
|
|
for (int j = 0; j < inputSet[i].size(); j++) {
|
|
std::cout << inputSet[i][j] << std::endl;
|
|
}
|
|
}
|
|
}
|
|
|
|
// SIMPLE
|
|
|
|
void MLPPData::setData(std::string fileName, std::vector<double> &inputSet, std::vector<double> &outputSet) {
|
|
std::string inputTemp, outputTemp;
|
|
|
|
std::ifstream dataFile(fileName);
|
|
if (!dataFile.is_open()) {
|
|
std::cout << "The file failed to open." << std::endl;
|
|
}
|
|
|
|
std::string line;
|
|
|
|
while (std::getline(dataFile, line)) {
|
|
std::stringstream ss(line);
|
|
|
|
std::getline(ss, inputTemp, ',');
|
|
std::getline(ss, outputTemp, ',');
|
|
|
|
inputSet.push_back(std::stod(inputTemp));
|
|
outputSet.push_back(std::stod(outputTemp));
|
|
}
|
|
|
|
dataFile.close();
|
|
}
|
|
|
|
void MLPPData::printData(std::string &inputName, std::string &outputName, std::vector<double> &inputSet, std::vector<double> &outputSet) {
|
|
std::cout << inputName << std::endl;
|
|
for (int i = 0; i < inputSet.size(); i++) {
|
|
std::cout << inputSet[i] << std::endl;
|
|
}
|
|
|
|
std::cout << outputName << std::endl;
|
|
for (int i = 0; i < inputSet.size(); i++) {
|
|
std::cout << outputSet[i] << std::endl;
|
|
}
|
|
}
|
|
|
|
// Images
|
|
std::vector<std::vector<double>> MLPPData::rgb2gray(std::vector<std::vector<std::vector<double>>> input) {
|
|
std::vector<std::vector<double>> grayScale;
|
|
grayScale.resize(input[0].size());
|
|
for (int i = 0; i < grayScale.size(); i++) {
|
|
grayScale[i].resize(input[0][i].size());
|
|
}
|
|
for (int i = 0; i < grayScale.size(); i++) {
|
|
for (int j = 0; j < grayScale[i].size(); j++) {
|
|
grayScale[i][j] = 0.299 * input[0][i][j] + 0.587 * input[1][i][j] + 0.114 * input[2][i][j];
|
|
}
|
|
}
|
|
return grayScale;
|
|
}
|
|
|
|
std::vector<std::vector<std::vector<double>>> MLPPData::rgb2ycbcr(std::vector<std::vector<std::vector<double>>> input) {
|
|
MLPPLinAlg alg;
|
|
std::vector<std::vector<std::vector<double>>> YCbCr;
|
|
YCbCr = alg.resize(YCbCr, input);
|
|
for (int i = 0; i < YCbCr[0].size(); i++) {
|
|
for (int j = 0; j < YCbCr[0][i].size(); j++) {
|
|
YCbCr[0][i][j] = 0.299 * input[0][i][j] + 0.587 * input[1][i][j] + 0.114 * input[2][i][j];
|
|
YCbCr[1][i][j] = -0.169 * input[0][i][j] - 0.331 * input[1][i][j] + 0.500 * input[2][i][j];
|
|
YCbCr[2][i][j] = 0.500 * input[0][i][j] - 0.419 * input[1][i][j] - 0.081 * input[2][i][j];
|
|
}
|
|
}
|
|
return YCbCr;
|
|
}
|
|
|
|
// Conversion formulas available here:
|
|
// https://www.rapidtables.com/convert/color/rgb-to-hsv.html
|
|
std::vector<std::vector<std::vector<double>>> MLPPData::rgb2hsv(std::vector<std::vector<std::vector<double>>> input) {
|
|
MLPPLinAlg alg;
|
|
std::vector<std::vector<std::vector<double>>> HSV;
|
|
HSV = alg.resize(HSV, input);
|
|
for (int i = 0; i < HSV[0].size(); i++) {
|
|
for (int j = 0; j < HSV[0][i].size(); j++) {
|
|
double rPrime = input[0][i][j] / 255;
|
|
double gPrime = input[1][i][j] / 255;
|
|
double bPrime = input[2][i][j] / 255;
|
|
|
|
double cMax = alg.max({ rPrime, gPrime, bPrime });
|
|
double cMin = alg.min({ rPrime, gPrime, bPrime });
|
|
double delta = cMax - cMin;
|
|
|
|
// H calculation.
|
|
if (delta == 0) {
|
|
HSV[0][i][j] = 0;
|
|
} else {
|
|
if (cMax == rPrime) {
|
|
HSV[0][i][j] = 60 * fmod(((gPrime - bPrime) / delta), 6);
|
|
} else if (cMax == gPrime) {
|
|
HSV[0][i][j] = 60 * ((bPrime - rPrime) / delta + 2);
|
|
} else { // cMax == bPrime
|
|
HSV[0][i][j] = 60 * ((rPrime - gPrime) / delta + 6);
|
|
}
|
|
}
|
|
|
|
// S calculation.
|
|
if (cMax == 0) {
|
|
HSV[1][i][j] = 0;
|
|
} else {
|
|
HSV[1][i][j] = delta / cMax;
|
|
}
|
|
|
|
// V calculation.
|
|
HSV[2][i][j] = cMax;
|
|
}
|
|
}
|
|
return HSV;
|
|
}
|
|
|
|
// http://machinethatsees.blogspot.com/2013/07/how-to-convert-rgb-to-xyz-or-vice-versa.html
|
|
std::vector<std::vector<std::vector<double>>> MLPPData::rgb2xyz(std::vector<std::vector<std::vector<double>>> input) {
|
|
MLPPLinAlg alg;
|
|
std::vector<std::vector<std::vector<double>>> XYZ;
|
|
XYZ = alg.resize(XYZ, input);
|
|
std::vector<std::vector<double>> RGB2XYZ = { { 0.4124564, 0.3575761, 0.1804375 }, { 0.2126726, 0.7151522, 0.0721750 }, { 0.0193339, 0.1191920, 0.9503041 } };
|
|
return alg.vector_wise_tensor_product(input, RGB2XYZ);
|
|
}
|
|
|
|
std::vector<std::vector<std::vector<double>>> MLPPData::xyz2rgb(std::vector<std::vector<std::vector<double>>> input) {
|
|
MLPPLinAlg alg;
|
|
std::vector<std::vector<std::vector<double>>> XYZ;
|
|
XYZ = alg.resize(XYZ, input);
|
|
std::vector<std::vector<double>> RGB2XYZ = alg.inverse({ { 0.4124564, 0.3575761, 0.1804375 }, { 0.2126726, 0.7151522, 0.0721750 }, { 0.0193339, 0.1191920, 0.9503041 } });
|
|
return alg.vector_wise_tensor_product(input, RGB2XYZ);
|
|
}
|
|
|
|
// TEXT-BASED & NLP
|
|
std::string MLPPData::toLower(std::string text) {
|
|
for (int i = 0; i < text.size(); i++) {
|
|
text[i] = tolower(text[i]);
|
|
}
|
|
return text;
|
|
}
|
|
|
|
std::vector<char> MLPPData::split(std::string text) {
|
|
std::vector<char> split_data;
|
|
for (int i = 0; i < text.size(); i++) {
|
|
split_data.push_back(text[i]);
|
|
}
|
|
return split_data;
|
|
}
|
|
|
|
std::vector<std::string> MLPPData::splitSentences(std::string data) {
|
|
std::vector<std::string> sentences;
|
|
std::string currentStr = "";
|
|
|
|
for (int i = 0; i < data.length(); i++) {
|
|
currentStr.push_back(data[i]);
|
|
if (data[i] == '.' && data[i + 1] != '.') {
|
|
sentences.push_back(currentStr);
|
|
currentStr = "";
|
|
i++;
|
|
}
|
|
}
|
|
return sentences;
|
|
}
|
|
|
|
std::vector<std::string> MLPPData::removeSpaces(std::vector<std::string> data) {
|
|
for (int i = 0; i < data.size(); i++) {
|
|
auto it = data[i].begin();
|
|
for (int j = 0; j < data[i].length(); j++) {
|
|
if (data[i][j] == ' ') {
|
|
data[i].erase(it);
|
|
}
|
|
it++;
|
|
}
|
|
}
|
|
return data;
|
|
}
|
|
|
|
std::vector<std::string> MLPPData::removeNullByte(std::vector<std::string> data) {
|
|
for (int i = 0; i < data.size(); i++) {
|
|
if (data[i] == "\0") {
|
|
data.erase(data.begin() + i);
|
|
}
|
|
}
|
|
return data;
|
|
}
|
|
|
|
std::vector<std::string> MLPPData::segment(std::string text) {
|
|
std::vector<std::string> segmented_data;
|
|
int prev_delim = 0;
|
|
for (int i = 0; i < text.length(); i++) {
|
|
if (text[i] == ' ') {
|
|
segmented_data.push_back(text.substr(prev_delim, i - prev_delim));
|
|
prev_delim = i + 1;
|
|
} else if (text[i] == ',' || text[i] == '!' || text[i] == '.' || text[i] == '-') {
|
|
segmented_data.push_back(text.substr(prev_delim, i - prev_delim));
|
|
std::string punc;
|
|
punc.push_back(text[i]);
|
|
segmented_data.push_back(punc);
|
|
prev_delim = i + 2;
|
|
i++;
|
|
} else if (i == text.length() - 1) {
|
|
segmented_data.push_back(text.substr(prev_delim, text.length() - prev_delim)); // hehe oops- forgot this
|
|
}
|
|
}
|
|
|
|
return segmented_data;
|
|
}
|
|
|
|
std::vector<double> MLPPData::tokenize(std::string text) {
|
|
int max_num = 0;
|
|
bool new_num = true;
|
|
std::vector<std::string> segmented_data = segment(text);
|
|
std::vector<double> tokenized_data;
|
|
tokenized_data.resize(segmented_data.size());
|
|
for (int i = 0; i < segmented_data.size(); i++) {
|
|
for (int j = i - 1; j >= 0; j--) {
|
|
if (segmented_data[i] == segmented_data[j]) {
|
|
tokenized_data[i] = tokenized_data[j];
|
|
new_num = false;
|
|
}
|
|
}
|
|
if (!new_num) {
|
|
new_num = true;
|
|
} else {
|
|
max_num++;
|
|
tokenized_data[i] = max_num;
|
|
}
|
|
}
|
|
return tokenized_data;
|
|
}
|
|
|
|
std::vector<std::string> MLPPData::removeStopWords(std::string text) {
|
|
std::vector<std::string> stopWords = { "i", "me", "my", "myself", "we", "our", "ours", "ourselves", "you", "your", "yours", "yourself", "yourselves", "he", "him", "his", "himself", "she", "her", "hers", "herself", "it", "its", "itself", "they", "them", "their", "theirs", "themselves", "what", "which", "who", "whom", "this", "that", "these", "those", "am", "is", "are", "was", "were", "be", "been", "being", "have", "has", "had", "having", "do", "does", "did", "doing", "a", "an", "the", "and", "but", "if", "or", "because", "as", "until", "while", "of", "at", "by", "for", "with", "about", "against", "between", "into", "through", "during", "before", "after", "above", "below", "to", "from", "up", "down", "in", "out", "on", "off", "over", "under", "again", "further", "then", "once", "here", "there", "when", "where", "why", "how", "all", "any", "both", "each", "few", "more", "most", "other", "some", "such", "no", "nor", "not", "only", "own", "same", "so", "than", "too", "very", "s", "t", "can", "will", "just", "don", "should", "now" };
|
|
std::vector<std::string> segmented_data = removeSpaces(segment(toLower(text)));
|
|
|
|
for (int i = 0; i < stopWords.size(); i++) {
|
|
for (int j = 0; j < segmented_data.size(); j++) {
|
|
if (segmented_data[j] == stopWords[i]) {
|
|
segmented_data.erase(segmented_data.begin() + j);
|
|
}
|
|
}
|
|
}
|
|
return segmented_data;
|
|
}
|
|
|
|
std::vector<std::string> MLPPData::removeStopWords(std::vector<std::string> segmented_data) {
|
|
std::vector<std::string> stopWords = { "i", "me", "my", "myself", "we", "our", "ours", "ourselves", "you", "your", "yours", "yourself", "yourselves", "he", "him", "his", "himself", "she", "her", "hers", "herself", "it", "its", "itself", "they", "them", "their", "theirs", "themselves", "what", "which", "who", "whom", "this", "that", "these", "those", "am", "is", "are", "was", "were", "be", "been", "being", "have", "has", "had", "having", "do", "does", "did", "doing", "a", "an", "the", "and", "but", "if", "or", "because", "as", "until", "while", "of", "at", "by", "for", "with", "about", "against", "between", "into", "through", "during", "before", "after", "above", "below", "to", "from", "up", "down", "in", "out", "on", "off", "over", "under", "again", "further", "then", "once", "here", "there", "when", "where", "why", "how", "all", "any", "both", "each", "few", "more", "most", "other", "some", "such", "no", "nor", "not", "only", "own", "same", "so", "than", "too", "very", "s", "t", "can", "will", "just", "don", "should", "now" };
|
|
for (int i = 0; i < segmented_data.size(); i++) {
|
|
for (int j = 0; j < stopWords.size(); j++) {
|
|
if (segmented_data[i] == stopWords[j]) {
|
|
segmented_data.erase(segmented_data.begin() + i);
|
|
}
|
|
}
|
|
}
|
|
return segmented_data;
|
|
}
|
|
|
|
std::string MLPPData::stemming(std::string text) {
|
|
// Our list of suffixes which we use to compare against
|
|
std::vector<std::string> suffixes = { "eer", "er", "ion", "ity", "ment", "ness", "or", "sion", "ship", "th", "able", "ible", "al", "ant", "ary", "ful", "ic", "ious", "ous", "ive", "less", "y", "ed", "en", "ing", "ize", "ise", "ly", "ward", "wise" };
|
|
int padding_size = 4;
|
|
char padding = ' '; // our padding
|
|
|
|
for (int i = 0; i < padding_size; i++) {
|
|
text[text.length() + i] = padding; // ' ' will be our padding value
|
|
}
|
|
|
|
for (int i = 0; i < text.size(); i++) {
|
|
for (int j = 0; j < suffixes.size(); j++) {
|
|
if (text.substr(i, suffixes[j].length()) == suffixes[j] && (text[i + suffixes[j].length()] == ' ' || text[i + suffixes[j].length()] == ',' || text[i + suffixes[j].length()] == '-' || text[i + suffixes[j].length()] == '.' || text[i + suffixes[j].length()] == '!')) {
|
|
text.erase(i, suffixes[j].length());
|
|
}
|
|
}
|
|
}
|
|
|
|
return text;
|
|
}
|
|
|
|
std::vector<std::vector<double>> MLPPData::BOW(std::vector<std::string> sentences, std::string type) {
|
|
/*
|
|
STEPS OF BOW:
|
|
1) To lowercase (done by removeStopWords function by def)
|
|
2) Removing stop words
|
|
3) Obtain a list of the used words
|
|
4) Create a one hot encoded vector of the words and sentences
|
|
5) Sentence.size() x list.size() matrix
|
|
*/
|
|
|
|
std::vector<std::string> wordList = removeNullByte(removeStopWords(createWordList(sentences)));
|
|
|
|
std::vector<std::vector<std::string>> segmented_sentences;
|
|
segmented_sentences.resize(sentences.size());
|
|
|
|
for (int i = 0; i < sentences.size(); i++) {
|
|
segmented_sentences[i] = removeStopWords(sentences[i]);
|
|
}
|
|
|
|
std::vector<std::vector<double>> bow;
|
|
|
|
bow.resize(sentences.size());
|
|
for (int i = 0; i < bow.size(); i++) {
|
|
bow[i].resize(wordList.size());
|
|
}
|
|
|
|
for (int i = 0; i < segmented_sentences.size(); i++) {
|
|
for (int j = 0; j < segmented_sentences[i].size(); j++) {
|
|
for (int k = 0; k < wordList.size(); k++) {
|
|
if (segmented_sentences[i][j] == wordList[k]) {
|
|
if (type == "Binary") {
|
|
bow[i][k] = 1;
|
|
} else {
|
|
bow[i][k]++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return bow;
|
|
}
|
|
|
|
std::vector<std::vector<double>> MLPPData::TFIDF(std::vector<std::string> sentences) {
|
|
MLPPLinAlg alg;
|
|
std::vector<std::string> wordList = removeNullByte(removeStopWords(createWordList(sentences)));
|
|
|
|
std::vector<std::vector<std::string>> segmented_sentences;
|
|
segmented_sentences.resize(sentences.size());
|
|
|
|
for (int i = 0; i < sentences.size(); i++) {
|
|
segmented_sentences[i] = removeStopWords(sentences[i]);
|
|
}
|
|
|
|
std::vector<std::vector<double>> TF;
|
|
std::vector<int> frequency;
|
|
frequency.resize(wordList.size());
|
|
TF.resize(segmented_sentences.size());
|
|
for (int i = 0; i < TF.size(); i++) {
|
|
TF[i].resize(wordList.size());
|
|
}
|
|
for (int i = 0; i < segmented_sentences.size(); i++) {
|
|
std::vector<bool> present(wordList.size(), 0);
|
|
for (int j = 0; j < segmented_sentences[i].size(); j++) {
|
|
for (int k = 0; k < wordList.size(); k++) {
|
|
if (segmented_sentences[i][j] == wordList[k]) {
|
|
TF[i][k]++;
|
|
if (!present[k]) {
|
|
frequency[k]++;
|
|
present[k] = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
TF[i] = alg.scalarMultiply(double(1) / double(segmented_sentences[i].size()), TF[i]);
|
|
}
|
|
|
|
std::vector<double> IDF;
|
|
IDF.resize(frequency.size());
|
|
|
|
for (int i = 0; i < IDF.size(); i++) {
|
|
IDF[i] = std::log((double)segmented_sentences.size() / (double)frequency[i]);
|
|
}
|
|
|
|
std::vector<std::vector<double>> TFIDF;
|
|
TFIDF.resize(segmented_sentences.size());
|
|
for (int i = 0; i < TFIDF.size(); i++) {
|
|
TFIDF[i].resize(wordList.size());
|
|
}
|
|
|
|
for (int i = 0; i < TFIDF.size(); i++) {
|
|
for (int j = 0; j < TFIDF[i].size(); j++) {
|
|
TFIDF[i][j] = TF[i][j] * IDF[j];
|
|
}
|
|
}
|
|
|
|
return TFIDF;
|
|
}
|
|
|
|
std::tuple<std::vector<std::vector<double>>, std::vector<std::string>> MLPPData::word2Vec(std::vector<std::string> sentences, std::string type, int windowSize, int dimension, double learning_rate, int max_epoch) {
|
|
std::vector<std::string> wordList = removeNullByte(removeStopWords(createWordList(sentences)));
|
|
|
|
std::vector<std::vector<std::string>> segmented_sentences;
|
|
segmented_sentences.resize(sentences.size());
|
|
|
|
for (int i = 0; i < sentences.size(); i++) {
|
|
segmented_sentences[i] = removeStopWords(sentences[i]);
|
|
}
|
|
|
|
std::vector<std::string> inputStrings;
|
|
std::vector<std::string> outputStrings;
|
|
|
|
for (int i = 0; i < segmented_sentences.size(); i++) {
|
|
for (int j = 0; j < segmented_sentences[i].size(); j++) {
|
|
for (int k = windowSize; k > 0; k--) {
|
|
if (j - k >= 0) {
|
|
inputStrings.push_back(segmented_sentences[i][j]);
|
|
|
|
outputStrings.push_back(segmented_sentences[i][j - k]);
|
|
}
|
|
if (j + k <= segmented_sentences[i].size() - 1) {
|
|
inputStrings.push_back(segmented_sentences[i][j]);
|
|
outputStrings.push_back(segmented_sentences[i][j + k]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
int inputSize = inputStrings.size();
|
|
|
|
inputStrings.insert(inputStrings.end(), outputStrings.begin(), outputStrings.end());
|
|
|
|
std::vector<std::vector<double>> BOW = MLPPData::BOW(inputStrings, "Binary");
|
|
|
|
std::vector<std::vector<double>> inputSet;
|
|
std::vector<std::vector<double>> outputSet;
|
|
|
|
for (int i = 0; i < inputSize; i++) {
|
|
inputSet.push_back(BOW[i]);
|
|
}
|
|
|
|
for (int i = inputSize; i < BOW.size(); i++) {
|
|
outputSet.push_back(BOW[i]);
|
|
}
|
|
MLPPLinAlg alg;
|
|
MLPPSoftmaxNet *model;
|
|
if (type == "Skipgram") {
|
|
model = new MLPPSoftmaxNet(outputSet, inputSet, dimension);
|
|
} else { // else = CBOW. We maintain it is a default.
|
|
model = new MLPPSoftmaxNet(inputSet, outputSet, dimension);
|
|
}
|
|
model->gradientDescent(learning_rate, max_epoch, 1);
|
|
|
|
std::vector<std::vector<double>> wordEmbeddings = model->getEmbeddings();
|
|
delete model;
|
|
return { wordEmbeddings, wordList };
|
|
}
|
|
|
|
struct WordsToVecResult {
|
|
std::vector<std::vector<double>> word_embeddings;
|
|
std::vector<std::string> word_list;
|
|
};
|
|
|
|
MLPPData::WordsToVecResult MLPPData::word_to_vec(std::vector<std::string> sentences, std::string type, int windowSize, int dimension, double learning_rate, int max_epoch) {
|
|
WordsToVecResult res;
|
|
|
|
res.word_list = removeNullByte(removeStopWords(createWordList(sentences)));
|
|
|
|
std::vector<std::vector<std::string>> segmented_sentences;
|
|
segmented_sentences.resize(sentences.size());
|
|
|
|
for (int i = 0; i < sentences.size(); i++) {
|
|
segmented_sentences[i] = removeStopWords(sentences[i]);
|
|
}
|
|
|
|
std::vector<std::string> inputStrings;
|
|
std::vector<std::string> outputStrings;
|
|
|
|
for (int i = 0; i < segmented_sentences.size(); i++) {
|
|
for (int j = 0; j < segmented_sentences[i].size(); j++) {
|
|
for (int k = windowSize; k > 0; k--) {
|
|
if (j - k >= 0) {
|
|
inputStrings.push_back(segmented_sentences[i][j]);
|
|
|
|
outputStrings.push_back(segmented_sentences[i][j - k]);
|
|
}
|
|
if (j + k <= segmented_sentences[i].size() - 1) {
|
|
inputStrings.push_back(segmented_sentences[i][j]);
|
|
outputStrings.push_back(segmented_sentences[i][j + k]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
int inputSize = inputStrings.size();
|
|
|
|
inputStrings.insert(inputStrings.end(), outputStrings.begin(), outputStrings.end());
|
|
|
|
std::vector<std::vector<double>> BOW = MLPPData::BOW(inputStrings, "Binary");
|
|
|
|
std::vector<std::vector<double>> inputSet;
|
|
std::vector<std::vector<double>> outputSet;
|
|
|
|
for (int i = 0; i < inputSize; i++) {
|
|
inputSet.push_back(BOW[i]);
|
|
}
|
|
|
|
for (int i = inputSize; i < BOW.size(); i++) {
|
|
outputSet.push_back(BOW[i]);
|
|
}
|
|
MLPPLinAlg alg;
|
|
MLPPSoftmaxNet *model;
|
|
if (type == "Skipgram") {
|
|
model = new MLPPSoftmaxNet(outputSet, inputSet, dimension);
|
|
} else { // else = CBOW. We maintain it is a default.
|
|
model = new MLPPSoftmaxNet(inputSet, outputSet, dimension);
|
|
}
|
|
model->gradientDescent(learning_rate, max_epoch, false);
|
|
|
|
res.word_embeddings = model->getEmbeddings();
|
|
delete model;
|
|
|
|
return res;
|
|
}
|
|
|
|
std::vector<std::vector<double>> MLPPData::LSA(std::vector<std::string> sentences, int dim) {
|
|
MLPPLinAlg alg;
|
|
std::vector<std::vector<double>> docWordData = BOW(sentences, "Binary");
|
|
|
|
auto [U, S, Vt] = alg.SVD(docWordData);
|
|
std::vector<std::vector<double>> S_trunc = alg.zeromat(dim, dim);
|
|
std::vector<std::vector<double>> Vt_trunc;
|
|
for (int i = 0; i < dim; i++) {
|
|
S_trunc[i][i] = S[i][i];
|
|
Vt_trunc.push_back(Vt[i]);
|
|
}
|
|
|
|
std::vector<std::vector<double>> embeddings = alg.matmult(S_trunc, Vt_trunc);
|
|
return embeddings;
|
|
}
|
|
|
|
std::vector<std::string> MLPPData::createWordList(std::vector<std::string> sentences) {
|
|
std::string combinedText = "";
|
|
for (int i = 0; i < sentences.size(); i++) {
|
|
if (i != 0) {
|
|
combinedText += " ";
|
|
}
|
|
combinedText += sentences[i];
|
|
}
|
|
|
|
return removeSpaces(vecToSet(removeStopWords(combinedText)));
|
|
}
|
|
|
|
// EXTRA
|
|
void MLPPData::setInputNames(std::string fileName, std::vector<std::string> &inputNames) {
|
|
std::string inputNameTemp;
|
|
std::ifstream dataFile(fileName);
|
|
if (!dataFile.is_open()) {
|
|
std::cout << fileName << " failed to open." << std::endl;
|
|
}
|
|
|
|
while (std::getline(dataFile, inputNameTemp)) {
|
|
inputNames.push_back(inputNameTemp);
|
|
}
|
|
|
|
dataFile.close();
|
|
}
|
|
|
|
std::vector<std::vector<double>> MLPPData::featureScaling(std::vector<std::vector<double>> X) {
|
|
MLPPLinAlg alg;
|
|
X = alg.transpose(X);
|
|
std::vector<double> max_elements, min_elements;
|
|
max_elements.resize(X.size());
|
|
min_elements.resize(X.size());
|
|
|
|
for (int i = 0; i < X.size(); i++) {
|
|
max_elements[i] = alg.max(X[i]);
|
|
min_elements[i] = alg.min(X[i]);
|
|
}
|
|
|
|
for (int i = 0; i < X.size(); i++) {
|
|
for (int j = 0; j < X[i].size(); j++) {
|
|
X[i][j] = (X[i][j] - min_elements[i]) / (max_elements[i] - min_elements[i]);
|
|
}
|
|
}
|
|
return alg.transpose(X);
|
|
}
|
|
|
|
std::vector<std::vector<double>> MLPPData::meanNormalization(std::vector<std::vector<double>> X) {
|
|
MLPPLinAlg alg;
|
|
MLPPStat stat;
|
|
// (X_j - mu_j) / std_j, for every j
|
|
|
|
X = meanCentering(X);
|
|
for (int i = 0; i < X.size(); i++) {
|
|
X[i] = alg.scalarMultiply(1 / stat.standardDeviation(X[i]), X[i]);
|
|
}
|
|
return X;
|
|
}
|
|
|
|
std::vector<std::vector<double>> MLPPData::meanCentering(std::vector<std::vector<double>> X) {
|
|
MLPPLinAlg alg;
|
|
MLPPStat stat;
|
|
for (int i = 0; i < X.size(); i++) {
|
|
double mean_i = stat.mean(X[i]);
|
|
for (int j = 0; j < X[i].size(); j++) {
|
|
X[i][j] -= mean_i;
|
|
}
|
|
}
|
|
return X;
|
|
}
|
|
|
|
std::vector<std::vector<double>> MLPPData::oneHotRep(std::vector<double> tempOutputSet, int n_class) {
|
|
std::vector<std::vector<double>> outputSet;
|
|
outputSet.resize(tempOutputSet.size());
|
|
for (int i = 0; i < tempOutputSet.size(); i++) {
|
|
for (int j = 0; j <= n_class - 1; j++) {
|
|
if (tempOutputSet[i] == j) {
|
|
outputSet[i].push_back(1);
|
|
} else {
|
|
outputSet[i].push_back(0);
|
|
}
|
|
}
|
|
}
|
|
return outputSet;
|
|
}
|
|
|
|
std::vector<double> MLPPData::reverseOneHot(std::vector<std::vector<double>> tempOutputSet) {
|
|
std::vector<double> outputSet;
|
|
int n_class = tempOutputSet[0].size();
|
|
for (int i = 0; i < tempOutputSet.size(); i++) {
|
|
int current_class = 1;
|
|
for (int j = 0; j < tempOutputSet[i].size(); j++) {
|
|
if (tempOutputSet[i][j] == 1) {
|
|
break;
|
|
} else {
|
|
current_class++;
|
|
}
|
|
}
|
|
outputSet.push_back(current_class);
|
|
}
|
|
|
|
return outputSet;
|
|
}
|
|
|
|
void MLPPData::_bind_methods() {
|
|
ClassDB::bind_method(D_METHOD("load_breast_cancer", "path"), &MLPPData::load_breast_cancer);
|
|
ClassDB::bind_method(D_METHOD("load_breast_cancer_svc", "path"), &MLPPData::load_breast_cancer_svc);
|
|
ClassDB::bind_method(D_METHOD("load_iris", "path"), &MLPPData::load_iris);
|
|
ClassDB::bind_method(D_METHOD("load_wine", "path"), &MLPPData::load_wine);
|
|
ClassDB::bind_method(D_METHOD("load_mnist_train", "path"), &MLPPData::load_mnist_train);
|
|
ClassDB::bind_method(D_METHOD("load_mnist_test", "path"), &MLPPData::load_mnist_test);
|
|
ClassDB::bind_method(D_METHOD("load_california_housing", "path"), &MLPPData::load_california_housing);
|
|
ClassDB::bind_method(D_METHOD("load_fires_and_crime", "path"), &MLPPData::load_fires_and_crime);
|
|
|
|
ClassDB::bind_method(D_METHOD("train_test_split", "data", "test_size"), &MLPPData::train_test_split_bind);
|
|
}
|