Compartir vía


Tutorial: Minimización del almacenamiento y los costos (RAG en Búsqueda de Azure AI)

Búsqueda de Azure AI ofrece varios enfoques para reducir el tamaño de los índices vectoriales. Estos enfoques van desde la compresión vectorial hasta ser más selectivos sobre lo que se almacena en el servicio de búsqueda.

En este tutorial, modificará el índice de búsqueda existente para usar lo siguiente:

  • Tipos de datos estrechos
  • Cuantificación escalar
  • Almacenamiento reducido mediante la exclusión de vectores en los resultados de la búsqueda

En este tutorial se actualiza el índice de búsqueda creado por la canalización de indexación. Todas estas actualizaciones afectan al contenido existente, para lo que tiene que volver a ejecutar el indexador. Pero, en lugar de eliminar el índice de búsqueda, se crea un segundo para poder comparar las reducciones en el tamaño del índice vectorial después de agregar las nuevas funcionalidades.

En conjunto, las técnicas que se muestran en este tutorial pueden reducir el almacenamiento de vectores aproximadamente a la mitad.

En el recorte de pantalla siguiente se compara el primer índice de un tutorial anterior con el índice creado en este.

Recorte de pantalla del índice vectorial original con el índice creado mediante el esquema de este tutorial.

Requisitos previos

Este tutorial es básicamente una nueva ejecución de la canalización de indexación. Necesita todos los recursos y permisos de Azure descritos en ese tutorial.

Para la comparación, debe tener un índice py-rag-tutorial-idx existente en el servicio Búsqueda de Azure AI. Debe tener casi 2 MB de tamaño y la parte del índice vectorial debe ser de 348 KB.

También debe tener los siguientes objetos:

  • py-rag-tutorial-ds (origen de datos)

  • py-rag-tutorial-ss (conjunto de aptitudes)

Descarga del ejemplo

Descargue un Jupyter Notebook de GitHub para enviar las solicitudes a Búsqueda de Azure AI. Para obtener más información, consulte Descarga de archivos de GitHub.

Actualización del índice para reducir el almacenamiento

Búsqueda de Azure AI tiene varios enfoques para reducir el tamaño del vector, lo que disminuye el costo de las cargas de trabajo de vectores. En este paso, cree un índice que use las siguientes funcionalidades:

  • Índices vectoriales más pequeños mediante la compresión de los vectores usados durante la ejecución de la consulta. La cuantificación escalar proporciona esta funcionalidad.

  • Índices vectoriales más pequeños al no participar en el almacenamiento de vectores para los resultados de la búsqueda. Si solo necesita vectores para las consultas y no en la carga de respuesta, puede quitar la copia vectorial usada para los resultados de la búsqueda.

  • Campos vectoriales más pequeños por medio de tipos de datos estrechos. Puede especificar Collection(Edm.Half) en el campo text_vector para almacenar las dimensiones float32 entrantes como float16.

Todas estas funcionalidades se especifican en un índice de búsqueda. Después de cargar el índice, compare la diferencia entre el índice original y el nuevo.

  1. Asigne el nombre py-rag-tutorial-small-vectors-idx al nuevo índice.

  2. Use la siguiente definición para el nuevo índice. La diferencia entre este esquema y las actualizaciones de esquema anteriores en Maximizar la relevancia son nuevas clases para la cuantificación escalar y una nueva sección de compresión, un nuevo tipo de datos (Collection(Edm.Half)) para el campo text_vector y una nueva propiedad stored establecida en 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")
    

Creación o reutilización del origen de datos

Esta es la definición del origen de datos del tutorial anterior. Si ya tiene este origen de datos en el servicio de búsqueda, puede omitir la creación de uno.

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")

Creación o reutilización del conjunto de aptitudes

El conjunto de aptitudes tampoco cambia con respecto al tutorial anterior. Aquí está de nuevo para que pueda revisarlo.

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")

Creación de un indexador y carga del índice

Aunque podría restablecer y volver a ejecutar el indexador existente mediante el nuevo índice, es igual de fácil crear uno. Tener dos índices e indexadores conserva el historial de ejecución y permite comparaciones más precisas.

Este indexador es idéntico al anterior, salvo que especifica el nuevo índice de este tutorial.

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.')

Como último paso, cambie a Azure Portal para comparar los requisitos de almacenamiento vectorial de los dos índices. Debería ver resultados similares a los del recorte de pantalla siguiente.

Recorte de pantalla del índice vectorial original con el índice creado mediante el esquema de este tutorial.

El índice creado en este tutorial usa números de punto flotante de precisión media (float16) para los vectores de texto. Esto reduce los requisitos de almacenamiento de los vectores a la mitad en comparación con el índice anterior que usaba números de punto flotante de precisión sencilla (float32). La compresión escalar y la omisión de un conjunto de vectores suponen el ahorro de almacenamiento restante. Para más información sobre cómo reducir el tamaño del vector, vea Elección de un enfoque para optimizar el almacenamiento de vectores y el procesamiento.

Considere la posibilidad de volver a revisar las consultas del tutorial anterior para poder comparar la velocidad y la utilidad de las consultas. Debe esperar alguna variación en la salida de LLM cada vez que repita una consulta, pero en general las técnicas de ahorro de almacenamiento que ha implementado no deben degradar la calidad de los resultados de la búsqueda.

Paso siguiente

Hay ejemplos de código en todos los SDK de Azure que proporcionan la programación de Búsqueda de Azure AI. También puede revisar el código de ejemplo de vectores para casos de uso específicos y combinaciones de tecnología.