벡터 (Vectors)

벡터 (Vectors)

벡터(또는 임베딩, embedding)는 Qdrant 벡터 검색 엔진의 핵심 개념이에요. 벡터는 벡터 공간에서 객체들 사이의 유사도를 정의하죠.

벡터 공간에서 두 벡터가 비슷하다는 건, 그 벡터가 나타내는 객체들이 어떤 면에서 비슷하다는 뜻이에요.

예를 들어 이미지 컬렉션이 있다고 해볼게요. 각 이미지를 하나의 벡터로 표현할 수 있는데, 두 이미지가 비슷하다면 그들의 벡터는 벡터 공간에서 서로 가까운 곳에 있게 돼요.

객체의 벡터 표현을 얻으려면 객체에 벡터화(vectorization) 알고리즘을 적용해야 해요. 보통 이 알고리즘은 객체를 고정 크기의 벡터로 변환하는 신경망이에요.

이 신경망은 보통 비슷한 객체끼리, 그리고 서로 다른 객체끼리의 쌍(pairs)이나 삼중항(triplets)으로 학습돼요. 그래서 특정한 종류의 유사도를 인식하는 법을 배우게 되죠.

이 벡터의 성질을 활용하면 데이터를 여러 방식으로 탐색할 수 있어요. 예를 들면 비슷한 객체를 검색하거나, 객체를 클러스터링하는 식으로요.

벡터 유형 (Vector Types)

요즘 신경망은 다양한 형태와 크기의 벡터를 출력할 수 있는데, Qdrant는 그 대부분을 지원해요. Qdrant가 지원하는 가장 흔한 벡터 유형들을 하나씩 살펴볼게요.

밀집 벡터 (Dense Vectors)

가장 흔한 유형의 벡터예요. 단순한 숫자 목록인데, 길이가 고정되어 있고 각 원소는 부동소수점 숫자(floating-point number)예요.

생긴 모습은 이렇습니다.

 // A piece of a real-world dense vector [ -0.013052909 , 0.020387933 , -0.007869 , -0.11111383 , -0.030188112 , -0.0053388323 , 0.0010654867 , 0.072027855 , -0.04167721 , 0.014839341 , -0.032948174 , -0.062975034 , -0.024837125 , .... ]

대부분의 신경망은 밀집 벡터를 만들어내기 때문에, 추가 처리 없이 바로 Qdrant에서 사용할 수 있어요. 대부분의 임베딩 모델과 호환되지만, Qdrant는 다음의 검증된 임베딩 프로바이더로 테스트되었어요.

희소 벡터 (Sparse Vectors)

희소 벡터는 특별한 유형의 벡터예요. 수학적으로는 밀집 벡터와 같지만, 0이 아주 많다는 특징이 있어서 특별한 형식으로 저장돼요.

Qdrant의 희소 벡터는 고정된 길이를 갖지 않아요. 벡터를 삽입할 때 동적으로 할당되거든요. 희소 벡터의 0이 아닌 값의 개수는 현재 u32 데이터 타입 범위(4294967295)로 제한돼요.

희소 벡터를 정의하려면 0이 아닌 원소들의 목록과 그 인덱스(indexes)를 제공해야 해요.

 // A sparse vector with 4 non-zero elements { "indexes" : [ 1 , 3 , 5 , 7 ], "values" : [ 0.1 , 0.2 , 0.3 , 0.4 ] }

Qdrant의 희소 벡터는 특별한 저장소에 보관되고 별도의 인덱스에 색인되기 때문에, 설정 방식이 밀집 벡터와 달라요.

희소 벡터를 가진 컬렉션을 만들려면 이렇게 하면 돼요.

 PUT /collections/{collection_name} { "sparse_vectors": { "text": { } } }
 curl -X PUT http://localhost:6333/collections/ { collection_name } \ -H 'Content-Type: application/json' \ --data-raw '{ "sparse_vectors": { "text": { } } }'
 from qdrant_client import QdrantClient , models client = QdrantClient ( url = "http://localhost:6333" ) client . create_collection ( collection_name = " {collection_name} " , vectors_config = {}, sparse_vectors_config = { "text" : models . SparseVectorParams (), }, )
 import { QdrantClient } from "@qdrant/js-client-rest" ; const client = new QdrantClient ({ host : "localhost" , port : 6333 }); client . createCollection ( "{collection_name}" , { sparse_vectors : { text : { }, }, });
 use qdrant_client :: Qdrant ; use qdrant_client :: qdrant :: { CreateCollectionBuilder , SparseVectorParamsBuilder , SparseVectorsConfigBuilder , }; let client = Qdrant :: from_url ( "http://localhost:6334" ). build () ? ; let mut sparse_vector_config = SparseVectorsConfigBuilder :: default (); sparse_vector_config . add_named_vector_params ( "text" , SparseVectorParamsBuilder :: default ()); client . create_collection ( CreateCollectionBuilder :: new ( "{collection_name}" ) . sparse_vectors_config ( sparse_vector_config ), ) . await ? ;
 import io.qdrant.client.QdrantClient ; import io.qdrant.client.QdrantGrpcClient ; import io.qdrant.client.grpc.Collections.CreateCollection ; import io.qdrant.client.grpc.Collections.SparseVectorConfig ; import io.qdrant.client.grpc.Collections.SparseVectorParams ; QdrantClient client = new QdrantClient ( QdrantGrpcClient . newBuilder ( "localhost" , 6334 , false ). build ()); client . createCollectionAsync ( CreateCollection . newBuilder () . setCollectionName ( "{collection_name}" ) . setSparseVectorsConfig ( SparseVectorConfig . newBuilder () . putMap ( "text" , SparseVectorParams . getDefaultInstance ())) . build ()) . get ();
 using Qdrant.Client ; using Qdrant.Client.Grpc ; var client = new QdrantClient ( "localhost" , 6334 ); await client . CreateCollectionAsync ( collectionName : "{collection_name}" , sparseVectorsConfig : ( "text" , new SparseVectorParams ()) );
 import ( "context" "github.com/qdrant/go-client/qdrant" ) client , err := qdrant . NewClient ( & qdrant . Config { Host : "localhost" , Port : 6334 , }) client . CreateCollection ( context . Background (), & qdrant . CreateCollection { CollectionName : "{collection_name}" , SparseVectorsConfig : qdrant . NewSparseVectorsConfig ( map [ string ] * qdrant . SparseVectorParams { "text" : {}, }), })

