Self Organizing Map - Food Relations
Reference - http://www.mql5.com/en/articles/283
/**
* Copyright greentec ( http://wonderfl.net/user/greentec )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/u98i
*/
// forked from greentec's Self Organizing Map
package {
import com.bit101.components.Label;
import com.bit101.components.PushButton;
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.Sprite;
import flash.events.Event;
import flash.geom.Rectangle;
import flash.geom.ColorTransform;
public class FlashTest extends Sprite {
public var outputNeuronWidthNum:int = 80;
public var outputNeuronHeightNum:int = 80;
public var outputNeuronNum:int = outputNeuronWidthNum * outputNeuronHeightNum;
public var inputNeuronNum:int = 25;
public var inputData:Object = {
"Apples": [0.4, 11.8, 0.1],
"Avocado": [1.9, 1.9, 19.5],
"Bananas": [1.2, 23.2, 0.3],
"Beef Steak": [20.9, 0, 7.9],
"Big Mac": [13, 19, 11],
"Brazil Nuts": [15.5, 2.9, 68.3],
"Bread": [10.5, 37, 3.2],
"Butter": [1, 0, 81],
"Cheese": [25, 0.1, 34.4],
"Cheesecake": [6.4, 28.2, 22.7],
"Cookies": [5.7, 58.7, 29.3],
"Cornflakes": [7, 84, 0.9],
"Eggs": [12.5, 0, 10.8],
"Fried Chicken": [17, 7, 20],
"Fries": [3, 36, 13],
"Hot Chocolate": [3.8, 19.4, 10.2],
"Pepperoni": [20.9, 5.1, 38.3],
"Pizza": [12.5, 30, 11],
"Pork Pie": [10.1, 27.3, 24.2],
"Potatoes": [1.7, 16.1, 0.3],
"Rice": [6.9, 74, 2.8],
"Roast Chicken": [26.1, 0.3, 5.8],
"Sugar": [0, 95.1, 0],
"Tuna Steak": [25.6, 0, 0.5],
"Water": [0, 0, 0]
}
public var _bitmap:Bitmap;
public var _bitmapData:BitmapData;
public var outputCellWidth:int = 5;
public var outputCellHeight:int = 5;
public var inputLayer:Layer;
public var outputLayer:Layer;
public var indent:int = 10;
public var trainingSet:Array;
public var trainingSetNum:int = inputNeuronNum;
public var startLearningRate:Number = 0.1;
public var learningRate:Number = 0.1;
public var iterMax:int = 150;
public var iter:int = 0;
public var proceedNumLabel:Label;
public var resetButton:PushButton;
public var labelArray:Array;
public var whiteColorTransform:ColorTransform;
public function FlashTest() {
// write as3 code here..
_bitmapData = new BitmapData(465, 465, false, 0x292929);
_bitmap = new Bitmap(_bitmapData);
addChild(_bitmap);
var i:int;
var neuron:Neuron;
inputLayer = new Layer();
for (i = 0; i < inputNeuronNum; i += 1)
{
neuron = new Neuron(0);
inputLayer.neurons.push(neuron);
}
outputLayer = new Layer();
for (i = 0; i < outputNeuronNum; i += 1)
{
neuron = new Neuron(inputNeuronNum);
outputLayer.neurons.push(neuron);
}
//drawMap();
proceedNumLabel = new Label(this, 10, 10 + outputNeuronHeightNum * outputCellHeight + 5, "Proceed #");
resetButton = new PushButton(this, proceedNumLabel.x, proceedNumLabel.y + proceedNumLabel.height + 5, "Reset", onReset);
var _label:Label = new Label(this, resetButton.x + resetButton.width + 20, proceedNumLabel.y, "Training Pattern");
whiteColorTransform = new ColorTransform();
whiteColorTransform.color = 0xffffff;
initTrainingSet();
initLabel();
addEventListener(Event.ENTER_FRAME, onLoop);
}
private function initLabel():void
{
var food:String;
var _label :Label;
labelArray = [];
for (food in inputData)
{
_label = new Label(this, -100, -100, food);
_label.transform.colorTransform = whiteColorTransform;
labelArray.push(_label);
}
}
private function onReset(e:Event):void
{
if (hasEventListener(Event.ENTER_FRAME) == true)
{
removeEventListener(Event.ENTER_FRAME, onLoop);
}
initTrainingSet();
var i:int;
var neuron:Neuron;
for (i = 0; i < outputNeuronNum; i += 1)
{
neuron = outputLayer.neurons[i];
neuron.resetW(inputNeuronNum);
}
iter = 0;
learningRate = startLearningRate;
if (hasEventListener(Event.ENTER_FRAME) == false)
{
addEventListener(Event.ENTER_FRAME, onLoop);
}
}
private function hsv(h:Number, s:Number, v:Number):Array
{
var r:Number, g:Number, b:Number;
var i:int;
var f:Number, p:Number, q:Number, t:Number;
if (s == 0){
r = g = b = v;
return [r * 2 - 1, g * 2 - 1, b * 2 - 1];
}
h /= 60;
i = Math.floor(h);
f = h - i;
p = v * (1 - s);
q = v * (1 - s * f);
t = v * (1 - s * (1 - f));
switch( i ) {
case 0:
r = v, g = t, b = p;
break;
case 1:
r = q, g = v, b = p;
break;
case 2:
r = p, g = v, b = t;
break;
case 3:
r = p, g = q, b = v;
break;
case 4:
r = t, g = p, b = v;
break;
default: // case 5:
r = v, g = p, b = q;
break;
}
return [r * 2 - 1, g * 2 - 1, b * 2 - 1];
}
private function initTrainingSet():void
{
var i:int;
var rect:Rectangle;
var red:Number;
var green:Number;
var blue:Number;
var color:uint;
var h:Number, s:Number, v:Number;
var hsvArray:Array;
var maxValues:Array;
var minValues:Array;
var food:String;
maxValues = [-9999, -9999, -9999];
minValues = [9999, 9999, 9999];
for (food in inputData)
{
for (i = 0; i < 3; i += 1)
{
if (inputData[food][i] > maxValues[i])
{
maxValues[i] = inputData[food][i];
}
if (inputData[food][i] < minValues[i])
{
minValues[i] = inputData[food][i];
}
}
}
rect = new Rectangle();
trainingSet = [];
i = 0;
for (food in inputData)
{
red = ((inputData[food][0] - minValues[0]) / (maxValues[0] - minValues[0]) * 2) - 1;
green = ((inputData[food][1] - minValues[1]) / (maxValues[1] - minValues[1]) * 2) - 1;
blue = ((inputData[food][2] - minValues[2]) / (maxValues[2] - minValues[2]) * 2) - 1;
trainingSet.push([red, green, blue]);
color = ((red + 1) * 127 << 16) | ((green + 1) * 127 << 8) | ((blue + 1) * 127 & 0xff);
rect.x = resetButton.x + resetButton.width + 20 + i * 10;
rect.y = resetButton.y;
rect.width = 10;
rect.height = 20;
_bitmapData.fillRect(rect, color);
i += 1;
}
}
private function onLoop(e:Event):void
{
iter += 1;
if (iter <= iterMax)
{
var i:int;
var j:int;
var neuron:Neuron;
var redDist:Number;
var greenDist:Number;
var blueDist:Number;
var bmuNumber:Number;
var bmuIndex:int = -1;
var v:Number;
var N:Number;
var dx:int;
var dy:int;
for (i = 0; i < trainingSetNum; i += 1)
{
bmuNumber = 99999;
for (j = 0; j < outputNeuronNum; j += 1) //get BMU - Best Matching Unit
{
neuron = outputLayer.neurons[j];
neuron.redDist = trainingSet[i][0] - neuron.w[0];
neuron.greenDist = trainingSet[i][1] - neuron.w[1];
neuron.blueDist = trainingSet[i][2] - neuron.w[2];
neuron.euclideanDist = Math.sqrt(neuron.redDist * neuron.redDist + neuron.greenDist * neuron.greenDist + neuron.blueDist * neuron.blueDist);
//trace(neuron.euclideanDist);
if (bmuNumber > neuron.euclideanDist)
{
bmuNumber = neuron.euclideanDist;
bmuIndex = j;
}
}
//trace(bmuNumber, bmuIndex);
for (j = 0; j < outputNeuronNum; j += 1)
{
neuron = outputLayer.neurons[j];
dx = (bmuIndex % outputNeuronWidthNum) - (j % outputNeuronWidthNum);
dx = (dx ^ dx >> 31) - (dx >> 31); // == MAth.abs(dx)
dy = int(bmuIndex / outputNeuronWidthNum) - int(j / outputNeuronWidthNum);
dy = (dy ^ dy >> 31) - (dy >> 31);
v = (dx + dy) / 18;
N = Math.exp( -v);
neuron.w[0] += learningRate * N * neuron.redDist;
neuron.w[1] += learningRate * N * neuron.greenDist;
neuron.w[2] += learningRate * N * neuron.blueDist;
//neuron.w[0] = neuron.w[0] > 1 ? 1 : (neuron.w[0] < -1 ? -1 : neuron.w[0]); //for speed.. sacrifice
//neuron.w[1] = neuron.w[1] > 1 ? 1 : (neuron.w[1] < -1 ? -1 : neuron.w[1]);
//neuron.w[2] = neuron.w[2] > 1 ? 1 : (neuron.w[2] < -1 ? -1 : neuron.w[2]);
}
labelArray[i].x = (bmuIndex % outputNeuronWidthNum) * outputCellWidth + 10;
labelArray[i].y = int(bmuIndex / outputNeuronWidthNum) * outputCellHeight + 10;
}
//trace(neuron.w[0], neuron.w[1], neuron.w[2]);
drawMap();
learningRate = startLearningRate * Math.exp( -iter / iterMax);
proceedNumLabel.text = "Proceed #" + String(iter) + "/" + String(iterMax);
if (iter == iterMax)
{
if (hasEventListener(Event.ENTER_FRAME) == true)
{
removeEventListener(Event.ENTER_FRAME, onLoop);
}
}
}
}
private function drawMap():void
{
var i:int;
var j:int;
var neuron:Neuron;
var color:uint;
var rect:Rectangle = new Rectangle();
rect.x = indent;
rect.y = indent;
rect.width = outputCellWidth * outputNeuronWidthNum;
rect.height = outputCellHeight * outputNeuronHeightNum;
_bitmapData.fillRect(rect, 0x292929);
for (i = 0; i < outputNeuronWidthNum; i += 1)
{
for (j = 0; j < outputNeuronHeightNum; j += 1)
{
neuron = outputLayer.neurons[i + j * outputNeuronWidthNum];
color = ((neuron.w[0] + 1) * 127 << 16) | ((neuron.w[1] + 1) * 127 << 8) | ((neuron.w[2] + 1) * 127 & 0xff);
rect.x = indent + i * outputCellWidth;
rect.y = indent + j * outputCellHeight;
rect.width = outputCellWidth;
rect.height = outputCellHeight;
_bitmapData.fillRect(rect, color);
}
}
}
}
}
Class
{
/**
* ...
* @author ypc
*/
class Neuron
{
public var w:Object;
public var input:Number;
public var output:Number;
public var euclideanDist:Number;
public var redDist:Number;
public var greenDist:Number;
public var blueDist:Number;
public function Neuron(n:int)
{
this.w = new Object();
if (n > 0)
{
var i:int;
for (i = 0; i < n; i += 1)
{
this.w[i] = Math.random() * 2 - 1;
}
}
}
public function resetW(n:int):void
{
var i:int;
for (i = 0; i < n; i += 1)
{
this.w[i] = Math.random() * 2 - 1;
}
}
}
}
Class
{
/**
* ...
* @author ypc
*/
class Layer
{
public var neurons:Array;
public function Layer()
{
this.neurons = [];
}
}
}