De l'OCR sur Edge Compute
Sur cette page
La technologie avancée se banalise, et la vision par ordinateur est devenue un outil qui a l’air indispensable, avec plein d’usages.
Elle sert à identifier des objets, des motifs, des personnes, des plantes et des animaux. Elle donne des informations utiles aux utilisateurs, aide à décider et permet de trier d’énormes jeux de données.
Ajoute à ça une demande croissante d’analyse en temps réel et de réponses à faible latence, et l’edge compute devient le meilleur endroit pour déployer ton modèle entraîné.
C’est quoi l’OCR ?
La reconnaissance optique de caractères (OCR) transforme des images de texte en texte codé pour une machine. On s’en sert d’habitude pour convertir des images de documents en texte que tu peux chercher et indexer.
Tu peux scanner des documents, des reçus, des cartes de visite, et plus encore. Tu vois tout de suite l’intérêt chez Blinq.
C’est quoi l’edge compute ?
L’edge compute, c’est faire tourner ton code au plus près de l’utilisateur. On le fait en général sur un CDN, comme Cloudflare Workers ou AWS Lambda@Edge.
Ça n’a rien à voir avec le cloud classique, où ton code tourne dans un datacenter souvent très loin de l’utilisateur.
Pourquoi l’edge compute ?
Tu peux faire tourner ton modèle sur l’appareil, mais le garder en edge a quelques avantages :
- Faible latence : le modèle est plus près de l’utilisateur, donc la réponse arrive plus vite. On dirait qu’il tourne sur l’appareil.
- Faible coût : tu ne paies pas les ressources de calcul pour faire tourner le modèle, tu paies seulement les requêtes.
- Facile à mettre à jour : tu peux changer le modèle sans mettre à jour l’app. C’est surtout utile quand beaucoup d’utilisateurs restent sur d’anciennes versions.
Chez Blinq, on maintient une longue traîne de versions de l’app mobile. Pouvoir mettre à jour le modèle sans toucher à l’app est un énorme avantage pour nous.
Comment faire tourner ton modèle en edge
Les runtimes edge comme Cloudflare Workers et Lambda@Edge n’ont rien de commun avec les runtimes classiques comme Node.js et Golang. Ils sont conçus pour être rapides et légers, donc ils n’ont pas accès aux mêmes bibliothèques ni aux mêmes API.
Ça ressemble beaucoup à faire tourner ton code dans un navigateur. Tu ne peux pas copier-coller ton code en espérant que ça marche.
Le modèle
Pour cet exemple, j’utilise un modèle d’OCR simple, Keras OCR. C’est un modèle TensorFlow qui prend une image et renvoie le texte qu’il y trouve.
Je m’appuie aussi sur Constellation de Cloudflare (en bêta), qui te permet de faire tourner des modèles ONNX en edge. C’est une bonne nouvelle option, parce qu’elle accepte des modèles que TensorFlow.js ne gère pas, et qu’elle n’est pas bridée par le quota de 10 Mo des Workers de Cloudflare.
Lambda@Edge a des limites bien plus hautes, mais mieux vaut quand même garder ton modèle le plus petit possible pour réduire le temps de démarrage à froid.
Pour cet exemple, j’ai pris le modèle crnn_kurapan, disponible ici.
Convertir Keras en ONNX
La première étape, c’est de convertir le modèle Keras en ONNX. C’est assez simple, mais ça demande quelques étapes. Je pars du principe que Conda est installé et que tu es sur Python 3.10.
Si tu es sur Apple Silicon, il te faut aussi :
tensorflow-macos
D’abord, crée un environnement conda.
$ conda create -n edge-onnx python=3.10 pip
$ conda activate edge-onnx
Ensuite, installe les dépendances.
$ python -m tensorflow tf2onnx keras-ocr
Si tu es sur Apple Silicon, installe tensorflow-macos à la place de tensorflow.
$ python -m pip install tensorflow-macos
Pour finir, convertis le modèle avec ce snippet.
import tf2onnx
import onnx
import keras_ocr
recognition = keras_ocr.recognition.Recognizer(
alphabet='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789', weights='kurapan')
output_path = 'crnn_kurapan.onnx'
model_proto, _ = tf2onnx.convert.from_keras(
recognition.model, opset=None, output_path=output_path)
onnx.save(model_proto, output_path)
Ça crée un modèle ONNX dans le dossier courant, sous le nom crnn_kurapan.onnx.
Faire tourner le modèle en edge
Pour exécuter le modèle ONNX en edge, j’ai pris Cloudflare Constellation, parce que le modèle est beaucoup trop gros pour Cloudflare Workers.
Tu peux le faire depuis le dashboard Cloudflare ou en CLI. J’utilise la CLI, c’est plus simple à automatiser.
D’abord, on crée un projet.
$ npx wrangler constellation project create "ocr" ONNX
$ npx wrangler constellation project list
Note l’ID du projet.
Ensuite, envoie le modèle :
$ wrangler constellation model upload ocr crnn_kurapan crnn_kurapan.onnx
$ npx wrangler constellation model list ocr
Note l’ID du modèle.
Puis configure le projet et initialise wrangler.
$ npm create cloudflare@2
J’ai appelé le projet ocr et pris le template « Hello World ».
Dans le fichier wrangler.toml, ajoute le binding constellation et active node_compat.
name = "ocr"
main = "src/index.js"
node_compat = true
workers_dev = true
compatibility_date = "2023-07-01"
constellation = [
{binding = 'OCR', project_id = '{{ project ID from earlier }}'},
]
Enfin, installe le client constellation et les dépendances pour envoyer les images au modèle en flux.
npm install --save @cloudflare/constellation string-to-stream pngjs
On peut maintenant écrire le code qui exécute le modèle.
import str from 'string-to-stream';
import { PNG } from 'pngjs/browser';
import { Tensor, run } from '@cloudflare/constellation';
const MODEL_ID = '{{ model ID from earlier }}';
function normalizeImage(data) {
return new Promise(async (resolve, reject) => {
const stream = str(data);
const png = new PNG({ filterType: 4 });
stream
.pipe(png)
.on('parsed', function () {
const [r, g, b] = new Array(3).fill([]);
for (let i = 0; i < this.data.length; i += 4) {
r.push(this.data[i] / 255.0);
g.push(this.data[i + 1] / 255.0);
b.push(this.data[i + 2] / 255.0);
}
resolve({
input: [...r, ...g, ...b],
shape: [3, this.height, this.width, 3],
});
})
.on('error', function (error) {
reject({ err: error.toString() });
});
});
}
export default {
async fetch(request, env) {
if (request.method !== 'POST') {
return new Response('Method not allowed', { status: 405 });
}
const formData = await request.formData();
const image = formData.get('image');
if (!image) {
return new Response('No image found', { status: 400 });
}
const buffer = await image.arrayBuffer();
try {
const normalized = await normalizeImage(buffer);
if (!normalized) {
return new Response('Unable to normalize image', { status: 500 });
}
const input = new Tensor(
'float32',
normalized.shape,
normalized.input,
'input_1'
);
const output = await run(env.OCR, MODEL_ID, input);
return new Response(JSON.stringify(output), { status: 200 });
} catch (err) {
return new Response(err, { status: 500 });
}
},
};
Tant que c’est en bêta, il se peut que tu doives désactiver les avertissements de constellation avec export NO_CONSTELLATION_WARNING=true.
Quand tu es prêt à déployer, lance simplement wrangler deploy.
Tester le modèle
Tu peux maintenant envoyer une requête HTTP au Worker pour tester le modèle.
$ curl -X POST -F "
image=@/path/to/image.png" https://ocr.example.workers.dev
Tu obtiens une réponse JSON avec la sortie du modèle.
{
"output_1": [...]
}
La sortie du modèle est un tableau de probabilités pour chaque caractère. Pour la convertir en texte, tu peux utiliser quelque chose comme ce snippet.
const charset =
'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
const text = output
.map((probabilities) => {
const max = Math.max(...probabilities);
const index = probabilities.indexOf(max);
return charset[index];
})
.join('');
Pourquoi faire ça ?
Je voulais voir s’il était possible de faire tourner un modèle plutôt gros en edge. Je voulais aussi voir ce que ça donnerait en performance.
Ce modèle ne finira probablement pas en production, mais il montre bien ce que permettent Cloudflare Workers et Constellation. Les gros modèles d’OCR sont en général meilleurs, mais tu peux en prendre des plus petits si tu acceptes de perdre en précision. Pour ça, il reste toujours Tesseract.js.
Performance
Le modèle met environ 1,5 seconde à tourner en edge. C’est bien plus lent qu’en local, mais c’est encore assez rapide pour la production. Dans un cas réel, tu peux aussi profiter des offres de Google et d’Amazon Web Services, bien plus précises. Elles coûtent aussi bien plus cher.
Précision
Le modèle est assez précis, mais pas parfait. Il peine avec certaines polices et reconnaît mal le texte dans les images très bruitées. Il ne gère aussi que les petites images, mais une fenêtre glissante peut arranger ça.
À toi d’essayer
Cloudflare développe Constellation activement, et le service est actuellement en bêta. Si tu veux l’essayer, tu peux t’inscrire à la bêta ici.
Il y a aussi un canal Discord bien pratique où tu peux poser des questions et recevoir de l’aide de l’équipe Cloudflare.