Kohonenová sieť

Tags: C Kohonen

Úvod

Zadanie: Kohonenová sieť
Cieľ: Vytvoriť program v ľubovolnom programovacom jazyku, ktorý bude vyhľadavať zhluky na prezentovanej vzorke pomocou kohonenovej mapy.

Moje riešenie je napísané v jazyku v C a pre vykresľovanie využíva Gnuplot knižnicu. Ako testovacie dáta boli použité body špirály. Pri sieťi 10x10, veľkosťi trenovacej množiny 1500 a počtu cyklov 200 bežal program 11,323 sekúnd pod operačným systemom Debian. Program je možné skompilovať a spustiť aj pod operačným systémom Windows, problém ale spôsobuje matematická knižnica (presnejšie funkcia pow z hlavičkového súboru math.h), ktorá je pod windowsom značne pomalšia a program je niekoľkonásobne pomalší (z mojích testovaní pri uvedených vstupných parametroch bol program pod windowsom približne 8-krát pomalší). Ďalší problém mi pod windowsom spôsobovala funkcia rand, ktorá pod windowsom generovala zreteľne nižšie čísla a tým pádom som mal problém s náhodným výberom z testovacích vzoriek (different rand() results on Windows and Linux).

Okrem Gnuplot bola pre tvorbu .pbm súborov použitá aj knižnica ImageMagick.

Príklad výsledku pri uvedených vstupných parametroch:
Kohonenová sieť 10x10

ImageMagick

Konvertovanie obrázkov do .pbm formátu

convert.exe -compress none input.png output.pbm

Vytvorenie animovaného gif obrázku z viacerých obrázkov:

convert.exe -delay 100 input1.png input2.png inputN.png output.gif
  convert.exe -delay 100 input[1-9].png output.gif
  

Gnuplot

Pre vykresľovanie siete bola použitá knižnica Gnuplot a jednoduchý skript vytvorený pre túto knižnicu, ktorý nastavoval výstupný obrázok.

#plot.cfg
set terminal jpeg size picture_width,picture_height;
set output filename_output;
set lmargin 0
set rmargin 0
set tmargin 0
set bmargin 0
unset xtics;
unset ytics;
set yrange [0:picture_height] reverse
set xrange [0:picture_width]
plot bg_image binary filetype=jpg with rgbimage,\
     filename_input every ::0::1 with lp notitle lc rgb "green"
  

Tento súbor bol následne predavaný gnuplot aj spolu s definíciou použitých premenných.

sprintf(command, "gnuplot -e \"filename_output='output/plot_training_set_%d.jpg'; filename_input='output/plot_training_set_%d.txt'; picture_width=%d; picture_height=%d; bg_image='%s' \" plot_train.cfg", number, number, width, height, bg_image);

Vstupné dáta gnuplot boli vo formáte:

ciara1_bod1
ciara1_bod2

ciara2_bod1
ciara2_bod2

ciara3_bod1
ciara3_bod2
  

Kohonen, funkcia susednosti, zmena váh, hľadanie víťaza

Následne sú uvedené funkcie:

  • pre nájdenie víťaza
  • výpočet lambdy (funkcia susednosti)
  • zmeny váh
// neurons - pointer to network
// neurons_count - network size
// inputx - position x
// inputy - position y
// count - number of closest neurons to find
// RETURN: returns pointer to array of "count"-closest neurons
Neuron** XClosestNeuron(Neuron *neurons, int neurons_count, double inputx, double inputy, int count)
{
	Neuron **n = (Neuron**) malloc(sizeof(Neuron*) * count);
	double best = 0;
	int q,i;

	// Get closest
	n[0] = neurons;
	best = Distance(n[0]->weightx, n[0]->weighty, inputx, inputy);
	for(i = 1; i < neurons_count; i++)
	{
		if(Distance(neurons[i].weightx, neurons[i].weighty, inputx, inputy) < best)
		{
			n[0] = neurons+i;
			best = Distance(neurons[i].weightx, neurons[i].weighty, inputx, inputy);
		}
	}

	// Get rest (if needed)
	best = Distance(n[0]->weightx, n[0]->weighty, inputx, inputy);
	for(q = 1; q < count; q++)
	{
		double closest = 9999;
		for(i = 0; i < neurons_count; i++)
		{
			if( Distance(neurons[i].weightx, neurons[i].weighty, inputx, inputy) < closest && Distance(neurons[i].weightx, neurons[i].weighty, inputx, inputy) > best )
			{
				n[q] = neurons+i;
				closest = Distance(n[q]->weightx, n[q]->weighty, inputx, inputy);
			}
		}

		best = closest;
	}

	return n;
}

double Lambda(int i, int k, int width, double h, double r)
{
	int x_dist = abs(array2x(i, width) - array2x(k, width));
	int y_dist = abs(array2y(i, width) - array2y(k, width));
	
	double distance = sqrt( pow(x_dist, 2) + pow(y_dist, 2) );

	return h * pow(EULER, -distance/r);
}

// n - neuron to update
// http://www.willamette.edu/~gorr/classes/cs449/Unsupervised/SOM.html
// Wk(new) = Wk(old) + u*N(i,k) * (x - Wk)
// i-neuron is winner
//
// k - neuron to update
// learning_rate
// i - winning neuron
// x,y - training set positons
// neighborhood_width
// neighborhood_height
// network_width - width of network
void UpdateWeights(Neuron *k, double learning_rate, Neuron *i, double x,double y, double neighborhood_width, double neighborhood_height, int network_width)
{
	int watch = 0;
	int verbose = (k->id == watch) ? 0 : 0;
	//double neighborhood = Neighborhood2(k->weightx, k->weighty, i->weightx, i->weighty, neighborhood_width, neighborhood_height, verbose);
	double lambda = Lambda(k->id, i->id, network_width, neighborhood_height, neighborhood_width);
	
	if(k->id == watch && verbose == 1) printf("x:%.1f,y:%.1f; neigh:%.6f, set = (%.1f, %.1f);", k->weightx, k->weighty, lambda, x,y);

	k->weightx = k->weightx + learning_rate * lambda * (x - k->weightx);
	k->weighty = k->weighty + learning_rate * lambda * (y - k->weighty);
	if(k->id == watch && verbose == 1) printf(" new = (%.1f, %.1f)\n", k->weightx, k->weighty);
}

Spustenie

Projekt obsahuje README súbor kde je opísane spúštanie programu, okrem toho obsahuje makefile pre linux aj make_win.bat pre windows. Pre správny beh je potrebné mať v priečinku projektu vytvorený adresár "output". Pod windowsom je okrem toho ešte potrebné mať v adresári projektu skopirovanú knižnicu gnuplot, pod linuxom je potrebné mať knižnicu gnuplot nainštalovanú.

Discussion