Neural baby
3 layer
with 5 neurons each layer
simply! addingsums
/**
* Copyright Andy.Huber ( http://wonderfl.net/user/Andy.Huber )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/iPzw
*/
// forked from tepe's forked from: Neural Network - Wine Classification
// forked from greentec's Neural Network - Wine Classification
package {
import flash.geom.ColorTransform;
import com.bit101.charts.LineChart;
import com.bit101.components.Label;
import com.bit101.components.PushButton;
import com.bit101.components.TextArea;
import flash.display.*;
import flash.text.*;
import flash.events.*;
import flash.events.SampleDataEvent;
import flash.media.Microphone;
//パーセプトロン
public class FlashTest extends Sprite {
private var mic:Microphone;
private var rec:Boolean = false;
private var red:int=0;
private var green:int=0;
private var blue:int=0;
private var color:uint;
private var goOn:Boolean = true;
public var iter:int = 0;
public var oneIter:int = 1;
//各レイヤーのニューロン数
public var inputNeuronNum:int = 4;
public var hiddenNeuronNum:int = 5;
public var outputNeuronNum:int = 4;
//レイヤー
public var inputLayer:Layer;
public var hiddenLayer:Layer;
public var outputLayer:Layer;
//画面構成
public var errorChart:LineChart;//チャート画面
public var proceedNumLabel:Label;//プロセス数
public var debugLabel:Label;//デバッグ表示
public var testButton:PushButton;//testボタン
public var testTextArea:TextField;//テキストエリア
public var resetButton:PushButton;//リセットボタン
public var redButton:PushButton;//リセットボタン
public var blueButton:PushButton;//リセットボタン
public var greenButton:PushButton;//リセットボタン
public var clearButton:PushButton;//リセットボタン
public var showButton:PushButton;//リセットボタン
public var goButton:PushButton;//リセットボタン
public var proceedEnd:Boolean = false;
//パラメータ
public var learnRate:Number = 0.25;
public var _alpha:Number = 0.9; // for momentum
public var beta:Number;
//ワインのデータとか
public var wineTxt:String = "0.0,0.9,0.9,0.9,0,0,0,0\n0.1,0.1,0.9,0.9,0,1,0,0\n0.9,0.9,0.2,0.2,0,0,1,0\n0.1,0.9,0.9,0.1,0,0,1,0\n0.9,0.1,0.2,0.9,0,0,0,1\n0.5,0.5,0.5,0.5,1,1,1,1"; //?
public var wineTxtLines:Array;
public var wineTxtArray:Array;
public var lineShape:Shape;
//エントリ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
public function FlashTest() {
// write as3 code here..
this.graphics.beginFill(0xdddddd);
this.graphics.drawRect(0,0,465,465);
this.graphics.endFill();
stage.scaleMode = "noScale";
lineShape = new Shape();
addChild(lineShape);//シナプスライン
var line:Array;
var i:int;
wineTxtLines = wineTxt.split("\n");
wineTxtArray = [];
for (i = 0; i < wineTxtLines.length; i++){
//wineTxtLines[i] = wineTxtLines[i].replace (/\s*\R/g, "");
line = wineTxtLines[i].split(",");
wineTxtArray.push(line);
}
var neuron:Neuron;
inputLayer = new Layer();
for (i = 0; i < inputNeuronNum; i++){
neuron = new Neuron(hiddenNeuronNum);
inputLayer.neurons.push(neuron);
neuron.x = 465 * 3 / 4 + (-(inputNeuronNum + 1) / 2 + i) * 16; //graphic
neuron.y = 40;
addChild(neuron);
}
neuron = new Neuron(hiddenNeuronNum);
neuron.biased = true; //biased One
neuron.x = 465 * 3 / 4 + (-(inputNeuronNum + 1) / 2 + i) * 16;
neuron.y = 40;
addChild(neuron);
inputLayer.neurons.push(neuron);
hiddenLayer = new Layer();
for (i = 0; i < hiddenNeuronNum; i++){
neuron = new Neuron(outputNeuronNum);
hiddenLayer.neurons.push(neuron);
neuron.x = 465 * 3 / 4 + (-(hiddenNeuronNum + 1) / 2 + i) * 16;
neuron.y = 95;
addChild(neuron);
}
neuron = new Neuron(outputNeuronNum);
neuron.biased = true; //biased One
neuron.x = 465 * 3 / 4 + (-(hiddenNeuronNum + 1) / 2 + i) * 16;
neuron.y = 95;
addChild(neuron);
hiddenLayer.neurons.push(neuron);
outputLayer = new Layer();
for (i = 0; i < outputNeuronNum; i++){
neuron = new Neuron(0);
outputLayer.neurons.push(neuron);
neuron.x = 465 * 3 / 4 + (-outputNeuronNum / 2 + i) * 16;
neuron.y = 150;
addChild(neuron);
}
beta = 0.7 * Math.pow(hiddenNeuronNum, (1 / inputNeuronNum) );
weightInit();
proceedNumLabel = new Label(this, 10, 10, "Proceed #0");
errorChart = new LineChart(this, 10, proceedNumLabel.y + proceedNumLabel.height + 5);
errorChart.data = [];
debugLabel = new Label(this, 10, errorChart.y + errorChart.height + 5, "");
testButton = new PushButton(this, 10, debugLabel.y + debugLabel.height + 20, "Test", onTest);
//testTextArea = new TextArea(this, 10, testButton.y + testButton.height + 5, "");
testTextArea = new TextField();
addChild(testTextArea);
with(testTextArea){
border=true;
text = "test";
y = 200;
}
testTextArea.width = 465 - 20;
testTextArea.height = 465 - 50 - testTextArea.y;
resetButton = new PushButton(this, testButton.x + testButton.width + 10, testButton.y, "Reset", onReset);
redButton = new PushButton(this, 250, testButton.y, "Red", onRed);
blueButton = new PushButton(this, 330, testButton.y, "Blue", onBlue);
greenButton = new PushButton(this, 400, testButton.y, "Green", onGreen);
clearButton = new PushButton(this, 200, 20, "Clear", onClear);
showButton = new PushButton(this, 200, 50, "Show", onShow);
goButton = new PushButton(this, 200, 150, "Watch", onWatch);
addEventListener(Event.ENTER_FRAME, onLoop);
//testTextArea.text += wineTxt+"\n";
mic = Microphone.getMicrophone();
// mic.addEventListener(SampleDataEvent.SAMPLE_DATA, micSampleDataHandler);
}
private function micSampleDataHandler(e:SampleDataEvent):void
{
var sw:int = stage.stageWidth;
var sh:int = stage.stageHeight;
graphics.clear();
graphics.lineStyle(1, 0x0, 1);
graphics.moveTo(0, sh * 0.5);
var n:int = e.data.length / 4;
var j:int = 0;
var help:Number=0;
for (var i:int = 0; i < n && i < inputNeuronNum; ++i)
{
var x:Number = i / n * sw;
var y:Number = 465 - e.data.readFloat() * 1000;
if ( goOn ) {
color = 0;
color += 10000* outputLayer.neurons[0].output;
color += 1000000000000*outputLayer.neurons[1].output;
color += outputLayer.neurons[2].output;
}
if (i % 2 == 0) graphics.lineTo(x, y);
if ( rec == true ) {
// setting new values for wineText
if ( i == 0 && wineTxt != "" ) wineTxt += "\n";
if ( j < inputNeuronNum ) {
help = e.data.readFloat();
help = 100*Math.sqrt( help * help );
if ( help > 1 ) help = 1.0;
if ( help < 0.02 ) help = 0.0;
wineTxt += String( help ) + ",";
j++; //;)
}else {
j=0;
wineTxt += red+","+green+","+blue+",0";
red = green = blue = 0;
}
}//if
}//for
if ( rec == true ) {
if ( j != 0 ){
while ( j < inputNeuronNum ){
wineTxt += "0,";
++j;
}
wineTxt += red+","+green+","+blue+",0";
red = green = blue = 0;
}
var line:Array;
wineTxtLines = wineTxt.split("\n");
wineTxtArray = [];
for (i = 0; i < wineTxtLines.length; i++){
//wineTxtLines[i] = wineTxtLines[i].replace (/\s*\R/g, "");
line = wineTxtLines[i].split(",");
wineTxtArray.push(line);
}
testTextArea.appendText("\n\n"+ wineTxt + "\n");
testTextArea.scrollV = testTextArea.maxScrollV;
draw();
}
rec = false;
if (hasEventListener(SampleDataEvent.SAMPLE_DATA)){
removeEventListener(SampleDataEvent.SAMPLE_DATA, micSampleDataHandler);
}
}
//初期化
public function weightInit():void{
// Nguyen-Widrow Initialization - for all Input to Hidden w
var normHidden:Number;
var j:int;
var i:int;
normHidden = 0;
for (i = 0; i < hiddenNeuronNum; i++){
for (j = 0; j < inputNeuronNum + 1; j++){ //biased까지 계산하므로 inputNeuronNum + 1
normHidden += inputLayer.neurons[j].w[i] * inputLayer.neurons[j].w[i];
}
normHidden = Math.sqrt(normHidden);
for (j = 0; j < inputNeuronNum + 1; j++){
inputLayer.neurons[j].w[i] *= beta / normHidden;
}
}
}
//リセット
public function onReset(e:Event):void{
if (hasEventListener(Event.ENTER_FRAME)){
removeEventListener(Event.ENTER_FRAME, onLoop);
}
var i:int;
for (i = 0; i < inputNeuronNum + 1; i++){
inputLayer.neurons[i].resetW(hiddenNeuronNum);
}
for (i = 0; i < hiddenNeuronNum + 1; i++){
hiddenLayer.neurons[i].resetW(outputNeuronNum);
}
weightInit();
iter = 0;
proceedEnd = false;
errorChart.data = [];
addEventListener(Event.ENTER_FRAME, onLoop);
}
public function onWatch(e:Event=null):void{
mic.addEventListener(SampleDataEvent.SAMPLE_DATA, micSampleDataHandler);
goOn = !goOn;
}
public function onRed(e:Event=null):void{
rec = true;
red = 1;
mic.addEventListener(SampleDataEvent.SAMPLE_DATA, micSampleDataHandler);
}
public function onGreen(e:Event=null):void{
rec = true;
green = 1;
mic.addEventListener(SampleDataEvent.SAMPLE_DATA, micSampleDataHandler);
}
public function onBlue(e:Event=null):void{
rec = true;
blue = 1;
mic.addEventListener(SampleDataEvent.SAMPLE_DATA, micSampleDataHandler);
}
public function onClear(e:Event=null):void{
onReset(e);
wineTxt = new String;
// wineTxtLines = [];
// wineTxtArray = [];
}
public function onShow(e:Event=null):void{
var str:String="\n\n InputNeurons: \n ";
var i:int;
var j:int;
for ( i = 0; true && i < inputNeuronNum +1; i++ ) {
for ( j = 0; true && j < hiddenNeuronNum +1; j++ ) {
str += i + " " + inputLayer.neurons[i].w[j] + "\n";
}}
str += "\n Hidden Neurons: \n";
for ( i = 0; true && i < hiddenNeuronNum +1; i++ ) {
for ( j = 0; true && j < outputNeuronNum +1; j++ ) {
str += i + " " + hiddenLayer.neurons[i].w[j] + "\n";
}}
str += "\n Output Neurons: \n ";
var a:int;
for ( a = 0; true && a < outputNeuronNum ; a++ ) {
str += a + " " + outputLayer.neurons[a].nodeDelta + "\n";
}
testTextArea.appendText("\n"+ str + "\n" + wineTxt );
testTextArea.scrollV = testTextArea.maxScrollV;
draw();
}
//テスト
public function onTest(e:Event=null):void{
//Sound
var arr:Array = printRandomNumber(wineTxtArray.length, 1);
var ideal:int;
var i:int;
var j:int;
for (var s:int = 0; s < arr.length; s++){
for (i = 0; i < inputNeuronNum; i++){
inputLayer.neurons[i].output = sigmoid(parseFloat(wineTxtArray[arr[s]][i]));//入力
}
inputLayer.neurons[inputNeuronNum].output = 1; // biased
for (i = 0; i < hiddenNeuronNum; i++){
hiddenLayer.neurons[i].input = 0;//値リセット
for (j = 0; j < inputNeuronNum + 1; j++){
hiddenLayer.neurons[i].input += inputLayer.neurons[j].output * inputLayer.neurons[j].w[i];
}
hiddenLayer.neurons[i].output = sigmoid(hiddenLayer.neurons[i].input);
}
hiddenLayer.neurons[hiddenNeuronNum].output = 1; // biased
for (i = 0; i < outputNeuronNum; i++){
outputLayer.neurons[i].input = 0;
for (j = 0; j < hiddenNeuronNum + 1; j++){
outputLayer.neurons[i].input += hiddenLayer.neurons[j].output * hiddenLayer.neurons[j].w[i];
}
outputLayer.neurons[i].output = sigmoid(outputLayer.neurons[i].input);
}
//テキスト出力
var str:String = "Sample #" + String(arr[s]) + "\n";
str += "出力値 : " + String(outputLayer.neurons[0].output) + ", ";
str += String(outputLayer.neurons[1].output) + ", ";
str += String(outputLayer.neurons[2].output) + "\n正解値 : " + wineTxtArray[arr[s]][13] + ", ";
str += wineTxtArray[arr[s]][14] + ", " + wineTxtArray[arr[s]][15] + "\n";// + "\t Error : " + String(int(outputLayer.neurons[0].output * 10000) / 100) + " % \n";
str += "精度 : ";
for (i = 0; i < 3; i++){
if (wineTxtArray[arr[s]][i + inputNeuronNum] == "1")
str += (int(outputLayer.neurons[i].output * 10000) / 100).toString()+"%, ";
else
str += (int((1 - outputLayer.neurons[i].output) * 10000) / 100).toString()+"%, ";
}
testTextArea.appendText(str+"\n");
testTextArea.scrollV = testTextArea.maxScrollV;
color = 0;
color += 10000* outputLayer.neurons[0].output;
color += 1000000000000*outputLayer.neurons[1].output;
color += outputLayer.neurons[2].output;
}
draw();
}
//ループ
public function onLoop(e:Event):void{
var sumError:Number = 0;
var ideal:int;
var i:int;
var j:int;
if (true){
for (var m:int = 0; m < oneIter; m++){
sumError = 0;//エラーカウンタ
iter += 1;
for (var s:int = 0; s < wineTxtArray.length; s++){
//入力層
for (i = 0; i < inputNeuronNum; i++){
//output
inputLayer.neurons[i].output = sigmoid(parseFloat(wineTxtArray[s][i]));
}
inputLayer.neurons[inputNeuronNum].output = 1; // biased
//中間層
for (i = 0; i < hiddenNeuronNum; i++){
hiddenLayer.neurons[i].input = 0;
for (j = 0; j < inputNeuronNum + 1; j++){
//input
hiddenLayer.neurons[i].input += inputLayer.neurons[j].output * inputLayer.neurons[j].w[i];
}
//output
hiddenLayer.neurons[i].output = sigmoid(hiddenLayer.neurons[i].input);
}
hiddenLayer.neurons[hiddenNeuronNum].output = 1; // biased
//出力層
for (i = 0; i < outputNeuronNum; i++){
outputLayer.neurons[i].input = 0;
for (j = 0; j < hiddenNeuronNum + 1; j++){
outputLayer.neurons[i].input += hiddenLayer.neurons[j].output * hiddenLayer.neurons[j].w[i];
}
outputLayer.neurons[i].output = sigmoid(outputLayer.neurons[i].input);
outputLayer.neurons[i].error = wineTxtArray[s][inputNeuronNum + i] - outputLayer.neurons[i].output;
sumError += outputLayer.neurons[i].error * outputLayer.neurons[i].error;
outputLayer.neurons[i].nodeDelta = outputLayer.neurons[i].error * outputLayer.neurons[i].output * (1 - outputLayer.neurons[i].output);
}
//バックプロパゲーション
for (i = 0; i < hiddenNeuronNum + 1; i++){ //中間層
hiddenLayer.neurons[i].nodeDelta = 0;//リセット
for (j = 0; j < outputNeuronNum; j++){//出力層
//deltaW更新
hiddenLayer.neurons[i].deltaW[j] =
learnRate * hiddenLayer.neurons[i].output * outputLayer.neurons[j].nodeDelta +
_alpha * hiddenLayer.neurons[i].deltaW[j];
//w更新
hiddenLayer.neurons[i].w[j] += hiddenLayer.neurons[i].deltaW[j]; //重み付け更新
//nodeDelta
hiddenLayer.neurons[i].nodeDelta +=
outputLayer.neurons[j].nodeDelta * hiddenLayer.neurons[i].w[j]; //get hidden neuron's nodeDelta
}
//nodeDelta
hiddenLayer.neurons[i].nodeDelta *=
hiddenLayer.neurons[i].output * (1 - hiddenLayer.neurons[i].output);
}
for (i = 0; i < inputNeuronNum + 1; i++){//入力層
for (j = 0; j < hiddenNeuronNum; j++){//中間層
//deltaW
inputLayer.neurons[i].deltaW[j] =
learnRate * inputLayer.neurons[i].output * hiddenLayer.neurons[j].nodeDelta +
_alpha * inputLayer.neurons[i].deltaW[j];
//w
inputLayer.neurons[i].w[j] += inputLayer.neurons[i].deltaW[j]; //weight update
}
}
}
if (sumError < 1){//精度が高まったら学習終了
//i = iter + 1;
proceedEnd = true;
break;
}
}
proceedNumLabel.text = "Proceed #" + String(iter);
errorChart.data.push(sumError);//チャートにデータ追加
errorChart.draw();//チャート画面更新
debugLabel.text = "Sum of Error : " + String(sumError);
}
draw();//描画
if (proceedEnd == true){//学習が終了した時だけ処理
onTest();
removeEventListener(Event.ENTER_FRAME, onLoop);
}
}
//描画
private function draw():void{
var lineThick:Number;
var lineColor:uint;
const c1:uint = 0x2a80b9;
const c2:uint = 0xa31605;
var i:int;
var j:int;
lineShape.graphics.clear();
this.graphics.beginFill( color );
this.graphics.drawRect(220,70,54,74);
this.graphics.endFill();
for(i=0;i<inputNeuronNum;i++){
if(0.65<inputLayer.neurons[i].output)inputLayer.neurons[i].draw(0xff0000);
else inputLayer.neurons[i].draw(0xffffff);
}
for(i=0;i<hiddenNeuronNum+1;i++){
if(1<hiddenLayer.neurons[i].input)hiddenLayer.neurons[i].draw(0xff0000);
else hiddenLayer.neurons[i].draw(0xffffff);
}
for(i=0;i<outputNeuronNum;i++){
if(1<outputLayer.neurons[i].input)outputLayer.neurons[i].draw(0xff0000);
else outputLayer.neurons[i].draw(0xffffff);
}
//入力層-中間層
for (i = 0; i < inputNeuronNum+1; i++){//入力層
for (j = 0; j < hiddenNeuronNum; j++){//中間層
//wが0以上なら赤 マイナスなら青
lineColor = inputLayer.neurons[i].w[j] >= 0 ? c1 : c2;//色
//wの値が0から離れているほど太く描画する
lineThick = Math.abs(inputLayer.neurons[i].w[j]);
lineThick = lineThick/10 > 3 ? 3 : ( lineThick < 0.1 ? 0.1 : lineThick/10 );//太さ
lineShape.graphics.lineStyle(lineThick, lineColor,0.8);
lineShape.graphics.moveTo(inputLayer.neurons[i].x, inputLayer.neurons[i].y);
lineShape.graphics.lineTo(hiddenLayer.neurons[j].x, hiddenLayer.neurons[j].y);
}
//if(0.0==inputLayer.neurons[i].w)inputLayer.neurons[i].draw(0xff0000);
//else inputLayer.neurons[i].draw(0xffaaff);
}
//中間層-出力層
for (i = 0; i < hiddenNeuronNum + 1; i++){//中間層
for (j = 0; j < outputNeuronNum; j++){//出力層
lineColor = hiddenLayer.neurons[i].w[j] >= 0 ? c1 : c2;
lineThick = Math.abs(hiddenLayer.neurons[i].w[j]);
lineThick = lineThick/10 > 3 ? 3 : ( lineThick < 0.1 ? 0.1 : lineThick/10 );
lineShape.graphics.lineStyle(lineThick, lineColor);
lineShape.graphics.moveTo(hiddenLayer.neurons[i].x, hiddenLayer.neurons[i].y);
lineShape.graphics.lineTo(outputLayer.neurons[j].x, outputLayer.neurons[j].y);
}
}
}
//^^^ シグモイド関数 ^^^^
public function sigmoid(n:Number):Number{
return 1 / (1 + 1 / Math.exp(n));
}
//^^^^^^^^^^^^^
private function printRandomNumber(n:int, k:int) : Array{
var original:Array= new Array();
var result:Array= new Array();
var i:int;
for(i=0;i<n;i++){
original.push(i);//整数を順に追加
}
for(i=0;i<k;i++){
var randInt:int = Math.random()*(n-i) + i;//乱数生成
//並べ替え
var temp:Object = original[i];
original[i] = original[randInt];
original[randInt] = temp;
result.push(original[i]);
}
return result;
}
}
}
//^^^^^ ニューロンクラス ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
import flash.display.Shape;
class Neuron extends Shape{
public var w:Object;
public var deltaW:Object;
public var updateValue:Object;
public var prevGradient:Object;
public var input:Number;
public var output:Number;
public var error:Number;
public var nodeDelta:Number;
public var gradient:Object;
public var gradientChanged:Object;
public var biased:Boolean = false;
//インスタンス
public function Neuron(n:int){
draw();
this.w = new Object();
this.deltaW = new Object();
this.updateValue = new Object();
this.prevGradient = new Object();
this.gradient = new Object();
this.gradientChanged = new Object();
if(n > 0){
for(var i:int = 0; i < n; i++){
this.w[i] = Math.random() * 2 - 1;
this.deltaW[i] = 0;
this.updateValue[i] = 0.01;
this.gradient[i] = 0;
this.prevGradient[i] = 0;
this.gradientChanged[i] = 0;
}
}
}
//リセット
public function resetW(n:int):void{
for(var i:int=0; i<n; i++){
this.w[i] = Math.random() * 2 - 1;
this.deltaW[i] = 0;
this.updateValue[i] = 0.01;
this.gradient[i] = 0;
this.prevGradient[i] = 0;
this.gradientChanged[i] = 0;
this.error = 0;
this.nodeDelta = 0;
}
}
public function draw(col:uint=0xffffff):void{
//描画
this.graphics.clear();
this.graphics.lineStyle(2, 0);
this.graphics.beginFill(col);
this.graphics.drawCircle(0, 0, 6);
this.graphics.endFill();
}
}
//^^^^ レイヤークラス ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
//階層
class Layer{
public var neurons:Array;//ニューロンのリスト
public function Layer(){
this.neurons = [];
}
}