Un framework de machine learning en PHP

Rubix ML

Par

Joris Langlois

Date

27/01/2026

Conférence

01

Qui suis-je ?

04

Premier modèle

Trions des fleurs avec des arbres

02

Introduction

Plus qu'un concurrent, un pas concurrent

05

Deuxième modèle

03

Définitions

Qu'est ce qu'un modèle prédictif ?

Etapes de détermination d'un modèle

06

Conclusion

Rubix ML : Un framework de machine learning en PHP _ Sommaire

Et questions si vous en avez !

Un peu plus corsé si on a le temps

Rubix ML :  un framework de machine learning en PHP - AFUP 27 janvier 2026

Qui suis-je ?

Rubix ML :  un framework de machine learning en PHP - AFUP 27 janvier 2026

Qui suis-je ?

En activité depuis 2013

Petit passage par les pays baltes entre

2020 et 2022 (outl1ne, Optimist Digital, ...)

Chez KNP Labs depuis 2018

Développeur. 

Rubix ML :  un framework de machine learning en PHP - AFUP 27 janvier 2026

Qui suis-je ?

Ecole d'ingénieur du Conservatoire National des Arts et Métiers (EiCNAM) depuis 2017

Ingéniérie des systèmes decisionnels, analyse prédictive & machine learning

Auditeur. 

Certificat de spécialisation : Union Européenne

Rubix ML :  un framework de machine learning en PHP - AFUP 27 janvier 2026

Introduction

Rubix ML :  un framework de machine learning en PHP - AFUP 27 janvier 2026

Créé par Andrew DalPino en 2018

40+ modèles pour de l'apprentissage supervisé / non supervisé 

Introduction _ Présentation

Rubix ML :  un framework de machine learning en PHP - AFUP 27 janvier 2026

Framework de machine learning / deep learning en PHP

Ce n'est PAS un concurrent de PyTorch, Keras, TensorFlow...

Définitions

Rubix ML :  un framework de machine learning en PHP - AFUP 27 janvier 2026

Définitions _ Qu'est ce qu'un modèle prédictif ?

Rubix ML :  un framework de machine learning en PHP - AFUP 27 janvier 2026

...généralement à la place d'un humain...

...mais de façon tout aussi fiable !

Fonction mathématique / algorithme qui permet de prendre une décision...

Quelques grands problèmes : classification, régression, ordonnancement, prédictions structurées...

Par une application méticuleuse d'un ensemble de règles (systèmes experts)

Par détermination d'un modèle a partir d'un ensemble d'observations dont on connaît l'issue (apprentissage supervisé)...

...ou sans connaissance à priori sur les données (apprentissage non suppervisé) !

Définitions _ Exemple : un problème de classification ?

Rubix ML :  un framework de machine learning en PHP - AFUP 27 janvier 2026

Définitions _ Exemple : premier modèle, premiers ennuis

Rubix ML :  un framework de machine learning en PHP - AFUP 27 janvier 2026

Définitions _ Détermination d'un modèle

Rubix ML :  un framework de machine learning en PHP - AFUP 27 janvier 2026

Choix d'un modèle / d'une famille de modèle

Collecte et préparation des données

Entraînement et optimisation

Validation

Déploiement et maintenance

Choix des objectifs (précision, coût calculatoire, explicabilité, aspects éthiques) 

Définitions _ Détermination d'un modèle

Rubix ML :  un framework de machine learning en PHP - AFUP 27 janvier 2026

Choix des objectifs (précision, coût calculatoire, explicabilité, aspects éthiques) 

Choix d'un modèle / d'une famille de modèle

Collecte et préparation des données

Entraînement et optimisation

Validation

Déploiement et maintenance

Premier modèle

Rubix ML :  un framework de machine learning en PHP - AFUP 27 janvier 2026

Premier modèle _ Définition du problème

Rubix ML :  un framework de machine learning en PHP - AFUP 27 janvier 2026

5.1,3.5,1.4,0.2,Iris-setosa
4.9,3.0,1.4,0.2,Iris-setosa
4.7,3.2,1.3,0.2,Iris-setosa
4.6,3.1,1.5,0.2,Iris-setosa
5.0,3.6,1.4,0.2,Iris-setosa
7.0,3.2,4.7,1.4,Iris-versicolor
6.4,3.2,4.5,1.5,Iris-versicolor
6.9,3.1,4.9,1.5,Iris-versicolor
5.5,2.3,4.0,1.3,Iris-versicolor
4.9,2.5,4.5,1.7,Iris-virginica
6.4,2.7,5.3,1.9,Iris-virginica
6.8,3.0,5.5,2.1,Iris-virginica
5.7,2.5,5.0,2.0,Iris-virginica
5.8,2.8,5.1,2.4,Iris-virginica
6.4,3.2,5.3,2.3,Iris-virginica
6.5,3.0,5.5,1.8,Iris-virginica
7.7,3.8,6.7,2.2,Iris-virginica
7.7,2.6,6.9,2.3,Iris-virginica
6.0,2.2,5.0,1.5,Iris-virginica
6.9,3.2,5.7,2.3,Iris-virginica
5.6,2.8,4.9,2.0,Iris-virginica
7.7,2.8,6.7,2.0,Iris-virginica
6.3,2.7,4.9,1.8,Iris-virginica
6.7,3.3,5.7,2.1,Iris-virginica
7.2,3.2,6.0,1.8,Iris-virginica
6.2,2.8,4.8,1.8,Iris-virginica
6.1,3.0,4.9,1.8,Iris-virginica

Variable expliquée qualitative :

y ∈ {iris-setosa, iris-versicolor, iris-virginica}

150 observations en tout

Données tabulaires

4 variables explicatives quantitatives continues :

X  

Classes parfaitement équilibrées (50 obs. de chaque)

Premier modèle _ Visualisation du problème

