milvus-logo
LFAI
홈페이지
  • 사용자 가이드

이진 벡터

이진 벡터는 기존의 고차원 부동소수점 벡터를 0과 1만 포함하는 이진 벡터로 변환하는 특수한 형태의 데이터 표현입니다. 이러한 변환은 벡터의 크기를 압축할 뿐만 아니라 의미 정보를 유지하면서 저장 및 계산 비용을 줄여줍니다. 중요하지 않은 특징에 대한 정밀도가 필수적이지 않은 경우, 이진 벡터는 원래 부동소수점 벡터의 무결성과 유용성 대부분을 효과적으로 유지할 수 있습니다.

바이너리 벡터는 특히 계산 효율성과 스토리지 최적화가 중요한 상황에서 폭넓게 활용될 수 있습니다. 검색 엔진이나 추천 시스템과 같은 대규모 AI 시스템에서는 방대한 양의 데이터를 실시간으로 처리하는 것이 핵심입니다. 바이너리 벡터는 벡터의 크기를 줄임으로써 정확도를 크게 떨어뜨리지 않으면서 지연 시간과 계산 비용을 낮추는 데 도움이 됩니다. 또한 바이너리 벡터는 메모리와 처리 능력이 제한된 모바일 디바이스나 임베디드 시스템과 같은 리소스 제약이 있는 환경에서 유용합니다. 바이너리 벡터를 사용하면 이러한 제한된 환경에서도 고성능을 유지하면서 복잡한 AI 기능을 구현할 수 있습니다.

개요

바이너리 벡터는 이미지, 텍스트, 오디오 등 복잡한 객체를 고정 길이의 이진 값으로 인코딩하는 방법입니다. Milvus에서 바이너리 벡터는 일반적으로 비트 배열 또는 바이트 배열로 표현됩니다. 예를 들어 8차원 바이너리 벡터는 [1, 0, 1, 1, 0, 0, 1, 0] 로 표현할 수 있습니다.

아래 다이어그램은 이진 벡터가 텍스트 콘텐츠에서 키워드의 존재를 나타내는 방법을 보여줍니다. 이 예에서는 10차원 이진 벡터를 사용하여 두 개의 서로 다른 텍스트(텍스트 1과 텍스트 2)를 표현하는데, 각 차원은 어휘의 한 단어에 해당하며, 1은 텍스트에 해당 단어가 있음을 나타내고 0은 해당 단어가 없음을 나타냅니다.

Binary vector representation of text content 텍스트 콘텐츠의 이진 벡터 표현

이진 벡터에는 다음과 같은 특징이 있습니다.

  • 효율적인 저장 공간: 각 차원에는 1비트만 저장 공간이 필요하므로 저장 공간을 크게 줄일 수 있습니다.

  • 빠른 계산: XOR과 같은 비트 연산을 사용하여 벡터 간의 유사도를 빠르게 계산할 수 있습니다.

  • 고정 길이: 원본 텍스트 길이에 관계없이 벡터의 길이가 일정하게 유지되므로 색인 및 검색이 더 쉬워집니다.

  • 간단하고 직관적입니다: 키워드의 존재 여부를 직접 반영하므로 특정 전문 검색 작업에 적합합니다.

다양한 방법을 통해 바이너리 벡터를 생성할 수 있습니다. 텍스트 처리에서는 사전 정의된 어휘를 사용하여 단어의 존재 여부에 따라 해당 비트를 설정할 수 있습니다. 이미지 처리의 경우, 지각 해싱 알고리즘(예: pHash)을 통해 이미지의 이진 특징을 생성할 수 있습니다. 머신 러닝 애플리케이션에서는 모델 출력을 2진법으로 변환하여 2진 벡터 표현을 얻을 수 있습니다.

이진 벡터화 후에는 데이터를 Milvus에 저장하여 관리 및 벡터 검색을 수행할 수 있습니다. 아래 다이어그램은 기본 프로세스를 보여줍니다.

