Fermer

janvier 31, 2024

Moteur de recherche alimenté par l’IA avec base de données vectorielles Milvus sur Vultr

Moteur de recherche alimenté par l’IA avec base de données vectorielles Milvus sur Vultr


Les bases de données vectorielles sont couramment utilisées pour stocker des intégrations vectorielles pour des tâches telles que la recherche de similarité afin de créer des systèmes de recommandation et de réponse aux questions. Milvus est l’une des bases de données open source qui stocke les intégrations sous forme de données vectorielles. Elle est bien adaptée car elle possède des fonctionnalités d’indexation telles que les voisins les plus proches approximatifs (ANN) permettant des résultats rapides et précis.

Dans cet article, nous montrerons les étapes à suivre pour utiliser un ensemble de données HuggingFace, créer des intégrations à partir de l’ensemble de données et diviser l’ensemble de données en deux moitiés (test et formation). Vous apprendrez également à stocker toutes les intégrations créées dans la base de données Milvus déployée en créant une collection, puis à effectuer une opération de recherche en posant une question et en générant les réponses les plus similaires.

Déployer un serveur sur Vultr

  1. Inscrivez-vous et connectez-vous au Portail client Vultr.
  2. Accédez au Des produits page.
  3. Dans le menu latéral, sélectionnez Calculer.Image du menu latéral de calcul du portail client Vultr pour déployer un nouveau serveur Cloud Compute
  4. Clique le Déployer le serveur bouton au centre.
  5. Sélectionner GPU cloud comme type de serveur.
  6. Sélectionner A100 comme type de GPU.
  7. Dans la section « Emplacement du serveur », sélectionnez la région de votre choix.
  8. Dans la section « Système d’exploitation », sélectionnez Pile GPU Vultr comme système d’exploitation.Image du menu de sélection du système d'exploitation du portail client Vultr pour déployer GPU StackPile GPU Vultr est conçu pour rationaliser le processus de création de projets d’intelligence artificielle (IA) et d’apprentissage automatique (ML) en fournissant une suite complète de logiciels préinstallés, notamment NVIDIA CUDA Toolkit, NVIDIA cuDNN, TensorFlow, PyTorch, etc.
  9. Dans la section « Taille du serveur », sélectionnez le 80 Go option.
  10. Sélectionnez les fonctionnalités supplémentaires nécessaires dans la section « Fonctionnalités supplémentaires ».
  11. Clique le Déployer maintenant bouton dans le coin inférieur droit.
  12. Accédez au Des produits page.
  13. Dans le menu latéral, sélectionnez Kubernetes.
  14. Clique le Ajouter un cluster bouton au centre.
  15. Tapez un Nom du cluster.
  16. Dans la section « Localisation du cluster », sélectionnez la région de votre choix.
  17. Tapez un Étiquette pour le pool de clusters.
  18. Augmenter le Nombre de nœuds à 5.
  19. Clique le Déployer maintenant bouton dans le coin inférieur droit.

Préparation du serveur

  1. Installer Kubectl
  2. Déployez un cluster Milvus sur le serveur GPU.

Installation des packages requis

Après avoir configuré un serveur Vultr et un cluster Vultr Kubernetes comme décrit précédemment, cette section vous guidera dans l’installation des packages de dépendances Python nécessaires à la création d’une base de données Milvus et à l’importation des modules nécessaires dans la console Python.

  1. Installer les dépendances requises
    pip install transformers datasets pymilvus torch
    

    Voici ce que représente chaque package :

    • transformers: Fournit un accès et permet de travailler avec des modèles LLM pré-entraînés pour des tâches telles que la classification et la génération de texte.
    • datasets: Fournit un accès et permet de travailler sur des ensembles de données prêts à l’emploi pour les tâches PNL.
    • pymilvus: Client Python pour Milvus qui permet la recherche de similarités vectorielles, le stockage et la gestion de grandes collections de vecteurs.
    • torch: bibliothèque d’apprentissage automatique utilisée pour la formation et la création de modèles d’apprentissage profond.
  2. Accéder à la console Python
    python3
    
  3. Importer les modules requis
    from pymilvus import connections, FieldSchema, CollectionSchema, DataType, Collection, utility
    from datasets import load_dataset_builder, load_dataset, Dataset
    from transformers import AutoTokenizer, AutoModel
    from torch import clamp, sum
    

    Voici ce que représente chaque package :

    • pymilvus modules:
      • connections: Fournit des fonctions de gestion des connexions avec la base de données Milvus.
      • FieldSchema: Définit le schéma des champs dans une base de données Milvus.
      • CollectionSchema: Définit le schéma de la collection.
      • DataType: Énumère les types de données qui peuvent être utilisés dans la collection Milvus.
      • Collection: Fournit la fonctionnalité permettant d’interagir avec les collections Milvus pour créer, insérer et rechercher des vecteurs.
      • utility: Fournit les fonctions de prétraitement des données et d’optimisation des requêtes pour fonctionner avec Milvus
    • datasets modules:
      • load_dataset_builder: charge et renvoie un objet d’ensemble de données pour accéder aux informations de la base de données et à ses métadonnées.
      • load_dataset: charge un ensemble de données à partir d’un générateur d’ensemble de données et renvoie l’objet ensemble de données pour l’accès aux données.
      • Dataset: Représente un ensemble de données, donnant accès aux opérations liées aux données.
    • transformers modules:
      • AutoTokenizer: charge les modèles de tokenisation pré-entraînés pour les tâches NLP.
      • AutoModel: Il s’agit d’une classe de chargement de modèles permettant de charger automatiquement les modèles pré-entraînés pour les tâches PNL.
    • torch modules:
      • clamp: Fournit des fonctions pour la limitation par élément des valeurs du tenseur.
      • sum: calcule la somme des éléments tenseurs selon les dimensions spécifiées.

