Guide détaillé du développement de Chromadb Python

Installation

pip install chromadb

Persistence des données Chromadb

import chromadb

Vous pouvez spécifier le chemin de stockage du fichier de base de données Chroma. Si les données existent, le fichier de base de données sera automatiquement chargé lorsque le programme démarre.

client = chromadb.PersistentClient(path="/data/tizi365.db")

Le paramètre path est le chemin vers le fichier de base de données Chroma.

Remarque: pour une base de données Chroma, il suffit de créer une fois un objet client. Charger et enregistrer plusieurs clients dans le même chemin peut entraîner un comportement inattendu, y compris la suppression de données. Généralement, un seul client Chroma devrait être créé dans l'application.

Quelques fonctions couramment utilisées de l'objet client:

client.reset()  # Efface et réinitialise complètement la base de données

Opérations de collection

Chromadb utilise le primitive collection pour gérer les collections de données vectorielles, qui peuvent être assimilées à des tables dans MYSQL.

Création, visualisation et suppression de collections

Chroma utilise le nom de collection dans l'URL, il a donc certaines restrictions de nommage:

  • La longueur du nom doit être comprise entre 3 et 63 caractères.
  • Le nom doit commencer et se terminer par une lettre minuscule ou un chiffre, et peut contenir des points, des tirets et des soulignés entre eux.
  • Le nom ne peut pas contenir deux points consécutifs.
  • Le nom ne peut pas être une adresse IP valide.