Use binary vectors in Milvus Milvus에서 바이너리 벡터 사용

이진 벡터는 특정 시나리오에서는 탁월하지만, 표현 능력에 한계가 있어 복잡한 의미 관계를 포착하기 어렵습니다. 따라서 실제 시나리오에서는 효율성과 표현력의 균형을 맞추기 위해 이진 벡터를 다른 벡터 유형과 함께 사용하는 경우가 많습니다. 자세한 내용은 고밀도 벡터스파스 벡터를 참조하십시오.

Milvus에서 바이너리 벡터 사용

벡터 필드 추가

Milvus에서 바이너리 벡터를 사용하려면 먼저 컬렉션을 만들 때 바이너리 벡터를 저장할 벡터 필드를 정의합니다. 이 과정에는 다음이 포함됩니다.

  1. datatype 을 지원되는 바이너리 벡터 데이터 유형(예: BINARY_VECTOR)으로 설정합니다.

  2. dim 매개변수를 사용하여 벡터의 차원을 지정합니다. 삽입할 때 바이너리 벡터를 바이트 배열로 변환해야 하므로 dim 은 8의 배수여야 한다는 점에 유의하세요. 8개의 부울 값(0 또는 1)은 모두 1바이트로 패킹됩니다. 예를 들어 dim=128 의 경우 삽입하려면 16바이트 배열이 필요합니다.

from pymilvus import MilvusClient, DataType

client = MilvusClient(uri="http://localhost:19530")

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="binary_vector", datatype=DataType.BINARY_VECTOR, dim=128)

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("binary_vector")
        .dataType(DataType.BinaryVector)
        .dimension(128)
        .build());

import { DataType } from "@zilliz/milvus2-sdk-node";

schema.push({
  name: "binary vector",
  data_type: DataType.BinaryVector,
  dim: 128,
});

export primaryField='{
    "fieldName": "pk",
    "dataType": "VarChar",
    "isPrimary": true,
    "elementTypeParams": {
        "max_length": 100
    }
}'

export vectorField='{
    "fieldName": "binary_vector",
    "dataType": "BinaryVector",
    "elementTypeParams": {
        "dim": 128
    }
}'

export schema="{
    \"autoID\": true,
    \"fields\": [
        $primaryField,
        $vectorField
    ],
    \"enableDynamicField\": true
}"


이 예에서는 바이너리 벡터를 저장하기 위해 binary_vector 이라는 벡터 필드가 추가되었습니다. 이 필드의 데이터 유형은 BINARY_VECTOR 이며, 차원은 128입니다.

벡터 필드에 대한 인덱스 매개변수 설정

검색 속도를 높이려면 바이너리 벡터 필드에 대한 인덱스를 만들어야 합니다. 인덱싱은 대규모 벡터 데이터의 검색 효율성을 크게 향상시킬 수 있습니다.

index_params = client.prepare_index_params()

index_params.add_index(
    field_name="binary_vector",
    index_name="binary_vector_index",
    index_type="BIN_IVF_FLAT",
    metric_type="HAMMING",
    params={"nlist": 128}
)

import io.milvus.v2.common.IndexParam;
import java.util.*;

List<IndexParam> indexParams = new ArrayList<>();
Map<String,Object> extraParams = new HashMap<>();
extraParams.put("nlist",128);
indexParams.add(IndexParam.builder()
        .fieldName("binary_vector")
        .indexType(IndexParam.IndexType.BIN_IVF_FLAT)
        .metricType(IndexParam.MetricType.HAMMING)
        .extraParams(extraParams)
        .build());

import { MetricType, IndexType } from "@zilliz/milvus2-sdk-node";

const indexParams = {
  indexName: "binary_vector_index",
  field_name: "binary_vector",
  metric_type: MetricType.HAMMING,
  index_type: IndexType.BIN_IVF_FLAT,
  params: {
    nlist: 128,
  },
};

