Руководство. Минимизация затрат на хранение и затраты (RAG в поиске ИИ Azure)
Поиск по искусственному интеллекту Azure предлагает несколько подходов для уменьшения размера векторных индексов. Эти подходы варьируются от сжатия векторов до более выборочного по сравнению с тем, что вы храните в службе поиска.
В этом руководстве вы измените существующий индекс поиска для использования:
- Узкие типы данных
- Скалярная квантизация
- Сокращение хранилища путем отказа от векторов в результатах поиска
В этом руководстве выполняется повторение индекса поиска, созданного конвейером индексирования. Все эти обновления влияют на существующее содержимое, требующее повторного запуска индексатора. Однако вместо удаления индекса поиска вы создадите вторую, чтобы сравнить сокращение размера векторного индекса после добавления новых возможностей.
В целом методы, иллюстрированные в этом руководстве, могут сократить векторное хранилище примерно на половину.
На следующем снимке экрана сравнивается первый индекс из предыдущего руководства с индексом, встроенным в этот.
Необходимые компоненты
Это руководство, по сути, повторное выполнение конвейера индексирования. Вам потребуются все ресурсы и разрешения Azure, описанные в этом руководстве.
Для сравнения у вас должен быть существующий индекс py-rag-tutorial-idx в служба ИИ Azure. Размер должен составлять почти 2 МБ, а часть векторного индекса должна составлять 348 КБ.
У вас также должны быть следующие объекты:
py-rag-tutorial-ds (источник данных)
py-rag-tutorial-ss (набор навыков)
Скачивание примера приложения
Скачайте записную книжку Jupyter из GitHub, чтобы отправить запросы в поиск ИИ Azure. Дополнительные сведения см. в статье "Скачивание файлов с GitHub".
Обновление индекса для сокращенного хранилища
Поиск ИИ Azure имеет несколько подходов к сокращению размера векторов, что снижает стоимость векторных рабочих нагрузок. На этом шаге создайте новый индекс, использующий следующие возможности:
Меньшие индексы векторов путем сжатия векторов, используемых во время выполнения запроса. Скалярная квантизация предоставляет эту возможность.
Меньшие индексы векторов, отказ от векторного хранилища результатов поиска. Если вам нужны только векторы для запросов, а не полезных данных ответа, можно удалить векторную копию, используемую для результатов поиска.
Небольшие векторные поля через узкие типы данных. Можно указать
Collection(Edm.Half)
в поле text_vector для хранения входящих измерений float32 как float16.
Все эти возможности указываются в индексе поиска. После загрузки индекса сравните разницу между исходным индексом и новым.
Присвойте новому индексу
py-rag-tutorial-small-vectors-idx
имя.Используйте следующее определение для нового индекса. Разница между этой схемой и предыдущими обновлениями схемы в "Максимальной релевантности " — это новые классы для скалярной квантизации и нового раздела сжатия, новый тип данных (
Collection(Edm.Half)
) для поля text_vector, а новое свойствоstored
имеет значение 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")
Создание или повторное использование источника данных
Ниже приведено определение источника данных из предыдущего руководства. Если у вас уже есть этот источник данных в службе поиска, можно пропустить создание нового.
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")
Создание или повторное использование набора навыков
Набор навыков также не изменяется из предыдущего руководства. Вот еще раз, чтобы вы могли просмотреть его.
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")
Создание индексатора и загрузка индекса
Хотя вы можете сбросить и повторно запустить существующий индексатор с помощью нового индекса, это так же легко создать новый индексатор. Наличие двух индексов и индексаторов сохраняет журнал выполнения и позволяет более тесно сравнивать.
Этот индексатор идентичен предыдущему индексатору, за исключением того, что он задает новый индекс из этого руководства.
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.')
В качестве последнего шага перейдите к портал Azure, чтобы сравнить требования к хранилищу векторов для двух индексов. Вы должны получить результаты, аналогичные следующему снимку экрана.
В индексе, созданном в этом руководстве, используются числа с плавающей запятой (float16) для текстовых векторов. Это сокращает требования к хранилищу для векторов наполовину по сравнению с предыдущим индексом, который использовал числа с плавающей запятой с одной точностью (float32). Скалярное сжатие и упущение одного набора векторов за оставшуюся экономию хранилища. Дополнительные сведения о снижении размера векторов см. в статье "Выбор подхода к оптимизации хранилища векторов и обработки".
Попробуйте пересмотреть запросы из предыдущего руководства , чтобы сравнить скорость запросов и служебную программу. Вы должны ожидать некоторые изменения в выходных данных LLM всякий раз, когда вы повторяете запрос, но в целом методы сохранения хранилища, которые вы реализовали, не должны ухудшать качество результатов поиска.
Следующий шаг
Существуют примеры кода во всех пакетах SDK Azure, которые предоставляют программируемость поиска ИИ Azure. Вы также можете просмотреть пример кода вектора для конкретных вариантов использования и сочетаний технологий.