JavaScript et Machine Learning : TensorFlow.js Democratise l'IA
Salut HaWkers, imaginez entrainer un modele de Machine Learning et l'executer directement dans le navigateur, sans backend Python, sans serveurs lourds. Ca semble futuriste ? C'est la realite avec TensorFlow.js.
Vous, developpeur JavaScript, avez maintenant acces a des capacites d'IA qui etaient autrefois reservees aux data scientists. Explorons comment ca fonctionne, des cas d'usage reels et du code pratique que vous pouvez executer aujourd'hui.
Pourquoi le Machine Learning dans le Navigateur ?
Avantages de TensorFlow.js :
- Confidentialite : Les donnees ne quittent jamais l'appareil de l'utilisateur
- Latence zero : Pas d'aller-retour serveur
- Cout : Traitement distribue sur les clients (pas votre serveur)
- Accessibilite : N'importe quel dev JavaScript peut commencer
- Multi-plateforme : Browser, Node.js, React Native, Electron
Cas d'usage reels en 2025 :
- Filtres camera en temps reel (Instagram, Snapchat)
- Transcription audio offline (Zoom, Meet)
- Detection de fraude dans les paiements
- Recommandations personnalisees sans envoyer de donnees
- Accessibilite (sous-titres, traduction en temps reel)
Configuration de Base : Votre Premier Modele
Creons un detecteur de sentiment de texte — classifie si une phrase est positive ou negative.
Installation :
npm install @tensorflow/tfjs
# Ou via CDN :
# <script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs"></script>Modele pre-entraine simple :
import * as tf from '@tensorflow/tfjs';
// 1. Creer un modele sequentiel simple
const model = tf.sequential({
layers: [
// Input : Texte converti en nombres (embedding)
tf.layers.dense({ inputShape: [100], units: 16, activation: 'relu' }),
// Hidden layer
tf.layers.dense({ units: 8, activation: 'relu' }),
// Output : 2 classes (positif/negatif)
tf.layers.dense({ units: 2, activation: 'softmax' })
]
});
// 2. Compiler le modele
model.compile({
optimizer: tf.train.adam(0.001),
loss: 'categoricalCrossentropy',
metrics: ['accuracy']
});
// 3. Entrainer avec des donnees
async function trainModel(reviews, labels) {
// reviews : array de textes
// labels : array de [0, 1] ou [1, 0]
const xs = tf.tensor2d(reviews);
const ys = tf.tensor2d(labels);
await model.fit(xs, ys, {
epochs: 50,
batchSize: 32,
validationSplit: 0.2,
callbacks: {
onEpochEnd: (epoch, logs) => {
console.log(`Epoque ${epoch}: loss = ${logs.loss.toFixed(4)}`);
}
}
});
console.log('✓ Modele entraine !');
}
// 4. Faire des predictions
function predict(text) {
// Convertir le texte en vecteur numerique (simplifie)
const vector = textToVector(text);
const input = tf.tensor2d([vector]);
const prediction = model.predict(input);
const scores = prediction.dataSync();
return {
positive: scores[0],
negative: scores[1],
sentiment: scores[0] > scores[1] ? 'Positif' : 'Negatif'
};
}
// Exemple d'utilisation
const result = predict('J\'adore ce produit, tres bon !');
console.log(result);
// { positive: 0.89, negative: 0.11, sentiment: 'Positif' }
Cas Pratique : Reconnaissance d'Images en Temps Reel
Utilisons un modele pre-entraine (MobileNet) pour classifier les images de la webcam.
import * as tf from '@tensorflow/tfjs';
import * as mobilenet from '@tensorflow-models/mobilenet';
class ImageClassifier {
constructor() {
this.model = null;
this.video = null;
}
async init() {
// 1. Charger le modele (telechargement automatique)
console.log('Chargement de MobileNet...');
this.model = await mobilenet.load();
console.log('✓ Modele charge !');
// 2. Configurer la webcam
this.video = document.getElementById('webcam');
const stream = await navigator.mediaDevices.getUserMedia({
video: { width: 640, height: 480 }
});
this.video.srcObject = stream;
await new Promise(resolve => {
this.video.onloadedmetadata = resolve;
});
this.video.play();
}
async classify() {
if (!this.model || !this.video) return;
// 3. Faire une prediction en temps reel
const predictions = await this.model.classify(this.video);
return predictions.map(p => ({
class: p.className,
probability: (p.probability * 100).toFixed(2) + '%'
}));
}
async classifyLoop() {
const resultsDiv = document.getElementById('results');
setInterval(async () => {
const predictions = await this.classify();
resultsDiv.innerHTML = predictions
.map(p => `<p>${p.class}: ${p.probability}</p>`)
.join('');
}, 1000); // Classifie toutes les 1 seconde
}
}
// HTML correspondant
/*
<video id="webcam" autoplay></video>
<div id="results"></div>
<script>
const classifier = new ImageClassifier();
classifier.init().then(() => {
classifier.classifyLoop();
});
</script>
*/Resultat : L'application identifie les objets dans la camera sans envoyer de donnees au serveur !
Transfer Learning : Entrainer Votre Propre Classificateur
Et si vous voulez classifier des choses specifiques ? Utilisez le Transfer Learning — prenez un modele pre-entraine et ajustez-le pour votre cas.
Exemple : Classificateur de poses (exercices physiques)
import * as tf from '@tensorflow/tfjs';
import * as mobilenet from '@tensorflow-models/mobilenet';
class PoseClassifier {
constructor() {
this.baseModel = null;
this.model = null;
this.classes = ['squat', 'pushup', 'plank', 'jump'];
}
async loadBaseModel() {
// Charger MobileNet sans la derniere couche
const mobilenet = await tf.loadLayersModel(
'https://storage.googleapis.com/tfjs-models/tfjs/mobilenet_v1_0.25_224/model.json'
);
// Retirer la derniere couche (classification generique)
const layer = mobilenet.getLayer('conv_pw_13_relu');
this.baseModel = tf.model({
inputs: mobilenet.inputs,
outputs: layer.output
});
}
createCustomModel() {
// Ajouter de nouvelles couches pour vos classes
const model = tf.sequential({
layers: [
tf.layers.flatten({
inputShape: this.baseModel.outputs[0].shape.slice(1)
}),
tf.layers.dense({
units: 128,
activation: 'relu',
kernelInitializer: 'varianceScaling'
}),
tf.layers.dropout({ rate: 0.5 }),
tf.layers.dense({
units: this.classes.length,
activation: 'softmax'
})
]
});
model.compile({
optimizer: tf.train.adam(0.0001),
loss: 'categoricalCrossentropy',
metrics: ['accuracy']
});
this.model = model;
}
async train(images, labels) {
// images : array d'elements <img>
// labels : array d'indices [0, 1, 2, 3]
// Extraire les features avec le modele de base
const features = tf.tidy(() => {
const imageTensors = images.map(img => {
return tf.browser.fromPixels(img)
.resizeBilinear([224, 224])
.toFloat()
.div(127.5)
.sub(1);
});
const batched = tf.stack(imageTensors);
return this.baseModel.predict(batched);
});
// Convertir les labels en one-hot
const ys = tf.oneHot(tf.tensor1d(labels, 'int32'), this.classes.length);
// Entrainer uniquement les nouvelles couches
await this.model.fit(features, ys, {
epochs: 20,
batchSize: 32,
validationSplit: 0.2,
callbacks: {
onEpochEnd: (epoch, logs) => {
console.log(
`Epoque ${epoch + 1}: ` +
`loss = ${logs.loss.toFixed(4)}, ` +
`accuracy = ${logs.acc.toFixed(4)}`
);
}
}
});
features.dispose();
ys.dispose();
}
async predict(imageElement) {
const processed = tf.tidy(() => {
const img = tf.browser.fromPixels(imageElement)
.resizeBilinear([224, 224])
.toFloat()
.div(127.5)
.sub(1)
.expandDims(0);
const features = this.baseModel.predict(img);
return this.model.predict(features);
});
const probabilities = await processed.data();
const classIndex = processed.argMax(-1).dataSync()[0];
processed.dispose();
return {
class: this.classes[classIndex],
confidence: (probabilities[classIndex] * 100).toFixed(2) + '%',
allProbabilities: this.classes.map((name, i) => ({
name,
probability: (probabilities[i] * 100).toFixed(2) + '%'
}))
};
}
}
// Utilisation
const classifier = new PoseClassifier();
await classifier.loadBaseModel();
classifier.createCustomModel();
// Collecter des donnees (prendre des photos de chaque exercice)
const squatImgs = [img1, img2, img3]; // 3 exemples
const pushupImgs = [img4, img5, img6];
// ... plus d'exemples
const allImages = [...squatImgs, ...pushupImgs, ...];
const labels = [0, 0, 0, 1, 1, 1, ...]; // Indices des classes
await classifier.train(allImages, labels);
// Faire une prediction sur une nouvelle image
const result = await classifier.predict(newImageElement);
console.log(result);
// {
// class: 'squat',
// confidence: '94.32%',
// allProbabilities: [...]
// }
Performance : Acceleration GPU
TensorFlow.js utilise WebGL pour le calcul sur GPU — crucial pour les grands modeles.
Optimisations importantes :
// 1. Utiliser tf.tidy() pour gerer la memoire
function processData(input) {
return tf.tidy(() => {
// Tous les tensors crees ici sont liberes automatiquement
const normalized = input.div(255);
const reshaped = normalized.reshape([1, 224, 224, 3]);
return model.predict(reshaped);
});
}
// 2. Batch processing pour plusieurs images
async function classifyMultiple(images) {
const batch = tf.tidy(() => {
const tensors = images.map(img =>
tf.browser.fromPixels(img).resizeBilinear([224, 224])
);
return tf.stack(tensors);
});
const predictions = await model.predict(batch).array();
batch.dispose();
return predictions;
}
// 3. Quantification pour des modeles plus petits
async function loadQuantizedModel() {
// Modele avec 4x moins de poids
const model = await tf.loadGraphModel(
'https://example.com/model_quantized/model.json'
);
return model;
}
// 4. Web Workers pour ne pas bloquer l'UI
// worker.js
importScripts('https://cdn.jsdelivr.net/npm/@tensorflow/tfjs');
self.onmessage = async (e) => {
const { imageData } = e.data;
const tensor = tf.browser.fromPixels(imageData);
const prediction = await model.predict(tensor);
self.postMessage({ prediction: await prediction.array() });
tensor.dispose();
prediction.dispose();
};Cas d'Usage Avances
1. Detection de Poses (PoseNet) :
import * as posenet from '@tensorflow-models/posenet';
const net = await posenet.load({
architecture: 'MobileNetV1',
outputStride: 16,
inputResolution: { width: 640, height: 480 },
multiplier: 0.75
});
const pose = await net.estimateSinglePose(video);
// pose.keypoints contient 17 points (nez, yeux, epaules, etc)
const nose = pose.keypoints.find(kp => kp.part === 'nose');
console.log(`Nez a : x=${nose.position.x}, y=${nose.position.y}`);2. Segmentation de Personnes (BodyPix) :
import * as bodyPix from '@tensorflow-models/body-pix';
const net = await bodyPix.load();
const segmentation = await net.segmentPerson(video);
// Appliquer un flou sur le fond (type Zoom)
const foregroundColor = { r: 0, g: 0, b: 0, a: 0 };
const backgroundColor = { r: 0, g: 0, b: 0, a: 255 };
const backgroundBlurAmount = 15;
const backgroundBlur = await bodyPix.blurBodyPart(
canvas,
video,
segmentation,
backgroundBlurAmount,
foregroundColor,
backgroundColor
);3. Reconnaissance Vocale :
import * as speechCommands from '@tensorflow-models/speech-commands';
const recognizer = speechCommands.create('BROWSER_FFT');
await recognizer.ensureModelLoaded();
// Ecouter les commandes
recognizer.listen(result => {
const scores = result.scores;
const maxScore = Math.max(...scores);
const command = recognizer.wordLabels()[scores.indexOf(maxScore)];
console.log(`Commande detectee : ${command} (${(maxScore * 100).toFixed(2)}%)`);
}, {
includeSpectrogram: true,
probabilityThreshold: 0.75
});
Limitations et Considerations
Ce pour quoi TensorFlow.js N'est PAS ideal :
- Entrainement de modeles gigantesques (GPT, DALL-E)
- Traitement batch massif (millions d'images)
- Recherche ML de pointe
Ideal pour :
- Inference en temps reel sur le client
- Modeles petits/moyens (<50MB)
- Prototypes rapides
- Applications necessitant de la confidentialite
Trade-offs :
- Performance : ~2-5x plus lent que Python/C++ natif
- Taille : Les modeles doivent etre legers pour le web
- Compatibilite : Pas toutes les APIs Python sont disponibles
L'Avenir du ML en JavaScript
Tendances 2025 :
- WebGPU pour une performance encore meilleure
- Modeles de plus en plus petits (techniques de compression)
- Edge computing (ML sur appareils IoT avec JS)
- Integration avec les frameworks (composants React, Vue avec ML)
Si vous voulez explorer davantage comment JavaScript s'etend a des domaines innovants, consultez JavaScript et le Monde de l'IoT : Integrer le Web au Monde Physique.