export indexParams='[
        {
            "fieldName": "binary_vector",
            "metricType": "HAMMING",
            "indexName": "binary_vector_index",
            "indexType": "BIN_IVF_FLAT",
            "params":{"nlist": 128}
        }
    ]'

위의 예에서는 BIN_IVF_FLAT 인덱스 유형을 사용하여 binary_vector 필드에 대해 binary_vector_index 라는 이름의 인덱스가 생성됩니다. metric_typeHAMMING 으로 설정되어 유사도 측정에 해밍 거리가 사용됨을 나타냅니다.

Milvus는 BIN_IVF_FLAT 외에도 바이너리 벡터에 대한 다른 인덱스 유형을 지원합니다. 자세한 내용은 바이너리 벡터 인덱스를 참조하세요. 또한 Milvus는 이진 벡터에 대한 다른 유사성 메트릭도 지원합니다. 자세한 내용은 메트릭 유형을 참조하세요.

컬렉션 만들기

바이너리 벡터 및 인덱스 설정이 완료되면 바이너리 벡터를 포함하는 컬렉션을 만듭니다. 아래 예제에서는 create_collection 메서드를 사용하여 my_binary_collection 이라는 이름의 컬렉션을 만듭니다.

client.create_collection(
    collection_name="my_binary_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_binary_collection")
        .collectionSchema(schema)
        .indexParams(indexParams)
        .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_dense_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_binary_collection\",
    \"schema\": $schema,
    \"indexParams\": $indexParams
}"

데이터 삽입

컬렉션을 만든 후 insert 메서드를 사용하여 바이너리 벡터가 포함된 데이터를 추가합니다. 바이너리 벡터는 바이트 배열의 형태로 제공해야 하며, 각 바이트는 8개의 부울 값을 나타냅니다.

예를 들어 128차원 바이너리 벡터의 경우 16바이트 배열이 필요합니다(128비트 ÷ 8비트/바이트 = 16바이트이므로). 다음은 데이터를 삽입하는 예제 코드입니다.

def convert_bool_list_to_bytes(bool_list):
    if len(bool_list) % 8 != 0:
        raise ValueError("The length of a boolean list must be a multiple of 8")

    byte_array = bytearray(len(bool_list) // 8)
    for i, bit in enumerate(bool_list):
        if bit == 1:
            index = i // 8
            shift = i % 8
            byte_array[index] |= (1 << shift)
    return bytes(byte_array)


bool_vectors = [
    [1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0] + [0] * 112,
    [0, 1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 1] + [0] * 112,
]

data = [{"binary_vector": convert_bool_list_to_bytes(bool_vector) for bool_vector in bool_vectors}]

client.insert(
    collection_name="my_binary_collection",
    data=data
)

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;

private static byte[] convertBoolArrayToBytes(boolean[] booleanArray) {
    byte[] byteArray = new byte[booleanArray.length / Byte.SIZE];
    for (int i = 0; i < booleanArray.length; i++) {
        if (booleanArray[i]) {
            int index = i / Byte.SIZE;
            int shift = i % Byte.SIZE;
            byteArray[index] |= (byte) (1 << shift);
        }
    }

    return byteArray;
}

List<JsonObject> rows = new ArrayList<>();
Gson gson = new Gson();
{
    boolean[] boolArray = {true, false, false, true, true, false, true, true, false, true, false, false, true, true, false, true};
    JsonObject row = new JsonObject();
    row.add("binary_vector", gson.toJsonTree(convertBoolArrayToBytes(boolArray)));
    rows.add(row);
}
{
    boolean[] boolArray = {false, true, false, true, false, true, false, false, true, true, false, false, true, true, false, true};
    JsonObject row = new JsonObject();
    row.add("binary_vector", gson.toJsonTree(convertBoolArrayToBytes(boolArray)));
    rows.add(row);
}

InsertResp insertR = client.insert(InsertReq.builder()
        .collectionName("my_binary_collection")
        .data(rows)
        .build());

const data = [
  { binary_vector: [1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1] },
  { binary_vector: [1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1] },
];

client.insert({
  collection_name: "my_binary_collection",
  data: data,
});

curl --request POST \
--url "${CLUSTER_ENDPOINT}/v2/vectordb/entities/insert" \
--header "Authorization: Bearer ${TOKEN}" \
--header "Content-Type: application/json" \
-d "{
    \"data\": $data,
    \"collectionName\": \"my_binary_collection\"
}"