Construire une architecture de questions-réponses

Dans cette section, vous apprendrez à créer une collection, à insérer des données dans la collection et à effectuer des opérations de recherche en fournissant une entrée sous forme de question-réponse.

  1. Déclarez les paramètres, assurez-vous de remplacer le EXTERNAL_IP_ADDRESS avec la valeur réelle.
    DATASET = 'squad'
    MODEL = 'bert-base-uncased' 
    TOKENIZATION_BATCH_SIZE = 1000  
    INFERENCE_BATCH_SIZE = 64  
    INSERT_RATIO = .001 
    COLLECTION_NAME = 'huggingface_db'  
    DIMENSION = 768  
    LIMIT = 10 
    MILVUS_HOST = "EXTERNAL_IP_ADDRESS"
    MILVUS_PORT = "19530"
    

    Voici ce que représente chaque paramètre :

    • DATASET: définit l’ensemble de données Huggingface à utiliser pour rechercher des réponses.
    • MODEL: Définit le transformateur à utiliser pour créer des intégrations.
    • TOKENIZATION_BATCH_SIZE: Détermine le nombre de textes traités simultanément pendant la tokenisation et aide à accélérer la tokenisation en utilisant le parallélisme.
    • INFERENCE_BATCH_SIZE: définit la taille du lot pour les prédictions, affectant l’efficacité des tâches de classification de texte. Vous pouvez réduire la taille du lot à 32 ou 18 lorsque vous utilisez une taille de GPU plus petite.
    • INSERT_RATIO: Contrôle la partie des données texte à convertir en intégrations en gérant le volume de données à indexer pour effectuer une recherche vectorielle.
    • COLLECTION_NAME: Définit le nom de la collection que vous allez créer.
    • DIMENSION: Définit la taille d’une intégration individuelle que vous allez stocker dans la collection.
    • LIMIT: Définit le nombre de résultats à rechercher et à afficher dans la sortie.
    • MILVUS_HOST: Définit l’adresse IP externe pour accéder à la base de données Milvus déployée.
    • MILVUS_PORT: Définit le port sur lequel la base de données Milvus déployée est exposée.
  2. Connectez-vous à la base de données Milvus externe que vous avez déployée en utilisant l’adresse IP externe et le port sur lesquels Milvus est exposé. Assurez-vous de remplacer le user et password valeurs de champ avec les valeurs appropriées. Si vous accédez à la base de données pour la première fois, le user = racine et password = Cerf-volant
    connections.connect(host="MILVUS_HOST", port="MILVUS_PORT", user="USER", password="PASSWORD")
    

Créer une collection

