In case Flash no longer exists; a copy of this site is included in the Flashpoint archive's "ultimate" collection.

Dead Code Preservation :: Archived AS3 works from wonderfl.net

Self Organizing Map

In machine learning, there are two types of learning style - supervised and unsupervised learning. Self Organizing Map is a good example of unsupervised learning. There is no ideal, but we can see many things than the former.

Push the Reset Button. You can see similar but slightly different Images.

Reference - http://www.ai-junkie.com/ann/som/som4.html
http://davis.wpi.edu/~matt/courses/soms/
<Introduction to the Math of Neural Networks>, Jeff Heaton

HSV function - http://actionsnippet.com/?p=1627
/**
 * Copyright greentec ( http://wonderfl.net/user/greentec )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/5nZ4
 */

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 com.bit101.components.RadioButton;
    
    public class FlashTest extends Sprite {
        
        public var outputNeuronWidthNum:int = 100;
        public var outputNeuronHeightNum:int = 100;
        public var outputNeuronNum:int = outputNeuronWidthNum * outputNeuronHeightNum;
        public var inputNeuronNum:int = 3;
        
        public var _bitmap:Bitmap;
        public var _bitmapData:BitmapData;
        
        public var outputCellWidth:int = 4;
        public var outputCellHeight:int = 4;
        
        
        public var inputLayer:Layer;
        public var outputLayer:Layer;
        
        public var indent:int = 10;
        
        public var trainingSet:Array;
        public var trainingSetNum:int = 15;
        
        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 randomRadioButton:RadioButton;
        public var tintRadioButton:RadioButton;        

        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");
            
            randomRadioButton = new RadioButton(this, _label.x + _label.width + 10, _label.y + 5, "random Color", false);
            randomRadioButton.groupName = "colorSelect";
            tintRadioButton = new RadioButton(this, randomRadioButton.x + randomRadioButton.width + 5, randomRadioButton.y, "tint Color", true);
            tintRadioButton.groupName = "colorSelect";
            
            initTrainingSet();
            
                        
            addEventListener(Event.ENTER_FRAME, onLoop);
        }
        
        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;
            
            rect = new Rectangle();
            trainingSet = [];
            for (i = 0; i < trainingSetNum; i += 1)
            {
                if (randomRadioButton.selected == true)
                {
                    red = Math.random() * 2 - 1;
                    green = Math.random() * 2 - 1;
                    blue = Math.random() * 2 - 1;
                }
                else
                {
                    h = Math.random() * 360;
                    s = 1;
                    v = 1;
                    hsvArray = hsv(h, s, v);
                    red = hsvArray[0];
                    green = hsvArray[1];
                    blue = hsvArray[2];
                    
                }
                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 * 20;
                rect.y = resetButton.y;
                rect.width = 20;
                rect.height = 20;
                _bitmapData.fillRect(rect, color);
                
            }
        }
        
        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]);
                        
                    }
                    
                }
                
                //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 = [];
        }
        
    }

}