kohonen_som_topology.cpp File Reference
Machine Learning Algorithms

Kohonen self organizing map (topological map) More...

`#include <algorithm>`
`#include <array>`
`#include <cerrno>`
`#include <cmath>`
`#include <cstdlib>`
`#include <cstring>`
`#include <ctime>`
`#include <fstream>`
`#include <iostream>`
`#include <valarray>`
`#include <vector>`
Include dependency graph for kohonen_som_topology.cpp:

## Namespaces

| machine_learning
| Machine learning algorithms.

## Functions

double | _random (double a, double b)
int | save_2d_data (const char *fname, const std::vector< std::valarray< double >> &X)
void | get_min_2d (const std::vector< std::valarray< double >> &X, double *val, int *x_idx, int *y_idx)
int | machine_learning::save_u_matrix (const char *fname, const std::vector< std::vector< std::valarray< double >>> &W)
double | machine_learning::update_weights (const std::valarray< double > &X, std::vector< std::vector< std::valarray< double >>> *W, std::vector< std::valarray< double >> *D, double alpha, int R)
void | machine_learning::kohonen_som (const std::vector< std::valarray< double >> &X, std::vector< std::vector< std::valarray< double >>> *W, double alpha_min)
void | test_2d_classes (std::vector< std::valarray< double >> *data)
void | test1 ()
void | test_3d_classes1 (std::vector< std::valarray< double >> *data)
void | test2 ()
void | test_3d_classes2 (std::vector< std::valarray< double >> *data)
void | test3 ()
double | get_clock_diff (clock_t start_t, clock_t end_t)
int | main (int argc, char **argv)

## Variables

constexpr double | machine_learning::MIN_DISTANCE = 1e-4

## Detailed Description

Kohonen self organizing map (topological map)

Author
  Krishna Vedala

This example implements a powerful unsupervised learning algorithm called as a self organizing map. The algorithm creates a connected network of weights that closely follows the given data points. This thus creates a topological map of the given data i.e., it maintains the relationship between varipus data points in a much higher dimesional space by creating an equivalent in a 2-dimensional space.

Note
  This C++ version of the program is considerable slower than its C counterpart
  The compiled code is much slower when compiled with MS Visual C++ 2019 than with GCC on windows

See also
  kohonen_som_trace.cpp

## Function Documentation

## get_clock_diff()

double get_clock_diff | ( | clock_t | start_t,
|  | clock_t | end_t
| ) |  |

Convert clock cycle difference to time in seconds

Parameters

  [in] | start_t | start clock
  [in] | end_t | end clock

Returns
  time difference in seconds

{
return static_cast<double>(end_t - start_t) / CLOCKS_PER_SEC;
}

## main()

int main | ( | int | argc,
|  | char ** | argv
| ) |  |

Main function

{
#ifdef _OPENMP
std::cout << "Using OpenMP based parallelization\n";
#else
std::cout << "NOT using OpenMP based parallelization\n";
#endif
std::srand(std::time(nullptr));
std::clock_t start_clk = std::clock();
test1();
auto end_clk = std::clock();
std::cout << "Test 1 completed in " << get_clock_diff(start_clk, end_clk)
<< " sec\n";
start_clk = std::clock();
test2();
end_clk = std::clock();
std::cout << "Test 2 completed in " << get_clock_diff(start_clk, end_clk)
<< " sec\n";
start_clk = std::clock();
test3();
end_clk = std::clock();
std::cout << "Test 3 completed in " << get_clock_diff(start_clk, end_clk)
<< " sec\n";
std::cout
<< "(Note: Calculated times include: creating test sets, training "
"model and writing files to disk.)\n\n";
return 0;
}
std::cout
std::clock
T clock(T... args)
std::clock_t
test2
void test2()
Definition: kohonen_som_topology.cpp:451
test1
void test1()
Definition: kohonen_som_topology.cpp:369
get_clock_diff
double get_clock_diff(clock_t start_t, clock_t end_t)
Definition: kohonen_som_topology.cpp:577
test3
void test3()
Definition: kohonen_som_topology.cpp:537
std::srand
T srand(T... args)
std::time
T time(T... args)
Here is the call graph for this function:

## test1()

void test1 | ( |  | ) |

Test that creates a random set of points distributed in four clusters in circumference of a circle and trains an SOM that finds that circular pattern. The following CSV files are created to validate the execution:

- `test1.csv`: random test samples points with a circular pattern
- `w11.csv`: initial random map
- `w12.csv`: trained SOM map

