You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
190 lines
5.5 KiB
Plaintext
190 lines
5.5 KiB
Plaintext
TITLE:: FluidMLPClassifier
|
|
summary:: Classification with a multi-layer perceptron
|
|
categories:: Machine learning
|
|
related:: Classes/FluidMLPRegressor, Classes/FluidDataSet
|
|
|
|
Perform classification between a link::Classes/FluidDataSet:: and a link::Classes/FluidLabelSet:: using a Multi-Layer Perception neural network.
|
|
|
|
CLASSMETHODS::
|
|
|
|
METHOD:: new
|
|
Creates a new instance on the server.
|
|
|
|
ARGUMENT:: server
|
|
The link::Classes/Server:: on which to run this model.
|
|
|
|
ARGUMENT:: hidden
|
|
An link::Classes/Array:: that gives the sizes of any hidden layers in the network (default is two hidden layers of three units each).
|
|
|
|
ARGUMENT:: activation
|
|
The activation function to use for the hidden layer units. Beware of the permitted ranges of each: relu (0->inf), sigmoid (0->1), tanh (-1,1).
|
|
|
|
ARGUMENT:: maxIter
|
|
The maximum number of iterations to use in training.
|
|
|
|
ARGUMENT:: learnRate
|
|
The learning rate of the network. Start small, increase slowly.
|
|
|
|
ARGUMENT:: momentum
|
|
The training momentum, default 0.9
|
|
|
|
ARGUMENT:: batchSize
|
|
The training batch size.
|
|
|
|
ARGUMENT:: validation
|
|
The fraction of the DataSet size to hold back during training to validate the network against.
|
|
|
|
METHOD:: identity, sigmoid, relu, tanh
|
|
A set of convinience constants for the available activation functions.
|
|
|
|
INSTANCEMETHODS::
|
|
|
|
PRIVATE:: init, uid
|
|
|
|
METHOD:: fit
|
|
Train the network to map between a source link::Classes/FluidDataSet:: and a target link::Classes/FluidLabelSet::
|
|
|
|
ARGUMENT:: sourceDataSet
|
|
Source data
|
|
|
|
ARGUMENT:: targetLabelSet
|
|
Target data
|
|
|
|
ARGUMENT:: action
|
|
Function to run when training is complete
|
|
|
|
returns:: The training loss, or -1 if training failed
|
|
|
|
METHOD:: predict
|
|
Apply the learned mapping to a DataSet (given a trained network)
|
|
|
|
ARGUMENT:: sourceDataSet
|
|
Input data
|
|
|
|
ARGUMENT:: targetLabelSet
|
|
Output data
|
|
|
|
ARGUMENT:: action
|
|
Function to run when complete
|
|
|
|
METHOD:: predictPoint
|
|
Apply the learned mapping to a single data point in a link::Classes/Buffer::
|
|
|
|
ARGUMENT:: sourceBuffer
|
|
Input point
|
|
|
|
ARGUMENT:: action
|
|
A function to run when complete
|
|
|
|
METHOD:: clear
|
|
This will erase all the learning done in the neural network.
|
|
|
|
ARGUMENT:: action
|
|
A function to run when complete
|
|
|
|
EXAMPLES::
|
|
|
|
code::
|
|
(
|
|
~classifier=FluidMLPClassifier(s).hidden_([6]).activation_(FluidMLPClassifier.tanh).maxIter_(1000).learnRate_(0.1).momentum_(0.1).batchSize_(50).validation_(0);
|
|
~sourcedata= FluidDataSet(s);
|
|
~labels = FluidLabelSet(s);
|
|
~testdata = FluidDataSet(s);
|
|
~predictedlabels = FluidLabelSet(s);
|
|
)
|
|
//Make some clumped 2D points and place into a DataSet
|
|
(
|
|
~centroids = [[0.5,0.5],[-0.5,0.5],[0.5,-0.5],[-0.5,-0.5]];
|
|
~categories = [\red,\orange,\green,\blue];
|
|
~trainingset = Dictionary();
|
|
~labeldata = Dictionary();
|
|
4.do{ |i|
|
|
64.do{ |j|
|
|
~trainingset.put("mlpclass"++i++\_++j, ~centroids[i].collect{|x| x.gauss(0.5/3)});
|
|
~labeldata.put("mlpclass"++i++\_++j,[~categories[i]]);
|
|
}
|
|
};
|
|
~sourcedata.load(Dictionary.with(*[\cols->2,\data->~trainingset]),action:{~sourcedata.print});
|
|
~labels.load(Dictionary.with(*[\cols->1,\data->~labeldata]),action:{~labels.print});
|
|
)
|
|
|
|
//Fit the classifier to the example DataSet and labels, and then run prediction on the test data into our mapping label set
|
|
~classifier.fit(~sourcedata,~labels,action:{|loss| ("Trained"+loss).postln});
|
|
|
|
//make some test data
|
|
(
|
|
~testset = Dictionary();
|
|
4.do{ |i|
|
|
64.do{ |j|
|
|
~testset.put("mlpclass_test"++i++\_++j, ~centroids[i].collect{|x| x.gauss(0.5/3)});
|
|
}
|
|
};
|
|
~testdata.load(Dictionary.with(*[\cols->2,\data->~testset]),action:{~testdata.print});
|
|
)
|
|
|
|
//Run the test data through the network, into the predicted labelset
|
|
~classifier.predict(~testdata,~predictedlabels,action:{"Test complete".postln});
|
|
OSCFunc.trace(true,true)
|
|
OSCFunc.allEnabled
|
|
//get labels from server
|
|
~predictedlabels.dump(action:{|d|~labelsdict = d["data"]};~labelsdict.postln);
|
|
|
|
//Visualise: we're hoping to see colours neatly mapped to quandrants...
|
|
(
|
|
c = Dictionary();
|
|
c.add("red"->Color.red);
|
|
c.add("blue"->Color.blue);
|
|
c.add("green"->Color.green);
|
|
c.add("orange"->Color.new255(255, 127, 0));
|
|
e = 200 * ((~centroids + 1) * 0.5).flatten(1).unlace;
|
|
w = Window("scatter", Rect(128, 64, 200, 200));
|
|
w.drawFunc = {
|
|
Pen.use {
|
|
~testset.keysValuesDo{|k,v|
|
|
var x = v[0].linlin(-1,1,200,0).asInteger;
|
|
var y = v[1].linlin(-1,1,200,0).asInteger;
|
|
var r = Rect(x,y,5,5);
|
|
Pen.fillColor = c.at(~labelsdict[k][0]);
|
|
Pen.fillOval(r);
|
|
}
|
|
}
|
|
};
|
|
w.refresh;
|
|
w.front;
|
|
)
|
|
|
|
// single point transform on arbitrary value
|
|
~inbuf = Buffer.loadCollection(s,0.5.dup);
|
|
~classifier.predictPoint(~inbuf,{|x|x.postln;});
|
|
::
|
|
subsection::Querying in a Synth
|
|
This is the equivalent of code::predictPoint::, but wholly on the server
|
|
code::
|
|
(
|
|
{
|
|
var trig = Impulse.kr(5);
|
|
var point = WhiteNoise.kr(1.dup);
|
|
var inputPoint = LocalBuf(2);
|
|
var outputPoint = LocalBuf(1);
|
|
Poll.kr(trig, point, [\pointX,\pointY]);
|
|
point.collect{ |p,i| BufWr.kr([p],inputPoint,i)};
|
|
~classifier.kr(trig,inputPoint,outputPoint);
|
|
Poll.kr(trig,BufRd.kr(1,outputPoint,0,interpolation:0),\cluster);
|
|
Silent.ar;
|
|
}.play;
|
|
)
|
|
|
|
// to sonify the output, here are random values alternating quadrant.
|
|
(
|
|
{
|
|
var trig = Impulse.kr(MouseX.kr(0,1).exprange(0.5,ControlRate.ir /2).poll(trig: 2,label: "Query Frequency"));
|
|
var step = Stepper.kr(trig,max:3);
|
|
var point = TRand.kr(-0.1, [0.1, 0.1], trig) + [step.mod(2).linlin(0,1,-0.6,0.6),step.div(2).linlin(0,1,-0.6,0.6)] ;
|
|
var inputPoint = LocalBuf(2);
|
|
var outputPoint = LocalBuf(1);
|
|
point.collect{|p,i| BufWr.kr([p],inputPoint,i)};
|
|
~classifier.kr(trig,inputPoint,outputPoint);
|
|
SinOsc.ar((BufRd.kr(1,outputPoint,0,interpolation:0) + 69).midicps,mul: 0.1)
|
|
}.play;
|
|
)
|
|
:: |