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