314 lines
7.6 KiB
C++
314 lines
7.6 KiB
C++
#ifndef USINGPCA_H
|
|
#define USINGPCA_H
|
|
|
|
#include "pca/TrainPCA.h"
|
|
#include "pca/KNN.h"
|
|
#include "pca/aKNN.h"
|
|
|
|
std::vector<std::string> COLORS = {"#000000", "#0000ff", "#00ff00", "#ff0000", "#00ffff"};
|
|
|
|
|
|
|
|
struct Plot {
|
|
|
|
K::Gnuplot gp;
|
|
K::GnuplotSplot splot;
|
|
K::GnuplotSplotElementLines lines[5];
|
|
|
|
|
|
public:
|
|
|
|
Plot() {
|
|
for (int i = 0; i < 5; ++i) {lines[i].setColorHex(COLORS[i]);}
|
|
for (int i = 0; i < 5; ++i) {splot.add(&lines[i]);}
|
|
}
|
|
|
|
void add(int idx, std::vector<float>& vec) {
|
|
K::GnuplotPoint3 p3(vec[0], vec[1], vec[2]);
|
|
lines[idx].add(p3);
|
|
}
|
|
|
|
void clear() {
|
|
for (int i = 0; i < 5; ++i) {lines[i].clear();}
|
|
}
|
|
|
|
void show() {
|
|
gp.setDebugOutput(false);
|
|
gp.draw(splot);
|
|
gp.flush();
|
|
}
|
|
|
|
};
|
|
|
|
std::string getClass(const std::vector<ClassifiedFeature>& nns) {
|
|
std::unordered_map<std::string, int> map;
|
|
for(const ClassifiedFeature& nn : nns) { map[nn.className] += 1; }
|
|
for (auto& it : map) {
|
|
if (it.second > nns.size() * 0.75) {return it.first;}
|
|
}
|
|
return "";
|
|
}
|
|
|
|
struct ClassStats {
|
|
int counts[6] = {};
|
|
};
|
|
|
|
struct Stats{
|
|
int match;
|
|
int error;
|
|
int unknown;
|
|
Stats() : match(0), error(0), unknown(0) {;}
|
|
float getSum() {return match+error+unknown;}
|
|
};
|
|
|
|
|
|
|
|
std::vector<ClassifiedPattern> removePatterns(const std::vector<ClassifiedPattern>& patAll, const std::string& fileName) {
|
|
std::vector<ClassifiedPattern> res;
|
|
for (const ClassifiedPattern& pat : patAll) {
|
|
if (pat.belongsToFile(fileName)) {
|
|
continue;
|
|
} else {
|
|
res.push_back(pat);
|
|
}
|
|
}
|
|
return res;
|
|
}
|
|
|
|
template <int numFeatures> struct PCA {
|
|
|
|
aKNN<ClassifiedFeature, numFeatures> knn;
|
|
TrainPCA::Matrices m;
|
|
|
|
};
|
|
|
|
|
|
void runPCA() {
|
|
|
|
const int numFeatures = 10;
|
|
|
|
TrainPCA::Settings setTrain;
|
|
TrainPCA::Settings setClass; setClass.regionStart_ms += 25;
|
|
|
|
|
|
Plot p;
|
|
|
|
// convert all provided datasets into patterns
|
|
std::vector<ClassifiedPattern> srcTrain = TrainPCA::getAllData(setTrain);
|
|
std::vector<ClassifiedPattern> srcClass = TrainPCA::getAllData(setClass);
|
|
std::cout << "windows: " << srcTrain.size() << std::endl;
|
|
|
|
|
|
|
|
|
|
// error calculation
|
|
std::unordered_map<std::string, Stats> stats;
|
|
std::unordered_map<std::string, ClassStats> classStats;
|
|
int xx = 0;
|
|
std::unordered_map<std::string, PCA<numFeatures>*> pcas;
|
|
|
|
// try to classify each pattern
|
|
for (const ClassifiedPattern& patClassify : srcClass) {
|
|
|
|
// construct knn search for this leave-one-out ONLY ONCE
|
|
if (pcas.find(patClassify.fileName) == pcas.end()) {
|
|
|
|
std::cout << "constructing PCA for all files but " << patClassify.fileName << std::endl;
|
|
|
|
// remove all training patterns belonging to the same source file as the to be classifed pattern
|
|
std::vector<ClassifiedPattern> srcTrainLOO = removePatterns(srcTrain, patClassify.fileName);
|
|
|
|
// sanity check (have we removed all patterns?)
|
|
int diff = srcTrain.size() - srcTrainLOO.size();
|
|
if (diff < 200) {throw 1;}
|
|
|
|
p.clear();
|
|
PCA<numFeatures>* pca = new PCA<numFeatures>();
|
|
pcas[patClassify.fileName] = pca;
|
|
|
|
// train PCA using all pattern without those belonging to the same source file as the to-be-classified one
|
|
pca->m = TrainPCA::getMatrices(srcTrainLOO, numFeatures);
|
|
|
|
// calculate features and add them to the KNN
|
|
for (const ClassifiedPattern& pat : srcTrainLOO) {
|
|
K::DynColVector<float> vec = pca->m.A1 * K::PCAHelper<float>::toVector(pat.pattern);
|
|
std::vector<float> arr;
|
|
for (int i = 0; i < numFeatures; ++i) {arr.push_back(vec(i));}
|
|
pca->knn.add(ClassifiedFeature(pat.className, arr));
|
|
|
|
const int idx = Settings::classToInt(pat.className);
|
|
p.add(idx, arr);
|
|
|
|
}
|
|
pca->knn.build();
|
|
|
|
if (xx == 0) {
|
|
++xx;
|
|
std::ofstream out("/tmp/pca.gp"); p.gp.draw(p.splot); out << p.gp.getBuffer(); out.close();
|
|
}
|
|
//p.show();
|
|
//sleep(100);
|
|
|
|
}
|
|
|
|
{
|
|
|
|
PCA<numFeatures>* pca = pcas[patClassify.fileName];
|
|
|
|
// calculate features for the to-be-classified pattern
|
|
//const int idx = Settings::classToInt(pat.className);
|
|
K::DynColVector<float> vec = pca->m.A1 * K::PCAHelper<float>::toVector(patClassify.pattern);
|
|
|
|
// get KNN's answer
|
|
std::vector<float> arr;
|
|
for (int i = 0; i < numFeatures; ++i) {arr.push_back(vec(i));}
|
|
std::vector<ClassifiedFeature> neighbors = pca->knn.get(arr.data(), 5);
|
|
std::string gotClass = getClass(neighbors);
|
|
|
|
if (patClassify.className == gotClass) {stats["all"].match++; stats[patClassify.fileName].match++; stats[patClassify.className].match++;}
|
|
else if (gotClass == "") {stats["all"].unknown++; stats[patClassify.fileName].unknown++; stats[patClassify.className].unknown++;}
|
|
else {stats["all"].error++; stats[patClassify.fileName].error++; stats[patClassify.className].error++;}
|
|
|
|
int gotIdx = (gotClass == "") ? (5) : Settings::classToInt(gotClass);
|
|
++classStats[patClassify.className].counts[gotIdx];
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
for (auto& it : stats) {
|
|
Stats& stats = it.second;
|
|
std::cout << "'" <<it.first << "',";
|
|
std::cout << stats.match/stats.getSum() << ",";
|
|
std::cout << stats.error/stats.getSum() << ",";
|
|
std::cout << stats.unknown/stats.getSum();
|
|
std::cout << std::endl;
|
|
}
|
|
|
|
for (auto& it : classStats) {
|
|
ClassStats& stats = it.second;
|
|
std::cout << "'" << it.first << "',";
|
|
for (int i = 0; i < 6; ++i) {
|
|
std::cout << stats.counts[i] << ",";
|
|
}
|
|
std::cout << std::endl;
|
|
}
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
#include <vector>
|
|
|
|
#include "sensors/SensorReader.h"
|
|
#include "Interpolator.h"
|
|
|
|
#include <eigen3/Eigen/Dense>
|
|
|
|
|
|
enum class PracticeType {
|
|
//REST,
|
|
JUMPING_JACK,
|
|
SITUPS,
|
|
PUSHUPS,
|
|
KNEEBEND,
|
|
FORWARDBEND,
|
|
};
|
|
|
|
struct Practice {
|
|
|
|
PracticeType type;
|
|
Recording rec;
|
|
std::vector<uint64_t> keyGyro;
|
|
|
|
//Practice(const PracticeType p, const Recording& rec, const std::vector<uint64_t>& keyGyro) : p(p), rec(rec), keyGyro(keyGyro) {;}
|
|
|
|
K::Interpolator<uint64_t, SensorGyro> getInterpol() const {
|
|
K::Interpolator<uint64_t, SensorGyro> interpol;
|
|
for (auto it : rec.gyro.values) {interpol.add(it.ts, it.val);}
|
|
interpol.makeRelative();
|
|
return interpol;
|
|
}
|
|
|
|
};
|
|
|
|
class UsingPCA {
|
|
|
|
|
|
public:
|
|
|
|
static Eigen::VectorXf getWindow(Practice& p, uint64_t pos) {
|
|
K::Interpolator<uint64_t, SensorGyro> interpol = p.getInterpol();
|
|
Eigen::VectorXf vec(600/50*3, 1);
|
|
int idx = 0;
|
|
for (int offset = -300; offset < 300; offset += 50) {
|
|
SensorGyro gyro = interpol.get(pos + offset);
|
|
vec(idx++,0) = (gyro.x);
|
|
vec(idx++,0) = (gyro.y);
|
|
vec(idx++,0) = (gyro.z);
|
|
}
|
|
std::cout << vec << std::endl;
|
|
return vec;
|
|
}
|
|
|
|
static std::vector<Eigen::VectorXf> getClassWindows(Practice& p) {
|
|
|
|
std::vector<Eigen::VectorXf> windows;
|
|
for (uint64_t pos = 1000; pos < 5000; pos += 500) {
|
|
Eigen::VectorXf window = getWindow(p, pos);
|
|
windows.push_back(window);
|
|
}
|
|
return windows;
|
|
|
|
}
|
|
|
|
static Eigen::MatrixXf getR(std::vector<Eigen::VectorXf>& vecs) {
|
|
Eigen::MatrixXf mat = Eigen::MatrixXf::Zero(vecs[0].rows(), vecs[0].rows());
|
|
for (const Eigen::VectorXf& vec : vecs) {
|
|
mat += vec * vec.transpose();
|
|
}
|
|
mat /= vecs.size();
|
|
return mat;
|
|
}
|
|
|
|
static Eigen::VectorXf getM(std::vector<Eigen::VectorXf>& vecs) {
|
|
Eigen::MatrixXf mat = Eigen::MatrixXf::Zero(vecs[0].rows(), vecs[0].cols());
|
|
for (const Eigen::VectorXf& vec : vecs) {
|
|
mat += vec;
|
|
}
|
|
mat /= vecs.size();
|
|
return mat;
|
|
}
|
|
|
|
static void run() {
|
|
|
|
std::vector<Practice> practices;
|
|
|
|
practices.push_back(
|
|
Practice {
|
|
PracticeType::JUMPING_JACK,
|
|
SensorReader::read("/mnt/firma/kunden/HandyGames/daten/jumpingjack/jumpingjack_gl_5_subject_3_left.txt"),
|
|
{1950, 2900, 3850, 4850, 5850, 6850, 7850, 8850, 9800, 10800, 11850}
|
|
}
|
|
);
|
|
|
|
|
|
std::vector<Eigen::VectorXf> windows = getClassWindows(practices.back());
|
|
Eigen::MatrixXf R = getR(windows);
|
|
Eigen::MatrixXf m = getM(windows);
|
|
Eigen::MatrixXf Q = R - (m * m.transpose());
|
|
|
|
Eigen::SelfAdjointEigenSolver<Eigen::MatrixXf> es;
|
|
es.compute(Q);
|
|
|
|
int i = 0;
|
|
|
|
}
|
|
|
|
};
|
|
*/
|
|
|
|
#endif // USINGPCA_H
|
|
|