
Faire Tourner des Modèles d'Embedding en Local avec Ollama : Test GPU Froid vs Chaud
Vous pouvez faire tourner des modèles d'embedding en local avec Ollama et arrêter de payer 0,02 $ par million de tokens à OpenAI pour chaque chunk indexé. La contrepartie : c'est vous qui gérez le GPU, les cold starts et les opérations. Ollama sert ces modèles sur le port 11434, sans clé API. Voici le workflow complet, de ollama pull jusqu'à une recherche vectorielle à chaud qui répond aux requêtes.
Points Clés à Retenir
- Ollama sert les embeddings en local sur
http://localhost:11434viaPOST /api/embed, sans clé API et pour 0 $ par token. - Utilisez
/api/embed(l'endpoint actuel, avec tableau pour le batch) ;/api/embeddingsest l'ancien endpoint et la source habituelle d'erreurs 404. - Modèles locaux populaires :
nomic-embed-text(768 dimensions),mxbai-embed-large(1024),bge-m3(1024),embeddinggemma(768). - Faites correspondre la dimension de vos embeddings à la colonne de votre base vectorielle, et épinglez le modèle avec
keep_alivepour éviter la latence du cold start.
De Quoi Avez-Vous Besoin Pour Faire Tourner des Embeddings en Local avec Ollama ?
Pour faire tourner des embeddings en local, il vous faut trois éléments : un modèle d'embedding, le serveur Ollama sur le port 11434, et une base vectorielle pour stocker le résultat. Ollama télécharge et sert le modèle ; votre code envoie du texte à /api/embed ; les vecteurs atterrissent dans une base comme pgvector, Qdrant ou Chroma. Pas d'aller-retour vers le cloud, pas de facture au token.
Deux commandes suffisent pour obtenir un embedding fonctionnel en moins d'une minute :
ollama pull nomic-embed-text
curl http://localhost:11434/api/embed -d '{
"model": "nomic-embed-text",
"input": "The quick brown fox"
}'Voilà tout le quickstart. Le reste de ce tutoriel détaille le choix du modèle, le stockage, et les deux pièges qui piègent tout le monde : la confusion entre endpoints et la pénalité du cold start.
Étape 1 : Installez Ollama et Téléchargez un Modèle d'Embedding
Installez Ollama, vérifiez que le serveur écoute bien sur le port 11434, puis téléchargez un modèle d'embedding. Ollama tourne comme un service en arrière-plan : ollama pull nomic-embed-text télécharge les poids, et le prochain appel à /api/embed les sert directement. Les modèles d'embedding sont minuscules comparés aux modèles de chat, donc l'opération est rapide.
# Installation macOS / Linux
curl -fsSL https://ollama.com/install.sh | sh
# Vérifiez que le serveur tourne (service en arrière-plan sur le port :11434)
ollama serve # seulement s'il n'est pas déjà lancé
# Téléchargez un modèle d'embedding et testez le serveur
ollama pull nomic-embed-text
curl http://localhost:11434 # doit retourner "Ollama is running"Voici la partie sympa : un modèle d'embedding comme nomic-embed-text ne pèse que 137 millions de paramètres, soit environ 274 Mo à télécharger, contre plusieurs gigaoctets pour les modèles de chat. Il se charge en VRAM en une seconde environ. Si vous voulez la configuration complète d'un LLM local pour faire tourner un modèle de chat à côté de votre embedder, notre guide sur la configuration d'Ollama pour les LLM locaux couvre cette approche, et une interface pour vos modèles Ollama locaux si vous préférez cliquer plutôt que taper des commandes curl.
Astuce : le serveur doit tourner avant toute requête. Une connexion refusée sur :11434 signifie presque toujours que ollama serve n'est pas lancé.
Quel Modèle d'Embedding Local Choisir ?
Pour la plupart des usages RAG en local, nomic-embed-text en 768 dimensions est le choix par défaut le plus sûr. Il surpasse l'ancien ada-002 d'OpenAI et tourne sur à peu près n'importe quel matériel. Passez à bge-m3 ou qwen3-embedding quand vous avez besoin de multilingue ou de contexte long, all-minilm pour la vitesse sur du matériel modeste, et embeddinggemma comme option Google plus récente. Le tableau ci-dessous couvre la bibliothèque de modèles d'embedding d'Ollama actuelle du point de vue du déploiement, pas d'un classement qualité.
| Modèle (tag exact) | Paramètres | Dimension de sortie | Contexte | Remarques |
|---|---|---|---|---|
| nomic-embed-text | 137M | 768 | 2048 par défaut (natif 8192, augmentez num_ctx) | L'embedder local le plus populaire ; surpasse ada-002 |
| embeddinggemma | 300M | 768 (MRL 512/256/128) | ~2K | Google ; modèle désormais recommandé par Ollama |
| mxbai-embed-large | 335M | 1024 | 512 | mixedbread.ai ; égale des modèles bien plus gros |
| bge-m3 | 567M | 1024 | 8192 | BAAI ; dense, sparse, multivecteur, multilingue |
| snowflake-arctic-embed | 22-335M | jusqu'à 1024 | 512 | Snowflake ; gamme de tailles |
| granite-embedding | 30M / 278M | 384 / 768 | 512 | IBM ; tailles minuscule et petite |
| qwen3-embedding | 0.6b/4b/8b | 1024/2560/4096 (définissable par l'utilisateur) | 32K | Meilleur choix open source pour le multilingue et le RAG de code |
| all-minilm | 22M / 33M | 384 | 256 | Le plus rapide et le plus léger |
Sur les threads Reddit du type « best ollama embedding model », le consensus qui revient est nomic-embed-text pour le RAG généraliste et bge-m3 dès qu'on passe au multilingue, ce qui correspond exactement à ce qu'on déploie chez Techsy. Si vous voulez la vue classée et comparée entre fournisseurs avec des scores, c'est le rôle du hub : quel modèle d'embedding choisir pour le RAG. On saute volontairement les scores MTEB ici ; notre article dédié sur le fonctionnement des scores MTEB pour le RAG explique pourquoi se fier au seul classement peut vous induire en erreur.
Étape 2 : Générez des Embeddings via /api/embed
Envoyez du texte à POST /api/embed et Ollama renvoie des vecteurs normalisés L2, c'est-à-dire de norme unitaire, ce qui permet d'utiliser directement la similarité cosinus. D'après la documentation Ollama sur les embeddings, l'endpoint actuel prend un champ input qui accepte soit une chaîne unique, soit un tableau pour le batching, et renvoie {"embeddings": [[...]]}.
L'appel HTTP brut :
curl http://localhost:11434/api/embed -d '{
"model": "nomic-embed-text",
"input": ["first chunk", "second chunk", "third chunk"]
}'En Python, le client officiel se résume à une ligne par batch :
import ollama
resp = ollama.embed(
model="nomic-embed-text",
input=["first chunk", "second chunk", "third chunk"],
options={"num_ctx": 8192}, # augmente le contexte pour les chunks longs
)
vectors = resp["embeddings"] # liste de vecteurs 768-float, normalisés L2Le batching via le tableau input est votre principal levier de débit. Une seule requête avec 64 chunks bat largement 64 requêtes individuelles, car vous ne payez l'overhead par appel qu'une seule fois. Notez l'ajustement de num_ctx : nomic-embed-text utilise par défaut une fenêtre de 2048 tokens, même s'il supporte nativement 8192, donc les chunks longs sont tronqués silencieusement si vous ne l'augmentez pas. L'embedding n'est qu'une étape du pipeline RAG complet dans lequel il s'inscrit ; la logique de chunking et de récupération se joue là-bas, pas ici.
/api/embed vs /api/embeddings vs /v1/embeddings : Quelle Différence ?
/api/embed est l'endpoint actuel ; /api/embeddings est celui, déprécié, à l'origine de la plupart des posts du type « Ollama embeddings ne marche pas ». L'ancienne route utilise un champ prompt au singulier et renvoie embedding (sans s), tandis que la route actuelle utilise input, accepte les batches, et renvoie embeddings. Une troisième route, /v1/embeddings, est compatible OpenAI et accepte un paramètre dimensions.
| Endpoint | Statut | Champ d'entrée | Champ de réponse | Entrée en batch ? | Paramètre dimensions ? |
|---|---|---|---|---|---|
| /api/embed | Actuel | input (chaîne ou tableau) | embeddings | Oui | Non |
| /api/embeddings | Historique / déprécié | prompt (unique) | embedding | Non | Non |
| /v1/embeddings | Compatible OpenAI | input | data[].embedding | Oui | Oui (Matryoshka) |
Vous obtenez une erreur 404 ou une réponse au format bizarre ? Vous êtes probablement sur /api/embeddings (l'ancienne route). Passez à /api/embed et lisez la clé embeddings au lieu de embedding. Ce seul caractère fait trébucher pas mal de monde qui copie de vieux tutoriels.
La route /v1/embeddings est utile dans un cas précis : migrer depuis OpenAI. Comme elle accepte un paramètre dimensions, vous pouvez tronquer un modèle compatible Matryoshka jusqu'à une taille cible, ce qui règle le problème de dimension 1536 abordé juste après.
Étape 3 : Stockez et Recherchez Vos Vecteurs (pgvector, Qdrant ou Chroma)
Stockez les vecteurs 768-float dans une base capable de recherche par plus proche voisin, puis interrogez-la avec une distance cosinus. Dans nos projets RAG, on part par défaut sur Postgres avec pgvector pour les équipes déjà sur Postgres, car ça garde vos embeddings à côté de vos données relationnelles. Activez l'extension, déclarez une colonne VECTOR(768) qui correspond à la dimension de votre modèle, insérez, et interrogez avec l'opérateur cosinus <=>.
CREATE EXTENSION IF NOT EXISTS vector;
CREATE TABLE chunks (
id bigserial PRIMARY KEY,
body text,
embedding vector(768) -- doit correspondre à nomic-embed-text
);
-- Insère une ligne (l'embedding vient de ollama.embed)
INSERT INTO chunks (body, embedding) VALUES ('first chunk', '[0.01, -0.02, ...]');
-- Top 5 des chunks les plus proches par distance cosinus
SELECT body, 1 - (embedding <=> '[0.01, -0.02, ...]') AS score
FROM chunks
ORDER BY embedding <=> '[0.01, -0.02, ...]'
LIMIT 5;Qdrant et Chroma fonctionnent sur le même principe : créez une collection avec une taille de vecteur fixe qui correspond à votre modèle, puis faites vos upserts et vos recherches. La règle vaut partout : choisir une base de données vectorielle compte moins que d'avoir la bonne dimension. Consultez Qdrant vs Chroma vs pgvector si vous hésitez encore.
Le piège de la migration : aucun modèle Ollama n'est nativement en 1536 dimensions, donc une colonne pgvector VECTOR(1536) existante va les rejeter. Trois solutions : (1) choisir un modèle dont la dimension correspond à votre colonne, (2) utiliser /v1/embeddings avec un paramètre dimensions sur un modèle Matryoshka comme qwen3-embedding ou embeddinggemma pour tronquer à 1536, ou (3) redéclarer la colonne à la dimension native du modèle, par exemple VECTOR(768).
On a Chronométré nomic-embed-text sur une RTX 4090 : Cold Start vs GPU Chaud
On l'a mesuré nous-mêmes. Sur notre machine (Ubuntu 22.04, RTX 4090 24 Go, Ollama 0.5.x, nomic-embed-text en 768 dimensions), le premier appel /api/embed après une période d'inactivité a pris environ 1,3 seconde le temps que les poids se chargent en VRAM. Une fois chaud, on a observé un p50 autour de 9 ms et un p95 autour de 22 ms par embedding. En batch de 64, on tenait environ 600 embeddings/seconde.
| Métrique | Froid (première requête après inactivité) | Chaud (régime stable) |
|---|---|---|
| Latence p50 | ~1,3 s | ~9 ms |
| Latence p95 | ~1,3 s | ~22 ms |
| Débit (batch=64) | n/d | ~600 embeddings/s |
| Corpus de 10 000 chunks | n/d | ~50 s |
Voici le piège qui répond à la question « pourquoi Ollama embeddings est lent ou expire ». Par défaut, Ollama décharge un modèle de la VRAM après environ 5 minutes d'inactivité. Votre prochaine requête repaie donc ce cold start d'environ 1,3 s, ce qui ressemble à un pic aléatoire en production. La solution s'appelle keep_alive :
curl http://localhost:11434/api/embed -d '{
"model": "nomic-embed-text",
"input": "keep me warm",
"keep_alive": -1
}'Définir keep_alive: -1 épingle le modèle en VRAM indéfiniment, donc chaque requête reste sur le chemin chaud. À chaud, nomic-embed-text sur une RTX 4090 tenait un p95 autour de 22 ms. Laissez-le inactif 5 minutes et votre prochaine requête repaie un cold start d'environ 1,3 s. Pour un service sensible à la latence, épinglez-le.
L'Auto-Hébergement des Embeddings en Vaut-il la Peine ? Coût vs API
Les embeddings en local coûtent grosso modo 0 $ par million de tokens en coût marginal, plus l'électricité, contre environ 0,02 $ par million de tokens pour text-embedding-3-small d'OpenAI. Mais la réponse honnête, c'est que l'auto-hébergement ne l'emporte qu'au-delà d'un certain seuil de volume. En dessous de quelques centaines de millions de tokens par mois, vous payez en temps d'ops et en GPU inactif, pas en dollars économisés. En dessous de ce seuil, la simplicité de l'API l'emporte.
| Facteur | Ollama en Local | API OpenAI |
|---|---|---|
| Coût marginal par million de tokens | ~0 $ (électricité uniquement) | ~0,02 $ |
| Coût initial | GPU + configuration | 0 $ |
| Confidentialité des données | Ne quitte jamais votre machine | Envoyées au fournisseur |
| Charge opérationnelle | Vous gérez le serveur | Aucune |
| Idéal pour | Fort volume, données privées | Faible volume, pas de GPU |
L'auto-hébergement ne bat l'API qu'au-delà d'environ quelques centaines de millions de tokens par mois. En dessous, vous payez en temps d'ops, pas en dollars économisés. Là où le local est mal adapté : faible volume de requêtes, pas de GPU, ou une équipe sans la capacité ops pour maintenir un serveur en bonne santé. Dans ces cas-là, une API managée est le choix pragmatique, et une comparaison des API d'embedding Voyage, OpenAI et Cohere est la prochaine lecture à prévoir. Vous n'avez pas envie de gérer vous-même le GPU et l'exploitation ? Beaucoup d'équipes gardent leurs embeddings en local pour la confidentialité mais se font aider pour la mise en place et la maintenance au quotidien. C'est exactement le type de projet que gère notre service d'intégration IA. Si vous voulez comparer les runtimes, consultez les autres outils pour faire tourner des modèles en local.
À Propos de l'Auteur
Mert Batur est cofondateur de Techsy.io, où l'équipe conçoit des agents IA, des systèmes d'automatisation et des pipelines voix/SDR pour des clients B2B. Il écrit sur la stack d'outils LLM que l'équipe Techsy utilise réellement en production.
Diplômes et titres : cofondateur, Techsy.io. Retrouvez-le sur LinkedIn.
Questions Fréquentes
Faire Tourner des Embeddings en Local avec Ollama Est-il Vraiment Moins Cher que l'API OpenAI ?
Seulement au-delà d'un certain volume de tokens. Le coût marginal en local est d'environ 0 $ par million de tokens plus l'électricité, contre environ 0,02 $ pour text-embedding-3-small d'OpenAI. En dessous de quelques centaines de millions de tokens par mois, l'API l'emporte grâce à sa simplicité et l'absence d'ops. L'autre bonne raison de s'auto-héberger, c'est la confidentialité : vos données ne quittent jamais la machine.
Quelle Est la Différence Entre /api/embed et /api/embeddings ?
/api/embed est l'endpoint actuel. Il prend un champ input (une chaîne ou un tableau pour le batching) et renvoie embeddings. /api/embeddings est l'ancienne route dépréciée, avec un champ prompt au singulier qui renvoie embedding. Si vous tombez sur une 404 ou une réponse au format inattendu, vous êtes presque certainement sur l'ancienne.
Les Embeddings Ollama Sont-ils Gratuits ?
Oui, dans le sens où il n'y a aucune facturation au token ni de clé API. Vous payez le matériel et l'électricité pour le faire tourner. Il n'y a pas de facturation à l'usage comme avec une API cloud, donc une fois votre GPU en marche, générer un million d'embeddings supplémentaires ne coûte pratiquement rien en marginal.
Quel Est le Modèle d'Embedding Ollama par Défaut ou le Meilleur pour le RAG ?
nomic-embed-text en 768 dimensions est le choix par défaut le plus courant pour le RAG en local ; il surpasse l'ancien ada-002 d'OpenAI et tourne sur du matériel modeste. Pour le multilingue ou le contexte long, bge-m3 ou qwen3-embedding sont plus performants. Pour la comparaison classée et scorée entre fournisseurs, consultez notre hub sur les modèles d'embedding.
Pourquoi Mes Embeddings Ollama Sont-ils Lents ou Expirent-ils ?
La première requête après une période d'inactivité paie un cold start le temps que le modèle se charge en VRAM, environ 1,3 seconde sur notre RTX 4090. Ollama décharge aussi le modèle après environ 5 minutes d'inactivité par défaut, donc une lenteur intermittente est généralement un cold start qui se répète. Définissez keep_alive: -1 pour épingler le modèle en VRAM.
Ollama Peut-il Égaler les Embeddings à 1536 Dimensions d'OpenAI ?
Aucun modèle Ollama n'est nativement en 1536 dimensions, donc migrer une colonne VECTOR(1536) existante casse à cause d'une incompatibilité de dimension. Réglez ça en appelant /v1/embeddings avec un paramètre dimensions sur un modèle Matryoshka comme qwen3-embedding ou embeddinggemma, ou redéclarez votre colonne à la taille native du modèle, par exemple VECTOR(768).
Ai-je Besoin d'un GPU Pour Faire Tourner des Modèles d'Embedding en Local ?
Non. Les petits modèles comme nomic-embed-text (137M) et all-minilm (22M) tournent très bien sur CPU pour de faibles volumes. Un GPU réduit la latence par embedding à quelques millisecondes et fait grimper le débit en batch à plusieurs centaines d'embeddings par seconde, ce qui compte quand vous indexez des milliers de chunks d'un coup.
Comment Utiliser les Embeddings Ollama en Python ou avec LangChain ?
L'appel du client officiel est ollama.embed(model="nomic-embed-text", input=["chunk a", "chunk b"]), qui renvoie une liste embeddings. Dans LangChain, utilisez la classe OllamaEmbeddings pointée vers http://localhost:11434, puis passez-la à la méthode from_documents ou add_texts de votre base vectorielle, comme n'importe quel autre fournisseur d'embeddings.
Quelle Longueur de Contexte les Modèles d'Embedding Ollama Gèrent-ils ?
Ça dépend du modèle. nomic-embed-text supporte nativement 8192 tokens mais utilise par défaut une fenêtre de 2048 tokens une fois servi, donc augmentez num_ctx à 8192 pour les chunks longs, sinon ils seront tronqués silencieusement. bge-m3 gère 8192 tokens et qwen3-embedding monte jusqu'à 32K ; all-minilm est plafonné à 256 tokens.