Source code for AMBER.iterativesom

import logging

import numpy as np

from .map import Map, vesanto_size

logger = logging.getLogger(__name__)


[docs] class IterativeSOM: """Trains multiple SOMs across a range of map sizes and optionally returns the best one.""" def __init__(self, data, period, initial_lr, size_range=None, give_best=False, random_seed=None, validation_data=None): """ :param data: numpy array of training data (samples x features) :param period: number of training iterations per map :param initial_lr: initial learning rate :param size_range: list or range of map sizes to try; defaults to ±2 around Vesanto size :param give_best: if True, self.best_map holds the map with lowest quantization error :param random_seed: base random seed for reproducibility; each map receives ``random_seed + i`` so maps are independent but deterministic :param validation_data: optional held-out data for model selection; if None, training data is used (standard SOM practice — QE is a reconstruction metric) """ if size_range is None: # Default range: ±2 around the Vesanto recommended size recommended = vesanto_size(data.shape[0]) size_range = range(max(2, recommended - 2), recommended + 3) self.maps = {} best_qe = np.inf self.best_map = None for i, size in enumerate(size_range): seed = random_seed + i if random_seed is not None else None m = Map(data=data, size=size, period=period, initial_lr=initial_lr, random_seed=seed) self.maps[size] = m if give_best: from .classification import Classification if validation_data is None: logger.warning( "IterativeSOM: model selection is evaluating QE on training data " "(validation_data=None). Pass validation_data= to avoid " "in-sample selection bias." ) eval_data = validation_data if validation_data is not None else data c = Classification(m, eval_data) if c.quantization_error < best_qe: best_qe = c.quantization_error self.best_map = m
[docs] @staticmethod def calculate_range(data, min_size=2, max_size=None): """Returns a range of map sizes centred on the Vesanto recommendation.""" recommended = vesanto_size(data.shape[0]) lo = max(min_size, recommended - 2) hi = recommended + 2 if max_size is None else min(max_size, recommended + 2) return range(lo, hi + 1)