Dans cette section, vous apprendrez à créer une collection et à définir son schéma pour stocker le contenu de l’ensemble de données de manière appropriée. Vous apprendrez également à créer des index et à charger la collection.

  1. Vérifiez l’existence de la collection, si la collection est présente alors elle est supprimée pour éviter tout conflit.
    if utility.has_collection(COLLECTION_NAME):
    utility.drop_collection(COLLECTION_NAME)
    
  2. Créez une collection nommée huggingface_db et définir le schéma de collection.
    fields = [
        FieldSchema(name='id', dtype=DataType.INT64, is_primary=True, auto_id=True),
        FieldSchema(name='original_question', dtype=DataType.VARCHAR, max_length=1000),
        FieldSchema(name='answer', dtype=DataType.VARCHAR, max_length=1000),
        FieldSchema(name='original_question_embedding', dtype=DataType.FLOAT_VECTOR, dim=DIMENSION)
    ]
    schema = CollectionSchema(fields=fields)
    collection = Collection(name=COLLECTION_NAME, schema=schema)
    

    Voici les champs utilisés pour définir le schéma de la collection :

    • id: Champ principal à partir duquel toutes les entrées de la base de données doivent être identifiées.
    • original_question: C’est le champ où est stockée la question d’origine à partir de laquelle la question que vous avez posée va être mise en correspondance.
    • answer: C’est le champ contenant la réponse à chaque original_quesition.
    • original_question_embedding: Contient les intégrations pour chaque entrée dans original_question pour effectuer une recherche de similarité avec la question que vous avez donnée en entrée.
  3. Créez un index pour le original_question_embedding champ pour effectuer une recherche de similarité.
    index_params = {
        'metric_type':'L2',
        'index_type':"IVF_FLAT",
        'params':{"nlist":1536}
    }
    
    collection.create_index(field_name="original_question_embedding", index_params=index_params)
    

    Une fois la création réussie de l’index du champ spécifié, le résultat ci-dessous s’affichera :

    Status(code=0, message=)
  4. Chargez la collection pour vous assurer qu’elle est prête à effectuer une opération de recherche.
    collection.load()
    

Insérer des données dans la collection

Dans cette section, vous apprendrez à diviser l’ensemble de données en ensembles, à tokeniser toutes les questions de l’ensemble de données, à créer des intégrations et à les insérer dans la collection.

  1. Chargez l’ensemble de données, divisez l’ensemble de données en ensembles d’entraînement et de test, et traitez l’ensemble de test pour supprimer toutes les autres colonnes à l’exception du texte de réponse.
    data_dataset = load_dataset(DATASET, split='all')
    
    data_dataset = data_dataset.train_test_split(test_size=INSERT_RATIO, seed=42)['test']
    
    data_dataset = data_dataset.map(lambda val: {'answer': val['answers']['text'][0]}, remove_columns=['answers'])
    
  2. Initialisez le tokenizer.
    tokenizer = AutoTokenizer.from_pretrained(MODEL)
    
  3. Définissez la fonction pour tokeniser les questions.
    def tokenize_question(batch):
        results = tokenizer(batch['question'], add_special_tokens = True, truncation = True, padding = "max_length", return_attention_mask = True, return_tensors = "pt")
        batch['input_ids'] = results['input_ids']
        batch['token_type_ids'] = results['token_type_ids']
        batch['attention_mask'] = results['attention_mask']
        return batch
    
  4. Tokenisez chaque entrée de question à l’aide du tokenize_question fonction définie précédemment et définissez la sortie sur torch format compatible pour les modèles d’apprentissage automatique basés sur PyTorch.
    data_dataset = data_dataset.map(tokenize_question, batch_size=TOKENIZATION_BATCH_SIZE, batched=True)
    
    data_dataset.set_format('torch', columns=['input_ids', 'token_type_ids', 'attention_mask'], output_all_columns=True)
    
  5. Chargez le modèle pré-entraîné, transmettez les questions tokenisées, générez les intégrations à partir des questions et insérez-les dans l’ensemble de données comme question_embeddings.
    model = AutoModel.from_pretrained(MODEL)
    
    def embed(batch):
        sentence_embs = model(
                    input_ids=batch['input_ids'],
                    token_type_ids=batch['token_type_ids'],
                    attention_mask=batch['attention_mask']
                    )[0]
        input_mask_expanded = batch['attention_mask'].unsqueeze(-1).expand(sentence_embs.size()).float()
        batch['question_embedding'] = sum(sentence_embs * input_mask_expanded, 1) / clamp(input_mask_expanded.sum(1), min=1e-9)
        return batch
    
    data_dataset = data_dataset.map(embed, remove_columns=['input_ids', 'token_type_ids', 'attention_mask'], batched = True, batch_size=INFERENCE_BATCH_SIZE)
    
  6. Insérez des questions dans la collection.
    def insert_function(batch):
        insertable = [
            batch['question'],
            [x[:995] + '...' if len(x) > 999 else x for x in batch['answer']],
            batch['question_embedding'].tolist()
            ]    
        collection.insert(insertable)
    
    data_dataset.map(insert_function, batched=True, batch_size=64)
    collection.flush()
    

    Le résultat ressemblera à ceci :

    Dataset({
            features: ['id', 'title', 'context', 'question', 'answer', 'input_ids', 'token_type_ids', 'attention_mask', 'question_embedding'],
            num_rows: 99
        })

Générer des réponses

