A map is a lower dimensional discretised representation of some input space. The self organising map (SOM) is a kind of artificial neural network. It differs in the sense of using a neighbourhood function which preserves the topological properties of the input space. This makes the SOM applicable for visualisation of high-dimensional data.

The SOM consists of a grid of nodes. Each node holds a randomised weight vector of the same dimension as the training/input data. The nodes are connected with another through a neighbourhood function, e.g. the Gaussian neighbourhood function.

During the training of the SOM, input vectors are assigned to the closest node based on some kind of distance metric. The weight vector of all nodes is updated towards the assigned input vector by \[ w_i(t+1) = w_i(t) + \Theta(d(v_i,BMU),t)\gamma(t)(In(t)-w_i(t)) \] where the weight vector \( w_i \) is updated dependent on the neighbourhood scaling \(\Theta(v_i,t)\) which depends on the distance between the updating neuron \(v_i\) and the Best Matching Unit (\(BMU\)), a decreasing learning coefficient \(\gamma(t)\) and the difference between Input Vector (\(In(t\)) and the present weight vector \(w_i\). The training is done sequentially with a great number of input vectors. During each step all nodes are by some degree moved due to the neighbourhood function. This leads to similar features mapped close together and dissimilar features apart. This way the SOM forms a semantic map over the feature space of the input data with more nodes pointing to feature reach parts of the input space. This characteristic can be used for visualisation by calculating an U-matrix (unified distance matrix). The U-matrix consists of the euclidean distance between neighbouring weights which can be represented in a grey scale picture where spots of light colour show clusters of near weights and therefore of the higher dimensional input data. After the training phase, input data can be mapped.

The following Python code is a simple implementation of a SOM to map pictures to a reduced colour space. The neighbourhood function is a simple radius function with a constant weight within the radius. The training is done with sampled pixels from the input image.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85

#!/usr/bin/python
# -*- coding: utf-8 -*-
# pictureSOM.py
# Version: 1
# Date: 25/09/2011
# Author: Jan Teichmann
"""
python pictureSOM.py IMAGE SOM_SIZE
"""
import numpy
from PIL import Image
import sys
def draw(arr):
size = (arr.shape[0], arr.shape[1])
arr = arr.reshape(arr.shape[0]*arr.shape[1], arr.shape[2])
arr = arr.astype(numpy.uint8)
return Image.frombuffer("RGB", size, arr.tostring(), 'raw', mode, 0, 1)
def trainingsVec(data):
V,H,c = data.shape
y = int(H*numpy.random.rand())
x = int(V*numpy.random.rand())
return data[x,y]
def BMU(som, vec):
dist = numpy.sqrt( numpy.square(som - vec).sum(2) )
H = som.shape[1]
v,h = divmod(dist.argmin(), H)
return (v,h)
def SOM_update(som, vec, gamma, bmu):
V, H, n = som.shape
ind = numpy.indices((V,H))
ind[0] -= bmu[0]
ind[1] -= bmu[1]
dist = numpy.sqrt(numpy.square(ind[0]) + numpy.square(ind[1]))
mask = numpy.less(dist, gamma).astype(float)
mask *= 0.01 # weight
mask = numpy.multiply.outer(mask, numpy.ones(n,int))
som += mask * (vec - som)
return som
def iteration(data, som, gamma_init, rate):
gamma = gamma_init
i = 1
while gamma > 1:
vec = trainingsVec(data)
bmu = BMU(som, vec)
som = SOM_update(som, vec, gamma, bmu)
gamma *= rate
i += 1
return som
def readImage(f):
img = Image.open(f)
arr = numpy.array(img.getdata())
arr = arr.reshape(img.size[0], img.size[1], 3)
return arr
def mapping(data, som):
V,H,n = data.shape
arr = numpy.zeros((V,H,n), numpy.uint8)
for i in range(V):
for j in range(H):
vec = data[i,j]
bmu = BMU(som, vec)
arr[i,j] = som[bmu[0], bmu[1]]
img = draw(arr)
return img
if __name__ == "__main__":
data = readImage(sys.argv[1])
origImg = draw(data)
origImg.show()
som = numpy.random.random_sample((int(sys.argv[2]),int(sys.argv[2]),3))
som = iteration(data, som, 30, 0.999)
somImg = draw(som)
somImg.show()
mapImg = mapping(data, som)
mapImg.show()