New Object Early Community Build #2: FluidPolynomialRegressor (NOW WITH SC TOO)

Another experimental object with the usual caveats: limited docs, examples, might change, might die, etc

Introducing FluidPolynomialRegressor: a dataset process enabling 1d to 1d regression via polynomials. if more than 1d is provided, it will be done ‘in parallel’. Implemented by @lewardo and quality docs by @MattS6464 - this one has the example in the helpfile

Again Max, MacOS and Windows, although only help is missing for Pd and SC for this one so any eager user of these CCEs is welcome to jump in, I can provide the binaries.

fluid.polynomialregressor.zip (4.1 MB)

1 Like

(if a Max person wants to do a preset interpolator for another tab that would be ace, like in is preset number 1 2 3 4 (duplicated to match the number of output dimensions) and the output is a parameter per dimension)

Does this require one of the nightlies install to work, or just add/replace the bits as mentioned?

Also, is it UB2 friendly?

edit:
it seems to work fine on the vanilla 1.0.6 on an M2.

I’ve done fat this time indeed

1 Like

A couple thoughts on the help file. (learn page link is not built yet correct? It points to a dead page at the moment)

The extract_data bit looks like it could be made much more generic like this:

As the existing bit only works if you’ve named your entries the same way (I most certainly don’t!). The bit on the right is label name agnostic and will dump the whole length. (If less are wanted, a filter can be put after the zl nth 2)

In order to predict, does a dummy dataset need to be made ahead of time? Wouldn’t the interface be cleaner if you could just specify a size or dimensionality you want to predict to as an argument instead? Either that or having an easier way to make fake/dummy datasets outside of needing to whip up a little uzi/sprintf helper patch every time you want to predict.

edit:
I could be misunderstanding here. Is the 25point version also meant to mirror the shape you are wanting to regress to? As in, do you need to feed it data, then manually create what you think the regression will be before predict-ing? Or is the data in there arbitrary?

