Udostępnij za pośrednictwem


Samouczek: minimalizowanie kosztów magazynowania i kosztów (RAG w usłudze Azure AI Search)

Usługa Azure AI Search oferuje kilka metod zmniejszania rozmiaru indeksów wektorowych. Te podejścia obejmują zarówno kompresję wektorów, jak i bardziej selektywną elementy przechowywane w usłudze wyszukiwania.

W tym samouczku zmodyfikujesz istniejący indeks wyszukiwania do użycia:

  • Wąskie typy danych
  • Kwantyzacja skalarna
  • Ograniczony magazyn dzięki rezygnacji z wektorów w wynikach wyszukiwania

W tym samouczku przedstawiono ponownie indeks wyszukiwania utworzony przez potok indeksowania. Wszystkie te aktualizacje mają wpływ na istniejącą zawartość, co wymaga ponownego uruchomienia indeksatora. Jednak zamiast usuwać indeks wyszukiwania, należy utworzyć drugi, aby można było porównać redukcje rozmiaru indeksu wektorowego po dodaniu nowych możliwości.

W sumie techniki przedstawione w tym samouczku mogą zmniejszyć ilość miejsca do magazynowania wektorów o około połowę.

Poniższy zrzut ekranu porównuje pierwszy indeks z poprzedniego samouczka do indeksu utworzonego w tym samouczku.

Zrzut ekranu przedstawiający oryginalny indeks wektorowy z indeksem utworzonym przy użyciu schematu w tym samouczku.

Wymagania wstępne

Ten samouczek jest zasadniczo ponownym uruchomieniem potoku indeksowania. Potrzebne są wszystkie zasoby i uprawnienia platformy Azure opisane w tym samouczku.

Dla porównania należy mieć istniejący indeks py-rag-tutorial-idx w usłudze Azure AI usługa wyszukiwania. Rozmiar powinien wynosić prawie 2 MB, a część indeksu wektorowego powinna wynosić 348 KB.

Powinny również istnieć następujące obiekty:

  • py-rag-tutorial-ds (źródło danych)

  • py-rag-tutorial-ss (zestaw umiejętności)

Pobierz przykład

Pobierz notes Jupyter z usługi GitHub, aby wysłać żądania do usługi Azure AI Search. Aby uzyskać więcej informacji, zobacz Pobieranie plików z usługi GitHub.

Aktualizowanie indeksu dla ograniczonego magazynu

Usługa Azure AI Search oferuje wiele metod zmniejszania rozmiaru wektora, co obniża koszt obciążeń wektorowych. W tym kroku utwórz nowy indeks, który korzysta z następujących funkcji:

  • Mniejsze indeksy wektorów przez kompresowanie wektorów używanych podczas wykonywania zapytania. Kwantyzacja skalarna zapewnia tę możliwość.

  • Mniejsze indeksy wektorów, rezygnując z magazynu wektorowego na potrzeby wyników wyszukiwania. Jeśli potrzebujesz tylko wektorów dla zapytań, a nie w ładunku odpowiedzi, możesz usunąć kopię wektorów używaną do wyszukiwania wyników wyszukiwania.

  • Mniejsze pola wektorów poprzez wąskie typy danych. Możesz określić Collection(Edm.Half) w polu text_vector, aby przechowywać przychodzące wymiary float32 jako float16.

Wszystkie te możliwości są określone w indeksie wyszukiwania. Po załadowaniu indeksu porównaj różnicę między oryginalnym indeksem a nowym.

  1. Nadaj nowej nazwie nowy indeks py-rag-tutorial-small-vectors-idx.

  2. Użyj następującej definicji dla nowego indeksu. Różnica między tym schematem a poprzednimi aktualizacjami schematu w artykule Maksymalizowanie istotności to nowe klasy kwantyzacji skalarnej i nowa sekcja kompresji, nowy typ danych (Collection(Edm.Half)) dla pola text_vector i nowa właściwość stored ustawiona na wartość 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")
    

Tworzenie lub ponowne używanie źródła danych

Poniżej przedstawiono definicję źródła danych z poprzedniego samouczka. Jeśli masz już to źródło danych w usłudze wyszukiwania, możesz pominąć tworzenie nowego źródła danych.

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

Tworzenie lub ponowne używanie zestawu umiejętności

Zestaw umiejętności nie zmienia się również z poprzedniego samouczka. Tutaj jest ponownie, aby można było go przejrzeć.

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

Tworzenie nowego indeksatora i ładowanie indeksu

Chociaż można zresetować i ponownie uruchomić istniejący indeksator przy użyciu nowego indeksu, równie łatwo jest utworzyć nowy indeksator. Posiadanie dwóch indeksów i indeksatorów zachowuje historię wykonywania i umożliwia bliższe porównania.

Ten indeksator jest identyczny z poprzednim indeksatorem, z tą różnicą, że określa nowy indeks z tego samouczka.

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

W ostatnim kroku przejdź do witryny Azure Portal, aby porównać wymagania dotyczące magazynu wektorowego dla dwóch indeksów. Wyniki powinny wyglądać podobnie jak na poniższym zrzucie ekranu.

Zrzut ekranu przedstawiający oryginalny indeks wektorowy z indeksem utworzonym przy użyciu schematu w tym samouczku.

Indeks utworzony w tym samouczku używa liczb zmiennoprzecinkowych o połowie precyzji (float16) dla wektorów tekstowych. Zmniejsza to wymagania dotyczące magazynu dla wektorów o połowę w porównaniu z poprzednim indeksem, który używał liczb zmiennoprzecinkowych o jedną precyzję (float32). Kompresja skalarna i pominięcie jednego zestawu wektorów stanowią pozostałe oszczędności magazynu. Aby uzyskać więcej informacji na temat zmniejszania rozmiaru wektora, zobacz Wybieranie podejścia do optymalizacji magazynu i przetwarzania wektorów.

Rozważ ponowne przejrzenie zapytań z poprzedniego samouczka , aby można było porównać szybkość zapytań i narzędzie. Podczas powtarzania zapytania należy oczekiwać pewnych zmian w danych wyjściowych usługi LLM, ale ogólnie zaimplementowane techniki zapisywania magazynu nie powinny obniżać jakości wyników wyszukiwania.

Następny krok

Istnieją przykłady kodu we wszystkich zestawach SDK platformy Azure, które zapewniają możliwość programowania usługi Azure AI Search. Możesz również przejrzeć przykładowy kod wektora pod kątem konkretnych przypadków użycia i kombinacji technologii.