som.py
1 """!
2 
3 @brief Neural Network: Self-Organized Feature Map
4 @details Implementation based on paper @cite article::nnet::som::1, @cite article::nnet::som::2.
5 
6 @authors Andrei Novikov (pyclustering@yandex.ru)
7 @date 2014-2019
8 @copyright GNU Public License
9 
10 @cond GNU_PUBLIC_LICENSE
11  PyClustering is free software: you can redistribute it and/or modify
12  it under the terms of the GNU General Public License as published by
13  the Free Software Foundation, either version 3 of the License, or
14  (at your option) any later version.
15 
16  PyClustering is distributed in the hope that it will be useful,
17  but WITHOUT ANY WARRANTY; without even the implied warranty of
18  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19  GNU General Public License for more details.
20 
21  You should have received a copy of the GNU General Public License
22  along with this program. If not, see <http://www.gnu.org/licenses/>.
23 @endcond
24 
25 """
26 
27 
28 import math
29 import random
30 import warnings
31 
32 try:
33  import matplotlib.pyplot as plt
34 except Exception as error_instance:
35  warnings.warn("Impossible to import matplotlib (please, install 'matplotlib'), pyclustering's visualization "
36  "functionality is not available (details: '%s')." % str(error_instance))
37 
38 import pyclustering.core.som_wrapper as wrapper
39 
40 from pyclustering.core.wrapper import ccore_library
41 
42 from pyclustering.utils import euclidean_distance_square
43 from pyclustering.utils.dimension import dimension_info
44 
45 from enum import IntEnum
46 
47 
48 class type_conn(IntEnum):
49  """!
50  @brief Enumeration of connection types for SOM.
51 
52  @see som
53 
54  """
55 
56 
57  grid_four = 0
58 
59 
60  grid_eight = 1
61 
62 
63  honeycomb = 2
64 
65 
66  func_neighbor = 3
67 
68 
69 class type_init(IntEnum):
70  """!
71  @brief Enumeration of initialization types for SOM.
72 
73  @see som
74 
75  """
76 
77 
78  random = 0
79 
80 
81  random_centroid = 1
82 
83 
84  random_surface = 2
85 
86 
87  uniform_grid = 3
88 
89 
91  """!
92  @brief Represents SOM parameters.
93 
94  """
95 
96  def __init__(self):
97  """!
98  @brief Constructor container of SOM parameters.
99 
100  """
101 
102 
103  self.init_type = type_init.uniform_grid
104 
105 
106  self.init_radius = None
107 
108 
109  self.init_learn_rate = 0.1
110 
111 
112  self.adaptation_threshold = 0.001
113 
114 
115 class som:
116  """!
117  @brief Represents self-organized feature map (SOM).
118  @details The self-organizing feature map (SOM) method is a powerful tool for the visualization of
119  of high-dimensional data. It converts complex, nonlinear statistical relationships between
120  high-dimensional data into simple geometric relationships on a low-dimensional display.
121 
122  @details CCORE option can be used to use the pyclustering core - C/C++ shared library for processing that significantly increases performance.
123 
124  Example:
125  @code
126  import random
127 
128  from pyclustering.utils import read_sample
129  from pyclustering.nnet.som import som, type_conn, type_init, som_parameters
130  from pyclustering.samples.definitions import FCPS_SAMPLES
131 
132  # read sample 'Lsun' from file
133  sample = read_sample(FCPS_SAMPLES.SAMPLE_LSUN)
134 
135  # create SOM parameters
136  parameters = som_parameters()
137 
138  # create self-organized feature map with size 7x7
139  rows = 10 # five rows
140  cols = 10 # five columns
141  structure = type_conn.grid_four; # each neuron has max. four neighbors.
142  network = som(rows, cols, structure, parameters)
143 
144  # train network on 'Lsun' sample during 100 epouchs.
145  network.train(sample, 100)
146 
147  # simulate trained network using randomly modified point from input dataset.
148  index_point = random.randint(0, len(sample) - 1)
149  point = sample[index_point] # obtain randomly point from data
150  point[0] += random.random() * 0.2 # change randomly X-coordinate
151  point[1] += random.random() * 0.2 # change randomly Y-coordinate
152  index_winner = network.simulate(point)
153 
154  # check what are objects from input data are much close to randomly modified.
155  index_similar_objects = network.capture_objects[index_winner]
156 
157  # neuron contains information of encoded objects
158  print("Point '%s' is similar to objects with indexes '%s'." % (str(point), str(index_similar_objects)))
159  print("Coordinates of similar objects:")
160  for index in index_similar_objects: print("\tPoint:", sample[index])
161 
162  # result visualization:
163  # show distance matrix (U-matrix).
164  network.show_distance_matrix()
165 
166  # show density matrix (P-matrix).
167  network.show_density_matrix()
168 
169  # show winner matrix.
170  network.show_winner_matrix()
171 
172  # show self-organized map.
173  network.show_network()
174  @endcode
175 
176  There is a visualization of 'Target' sample that was done by the self-organized feature map:
177  @image html target_som_processing.png
178 
179  """
180 
181 
182  @property
183  def size(self):
184  """!
185  @brief Return size of self-organized map that is defined by total number of neurons.
186 
187  @return (uint) Size of self-organized map (number of neurons).
188 
189  """
190 
191  if self.__ccore_som_pointer is not None:
192  self._size = wrapper.som_get_size(self.__ccore_som_pointer)
193 
194  return self._size
195 
196  @property
197  def weights(self):
198  """!
199  @brief Return weight of each neuron.
200 
201  @return (list) Weights of each neuron.
202 
203  """
204 
205  if self.__ccore_som_pointer is not None:
206  self._weights = wrapper.som_get_weights(self.__ccore_som_pointer)
207 
208  return self._weights
209 
210  @property
211  def awards(self):
212  """!
213  @brief Return amount of captured objects by each neuron after training.
214 
215  @return (list) Amount of captured objects by each neuron.
216 
217  @see train()
218 
219  """
220 
221  if self.__ccore_som_pointer is not None:
222  self._award = wrapper.som_get_awards(self.__ccore_som_pointer)
223 
224  return self._award
225 
226  @property
227  def capture_objects(self):
228  """!
229  @brief Returns indexes of captured objects by each neuron.
230  @details For example, network with size 2x2 has been trained on 5 sample, we neuron #1 has won one object with
231  index '1', neuron #2 - objects with indexes '0', '3', '4', neuron #3 - nothing, neuron #4 - object
232  with index '2'. Thus, output is [ [1], [0, 3, 4], [], [2] ].
233 
234  @return (list) Indexes of captured objects by each neuron.
235 
236  """
237 
238  if self.__ccore_som_pointer is not None:
239  self._capture_objects = wrapper.som_get_capture_objects(self.__ccore_som_pointer)
240 
241  return self._capture_objects
242 
243 
244  def __init__(self, rows, cols, conn_type = type_conn.grid_eight, parameters = None, ccore = True):
245  """!
246  @brief Constructor of self-organized map.
247 
248  @param[in] rows (uint): Number of neurons in the column (number of rows).
249  @param[in] cols (uint): Number of neurons in the row (number of columns).
250  @param[in] conn_type (type_conn): Type of connection between oscillators in the network (grid four, grid eight, honeycomb, function neighbour).
251  @param[in] parameters (som_parameters): Other specific parameters.
252  @param[in] ccore (bool): If True simulation is performed by CCORE library (C++ implementation of pyclustering).
253 
254  """
255 
256  # some of these parameters are required despite core implementation, for example, for network demonstration.
257  self._cols = cols
258 
259  self._rows = rows
260 
261  self._size = cols * rows
262 
263  self._conn_type = conn_type
264 
265  self._data = None
266 
267  self._neighbors = None
268 
269  self._local_radius = 0.0
270 
271  self._learn_rate = 0.0
272 
273  self.__ccore_som_pointer = None
274 
275  if parameters is not None:
276  self._params = parameters
277  else:
278  self._params = som_parameters()
279 
280  if self._params.init_radius is None:
281  self._params.init_radius = self.__initialize_initial_radius(rows, cols)
282 
283  if (ccore is True) and ccore_library.workable():
284  self.__ccore_som_pointer = wrapper.som_create(rows, cols, conn_type, self._params)
285 
286  else:
287  # location
288  self._location = self.__initialize_locations(rows, cols)
289 
290  # default weights
291  self._weights = [ [0.0] ] * self._size
292 
293  # awards
294  self._award = [0] * self._size
295 
296  # captured objects
297  self._capture_objects = [ [] for i in range(self._size) ]
298 
299  # distances - calculate and store them only during training
300  self._sqrt_distances = None
301 
302  # connections
303  if conn_type != type_conn.func_neighbor:
304  self._create_connections(conn_type)
305 
306 
307  def __del__(self):
308  """!
309  @brief Destructor of the self-organized feature map.
310 
311  """
312 
313  if self.__ccore_som_pointer is not None:
314  wrapper.som_destroy(self.__ccore_som_pointer)
315 
316 
317  def __len__(self):
318  """!
319  @brief Returns size of the network that defines by amount of neuron in it.
320 
321  @return (uint) Size of self-organized map (amount of neurons).
322 
323  """
324 
325  return self._size
326 
327 
328  def __getstate__(self):
329  """
330  @brief Returns state of SOM network that can be used to store network.
331 
332  """
333  if self.__ccore_som_pointer is not None:
335  return self.__get_dump_from_python(True)
336 
337  return self.__get_dump_from_python(False)
338 
339 
340  def __setstate__(self, som_state):
341  """
342  @brief Set state of SOM network that can be used to load network.
343 
344  """
345  if som_state['ccore'] is True and ccore_library.workable():
346  self.__upload_dump_to_ccore(som_state['state'])
347  else:
348  self.__upload_dump_to_python(som_state['state'])
349 
350 
351  def __initialize_initial_radius(self, rows, cols):
352  """!
353  @brief Initialize initial radius using map sizes.
354 
355  @param[in] rows (uint): Number of neurons in the column (number of rows).
356  @param[in] cols (uint): Number of neurons in the row (number of columns).
357 
358  @return (list) Value of initial radius.
359 
360  """
361 
362  if (cols + rows) / 4.0 > 1.0:
363  return 2.0
364 
365  elif (cols > 1) and (rows > 1):
366  return 1.5
367 
368  else:
369  return 1.0
370 
371 
372  def __initialize_locations(self, rows, cols):
373  """!
374  @brief Initialize locations (coordinates in SOM grid) of each neurons in the map.
375 
376  @param[in] rows (uint): Number of neurons in the column (number of rows).
377  @param[in] cols (uint): Number of neurons in the row (number of columns).
378 
379  @return (list) List of coordinates of each neuron in map.
380 
381  """
382 
383  location = list()
384  for i in range(rows):
385  for j in range(cols):
386  location.append([float(i), float(j)])
387 
388  return location
389 
390 
391  def __initialize_distances(self, size, location):
392  """!
393  @brief Initialize distance matrix in SOM grid.
394 
395  @param[in] size (uint): Amount of neurons in the network.
396  @param[in] location (list): List of coordinates of each neuron in the network.
397 
398  @return (list) Distance matrix between neurons in the network.
399 
400  """
401  sqrt_distances = [ [ [] for i in range(size) ] for j in range(size) ]
402  for i in range(size):
403  for j in range(i, size, 1):
404  dist = euclidean_distance_square(location[i], location[j])
405  sqrt_distances[i][j] = dist
406  sqrt_distances[j][i] = dist
407 
408  return sqrt_distances
409 
410 
411  def _create_initial_weights(self, init_type):
412  """!
413  @brief Creates initial weights for neurons in line with the specified initialization.
414 
415  @param[in] init_type (type_init): Type of initialization of initial neuron weights (random, random in center of the input data, random distributed in data, ditributed in line with uniform grid).
416 
417  """
418 
419  dim_info = dimension_info(self._data)
420 
421  step_x = dim_info.get_center()[0]
422  if self._rows > 1: step_x = dim_info.get_width()[0] / (self._rows - 1);
423 
424  step_y = 0.0
425  if dim_info.get_dimensions() > 1:
426  step_y = dim_info.get_center()[1]
427  if self._cols > 1: step_y = dim_info.get_width()[1] / (self._cols - 1);
428 
429  # generate weights (topological coordinates)
430  random.seed()
431 
432  # Uniform grid.
433  if init_type == type_init.uniform_grid:
434  # Predefined weights in line with input data.
435  self._weights = [ [ [] for i in range(dim_info.get_dimensions()) ] for j in range(self._size)]
436  for i in range(self._size):
437  location = self._location[i]
438  for dim in range(dim_info.get_dimensions()):
439  if dim == 0:
440  if self._rows > 1:
441  self._weights[i][dim] = dim_info.get_minimum_coordinate()[dim] + step_x * location[dim]
442  else:
443  self._weights[i][dim] = dim_info.get_center()[dim]
444 
445  elif dim == 1:
446  if self._cols > 1:
447  self._weights[i][dim] = dim_info.get_minimum_coordinate()[dim] + step_y * location[dim]
448  else:
449  self._weights[i][dim] = dim_info.get_center()[dim]
450  else:
451  self._weights[i][dim] = dim_info.get_center()[dim]
452 
453  elif init_type == type_init.random_surface:
454  # Random weights at the full surface.
455  self._weights = [[random.uniform(dim_info.get_minimum_coordinate()[i], dim_info.get_maximum_coordinate()[i]) for i in range(dim_info.get_dimensions())] for _ in range(self._size)]
456 
457  elif init_type == type_init.random_centroid:
458  # Random weights at the center of input data.
459  self._weights = [[(random.random() + dim_info.get_center()[i]) for i in range(dim_info.get_dimensions())] for _ in range(self._size)]
460 
461  else:
462  # Random weights of input data.
463  self._weights = [[random.random() for i in range(dim_info.get_dimensions())] for _ in range(self._size)]
464 
465 
466  def _create_connections(self, conn_type):
467  """!
468  @brief Create connections in line with input rule (grid four, grid eight, honeycomb, function neighbour).
469 
470  @param[in] conn_type (type_conn): Type of connection between oscillators in the network.
471 
472  """
473 
474  self._neighbors = [[] for index in range(self._size)]
475 
476  for index in range(0, self._size, 1):
477  upper_index = index - self._cols
478  upper_left_index = index - self._cols - 1
479  upper_right_index = index - self._cols + 1
480 
481  lower_index = index + self._cols
482  lower_left_index = index + self._cols - 1
483  lower_right_index = index + self._cols + 1
484 
485  left_index = index - 1
486  right_index = index + 1
487 
488  node_row_index = math.floor(index / self._cols)
489  upper_row_index = node_row_index - 1
490  lower_row_index = node_row_index + 1
491 
492  if (conn_type == type_conn.grid_eight) or (conn_type == type_conn.grid_four):
493  if upper_index >= 0:
494  self._neighbors[index].append(upper_index)
495 
496  if lower_index < self._size:
497  self._neighbors[index].append(lower_index)
498 
499  if (conn_type == type_conn.grid_eight) or (conn_type == type_conn.grid_four) or (conn_type == type_conn.honeycomb):
500  if (left_index >= 0) and (math.floor(left_index / self._cols) == node_row_index):
501  self._neighbors[index].append(left_index)
502 
503  if (right_index < self._size) and (math.floor(right_index / self._cols) == node_row_index):
504  self._neighbors[index].append(right_index)
505 
506 
507  if conn_type == type_conn.grid_eight:
508  if (upper_left_index >= 0) and (math.floor(upper_left_index / self._cols) == upper_row_index):
509  self._neighbors[index].append(upper_left_index)
510 
511  if (upper_right_index >= 0) and (math.floor(upper_right_index / self._cols) == upper_row_index):
512  self._neighbors[index].append(upper_right_index)
513 
514  if (lower_left_index < self._size) and (math.floor(lower_left_index / self._cols) == lower_row_index):
515  self._neighbors[index].append(lower_left_index)
516 
517  if (lower_right_index < self._size) and (math.floor(lower_right_index / self._cols) == lower_row_index):
518  self._neighbors[index].append(lower_right_index)
519 
520 
521  if conn_type == type_conn.honeycomb:
522  if (node_row_index % 2) == 0:
523  upper_left_index = index - self._cols
524  upper_right_index = index - self._cols + 1
525 
526  lower_left_index = index + self._cols
527  lower_right_index = index + self._cols + 1
528  else:
529  upper_left_index = index - self._cols - 1
530  upper_right_index = index - self._cols
531 
532  lower_left_index = index + self._cols - 1
533  lower_right_index = index + self._cols
534 
535  if (upper_left_index >= 0) and (math.floor(upper_left_index / self._cols) == upper_row_index):
536  self._neighbors[index].append(upper_left_index)
537 
538  if (upper_right_index >= 0) and (math.floor(upper_right_index / self._cols) == upper_row_index):
539  self._neighbors[index].append(upper_right_index)
540 
541  if (lower_left_index < self._size) and (math.floor(lower_left_index / self._cols) == lower_row_index):
542  self._neighbors[index].append(lower_left_index)
543 
544  if (lower_right_index < self._size) and (math.floor(lower_right_index / self._cols) == lower_row_index):
545  self._neighbors[index].append(lower_right_index)
546 
547 
548  def _competition(self, x):
549  """!
550  @brief Calculates neuron winner (distance, neuron index).
551 
552  @param[in] x (list): Input pattern from the input data set, for example it can be coordinates of point.
553 
554  @return (uint) Returns index of neuron that is winner.
555 
556  """
557 
558  index = 0
559  minimum = euclidean_distance_square(self._weights[0], x)
560 
561  for i in range(1, self._size, 1):
562  candidate = euclidean_distance_square(self._weights[i], x)
563  if candidate < minimum:
564  index = i
565  minimum = candidate
566 
567  return index
568 
569 
570  def _adaptation(self, index, x):
571  """!
572  @brief Change weight of neurons in line with won neuron.
573 
574  @param[in] index (uint): Index of neuron-winner.
575  @param[in] x (list): Input pattern from the input data set.
576 
577  """
578 
579  dimension = len(self._weights[0])
580 
581  if self._conn_type == type_conn.func_neighbor:
582  for neuron_index in range(self._size):
583  distance = self._sqrt_distances[index][neuron_index]
584 
585  if distance < self._local_radius:
586  influence = math.exp(-(distance / (2.0 * self._local_radius)))
587 
588  for i in range(dimension):
589  self._weights[neuron_index][i] = self._weights[neuron_index][i] + self._learn_rate * influence * (x[i] - self._weights[neuron_index][i])
590 
591  else:
592  for i in range(dimension):
593  self._weights[index][i] = self._weights[index][i] + self._learn_rate * (x[i] - self._weights[index][i])
594 
595  for neighbor_index in self._neighbors[index]:
596  distance = self._sqrt_distances[index][neighbor_index]
597  if distance < self._local_radius:
598  influence = math.exp(-(distance / (2.0 * self._local_radius)))
599 
600  for i in range(dimension):
601  self._weights[neighbor_index][i] = self._weights[neighbor_index][i] + self._learn_rate * influence * (x[i] - self._weights[neighbor_index][i])
602 
603 
604  def train(self, data, epochs, autostop=False):
605  """!
606  @brief Trains self-organized feature map (SOM).
607 
608  @param[in] data (list): Input data - list of points where each point is represented by list of features, for example coordinates.
609  @param[in] epochs (uint): Number of epochs for training.
610  @param[in] autostop (bool): Automatic termination of learining process when adaptation is not occurred.
611 
612  @return (uint) Number of learining iterations.
613 
614  """
615 
616  self._data = data
617 
618  if self.__ccore_som_pointer is not None:
619  return wrapper.som_train(self.__ccore_som_pointer, data, epochs, autostop)
620 
621  self._sqrt_distances = self.__initialize_distances(self._size, self._location)
622 
623  for i in range(self._size):
624  self._award[i] = 0
625  self._capture_objects[i].clear()
626 
627  # weights
628  self._create_initial_weights(self._params.init_type)
629 
630  previous_weights = None
631 
632  for epoch in range(1, epochs + 1):
633  # Depression term of coupling
634  self._local_radius = (self._params.init_radius * math.exp(-(epoch / epochs))) ** 2
635  self._learn_rate = self._params.init_learn_rate * math.exp(-(epoch / epochs))
636 
637  # Clear statistics
638  if autostop:
639  for i in range(self._size):
640  self._award[i] = 0
641  self._capture_objects[i].clear()
642 
643  for i in range(len(self._data)):
644  # Step 1: Competition:
645  index = self._competition(self._data[i])
646 
647  # Step 2: Adaptation:
648  self._adaptation(index, self._data[i])
649 
650  # Update statistics
651  if (autostop == True) or (epoch == epochs):
652  self._award[index] += 1
653  self._capture_objects[index].append(i)
654 
655  # Check requirement of stopping
656  if autostop:
657  if previous_weights is not None:
658  maximal_adaptation = self._get_maximal_adaptation(previous_weights)
659  if maximal_adaptation < self._params.adaptation_threshold:
660  return epoch
661 
662  previous_weights = [item[:] for item in self._weights]
663 
664  return epochs
665 
666 
667  def simulate(self, input_pattern):
668  """!
669  @brief Processes input pattern (no learining) and returns index of neuron-winner.
670  Using index of neuron winner catched object can be obtained using property capture_objects.
671 
672  @param[in] input_pattern (list): Input pattern.
673 
674  @return (uint) Returns index of neuron-winner.
675 
676  @see capture_objects
677 
678  """
679 
680  if self.__ccore_som_pointer is not None:
681  return wrapper.som_simulate(self.__ccore_som_pointer, input_pattern)
682 
683  return self._competition(input_pattern)
684 
685 
686  def _get_maximal_adaptation(self, previous_weights):
687  """!
688  @brief Calculates maximum changes of weight in line with comparison between previous weights and current weights.
689 
690  @param[in] previous_weights (list): Weights from the previous step of learning process.
691 
692  @return (double) Value that represents maximum changes of weight after adaptation process.
693 
694  """
695 
696  dimension = len(self._data[0])
697  maximal_adaptation = 0.0
698 
699  for neuron_index in range(self._size):
700  for dim in range(dimension):
701  current_adaptation = previous_weights[neuron_index][dim] - self._weights[neuron_index][dim]
702 
703  if current_adaptation < 0:
704  current_adaptation = -current_adaptation
705 
706  if maximal_adaptation < current_adaptation:
707  maximal_adaptation = current_adaptation
708 
709  return maximal_adaptation
710 
711 
712  def get_winner_number(self):
713  """!
714  @brief Calculates number of winner at the last step of learning process.
715 
716  @return (uint) Number of winner.
717 
718  """
719 
720  if self.__ccore_som_pointer is not None:
721  self._award = wrapper.som_get_awards(self.__ccore_som_pointer)
722 
723  winner_number = 0
724  for i in range(self._size):
725  if self._award[i] > 0:
726  winner_number += 1
727 
728  return winner_number
729 
730 
732  """!
733  @brief Shows gray visualization of U-matrix (distance matrix).
734 
735  @see get_distance_matrix()
736 
737  """
738  distance_matrix = self.get_distance_matrix()
739 
740  plt.imshow(distance_matrix, cmap = plt.get_cmap('hot'), interpolation='kaiser')
741  plt.title("U-Matrix")
742  plt.colorbar()
743  plt.show()
744 
745 
747  """!
748  @brief Calculates distance matrix (U-matrix).
749  @details The U-Matrix visualizes based on the distance in input space between a weight vector and its neighbors on map.
750 
751  @return (list) Distance matrix (U-matrix).
752 
753  @see show_distance_matrix()
754  @see get_density_matrix()
755 
756  """
757  if self.__ccore_som_pointer is not None:
758  self._weights = wrapper.som_get_weights(self.__ccore_som_pointer)
759 
760  if self._conn_type != type_conn.func_neighbor:
761  self._neighbors = wrapper.som_get_neighbors(self.__ccore_som_pointer)
762 
763  distance_matrix = [[0.0] * self._cols for i in range(self._rows)]
764 
765  for i in range(self._rows):
766  for j in range(self._cols):
767  neuron_index = i * self._cols + j
768 
769  if self._conn_type == type_conn.func_neighbor:
770  self._create_connections(type_conn.grid_eight)
771 
772  for neighbor_index in self._neighbors[neuron_index]:
773  distance_matrix[i][j] += euclidean_distance_square(self._weights[neuron_index], self._weights[neighbor_index])
774 
775  distance_matrix[i][j] /= len(self._neighbors[neuron_index])
776 
777  return distance_matrix
778 
779 
780  def show_density_matrix(self, surface_divider = 20.0):
781  """!
782  @brief Show density matrix (P-matrix) using kernel density estimation.
783 
784  @param[in] surface_divider (double): Divider in each dimension that affect radius for density measurement.
785 
786  @see show_distance_matrix()
787 
788  """
789  density_matrix = self.get_density_matrix(surface_divider)
790 
791  plt.imshow(density_matrix, cmap = plt.get_cmap('hot'), interpolation='kaiser')
792  plt.title("P-Matrix")
793  plt.colorbar()
794  plt.show()
795 
796 
797  def get_density_matrix(self, surface_divider = 20.0):
798  """!
799  @brief Calculates density matrix (P-Matrix).
800 
801  @param[in] surface_divider (double): Divider in each dimension that affect radius for density measurement.
802 
803  @return (list) Density matrix (P-Matrix).
804 
805  @see get_distance_matrix()
806 
807  """
808 
809  if self.__ccore_som_pointer is not None:
810  self._weights = wrapper.som_get_weights(self.__ccore_som_pointer)
811 
812  density_matrix = [[0] * self._cols for i in range(self._rows)]
813  dimension = len(self._weights[0])
814 
815  dim_max = [ float('-Inf') ] * dimension
816  dim_min = [ float('Inf') ] * dimension
817 
818  for weight in self._weights:
819  for index_dim in range(dimension):
820  if weight[index_dim] > dim_max[index_dim]:
821  dim_max[index_dim] = weight[index_dim]
822 
823  if weight[index_dim] < dim_min[index_dim]:
824  dim_min[index_dim] = weight[index_dim]
825 
826  radius = [0.0] * len(self._weights[0])
827  for index_dim in range(dimension):
828  radius[index_dim] = ( dim_max[index_dim] - dim_min[index_dim] ) / surface_divider
829 
830 
831  for point in self._data:
832  for index_neuron in range(len(self)):
833  point_covered = True
834 
835  for index_dim in range(dimension):
836  if abs(point[index_dim] - self._weights[index_neuron][index_dim]) > radius[index_dim]:
837  point_covered = False
838  break
839 
840  row = int(math.floor(index_neuron / self._cols))
841  col = index_neuron - row * self._cols
842 
843  if point_covered is True:
844  density_matrix[row][col] += 1
845 
846  return density_matrix
847 
848 
850  """!
851  @brief Show winner matrix where each element corresponds to neuron and value represents
852  amount of won objects from input dataspace at the last training iteration.
853 
854  @see show_distance_matrix()
855 
856  """
857 
858  if self.__ccore_som_pointer is not None:
859  self._award = wrapper.som_get_awards(self.__ccore_som_pointer)
860 
861  (fig, ax) = plt.subplots()
862  winner_matrix = [[0] * self._cols for i in range(self._rows)]
863 
864  for i in range(self._rows):
865  for j in range(self._cols):
866  neuron_index = i * self._cols + j
867 
868  winner_matrix[i][j] = self._award[neuron_index]
869  ax.text(i, j, str(winner_matrix[i][j]), va='center', ha='center')
870 
871  ax.imshow(winner_matrix, cmap = plt.get_cmap('cool'), interpolation='none')
872  ax.grid(True)
873 
874  plt.title("Winner Matrix")
875  plt.show()
876 
877 
878  def show_network(self, awards = False, belongs = False, coupling = True, dataset = True, marker_type = 'o'):
879  """!
880  @brief Shows neurons in the dimension of data.
881 
882  @param[in] awards (bool): If True - displays how many objects won each neuron.
883  @param[in] belongs (bool): If True - marks each won object by according index of neuron-winner (only when dataset is displayed too).
884  @param[in] coupling (bool): If True - displays connections between neurons (except case when function neighbor is used).
885  @param[in] dataset (bool): If True - displays inputs data set.
886  @param[in] marker_type (string): Defines marker that is used for dispaying neurons in the network.
887 
888  """
889 
890  if self.__ccore_som_pointer is not None:
891  self._size = wrapper.som_get_size(self.__ccore_som_pointer)
892  self._weights = wrapper.som_get_weights(self.__ccore_som_pointer)
893  self._neighbors = wrapper.som_get_neighbors(self.__ccore_som_pointer)
894  self._award = wrapper.som_get_awards(self.__ccore_som_pointer)
895 
896  dimension = len(self._weights[0])
897 
898  fig = plt.figure()
899 
900  # Check for dimensions
901  if (dimension == 1) or (dimension == 2):
902  axes = fig.add_subplot(111)
903  elif dimension == 3:
904  axes = fig.gca(projection='3d')
905  else:
906  raise NotImplementedError('Impossible to show network in data-space that is differ from 1D, 2D or 3D.')
907 
908  if (self._data is not None) and (dataset is True):
909  for x in self._data:
910  if dimension == 1:
911  axes.plot(x[0], 0.0, 'b|', ms = 30)
912 
913  elif dimension == 2:
914  axes.plot(x[0], x[1], 'b.')
915 
916  elif dimension == 3:
917  axes.scatter(x[0], x[1], x[2], c = 'b', marker = '.')
918 
919  # Show neurons
920  for index in range(self._size):
921  color = 'g'
922  if self._award[index] == 0:
923  color = 'y'
924 
925  if dimension == 1:
926  axes.plot(self._weights[index][0], 0.0, color + marker_type)
927 
928  if awards:
929  location = '{0}'.format(self._award[index])
930  axes.text(self._weights[index][0], 0.0, location, color='black', fontsize = 10)
931 
932  if belongs and self._data is not None:
933  location = '{0}'.format(index)
934  axes.text(self._weights[index][0], 0.0, location, color='black', fontsize = 12)
935  for k in range(len(self._capture_objects[index])):
936  point = self._data[self._capture_objects[index][k]]
937  axes.text(point[0], 0.0, location, color='blue', fontsize = 10)
938 
939  if dimension == 2:
940  axes.plot(self._weights[index][0], self._weights[index][1], color + marker_type)
941 
942  if awards:
943  location = '{0}'.format(self._award[index])
944  axes.text(self._weights[index][0], self._weights[index][1], location, color='black', fontsize=10)
945 
946  if belongs and self._data is not None:
947  location = '{0}'.format(index)
948  axes.text(self._weights[index][0], self._weights[index][1], location, color='black', fontsize=12)
949  for k in range(len(self._capture_objects[index])):
950  point = self._data[self._capture_objects[index][k]]
951  axes.text(point[0], point[1], location, color='blue', fontsize=10)
952 
953  if (self._conn_type != type_conn.func_neighbor) and (coupling != False):
954  for neighbor in self._neighbors[index]:
955  if neighbor > index:
956  axes.plot([self._weights[index][0], self._weights[neighbor][0]],
957  [self._weights[index][1], self._weights[neighbor][1]],
958  'g', linewidth=0.5)
959 
960  elif dimension == 3:
961  axes.scatter(self._weights[index][0], self._weights[index][1], self._weights[index][2], c=color, marker=marker_type)
962 
963  if (self._conn_type != type_conn.func_neighbor) and (coupling != False):
964  for neighbor in self._neighbors[index]:
965  if neighbor > index:
966  axes.plot([self._weights[index][0], self._weights[neighbor][0]],
967  [self._weights[index][1], self._weights[neighbor][1]],
968  [self._weights[index][2], self._weights[neighbor][2]],
969  'g-', linewidth=0.5)
970 
971  plt.title("Network Structure")
972  plt.grid()
973  plt.show()
974 
975 
976  def __get_dump_from_python(self, ccore_usage):
977  return { 'ccore': ccore_usage,
978  'state' : { 'cols': self._cols,
979  'rows': self._rows,
980  'size': self._size,
981  'conn_type': self._conn_type,
982  'neighbors': self._neighbors,
983  'local_radius': self._local_radius,
984  'learn_rate': self._learn_rate,
985  'params': self._params,
986  'location': self._location,
987  'weights': self._weights,
988  'award': self._award,
989  'capture_objects': self._capture_objects } }
990 
991 
992  def __download_dump_from_ccore(self):
993  self._location = self.__initialize_locations(self._rows, self._cols)
994  self._weights = wrapper.som_get_weights(self.__ccore_som_pointer)
995  self._award = wrapper.som_get_awards(self.__ccore_som_pointer)
996  self._capture_objects = wrapper.som_get_capture_objects(self.__ccore_som_pointer)
997 
998 
999  def __upload_common_part(self, state_dump):
1000  self._cols = state_dump['cols']
1001  self._rows = state_dump['rows']
1002  self._size = state_dump['size']
1003  self._conn_type = state_dump['conn_type']
1004  self._neighbors = state_dump['neighbors']
1005  self._local_radius = state_dump['local_radius']
1006  self._learn_rate = state_dump['learn_rate']
1007  self._params = state_dump['params']
1008  self._neighbors = None
1009 
1010 
1011  def __upload_dump_to_python(self, state_dump):
1012  self.__ccore_som_pointer = None
1013 
1014  self.__upload_common_part(state_dump)
1015 
1016  self._location = state_dump['location']
1017  self._weights = state_dump['weights']
1018  self._award = state_dump['award']
1019  self._capture_objects = state_dump['capture_objects']
1020 
1021  self._location = self.__initialize_locations(self._rows, self._cols)
1022  self._create_connections(self._conn_type)
1023 
1024 
1025  def __upload_dump_to_ccore(self, state_dump):
1026  self.__upload_common_part(state_dump)
1027  self.__ccore_som_pointer = wrapper.som_create(self._rows, self._cols, self._conn_type, self._params)
1028  wrapper.som_load(self.__ccore_som_pointer, state_dump['weights'], state_dump['award'], state_dump['capture_objects'])
def show_winner_matrix(self)
Show winner matrix where each element corresponds to neuron and value represents amount of won object...
Definition: som.py:849
def __upload_dump_to_ccore(self, state_dump)
Definition: som.py:1025
def __get_dump_from_python(self, ccore_usage)
Definition: som.py:976
def awards(self)
Return amount of captured objects by each neuron after training.
Definition: som.py:211
def size(self)
Return size of self-organized map that is defined by total number of neurons.
Definition: som.py:183
init_learn_rate
Rate of learning.
Definition: som.py:109
def _adaptation(self, index, x)
Change weight of neurons in line with won neuron.
Definition: som.py:570
def show_density_matrix(self, surface_divider=20.0)
Show density matrix (P-matrix) using kernel density estimation.
Definition: som.py:780
def _get_maximal_adaptation(self, previous_weights)
Calculates maximum changes of weight in line with comparison between previous weights and current wei...
Definition: som.py:686
Enumeration of initialization types for SOM.
Definition: som.py:69
def __len__(self)
Returns size of the network that defines by amount of neuron in it.
Definition: som.py:317
Utils that are used by modules of pyclustering.
Definition: __init__.py:1
Represents SOM parameters.
Definition: som.py:90
def weights(self)
Return weight of each neuron.
Definition: som.py:197
def __initialize_initial_radius(self, rows, cols)
Initialize initial radius using map sizes.
Definition: som.py:351
def _create_connections(self, conn_type)
Create connections in line with input rule (grid four, grid eight, honeycomb, function neighbour)...
Definition: som.py:466
def show_distance_matrix(self)
Shows gray visualization of U-matrix (distance matrix).
Definition: som.py:731
def __initialize_locations(self, rows, cols)
Initialize locations (coordinates in SOM grid) of each neurons in the map.
Definition: som.py:372
def __initialize_distances(self, size, location)
Initialize distance matrix in SOM grid.
Definition: som.py:391
def train(self, data, epochs, autostop=False)
Trains self-organized feature map (SOM).
Definition: som.py:604
def show_network(self, awards=False, belongs=False, coupling=True, dataset=True, marker_type='o')
Shows neurons in the dimension of data.
Definition: som.py:878
init_type
Type of initialization of initial neuron weights (random, random in center of the input data...
Definition: som.py:103
def _create_initial_weights(self, init_type)
Creates initial weights for neurons in line with the specified initialization.
Definition: som.py:411
def get_distance_matrix(self)
Calculates distance matrix (U-matrix).
Definition: som.py:746
def __download_dump_from_ccore(self)
Definition: som.py:992
def get_density_matrix(self, surface_divider=20.0)
Calculates density matrix (P-Matrix).
Definition: som.py:797
def __upload_dump_to_python(self, state_dump)
Definition: som.py:1011
def __init__(self, rows, cols, conn_type=type_conn.grid_eight, parameters=None, ccore=True)
Constructor of self-organized map.
Definition: som.py:244
Enumeration of connection types for SOM.
Definition: som.py:48
def _competition(self, x)
Calculates neuron winner (distance, neuron index).
Definition: som.py:548
adaptation_threshold
Condition when learining process should be stoped.
Definition: som.py:112
def __getstate__(self)
Definition: som.py:328
def get_winner_number(self)
Calculates number of winner at the last step of learning process.
Definition: som.py:712
def __setstate__(self, som_state)
Definition: som.py:340
init_radius
Initial radius (if not specified then will be calculated by SOM).
Definition: som.py:106
def simulate(self, input_pattern)
Processes input pattern (no learining) and returns index of neuron-winner.
Definition: som.py:667
def capture_objects(self)
Returns indexes of captured objects by each neuron.
Definition: som.py:227
def __del__(self)
Destructor of the self-organized feature map.
Definition: som.py:307
def __init__(self)
Constructor container of SOM parameters.
Definition: som.py:96
def __upload_common_part(self, state_dump)
Definition: som.py:999
Represents self-organized feature map (SOM).
Definition: som.py:115