이제 생성한 컬렉션에 희소 벡터를 가진 포인트를 삽입해볼게요.

 PUT /collections/{collection_name}/points { "points": [ { "id": 1, "vector": { "text": { "indices": [1, 3, 5, 7], "values": [0.1, 0.2, 0.3, 0.4] } } } ] }
 from qdrant_client import QdrantClient , models client = QdrantClient ( url = "http://localhost:6333" ) client . upsert ( collection_name = " {collection_name} " , points = [ models . PointStruct ( id = 1 , payload = {}, # Add any additional payload if necessary vector = { "text" : models . SparseVector ( indices = [ 1 , 3 , 5 , 7 ], values = [ 0.1 , 0.2 , 0.3 , 0.4 ] ) }, ) ], )
 import { QdrantClient } from "@qdrant/js-client-rest" ; const client = new QdrantClient ({ host : "localhost" , port : 6333 }); client . upsert ( "{collection_name}" , { points : [ { id : 1 , vector : { text : { indices : [ 1 , 3 , 5 , 7 ], values : [ 0.1 , 0.2 , 0.3 , 0.4 ] }, }, } ] });
 use qdrant_client :: qdrant :: { NamedVectors , PointStruct , UpsertPointsBuilder , Vector }; use qdrant_client :: { Payload , Qdrant }; let client = Qdrant :: from_url ( "http://localhost:6334" ). build () ? ; let points = vec! [ PointStruct :: new ( 1 , NamedVectors :: default (). add_vector ( "text" , Vector :: new_sparse ( vec! [ 1 , 3 , 5 , 7 ], vec! [ 0.1 , 0.2 , 0.3 , 0.4 ]), ), Payload :: new (), )]; client . upsert_points ( UpsertPointsBuilder :: new ( "{collection_name}" , points )) . await ? ;
 import static io.qdrant.client.PointIdFactory.id ; import static io.qdrant.client.VectorFactory.vector ; import static io.qdrant.client.VectorsFactory.namedVectors ; import io.qdrant.client.QdrantClient ; import io.qdrant.client.QdrantGrpcClient ; import io.qdrant.client.grpc.Points.PointStruct ; import java.util.List ; import java.util.Map ; QdrantClient client = new QdrantClient ( QdrantGrpcClient . newBuilder ( "localhost" , 6334 , false ). build ()); client . upsertAsync ( "{collection_name}" , List . of ( PointStruct . newBuilder () . setId ( id ( 1 )) . setVectors ( namedVectors ( Map . of ( "text" , vector ( List . of ( 1 . 0f , 2 . 0f ), List . of ( 6 , 7 )))) ) . build ())) . get ();
 using Qdrant.Client ; using Qdrant.Client.Grpc ; var client = new QdrantClient ( "localhost" , 6334 ); await client . UpsertAsync ( collectionName : "{collection_name}" , points : new List < PointStruct > { new () { Id = 1 , Vectors = new Dictionary < string , Vector > { ["text"] = ([ 0.1f , 0.2f , 0.3f , 0.4f ], [ 1 , 3 , 5 , 7 ]) } } } );
 import ( "context" "github.com/qdrant/go-client/qdrant" ) client , err := qdrant . NewClient ( & qdrant . Config { Host : "localhost" , Port : 6334 , }) client . Upsert ( context . Background (), & qdrant . UpsertPoints { CollectionName : "{collection_name}" , Points : [] * qdrant . PointStruct { { Id : qdrant . NewIDNum ( 1 ), Vectors : qdrant . NewVectorsMap ( map [ string ] * qdrant . Vector { "text" : qdrant . NewVectorSparse ( [] uint32 { 1 , 3 , 5 , 7 }, [] float32 { 0.1 , 0.2 , 0.3 , 0.4 }), }), }, }, })

이제 희소 벡터로 검색을 실행할 수 있어요.

 POST /collections/{collection_name}/points/query { "query": { "indices": [1, 3, 5, 7], "values": [0.1, 0.2, 0.3, 0.4] }, "using": "text" }
 from qdrant_client import QdrantClient , models client = QdrantClient ( url = "http://localhost:6333" ) result = client . query_points ( collection_name = " {collection_name} " , query = models . SparseVector ( indices = [ 1 , 3 , 5 , 7 ], values = [ 0.1 , 0.2 , 0.3 , 0.4 ]), using = "text" , ) . points
 import { QdrantClient } from "@qdrant/js-client-rest" ; const client = new QdrantClient ({ host : "localhost" , port : 6333 }); client . query ( "{collection_name}" , { query : { indices : [ 1 , 3 , 5 , 7 ], values : [ 0.1 , 0.2 , 0.3 , 0.4 ] }, using : "text" , limit : 3 , });
 use qdrant_client :: qdrant :: QueryPointsBuilder ; use qdrant_client :: Qdrant ; let client = Qdrant :: from_url ( "http://localhost:6334" ). build () ? ; client . query ( QueryPointsBuilder :: new ( "{collection_name}" ) . query ( vec! [( 1 , 0.2 ), ( 3 , 0.1 ), ( 5 , 0.9 ), ( 7 , 0.7 )]) . limit ( 10 ) . using ( "text" ), ) . await ? ;
 import static io.qdrant.client.QueryFactory.nearest ; import io.qdrant.client.QdrantClient ; import io.qdrant.client.QdrantGrpcClient ; import io.qdrant.client.grpc.Points.QueryPoints ; import java.util.List ; QdrantClient client = new QdrantClient ( QdrantGrpcClient . newBuilder ( "localhost" , 6334 , false ). build ()); client . queryAsync ( QueryPoints . newBuilder () . setCollectionName ( "{collection_name}" ) . setUsing ( "text" ) . setQuery ( nearest ( List . of ( 0 . 1f , 0 . 2f , 0 . 3f , 0 . 4f ), List . of ( 1 , 3 , 5 , 7 ))) . setLimit ( 3 ) . build ()) . get ();
 using Qdrant.Client ; var client = new QdrantClient ( "localhost" , 6334 ); await client . QueryAsync ( collectionName : "{collection_name}" , query : new ( float , uint )[] {( 0.1f , 1 ), ( 0.2f , 3 ), ( 0.3f , 5 ), ( 0.4f , 7 )}, usingVector : "text" , limit : 3 );
 import ( "context" "github.com/qdrant/go-client/qdrant" ) client , err := qdrant . NewClient ( & qdrant . Config { Host : "localhost" , Port : 6334 , }) client . Query ( context . Background (), & qdrant . QueryPoints { CollectionName : "{collection_name}" , Query : qdrant . NewQuerySparse ( [] uint32 { 1 , 3 , 5 , 7 }, [] float32 { 0.1 , 0.2 , 0.3 , 0.4 }), Using : qdrant . PtrOf ( "text" ), })

멀티벡터 (Multivectors)

v1.10.0부터 사용 가능

Qdrant는 하나의 포인트에 같은 형태의 밀집 벡터 여러 개를 가변 개수로 저장하는 것을 지원해요. 즉, 단일 밀집 벡터 대신 밀집 벡터의 행렬(matrix)을 올릴 수 있다는 뜻이에요.

행렬의 각 벡터 길이는 고정되어 있지만, 행렬 안의 벡터 개수는 포인트마다 다를 수 있어요.

멀티벡터의 모습은 이래요.

 // A multivector of size 4 "vector" : [ [ -0.013 , 0.020 , -0.007 , -0.111 ], [ -0.030 , -0.055 , 0.001 , 0.072 ], [ -0.041 , 0.014 , -0.032 , -0.062 ], .... ]

멀티벡터가 유용한 두 가지 시나리오가 있어요.

  • 같은 객체의 여러 표현 – 예를 들어 같은 객체의 사진을 서로 다른 각도에서 찍은 임베딩을 여러 개 저장할 수 있어요. 이 방식은 모든 벡터에 대해 페이로드(payload)가 동일하다고 가정해요.
  • 지연 상호작용 임베딩 (Late interaction embeddings) – 일부 텍스트 임베딩 모델은 한 텍스트에 대해 여러 벡터를 출력할 수 있어요. 예를 들어 ColBERT 같은 모델 계열은 텍스트의 각 토큰마다 상대적으로 작은 벡터를 출력하죠.

MaxSim은 서브벡터(subvector)마다가 아니라 포인트당 단일 결합 점수(combined score)를 반환해요. title, summary, chunk 임베딩별로 개별 제어가 필요하다면 명명 벡터(Named Vectors)와 멀티 표현 검색 튜토리얼을 참고하세요. 멀티벡터 강의에서는 확장 시의 제약 사항을 다룹니다.

멀티벡터를 사용하려면 벡터 행렬들을 비교하는 데 쓸 함수를 지정해야 해요.

현재 Qdrant는 max_sim 함수를 지원하는데, 이 함수는 행렬 안 각 벡터 쌍 사이의 최대 유사도들을 합한 값으로 정의돼요.

$$ score = \sum_{i=1}^{N} \max_{j=1}^{M} \text{Sim}(\text{vectorA}_i, \text{vectorB}_j) $$

여기서 $N$은 첫 번째 행렬의 벡터 수, $M$은 두 번째 행렬의 벡터 수, $\text{Sim}$은 예를 들어 코사인 유사도 같은 유사도 함수예요.

멀티벡터를 사용하려면 멀티벡터 비교자(comparator)를 가진 밀집 벡터를 생성하면 돼요.

 PUT /collections/{collection_name} { "vectors": { "size": 128, "distance": "Cosine", "multivector_config": { "comparator": "max_sim" } } }
 from qdrant_client import QdrantClient , models client = QdrantClient ( url = "http://localhost:6333" ) client . create_collection ( collection_name = " {collection_name} " , vectors_config = models . VectorParams ( size = 128 , distance = models . Distance . COSINE , multivector_config = models . MultiVectorConfig ( comparator = models . MultiVectorComparator . MAX_SIM ), ), )
 import { QdrantClient } from "@qdrant/js-client-rest" ; const client = new QdrantClient ({ host : "localhost" , port : 6333 }); client . createCollection ( "{collection_name}" , { vectors : { size : 128 , distance : "Cosine" , multivector_config : { comparator : "max_sim" } }, });
 use qdrant_client :: qdrant :: { CreateCollectionBuilder , Distance , VectorParamsBuilder , MultiVectorComparator , MultiVectorConfigBuilder , }; use qdrant_client :: Qdrant ; let client = Qdrant :: from_url ( "http://localhost:6334" ). build () ? ; client . create_collection ( CreateCollectionBuilder :: new ( "{collection_name}" ) . vectors_config ( VectorParamsBuilder :: new ( 100 , Distance :: Cosine ) . multivector_config ( MultiVectorConfigBuilder :: new ( MultiVectorComparator :: MaxSim ) ), ), ) . await ? ;
 import io.qdrant.client.QdrantClient ; import io.qdrant.client.QdrantGrpcClient ; import io.qdrant.client.grpc.Collections.Distance ; import io.qdrant.client.grpc.Collections.MultiVectorComparator ; import io.qdrant.client.grpc.Collections.MultiVectorConfig ; import io.qdrant.client.grpc.Collections.VectorParams ; QdrantClient client = new QdrantClient ( QdrantGrpcClient . newBuilder ( "localhost" , 6334 , false ). build ()); client . createCollectionAsync ( "{collection_name}" , VectorParams . newBuilder (). setSize ( 128 ) . setDistance ( Distance . Cosine ) . setMultivectorConfig ( MultiVectorConfig . newBuilder () . setComparator ( MultiVectorComparator . MaxSim ) . build ()) . build ()). get ();
 using Qdrant.Client ; using Qdrant.Client.Grpc ; var client = new QdrantClient ( "localhost" , 6334 ); await client . CreateCollectionAsync ( collectionName : "{collection_name}" , vectorsConfig : new VectorParams { Size = 128 , Distance = Distance . Cosine , MultivectorConfig = new () { Comparator = MultiVectorComparator . MaxSim } } );
 import ( "context" "github.com/qdrant/go-client/qdrant" ) client , err := qdrant . NewClient ( & qdrant . Config { Host : "localhost" , Port : 6334 , }) client . CreateCollection ( context . Background (), & qdrant . CreateCollection { CollectionName : "{collection_name}" , VectorsConfig : qdrant . NewVectorsConfig ( & qdrant . VectorParams { Size : 128 , Distance : qdrant . Distance_Cosine , MultivectorConfig : & qdrant . MultiVectorConfig { Comparator : qdrant . MultiVectorComparator_MaxSim , }, }), })

멀티벡터를 가진 포인트를 삽입하려면:

 PUT /collections/{collection_name}/points { "points": [ { "id": 1, "vector": [ [-0.013, 0.020, -0.007, -0.111, ...], [-0.030, -0.055, 0.001, 0.072, ...], [-0.041, 0.014, -0.032, -0.062, ...] ] } ] }
 from qdrant_client import QdrantClient , models client = QdrantClient ( url = "http://localhost:6333" ) client . upsert ( collection_name = " {collection_name} " , points = [ models . PointStruct ( id = 1 , vector = [ [ - 0.013 , 0.020 , - 0.007 , - 0.111 ], [ - 0.030 , - 0.055 , 0.001 , 0.072 ], [ - 0.041 , 0.014 , - 0.032 , - 0.062 ] ], ) ], )
 import { QdrantClient } from "@qdrant/js-client-rest" ; const client = new QdrantClient ({ host : "localhost" , port : 6333 }); client . upsert ( "{collection_name}" , { points : [ { id : 1 , vector : [ [ - 0.013 , 0.020 , - 0.007 , - 0.111 ], [ - 0.030 , - 0.055 , 0.001 , 0.072 ], [ - 0.041 , 0.014 , - 0.032 , - 0.062 ] ], } ] });
 use qdrant_client :: qdrant :: { PointStruct , UpsertPointsBuilder , Vector }; use qdrant_client :: { Payload , Qdrant }; let client = Qdrant :: from_url ( "http://localhost:6334" ). build () ? ; let points = vec! [ PointStruct :: new ( 1 , Vector :: new_multi ( vec! [ vec! [ - 0.013 , 0.020 , - 0.007 , - 0.111 ], vec! [ - 0.030 , - 0.055 , 0.001 , 0.072 ], vec! [ - 0.041 , 0.014 , - 0.032 , - 0.062 ], ]), Payload :: new () ) ]; client . upsert_points ( UpsertPointsBuilder :: new ( "{collection_name}" , points ) ). await ? ;
 import static io.qdrant.client.PointIdFactory.id ; import static io.qdrant.client.VectorFactory.multiVector ; import static io.qdrant.client.VectorsFactory.vectors ; import io.qdrant.client.QdrantClient ; import io.qdrant.client.QdrantGrpcClient ; import io.qdrant.client.grpc.Points.PointStruct ; import java.util.List ; QdrantClient client = new QdrantClient ( QdrantGrpcClient . newBuilder ( "localhost" , 6334 , false ). build ()); client . upsertAsync ( "{collection_name}" , List . of ( PointStruct . newBuilder () . setId ( id ( 1 )) . setVectors ( vectors ( multiVector ( new float [][] { { - 0 . 013f , 0 . 020f , - 0 . 007f , - 0 . 111f }, { - 0 . 030f , - 0 . 055f , 0 . 001f , 0 . 072f }, { - 0 . 041f , 0 . 014f , - 0 . 032f , - 0 . 062f } }))) . build () )) . get ();
 using Qdrant.Client ; using Qdrant.Client.Grpc ; var client = new QdrantClient ( "localhost" , 6334 ); await client . UpsertAsync ( collectionName : "{collection_name}" , points : new List < PointStruct > { new () { Id = 1 , Vectors = new float [][] { [-0.013f, 0.020f, -0.007f, -0.111f] , [-0.030f, -0.05f, 0.001f, 0.072f] , [-0.041f, 0.014f, -0.032f, -0.062f ] , }, }, } );
 import ( "context" "github.com/qdrant/go-client/qdrant" ) client , err := qdrant . NewClient ( & qdrant . Config { Host : "localhost" , Port : 6334 , }) client . Upsert ( context . Background (), & qdrant . UpsertPoints { CollectionName : "{collection_name}" , Points : [] * qdrant . PointStruct { { Id : qdrant . NewIDNum ( 1 ), Vectors : qdrant . NewVectorsMulti ( [][] float32 { { - 0.013 , 0.020 , - 0.007 , - 0.111 }, { - 0.030 , - 0.055 , 0.001 , 0.072 }, { - 0.041 , 0.014 , - 0.032 , - 0.062 }}), }, }, })

멀티벡터로 검색하려면 (query API에서 사용 가능):

 POST /collections/{collection_name}/points/query { "query": [ [-0.013, 0.020, -0.007, -0.111, ...], [-0.030, -0.055, 0.001, 0.072, ...], [-0.041, 0.014, -0.032, -0.062, ...] ] }
 from qdrant_client import QdrantClient , models client = QdrantClient ( url = "http://localhost:6333" ) client . query_points ( collection_name = " {collection_name} " , query = [ [ - 0.013 , 0.020 , - 0.007 , - 0.111 ], [ - 0.030 , - 0.055 , 0.001 , 0.072 ], [ - 0.041 , 0.014 , - 0.032 , - 0.062 ] ], )
 import { QdrantClient } from "@qdrant/js-client-rest" ; const client = new QdrantClient ({ host : "localhost" , port : 6333 }); client . query ( "{collection_name}" , { "query" : [ [ - 0.013 , 0.020 , - 0.007 , - 0.111 ], [ - 0.030 , - 0.055 , 0.001 , 0.072 ], [ - 0.041 , 0.014 , - 0.032 , - 0.062 ] ] });
 use qdrant_client :: Qdrant ; use qdrant_client :: qdrant :: { QueryPointsBuilder , VectorInput }; let client = Qdrant :: from_url ( "http://localhost:6334" ). build () ? ; let res = client . query ( QueryPointsBuilder :: new ( "{collection_name}" ) . query ( VectorInput :: new_multi ( vec! [ vec! [ - 0.013 , 0.020 , - 0.007 , - 0.111 ], vec! [ - 0.030 , - 0.055 , 0.001 , 0.072 ], vec! [ - 0.041 , 0.014 , - 0.032 , - 0.062 ], ] )) ). await ? ;
 import static io.qdrant.client.QueryFactory.nearest ; import io.qdrant.client.QdrantClient ; import io.qdrant.client.QdrantGrpcClient ; import io.qdrant.client.grpc.Points.QueryPoints ; QdrantClient client = new QdrantClient ( QdrantGrpcClient . newBuilder ( "localhost" , 6334 , false ). build ()); client . queryAsync ( QueryPoints . newBuilder () . setCollectionName ( "{collection_name}" ) . setQuery ( nearest ( new float [][] { { - 0 . 013f , 0 . 020f , - 0 . 007f , - 0 . 111f }, { - 0 . 030f , - 0 . 055f , 0 . 001f , 0 . 072f }, { - 0 . 041f , 0 . 014f , - 0 . 032f , - 0 . 062f } })) . build ()). get ();
 using Qdrant.Client ; var client = new QdrantClient ( "localhost" , 6334 ); await client . QueryAsync ( collectionName : "{collection_name}" , query : new float [][] { [-0.013f, 0.020f, -0.007f, -0.111f] , [-0.030f, -0.055f, 0.001f, 0.072f] , [-0.041f, 0.014f, -0.032f, -0.062f] , } );
 import ( "context" "github.com/qdrant/go-client/qdrant" ) client , err := qdrant . NewClient ( & qdrant . Config { Host : "localhost" , Port : 6334 , }) client . Query ( context . Background (), & qdrant . QueryPoints { CollectionName : "{collection_name}" , Query : qdrant . NewQueryMulti ( [][] float32 { { - 0.013 , 0.020 , - 0.007 , - 0.111 }, { - 0.030 , - 0.055 , 0.001 , 0.072 }, { - 0.041 , 0.014 , - 0.032 , - 0.062 }, }), })

명명 벡터 (Named Vectors)

Qdrant에서는 같은 포인트에 서로 다른 크기와 유형의 벡터 여러 개를 저장할 수 있어요. 이미지, 텍스트, 비디오 같은 서로 다른 특징이나 모달리티(modality)를 나타내기 위해 데이터를 여러 임베딩으로 정의할 때 유용하죠.

포인트마다 서로 다른 벡터를 저장하려면 컬렉션에 별도의 명명 벡터 공간(named vector space)을 만들어야 해요. 이런 벡터 공간은 컬렉션 생성 시 정의하거나, 나중에 추가하고 독립적으로 관리할 수도 있어요.

명명 벡터를 가진 컬렉션을 만들려면 각 벡터에 대한 설정을 지정해야 해요.

 PUT /collections/{collection_name} { "vectors": { "image": { "size": 4, "distance": "Dot" }, "text": { "size": 5, "distance": "Cosine" } }, "sparse_vectors": { "text-sparse": {} } }
 from qdrant_client import QdrantClient , models client = QdrantClient ( url = "http://localhost:6333" ) client . create_collection ( collection_name = " {collection_name} " , vectors_config = { "image" : models . VectorParams ( size = 4 , distance = models . Distance . DOT ), "text" : models . VectorParams ( size = 5 , distance = models . Distance . COSINE ), }, sparse_vectors_config = { "text-sparse" : models . SparseVectorParams ()}, )
 import { QdrantClient } from "@qdrant/js-client-rest" ; const client = new QdrantClient ({ host : "localhost" , port : 6333 }); client . createCollection ( "{collection_name}" , { vectors : { image : { size : 4 , distance : "Dot" }, text : { size : 5 , distance : "Cosine" }, }, sparse_vectors : { text_sparse : {} } });
 use qdrant_client :: qdrant :: { CreateCollectionBuilder , Distance , SparseVectorParamsBuilder , SparseVectorsConfigBuilder , VectorParamsBuilder , VectorsConfigBuilder , }; use qdrant_client :: Qdrant ; let client = Qdrant :: from_url ( "http://localhost:6334" ). build () ? ; let mut vector_config = VectorsConfigBuilder :: default (); vector_config . add_named_vector_params ( "text" , VectorParamsBuilder :: new ( 5 , Distance :: Dot )); vector_config . add_named_vector_params ( "image" , VectorParamsBuilder :: new ( 4 , Distance :: Cosine )); let mut sparse_vectors_config = SparseVectorsConfigBuilder :: default (); sparse_vectors_config . add_named_vector_params ( "text-sparse" , SparseVectorParamsBuilder :: default ()); client . create_collection ( CreateCollectionBuilder :: new ( "{collection_name}" ) . vectors_config ( vector_config ) . sparse_vectors_config ( sparse_vectors_config ), ) . await ? ;
 import io.qdrant.client.QdrantClient ; import io.qdrant.client.QdrantGrpcClient ; import io.qdrant.client.grpc.Collections.CreateCollection ; import io.qdrant.client.grpc.Collections.Distance ; import io.qdrant.client.grpc.Collections.SparseVectorConfig ; import io.qdrant.client.grpc.Collections.SparseVectorParams ; import io.qdrant.client.grpc.Collections.VectorParams ; import io.qdrant.client.grpc.Collections.VectorParamsMap ; import io.qdrant.client.grpc.Collections.VectorsConfig ; import java.util.Map ; QdrantClient client = new QdrantClient ( QdrantGrpcClient . newBuilder ( "localhost" , 6334 , false ). build ()); client . createCollectionAsync ( CreateCollection . newBuilder () . setCollectionName ( "{collection_name}" ) . setVectorsConfig ( VectorsConfig . newBuilder (). setParamsMap ( VectorParamsMap . newBuilder (). putAllMap ( Map . of ( "image" , VectorParams . newBuilder () . setSize ( 4 ) . setDistance ( Distance . Dot ) . build (), "text" , VectorParams . newBuilder () . setSize ( 5 ) . setDistance ( Distance . Cosine ) . build ())))) . setSparseVectorsConfig ( SparseVectorConfig . newBuilder (). putMap ( "text-sparse" , SparseVectorParams . getDefaultInstance ())) . build ()) . get ();
 using Qdrant.Client ; using Qdrant.Client.Grpc ; var client = new QdrantClient ( "localhost" , 6334 ); await client . CreateCollectionAsync ( collectionName : "{collection_name}" , vectorsConfig : new VectorParamsMap { Map = { ["image"] = new VectorParams { Size = 4 , Distance = Distance . Dot }, ["text"] = new VectorParams { Size = 5 , Distance = Distance . Cosine }, } }, sparseVectorsConfig : new SparseVectorConfig { Map = { ["text-sparse"] = new () } } );
 import ( "context" "github.com/qdrant/go-client/qdrant" ) client , err := qdrant . NewClient ( & qdrant . Config { Host : "localhost" , Port : 6334 , }) client . CreateCollection ( context . Background (), & qdrant . CreateCollection { CollectionName : "{collection_name}" , VectorsConfig : qdrant . NewVectorsConfigMap ( map [ string ] * qdrant . VectorParams { "image" : { Size : 4 , Distance : qdrant . Distance_Dot , }, "text" : { Size : 5 , Distance : qdrant . Distance_Cosine , }, }), SparseVectorsConfig : qdrant . NewSparseVectorsConfig ( map [ string ] * qdrant . SparseVectorParams { "text-sparse" : {}, }, ), })

명명 벡터를 가진 포인트를 삽입하려면:

 PUT /collections/{collection_name}/points?wait=true { "points": [ { "id": 1, "vector": { "image": [0.9, 0.1, 0.1, 0.2], "text": [0.4, 0.7, 0.1, 0.8, 0.1], "text-sparse": { "indices": [1, 3, 5, 7], "values": [0.1, 0.2, 0.3, 0.4] } } } ] }
 client . upsert ( collection_name = " {collection_name} " , points = [ models . PointStruct ( id = 1 , vector = { "image" : [ 0.9 , 0.1 , 0.1 , 0.2 ], "text" : [ 0.4 , 0.7 , 0.1 , 0.8 , 0.1 ], "text-sparse" : { "indices" : [ 1 , 3 , 5 , 7 ], "values" : [ 0.1 , 0.2 , 0.3 , 0.4 ], }, }, ), ], )
 client . upsert ( "{collection_name}" , { points : [ { id : 1 , vector : { image : [ 0.9 , 0.1 , 0.1 , 0.2 ], text : [ 0.4 , 0.7 , 0.1 , 0.8 , 0.1 ], text_sparse : { indices : [ 1 , 3 , 5 , 7 ], values : [ 0.1 , 0.2 , 0.3 , 0.4 ] } }, }, ], });
 use qdrant_client :: qdrant :: { NamedVectors , PointStruct , UpsertPointsBuilder , Vector , }; use qdrant_client :: Payload ; client . upsert_points ( UpsertPointsBuilder :: new ( "{collection_name}" , vec! [ PointStruct :: new ( 1 , NamedVectors :: default () . add_vector ( "text" , Vector :: new_dense ( vec! [ 0.4 , 0.7 , 0.1 , 0.8 , 0.1 ])) . add_vector ( "image" , Vector :: new_dense ( vec! [ 0.9 , 0.1 , 0.1 , 0.2 ])) . add_vector ( "text-sparse" , Vector :: new_sparse ( vec! [ 1 , 3 , 5 , 7 ], vec! [ 0.1 , 0.2 , 0.3 , 0.4 ]), ), Payload :: default (), )], ) . wait ( true ), ) . await ? ;
 import static io.qdrant.client.PointIdFactory.id ; import static io.qdrant.client.VectorFactory.vector ; import static io.qdrant.client.VectorsFactory.namedVectors ; import io.qdrant.client.grpc.Points.PointStruct ; import java.util.List ; import java.util.Map ; client . upsertAsync ( "{collection_name}" , List . of ( PointStruct . newBuilder () . setId ( id ( 1 )) . setVectors ( namedVectors ( Map . of ( "image" , vector ( List . of ( 0 . 9f , 0 . 1f , 0 . 1f , 0 . 2f )), "text" , vector ( List . of ( 0 . 4f , 0 . 7f , 0 . 1f , 0 . 8f , 0 . 1f )), "text-sparse" , vector ( List . of ( 0 . 1f , 0 . 2f , 0 . 3f , 0 . 4f ), List . of ( 1 , 3 , 5 , 7 ))))) . build ())) . get ();
 using Qdrant.Client ; using Qdrant.Client.Grpc ; await client . UpsertAsync ( collectionName : "{collection_name}" , points : new List < PointStruct > { new () { Id = 1 , Vectors = new Dictionary < string , Vector > { ["image"] = new float [] { 0.9f , 0.1f , 0.1f , 0.2f }, ["text"] = new float [] { 0.4f , 0.7f , 0.1f , 0.8f , 0.1f }, ["text-sparse"] = ([ 0.1f , 0.2f , 0.3f , 0.4f ], [ 1 , 3 , 5 , 7 ]), } } } );
 import ( "context" "github.com/qdrant/go-client/qdrant" ) client . Upsert ( context . Background (), & qdrant . UpsertPoints { CollectionName : "{collection_name}" , Points : [] * qdrant . PointStruct { { Id : qdrant . NewIDNum ( 1 ), Vectors : qdrant . NewVectorsMap ( map [ string ] * qdrant . Vector { "image" : qdrant . NewVector ( 0.9 , 0.1 , 0.1 , 0.2 ), "text" : qdrant . NewVector ( 0.4 , 0.7 , 0.1 , 0.8 , 0.1 ), "text-sparse" : qdrant . NewVectorSparse ( [] uint32 { 1 , 3 , 5 , 7 }, [] float32 { 0.1 , 0.2 , 0.3 , 0.4 }), }), }, }, })

명명 벡터로 검색하려면 (query API에서 사용 가능):

 POST /collections/{collection_name}/points/query { "query": [0.2, 0.1, 0.9, 0.7], "using": "image", "limit": 3 }
 from qdrant_client import QdrantClient client = QdrantClient ( url = "http://localhost:6333" ) client . query_points ( collection_name = " {collection_name} " , query = [ 0.2 , 0.1 , 0.9 , 0.7 ], using = "image" , limit = 3 , )
 import { QdrantClient } from "@qdrant/js-client-rest" ; const client = new QdrantClient ({ host : "localhost" , port : 6333 }); client . query ( "{collection_name}" , { query : [ 0.2 , 0.1 , 0.9 , 0.7 ], using : "image" , limit : 3 , });
 use qdrant_client :: qdrant :: QueryPointsBuilder ; use qdrant_client :: Qdrant ; let client = Qdrant :: from_url ( "http://localhost:6334" ). build () ? ; client . query ( QueryPointsBuilder :: new ( "{collection_name}" ) . query ( vec! [ 0.2 , 0.1 , 0.9 , 0.7 ]) . limit ( 3 ) . using ( "image" ), ) . await ? ;
 import static io.qdrant.client.QueryFactory.nearest ; import io.qdrant.client.QdrantClient ; import io.qdrant.client.QdrantGrpcClient ; import io.qdrant.client.grpc.Points.QueryPoints ; import java.util.List ; QdrantClient client = new QdrantClient ( QdrantGrpcClient . newBuilder ( "localhost" , 6334 , false ). build ()); client . queryAsync ( QueryPoints . newBuilder () . setCollectionName ( "{collection_name}" ) . setQuery ( nearest ( 0 . 2f , 0 . 1f , 0 . 9f , 0 . 7f )) . setUsing ( "image" ) . setLimit ( 3 ) . build ()). get ();
 using Qdrant.Client ; var client = new QdrantClient ( "localhost" , 6334 ); await client . QueryAsync ( collectionName : "{collection_name}" , query : new float [] { 0.2f , 0.1f , 0.9f , 0.7f }, usingVector : "image" , limit : 3 );
 import ( "context" "github.com/qdrant/go-client/qdrant" ) client , err := qdrant . NewClient ( & qdrant . Config { Host : "localhost" , Port : 6334 , }) client . Query ( context . Background (), & qdrant . QueryPoints { CollectionName : "{collection_name}" , Query : qdrant . NewQuery ( 0.2 , 0.1 , 0.9 , 0.7 ), Using : qdrant . PtrOf ( "image" ), })

명명 벡터 추가 및 제거 (Adding and Removing Named Vectors)

v1.18.0부터 사용 가능

기존 컬렉션을 다시 만들지 않고도 명명 벡터를 추가하거나 제거할 수 있어요.

예를 들어:

 PUT /collections/{collection_name}/vectors/{vector_name} { "dense": { "size": 256, "distance": "Cosine" } }
 client . create_vector_name ( collection_name = " {collection_name} " , vector_name = " {vector_name} " , vector_name_config = models . DenseVectorNameConfig ( dense = models . DenseVectorConfig ( size = 256 , distance = models . Distance . COSINE , ), ), )
 client . createVectorName ( "{collection_name}" , "{vector_name}" , { dense : { size : 256 , distance : "Cosine" , }, });
 use qdrant_client :: qdrant :: { CreateVectorNameRequestBuilder , DenseVectorCreationConfigBuilder , Distance , }; use qdrant_client :: Qdrant ; client . create_vector_name ( CreateVectorNameRequestBuilder :: new ( "{collection_name}" , "{vector_name}" , DenseVectorCreationConfigBuilder :: new ( 256 , Distance :: Cosine ), ), ) . await ? ;
 import io.qdrant.client.QdrantClient ; import io.qdrant.client.QdrantGrpcClient ; import io.qdrant.client.grpc.Collections.Distance ; import io.qdrant.client.grpc.Points.CreateVectorNameRequest ; import io.qdrant.client.grpc.Points.DenseVectorCreationConfig ; client . createVectorNameAsync ( CreateVectorNameRequest . newBuilder () . setCollectionName ( "{collection_name}" ) . setVectorName ( "{vector_name}" ) . setDenseConfig ( DenseVectorCreationConfig . newBuilder () . setSize ( 256 ) . setDistance ( Distance . Cosine ) . build ()) . build ()) . get ();
 using Qdrant.Client ; using Qdrant.Client.Grpc ; await client . CreateVectorNameAsync ( new () { CollectionName = "{collection_name}" , VectorName = "{vector_name}" , DenseConfig = new () { Size = 256 , Distance = Distance . Cosine } });
 import ( "context" "github.com/qdrant/go-client/qdrant" ) client . CreateVectorName ( context . Background (), & qdrant . CreateVectorNameRequest { CollectionName : "{collection_name}" , VectorName : "{vector_name}" , VectorConfig : & qdrant . CreateVectorNameRequest_DenseConfig { DenseConfig : & qdrant . DenseVectorCreationConfig { Size : 256 , Distance : qdrant . Distance_Cosine , }, }, })

자세한 내용은 벡터 업데이트를 참고하세요.

추론 (Inference)

데이터를 입력하거나 쿼리할 때 벡터를 직접 제공하는 대신, Qdrant가 추론(inference)이라는 과정을 통해 벡터를 생성할 수도 있어요. 추론은 텍스트, 이미지, 또는 다른 데이터 유형에서 머신러닝 모델을 사용해 벡터 임베딩을 만드는 과정이에요.

일반 벡터를 사용할 수 있는 API 어디에서든 추론을 사용할 수 있어요. 예를 들어 포인트를 upsert할 때 텍스트나 이미지와 임베딩 모델을 제공할 수 있죠.

 PUT /collections/{collection_name}/points { "points": [ { "id": 1, "vector": { "my-bm25-vector": { "text": "Recipe for baking chocolate chip cookies", "model": "qdrant/bm25" } } } ] }
 from qdrant_client import QdrantClient , models client = QdrantClient ( url = "https://xyz-example.qdrant.io:6333" , api_key = "<your-api-key>" , cloud_inference = True ) client . upsert ( collection_name = " {collection_name} " , points = [ models . PointStruct ( id = 1 , vector = { "my-bm25-vector" : models . Document ( text = "Recipe for baking chocolate chip cookies" , model = "Qdrant/bm25" , ) }, ) ], )
 import { QdrantClient } from "@qdrant/js-client-rest" ; const client = new QdrantClient ({ host : "localhost" , port : 6333 }); client . upsert ( "{collection_name}" , { points : [ { id : 1 , vector : { 'my-bm25-vector' : { text : 'Recipe for baking chocolate chip cookies' , model : 'Qdrant/bm25' , }, }, }, ], });
 use qdrant_client :: { Payload , Qdrant , qdrant :: { DocumentBuilder , PointStruct , UpsertPointsBuilder }, }; use std :: collections :: HashMap ; let client = Qdrant :: from_url ( "<your-qdrant-url>" ). build () ? ; client . upsert_points ( UpsertPointsBuilder :: new ( "{collection_name}" , vec! [ PointStruct :: new ( 1 , HashMap :: from ([( "my-bm25-vector" . to_string (), DocumentBuilder :: new ( "Recipe for baking chocolate chip cookies" , "qdrant/bm25" ) . build (), )]), Payload :: default (), )], )) . await ? ;
 import static io.qdrant.client.PointIdFactory.id ; import static io.qdrant.client.ValueFactory.value ; import static io.qdrant.client.VectorFactory.vector ; import static io.qdrant.client.VectorsFactory.namedVectors ; import io.qdrant.client.QdrantClient ; import io.qdrant.client.QdrantGrpcClient ; import io.qdrant.client.grpc.Points.Document ; import io.qdrant.client.grpc.Points.Image ; import io.qdrant.client.grpc.Points.PointStruct ; import java.util.List ; import java.util.Map ; QdrantClient client = new QdrantClient ( QdrantGrpcClient . newBuilder ( "xyz-example.qdrant.io" , 6334 , true ) . withApiKey ( "<your-api-key" ) . build ()); client . upsertAsync ( "{collection_name}" , List . of ( PointStruct . newBuilder () . setId ( id ( 1 )) . setVectors ( namedVectors ( Map . of ( "my-bm25-vector" , vector ( Document . newBuilder () . setModel ( "qdrant/bm25" ) . setText ( "Recipe for baking chocolate chip cookies" ) . build ())))) . build ())) . get ();
 using Qdrant.Client ; using Qdrant.Client.Grpc ; var client = new QdrantClient ( host : "xyz-example.qdrant.io" , port : 6334 , https : true , apiKey : *** ); await client . UpsertAsync ( collectionName : "{collection_name}" , points : new List < PointStruct > { new () { Id = 1 , Vectors = new Dictionary < string , Vector > { ["my-bm25-vector"] = new Document () { Model = "qdrant/bm25" , Text = "Recipe for baking chocolate chip cookies" , }, }, }, } );
 import ( "context" "github.com/qdrant/go-client/qdrant" ) client , err := qdrant . NewClient ( & qdrant . Config { Host : "xyz-example.qdrant.io" , Port : 6334 , APIKey : "<past...re>" , UseTLS : true , }) client . Upsert ( context . Background (), & qdrant . UpsertPoints { CollectionName : "{collection_name}" , Points : [] * qdrant . PointStruct { { Id : qdrant . NewIDNum ( uint64 ( 1 )), Vectors : qdrant . NewVectorsMap ( map [ string ] * qdrant . Vector { "my-bm25-vector" : qdrant . NewVectorDocument ( & qdrant . Document { Model : "qdrant/bm25" , Text : "Recipe for baking chocolate chip cookies" , }), }), }, }, })

Qdrant는 이 모델을 사용해 임베딩을 생성하고, 결과 벡터와 함께 포인트를 저장해요.

마찬가지로 쿼리 시에도 추론을 사용할 수 있어요. 쿼리할 텍스트나 이미지와 임베딩 모델을 제공하면 되죠.

 POST /collections/{collection_name}/points/query { "query": { "text": "How to bake cookies?", "model": "qdrant/bm25" }, "using": "my-bm25-vector" }
 from qdrant_client import QdrantClient , models client = QdrantClient ( url = "https://xyz-example.qdrant.io:6333" , api_key = "<your-api-key>" , cloud_inference = True ) client . query_points ( collection_name = " {collection_name} " , query = models . Document ( text = "How to bake cookies?" , model = "Qdrant/bm25" , ), using = "my-bm25-vector" , )
 import { QdrantClient } from "@qdrant/js-client-rest" ; const client = new QdrantClient ({ host : "localhost" , port : 6333 }); client . query ( "{collection_name}" , { query : { text : 'How to bake cookies?' , model : 'qdrant/bm25' , }, using : 'my-bm25-vector' , });
 use qdrant_client :: { Qdrant , qdrant :: { Document , Query , QueryPointsBuilder }, }; let client = Qdrant :: from_url ( "<your-qdrant-url>" ). build (). unwrap (); client . query ( QueryPointsBuilder :: new ( "{collection_name}" ) . query ( Query :: new_nearest ( Document { text : "How to bake cookies?" . into (), model : "qdrant/bm25" . into (), .. Default :: default () })) . using ( "my-bm25-vector" ) . build (), ) . await ? ;
 import static io.qdrant.client.QueryFactory.nearest ; import io.qdrant.client.QdrantClient ; import io.qdrant.client.QdrantGrpcClient ; import io.qdrant.client.grpc.Points.Document ; import io.qdrant.client.grpc.Points ; QdrantClient client = new QdrantClient ( QdrantGrpcClient . newBuilder ( "xyz-example.qdrant.io" , 6334 , true ) . withApiKey ( "<your-api-key" ) . build ()); client . queryAsync ( Points . QueryPoints . newBuilder () . setCollectionName ( "{collection_name}" ) . setQuery ( nearest ( Document . newBuilder () . setModel ( "qdrant/bm25" ) . setText ( "How to bake cookies?" ) . build ())) . setUsing ( "my-bm25-vector" ) . build ()) . get ();
 using Qdrant.Client ; using Qdrant.Client.Grpc ; var client = new QdrantClient ( host : "xyz-example.qdrant.io" , port : 6334 , https : true , apiKey : *** ); await client . QueryAsync ( collectionName : "{collection_name}" , query : new Document () { Model = "qdrant/bm25" , Text = "How to bake cookies?" }, usingVector : "my-bm25-vector" );
 import ( "context" "github.com/qdrant/go-client/qdrant" ) client , err := qdrant . NewClient ( & qdrant . Config { Host : "xyz-example.qdrant.io" , Port : 6334 , APIKey : "<past...re>" , UseTLS : true , }) client . Query ( context . Background (), & qdrant . QueryPoints { CollectionName : "{collection_name}" , Query : qdrant . NewQueryNearest ( qdrant . NewVectorInputDocument ( & qdrant . Document { Model : "qdrant/bm25" , Text : "How to bake cookies?" , }), ), Using : qdrant . PtrOf ( "my-bm25-vector" ), })

데이터 타입 (Datatypes)

최신 임베딩 모델은 차원(dimensionality)이 매우 큰 벡터를 생성해요. OpenAI의 text-embedding-3-large 임베딩 모델은 차원이 최대 3072까지 올라갈 수 있어요.

이런 벡터를 저장하는 데 필요한 메모리 양은 차원에 따라 선형적으로 늘어나기 때문에, 벡터에 맞는 데이터 타입을 고르는 게 중요해요.

데이터 타입 선택은 메모리 사용량과 벡터 정밀도(precision) 사이의 트레이드오프(trade-off)예요.

Qdrant는 밀집 벡터와 희소 벡터 모두에 대해 여러 데이터 타입을 지원해요.

Float32

Qdrant에서 벡터의 기본 데이터 타입이에요. 32비트(4바이트) 부동소수점 숫자예요. 표준 OpenAI 임베딩(차원 1536)을 Float32로 저장하면 6KB의 메모리가 필요해요.

Qdrant에서 벡터 데이터 타입은 기본적으로 Float32로 설정되어 있기 때문에 별도로 지정할 필요가 없어요.

Float16

16비트(2바이트) 부동소수점 숫자예요. 반정밀도(half-precision) 부동소수점이라고도 해요. 직관적으로는 이렇게 생겼어요.

 float32 -> float16 delta (float32 - float16).abs 0.79701585 -> 0.796875 delta 0.00014084578 0.7850789 -> 0.78515625 delta 0.00007736683 0.7775044 -> 0.77734375 delta 0.00016063452 0.85776305 -> 0.85791016 delta 0.00014710426 0.6616839 -> 0.6616211 delta 0.000062823296

Float16의 가장 큰 장점은 벡터 검색 품질에는 사실상 영향이 없으면서도, Float32의 절반 메모리만 사용한다는 점이에요.

Float16을 사용하려면 컬렉션 설정에서 벡터 데이터 타입을 지정해야 해요.

 PUT /collections/{collection_name} { "vectors": { "size": 128, "distance": "Cosine", "datatype": "float16" // <-- For dense vectors }, "sparse_vectors": { "text": { "index": { "datatype": "float16" // <-- And for sparse vectors } } } }
 from qdrant_client import QdrantClient , models client = QdrantClient ( url = "http://localhost:6333" ) client . create_collection ( collection_name = " {collection_name} " , vectors_config = models . VectorParams ( size = 128 , distance = models . Distance . COSINE , datatype = models . Datatype . FLOAT16 ), sparse_vectors_config = { "text" : models . SparseVectorParams ( index = models . SparseIndexParams ( datatype = models . Datatype . FLOAT16 ) ), }, )
 import { QdrantClient } from "@qdrant/js-client-rest" ; const client = new QdrantClient ({ host : "localhost" , port : 6333 }); client . createCollection ( "{collection_name}" , { vectors : { size : 128 , distance : "Cosine" , datatype : "float16" }, sparse_vectors : { text : { index : { datatype : "float16" } } } });
 use qdrant_client :: qdrant :: { CreateCollectionBuilder , Datatype , Distance , SparseIndexConfigBuilder , SparseVectorParamsBuilder , SparseVectorsConfigBuilder , VectorParamsBuilder }; use qdrant_client :: Qdrant ; let client = Qdrant :: from_url ( "http://localhost:6334" ). build () ? ; let mut sparse_vector_config = SparseVectorsConfigBuilder :: default (); sparse_vector_config . add_named_vector_params ( "text" , SparseVectorParamsBuilder :: default () . index ( SparseIndexConfigBuilder :: default (). datatype ( Datatype :: Float32 )), ); let create_collection = CreateCollectionBuilder :: new ( "{collection_name}" ) . sparse_vectors_config ( sparse_vector_config ) . vectors_config ( VectorParamsBuilder :: new ( 128 , Distance :: Cosine ). datatype ( Datatype :: Float16 ), ); client . create_collection ( create_collection ). await ? ;
 import io.qdrant.client.QdrantClient ; import io.qdrant.client.QdrantGrpcClient ; import io.qdrant.client.grpc.Collections.CreateCollection ; import io.qdrant.client.grpc.Collections.Datatype ; import io.qdrant.client.grpc.Collections.Distance ; import io.qdrant.client.grpc.Collections.SparseIndexConfig ; import io.qdrant.client.grpc.Collections.SparseVectorConfig ; import io.qdrant.client.grpc.Collections.SparseVectorParams ; import io.qdrant.client.grpc.Collections.VectorParams ; import io.qdrant.client.grpc.Collections.VectorsConfig ; QdrantClient client = new QdrantClient ( QdrantGrpcClient . newBuilder ( "localhost" , 6334 , false ). build ()); client . createCollectionAsync ( CreateCollection . newBuilder () . setCollectionName ( "{collection_name}" ) . setVectorsConfig ( VectorsConfig . newBuilder () . setParams ( VectorParams . newBuilder () . setSize ( 128 ) . setDistance ( Distance . Cosine ) . setDatatype ( Datatype . Float16 ) . build ()) . build ()) . setSparseVectorsConfig ( SparseVectorConfig . newBuilder () . putMap ( "text" , SparseVectorParams . newBuilder () . setIndex ( SparseIndexConfig . newBuilder () . setDatatype ( Datatype . Float16 ) . build ()) . build ())) . build ()) . get ();
 using Qdrant.Client ; using Qdrant.Client.Grpc ; var client = new QdrantClient ( "localhost" , 6334 ); await client . CreateCollectionAsync ( collectionName : "{collection_name}" , vectorsConfig : new VectorParams { Size = 128 , Distance = Distance . Cosine , Datatype = Datatype . Float16 }, sparseVectorsConfig : ( "text" , new SparseVectorParams { Index = new SparseIndexConfig { Datatype = Datatype . Float16 } } ) );
 import ( "context" "github.com/qdrant/go-client/qdrant" ) client , err := qdrant . NewClient ( & qdrant . Config { Host : "localhost" , Port : 6334 , }) client . CreateCollection ( context . Background (), & qdrant . CreateCollection { CollectionName : "{collection_name}" , VectorsConfig : qdrant . NewVectorsConfig ( & qdrant . VectorParams { Size : 128 , Distance : qdrant . Distance_Cosine , Datatype : qdrant . Datatype_Float16 . Enum (), }), SparseVectorsConfig : qdrant . NewSparseVectorsConfig ( map [ string ] * qdrant . SparseVectorParams { "text" : { Index : & qdrant . SparseIndexConfig { Datatype : qdrant . Datatype_Float16 . Enum (), }, }, }), })

Uint8

메모리 최적화의 다음 단계는 벡터에 Uint8 데이터 타입을 사용하는 거예요. Float16과 달리 Uint8은 부동소수점 숫자가 아니라 0부터 255 사이의 정수예요.

모든 임베딩 모델이 0부터 255 범위의 벡터를 생성하는 건 아니기 때문에, Uint8 데이터 타입을 사용할 때는 주의가 필요해요.

숫자를 float 범위에서 Uint8 범위로 변환하려면 양자화(quantization)라는 과정을 적용해야 해요.

일부 임베딩 프로바이더는 사전 양자화된(pre-quantized) 형식의 임베딩을 제공할 수 있어요. 가장 대표적인 예는 Cohere int8 & binary 임베딩이에요.

다른 임베딩의 경우에는 직접 양자화를 적용해야 해요.

 PUT /collections/{collection_name} { "vectors": { "size": 128, "distance": "Cosine", "datatype": "uint8" // <-- For dense vectors }, "sparse_vectors": { "text": { "index": { "datatype": "uint8" // <-- For sparse vectors } } } }
 from qdrant_client import QdrantClient , models client = QdrantClient ( url = "http://localhost:6333" ) client . create_collection ( collection_name = " {collection_name} " , vectors_config = models . VectorParams ( size = 128 , distance = models . Distance . COSINE , datatype = models . Datatype . UINT8 ), sparse_vectors_config = { "text" : models . SparseVectorParams ( index = models . SparseIndexParams ( datatype = models . Datatype . UINT8 ) ), }, )
 import { QdrantClient } from "@qdrant/js-client-rest" ; const client = new QdrantClient ({ host : "localhost" , port : 6333 }); client . createCollection ( "{collection_name}" , { vectors : { size : 128 , distance : "Cosine" , datatype : "uint8" }, sparse_vectors : { text : { index : { datatype : "uint8" } } } });

참고: 원문의 Uint8 섹션에서 Rust/Java/C#/Go의 코드 블록은 본 페이지를 수집하는 과정에서 잘려서 일부만 확보되었다. 내용 자체는 위 Float16 예제와 동일한 패턴에 datatypeuint8로 바꾼 형태다. 필요하면 Qdrant 공식 문서에서 확인.