스파스 벡터
스파스 벡터는 정보 검색과 자연어 처리에서 데이터를 표현하는 중요한 방법입니다. 고밀도 벡터는 뛰어난 의미 이해 기능으로 인기가 있지만, 키워드나 구문을 정확하게 일치시켜야 하는 애플리케이션의 경우 희소 벡터가 더 정확한 결과를 제공하는 경우가 많습니다.
개요
스파스 벡터는 대부분의 요소가 0이고 일부 차원만 0이 아닌 값을 갖는 고차원 벡터의 특수한 표현입니다. 이러한 특성으로 인해 희소 벡터는 대규모의 고차원이지만 희박한 데이터를 처리하는 데 특히 효과적입니다. 일반적인 응용 분야는 다음과 같습니다.
텍스트 분석: 각 차원이 단어에 해당하고 문서에 나타나는 단어만 0이 아닌 값을 갖는 단어 가방 벡터로 문서를 표현합니다.
추천 시스템: 사용자-항목 상호 작용 행렬: 각 차원은 특정 항목에 대한 사용자의 평가를 나타내며, 대부분의 사용자는 몇 개의 항목과만 상호 작용합니다.
이미지 처리: 이미지의 핵심 포인트에만 초점을 맞춘 로컬 특징 표현으로, 고차원 스파스 벡터를 생성합니다.
아래 다이어그램에서 볼 수 있듯이 고밀도 벡터는 일반적으로 각 위치에 값이 있는 연속 배열로 표현됩니다(예: [0.3, 0.8, 0.2, 0.3, 0.1]
). 반면, 스파스 벡터는 0이 아닌 요소와 그 인덱스만 저장하며, 키-값 쌍으로 표현되는 경우가 많습니다(예: [{2: 0.2}, ..., {9997: 0.5}, {9999: 0.7}]
). 이 표현은 저장 공간을 크게 줄이고 계산 효율성을 높이며, 특히 매우 고차원적인 데이터(예: 10,000차원)를 다룰 때 유용합니다.
스페어 벡터 표현
희소 벡터는 텍스트 처리에서 TF-IDF (용어 빈도-역 문서 빈도) 및 BM25와 같은 다양한 방법을 사용하여 생성할 수 있습니다. 또한 Milvus는 희소 벡터를 생성하고 처리하는 데 도움이 되는 편리한 방법을 제공합니다. 자세한 내용은 임베딩을 참조하세요.
텍스트 데이터의 경우, Milvus는 전체 텍스트 검색 기능도 제공하므로 외부 임베딩 모델을 사용해 스파스 벡터를 생성하지 않고도 원시 텍스트 데이터에서 직접 벡터 검색을 수행할 수 있습니다. 자세한 내용은 전체 텍스트 검색을 참조하세요.
벡터화 후에는 데이터를 Milvus에 저장하여 관리 및 벡터 검색을 할 수 있습니다. 아래 다이어그램은 기본 프로세스를 보여줍니다.
Milvus에서 스파스 벡터 사용
밀버스는 스파스 벡터 외에도 고밀도 벡터와 바이너리 벡터도 지원합니다. 고밀도 벡터는 깊은 의미 관계를 캡처하는 데 이상적이며, 이진 벡터는 빠른 유사도 비교 및 콘텐츠 중복 제거와 같은 시나리오에 탁월합니다. 자세한 내용은 고밀도 벡터 및 이진 벡터를 참조하세요.
Milvus에서 스파스 벡터 사용
Milvus는 다음 형식의 스파스 벡터 표현을 지원합니다.
스파스 행렬(
scipy.sparse
클래스 사용)from scipy.sparse import csr_matrix # Create a sparse matrix row = [0, 0, 1, 2, 2, 2] col = [0, 2, 2, 0, 1, 2] data = [1, 2, 3, 4, 5, 6] sparse_matrix = csr_matrix((data, (row, col)), shape=(3, 3)) # Represent sparse vector using the sparse matrix sparse_vector = sparse_matrix.getrow(0)
사전 목록(
{dimension_index: value, ...}
형식)# Represent sparse vector using a dictionary sparse_vector = [{1: 0.5, 100: 0.3, 500: 0.8, 1024: 0.2, 5000: 0.6}]
SortedMap<Long, Float> sparseVector = new TreeMap<>(); sparseVector.put(1L, 0.5f); sparseVector.put(100L, 0.3f); sparseVector.put(500L, 0.8f); sparseVector.put(1024L, 0.2f); sparseVector.put(5000L, 0.6f);
튜플 이터레이터 목록(
[(dimension_index, value)]
로 포맷)# Represent sparse vector using a list of tuples sparse_vector = [[(1, 0.5), (100, 0.3), (500, 0.8), (1024, 0.2), (5000, 0.6)]]
벡터 필드 추가
Milvus에서 스파스 벡터를 사용하려면 컬렉션을 생성할 때 스파스 벡터를 저장할 필드를 정의하세요. 이 프로세스에는 다음이 포함됩니다.
datatype
을 지원되는 스파스 벡터 데이터 유형인SPARSE_FLOAT_VECTOR
으로 설정합니다.차원을 지정할 필요가 없습니다.
from pymilvus import MilvusClient, DataType
client = MilvusClient(uri="http://localhost:19530")
client.drop_collection(collection_name="my_sparse_collection")
schema = client.create_schema(
auto_id=True,
enable_dynamic_fields=True,
)
schema.add_field(field_name="pk", datatype=DataType.VARCHAR, is_primary=True, max_length=100)
schema.add_field(field_name="sparse_vector", datatype=DataType.SPARSE_FLOAT_VECTOR)
import io.milvus.v2.client.ConnectConfig;
import io.milvus.v2.client.MilvusClientV2;
import io.milvus.v2.common.DataType;
import io.milvus.v2.service.collection.request.AddFieldReq;
import io.milvus.v2.service.collection.request.CreateCollectionReq;
MilvusClientV2 client = new MilvusClientV2(ConnectConfig.builder()
.uri("http://localhost:19530")
.build());
CreateCollectionReq.CollectionSchema schema = client.createSchema();
schema.setEnableDynamicField(true);
schema.addField(AddFieldReq.builder()
.fieldName("pk")
.dataType(DataType.VarChar)
.isPrimaryKey(true)
.autoID(true)
.maxLength(100)
.build());
schema.addField(AddFieldReq.builder()
.fieldName("sparse_vector")
.dataType(DataType.SparseFloatVector)
.build());
import { DataType } from "@zilliz/milvus2-sdk-node";
const schema = [
{
name: "metadata",
data_type: DataType.JSON,
},
{
name: "pk",
data_type: DataType.Int64,
is_primary_key: true,
},
{
name: "sparse_vector",
data_type: DataType.SparseFloatVector,
}
];
export primaryField='{
"fieldName": "pk",
"dataType": "VarChar",
"isPrimary": true,
"elementTypeParams": {
"max_length": 100
}
}'
export vectorField='{
"fieldName": "sparse_vector",
"dataType": "SparseFloatVector"
}'
export schema="{
\"autoID\": true,
\"fields\": [
$primaryField,
$vectorField
]
}"
이 예에서는 스파스 벡터를 저장하기 위해 sparse_vector
이라는 이름의 벡터 필드가 추가됩니다. 이 필드의 데이터 유형은 SPARSE_FLOAT_VECTOR
입니다.
벡터 필드에 대한 인덱스 매개변수 설정
스파스 벡터에 대한 인덱스를 생성하는 과정은 밀도 벡터에 대한 인덱스 생성 과정과 유사하지만 지정된 인덱스 유형(index_type
), 거리 메트릭(metric_type
), 인덱스 파라미터(params
)에 차이가 있습니다.
index_params = client.prepare_index_params()
index_params.add_index(
field_name="sparse_vector",
index_name="sparse_inverted_index",
index_type="SPARSE_INVERTED_INDEX",
metric_type="IP",
params={"drop_ratio_build": 0.2},
)
import io.milvus.v2.common.IndexParam;
import java.util.*;
List<IndexParam> indexes = new ArrayList<>();
Map<String,Object> extraParams = new HashMap<>();
extraParams.put("drop_ratio_build", 0.2);
indexes.add(IndexParam.builder()
.fieldName("sparse_vector")
.indexName("sparse_inverted_index")
.indexType(IndexParam.IndexType.SPARSE_INVERTED_INDEX)
.metricType(IndexParam.MetricType.IP)
.extraParams(extraParams)
.build());
const indexParams = await client.createIndex({
index_name: 'sparse_inverted_index',
field_name: 'sparse_vector',
metric_type: MetricType.IP,
index_type: IndexType.SPARSE_WAND,
params: {
drop_ratio_build: 0.2,
},
});
export indexParams='[
{
"fieldName": "sparse_vector",
"metricType": "IP",
"indexName": "sparse_inverted_index",
"indexType": "SPARSE_INVERTED_INDEX",
"params":{"drop_ratio_build": 0.2}
}
]'
위의 예제에서.
스파스 벡터에 대해
SPARSE_INVERTED_INDEX
유형의 인덱스가 생성됩니다. 스파스 벡터의 경우SPARSE_INVERTED_INDEX
또는SPARSE_WAND
을 지정할 수 있습니다. 자세한 내용은 스파스 벡터 인덱스를 참조하세요.스파스 벡터의 경우
metric_type
은 두 스파스 벡터 간의 유사도를 측정하는 데 사용되는IP
(내적 곱)만 지원합니다. 유사도에 대한 자세한 내용은 메트릭 유형을 참조하세요.drop_ratio_build
는 스파스 벡터를 위한 선택적 인덱스 매개변수입니다. 인덱스 구축 중에 제외되는 작은 벡터 값의 비율을 제어합니다. 예를 들어{"drop_ratio_build": 0.2}
을 사용하면 인덱스 생성 중에 가장 작은 20%의 벡터 값이 제외되어 검색 시 계산 노력이 줄어듭니다.
컬렉션 만들기
스파스 벡터 및 인덱스 설정이 완료되면 스파스 벡터를 포함하는 컬렉션을 만들 수 있습니다. 아래 예제에서는 create_collection
메서드를 사용하여 my_sparse_collection
라는 이름의 컬렉션을 생성하는 예제입니다.
client.create_collection(
collection_name="my_sparse_collection",
schema=schema,
index_params=index_params
)
import io.milvus.v2.client.ConnectConfig;
import io.milvus.v2.client.MilvusClientV2;
MilvusClientV2 client = new MilvusClientV2(ConnectConfig.builder()
.uri("http://localhost:19530")
.build());
CreateCollectionReq requestCreate = CreateCollectionReq.builder()
.collectionName("my_sparse_collection")
.collectionSchema(schema)
.indexParams(indexes)
.build();
client.createCollection(requestCreate);
import { MilvusClient } from "@zilliz/milvus2-sdk-node";
const client = new MilvusClient({
address: 'http://localhost:19530'
});
await client.createCollection({
collection_name: 'my_sparse_collection',
schema: schema,
index_params: indexParams
});
curl --request POST \
--url "${CLUSTER_ENDPOINT}/v2/vectordb/collections/create" \
--header "Authorization: Bearer ${TOKEN}" \
--header "Content-Type: application/json" \
-d "{
\"collectionName\": \"my_sparse_collection\",
\"schema\": $schema,
\"indexParams\": $indexParams
}"
데이터 삽입
컬렉션을 만든 후 스파스 벡터가 포함된 데이터를 삽입합니다.
sparse_vectors = [
{"sparse_vector": {1: 0.5, 100: 0.3, 500: 0.8}},
{"sparse_vector": {10: 0.1, 200: 0.7, 1000: 0.9}},
]
client.insert(
collection_name="my_sparse_collection",
data=sparse_vectors
)
import com.google.gson.Gson;
import com.google.gson.JsonObject;
import io.milvus.v2.service.vector.request.InsertReq;
import io.milvus.v2.service.vector.response.InsertResp;
List<JsonObject> rows = new ArrayList<>();
Gson gson = new Gson();
{
JsonObject row = new JsonObject();
SortedMap<Long, Float> sparse = new TreeMap<>();
sparse.put(1L, 0.5f);
sparse.put(100L, 0.3f);
sparse.put(500L, 0.8f);
row.add("sparse_vector", gson.toJsonTree(sparse));
rows.add(row);
}
{
JsonObject row = new JsonObject();
SortedMap<Long, Float> sparse = new TreeMap<>();
sparse.put(10L, 0.1f);
sparse.put(200L, 0.7f);
sparse.put(1000L, 0.9f);
row.add("sparse_vector", gson.toJsonTree(sparse));
rows.add(row);
}
InsertResp insertR = client.insert(InsertReq.builder()
.collectionName("my_sparse_collection")
.data(rows)
.build());
const data = [
{ sparse_vector: { "1": 0.5, "100": 0.3, "500": 0.8 } },
{ sparse_vector: { "10": 0.1, "200": 0.7, "1000": 0.9 } },
];
client.insert({
collection_name: "my_sparse_collection",
data: data,
});
curl --request POST \
--url "${CLUSTER_ENDPOINT}/v2/vectordb/entities/insert" \
--header "Authorization: Bearer ${TOKEN}" \
--header "Content-Type: application/json" \
-d '{
"data": [
{"sparse_vector": {"1": 0.5, "100": 0.3, "500": 0.8}},
{"sparse_vector": {"10": 0.1, "200": 0.7, "1000": 0.9}}
],
"collectionName": "my_sparse_collection"
}'
## {"code":0,"cost":0,"data":{"insertCount":2,"insertIds":["453577185629572534","453577185629572535"]}}
유사도 검색 수행
스파스 벡터를 사용하여 유사도 검색을 수행하려면 쿼리 벡터와 검색 매개변수를 준비합니다.
# Prepare search parameters
search_params = {
"params": {"drop_ratio_search": 0.2}, # Additional optional search parameters
}
# Prepare the query vector
query_vector = [{1: 0.2, 50: 0.4, 1000: 0.7}]
이 예에서 drop_ratio_search
는 희소 벡터를 위한 선택적 파라미터로, 검색 중에 쿼리 벡터의 작은 값을 미세 조정할 수 있습니다. 예를 들어 {"drop_ratio_search": 0.2}
을 사용하면 검색 중에 쿼리 벡터에서 가장 작은 20%의 값은 무시됩니다.
그런 다음 search
메서드를 사용하여 유사도 검색을 실행합니다.
res = client.search(
collection_name="my_sparse_collection",
data=query_vector,
limit=3,
output_fields=["pk"],
search_params=search_params,
)
print(res)
# Output
# data: ["[{'id': '453718927992172266', 'distance': 0.6299999952316284, 'entity': {'pk': '453718927992172266'}}, {'id': '453718927992172265', 'distance': 0.10000000149011612, 'entity': {'pk': '453718927992172265'}}]"]
import io.milvus.v2.service.vector.request.SearchReq;
import io.milvus.v2.service.vector.request.data.SparseFloatVec;
import io.milvus.v2.service.vector.response.SearchResp;
Map<String,Object> searchParams = new HashMap<>();
searchParams.put("drop_ratio_search", 0.2);
SortedMap<Long, Float> sparse = new TreeMap<>();
sparse.put(10L, 0.1f);
sparse.put(200L, 0.7f);
sparse.put(1000L, 0.9f);
SparseFloatVec queryVector = new SparseFloatVec(sparse);
SearchResp searchR = client.search(SearchReq.builder()
.collectionName("my_sparse_collection")
.data(Collections.singletonList(queryVector))
.annsField("sparse_vector")
.searchParams(searchParams)
.topK(3)
.outputFields(Collections.singletonList("pk"))
.build());
System.out.println(searchR.getSearchResults());
// Output
//
// [[SearchResp.SearchResult(entity={pk=453444327741536759}, score=1.31, id=453444327741536759), SearchResp.SearchResult(entity={pk=453444327741536756}, score=1.31, id=453444327741536756), SearchResp.SearchResult(entity={pk=453444327741536753}, score=1.31, id=453444327741536753)]]
client.search({
collection_name: 'my_sparse_collection',
data: {1: 0.2, 50: 0.4, 1000: 0.7},
limit: 3,
output_fields: ['pk'],
params: {
drop_ratio_search: 0.2
}
});
curl --request POST \
--url "${CLUSTER_ENDPOINT}/v2/vectordb/entities/search" \
--header "Authorization: Bearer ${TOKEN}" \
--header "Content-Type: application/json" \
-d '{
"collectionName": "my_sparse_collection",
"data": [
{"1": 0.2, "50": 0.4, "1000": 0.7}
],
"annsField": "sparse_vector",
"limit": 3,
"searchParams":{
"params":{"drop_ratio_search": 0.2}
},
"outputFields": ["pk"]
}'
## {"code":0,"cost":0,"data":[{"distance":0.63,"id":"453577185629572535","pk":"453577185629572535"},{"distance":0.1,"id":"453577185629572534","pk":"453577185629572534"}]}
유사도 검색 매개변수에 대한 자세한 내용은 기본 ANN 검색을 참조하세요.
제한 사항
Milvus에서 스파스 벡터를 사용할 때는 다음과 같은 제한 사항을 고려하세요:
현재 스파스 벡터에는 IP 및 BM25 (전체 텍스트 검색용) 거리 메트릭만 지원됩니다. 희소 벡터의 차원이 높기 때문에 L2 및 코사인 거리는 비실용적입니다.
스파스 벡터 필드의 경우, SPARSE_INVERTED_INDEX 및 SPARSE_WAND 인덱스 유형만 지원됩니다.
스파스 벡터에 지원되는 데이터 유형:
- 차원 부분은 부호가 없는 32비트 정수여야 합니다;
- 값 부분은 음수가 아닌 32비트 부동 소수점 숫자일 수 있습니다.
스파스 벡터는 삽입 및 검색을 위해 다음 요구 사항을 충족해야 합니다:
- 벡터의 값이 하나 이상 0이 아닐 것;
- 벡터 인덱스는 음수가 아닙니다.
FAQ
SPARSE_INVERTED_INDEX와 SPARSE_WAND의 차이점을 설명해 주시고, 둘 중 하나를 선택하려면 어떻게 해야 하나요?
SPARSE_INVERTED_INDEX는 기존의 반전 인덱스인 반면, SPARSE_WAND는 검색 시 전체 IP 거리 평가 횟수를 줄이기 위해 Weak-AND 알고리즘을 사용합니다. SPARSE_WAND는 일반적으로 더 빠르지만 벡터 밀도가 증가하면 성능이 저하될 수 있습니다. 이 중 하나를 선택하려면 특정 데이터 세트와 사용 사례에 따라 실험과 벤치마크를 수행하세요.
drop_ratio_build 및 drop_ratio_search 매개변수는 어떻게 선택해야 하나요?
drop_ratio_build 및 drop_ratio_search의 선택은 데이터의 특성과 검색 지연 시간/처리량 및 정확도에 대한 요구 사항에 따라 달라집니다.
스파스 임베딩의 차원은 uint32 공간 내에서 임의의 불연속형 값이 될 수 있나요?
예, 한 가지 예외가 있습니다. 희소 임베딩의 차원은
[0, maximum of uint32)
범위의 모든 값을 사용할 수 있습니다. 즉, 최대값인 uint32를 사용할 수 없습니다.증가하는 세그먼트에 대한 검색은 인덱스를 통해 수행되나요 아니면 무차별 대입으로 수행되나요?
증가하는 세그먼트에 대한 검색은 봉인된 세그먼트 인덱스와 동일한 유형의 인덱스를 통해 수행됩니다. 인덱스가 구축되기 전에 새로 증가하는 세그먼트의 경우 무차별 대입 검색이 사용됩니다.
하나의 컬렉션에 희소 벡터와 고밀도 벡터를 모두 포함할 수 있나요?
예, 여러 벡터 유형을 지원하므로 희소 벡터 열과 고밀도 벡터 열이 모두 포함된 컬렉션을 생성하고 하이브리드 검색을 수행할 수 있습니다.