Rubix ML :  un framework de machine learning en PHP - AFUP 27 janvier 2026

Premier modèle _ Arbre de décision (CART)

Rubix ML :  un framework de machine learning en PHP - AFUP 27 janvier 2026

x (petal w) > 0.8

Premier modèle _ Arbre de décision (CART)

Rubix ML :  un framework de machine learning en PHP - AFUP 27 janvier 2026

x (petal w) > 0.8

y (petal L) > 5.2

Premier modèle _ Arbre de décision (CART)

Rubix ML :  un framework de machine learning en PHP - AFUP 27 janvier 2026

x (petal w) > 0.8

y (petal L) > 5.2

x (petal w) > 1.7

Premier modèle _ Arbre de décision (CART)

Rubix ML :  un framework de machine learning en PHP - AFUP 27 janvier 2026

x (petal w) > 0.8

y (petal L) > 5.2

x (petal w) > 1.7

y (petal L) > 4.9

Premier modèle _ Arbre de décision (CART)

Rubix ML :  un framework de machine learning en PHP - AFUP 27 janvier 2026

x (petal w) > 0.8

y (petal L) > 5.2

x (petal w) > 1.7

y (petal L) > 4.9

Premier modèle _ Arbre de décision (CART)

Rubix ML :  un framework de machine learning en PHP - AFUP 27 janvier 2026

H = 4

Premier modèle _ Enfin un peu de code

Rubix ML :  un framework de machine learning en PHP - AFUP 27 janvier 2026

5.1,3.5,1.4,0.2,Iris-setosa
4.9,3.0,1.4,0.2,Iris-setosa
4.7,3.2,1.3,0.2,Iris-setosa
4.6,3.1,1.5,0.2,Iris-setosa
5.0,3.6,1.4,0.2,Iris-setosa
7.0,3.2,4.7,1.4,Iris-versicolor
6.4,3.2,4.5,1.5,Iris-versicolor
6.9,3.1,4.9,1.5,Iris-versicolor
5.5,2.3,4.0,1.3,Iris-versicolor
4.9,2.5,4.5,1.7,Iris-virginica
6.4,2.7,5.3,1.9,Iris-virginica
6.8,3.0,5.5,2.1,Iris-virginica
5.7,2.5,5.0,2.0,Iris-virginica
5.8,2.8,5.1,2.4,Iris-virginica
6.4,3.2,5.3,2.3,Iris-virginica
6.5,3.0,5.5,1.8,Iris-virginica
7.7,3.8,6.7,2.2,Iris-virginica
7.7,2.6,6.9,2.3,Iris-virginica
6.0,2.2,5.0,1.5,Iris-virginica
6.9,3.2,5.7,2.3,Iris-virginica
5.6,2.8,4.9,2.0,Iris-virginica
7.7,2.8,6.7,2.0,Iris-virginica
6.3,2.7,4.9,1.8,Iris-virginica
6.7,3.3,5.7,2.1,Iris-virginica
7.2,3.2,6.0,1.8,Iris-virginica
6.2,2.8,4.8,1.8,Iris-virginica
6.1,3.0,4.9,1.8,Iris-virginica
<?php

use Rubix\ML\Classifiers\ClassificationTree;
use Rubix\ML\CrossValidation\Metrics\Accuracy;
use Rubix\ML\Datasets\Labeled;
use Rubix\ML\Extractors\CSV;
use Rubix\ML\Transformers\NumericStringConverter;

srand(0);
//

Premier modèle _ Import des données

Rubix ML :  un framework de machine learning en PHP - AFUP 27 janvier 2026

5.1,3.5,1.4,0.2,Iris-setosa
4.9,3.0,1.4,0.2,Iris-setosa
4.7,3.2,1.3,0.2,Iris-setosa
4.6,3.1,1.5,0.2,Iris-setosa
5.0,3.6,1.4,0.2,Iris-setosa
7.0,3.2,4.7,1.4,Iris-versicolor
6.4,3.2,4.5,1.5,Iris-versicolor
6.9,3.1,4.9,1.5,Iris-versicolor
5.5,2.3,4.0,1.3,Iris-versicolor
4.9,2.5,4.5,1.7,Iris-virginica
6.4,2.7,5.3,1.9,Iris-virginica
6.8,3.0,5.5,2.1,Iris-virginica
5.7,2.5,5.0,2.0,Iris-virginica
5.8,2.8,5.1,2.4,Iris-virginica
6.4,3.2,5.3,2.3,Iris-virginica
6.5,3.0,5.5,1.8,Iris-virginica
7.7,3.8,6.7,2.2,Iris-virginica
7.7,2.6,6.9,2.3,Iris-virginica
6.0,2.2,5.0,1.5,Iris-virginica
6.9,3.2,5.7,2.3,Iris-virginica
5.6,2.8,4.9,2.0,Iris-virginica
7.7,2.8,6.7,2.0,Iris-virginica
6.3,2.7,4.9,1.8,Iris-virginica
6.7,3.3,5.7,2.1,Iris-virginica
7.2,3.2,6.0,1.8,Iris-virginica
6.2,2.8,4.8,1.8,Iris-virginica
6.1,3.0,4.9,1.8,Iris-virginica
<?php

use Rubix\ML\Classifiers\ClassificationTree;
use Rubix\ML\CrossValidation\Metrics\Accuracy;
use Rubix\ML\Datasets\Labeled;
use Rubix\ML\Extractors\CSV;
use Rubix\ML\Transformers\NumericStringConverter;

srand(0);

$iterator = new CSV(__DIR__ . '/../data/iris/iris.data', header: false);

Premier modèle _ Préparation du dataset

Rubix ML :  un framework de machine learning en PHP - AFUP 27 janvier 2026