유사도 검색은 밀버스의 핵심 기능 중 하나로, 벡터 사이의 거리를 기준으로 쿼리 벡터와 가장 유사한 데이터를 빠르게 찾을 수 있습니다. 이진 벡터를 사용하여 유사도 검색을 수행하려면 쿼리 벡터와 검색 매개변수를 준비한 다음 search 메서드를 호출합니다.

검색 작업 중에는 바이너리 벡터도 바이트 배열 형태로 제공해야 합니다. 쿼리 벡터의 차원이 dim 을 정의할 때 지정한 차원과 일치하는지, 8개의 부울 값이 모두 1바이트로 변환되는지 확인하세요.

search_params = {
    "params": {"nprobe": 10}
}

query_bool_list = [1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0] + [0] * 112
query_vector = convert_bool_list_to_bytes(query_bool_list)

res = client.search(
    collection_name="my_binary_collection",
    data=[query_vector],
    anns_field="binary_vector",
    search_params=search_params,
    limit=5,
    output_fields=["pk"]
)

print(res)

# Output
# data: ["[{'id': '453718927992172268', 'distance': 10.0, 'entity': {'pk': '453718927992172268'}}]"] 

import io.milvus.v2.service.vector.request.SearchReq;
import io.milvus.v2.service.vector.request.data.BinaryVec;
import io.milvus.v2.service.vector.response.SearchResp;

Map<String,Object> searchParams = new HashMap<>();
searchParams.put("nprobe",10);

boolean[] boolArray = {true, false, false, true, true, false, true, true, false, true, false, false, true, true, false, true};
BinaryVec queryVector = new BinaryVec(convertBoolArrayToBytes(boolArray));

SearchResp searchR = client.search(SearchReq.builder()
        .collectionName("my_binary_collection")
        .data(Collections.singletonList(queryVector))
        .annsField("binary_vector")
        .searchParams(searchParams)
        .topK(5)
        .outputFields(Collections.singletonList("pk"))
        .build());
        
 System.out.println(searchR.getSearchResults());
 
 // Output
 //
 // [[SearchResp.SearchResult(entity={pk=453444327741536775}, score=0.0, id=453444327741536775), SearchResp.SearchResult(entity={pk=453444327741536776}, score=7.0, id=453444327741536776)]]

query_vector = [1,0,1,0,1,1,1,1,1,1,1,1];

client.search({
    collection_name: 'my_binary_collection',
    data: query_vector,
    limit: 5,
    output_fields: ['pk'],
    params: {
        nprobe: 10
    }
});

export searchParams='{
        "params":{"nprobe":10}
    }'

curl --request POST \
--url "${CLUSTER_ENDPOINT}/v2/vectordb/entities/search" \
--header "Authorization: Bearer ${TOKEN}" \
--header "Content-Type: application/json" \
-d "{
    \"collectionName\": \"my_binary_collection\",
    \"data\": $data,
    \"annsField\": \"binary_vector\",
    \"limit\": 5,
    \"searchParams\":$searchParams,
    \"outputFields\": [\"pk\"]
}"

유사도 검색 매개변수에 대한 자세한 내용은 기본 ANN 검색을 참조하세요.

번역DeepL

Try Managed Milvus for Free

Zilliz Cloud is hassle-free, powered by Milvus and 10x faster.

Get Started
피드백

이 페이지가 도움이 되었나요?