The first tab should hide all that mess at the top (similar to the p Generate_Data subpatch on the second tab. Most of the first tab (with regards to visual info/noise) is random data generation.

So the explanation is useful in a way, but I’m not entirely sure what it does, like in a musical sense. The reference file is also quite math/jargon heavy (is this a typo? It’s in there a couple of times: x to :math"y"). Granted, I’m not the mathiest dude out there but I don’t know what this means: " Linear regression is a special case when the degree of the polynomial is 1, meaning the highest power of x fitted is 1 (it find the best :mathy = mx + c to fit the data)".

I know @tremblap loves his didactic non-sounding producing help patches where “push this button to a see a number, now push this button to see a number that’s slightly different” is the thing being presented, but having some sound-producing stuff would be helpful towards understanding this.

(Obviously this is a work-in-progress, so these comments are meant to be helpful)

Oooooohh, over here, over here! :wave:

SC pretty please!

1 Like

I’ll compile while I practice the bass, then will write a cursory class def. which OS are you on @mccrmck so I start with that one?

Currently enjoying a cask-aged macOS 13.5 (M1) from 2022 - should pair well with non-linear regression, no?

2 Likes

ok here it is with very very very rough example and not tested in kr mode. but it should get you started on a fat version (and maybe @tedmoore or @spluta will get excited too)

Archive.zip (3.8 MB)

2 Likes

Ah perhaps the example didn’t make it into the helpfile?

strange it is in the file but doesn’t parse. for now:

code::
s.boot;

// 10 random points and their square
~somepoints = 10.collect{var x = 1.0.rand; [x, x**2]}.flop

// load the ins and the outs in 1D datasets
~in = FluidDataSet(s).load(Dictionary.newFrom(["cols", 1, "data", Dictionary.newFrom(~somepoints[0].collect{|j,i|[i.asSymbol, j]}.flat)]))

~out = FluidDataSet(s).load(Dictionary.newFrom(["cols", 1, "data", Dictionary.newFrom(~somepoints[1].collect{|j,i|[i.asSymbol, j]}.flat)]))

~in.print;~out.print

~polyreg = FluidPolynomialRegressor(s);

~polyreg.fit(~in, ~out, {\done.postln});

// 100 points to draw the function

~question = FluidDataSet(s).load(Dictionary.newFrom(["cols", 1, "data", Dictionary.newFrom(100.collect{|i|[i,i/100]}.flat)]))
~answer = FluidDataSet(s)

~polyreg.predict(~question, ~answer, {\done.postln});

~arrayedanswer = Array.fill(100,0)
~answer.dump{|x|x["data"].keysValuesDo{|k,v|~arrayedanswer[k.asInteger]=v}}
~arrayedanswer.flat.plot

//compare with the real function
100.collect{|x|(x/100)**2}.plot
::
1 Like

this version should be better in term of formating. the content is to be revised for more musician friendly approach eventually thanks for confirming

fluid.polynomialregressor~.maxref.xml.zip (3.0 KB)

1 Like

The

seems to work over here! The first time I ran it the first index of ~arrayedanswer was very slightly below zero (-7e-11 or something) so the plot origin was offset a bit…which maybe spoiled the dramatic reveal? It’s easy enough to avoid with ...plot(minval:0) of course…

Thanks for sharing! I’ll try to play around a bit with this tomorrow and have a report on your desk by the weekend!

1 Like

Interesting and potentially very useful! I found something strange when trying to set the @degree higher than 9:
fluid.polynomialregressor_test

1 Like

@weefuzzy predicted that someone would see that - it is a known problem of having crazy overfitting (you are doing a 10th order polynomial aka ax11+bx10+cx9+dx8+ex7+fx6+gx5+hx4+ix3+jx2+kx1+lx0 to match a little number of items so it freaks out.

now try that tikhonov number around 1 and see how it behaves. we’ll explain better but it helps agains overfitting IIUC

1 Like

Ah I see, thanks! Indeed tikhonov did it. I made a little demo patch where you can draw a squiggly line of noisy points in the left plotter and ask for the line that fits them on the right. Then you can turn the dial with the polynomial degree and see the effect.
fluid.polynomialregressor_fun
polynomial_fun.maxpat (18.9 KB)

<pre><code>
----------begin_max5_patcher----------
1965.3oc4assjaZCF95ceJz3ocljVGWj.wgdW6qQSlcvFYuZCf7BBG6lI4Yu
5.fs2EikMB2MStX8AgL+56+3m9E6Wu+tIyYaIkS.+I3e.2c2Wu+t6TCIG3t5
ue2jr3sKRiKUSaxBVVFImOYp9ZbxVtZ70ow6.egxeDjPVUPHMSflntLa9Sev
2uYv7pLZdJgqtkv8Cxp3Mi5TO55X9hGo4qdnfrfqWngA3YH7T.1el30H+YNS
AHmYNfOI+Ie696kuL0P7HAv7JNmk2r3DRSCoVHrJmIjdJcwmOb8pWr7cqI5k
kX95+T+9OUOqBVUdBQoD7Eqw5QmuZAKkUn+gPI.blEDFhbwxO4E3fCCmJuP6
8oUQ6dnp+f6hyLWuHGzzN9vg2mCsGASZUxEwYDNo3ARd77TxgFfkrbdI8eUi
AcaQPmlP4byE2IkH9qBZbJ3uYoIMhIkrJdwtiV2ca7c6w3iQR0k11Ks5Cv1e
Je4kTNXGfy.amBhyS.qKHIzEbPLHklS.7Gi4.wbJEeh.VTUro0aWd8EBKt59
fZPQAoTHlXNkk+vQyvsCihmcBR78j1Jf3UgdBhvx2bc+YIHAYufD7OfAI9vn
YAxLjswGCHJIm7Egh3UAIbvb.cxoLyyiyW8Jy7gpUzE3li5qVfmDbPuPEFiT
YEPWIRSoaHyRDFjl01l3hVK0qtXmt1KSYwm.wv9bjf1TaDoBV7zuFdfecY7F
RxCwbdAUDNS1+oxZsRsZQh7zJBaYyvMie3hOkkupU6rlktKmkIclOp9+Q+hL
g1Vgf1X7itb4irh8QFm91znxgcbspbQdY9t5nS80UdAMuYGmeQBKtnPPQbd4
RVQ1SLZdutEc6Q3zYL.5Rs5Xs81MTkwG5DoBEP1MbeN.c1fc06cgTbjch1qQ
ZnFuCKX+TE+iSR.3eEjynkcQhEGXm5ytPjFDGfHqSjIoH9K.Ztf3R4yUzUqR
2oYvHFRRcIkrjCVmx3hHmdYvbH9sDIdeEscXjldhpB0OOzSfVidB18GP5IdH
E6DjuEXmbrcu6TScp3bLQwYfON7roqpYg407pMSKuLshlLKIlGWR3eGrc2L4
lUt7ZQdgVJCsiZCG9HExgHzfpEcsFWO7My35oyiOPqaForLdE4Ul2Eoj3Bwl
PSDYxyWQ.NhUzwzN.ZKNX6Ks8mdWmnS5czotz+BXobd8kac7tlopsYobT3vy
Ujhce+JhEBrJaEWrdKJ5Phaerf2MKV.Fc6xzc0I5P1JQmpCT951QAg9ig6rd
aFIUYquBfBsytK7i7zEwT1V+gs4hSkmqeH1I7brZZIOO+58TLD38TYE8xsTt
gFwgilRZ4koCFdA08gYmcQgGrSDTT6asK44rhDRQ6FquQ4ubCtY4uPgdu8Yp
4hsZ0Iw9TrASsSEXKak9Vvtos8Pe6Yyb2In8rZzNBoaUh9sQkDhnR0UXisTQ
JOGUVs5hT0UmGWzt8JPKztnUSzZfn87LxE6+sJKWjAZOgbQTs7Lid0bfuXN6
5saKlERfrDubeXMCFcHQTjly5aPd4HKwKuAw9N1fWduHdeawKjsytrjU7c.m
TxuBz6Z079HLVg9.z3Qbcd0xk6ombAPEYUJMXjpmDAiX5uTZIGIf6oo2nUEc
h1KI8G7rn0SyWebrqhJ2qI4I.QT7ZFMmeYUwQNVBov.oAEq4pFENFHs+izoK
zAirSB4FzgcsvQZ1aYrKDeVpfCJLT0z+vlBMu0bSgdVxMUm8ID8lxK00RdoZ
vo6s0.cR64ILH8rG5X2nzRDn0nzKLZDCEclEE5fkGYjHxe+i+iglSnkXA5qe
Zgz0Hgihy5Fx10EfeYID76f2InCmvxd2GfNNNe7ixis1w48f+P89LvuITGN3
2eY5hHaF1VevrH+fwPUn372C0HUNqSbZjGf3tOzFbmH16rwx5cC6CGk8MAkN
3vHD7BcvCrj+s9Dl0c4.FfuE6D9J13uk5sScI1lcBOJ8l94TZFXOiNC6ZqMK
93hCzcv5+gd1BeSzx1wsisxmNr9zMshQNQcEa5mejky1z0yMCb7aiaPc4q5t
6gG1F9tb3exm7cL5F.d81+fQ39.uZQqZ6zKdp+U2Q43GqQJYUEKZfbyCtIXu
GVBojSyUGx7ASRdlifSo0uHIAOijvHaHInAXx2FRRdzxmWRN1.SNlXmvVPRx
GtuaiGgRRmyiP1WzgKISzdM.eXRxj3IYu+Ftjf2JI03V0uGgM7xcMIdxyFdD
MNv8JIWa3kC8MQRgGMoyTm2X0I1f.LOnMTmlDf4ZizSdlnNaxKOLIAubCWFM
Q0gp5pwA5Get5d+g0OsEG+sf5mU1fldZOvUcSDe+qZaneb8tUIfLJDB2cDDb
XZSGyk7vDjIPDYCylIk2gnwPYZh+RzsBgviRMZoDtHipIaClmHS3Y.qIXMrf
OS3s+BfaI0YnoXz5RFZhODxFTdLR8J6xeWdqvwOs5ILrngIYuKVxunxpmpdo
OTu63vW8EcYUOaUUEZRUUnMnKZjOu6XjdFZRJkPa3xaBCtQAgFH3fyWWP2zk
30q2PJJq+0JYNIK9I8+CMgSUeklq+ppE9SJHanMyWORbwhGobxBdUgtqhaqO
vnIYLgfyqn0fVfVgHUMyR1Axx0wZfo5408e69+C.4CNjA
-----------end_max5_patcher-----------
</code></pre>
1 Like

wow this is some of the most elegant use of fluiddatasetquery I’ve seen - and @lewardo got all excited because it works so beautifully :slight_smile:

@MattS6464 this is a great inspiration for the helpfile maybe tab 2 - that is if @balintlaczko allows us…

1 Like

Of course, glad to help!

1 Like

Tried to copy @balintlaczko’s homework without actually studying it:

goodArtistsCopy

polyReg.scd (2.5 KB)

Is my understanding correct that the Fluid objects’ action function is called after the OSC message is sent to the server, and not after a response from the server? I had to add a limiter to the degree and tikhonov knobs so as to avoid ERROR: Primitive '_FileLength' failed. and ERROR: FluidPolynomialRegressor - No data fitted …though they’re still thrown occasionally. Or perhaps my approach is totally wrong to begin with?

If I get a chance in the next days I can try to copy Bálint’s approach with FluidDatasetQuery; perhaps that will be more consistent!

2 Likes

the function is supposed to be called after the response. but I might have messed up the implementation.

but the tikhonov factor and the degree would refinitely need to refit, so bundling the param change with the fit message is probably the most efficient way to send that to the server (although I always need to reaccomodate myself to that paradigm)