Dans cette section, vous apprendrez comment fournir une invite, tokeniser et intégrer l’invite pour effectuer une recherche de similarité et générer les réponses les plus pertinentes.

  1. Créez un ensemble de données d’invite, vous pouvez remplacer la question par n’importe quelle invite personnalisée et vous pouvez également déterminer le nombre de questions par invite.
    questions = {'question':['When was maths invented?']}
    question_dataset = Dataset.from_dict(questions)
    
  2. Tokenisez et intégrez l’invite.
    question_dataset = question_dataset.map(tokenize_question, batched = True, batch_size=TOKENIZATION_BATCH_SIZE)
    
    question_dataset.set_format('torch', columns=['input_ids', 'token_type_ids', 'attention_mask'], output_all_columns=True)
    
    question_dataset = question_dataset.map(embed, remove_columns=['input_ids', 'token_type_ids', 'attention_mask'], batched = True, batch_size=INFERENCE_BATCH_SIZE)
    
  3. Définir la search fonction qui effectue des opérations de recherche à l’aide des intégrations créées précédemment. Les informations récupérées sont organisées en listes et renvoyées sous forme de dictionnaire.
    def search(batch):
        res = collection.search(batch['question_embedding'].tolist(), anns_field='original_question_embedding', param = {}, output_fields=['answer', 'original_question'], limit = LIMIT)
        overall_id = []
        overall_distance = []
        overall_answer = []
        overall_original_question = []
        for hits in res:
            ids = []
            distance = []
            answer = []
            original_question = []
            for hit in hits:
                ids.append(hit.id)
                distance.append(hit.distance)
                answer.append(hit.entity.get('answer'))
                original_question.append(hit.entity.get('original_question'))
            overall_id.append(ids)
            overall_distance.append(distance)
            overall_answer.append(answer)
            overall_original_question.append(original_question)
        return {
            'id': overall_id,
            'distance': overall_distance,
            'answer': overall_answer,
            'original_question': overall_original_question
        }
    
  4. Effectuez l’opération de recherche en appliquant le paramètre défini précédemment search fonctionner dans le question_dataset.
    question_dataset = question_dataset.map(search, batched=True, batch_size = 1)
    
    for x in question_dataset:
        print()
        print('Question:')
        print(x['question'])
        print('Answer, Distance, Original Question')
        for x in zip(x['answer'], x['distance'], x['original_question']):
            print(x)
    

    Le résultat ressemblera à ceci :

    Question:
    When was maths invented?
    Answer, Distance, Original Question
    ('until 1870', tensor(33.3018), 'When did the Papal States exist?')
    ('October 1992', tensor(34.8276), 'When were free elections held?')
    ('1787', tensor(36.0596), 'When was the Tower constructed?')
    ('Poland, Bulgaria, the Czech Republic, Slovakia, Hungary, Albania, former East Germany and Cuba', tensor(38.3254), 'Where was Russian schooling mandatory in the 20th century?')
    ('6,000 years', tensor(41.9444), 'How old did biblical scholars think the Earth was?')
    ('1992', tensor(42.2079), 'In what year was the Premier League created?')
    ('1981', tensor(44.7781), "When was ZE's Mutant Disco released?")
    ('Medieval Latin', tensor(46.9699), "What was the Latin of Charlemagne's era later known as?")
    ('taxation', tensor(49.2372), 'How did Hobson argue to rid the world of imperialism?')
    ('light weight, relative unbreakability and low surface noise', tensor(49.5037), "What were advantages of vinyl in the 1930's?")
    

    Dans la sortie ci-dessus, les 10 réponses les plus proches sont imprimées dans un ordre décroissant pour la question que vous avez posée avec les questions d’origine auxquelles appartiennent ces réponses, la sortie affiche également les valeurs tensorielles avec chaque réponse, moins de valeur tensorielle signifie que la réponse est plus précise. pour la question que vous avez posée.

Conclusion

Dans cet article, vous avez appris à créer un système de questions-réponses à l’aide d’un ensemble de données HuggingFace et d’une base de données Milvus. Le didacticiel vous a guidé à travers les étapes permettant de créer des intégrations à partir d’un ensemble de données, de les stocker dans une collection, puis d’effectuer une recherche de similarité pour trouver les réponses les mieux adaptées à l’invite en créant l’intégration de la question fournie et en calculant les tenseurs.

Il s’agit d’un article sponsorisé par Vultr. Vultr est la plus grande plateforme de cloud computing privée au monde. Un favori des développeurs, Vultr a servi plus de 1,5 million de clients dans 185 pays avec des solutions mondiales flexibles et évolutives de Cloud Compute, Cloud GPU, Bare Metal et Cloud Storage. En savoir plus sur Vultr.




Source link