Difference between revisions of "User:Chase-san/KohonenMap"
Jump to navigation
Jump to search
(My implementation) |
(use <syntaxhighlight>) |
||
Line 2: | Line 2: | ||
===org.csdgn.nn.KohonenMap=== | ===org.csdgn.nn.KohonenMap=== | ||
− | < | + | <syntaxhighlight> |
package org.csdgn.nn; | package org.csdgn.nn; | ||
Line 267: | Line 267: | ||
} | } | ||
} | } | ||
− | </ | + | </syntaxhighlight> |
===org.csdgn.nn.DensityFunction=== | ===org.csdgn.nn.DensityFunction=== | ||
− | < | + | <syntaxhighlight> |
package org.csdgn.nn; | package org.csdgn.nn; | ||
Line 279: | Line 279: | ||
public double calculate(double x); | public double calculate(double x); | ||
} | } | ||
− | </ | + | </syntaxhighlight> |
===org.csdgn.nn.DistanceFunction=== | ===org.csdgn.nn.DistanceFunction=== | ||
− | < | + | <syntaxhighlight> |
package org.csdgn.nn; | package org.csdgn.nn; | ||
Line 291: | Line 291: | ||
public double calculate(double[] p, double[] q); | public double calculate(double[] p, double[] q); | ||
} | } | ||
− | </ | + | </syntaxhighlight> |
===org.csdgn.nn.density.StandardDensity=== | ===org.csdgn.nn.density.StandardDensity=== | ||
− | < | + | <syntaxhighlight> |
package org.csdgn.nn.density; | package org.csdgn.nn.density; | ||
Line 310: | Line 310: | ||
} | } | ||
} | } | ||
− | </ | + | </syntaxhighlight> |
===org.csdgn.nn.density.NormalDistribution=== | ===org.csdgn.nn.density.NormalDistribution=== | ||
− | < | + | <syntaxhighlight> |
package org.csdgn.nn.density; | package org.csdgn.nn.density; | ||
Line 337: | Line 337: | ||
} | } | ||
− | </ | + | </syntaxhighlight> |
===org.csdgn.nn.distance.EulerDistanceSquared=== | ===org.csdgn.nn.distance.EulerDistanceSquared=== | ||
− | < | + | <syntaxhighlight> |
package org.csdgn.nn.distance; | package org.csdgn.nn.distance; | ||
Line 360: | Line 360: | ||
} | } | ||
− | </ | + | </syntaxhighlight> |
Revision as of 07:06, 1 July 2010
This is my implementation of a Self-organizing map. It is untested, but it should work just fine.
Contents
org.csdgn.nn.KohonenMap
package org.csdgn.nn;
import java.util.Random;
import org.csdgn.nn.density.StandardDensity;
import org.csdgn.nn.distance.EulerDistanceSquared;
/**
* A Self-Organizing Map implementation.
*
* Requires: <br>
* org.csdgn.nn.DensityFunction<br>
* org.csdgn.nn.DistanceFunction<br>
* org.csdgn.nn.density.StandardDensity<br>
* org.csdgn.nn.distance.EulerDistanceSquared
*
* @author Chase
*
*/
public class KohonenMap {
/**
* Holds the neighborhood layout;
*/
private final Node[] map;
private DensityFunction density;
private DistanceFunction distance;
private int BMU;
/**
* @param mapSize Size of the neighborhood. Example: {10,10} produces a 2 dimensional
* map, each dimension having 10 nodes. Total nodes would be 100.
* @param input The length of the input vector (2D only)
* @param output The length of the output vector (2D only)
*/
public KohonenMap(int[] mapSize, int input, int output) {
/* Setup the map */
int size = 1;
for(int m : mapSize) size *= m;
this.map = new Node[size];
this.density = new StandardDensity();
this.distance = new EulerDistanceSquared();
int[] pos = new int[mapSize.length];
for(int i=0; i<map.length; ++i) {
this.map[i] = new Node(mapSize.length,input,output);
/* Setup the location of each node, for speed reasons. */
System.arraycopy(pos, 0, this.map[i].position, 0, pos.length);
/* Update the position marker */
++pos[0];
for(int j=0;j<pos.length-1;++j) {
if(pos[j] >= mapSize[j]) {
++pos[j+1];
pos[j] = 0;
}
}
}
}
/**
* Initializes the map to random values
*/
public final void initialize() {
Random r = new Random();
initialize(r);
}
/**
* Initializes the map with the given random function.
* Uses the nextDouble function.
*/
public final void initialize(Random random) {
for(Node n : map) {
for(int i = 0; i < n.input.length; ++i)
n.input[i] = random.nextDouble();
for(int i = 0; i < n.output.length; ++i)
n.output[i] = random.nextDouble();
}
}
/**
* Finds the Best Matching Unit for the given input.
* @return the BMUs identifier
*/
public final int findBMInput(double[] input) {
BMU = 0;
double distance = Double.MAX_VALUE;
for(int i=0; i<map.length; ++i) {
double dist = this.distance.calculate(map[i].input, input);
if(dist < distance) {
distance = dist;
BMU = i;
}
}
return BMU;
}
/**
* Finds the Best Matching Unit for the given output.
* @return the BMUs identifier
*/
public final int findBMOutput(double[] output) {
BMU = 0;
double distance = Double.MAX_VALUE;
for(int i=0; i<map.length; ++i) {
double dist = this.distance.calculate(map[i].output, output);
if(dist < distance) {
distance = dist;
BMU = i;
}
}
return BMU;
}
/**
* Finds the Worst Matching Unit for the given input
* @return the WMUs identifier
*/
public final int findWMInput(double[] input) {
BMU = 0;
double distance = Double.MIN_VALUE;
for(int i=0; i<map.length; ++i) {
double dist = this.distance.calculate(map[i].input, input);
if(dist > distance) {
distance = dist;
BMU = i;
}
}
return BMU;
}
/**
* Finds the Worst Matching Unit for the given output
* @return the WMUs identifier
*/
public final int findWMOutput(double[] output) {
BMU = 0;
double distance = Double.MIN_VALUE;
for(int i=0; i<map.length; ++i) {
double dist = this.distance.calculate(map[i].output, output);
if(dist > distance) {
distance = dist;
BMU = i;
}
}
return BMU;
}
/**
* This returns the input of the last found BMU or WMU.
* @return the input vector
*/
public final double[] getInput() {
return this.map[BMU].input;
}
/**
* This returns the output of the last found BMU or WMU.
* @return the output vector
*/
public final double[] getOutput() {
return this.map[BMU].output;
}
/**
* This returns the input of the given ID.
* @return the input vector
*/
public final double[] getInput(int id) {
if(id > 0 && id < map.length)
return this.map[id].input;
return new double[0];
}
/**
* This returns the output of the given ID.
* @return the output vector
*/
public final double[] getOutput(int id) {
if(id > 0 && id < map.length)
return this.map[id].output;
return new double[0];
}
/**
* Sets the density function this map uses for updating nearby nodes.
* If unset it uses the StandardDensity class.
* @param func
*/
public final void setDensityFunction(DensityFunction func) {
this.density = func;
}
/**
* Sets the distance function used to find the best or worst matching unit.
* If unset, this map uses the EulerDistanceSquared class.
* @param func
*/
public final void setDistanceFunction(DistanceFunction func) {
this.distance = func;
}
/**
* Updates the map with the given data.
* @param input input vector
* @param output output vector
*/
public final void update(double input[], double output[]) {
Node bmu = map[BMU];
for(int i=0; i<map.length; ++i) {
map[i].update(bmu.position, input, output);
}
}
/**
* <math>distSqr(p,q) = \sum_{i=0}^n (p_i - q_i)^2</math> where <math>n</math> is the
* size of the smaller of <math>p</math> or <math>q</math>
*/
private static final int neighborhood(int[] p, int[] q) {
if(p == null || q == null) return 0;
int len = Math.min(p.length, q.length);
int output = 0;
for(int i=0; i<len; ++i)
output += (p[i] - q[i])*(p[i] - q[i]);
return output;
}
private final class Node {
/** Location in the neighborhood */
private final int[] position;
/** Input vector */
private final double[] input;
/** Output vector */
private final double[] output;
public Node(int mapSize, int inputSize, int outputSize) {
position = new int[mapSize];
input = new double[inputSize];
output = new double[outputSize];
}
private final double weight(double c, double t, double n) {
return c + n * (t - c);
}
private final void update(int[] pos, double[] in, double[] out) {
double distance = neighborhood(pos,position);
double neighborhood = density.calculate(distance);
/* Changes below this point benefits are negligible */
if(neighborhood < 1e-4) return;
for(int i=0; i<input.length; ++i)
input[i] = weight(input[i], in[i], neighborhood);
for(int i=0; i<output.length; ++i)
output[i] = weight(output[i], out[i], neighborhood);
}
}
}
org.csdgn.nn.DensityFunction
package org.csdgn.nn;
public interface DensityFunction {
/**
* Calculates the density at the given point, where x is a certain distance from the center of the distribution.
*/
public double calculate(double x);
}
org.csdgn.nn.DistanceFunction
package org.csdgn.nn;
/**
* A function for determining the distance between two double arrays.
* @author Chase
*/
public interface DistanceFunction {
public double calculate(double[] p, double[] q);
}
org.csdgn.nn.density.StandardDensity
package org.csdgn.nn.density;
import org.csdgn.nn.DensityFunction;
/**
* <math>density(x) = 2^{-x^2}</math>
*/
public final class StandardDensity implements DensityFunction {
/**
* <math>density(x) = 2^{-x^2}</math>
*/
@Override
public double calculate(double x) {
return Math.pow(2, -(x*x));
}
}
org.csdgn.nn.density.NormalDistribution
package org.csdgn.nn.density;
import org.csdgn.nn.DensityFunction;
public final class NormalDistribution implements DensityFunction {
private final double multi;
private final double variance;
private final double mean;
public NormalDistribution() {
this(1,0);
}
public NormalDistribution(double variance, double mean) {
this.multi = 1.0 / Math.sqrt(2*Math.PI*variance);
this.variance = variance;
this.mean = mean;
}
@Override
public double calculate(double x) {
double e = ((x - mean)*(x - mean)) / (2*variance);
return multi*Math.exp(-e);
}
}
org.csdgn.nn.distance.EulerDistanceSquared
package org.csdgn.nn.distance;
import org.csdgn.nn.DistanceFunction;
public class EulerDistanceSquared implements DistanceFunction {
/**
* <math>distSqr(p,q) = \sum_{i=0}^n (p_i - q_i)^2</math> where <math>n</math> is the
* size of the smaller of <math>p</math> or <math>q</math>
*/
@Override
public final double calculate(double[] p, double[] q) {
if(p == null || q == null) return 0;
int len = Math.min(p.length, q.length);
double k,output = 0;
for(int i=0; i<len; ++i)
output += (k=(p[i] - q[i]))*k;
return output;
}
}