
Une recherche SQL classique se fait sur du texte précis : WHERE nom = 'ATLAS', WHERE commentaire LIKE '%rapide%', elle n’a aucune notion de sémantique,c’est-à-dire du sens des mots.
“Réactif” et “rapide” veulent dire la même chose, mais pour la recherche classique, ce sont deux mots qui n’ont rien en commun.
Oracle 23ai change ça avec le type VECTOR. Au lieu de chercher des mots, on cherche du sens. Et tout cela se passe directement dans la base, sans avoir besoin d’exporter ses données vers un outil externe.
Dans cet article, je vous montre concrètement ce qu’est un vecteur, comment Oracle le stocke, comment le générer, et comment l’utiliser en SQL, avec un exemple simple.
1. C’est quoi un vecteur, concrètement ?
Un vecteur c’est juste une liste de nombres, genre [0.82, 0.15, -0.30, 0.44].
Ces nombres ne veulent rien dire tout seuls,mais un modèle d’IA sait transformer n’importe quelle donnée (un texte, une image, un son) en une liste comme celle-ci, de façon à ce que le sens de la donnée soit encodé dedans.
Le principe est simple : deux phrases qui veulent dire la même chose auront des listes de nombres proches, même si elles n’ont aucun mot en commun. “Service rapide” et “équipe réactive” auront des vecteurs voisins. “Service rapide” et “facture erronée” auront des vecteurs très éloignés.
C’est cette proximité entre les nombres qu’Oracle sait mesurer. Et c’est exactement ce qui permet de chercher par sens plutôt que par mots.
2. Le type VECTOR : syntaxe et prérequis
Oracle 23ai ajoute VECTOR comme un type de colonne classique, au même titre que NUMBER ou VARCHAR2. On peut donc stocker un vecteur juste à côté de ses données métier, dans la même table.
CREATE TABLE documents (
doc_id NUMBER PRIMARY KEY,
doc_content CLOB,
doc_vector VECTOR(384, FLOAT32)
);Ici, 384 correspond au nombre de dimensions du vecteur (donc 384 nombres par ligne), et ce chiffre doit correspondre au modèle d’IA utilisé pour générer les vecteurs. FLOAT32 est le format de stockage. Oracle propose aussi INT8 (pour économiser de la mémoire), FLOAT64 (pour plus de précision) et BINARY (pour des cas particuliers).
3. Générer des vecteurs avec un modèle ONNX
Une colonne VECTOR vide ne sert à rien. Il faut d’abord transformer les données en vecteurs, et pour ça, Oracle a besoin d’un modèle d’IA.
Deux façons de faire : générer les vecteurs en dehors de la base puis les charger dans la colonne VECTOR, ou charger un modèle directement dans Oracle et laisser la base générer les vecteurs elle-même. La deuxième option est la plus pratique au quotidien.
Oracle utilise le format ONNX (Open Neural Network Exchange), un format standard qui permet d’importer des modèles d’IA déjà entraînés dans la base. Le modèle le plus utilisé pour démarrer avec le texte est all-MiniLM-L12-v2, léger, rapide, et suffisant pour la plupart des cas de recherche sémantique. Voici comment l’installer sur une base on-premise.
Télécharger le modèle
Oracle héberge une version prête à l’emploi de ce modèle :
mkdir -p /u01/models
cd /u01/models
wget https://adwc4pm.objectstorage.us-ashburn-1.oci.customer-oci.com/p/eLddQappgBJ7jNi6Guz9m9LOtYe2u8LWY19GfgU8flFK4N9YgP4kTlrE9Px3pE12/n/adwc4pm/b/OML-Resources/o/all_MiniLM_L12_v2.onnx
On obtient un fichier all_MiniLM_L12_v2.onnx d’environ 130 Mo, plus un fichier README qui documente le modèle.
Créer l’objet DIRECTORY et donner les droits
Comme pour un import DataPump classique, Oracle a besoin d’un objet DIRECTORY qui pointe vers le dossier contenant le modèle :
SQL> alter session set container=ORCLPDB1;
SQL> CREATE OR REPLACE DIRECTORY model_dir AS '/u01/models';
Repertoire cree.
Pour notre exemple on va créer un utilisateur dédié et lui octroyer les droits nécessaires :
SQL> alter session set container=ORCLPDB1;
CREATE USER test_vector IDENTIFIED BY "***********"
DEFAULT TABLESPACE users
QUOTA UNLIMITED ON users;
GRANT CREATE SESSION TO test_vector;
GRANT CREATE TABLE TO test_vector;
GRANT CREATE MINING MODEL TO test_vector;
GRANT READ, WRITE ON DIRECTORY model_dir TO test_vector;
Le rôle CREATE MINING MODEL est indispensable. Sans lui, l’import du modèle échoue.
Charger le modèle dans la base
sqlplus test_vector/**********@ORCLPDB1
BEGIN
DBMS_VECTOR.DROP_ONNX_MODEL(model_name => 'ALL_MINILM_L12_V2', force => true);
DBMS_VECTOR.LOAD_ONNX_MODEL(
directory => 'MODEL_DIR',
file_name => 'all_MiniLM_L12_v2.onnx',
model_name => 'ALL_MINILM_L12_V2'
);
END;
/Le premier appel supprime un éventuel modèle du même nom déjà présent, pour éviter une erreur si on relance le script.
Le chargement se fait sur la PDB pas sur la CDB root, et avec un utilisateur local au PDB (jamais SYS ni un utilisateur commun) disposant des privilèges CREATE MINING MODEL et des droits sur le DIRECTORY.
Vérifier l’import
SELECT model_name, mining_function, algorithm
FROM user_mining_models
WHERE model_name = 'ALL_MINILM_L12_V2';On doit voir EMBEDDING comme fonction et ONNX comme algorithme. On peut aussi vérifier la dimension produite par le modèle :
SQL> SELECT model_name, mining_function, algorithm
FROM user_mining_models
WHERE model_name = 'ALL_MINILM_L12_V2'; 2 3
MODEL_NAME MINING_FUNCTION ALGORITHM
------------------------------ ------------------------------ ------------------------------
ALL_MINILM_L12_V2 EMBEDDING ONNX
Générer un vecteur
SQL> SELECT VECTOR_EMBEDDING(ALL_MINILM_L12_V2 USING 'test rapide' AS DATA) AS vecteur;
VECTEUR
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
[-1.05790548E-001,1.03741869E-001,-2.28654761E-002,-2.88233384E-002,
Si ça retourne une liste de 384 nombres, le modèle est opérationnel. Aucune ligne de code Python, aucun appel API externe : le modèle tourne dans la base, sur la donnée qui y est déjà stockée. Un vrai plus pour la sécurité, notamment dans des environnements isolés comme ceux qu’on trouve souvent en banque ou en assurance.
4. Exemple pas à pas : chercher un avis client par le sens
Reprenons l’exemple du début et allons jusqu’au bout, avec le modèle qu’on vient d’installer.
Créer la table
CREATE TABLE avis_clients (
id NUMBER PRIMARY KEY,
commentaire VARCHAR2(200),
embedding VECTOR(384, FLOAT32)
);
INSERT INTO avis_clients (id, commentaire) VALUES (1, 'Le service client était excellent et très réactif');
INSERT INTO avis_clients (id, commentaire) VALUES (2, 'L''équipe support a répondu rapidement, très bon accueil');
INSERT INTO avis_clients (id, commentaire) VALUES (3, 'La facturation de ma police d''assurance est incorrecte');
COMMIT;Générer les vecteurs
UPDATE avis_clients
SET embedding = VECTOR_EMBEDDING(ALL_MINILM_L12_V2 USING commentaire AS DATA);
COMMIT;Chaque ligne a maintenant son propre vecteur de 384 nombres, calculé à partir du texte.
Chercher par sens
Un client tape : “Est-ce que le support a été rapide ?”. On transforme cette question en vecteur, et on cherche les commentaires les plus proches :
SQL> COLUMN id FORMAT 999999
SQL> COLUMN commentaire FORMAT A45 WORD_WRAPPED
SQL> COLUMN distance FORMAT 0.9999
SQL> SET LINESIZE 150
SQL>SET PAGESIZE 50
SQL>SELECT id, commentaire,
VECTOR_DISTANCE(embedding,
VECTOR_EMBEDDING(ALL_MINILM_L12_V2 USING 'Est-ce que le support a été rapide ?' AS DATA),
COSINE) AS distance
FROM avis_clients
ORDER BY distance;
ID COMMENTAIRE DISTANCE
----- --------------------------------------------- --------
2 L'équipe support a répondu rapidement, très bon accueil 0.3161
1 Le service client était excellent et très réactif 0.5847
3 La facturation de ma police d'assurance est incorrecte 0.7287
L’avis 2 sort largement devant, avec une distance presque deux fois plus petite que les deux autres. L’avis 3, qui parle de facturation, ressort avec la distance la plus grande. Pourtant, aucun des commentaires ne contient le mot “rapide” tel qu’écrit dans la question. C’est exactement ce qu’on cherchait : une recherche qui comprend le sens, pas seulement les mots.
Avec un LIKE '%rapide%' classique, on aurait raté l’avis 1, qui utilise “réactif” à la place. C’est toute la différence.
5. La recherche de similarité et les métriques de distance
On vient de voir VECTOR_DISTANCE en action. Cette fonction mesure l’écart entre deux vecteurs : plus le résultat est petit, plus les deux données sont proches en sens.
Mais “proche” peut se calculer de plusieurs façons. Oracle propose plusieurs métriques, et le choix dépend du cas d’usage.
COSINE
C’est la métrique la plus utilisée pour le texte. Elle mesure l’angle entre deux vecteurs plutôt que leur distance réelle, ce qui la rend insensible à la longueur du texte d’origine. Un commentaire court et un commentaire long, s’ils parlent de la même chose, auront quand même une distance COSINE faible.
VECTOR_DISTANCE(v1, v2, COSINE)EUCLIDEAN
C’est la distance “à vol d’oiseau” classique, celle qu’on apprend en maths au collège. Elle est plus sensible à la magnitude des vecteurs, donc plus adaptée à des données comme des images ou des mesures numériques, où la taille du vecteur porte une information utile.
VECTOR_DISTANCE(v1, v2, EUCLIDEAN)DOT (produit scalaire)
Utilisée quand on veut aussi tenir compte de l’intensité du signal, pas seulement de la direction. Courante dans les systèmes de recommandation.
VECTOR_DISTANCE(v1, v2, DOT)MANHATTAN
Calcule la distance en suivant les axes plutôt qu’en ligne droite (comme se déplacer dans une ville en quadrillage). Moins courante, mais utile dans certains contextes de données éparses.
VECTOR_DISTANCE(v1, v2, MANHATTAN)HAMMING
Réservée aux vecteurs binaires. Elle compte le nombre de positions différentes entre deux vecteurs. Utile pour des cas très spécifiques de comparaison bit à bit.
VECTOR_DISTANCE(v1, v2, HAMMING)Pour la grande majorité des cas de recherche sémantique sur du texte (avis clients, documents, tickets support), COSINE est le bon choix par défaut. C’est aussi celui utilisé par le modèle all-MiniLM-L12-v2 qu’on a installé plus haut. Pas besoin de se poser trop de questions au démarrage : on part sur COSINE, et on ajuste seulement si un cas d’usage précis le justifie.
6. Les index vectoriels : HNSW et IVF
Sur une petite table comme avis_clients, chercher les vecteurs les plus proches en scannant toute la table ne pose aucun problème. Mais sur des millions de lignes, ce scan complet devient lent. C’est là qu’interviennent les index vectoriels : ils réduisent la zone de recherche au lieu de tout parcourir.
Oracle propose deux types d’index.
HNSW (Hierarchical Navigable Small World)
C’est un index en mémoire, organisé en graphe. Il offre la meilleure vitesse et la meilleure précision, mais il faut que le graphe tienne dans une zone mémoire dédiée, le vector pool, dimensionnée via le paramètre VECTOR_MEMORY_SIZE.
ALTER SYSTEM SET VECTOR_MEMORY_SIZE = 2G SCOPE=BOTH;
CREATE VECTOR INDEX avis_hnsw_idx
ON avis_clients (embedding)
ORGANIZATION INMEMORY NEIGHBOR GRAPH
DISTANCE COSINE
WITH TARGET ACCURACY 95;Pour estimer la mémoire nécessaire, une règle simple : nombre de vecteurs × nombre de dimensions × taille du format × 1.3. Sur un million de lignes en 384 dimensions et FLOAT32, ça tourne autour de 2 Go.
IVF (Inverted File Flat)
C’est un index sur disque, basé sur des partitions. Les vecteurs sont regroupés en clusters, chacun avec un centre (centroïde). Une recherche ne compare qu’aux clusters les plus proches du vecteur recherché, au lieu de tout comparer.
CREATE VECTOR INDEX avis_ivf_idx
ON avis_clients (embedding)
ORGANIZATION NEIGHBOR PARTITIONS
DISTANCE COSINE
WITH TARGET ACCURACY 95;IVF est un peu moins rapide que HNSW, mais il n’a pas cette contrainte de mémoire : il convient mieux aux très gros volumes, et surtout, il fonctionne en RAC, contrairement à HNSW qui reste une limite pour un environnement multi-nœuds.
Comment choisir
La règle pratique : on part sur HNSW si les données tiennent en mémoire et que l’instance est unique. On bascule sur IVF dès que le volume dépasse la capacité du vector pool, ou dès qu’on est en RAC.
Le paramètre TARGET ACCURACY (de 0 à 100) permet d’arbitrer entre vitesse et précision : plus il est élevé, plus la recherche est précise, mais plus elle coûte cher en ressources. 95 est un bon point de départ pour la plupart des cas.
7. Conclusion
Le type VECTOR change une chose fondamentale : Oracle sait maintenant chercher par sens, pas seulement par mots. Et surtout, tout ça se passe dans la base, sans avoir besoin d’exporter ses données vers un outil externe. Pour une entreprise qui a des exigences fortes en matière de sécurité et de conformité, comme en banque ou en assurance, c’est un vrai atout : les données sensibles n’ont jamais besoin de sortir de leur environnement habituel.
Quelques cas d’usage concrets où cette fonctionnalité prend tout son sens :
Recherche sémantique : retrouver un document, un ticket support ou un avis client à partir d’une question posée en langage naturel, même sans les bons mots-clés. C’est l’exemple qu’on a construit dans cet article.
RAG (Retrieval Augmented Generation) : donner à un modèle de langage l’accès à des données internes précises avant de générer une réponse, pour éviter les réponses inventées et s’appuyer sur des documents réels de l’entreprise.
Recommandation : proposer des produits, des contenus ou des documents similaires à ce qu’un utilisateur a déjà consulté, en se basant sur la proximité de sens plutôt que sur des règles figées.
Détection d’anomalies ou de doublons : repérer des enregistrements qui se ressemblent fortement en sens, même s’ils sont formulés différemment, utile pour du nettoyage de données ou de la déduplication.
Poster un Commentaire