Skip to content

ufvceiec/GEMA

Repository files navigation

GEMA — Self-Organizing Maps in Python

Tests Python PyPI License

GEMA (GEnerador de Mapas Autoasociativos) is an open-source Python library for building, training, and analysing Self-Organizing Maps (SOMs / Kohonen maps). It covers the full workflow: data normalisation → training → classification → quality metrics → interactive visualisation.

Cite as:
García-Tejedor, Á. J., & Nogales, A. (2022). An Open-Source Python Library for Self-Organizing-Maps. Software Impacts, 12. https://doi.org/10.1016/j.simpa.2022.100280


Table of Contents


Features

  • Train a SOM from scratch with a single call
  • Rectangular and hexagonal grid topologies
  • Sequential or random data presentation
  • Euclidean and Chebyshev distance metrics
  • Neighbourhood decay
  • Four normalization strategies (none, FWN, 0-1 scale, Euclidean — fully vectorised)
  • Four weight-initialization strategies (random, random_negative, sample, PCA)
  • Save / load trained models as JSON
  • Classification with topological and quantization error metrics
  • U-matrix computation
  • Interactive Plotly visualisations (heat map, elevation map, codebook vectors)
  • Static Matplotlib visualisations (characteristics graph, bar graph, full weight map)
  • IterativeSOM — automatically explores a range of map sizes and picks the best

Installation

pip install GEMA

Conda (Windows x64):

conda install -c ceiecadmin gema

Quick Start

import numpy as np
from GEMA import Map, Classification, Visualization

# 1. Load your data (samples × features)
data = np.loadtxt('my_data.csv', delimiter=',')

# 2. Train a 10×10 SOM for 5000 iterations
som = Map(
    data=data,
    size=10,
    period=5000,
    initial_lr=0.1,
    distance='euclidean',
    normalization='none',
    weights='random'
)

# 3. Save the trained model
som.save_classifier('my_model')

# 4. Classify data
classification = Classification(som, data)
print('Topological error :', classification.topological_error)
print('Quantization error:', classification.quantization_error)

# 5. Visualise
Visualization.heat_map(classification)
Visualization.elevation_map(classification)

Load a pre-trained model

som = Map.load_classifier('my_model')

Hardware Acceleration

GEMA supports two optional acceleration back-ends that are activated automatically when the relevant package is installed. The public API — including Classification, save_classifier, and all Visualization methods — is identical regardless of which back-end is used.

CPU / numba

Install numba once and every future training run on CPU is automatically JIT-compiled and parallelised across all cores:

pip install numba
# or via the GEMA extras shortcut:
pip install "GEMA[cpu]"

No code change needed — GEMA detects numba at import time:

from GEMA import device_info
print(device_info())
# • NumPy (always available)
# • numba 0.59.0 (CPU JIT, parallel)
# • CuPy NOT installed

Benchmark (10×10 map, 5 000 D=20 samples, 10 000 iterations, Apple M2):

Back-end Time
NumPy (baseline) 4.8 s
numba (parallel) 1.1 s

GPU / CuPy

Install CuPy matching your CUDA version:

pip install cupy-cuda12x   # CUDA 12.x
pip install cupy-cuda11x   # CUDA 11.x

Then pass device='gpu' to Map:

som = Map(
    data=data,
    size=10,
    period=50_000,
    initial_lr=0.1,
    device='gpu',          # ← run on GPU
)
# Classification, save/load, and Visualization work with no changes:
classification = Classification(som, data)
som.save_classifier('my_model')

The weight tensor and training data are transferred to the GPU once at the start of training and copied back to CPU automatically when training finishes. All downstream code (classification, plotting, JSON export) continues to use plain NumPy arrays.

Benchmark (20×20 map, 50 000 D=50 samples, 100 000 iterations, NVIDIA RTX 3090):

Back-end Time
NumPy CPU 210 s
numba CPU parallel 48 s
CuPy GPU 9 s

Note: GPU acceleration is most effective for large maps (≥ 15×15) and high-dimensional data (D ≥ 20). For small maps the GPU transfer overhead may outweigh the benefit.


API Reference

Map

Map(data=None, size, period, initial_lr, initial_neighbourhood=0,
    distance='euclidean', use_decay=False, normalization='none',
    presentation='random', weights='random', topology='rectangular',
    device='cpu')
Parameter Type Default Description
data np.ndarray (N×D) None Training data. If provided, training starts immediately.
size int Side length of the square map (minimum 2).
period int 10 Number of training iterations.
initial_lr float 0.1 Initial learning rate (0 < lr < 1).
initial_neighbourhood int 0 Initial neighbourhood radius. Defaults to size when 0.
distance str 'euclidean' Distance metric: 'euclidean' or 'chebyshev'.
use_decay bool False Apply Gaussian decay to neighbour weight updates.
normalization str 'none' Input normalization strategy (see Normalization Options).
presentation str 'random' Data presentation order: 'random' or 'sequential'.
weights str 'random' Weight initialization method (see Weight Initialization Options).
topology str 'rectangular' Grid layout: 'rectangular' (default) or 'hexagonal'. Hexagonal grids give each neuron 6 equidistant neighbours, producing more uniform cluster boundaries.
device str 'cpu' Compute device: 'cpu' (NumPy; numba JIT if installed) or 'gpu' (CuPy CUDA). See Hardware Acceleration.

Key methods:

Method Description
train(data) Train the map on data.
reinforce(data, reinforcement, extension, compression) Continue training with a compressed learning rate for fine-tuning.
calculate_bmu(pattern) Return BMU distance, position, second-BMU distance and position.
save_classifier(filename) Save the trained model to <filename>.json.
Map.load_classifier(filename) Class method — load a model from <filename>.json.

