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.

206 lines
6.1 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, [6], FluidMLPClassifier.tanh, 1000, 0.1, 0.1, 50, 0);
~sourcedata= FluidDataSet(s,\mlpclassify_help_examples);
~labels = FluidLabelSet(s,\mlpclassify_help_labels);
~testdata = FluidDataSet(s,\mlpclassify_help_test);
~predictedlabels = FluidLabelSet(s,\mlpclassify_help_mapping);
)
//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]));
~labels.load(Dictionary.with(*[\cols->1,\data->~labeldata]));
)
//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]));
)
//Run the test data through the network, into the predicted labelset
~classifier.predict(~testdata,~predictedlabels,action:{"Test complete".postln});
//get labels from server
~predictedlabels.dump(action:{|d| ~labelsdict = d["data"]});
//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;
)
::
subsection::Server Side Queries
This is the equivalent of predictPoint, but wholly on the server
FluidMLPClassifier is accessed via its own synth, so we need to use
a bus to communicate with it. The inBus receives a trigger to query, using data
from inBuffer; a trigger is then send to outBus with the prediction in outBuffer
code::
(
~ib = Bus.control(s); // input bus
~ob = Bus.control(s); //output bus
~inpPoint = Buffer.alloc(s,2);
~outPoint = Buffer.alloc(s,1);
)
//We make two Synths. One, before FluidKMeans, generates a random point and sends
//a trigger to query. The second, after FluidKMeans, gives us the predicted cluster //triggering upadtes from the outBus
(
//Set properties on FluidMLPClassifier:
~classifier.inBus_(~ib).outBus_(~ob).inBuffer_(~inpPoint).outBuffer_(~outPoint);
//pitching
{
var trig = Impulse.kr(5);
var point = WhiteNoise.kr(1.dup);
Poll.kr(trig, point, [\pointX,\pointY]);
point.collect{ |p,i| BufWr.kr([p],~inpPoint,i)};
Out.kr(~ib.index,[trig]);
}.play(~classifier.synth,addAction:\addBefore);
//catching
{
Poll.kr(In.kr(~ob),Latch.kr(BufRd.kr(1,~outPoint,0,interpolation:0),In.kr(~ob)),\cluster);
}.play(~classifier.synth,addAction:\addAfter);
)
// to sonify the output, here are random values alternating quadrant.
(
//Set properties on FluidMLPClassifier:
~classifier.inBus_(~ib).outBus_(~ob).inBuffer_(~inpPoint).outBuffer_(~outPoint);
//pitching
{
var trig = Impulse.kr(MouseX.kr(0,1).exprange(0.5,ControlRate.ir /2).poll);
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)] ;
point.collect{|p,i| BufWr.kr([p],~inpPoint,i)};
Out.kr(~ib.index,[trig]);
T2A.ar(trig)*0.1;
}.play(~classifier.synth,addAction:\addBefore);
//catching
{
SinOsc.ar((Latch.kr(BufRd.kr(1,~outPoint,0,interpolation:0),In.kr(~ob)) + 69).midicps,mul: 0.1);
}.play(~classifier.synth,addAction:\addAfter);
)
::