자습서: 스토리지 및 비용 최소화(Azure AI Search의 RAG)
Azure AI Search는 벡터 인덱스의 크기를 줄이기 위한 몇 가지 방법을 제공합니다. 이러한 접근 방식은 벡터 압축부터 검색 서비스에 저장하는 항목에 대해 보다 선택적인 방법까지 다양합니다.
이 자습서에서는 사용할 기존 검색 인덱스 수정:
- 좁은 데이터 형식
- 스칼라 양자화
- 검색 결과에서 벡터를 옵트아웃하여 스토리지 감소
이 자습서에서는 인덱싱 파이프라인에서 만든 검색 인덱스를 다시 표시합니다. 이러한 모든 업데이트는 기존 콘텐츠에 영향을 미치며 인덱서 다시 실행해야 합니다. 그러나 검색 인덱스를 삭제하는 대신 새 기능을 추가한 후 벡터 인덱스 크기 축소를 비교할 수 있도록 두 번째 인덱스를 만듭니다.
이 자습서에 설명된 기술은 모두 벡터 스토리지를 절반 정도 줄일 수 있습니다.
다음 스크린샷은 이전 자습서의 첫 번째 인덱 스를 이 자습서에서 빌드된 인덱스와 비교합니다.
필수 조건
이 자습서는 기본적으로 인덱싱 파이프라인의 다시 실행입니다. 이 자습서에 설명된 모든 Azure 리소스 및 권한이 필요합니다.
비교를 위해 Azure AI Search 서비스에 기존 py-rag-tutorial-idx 인덱스가 있어야 합니다. 크기는 거의 2MB이고 벡터 인덱스 부분은 348KB여야 합니다.
다음 개체도 있어야 합니다.
py-rag-tutorial-ds(데이터 원본)
py-rag-tutorial-ss(기술 세트)
샘플 다운로드
GitHub에서 Jupyter Notebook을 다운로드하여 Azure AI 검색으로 요청을 보냅니다. 자세한 내용은 GitHub에서 파일 다운로드를 참조하세요.
감소된 스토리지에 대한 인덱스 업데이트
Azure AI Search에는 벡터 크기를 줄이기 위한 여러 가지 방법이 있어 벡터 워크로드 비용이 절감됩니다. 이 단계에서는 다음 기능을 사용하는 새 인덱스 만들기:
쿼리 실행 중에 사용되는 벡터를 압축하여 더 작은 벡터 인덱스입니다. 스칼라 양자화는 이 기능을 제공합니다.
검색 결과를 위해 벡터 스토리지를 옵트아웃하여 더 작은 벡터 인덱스입니다. 응답 페이로드가 아닌 쿼리에 대한 벡터만 필요한 경우 검색 결과에 사용되는 벡터 복사본을 삭제할 수 있습니다.
좁은 데이터 형식을 통한 더 작은 벡터 필드입니다. text_vector 필드에 지정
Collection(Edm.Half)
하여 들어오는 float32 차원을 float16으로 저장할 수 있습니다.
이러한 모든 기능은 검색 인덱스로 지정됩니다. 인덱스가 로드되면 원래 인덱스와 새 인덱스 간의 차이를 비교합니다.
새 인덱
py-rag-tutorial-small-vectors-idx
스 이름을 지정합니다.새 인덱스로 다음 정의를 사용합니다. 이 스키마와 최대화 관련성의 이전 스키마 업데이트 간의 차이점은 스칼라 정량화 및 새 압축 섹션, text_vector 필드의 새 데이터 형식(
Collection(Edm.Half)
) 및 false로 설정된 새 속성stored
에 대한 새 클래스입니다.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 Portal로 전환하여 두 인덱스에 대한 벡터 스토리지 요구 사항을 비교합니다. 다음 스크린샷과 비슷한 결과가 나타납니다.
이 자습서에서 만든 인덱스는 텍스트 벡터에 대해 반정밀도 부동 소수점 숫자(float16)를 사용합니다. 이렇게 하면 단정밀도 부동 소수점 숫자(float32)를 사용한 이전 인덱스에 비해 벡터에 대한 스토리지 요구 사항이 절반으로 줄어듭니다. 스칼라 압축 및 한 벡터 집합의 누락은 나머지 스토리지 절감액을 차지합니다. 벡터 크기를 줄이는 방법에 대한 자세한 내용은 벡터 스토리지 및 처리를 최적화하기 위한 방법 선택을 참조하세요.
쿼리 속도와 유틸리티를 비교할 수 있도록 이전 자습서 의 쿼리를 다시 검토하는 것이 좋습니다. 쿼리를 반복할 때마다 LLM 출력이 약간 변형될 것으로 예상해야 하지만 일반적으로 구현한 스토리지 저장 기술이 검색 결과의 품질을 저하해서는 안 됩니다.
다음 단계
모든 Azure SDK에는 Azure AI Search 프로그래밍 기능을 제공하는 코드 샘플이 있습니다. 특정 사용 사례 및 기술 조합에 대한 벡터 샘플 코드를 검토할 수도 있습니다.