Hexagonal topology example

som = Map(
    data=data,
    size=10,
    period=5000,
    initial_lr=0.1,
    topology='hexagonal',   # ← hex grid
    weights='PCA',
)

Classification

Classification(som, classification_data, other=None, tagged=False, verbose=1)
Parameter Type Description
som Map Trained SOM.
classification_data np.ndarray Data to classify.
other pd.DataFrame Optional extra columns to attach to the result table.
tagged bool If True, first column of classification_data is treated as labels.
verbose int 0 = silent, 1 = progress bar, 2 = debug output.

Key attributes after classification:

Attribute Description
activations_map 2-D array — how many samples each neuron won.
distances_map 2-D array — cumulative quantization distances.
topological_error Fraction of samples whose 2nd BMU is not adjacent to the 1st.
quantization_error Mean distance between each sample and its BMU.
classification_map Pandas DataFrame with label, coordinates and distance for each sample.
umatriz U-matrix (unified distance matrix).

IterativeSOM

Trains a family of SOMs of different sizes and selects the one with the lowest quantization error.

from GEMA import IterativeSOM

isom = IterativeSOM(
    data=data,
    period=2000,
    initial_lr=0.1,
    range_from=np.array([5, 15]),  # explore sizes 5×5 → 15×15
    try_best=True,                 # evaluate and pick the best
)

best = isom.get_best_map()         # Map instance with lowest error
print(isom.get_scores())           # {size: quantization_error, ...}
Parameter Type Default Description
data np.ndarray (N×D) Training data.
period int Training iterations per map.
initial_lr float Initial learning rate.
range_from np.ndarray [min, max] [0, 0] Size range to explore. When [0, 0] a range is derived automatically from the dataset size.
try_best bool False Evaluate every map and store the best.
give_best bool False Alias for try_best.
**map_kwargs Any extra arguments forwarded to each Map() call (topology, distance, etc.).
Method Description
get_best_map() Return the Map with the lowest quantization error (None if try_best was not set).
get_scores() Return {size: quantization_error} dict.
IterativeSOM.calculate_range(data) Static method — compute a sensible [min, max] size range from the data.

Visualization

All methods are @staticmethod.

Method Description
heat_map(classification, ...) Interactive Plotly heat map of activations.
elevation_map(classification, ...) Interactive Plotly 3-D elevation map.
characteristics_graph(map, row, col, labels, ...) Line plot of a single neuron's weight vector.
characteristics_bargraph(map, row, col, labels, ...) Bar chart of a single neuron's weight vector.
codebook_vector(map, index, header, ...) Annotated heat map for one feature across the whole map.
codebook_vectors(map, headers) Render all codebook vectors.
umatrix(classification, colorscale) Matplotlib U-matrix plot.
full_map_weights(map, labels, ...) Grid of weight plots for every neuron.
neurons_per_num_activations_map(classification, ...) Bar chart of activation frequency distribution.

Normalization Options

Value Description
'none' No normalization applied.
'fwn' Feature-wise normalization (zero mean, unit variance per feature).
'01scale' Scale all values to the [0, 1] interval.
'euclidean' Euclidean (L2) normalization of each sample vector.

Recommendation: normalize your data before passing it to GEMA rather than relying on the built-in options.


Weight Initialization Options

Value Description
'random' Uniform random values in [0, 1].
'random_negative' Uniform random values in [−1, 1].
'sample' Random values sampled directly from the training data. Useful for unnormalized data.
'PCA' Weights initialised along the hyperplane spanned by the two largest principal components.

Requirements

numpy
tqdm
pandas
matplotlib
plotly
scikit-learn
scipy

Install all at once:

pip install -r requirements.txt

Comparison with other SOM libraries

Training time

Best of 3 runs per configuration. GEMA times shown for all three back-ends.

Library Small (500×4, 5×5, 1 k iter) Medium (2 000×10, 8×8, 5 k iter) Large (5 000×20, 10×10, 10 k iter)
GEMA — numba CPU 0.011 s 0.051 s 0.098 s
GEMA — CuPy GPU 🚀 0.008 s 0.021 s 0.039 s
MiniSom 0.017 s 0.096 s 0.229 s
sklearn-som 0.061 s 0.384 s 0.728 s

GEMA and MiniSom both use an online (one-sample-per-step) update rule. sklearn-som uses full-batch epochs (iteration count converted to epochs = iterations / N). CPU times measured on Apple M-series (all cores); GPU times on NVIDIA RTX 3090. Lower is better.

Feature comparison

Feature GEMA MiniSom sklearn-som
Rectangular topology
Hexagonal topology
CPU JIT (numba)
GPU acceleration (CUDA)
Classification module
Topological & quantization error
U-matrix
Interactive visualisation (Plotly)
Automatic map-size search
Save / load (JSON)
Reinforcement learning
Install size (core deps) Medium Minimal Small

Contributing

  1. Fork the repository and create a feature branch.
  2. Make your changes and add tests where applicable.
  3. Open a pull request describing the change and its motivation.

Bug reports and feature requests are welcome via the issue tracker.
Mailing list: gema-som@googlegroups.com


Contact

Role Name Email
Responsible Alberto Nogales alberto.nogales@ceiec.es
Supervisor Álvaro José García-Tejedor
Developers Adrián Prieto, Gonzalo de las Heras de Matías, Antonio Pérez Morales
Contributors Santiago Donaher Naranjo, Afonso Reis (IST Lisbon)

License

Under license of CEIEC — http://www.ceiec.es

About

A small Python 3 library to train Self Organizing Maps and use them to classify patterns.

Topics

Resources

License

Contributing

Stars

27 stars

Watchers

5 watching

Forks

Packages

 
 
 

Contributors

Languages