Pour créer une collection, vous devez spécifier le nom de la collection et éventuellement une fonction de calcul vectoriel optionnelle (également appelée fonction d'incorporation). Si une fonction d'incorporation est fournie, elle doit être fournie à chaque fois que la collection est consultée.

Remarque: le but de la fonction de calcul vectoriel (fonction d'incorporation) est de calculer le vecteur de texte.

collection = client.create_collection(name="ma_collection", embedding_function=emb_fn)
collection = client.get_collection(name="ma_collection", embedding_function=emb_fn)

La fonction d'incorporation prend du texte en entrée et renvoie un vecteur calculé.

Remarque: Les débutants peuvent apprendre à partir des tutoriels sur le modèle d'incorporation de texte.

Vous pouvez faire référence à une collection existante avec la fonction .get_collection, et utiliser .delete_collection pour supprimer une collection. Vous pouvez également utiliser .get_or_create_collection pour faire référence à une collection (si elle existe) ou la créer si elle n'existe pas.

collection = client.get_collection(name="tizi365")
collection = client.get_or_create_collection(name="tizi365")
client.delete_collection(name="tizi365")

Autres opérations de collection couramment utilisées:

collection.peek() # Renvoie une liste des 10 premières données de la collection
collection.count() # Renvoie le nombre total de données dans la collection
collection.modify(name="nouveau_nom") # Renomme la collection

Spécification de la méthode de calcul de la distance vectorielle

La fonction create_collection inclut également un paramètre facultatif metadata. En définissant la valeur de hnsw:space pour personnaliser la méthode de calcul de distance de l'espace vectoriel.

Remarque: Les données vectorielles représentent la similarité entre les vecteurs en calculant la distance spatiale entre les vecteurs. Plus la distance est courte, plus la similarité est élevée, et vice versa.

collection = client.create_collection(
        name="nom_de_collection",
        metadata={"hnsw:space": "cosine"} # l2 est la méthode de calcul par défaut
    )

Les options valides pour hnsw:space sont "l2", "ip", ou "cosine". Par défaut, c'est "l2".

Ajout de données à une collection

Utilisez la méthode .add pour ajouter des données à Chroma.

Ajoutez des données directement sans spécifier les vecteurs de document :

collection.add(
    documents=["lorem ipsum...", "doc2", "doc3", ...],
    metadatas=[{"chapitre": "3", "verset": "16"}, {"chapitre": "3", "verset": "5"}, {"chapitre": "29", "verset": "11"}, ...],
    ids=["id1", "id2", "id3", ...]
)

Si Chroma reçoit une liste de documents, il utilisera automatiquement la fonction d'incorporation de la collection pour calculer les vecteurs des documents (si aucune fonction d'incorporation n'a été fournie lors de la création de la collection, la valeur par défaut sera utilisée). Chroma stockera également les documents eux-mêmes. Si un document est trop volumineux pour être calculé en utilisant la fonction d'incorporation sélectionnée, une exception se produira.

Chaque document doit avoir un identifiant unique (ids). Ajouter le même identifiant deux fois se traduira par le stockage de la valeur initiale uniquement. Optionnellement, vous pouvez fournir une liste de dictionnaires de métadonnées (metadatas) pour chaque document, afin de stocker des informations supplémentaires pouvant être utilisées pour filtrer les données lors de requêtes.

Alternativement, vous pouvez directement fournir une liste des données vectorielles associées au document, et Chroma utilisera les données vectorielles que vous fournissez sans calculer automatiquement les vecteurs.

collection.add(
    documents=["doc1", "doc2", "doc3", ...],
    embeddings=[[1.1, 2.3, 3.2], [4.5, 6.9, 4.4], [1.1, 2.3, 3.2], ...],
    metadatas=[{"chapitre": "3", "verset": "16"}, {"chapitre": "3", "verset": "5"}, {"chapitre": "29", "verset": "11"}, ...],
    ids=["id1", "id2", "id3", ...]
)

Si les dimensions des données vectorielles fournies ne correspondent pas aux dimensions de la collection, une exception se produira.

Vous pouvez également stocker les documents ailleurs et fournir à Chroma les données vectorielles et la liste de métadonnées. Vous pouvez utiliser les ids pour associer les vecteurs aux documents stockés ailleurs.

collection.add(
    embeddings=[[1.1, 2.3, 3.2], [4.5, 6.9, 4.4], [1.1, 2.3, 3.2], ...],
    metadatas=[{"chapitre": "3", "verset": "16"}, {"chapitre": "3", "verset": "5"}, {"chapitre": "29", "verset": "11"}, ...],
    ids=["id1", "id2", "id3", ...]
)

Remarque : La fonction principale de la base de données vectorielle est la recherche de similarité sémantique basée sur les données vectorielles. Pour réduire la taille de la base de données vectorielles et améliorer l'efficacité, nous pouvons choisir de stocker des données vectorielles et certaines attributs de filtrage nécessaires dans la base de données vectorielles. D'autres données, telles que le contenu des articles, peuvent être stockées dans des bases de données comme MYSQL, tant qu'elles sont associées via des identifiants.

Interrogation des données de la collection

La méthode .query peut être utilisée pour interroger les ensembles de données Chroma de plusieurs façons.

Vous pouvez interroger en utilisant un ensemble de query_embeddings (données vectorielles).

Astuce : Dans des scénarios de développement réels, les query_embeddings sont généralement obtenus en calculant d'abord le vecteur de la requête de l'utilisateur à l'aide d'un modèle d'incrustation de texte, puis en utilisant ce vecteur pour interroger un contenu similaire.

collection.query(
    query_embeddings=[[11.1, 12.1, 13.1],[1.1, 2.3, 3.2], ...],
    n_results=10,
    where={"metadata_field": "is_equal_to_this"},
    where_document={"$contains":"search_string"}
)

La requête renverra les n_results résultats correspondant le mieux à chaque vecteur de requête (query_embedding) dans l'ordre. Un dictionnaire de filtre où optionnel peut être fourni pour filtrer les résultats en fonction des métadonnées associées à chaque document. De plus, un dictionnaire de filtre où_document optionnel peut être fourni pour filtrer les résultats en fonction du contenu du document.

Si les query_embeddings fournis ne sont pas cohérents avec les dimensions de la collection, une exception se produira. Pour assurer des dimensions de vecteur cohérentes, utilisez le même modèle d'incrustation de texte pour calculer les vecteurs.

Vous pouvez également interroger en utilisant un ensemble de textes de requête. Chroma calculera d'abord le vecteur pour chaque texte de requête en utilisant la fonction d'incrustation de la collection, puis effectuera la requête en utilisant les vecteurs de texte générés.

collection.query(
    query_texts=["doc10", "ainsi parlait Zarathoustra", ...],
    n_results=10,
    where={"metadata_field": "is_equal_to_this"},
    where_document={"$contains":"search_string"}
)

Vous pouvez également utiliser .get pour interroger les données de la collection par identifiant.

collection.get(
    ids=["id1", "id2", "id3", ...],
    where={"style": "style1"}
)

.get prend également en charge les filtres where et where_document. Si aucun identifiant n'est fourni, il renverra tous les éléments de la collection correspondant aux filtres where et where_document.

Spécification des champs renvoyés

Lors de l'utilisation de get ou query, vous pouvez utiliser le paramètre include pour spécifier les données à renvoyer--embeddings, documents, ou metadatas, et pour les requêtes, les données de distance doivent être renvoyées. Par défaut, Chroma renvoie des documents et des métadonnées, et renvoie des données de distance pour les requêtes, tandis que "ids" est toujours renvoyé. Vous pouvez spécifier les champs à renvoyer en passant un tableau de noms de champs au paramètre includes de la méthode query ou get.

collection.get(
    include=["documents"]
)

collection.query(
    query_embeddings=[[11.1, 12.1, 13.1],[1.1, 2.3, 3.2], ...],
    include=["documents"]
)

Utilisation des filtres where

Chroma prend en charge le filtrage des requêtes en fonction des métadonnées et du contenu du document. Le filtre where est utilisé pour filtrer les métadonnées, et le filtre where_document est utilisé pour filtrer le contenu du document, et ce qui suit explique comment écrire les expressions de condition de filtre.

Filtrage par métadonnées

Pour filtrer les métadonnées, vous devez fournir un dictionnaire de filtre where pour la requête. Le dictionnaire doit avoir la structure suivante:

{
    "champ_de_métadonnées": {
        <Opérateur>: <Valeur>
    }
}

Le filtrage des métadonnées prend en charge les opérateurs suivants:

  • $eq - égal à (chaîne, entier, flottant)
  • $ne - non égal à (chaîne, entier, flottant)
  • $gt - supérieur à (entier, flottant)
  • $gte - supérieur ou égal à (entier, flottant)
  • $lt - inférieur à (entier, flottant)
  • $lte - inférieur ou égal à (entier, flottant)

L'utilisation de l'opérateur $eq est équivalente à l'utilisation du filtre where.

{
    "champ_de_métadonnées": "search_string"
}

{
    "champ_de_métadonnées": {
        "$eq": "search_string"
    }
}

Filtrage du contenu du document

Pour filtrer le contenu du document, vous devez fournir un dictionnaire de filtre where_document pour la requête. Le dictionnaire doit avoir la structure suivante:

{
    "$contains": "search_string"
}

Utilisation des opérateurs logiques

Vous pouvez également utiliser les opérateurs logiques $and et $or pour combiner plusieurs filtres.

L'opérateur $and renverra les résultats correspondant à l'ensemble des filtres de la liste.

{
    "$and": [
        {
            "metadata_field": {
                <Opérateur>: <Valeur>
            }
        },
        {
            "metadata_field": {
                <Opérateur>: <Valeur>
            }
        }
    ]
}

L'opérateur $or renverra les résultats correspondant à l'une quelconque des conditions de filtre de la liste.

{
    "$or": [
        {
            "metadata_field": {
                <Opérateur>: <Valeur>
            }
        },
        {
            "metadata_field": {
                <Opérateur>: <Valeur>
            }
        }
    ]
}

Mise à jour des données dans une collection

L'utilisation de .update vous permet de mettre à jour toutes les propriétés des données dans une collection.

collection.update(
    ids=["id1", "id2", "id3", ...],
    embeddings=[[1.1, 2.3, 3.2], [4.5, 6.9, 4.4], [1.1, 2.3, 3.2], ...],
    metadatas=[{"chapter": "3", "verse": "16"}, {"chapter": "3", "verse": "5"}, {"chapter": "29", "verse": "11"}, ...],
    documents=["doc1", "doc2", "doc3", ...],
)

Si un identifiant n'est pas trouvé dans la collection, une erreur sera enregistrée et la mise à jour sera ignorée. Si le document fourni n'a pas de vecteur correspondant, la fonction d'encastrement de la collection sera utilisée pour calculer le vecteur.

Si les données de vecteur fournies ont une dimension différente de la collection, une exception se produira.

Chroma prend également en charge l'opération d'upsert, qui peut mettre à jour des données existantes et insérer de nouvelles données si elles n'existent pas.

collection.upsert(
    ids=["id1", "id2", "id3", ...],
    embeddings=[[1.1, 2.3, 3.2], [4.5, 6.9, 4.4], [1.1, 2.3, 3.2], ...],
    metadatas=[{"chapter": "3", "verse": "16"}, {"chapter": "3", "verse": "5"}, {"chapter": "29", "verse": "11"}, ...],
    documents=["doc1", "doc2", "doc3", ...],
)

Suppression de données de la collection

Chroma prend en charge l'utilisation de .delete pour supprimer des données d'une collection par id. Les vecteurs, documents et métadonnées associés à chaque donnée seront également supprimés.

collection.delete(
    ids=["id1", "id2", "id3",...],
    where={"chapter": "20"}
)

.delete prend également en charge un filtre where. Si aucun identifiant n'est fourni, il supprimera tous les éléments de la collection correspondant au filtre where.