TheAlgorithms/C++ 1.0.0
All the algorithms implemented in C++
Loading...
Searching...
No Matches
kohonen_som_trace.cpp
Go to the documentation of this file.
1
21#define _USE_MATH_DEFINES // required for MS Visual C++
22#include <algorithm>
23#include <array>
24#include <cmath>
25#include <cstdlib>
26#include <ctime>
27#include <fstream>
28#include <iostream>
29#include <valarray>
30#include <vector>
31#ifdef _OPENMP // check if OpenMP based parallellization is available
32#include <omp.h>
33#endif
34
46double _random(double a, double b) {
47 return ((b - a) * (std::rand() % 100) / 100.f) + a;
48}
49
58int save_nd_data(const char *fname,
59 const std::vector<std::valarray<double>> &X) {
60 size_t num_points = X.size(); // number of rows
61 size_t num_features = X[0].size(); // number of columns
62
63 std::ofstream fp;
64 fp.open(fname);
65 if (!fp.is_open()) {
66 // error with opening file to write
67 std::cerr << "Error opening file " << fname << "\n";
68 return -1;
69 }
70
71 // for each point in the array
72 for (int i = 0; i < num_points; i++) {
73 // for each feature in the array
74 for (int j = 0; j < num_features; j++) {
75 fp << X[i][j]; // print the feature value
76 if (j < num_features - 1) { // if not the last feature
77 fp << ","; // suffix comma
78 }
79 }
80 if (i < num_points - 1) { // if not the last row
81 fp << "\n"; // start a new line
82 }
83 }
84
85 fp.close();
86 return 0;
87}
88
92namespace machine_learning {
93
103void update_weights(const std::valarray<double> &x,
104 std::vector<std::valarray<double>> *W,
105 std::valarray<double> *D, double alpha, int R) {
106 int j = 0, k = 0;
107 int num_out = W->size(); // number of SOM output nodes
108 // int num_features = x.size(); // number of data features
109
110#ifdef _OPENMP
111#pragma omp for
112#endif
113 // step 1: for each output point
114 for (j = 0; j < num_out; j++) {
115 // compute Euclidian distance of each output
116 // point from the current sample
117 (*D)[j] = (((*W)[j] - x) * ((*W)[j] - x)).sum();
118 }
119
120 // step 2: get closest node i.e., node with snallest Euclidian distance to
121 // the current pattern
122 auto result = std::min_element(std::begin(*D), std::end(*D));
123 // double d_min = *result;
124 int d_min_idx = std::distance(std::begin(*D), result);
125
126 // step 3a: get the neighborhood range
127 int from_node = std::max(0, d_min_idx - R);
128 int to_node = std::min(num_out, d_min_idx + R + 1);
129
130 // step 3b: update the weights of nodes in the
131 // neighborhood
132#ifdef _OPENMP
133#pragma omp for
134#endif
135 for (j = from_node; j < to_node; j++) {
136 // update weights of nodes in the neighborhood
137 (*W)[j] += alpha * (x - (*W)[j]);
138 }
139}
140
149void kohonen_som_tracer(const std::vector<std::valarray<double>> &X,
150 std::vector<std::valarray<double>> *W,
151 double alpha_min) {
152 int num_samples = X.size(); // number of rows
153 // int num_features = X[0].size(); // number of columns
154 int num_out = W->size(); // number of rows
155 int R = num_out >> 2, iter = 0;
156 double alpha = 1.f;
157
158 std::valarray<double> D(num_out);
159
160 // Loop alpha from 1 to slpha_min
161 do {
162 // Loop for each sample pattern in the data set
163 for (int sample = 0; sample < num_samples; sample++) {
164 // update weights for the current input pattern sample
165 update_weights(X[sample], W, &D, alpha, R);
166 }
167
168 // every 10th iteration, reduce the neighborhood range
169 if (iter % 10 == 0 && R > 1) {
170 R--;
171 }
172
173 alpha -= 0.01;
174 iter++;
175 } while (alpha > alpha_min);
176}
177
178} // namespace machine_learning
179
183
196void test_circle(std::vector<std::valarray<double>> *data) {
197 const int N = data->size();
198 const double R = 0.75, dr = 0.3;
199 double a_t = 0., b_t = 2.f * M_PI; // theta random between 0 and 2*pi
200 double a_r = R - dr, b_r = R + dr; // radius random between R-dr and R+dr
201 int i = 0;
202
203#ifdef _OPENMP
204#pragma omp for
205#endif
206 for (i = 0; i < N; i++) {
207 double r = _random(a_r, b_r); // random radius
208 double theta = _random(a_t, b_t); // random theta
209 data[0][i][0] = r * cos(theta); // convert from polar to cartesian
210 data[0][i][1] = r * sin(theta);
211 }
212}
213
233void test1() {
234 int j = 0, N = 500;
235 int features = 2;
236 int num_out = 50;
237 std::vector<std::valarray<double>> X(N);
238 std::vector<std::valarray<double>> W(num_out);
239 for (int i = 0; i < std::max(num_out, N); i++) {
240 // loop till max(N, num_out)
241 if (i < N) { // only add new arrays if i < N
242 X[i] = std::valarray<double>(features);
243 }
244 if (i < num_out) { // only add new arrays if i < num_out
245 W[i] = std::valarray<double>(features);
246
247#ifdef _OPENMP
248#pragma omp for
249#endif
250 for (j = 0; j < features; j++) {
251 // preallocate with random initial weights
252 W[i][j] = _random(-1, 1);
253 }
254 }
255 }
256
257 test_circle(&X); // create test data around circumference of a circle
258 save_nd_data("test1.csv", X); // save test data points
259 save_nd_data("w11.csv", W); // save initial random weights
260 kohonen_som_tracer(X, &W, 0.1); // train the SOM
261 save_nd_data("w12.csv", W); // save the resultant weights
262}
263
277void test_lamniscate(std::vector<std::valarray<double>> *data) {
278 const int N = data->size();
279 const double dr = 0.2;
280 int i = 0;
281
282#ifdef _OPENMP
283#pragma omp for
284#endif
285 for (i = 0; i < N; i++) {
286 double dx = _random(-dr, dr); // random change in x
287 double dy = _random(-dr, dr); // random change in y
288 double theta = _random(0, M_PI); // random theta
289 data[0][i][0] = dx + cos(theta); // convert from polar to cartesian
290 data[0][i][1] = dy + sin(2. * theta) / 2.f;
291 }
292}
293
315void test2() {
316 int j = 0, N = 500;
317 int features = 2;
318 int num_out = 20;
319 std::vector<std::valarray<double>> X(N);
320 std::vector<std::valarray<double>> W(num_out);
321 for (int i = 0; i < std::max(num_out, N); i++) {
322 // loop till max(N, num_out)
323 if (i < N) { // only add new arrays if i < N
324 X[i] = std::valarray<double>(features);
325 }
326 if (i < num_out) { // only add new arrays if i < num_out
327 W[i] = std::valarray<double>(features);
328
329#ifdef _OPENMP
330#pragma omp for
331#endif
332 for (j = 0; j < features; j++) {
333 // preallocate with random initial weights
334 W[i][j] = _random(-1, 1);
335 }
336 }
337 }
338
339 test_lamniscate(&X); // create test data around the lamniscate
340 save_nd_data("test2.csv", X); // save test data points
341 save_nd_data("w21.csv", W); // save initial random weights
342 kohonen_som_tracer(X, &W, 0.01); // train the SOM
343 save_nd_data("w22.csv", W); // save the resultant weights
344}
345
359void test_3d_classes(std::vector<std::valarray<double>> *data) {
360 const int N = data->size();
361 const double R = 0.1; // radius of cluster
362 int i = 0;
363 const int num_classes = 8;
364 const std::array<const std::array<double, 3>, num_classes> centres = {
365 // centres of each class cluster
366 std::array<double, 3>({.5, .5, .5}), // centre of class 0
367 std::array<double, 3>({.5, .5, -.5}), // centre of class 1
368 std::array<double, 3>({.5, -.5, .5}), // centre of class 2
369 std::array<double, 3>({.5, -.5, -.5}), // centre of class 3
370 std::array<double, 3>({-.5, .5, .5}), // centre of class 4
371 std::array<double, 3>({-.5, .5, -.5}), // centre of class 5
372 std::array<double, 3>({-.5, -.5, .5}), // centre of class 6
373 std::array<double, 3>({-.5, -.5, -.5}) // centre of class 7
374 };
375
376#ifdef _OPENMP
377#pragma omp for
378#endif
379 for (i = 0; i < N; i++) {
380 int cls =
381 std::rand() % num_classes; // select a random class for the point
382
383 // create random coordinates (x,y,z) around the centre of the class
384 data[0][i][0] = _random(centres[cls][0] - R, centres[cls][0] + R);
385 data[0][i][1] = _random(centres[cls][1] - R, centres[cls][1] + R);
386 data[0][i][2] = _random(centres[cls][2] - R, centres[cls][2] + R);
387
388 /* The follosing can also be used
389 for (int j = 0; j < 3; j++)
390 data[0][i][j] = _random(centres[cls][j] - R, centres[cls][j] + R);
391 */
392 }
393}
394
414void test3() {
415 int j = 0, N = 200;
416 int features = 3;
417 int num_out = 20;
418 std::vector<std::valarray<double>> X(N);
419 std::vector<std::valarray<double>> W(num_out);
420 for (int i = 0; i < std::max(num_out, N); i++) {
421 // loop till max(N, num_out)
422 if (i < N) { // only add new arrays if i < N
423 X[i] = std::valarray<double>(features);
424 }
425 if (i < num_out) { // only add new arrays if i < num_out
426 W[i] = std::valarray<double>(features);
427
428#ifdef _OPENMP
429#pragma omp for
430#endif
431 for (j = 0; j < features; j++) {
432 // preallocate with random initial weights
433 W[i][j] = _random(-1, 1);
434 }
435 }
436 }
437
438 test_3d_classes(&X); // create test data around the lamniscate
439 save_nd_data("test3.csv", X); // save test data points
440 save_nd_data("w31.csv", W); // save initial random weights
441 kohonen_som_tracer(X, &W, 0.01); // train the SOM
442 save_nd_data("w32.csv", W); // save the resultant weights
443}
444
452double get_clock_diff(clock_t start_t, clock_t end_t) {
453 return static_cast<double>(end_t - start_t) / CLOCKS_PER_SEC;
454}
455
457int main(int argc, char **argv) {
458#ifdef _OPENMP
459 std::cout << "Using OpenMP based parallelization\n";
460#else
461 std::cout << "NOT using OpenMP based parallelization\n";
462#endif
463
464 std::srand(std::time(nullptr));
465
466 std::clock_t start_clk = std::clock();
467 test1();
468 auto end_clk = std::clock();
469 std::cout << "Test 1 completed in " << get_clock_diff(start_clk, end_clk)
470 << " sec\n";
471
472 start_clk = std::clock();
473 test2();
474 end_clk = std::clock();
475 std::cout << "Test 2 completed in " << get_clock_diff(start_clk, end_clk)
476 << " sec\n";
477
478 start_clk = std::clock();
479 test3();
480 end_clk = std::clock();
481 std::cout << "Test 3 completed in " << get_clock_diff(start_clk, end_clk)
482 << " sec\n";
483
484 std::cout
485 << "(Note: Calculated times include: creating test sets, training "
486 "model and writing files to disk.)\n\n";
487 return 0;
488}
int main()
Main function.
int save_nd_data(const char *fname, const std::vector< std::valarray< double > > &X)
double _random(double a, double b)
int data[MAX]
test data
void test2()
void test1()
double get_clock_diff(clock_t start_t, clock_t end_t)
void test_lamniscate(std::vector< std::valarray< double > > *data)
void test3()
void test_3d_classes(std::vector< std::valarray< double > > *data)
void test_circle(std::vector< std::valarray< double > > *data)
A* search algorithm
void kohonen_som_tracer(const std::vector< std::valarray< double > > &X, std::vector< std::valarray< double > > *W, double alpha_min)
T sum(const std::vector< std::valarray< T > > &A)
double 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)