5.1,3.5,1.4,0.2,Iris-setosa
4.9,3.0,1.4,0.2,Iris-setosa
4.7,3.2,1.3,0.2,Iris-setosa
4.6,3.1,1.5,0.2,Iris-setosa
5.0,3.6,1.4,0.2,Iris-setosa
7.0,3.2,4.7,1.4,Iris-versicolor
6.4,3.2,4.5,1.5,Iris-versicolor
6.9,3.1,4.9,1.5,Iris-versicolor
5.5,2.3,4.0,1.3,Iris-versicolor
4.9,2.5,4.5,1.7,Iris-virginica
6.4,2.7,5.3,1.9,Iris-virginica
6.8,3.0,5.5,2.1,Iris-virginica
5.7,2.5,5.0,2.0,Iris-virginica
5.8,2.8,5.1,2.4,Iris-virginica
6.4,3.2,5.3,2.3,Iris-virginica
6.5,3.0,5.5,1.8,Iris-virginica
7.7,3.8,6.7,2.2,Iris-virginica
7.7,2.6,6.9,2.3,Iris-virginica
6.0,2.2,5.0,1.5,Iris-virginica
6.9,3.2,5.7,2.3,Iris-virginica
5.6,2.8,4.9,2.0,Iris-virginica
7.7,2.8,6.7,2.0,Iris-virginica
6.3,2.7,4.9,1.8,Iris-virginica
6.7,3.3,5.7,2.1,Iris-virginica
7.2,3.2,6.0,1.8,Iris-virginica
6.2,2.8,4.8,1.8,Iris-virginica
6.1,3.0,4.9,1.8,Iris-virginica
<?php

use Rubix\ML\Classifiers\ClassificationTree;
use Rubix\ML\CrossValidation\Metrics\Accuracy;
use Rubix\ML\Datasets\Labeled;
use Rubix\ML\Extractors\CSV;
use Rubix\ML\Transformers\NumericStringConverter;

srand(0);

$iterator = new CSV(__DIR__ . '/../data/iris/iris.data', header: false);

$dataset = Labeled::fromIterator($iterator)
    ->apply(new NumericStringConverter())
    ->dropFeature(0)
    ->dropFeature(0)
    ->randomize();

Premier modèle _ Split train / val

Rubix ML :  un framework de machine learning en PHP - AFUP 27 janvier 2026

5.1,3.5,1.4,0.2,Iris-setosa
4.9,3.0,1.4,0.2,Iris-setosa
4.7,3.2,1.3,0.2,Iris-setosa
4.6,3.1,1.5,0.2,Iris-setosa
5.0,3.6,1.4,0.2,Iris-setosa
7.0,3.2,4.7,1.4,Iris-versicolor
6.4,3.2,4.5,1.5,Iris-versicolor
6.9,3.1,4.9,1.5,Iris-versicolor
5.5,2.3,4.0,1.3,Iris-versicolor
4.9,2.5,4.5,1.7,Iris-virginica
6.4,2.7,5.3,1.9,Iris-virginica
6.8,3.0,5.5,2.1,Iris-virginica
5.7,2.5,5.0,2.0,Iris-virginica
5.8,2.8,5.1,2.4,Iris-virginica
6.4,3.2,5.3,2.3,Iris-virginica
6.5,3.0,5.5,1.8,Iris-virginica
7.7,3.8,6.7,2.2,Iris-virginica
7.7,2.6,6.9,2.3,Iris-virginica
6.0,2.2,5.0,1.5,Iris-virginica
6.9,3.2,5.7,2.3,Iris-virginica
5.6,2.8,4.9,2.0,Iris-virginica
7.7,2.8,6.7,2.0,Iris-virginica
6.3,2.7,4.9,1.8,Iris-virginica
6.7,3.3,5.7,2.1,Iris-virginica
7.2,3.2,6.0,1.8,Iris-virginica
6.2,2.8,4.8,1.8,Iris-virginica
6.1,3.0,4.9,1.8,Iris-virginica
<?php

use Rubix\ML\Classifiers\ClassificationTree;
use Rubix\ML\CrossValidation\Metrics\Accuracy;
use Rubix\ML\Datasets\Labeled;
use Rubix\ML\Extractors\CSV;
use Rubix\ML\Transformers\NumericStringConverter;

srand(0);

$iterator = new CSV(__DIR__ . '/../data/iris/iris.data', header: false);

$dataset = Labeled::fromIterator($iterator)
    ->apply(new NumericStringConverter())
    ->dropFeature(0)
    ->dropFeature(0)
    ->randomize();
    
[ $train, $val ] = $dataset->stratifiedSplit(0.8);

Premier modèle _ Entraînement du modèle

Rubix ML :  un framework de machine learning en PHP - AFUP 27 janvier 2026

5.1,3.5,1.4,0.2,Iris-setosa
4.9,3.0,1.4,0.2,Iris-setosa
4.7,3.2,1.3,0.2,Iris-setosa
4.6,3.1,1.5,0.2,Iris-setosa
5.0,3.6,1.4,0.2,Iris-setosa
7.0,3.2,4.7,1.4,Iris-versicolor
6.4,3.2,4.5,1.5,Iris-versicolor
6.9,3.1,4.9,1.5,Iris-versicolor
5.5,2.3,4.0,1.3,Iris-versicolor
4.9,2.5,4.5,1.7,Iris-virginica
6.4,2.7,5.3,1.9,Iris-virginica
6.8,3.0,5.5,2.1,Iris-virginica
5.7,2.5,5.0,2.0,Iris-virginica
5.8,2.8,5.1,2.4,Iris-virginica
6.4,3.2,5.3,2.3,Iris-virginica
6.5,3.0,5.5,1.8,Iris-virginica
7.7,3.8,6.7,2.2,Iris-virginica
7.7,2.6,6.9,2.3,Iris-virginica
6.0,2.2,5.0,1.5,Iris-virginica
6.9,3.2,5.7,2.3,Iris-virginica
5.6,2.8,4.9,2.0,Iris-virginica
7.7,2.8,6.7,2.0,Iris-virginica
6.3,2.7,4.9,1.8,Iris-virginica
6.7,3.3,5.7,2.1,Iris-virginica
7.2,3.2,6.0,1.8,Iris-virginica
6.2,2.8,4.8,1.8,Iris-virginica
6.1,3.0,4.9,1.8,Iris-virginica
<?php

use Rubix\ML\Classifiers\ClassificationTree;
use Rubix\ML\CrossValidation\Metrics\Accuracy;
use Rubix\ML\Datasets\Labeled;
use Rubix\ML\Extractors\CSV;
use Rubix\ML\Transformers\NumericStringConverter;

srand(0);

$iterator = new CSV(__DIR__ . '/../data/iris/iris.data', header: false);

$dataset = Labeled::fromIterator($iterator)
    ->apply(new NumericStringConverter())
    ->dropFeature(0)
    ->dropFeature(0)
    ->randomize();
    
[ $train, $val ] = $dataset->stratifiedSplit(0.8);

$estimator = new ClassificationTree(maxHeight: 4);
$estimator->train($train);

Premier modèle _ Validation

Rubix ML :  un framework de machine learning en PHP - AFUP 27 janvier 2026

5.1,3.5,1.4,0.2,Iris-setosa
4.9,3.0,1.4,0.2,Iris-setosa
4.7,3.2,1.3,0.2,Iris-setosa
4.6,3.1,1.5,0.2,Iris-setosa
5.0,3.6,1.4,0.2,Iris-setosa
7.0,3.2,4.7,1.4,Iris-versicolor
6.4,3.2,4.5,1.5,Iris-versicolor
6.9,3.1,4.9,1.5,Iris-versicolor
5.5,2.3,4.0,1.3,Iris-versicolor
4.9,2.5,4.5,1.7,Iris-virginica
6.4,2.7,5.3,1.9,Iris-virginica
6.8,3.0,5.5,2.1,Iris-virginica
5.7,2.5,5.0,2.0,Iris-virginica
5.8,2.8,5.1,2.4,Iris-virginica
6.4,3.2,5.3,2.3,Iris-virginica
6.5,3.0,5.5,1.8,Iris-virginica
7.7,3.8,6.7,2.2,Iris-virginica
7.7,2.6,6.9,2.3,Iris-virginica
6.0,2.2,5.0,1.5,Iris-virginica
6.9,3.2,5.7,2.3,Iris-virginica
5.6,2.8,4.9,2.0,Iris-virginica
7.7,2.8,6.7,2.0,Iris-virginica
6.3,2.7,4.9,1.8,Iris-virginica
6.7,3.3,5.7,2.1,Iris-virginica
7.2,3.2,6.0,1.8,Iris-virginica
6.2,2.8,4.8,1.8,Iris-virginica
6.1,3.0,4.9,1.8,Iris-virginica
<?php

use Rubix\ML\Classifiers\ClassificationTree;
use Rubix\ML\CrossValidation\Metrics\Accuracy;
use Rubix\ML\Datasets\Labeled;
use Rubix\ML\Extractors\CSV;
use Rubix\ML\Transformers\NumericStringConverter;

srand(0);

$iterator = new CSV(__DIR__ . '/../data/iris/iris.data', header: false);

$dataset = Labeled::fromIterator($iterator)
    ->apply(new NumericStringConverter())
    ->dropFeature(0)
    ->dropFeature(0)
    ->randomize();
    
[ $train, $val ] = $dataset->stratifiedSplit(0.8);

$estimator = new ClassificationTree(maxHeight: 4);
$estimator->train($train);

$predictions = $estimator->predict($val);

Premier modèle _ Evaluation

Rubix ML :  un framework de machine learning en PHP - AFUP 27 janvier 2026

5.1,3.5,1.4,0.2,Iris-setosa
4.9,3.0,1.4,0.2,Iris-setosa
4.7,3.2,1.3,0.2,Iris-setosa
4.6,3.1,1.5,0.2,Iris-setosa
5.0,3.6,1.4,0.2,Iris-setosa
7.0,3.2,4.7,1.4,Iris-versicolor
6.4,3.2,4.5,1.5,Iris-versicolor
6.9,3.1,4.9,1.5,Iris-versicolor
5.5,2.3,4.0,1.3,Iris-versicolor
4.9,2.5,4.5,1.7,Iris-virginica
6.4,2.7,5.3,1.9,Iris-virginica
6.8,3.0,5.5,2.1,Iris-virginica
5.7,2.5,5.0,2.0,Iris-virginica
5.8,2.8,5.1,2.4,Iris-virginica
6.4,3.2,5.3,2.3,Iris-virginica
6.5,3.0,5.5,1.8,Iris-virginica
7.7,3.8,6.7,2.2,Iris-virginica
7.7,2.6,6.9,2.3,Iris-virginica
6.0,2.2,5.0,1.5,Iris-virginica
6.9,3.2,5.7,2.3,Iris-virginica
5.6,2.8,4.9,2.0,Iris-virginica
7.7,2.8,6.7,2.0,Iris-virginica
6.3,2.7,4.9,1.8,Iris-virginica
6.7,3.3,5.7,2.1,Iris-virginica
7.2,3.2,6.0,1.8,Iris-virginica
6.2,2.8,4.8,1.8,Iris-virginica
6.1,3.0,4.9,1.8,Iris-virginica
<?php

use Rubix\ML\Classifiers\ClassificationTree;
use Rubix\ML\CrossValidation\Metrics\Accuracy;
use Rubix\ML\Datasets\Labeled;
use Rubix\ML\Extractors\CSV;
use Rubix\ML\Transformers\NumericStringConverter;

srand(0);

$iterator = new CSV(__DIR__ . '/../data/iris/iris.data', header: false);

$dataset = Labeled::fromIterator($iterator)
    ->apply(new NumericStringConverter())
    ->dropFeature(0)
    ->dropFeature(0)
    ->randomize();
    
[ $train, $val ] = $dataset->stratifiedSplit(0.8);

$estimator = new ClassificationTree(maxHeight: 4);
$estimator->train($train);

$predictions = $estimator->predict($val);

echo (new Accuracy())->score($predictions, $val->labels());

Premier modèle _ Evaluation

Rubix ML :  un framework de machine learning en PHP - AFUP 27 janvier 2026

$ docker compose run --rm rubixml php src/iris-cart
$ float(0.9333333333333333)

Premier modèle _ Evaluation

Rubix ML :  un framework de machine learning en PHP - AFUP 27 janvier 2026

$ docker compose run --rm rubixml php src/iris-cart
$ float(0.9333333333333333)

Sur le jeu de validation, notre petit modèle a

prédit la bonne classe dans 93% des cas !

100% avec H > 4, ou en utilisant toutes les variables

explicatives

Premier modèle _ Evaluation

Rubix ML :  un framework de machine learning en PHP - AFUP 27 janvier 2026

$ docker compose run --rm rubixml php src/iris-cart
$ float(0.9333333333333333)

Sur le jeu de validation, notre petit modèle a

prédit la bonne classe dans 93% des cas !

100% avec H > 4, ou en utilisant toutes les variables

explicatives

Cependant...

Premier modèle _ Limite des arbres de décision

Rubix ML :  un framework de machine learning en PHP - AFUP 27 janvier 2026

Apprenant faible (weak learner)

Très sensible à la variation des données (instabilité)

=> élagage (pruning)

Algorithme glouton (greedy)

Premier modèle _ Ensembling

Rubix ML :  un framework de machine learning en PHP - AFUP 27 janvier 2026

AdaBoost, XGBoost :

=> Boosting (méthode séquentielle)

Forêt aléatoire :

=> Bagging (méthode parallèle)

Premier modèle _ Forêt aléatoire

Rubix ML :  un framework de machine learning en PHP - AFUP 27 janvier 2026

<?php

use Rubix\ML\Classifiers\ClassificationTree;
use Rubix\ML\Classifiers\RandomForest;
use Rubix\ML\CrossValidation\Metrics\Accuracy;
use Rubix\ML\Datasets\Labeled;
use Rubix\ML\Extractors\CSV;
use Rubix\ML\Transformers\NumericStringConverter;

srand(0);

$iterator = new CSV(__DIR__ . '/../data/iris/iris.data', header: false);
$dataset = Labeled::fromIterator($iterator)->apply(new NumericStringConverter())
    ->randomize();

[ $train, $val ] = $dataset->stratifiedSplit(0.8);

$estimator = new RandomForest(base: new ClassificationTree(), estimators: 100);
$estimator->train($train);

$predictions = $estimator->predict($val);

echo (new Accuracy())->score($predictions, $val->labels());

Rubix ML :  un framework de machine learning en PHP - AFUP 27 janvier 2026

<?php

use Rubix\ML\Classifiers\ClassificationTree;
use Rubix\ML\Classifiers\RandomForest;
use Rubix\ML\CrossValidation\Metrics\Accuracy;
use Rubix\ML\Datasets\Labeled;
use Rubix\ML\Extractors\CSV;
use Rubix\ML\Transformers\NumericStringConverter;

srand(0);

$iterator = new CSV(__DIR__ . '/../data/iris/iris.data', header: false);
$dataset = Labeled::fromIterator($iterator)->apply(new NumericStringConverter())
    ->randomize();

[ $train, $val ] = $dataset->stratifiedSplit(0.8);

$estimator = new RandomForest(base: new ClassificationTree(), estimators: 100);
$estimator->train($train);

$predictions = $estimator->predict($val);

echo (new Accuracy())->score($predictions, $val->labels());
$ docker compose run --rm rubixml php src/iris-random-forest
$ float(1)

Premier modèle _ Forêt aléatoire

Rubix ML :  un framework de machine learning en PHP - AFUP 27 janvier 2026

$ docker compose run --rm rubixml php src/iris-random-forest
$ float(1)
<?php

use Rubix\ML\Classifiers\ClassificationTree;
use Rubix\ML\Classifiers\RandomForest;
use Rubix\ML\CrossValidation\Metrics\Accuracy;
use Rubix\ML\Datasets\Labeled;
use Rubix\ML\Extractors\CSV;
use Rubix\ML\Transformers\NumericStringConverter;

srand(0);

$iterator = new CSV(__DIR__ . '/../data/iris/iris.data', header: false);
$dataset = Labeled::fromIterator($iterator)->apply(new NumericStringConverter())
    ->randomize();

[ $train, $val ] = $dataset->stratifiedSplit(0.8);

$estimator = new RandomForest(base: new ClassificationTree(), estimators: 100);
$estimator->train($train);

$predictions = $estimator->predict($val);

echo (new Accuracy())->score($predictions, $val->labels());

Premier modèle _ Forêt aléatoire

Deuxième modèle

Rubix ML :  un framework de machine learning en PHP - AFUP 27 janvier 2026

Deuxième modèle _ Définition du problème

Rubix ML :  un framework de machine learning en PHP - AFUP 27 janvier 2026

Variable expliquée qualitative

y  {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}

70000 observations (60K/10K)

Classes à peu près equilibrées

Images bitmaps normalisées

=> Toutes les obs. ont la même taille, sont

centrées, antialiasées et très contrastées

MNIST database

Certaines classes très différentiables :

D'autres pas : ex 1 et 7...

Deuxième modèle _ Définition du problème

Rubix ML :  un framework de machine learning en PHP - AFUP 27 janvier 2026

28px

28px

Variables explicatives => vectorisation

Une observation = 784 pixels en niveaux de gris

X [0,255]⁷⁸⁴

Pourquoi pas un arbre ?

- T. sensible a la variation des données

- Glouton : 1 split = 1 feature (valeur d'un pixel)

- Risque de surapprentissage / mauvaise généralisation

- Ne peut capturer les relations entre pixels

Deuxième modèle _ Définition du problème

Rubix ML :  un framework de machine learning en PHP - AFUP 27 janvier 2026

28px

28px

$ docker compose run --rm rubixml php src/mnist-cart
$ float(0.2648423557406306)

Une observation = 784 pixels en niveaux de gris

X [0,255]⁷⁸⁴

Variables explicatives => vectorisation

Pourquoi pas un arbre ?

- T. sensible a la variation des données

- Glouton : 1 split = 1 feature (valeur d'un pixel)

- Risque de surapprentissage / mauvaise généralisation

- Ne peut capturer les relations entre pixels

Deuxième modèle _ Neurone formel

Rubix ML :  un framework de machine learning en PHP - AFUP 27 janvier 2026

Voir Crucianu M., Réseaux de neurones multicouches, Conservatoire National des Arts et Métiers,

https://cedric.cnam.fr/vertigo/Cours/ml/coursReseauxNeuronesMulticouches.html#au-dela-de-la-separation-lineaire-introduction-de-couche-s-cachee-s

Entrées x1, ..., xd ou d est le nombre de dimension

Poids x1, ..., xd pour pondérer chaque entrée

Seuil w0 ou biais

Fonction d'activation φ (identité, sigmoïde, ...)

Sortie y = φ(wx + w0)

y

Deuxième modèle _ Perceptron multi couche

Rubix ML :  un framework de machine learning en PHP - AFUP 27 janvier 2026

h1

h2

h1.1

...

h1.j

h2.1

h2.2

...

h2.k

...

Entrée : {x1, x2, ... xi}, i = 784

Sortie : S [0,1]ᶜ, c = 4

Wᵢⱼ

...

h1 = φ(Wx + b)

Activation ReLU φ(x) = max(0, x)

W pondère le ième élément de

X avec le jième neurone de h1

Xi

h2 = φ(W'ᵀh1 + b')

Deuxième modèle _ Fonction de perte

Rubix ML :  un framework de machine learning en PHP - AFUP 27 janvier 2026

Supposons que notre réseau à produit le vecteur suivant S = (0.19, 0.12, 0.63, 0.06)

S est la probabilité que l'entrée soit de la t-ième classe (rappel, y  {0, 1, 2, 3} ici)

Pour un pb de classification multiclasse, on utilise généralement l'entropie croisée

Pour pouvoir entraîner le réseau il faut définir une fonction de perte, notée L (loss)

que l'on va chercher a minimiser pour optimisation les paramètres du modèle

On suppose t = 3 (l'entrée en apprentissage correspond au chiffre "3")

Ce n'est pas du tout ce qu'a prédit notre réseau ! L = -ln(S) = -ln(0.06) ≈ 1.22

Deuxième modèle _ Descente de gradient

Rubix ML :  un framework de machine learning en PHP - AFUP 27 janvier 2026

Voir Crucianu M., Réseaux de neurones multicouches, Conservatoire National des Arts et Métiers,

https://cedric.cnam.fr/vertigo/Cours/ml/coursReseauxNeuronesMulticouches.html#au-dela-de-la-separation-lineaire-introduction-de-couche-s-cachee-s

Soit W une matrice de poids et L une fonction de perte

Soit g un modèle paramétrique et t un entier designant

l'itération de l'entraînement du modèle

On initialise W  (t=0) et on calcule f et sa perte L

On calcule le gradient de L en Wt

On met a jour les poids dans le sens inverse de la pente

d'une certaine quantité, notée η

η est le taux d'apprentissage

Quand L(W) tend vers 0, le modèle a convergé

Deuxième modèle _ Import des données

Rubix ML :  un framework de machine learning en PHP - AFUP 27 janvier 2026

<?php

// ...

srand(0);

$samples = [];
$labels = [];
$nf = new NumberFormatter('en-US', NumberFormatter::SPELLOUT);

foreach (range(0, 9) as $class) {
  foreach (glob(__DIR__ . '/../data/mnist/trainingSet/trainingSet/' . $class . '/*.jpg') as $idx => $file) {
    $samples[] = [imagecreatefromjpeg($file)];
    $labels[] = $nf->format($class);
  }
}

Rubix ML attend des etiquettes catégorielles =/= continues

imagecreatefromjpeg => créé un objet GDImage à partir d'un path

Deuxième modèle _ Vectorisation / Normalisation

Rubix ML :  un framework de machine learning en PHP - AFUP 27 janvier 2026

<?php

// ...

$dataset = Labeled::build($samples, $labels)
  ->apply(new ImageVectorizer(grayscale: true))
  ->apply(new MinMaxNormalizer())
  ->randomize();
  
[ $train, $val ] = $dataset->stratifiedSplit(0.8);

Vectorisation : sans l'option grayscale, d = 2352 (28*28*3)...

Normalisation : redimensionne les composants de chaque vecteur dans une plage [0, 1]

Deuxième modèle _ Entraînement

Rubix ML :  un framework de machine learning en PHP - AFUP 27 janvier 2026

<?php

// ...

$dataset = Labeled::build($samples, $labels)
  ->apply(new ImageVectorizer(grayscale: true))
  ->apply(new MinMaxNormalizer())
  ->randomize();
  
[ $train, $val ] = $dataset->stratifiedSplit(0.8);

$estimator = new MultilayerPerceptron(
  hiddenLayers: [
    new Dense(256),
    new Activation(new ReLU()),
    new Dense(128),
    new Activation(new ReLU()),
  ],
);

$estimator->setLogger(new Screen());

$estimator->train($train);

Voir Simard et. al.,  Best practices for Convolutional Networks Applied to Visual Document Analysis, Microsoft Research,

https://www.cs.cmu.edu/~bhiksha/courses/deeplearning/Fall.2016/pdfs/Simard.pdf

Deuxième modèle _ Validation

Rubix ML :  un framework de machine learning en PHP - AFUP 27 janvier 2026

<?php

// ...

$dataset = Labeled::build($samples, $labels)
  ->apply(new ImageVectorizer(grayscale: true))
  ->apply(new MinMaxNormalizer())
  ->randomize();
  
[ $train, $val ] = $dataset->stratifiedSplit(0.8);

$estimator = new MultilayerPerceptron(
  hiddenLayers: [
    new Dense(256),
    new Activation(new ReLU()),
    new Dense(128),
    new Activation(new ReLU()),
  ],
);

$estimator->setLogger(new Screen());

$estimator->train($train);

$predictions = $estimator->predict($val);

$accuracy = (new Accuracy())->score($predictions, $val->labels());

Deuxième modèle _ Evaluation

Rubix ML :  un framework de machine learning en PHP - AFUP 27 janvier 2026

[2026-01-20 09:46:10] INFO: 235,146 trainable parameters
[2026-01-20 09:49:29] INFO: Epoch: 1, Cross Entropy: 0.028056610033243, Loss Change: ↓INF, F Beta (beta: 1): 0.95806186901078
[2026-01-20 09:52:51] INFO: Epoch: 2, Cross Entropy: 0.01014727140404, Loss Change: ↓0.017909338629203, F Beta (beta: 1): 0.9704023241577
[2026-01-20 09:56:08] INFO: Epoch: 3, Cross Entropy: 0.00614068075217, Loss Change: ↓0.0040065906518703, F Beta (beta: 1): 0.97053168673878
[2026-01-20 09:59:20] INFO: Epoch: 4, Cross Entropy: 0.0040954610794324, Loss Change: ↓0.0020452196727375, F Beta (beta: 1): 0.9736630607532
[2026-01-20 10:02:37] INFO: Epoch: 5, Cross Entropy: 0.0026533399921632, Loss Change: ↓0.0014421210872692, F Beta (beta: 1): 0.97252068107044
[2026-01-20 10:05:57] INFO: Epoch: 6, Cross Entropy: 0.00185256418866, Loss Change: ↓0.00080077580350322, F Beta (beta: 1): 0.97512856557577
[2026-01-20 10:09:26] INFO: Epoch: 7, Cross Entropy: 0.0012127363271564, Loss Change: ↓0.00063982786150356, F Beta (beta: 1): 0.97627551424582
[2026-01-20 10:12:59] INFO: Epoch: 8, Cross Entropy: 0.00083660630611914, Loss Change: ↓0.00037613002103729, F Beta (beta: 1): 0.97268954487787
[2026-01-20 10:16:30] INFO: Epoch: 9, Cross Entropy: 0.00048426073676602, Loss Change: ↓0.00035234556935313, F Beta (beta: 1): 0.97809457045849
[2026-01-20 10:19:54] INFO: Epoch: 10, Cross Entropy: 0.00034757528351459, Loss Change: ↓0.00013668545325143, F Beta (beta: 1): 0.97551072740256
[2026-01-20 10:23:12] INFO: Epoch: 11, Cross Entropy: 0.00034966666883631, Loss Change: ↑2.0913853217257E-6, F Beta (beta: 1): 0.97709340267375
[2026-01-20 10:23:12] INFO: Model state restored to epoch 9
[2026-01-20 10:23:12] INFO: Training complete
float(0.9770374776918501)

~ 0.98  = plafond de verre des MLP sur un problème de type MNIST

Performances : Rubix ML + containerisation

Deuxième modèle _ Un autre essai

Rubix ML :  un framework de machine learning en PHP - AFUP 27 janvier 2026

[2026-01-20 10:42:24] INFO: Training Multilayer Perceptron (hidden layers: [0: Dense (neurons: 800, l2 penalty: 0, bias: true, weight initializer: He, bias initializer: Constant (value: 0)), 1: Activation (activation fn: ReLU), 2: Dense (neurons: 800, l2 penalty: 0, bias: true, weight initializer: He, bias initializer: Constant (value: 0)), 3: Activation (activation fn: ReLU)], batch size: 128, optimizer: Adam (rate: 0.001, momentum decay: 0.1, norm decay: 0.001), l2 penalty: 0.0001, epochs: 1000, min change: 0.0001, window: 5, hold out: 0.1, cost fn: Cross Entropy, metric: F Beta (beta: 1))
[2026-01-20 10:42:24] INFO: 1,276,810 trainable parameters
[2026-01-20 11:05:04] INFO: Epoch: 1, Cross Entropy: 0.028509642620598, Loss Change: ↓INF, F Beta (beta: 1): 0.95519431016552
[2026-01-20 11:26:07] INFO: Epoch: 2, Cross Entropy: 0.0093072508156439, Loss Change: ↓0.019202391804954, F Beta (beta: 1): 0.96732084842461
[2026-01-20 11:45:32] INFO: Epoch: 3, Cross Entropy: 0.0050541962096349, Loss Change: ↓0.004253054606009, F Beta (beta: 1): 0.9762638985849
[2026-01-20 12:04:37] INFO: Epoch: 4, Cross Entropy: 0.0030365370136474, Loss Change: ↓0.0020176591959875, F Beta (beta: 1): 0.97548908732561
[2026-01-20 12:39:36] INFO: Epoch: 5, Cross Entropy: 0.0018745271191079, Loss Change: ↓0.0011620098945395, F Beta (beta: 1): 0.97287510777357
[2026-01-20 13:13:23] INFO: Epoch: 6, Cross Entropy: 0.0014040026579773, Loss Change: ↓0.00047052446113052, F Beta (beta: 1): 0.97626549027491
[2026-01-20 13:34:08] INFO: Epoch: 7, Cross Entropy: 0.00095465107994132, Loss Change: ↓0.00044935157803603, F Beta (beta: 1): 0.97806147365318
...
[2026-01-20 15:16:53] INFO: Epoch: 12, Cross Entropy: 0.0010775348770761, Loss Change: ↑0.00041497151750168, F Beta (beta: 1): 0.97595296254303
[2026-01-20 15:16:53] INFO: Model state restored to epoch 7
[2026-01-20 15:16:53] INFO: Training complete
float(0.9750148720999405)

Deux CC, 800 neurones dans chaque

=> Performances comparables, temps d'entraînement multiplié par 6

Quelles optimisations possibles des Hyperparamètres ? GridSearch, RandomSearch, ...

Conclusion

Rubix ML :  un framework de machine learning en PHP - AFUP 27 janvier 2026

Conclusion _ Train offline / Predict online

Rubix ML :  un framework de machine learning en PHP - AFUP 27 janvier 2026

<?php

// ...

$dataset = Labeled::build($samples, $labels)
  ->apply(new ImageVectorizer(grayscale: true))
  ->apply(new MinMaxNormalizer())
  ->randomize();
  
[ $train, $val ] = $dataset->stratifiedSplit(0.8);

$model = new MultilayerPerceptron(
  hiddenLayers: [
    new Dense(256),
    new Activation(new ReLU()),
    new Dense(128),
    new Activation(new ReLU()),
  ],
);

$persister = new Filesystem(__DIR__ . '/../model/mnist-nn-256-128.rbx');

$estimator = new PersistentModel(base: $model, persister: $persister);

$estimator->train($train);

$estimator->save();

// ...

Rubix ML :  un framework de machine learning en PHP - AFUP 27 janvier 2026

Original

Desaturate

Levels

Invert

Crop & scale

Conclusion _ Train offline / Predict online

test_3.jpg

Rubix ML :  un framework de machine learning en PHP - AFUP 27 janvier 2026

<?php

// ...

$image = imagecreatefromjpeg(__DIR__ . '/../data/test_3.jpg');
$samples = [[$image]];
$dataset = Unlabeled::build($samples)
  ->apply(new ImageVectorizer(grayscale: true));

$vector = $dataset->sample(0);
$normalizedVector = array_map(fn (int $pixel) => $pixel / 255.0, $vector);
$normalizedDataset = new Unlabeled([$normalizedVector]);

$estimator = PersistentModel::load(new Filesystem(__DIR__ . '/../model/mnist-nn-256-128.rbx'));

$predictions = $estimator->predict($normalizedDataset);

var_dump($predictions);

Conclusion _ Train offline / Predict online

Rubix ML :  un framework de machine learning en PHP - AFUP 27 janvier 2026

Conclusion _ Train offline / Predict online

$ docker compose run --rm rubixml php src/mnist-nn-predict

array(1) {
  [0]=> string(5) "three"
}
<?php

// ...

$image = imagecreatefromjpeg(__DIR__ . '/../data/test_3.jpg');
$samples = [[$image]];
$dataset = Unlabeled::build($samples)
  ->apply(new ImageVectorizer(grayscale: true));

$vector = $dataset->sample(0);
$normalizedVector = array_map(fn (int $pixel) => $pixel / 255.0, $vector);
$normalizedDataset = new Unlabeled([$normalizedVector]);

$estimator = PersistentModel::load(new Filesystem(__DIR__ . '/../model/mnist-nn-256-128.rbx'));

$predictions = $estimator->predict($normalizedDataset);

var_dump($predictions);

Rubix ML :  un framework de machine learning en PHP - AFUP 27 janvier 2026

Conclusion _ Train offline / Predict online

$ docker compose run --rm rubixml php src/mnist-nn-predict

array(1) {
  [0]=> string(5) "three"
}
<?php

// ...

$image = imagecreatefromjpeg(__DIR__ . '/../data/test_3.jpg');
$samples = [[$image]];
$dataset = Unlabeled::build($samples)
  ->apply(new ImageVectorizer(grayscale: true));

$vector = $dataset->sample(0);
$normalizedVector = array_map(fn (int $pixel) => $pixel / 255.0, $vector);
$normalizedDataset = new Unlabeled([$normalizedVector]);

$estimator = PersistentModel::load(new Filesystem(__DIR__ . '/../model/mnist-nn-256-128.rbx'));

$predictions = $estimator->predict($normalizedDataset);

var_dump($predictions);

Merci !

Rubix ML :  un framework de machine learning en PHP - AFUP 27 janvier 2026

https://github.com/jaljo/rubixml

Deuxième modèle _ Visualisation du problème

Rubix ML :  un framework de machine learning en PHP - AFUP 27 janvier 2026

Visualizing Data using t-SNE, Laurens van der Maaten, Geoffrey Hinton, Journal of Machine Learning Research 9, 2008

Quelle que soit f une fonction continue sur des ensembles bornés et fermés de , une approximation aussi bonne que souhaitée de f peut être obtenue avec un perceptron multi couche (PMC) ayant un nombre fini de neurones dans une unique couche cachée et une fonction d'activation non linéaire.

Deuxième modèle _ Théorème d'approximation universelle

Rubix ML :  un framework de machine learning en PHP - AFUP 27 janvier 2026

Voir Crucianu M., Réseaux de neurones multicouches, Conservatoire National des Arts et Métiers,

https://cedric.cnam.fr/vertigo/Cours/ml/coursReseauxNeuronesMulticouches.html#au-dela-de-la-separation-lineaire-introduction-de-couche-s-cachee-s

RUBIXML AFUP - 27 janvier 2026

By KNP Labs

RUBIXML AFUP - 27 janvier 2026

  • 8