{
int j = 0, N = 300;
int features = 2;
int num_out = 30;
std::vector<std::valarray<double>> X(N);
std::vector<std::vector<std::valarray<double>>> W(num_out);
for (int i = 0; i < std::max(num_out, N); i++) {
// loop till max(N, num_out)
if (i < N) { // only add new arrays if i < N
X[i] = std::valarray<double>(features);
}
if (i < num_out) { // only add new arrays if i < num_out
W[i] = std::vector<std::valarray<double>>(num_out);
for (int k = 0; k < num_out; k++) {
W[i][k] = std::valarray<double>(features);
#ifdef _OPENMP
#pragma omp for
#endif
for (j = 0; j < features; j++) {
// preallocate with random initial weights
W[i][k][j] = _random(-10, 10);
}
}
}
}
test_2d_classes(&X); // create test data around circumference of a circle
save_2d_data("test1.csv", X); // save test data points
save_u_matrix("w11.csv", W); // save initial random weights
kohonen_som(X, &W, 1e-4); // train the SOM
save_u_matrix("w12.csv", W); // save the resultant weights
}
save_2d_data
int save_2d_data(const char *fname, const std::vector< std::valarray< double >> &X)
Definition: kohonen_som_topology.cpp:65
_random
double _random(double a, double b)
Definition: kohonen_som_topology.cpp:53
test_2d_classes
void test_2d_classes(std::vector< std::valarray< double >> *data)
Definition: kohonen_som_topology.cpp:330
std::max
T max(T... args)
machine_learning::save_u_matrix
int save_u_matrix(const char *fname, const std::vector< std::vector< std::valarray< double >>> &W)
Definition: kohonen_som_topology.cpp:142
machine_learning::kohonen_som
void kohonen_som(const std::vector< std::valarray< double >> &X, std::vector< std::vector< std::valarray< double >>> *W, double alpha_min)
Definition: kohonen_som_topology.cpp:269
std::valarray< double >
std::vector
Here is the call graph for this function:

## test2()

void test2 | ( |  | ) |

Test that creates a random set of points distributed in 4 clusters in 3D space and trains an SOM that finds the topological pattern. The following CSV files are created to validate the execution:

- `test2.csv`: random test samples points with a lamniscate pattern
- `w21.csv`: initial random map
- `w22.csv`: trained SOM map

{
int j = 0, N = 300;
int features = 3;
int num_out = 30;
std::vector<std::valarray<double>> X(N);
std::vector<std::vector<std::valarray<double>>> W(num_out);
for (int i = 0; i < std::max(num_out, N); i++) {
// loop till max(N, num_out)
if (i < N) { // only add new arrays if i < N
X[i] = std::valarray<double>(features);
}
if (i < num_out) { // only add new arrays if i < num_out
W[i] = std::vector<std::valarray<double>>(num_out);
for (int k = 0; k < num_out; k++) {
W[i][k] = std::valarray<double>(features);
#ifdef _OPENMP
#pragma omp for
#endif
for (j = 0; j < features; j++) {
// preallocate with random initial weights
W[i][k][j] = _random(-10, 10);
}
}
}
}
test_3d_classes1(&X); // create test data around circumference of a circle
save_2d_data("test2.csv", X); // save test data points
save_u_matrix("w21.csv", W); // save initial random weights
kohonen_som(X, &W, 1e-4); // train the SOM
save_u_matrix("w22.csv", W); // save the resultant weights
}
test_3d_classes1
void test_3d_classes1(std::vector< std::valarray< double >> *data)
Definition: kohonen_som_topology.cpp:411
Here is the call graph for this function:

## test3()

void test3 | ( |  | ) |

Test that creates a random set of points distributed in eight clusters in 3D space and trains an SOM that finds the topological pattern. The following CSV files are created to validate the execution:

- `test3.csv`: random test samples points with a circular pattern
- `w31.csv`: initial random map
- `w32.csv`: trained SOM map

{
int j = 0, N = 500;
int features = 3;
int num_out = 30;
std::vector<std::valarray<double>> X(N);
std::vector<std::vector<std::valarray<double>>> W(num_out);
for (int i = 0; i < std::max(num_out, N); i++) {
// loop till max(N, num_out)
if (i < N) { // only add new arrays if i < N
X[i] = std::valarray<double>(features);
}
if (i < num_out) { // only add new arrays if i < num_out
W[i] = std::vector<std::valarray<double>>(num_out);
for (int k = 0; k < num_out; k++) {
W[i][k] = std::valarray<double>(features);
#ifdef _OPENMP
#pragma omp for
#endif
for (j = 0; j < features; j++) {
// preallocate with random initial weights
W[i][k][j] = _random(-10, 10);
}
}
}
}
test_3d_classes2(&X); // create test data around circumference of a circle
save_2d_data("test3.csv", X); // save test data points
save_u_matrix("w31.csv", W); // save initial random weights
kohonen_som(X, &W, 1e-4); // train the SOM
save_u_matrix("w32.csv", W); // save the resultant weights
}
test_3d_classes2
void test_3d_classes2(std::vector< std::valarray< double >> *data)
Definition: kohonen_som_topology.cpp:493
Here is the call graph for this function:

## test_2d_classes()

void test_2d_classes | ( | std::vector< std::valarray< double >> * | data | ) |

Creates a random set of points distributed in four clusters in 3D space with centroids at the points

- \((0,5, 0.5, 0.5)\)
- \((0,5,-0.5, -0.5)\)
- \((-0,5, 0.5, 0.5)\)
- \((-0,5,-0.5, -0.5)\)

Parameters

  [out] | data | matrix to store data in

{
const int N = data->size();
const double R = 0.3; // radius of cluster
int i = 0;
const int num_classes = 4;
std::array<std::array<double, 2>, num_classes> centres = {
// centres of each class cluster
std::array<double, 2>({.5, .5}), // centre of class 1
std::array<double, 2>({.5, -.5}), // centre of class 2
std::array<double, 2>({-.5, .5}), // centre of class 3
std::array<double, 2>({-.5, -.5}) // centre of class 4
};
#ifdef _OPENMP
#pragma omp for
#endif
for (i = 0; i < N; i++) {
// select a random class for the point
int cls = std::rand() % num_classes;
// create random coordinates (x,y,z) around the centre of the class
data[0][i][0] = _random(centres[cls][0] - R, centres[cls][0] + R);
data[0][i][1] = _random(centres[cls][1] - R, centres[cls][1] + R);
/* The follosing can also be used
for (int j = 0; j < 2; j++)
data[i][j] = _random(centres[class][j] - R, centres[class][j] + R);
*/
}
}
std::array
data
int data[MAX]
test data
Definition: hash_search.cpp:24
std::rand
T rand(T... args)
Here is the call graph for this function:

## test_3d_classes1()

void test_3d_classes1 | ( | std::vector< std::valarray< double >> * | data | ) |

Creates a random set of points distributed in four clusters in 3D space with centroids at the points

- \((0,5, 0.5, 0.5)\)
- \((0,5,-0.5, -0.5)\)
- \((-0,5, 0.5, 0.5)\)
- \((-0,5,-0.5, -0.5)\)

Parameters

  [out] | data | matrix to store data in

{
const size_t N = data->size();
const double R = 0.3; // radius of cluster
int i = 0;
const int num_classes = 4;
const std::array<std::array<double, 3>, num_classes> centres = {
// centres of each class cluster
std::array<double, 3>({.5, .5, .5}), // centre of class 1
std::array<double, 3>({.5, -.5, -.5}), // centre of class 2
std::array<double, 3>({-.5, .5, .5}), // centre of class 3
std::array<double, 3>({-.5, -.5 - .5}) // centre of class 4
};
#ifdef _OPENMP
#pragma omp for
#endif
for (i = 0; i < N; i++) {
// select a random class for the point
int cls = std::rand() % num_classes;
// create random coordinates (x,y,z) around the centre of the class
data[0][i][0] = _random(centres[cls][0] - R, centres[cls][0] + R);
data[0][i][1] = _random(centres[cls][1] - R, centres[cls][1] + R);
data[0][i][2] = _random(centres[cls][2] - R, centres[cls][2] + R);
/* The follosing can also be used
for (int j = 0; j < 3; j++)
data[i][j] = _random(centres[class][j] - R, centres[class][j] + R);
*/
}
}
Here is the call graph for this function:

## test_3d_classes2()

void test_3d_classes2 | ( | std::vector< std::valarray< double >> * | data | ) |

Creates a random set of points distributed in four clusters in 3D space with centroids at the points

- \((0,5, 0.5, 0.5)\)
- \((0,5,-0.5, -0.5)\)
- \((-0,5, 0.5, 0.5)\)
- \((-0,5,-0.5, -0.5)\)

Parameters

  [out] | data | matrix to store data in

{
const size_t N = data->size();
const double R = 0.2; // radius of cluster
int i = 0;
const int num_classes = 8;
const std::array<std::array<double, 3>, num_classes> centres = {
// centres of each class cluster
std::array<double, 3>({.5, .5, .5}), // centre of class 1
std::array<double, 3>({.5, .5, -.5}), // centre of class 2
std::array<double, 3>({.5, -.5, .5}), // centre of class 3
std::array<double, 3>({.5, -.5, -.5}), // centre of class 4
std::array<double, 3>({-.5, .5, .5}), // centre of class 5
std::array<double, 3>({-.5, .5, -.5}), // centre of class 6
std::array<double, 3>({-.5, -.5, .5}), // centre of class 7
std::array<double, 3>({-.5, -.5, -.5}) // centre of class 8
};
#ifdef _OPENMP
#pragma omp for
#endif
for (i = 0; i < N; i++) {
// select a random class for the point
int cls = std::rand() % num_classes;
// create random coordinates (x,y,z) around the centre of the class
data[0][i][0] = _random(centres[cls][0] - R, centres[cls][0] + R);
data[0][i][1] = _random(centres[cls][1] - R, centres[cls][1] + R);
data[0][i][2] = _random(centres[cls][2] - R, centres[cls][2] + R);
/* The follosing can also be used
for (int j = 0; j < 3; j++)
data[i][j] = _random(centres[class][j] - R, centres[class][j] + R);
*/
}
}
Here is the call graph for this function: