Skip to content

Instantly share code, notes, and snippets.

@EmmanuelMess
Last active August 20, 2018 22:49
Show Gist options
  • Save EmmanuelMess/f690efd1bbbedc6ae4701b19b5540c1b to your computer and use it in GitHub Desktop.
Save EmmanuelMess/f690efd1bbbedc6ae4701b19b5540c1b to your computer and use it in GitHub Desktop.
Competitive unsupervised learning without Oja's algorithm
#include <iostream>
#include <vector>
#include <random>
typedef double T;
class VectorWrongSizeException : public std::exception {
const char* what() const noexcept override {
return "Different vector sizes!";
}
};
class NonOneAfterNormalization : public VectorWrongSizeException {
T size;
const char* what() const noexcept override {
std::cerr << ("Vector size " + std::to_string(size) + " after normalization!").data();
return ("Vector size " + std::to_string(size) + " after normalization!").data();
}
public:
NonOneAfterNormalization(T size) : size(size) { }
};
class Vector : public std::vector<T> {
public:
explicit Vector(size_t size) : std::vector<T>(size) { }
explicit Vector(const vector<T>& v) : std::vector<T>(v) { }
void operator+=(const Vector& o) {
if(size() != o.size()) {
throw VectorWrongSizeException();
}
for (size_t i = 0; i < size(); i++) {
(*this)[i] += o[i];
}
}
void operator-=(const Vector& o) {
if(size() != o.size()) {
throw VectorWrongSizeException();
}
for (size_t i = 0; i < size(); i++) {
(*this)[i] -= o[i];
}
}
friend Vector operator+(Vector s, const Vector& o) {
if(s.size() != o.size()) {
throw VectorWrongSizeException();
}
Vector r(s);
for (size_t i = 0; i < s.size(); i++) {
r[i] += o[i];
}
return r;
}
friend Vector operator-(Vector s, const Vector& o) {
if(s.size() != o.size()) {
throw VectorWrongSizeException();
}
Vector r(s);
for (size_t i = 0; i < s.size(); i++) {
r[i] -= o[i];
}
return r;
}
T operator*(const Vector& o) const {//scalar product
T r = 0;
for (size_t i = 0; i < size(); i++) {
r += (*this)[i] * o[i];
}
return r;
}
Vector operator/(T o) const {
Vector newV(size());
for (size_t i = 0; i < size(); i++) {
newV[i] = (*this)[i] / o;
}
return newV;
}
friend Vector operator*(T o, const Vector& v);
Vector operator-() {
Vector newV(size());
for (size_t i = 0; i < size(); ++i) {
newV[i] = -(*this)[i];
}
return newV;
}
void normalize(T to = 1) {
double len = length();
if(len == 1) return;
for(auto& axis : *this) {
axis /= len;
axis *= to;
}
len = length();
if(len <= (T)(to - 0.000001) || len >= (T)(to + 0.000001)) {
throw NonOneAfterNormalization(len);
}
}
Vector normalized() const {
T len = length();
if(len == 1) return Vector(*this);
Vector r(*this);
r.normalize();
return r;
}
T length() const {
T r = 0;
for (size_t i = 0; i < size(); ++i) {
r += (*this)[i]*(*this)[i];
}
return static_cast<T>(sqrt(r));
}
friend std::ostream &operator<<(std::ostream& strm, const Vector& obj);
};
Vector operator*(T o, const Vector& v) {
Vector newV(v.size());
for (size_t i = 0; i < v.size(); i++) {
newV[i] = v[i] * o;
}
return newV;
}
class Perceptron {
Vector w;
std::string name;
public:
explicit Perceptron(size_t size, std::string name)
: w(size), name(name) {
static std::random_device rd;
static std::mt19937 mt(rd());
static std::uniform_real_distribution<double> dist(-1, 1);
do {
for (auto &axis : w) {
axis = dist(mt);
}
} while(w.length() <= 1);
w.normalize();
}
void operator+=(const Vector& o) {
w += o;
}
friend Vector operator+(Perceptron p, const Vector& o) {
return p.w + o;
}
void operator-=(const Vector& o) {
w -= o;
}
Vector weights() const {
return w;
}
void setWeights(const Vector& weights) {
w = Vector(weights);
}
const std::string& getName() const {
return name;
}
friend std::ostream &operator<<(std::ostream& strm, const Perceptron& obj);
};
std::ostream &operator<<(std::ostream& strm, const Perceptron& obj) {
strm << obj.w << "[Θ = 0]";
}
std::ostream &operator<<(std::ostream& strm, const Vector& obj) {
strm << "[" << obj.size() << "]{";
for (int i = 0; i < obj.size(); i++) {
strm << obj[i];
if(i < obj.size()-1) {
strm << ", ";
}
}
strm << "}";
}
std::vector<std::string> simpleDatapoints(std::vector<Vector>& inputs) {
inputs.push_back(Vector({1, 1}));
inputs.push_back(Vector({1.5, 2}));
inputs.push_back(Vector({1.5, 3}));
inputs.push_back(Vector({2, 1}));
inputs.push_back(Vector({-1, -1}));
inputs.push_back(Vector({-2, -1}));
inputs.push_back(Vector({-2, -2}));
inputs.push_back(Vector({1, -1}));
inputs.push_back(Vector({1.5, -1}));
inputs.push_back(Vector({1.5, -2}));
std::vector<std::string> clusters;
clusters.push_back("cluster a");
clusters.push_back("cluster b");
clusters.push_back("cluster c");
return clusters;
}
int winnerTakesAllUpdate(Perceptron &perceptron, const Vector &x) {
perceptron.setWeights((perceptron + x).normalized());
}
int withLearningConstantUpdate(Perceptron &perceptron, const Vector &x) {
static float constant = 0.01f;
perceptron.setWeights((perceptron + (constant*x)).normalized());
if(constant > 0.0001f) constant -= 0.0001f;
}
int differenceUpdate(Perceptron &perceptron, const Vector &x) {
static float constant = 0.001f;
perceptron.setWeights((perceptron + (constant * (x - perceptron.weights()))).normalized());
}
int main() {
using std::cout;
using std::cin;
using std::endl;
std::vector<Vector> inputs;
std::vector<std::string> clusters = simpleDatapoints(inputs);//Change datapoints for different classifications
for(auto& point : inputs) {
point.normalize();
}
std::random_device rd;
std::mt19937 mt(rd());
std::uniform_int_distribution<size_t> dist(0, inputs.size()-1);
std::vector<Perceptron> perceptrons;
for(const auto &cluster : clusters) {
perceptrons.push_back(Perceptron(inputs[0].size(), cluster));
cout << perceptrons.back() << endl;
}
int total = 4000000;
for(size_t i = 0; i < total; i++) {
Vector x = inputs[dist(mt)];
T maxResult = 0;
Perceptron *toUpdate = &perceptrons.back();
for(auto& perceptron : perceptrons) {
T result = perceptron.weights() * x;
if(result > maxResult) {
maxResult = result;
toUpdate = &perceptron;
}
}
differenceUpdate(*toUpdate, x);//Change function for different update algorithms
}
cout << "Tries: " << total << endl;
std::vector<std::pair<T, T>> plotdata;
for (const auto &perceptron : perceptrons) {
cout << perceptron.getName() << " = " << perceptron.weights() << endl;
plotdata.push_back(std::pair<T, T>(perceptron.weights()[0], perceptron.weights()[1]));
}
for (const auto &x : inputs) {
T maxResult = 0;
std::string maxName;
for(auto& perceptron : perceptrons) {
T result = perceptron.weights() * x;
if(result > maxResult) {
maxResult = result;
maxName = perceptron.getName();
}
}
cout << "(" << x[0] << ", " << x[1] << ") = " << maxName << endl;
plotdata.push_back(std::pair<T, T>(x[0], x[1]));
}
cout << endl << "---- start dump ----" << endl;
for(const auto& point : plotdata) {
cout << "(" << point.first << ", " << point.second << ")" << endl;
}
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment