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-virginicaVariable 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 = φ(wᵀx + 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 = φ(Wᵀx + 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