Tutoriel : Réduire le stockage et les coûts (RAG dans Recherche Azure AI)
Recherche Azure AI offre plusieurs approches pour réduire la taille des index vectoriels. Ces approches vont de la compression vectorielle, au fait d’être plus sélectif sur ce que vous stockez sur votre service de recherche.
Dans ce tutoriel, vous modifiez l'index de recherche existant pour l'utiliser :
- Types de données restreints
- Quantification scalaire
- Stockage réduit en optant pour les vecteurs dans les résultats de la recherche
Ce tutoriel reprend l’index de recherche créé par le pipeline d’indexation. Toutes ces mises à jour affectent le contenu existant, ce qui vous oblige à réexécuter l’indexeur. Toutefois, au lieu de supprimer l’index de recherche, vous créez un deuxième afin de pouvoir comparer les réductions de la taille de l’index vectoriel après avoir ajouté les nouvelles fonctionnalités.
Dans l’ensemble, les techniques illustrées dans ce tutoriel permettent de réduire de moitié environ la taille de l’index vectoriel.
La capture d’écran suivante compare le premier index d’un tutoriel précédent à l’index intégré dans celui-ci.
Prérequis
Ce tutoriel est essentiellement une réexécution du pipeline d’indexation. Vous avez besoin de toutes les ressources et autorisations Azure décrites dans ce tutoriel.
Pour comparaison, vous devez disposer d’un index py-rag-tutorial-idx existant sur votre service Recherche d’IA Azure. Il doit avoir une taille de près de 2 Mo, et la partie index vectoriel doit avoir une taille de 348 Ko.
Vous devez également disposer des objets suivants :
py-rag-tutorial-ds (source de données)
py-rag-tutorial-ss (ensemble de compétences)
Télécharger l’exemple
Téléchargez un notebook Jupyter depuis GitHub pour envoyer les requêtes à Recherche Azure AI. Si vous souhaitez obtenir plus d’informations, consultez Téléchargement de fichiers à partir de GitHub.
Mettre à jour l’index pour le stockage réduit
Recherche Azure AI a plusieurs approches pour réduire la taille des vecteurs, réduisant ainsi le coût des charges de travail vectorielles. Dans cette étape, créez un index qui utilise les fonctionnalités suivantes :
Des index vectoriels plus petits en compressant les vecteurs utilisés pendant l'exécution de la requête. La quantification scalaire fournit cette capacité.
Des index vectoriels plus petits en optant pour le stockage vectoriel pour les résultats de la recherche. Si vous avez besoin de vecteurs uniquement pour les requêtes et non pour les réponses dans la charge utile, vous pouvez supprimer la copie de vecteurs utilisée pour les résultats de la recherche.
Champs vectoriels plus petits via des types de données étroits. Vous pouvez spécifier
Collection(Edm.Half)
dans le champ text_vector pour stocker les dimensions float32 entrantes sous forme de float16.
Toutes ces fonctionnalités sont spécifiées dans un index de recherche. Après avoir chargé l’index, comparez la différence entre l’index d’origine et la nouvelle.
Nommez le nouvel index
py-rag-tutorial-small-vectors-idx
.Utilisez la définition suivante pour le nouvel index. La différence entre ce schéma et les mises à jour précédentes du schéma dans Améliorer la pertinence sont de nouvelles classes pour la quantification scalaire et une nouvelle section sur les compressions, un nouveau type de données (
Collection(Edm.Half)
) pour le champ text_vector et une nouvelle propriétéstored
définie sur false.from azure.identity import DefaultAzureCredential from azure.identity import get_bearer_token_provider from azure.search.documents.indexes import SearchIndexClient from azure.search.documents.indexes.models import ( SearchField, SearchFieldDataType, VectorSearch, HnswAlgorithmConfiguration, VectorSearchProfile, AzureOpenAIVectorizer, AzureOpenAIVectorizerParameters, ScalarQuantizationCompression, ScalarQuantizationParameters, SearchIndex, SemanticConfiguration, SemanticPrioritizedFields, SemanticField, SemanticSearch, ScoringProfile, TagScoringFunction, TagScoringParameters ) credential = DefaultAzureCredential() index_name = "py-rag-tutorial-small-vectors-idx" index_client = SearchIndexClient(endpoint=AZURE_SEARCH_SERVICE, credential=credential) fields = [ SearchField(name="parent_id", type=SearchFieldDataType.String), SearchField(name="title", type=SearchFieldDataType.String), SearchField(name="locations", type=SearchFieldDataType.Collection(SearchFieldDataType.String), filterable=True), SearchField(name="chunk_id", type=SearchFieldDataType.String, key=True, sortable=True, filterable=True, facetable=True, analyzer_name="keyword"), SearchField(name="chunk", type=SearchFieldDataType.String, sortable=False, filterable=False, facetable=False), SearchField(name="text_vector", type="Collection(Edm.Half)", vector_search_dimensions=1024, vector_search_profile_name="myHnswProfile", stored= False) ] # Configure the vector search configuration vector_search = VectorSearch( algorithms=[ HnswAlgorithmConfiguration(name="myHnsw"), ], profiles=[ VectorSearchProfile( name="myHnswProfile", algorithm_configuration_name="myHnsw", compression_name="myScalarQuantization", vectorizer_name="myOpenAI", ) ], vectorizers=[ AzureOpenAIVectorizer( vectorizer_name="myOpenAI", kind="azureOpenAI", parameters=AzureOpenAIVectorizerParameters( resource_url=AZURE_OPENAI_ACCOUNT, deployment_name="text-embedding-3-large", model_name="text-embedding-3-large" ), ), ], compressions=[ ScalarQuantizationCompression( compression_name="myScalarQuantization", rerank_with_original_vectors=True, default_oversampling=10, parameters=ScalarQuantizationParameters(quantized_data_type="int8"), ) ] ) semantic_config = SemanticConfiguration( name="my-semantic-config", prioritized_fields=SemanticPrioritizedFields( title_field=SemanticField(field_name="title"), keywords_fields=[SemanticField(field_name="locations")], content_fields=[SemanticField(field_name="chunk")] ) ) semantic_search = SemanticSearch(configurations=[semantic_config]) scoring_profiles = [ ScoringProfile( name="my-scoring-profile", functions=[ TagScoringFunction( field_name="locations", boost=5.0, parameters=TagScoringParameters( tags_parameter="tags", ), ) ] ) ] index = SearchIndex(name=index_name, fields=fields, vector_search=vector_search, semantic_search=semantic_search, scoring_profiles=scoring_profiles) result = index_client.create_or_update_index(index) print(f"{result.name} created")
Créer ou réutiliser la source de données
Voici la définition de la source de données du tutoriel précédent. Si vous disposez déjà de cette source de données sur votre service de recherche, vous pouvez ignorer la création d’une nouvelle source.
from azure.search.documents.indexes import SearchIndexerClient
from azure.search.documents.indexes.models import (
SearchIndexerDataContainer,
SearchIndexerDataSourceConnection
)
# Create a data source
indexer_client = SearchIndexerClient(endpoint=AZURE_SEARCH_SERVICE, credential=credential)
container = SearchIndexerDataContainer(name="nasa-ebooks-pdfs-all")
data_source_connection = SearchIndexerDataSourceConnection(
name="py-rag-tutorial-ds",
type="azureblob",
connection_string=AZURE_STORAGE_CONNECTION,
container=container
)
data_source = indexer_client.create_or_update_data_source_connection(data_source_connection)
print(f"Data source '{data_source.name}' created or updated")
Créer ou réutiliser l’ensemble de compétences
L’ensemble de compétences est également inchangé par rapport au tutoriel précédent. Vous pouvez le consulter à nouveau ici.
from azure.search.documents.indexes.models import (
SplitSkill,
InputFieldMappingEntry,
OutputFieldMappingEntry,
AzureOpenAIEmbeddingSkill,
EntityRecognitionSkill,
SearchIndexerIndexProjection,
SearchIndexerIndexProjectionSelector,
SearchIndexerIndexProjectionsParameters,
IndexProjectionMode,
SearchIndexerSkillset,
CognitiveServicesAccountKey
)
# Create a skillset
skillset_name = "py-rag-tutorial-ss"
split_skill = SplitSkill(
description="Split skill to chunk documents",
text_split_mode="pages",
context="/document",
maximum_page_length=2000,
page_overlap_length=500,
inputs=[
InputFieldMappingEntry(name="text", source="/document/content"),
],
outputs=[
OutputFieldMappingEntry(name="textItems", target_name="pages")
],
)
embedding_skill = AzureOpenAIEmbeddingSkill(
description="Skill to generate embeddings via Azure OpenAI",
context="/document/pages/*",
resource_url=AZURE_OPENAI_ACCOUNT,
deployment_name="text-embedding-3-large",
model_name="text-embedding-3-large",
dimensions=1536,
inputs=[
InputFieldMappingEntry(name="text", source="/document/pages/*"),
],
outputs=[
OutputFieldMappingEntry(name="embedding", target_name="text_vector")
],
)
entity_skill = EntityRecognitionSkill(
description="Skill to recognize entities in text",
context="/document/pages/*",
categories=["Location"],
default_language_code="en",
inputs=[
InputFieldMappingEntry(name="text", source="/document/pages/*")
],
outputs=[
OutputFieldMappingEntry(name="locations", target_name="locations")
]
)
index_projections = SearchIndexerIndexProjection(
selectors=[
SearchIndexerIndexProjectionSelector(
target_index_name=index_name,
parent_key_field_name="parent_id",
source_context="/document/pages/*",
mappings=[
InputFieldMappingEntry(name="chunk", source="/document/pages/*"),
InputFieldMappingEntry(name="text_vector", source="/document/pages/*/text_vector"),
InputFieldMappingEntry(name="locations", source="/document/pages/*/locations"),
InputFieldMappingEntry(name="title", source="/document/metadata_storage_name"),
],
),
],
parameters=SearchIndexerIndexProjectionsParameters(
projection_mode=IndexProjectionMode.SKIP_INDEXING_PARENT_DOCUMENTS
),
)
cognitive_services_account = CognitiveServicesAccountKey(key=AZURE_AI_MULTISERVICE_KEY)
skills = [split_skill, embedding_skill, entity_skill]
skillset = SearchIndexerSkillset(
name=skillset_name,
description="Skillset to chunk documents and generating embeddings",
skills=skills,
index_projection=index_projections,
cognitive_services_account=cognitive_services_account
)
client = SearchIndexerClient(endpoint=AZURE_SEARCH_SERVICE, credential=credential)
client.create_or_update_skillset(skillset)
print(f"{skillset.name} created")
Créer un indexeur et charger l’index
Bien que vous puissiez réinitialiser et réexécuter l’indexeur existant en utilisant le nouvel index, il est tout aussi facile de créer un nouvel indexeur. Le fait d’avoir deux index et indexeurs conserve l’historique d’exécution et permet des comparaisons plus étroites.
Cet indexeur est identique à l’indexeur précédent, sauf qu’il spécifie le nouvel index de ce tutoriel.
from azure.search.documents.indexes.models import (
SearchIndexer
)
# Create an indexer
indexer_name = "py-rag-tutorial-small-vectors-idxr"
indexer_parameters = None
indexer = SearchIndexer(
name=indexer_name,
description="Indexer to index documents and generate embeddings",
target_index_name="py-rag-tutorial-small-vectors-idx",
skillset_name="py-rag-tutorial-ss",
data_source_name="py-rag-tutorial-ds",
parameters=indexer_parameters
)
# Create and run the indexer
indexer_client = SearchIndexerClient(endpoint=AZURE_SEARCH_SERVICE, credential=credential)
indexer_result = indexer_client.create_or_update_indexer(indexer)
print(f' {indexer_name} is created and running. Give the indexer a few minutes before running a query.')
Pour terminer, passez au portail Azure pour comparer les exigences de stockage vectoriel pour les deux index. Vous devriez obtenir des résultats similaires à la capture d’écran suivante.
L’index créé dans ce tutoriel utilise des nombres en virgule flottante de demi-précision (float16) pour les vecteurs de texte. Les besoins de stockage des vecteurs sont ainsi réduits de moitié par rapport à l’index précédent qui utilisait des nombres à virgule flottante en simple précision (float32). La compression scalaire et l’omission d’un ensemble de vecteurs comptent pour les épargnes de stockage restantes. Pour plus d’informations sur la réduction de la taille du vecteur, consultez Choisir une approche pour optimiser le stockage et le traitement vectoriels.
Envisagez de revoir les requêtes du tutoriel précédent afin de pouvoir comparer la vitesse et l’utilité des requêtes. Vous devez vous attendre à une variation de la sortie LLM chaque fois que vous répétez une requête, mais en général, les techniques d’enregistrement de stockage que vous avez implémentées ne doivent pas dégrader la qualité de vos résultats de la recherche.
Étape suivante
Il existe des exemples de code dans tous les Kits de développement logiciel (SDK) Azure qui fournissent une programmabilité Recherche Azure AI. Vous pouvez également examiner l’exemple de code vectoriel pour des cas d’usage et des combinaisons technologiques spécifiques.