Vektor Jarang
Vektor jarang adalah metode penting untuk menangkap pencocokan istilah tingkat permukaan dalam pencarian informasi dan pemrosesan bahasa alami. Meskipun vektor padat lebih unggul dalam pemahaman semantik, vektor jarang sering kali memberikan hasil pencocokan yang lebih dapat diprediksi, terutama saat mencari istilah khusus atau pengidentifikasi tekstual.
Gambaran Umum
Vektor jarang adalah vektor berdimensi tinggi khusus yang sebagian besar elemennya bernilai nol, dan hanya beberapa dimensi yang memiliki nilai bukan nol. Seperti yang ditunjukkan pada diagram di bawah ini, vektor padat biasanya direpresentasikan sebagai larik kontinu di mana setiap posisi memiliki nilai (misalnya, [0.3, 0.8, 0.2, 0.3, 0.1]). Sebaliknya, vektor jarang hanya menyimpan elemen non-nol dan indeks dimensinya, yang sering direpresentasikan sebagai pasangan kunci-nilai dari { index: value} (misalnya, [{2: 0.2}, ..., {9997: 0.5}, {9999: 0.7}]).
Representasi Vektor Jarang
Dengan tokenisasi dan penilaian, dokumen dapat direpresentasikan sebagai vektor kantong kata, di mana setiap dimensi berhubungan dengan kata tertentu dalam kosakata. Hanya kata-kata yang ada di dalam dokumen yang memiliki nilai bukan nol, sehingga menciptakan representasi vektor yang jarang. Vektor jarang dapat dihasilkan dengan menggunakan dua pendekatan:
Teknik statistik tradisional, seperti TF-IDF (Term Frequency-Inverse Document Frequency) dan BM25 (Best Matching 25), memberikan bobot pada kata-kata berdasarkan frekuensi dan kepentingannya dalam sebuah korpus. Metode-metode ini menghitung statistik sederhana sebagai skor untuk setiap dimensi, yang mewakili sebuah token. Milvus menyediakan pencarian teks lengkap dengan metode BM25, yang secara otomatis mengubah teks menjadi vektor yang jarang, sehingga tidak memerlukan prapemrosesan manual. Pendekatan ini ideal untuk pencarian berbasis kata kunci, di mana ketepatan dan kecocokan yang tepat adalah penting. Lihat Pencarian Teks Lengkap untuk informasi lebih lanjut.
Model penyematan jarang saraf adalah metode yang dipelajari untuk menghasilkan representasi jarang dengan melatih set data yang besar. Model ini biasanya merupakan model pembelajaran mendalam dengan arsitektur Transformer, yang mampu memperluas dan menimbang istilah berdasarkan konteks semantik. Milvus juga mendukung sematan jarang yang dihasilkan secara eksternal dari model seperti SPLADE. Lihat Penyematan untuk detailnya.
Vektor-vektor jarang dan teks asli dapat disimpan di Milvus untuk pengambilan yang efisien. Diagram di bawah ini menguraikan keseluruhan proses.
Alur Kerja Vektor Jarang
Selain vektor jarang, Milvus juga mendukung vektor padat dan vektor biner. Vektor padat ideal untuk menangkap hubungan semantik yang dalam, sementara vektor biner unggul dalam skenario seperti perbandingan kemiripan cepat dan deduplikasi konten. Untuk informasi lebih lanjut, lihat Vektor Padat dan Vektor Biner.
Format Data
Pada bagian berikut, kami mendemonstrasikan cara menyimpan vektor dari model penyematan jarang yang telah dipelajari seperti SPLADE. Jika Anda mencari sesuatu untuk melengkapi pencarian semantik berbasis vektor padat, kami merekomendasikan Pencarian Teks Lengkap dengan BM25 daripada SPLADE untuk kemudahan. Jika Anda telah melakukan evaluasi kualitas dan berdedikasi untuk menggunakan SPLADE, Anda dapat merujuk ke Penyematan tentang cara menghasilkan vektor jarang dengan SPLADE.
Milvus mendukung input vektor jarang dengan format berikut:
Daftar Kamus (diformat sebagai
{dimension_index: value, ...})# Represent each sparse vector using a dictionary sparse_vectors = [{27: 0.5, 100: 0.3, 5369: 0.6} , {100: 0.1, 3: 0.8}]Matriks jarang (menggunakan kelas
scipy.sparse)from scipy.sparse import csr_matrix # First vector: indices [27, 100, 5369] with values [0.5, 0.3, 0.6] # Second vector: indices [3, 100] with values [0.8, 0.1] indices = [[27, 100, 5369], [3, 100]] values = [[0.5, 0.3, 0.6], [0.8, 0.1]] sparse_vectors = [csr_matrix((vals, ([0]*len(idx), idx)), shape=(1, 5369+1)) for idx, vals in zip(indices, values)]Daftar Tuple Iterables (misalnya
[(dimension_index, value)])# Represent each sparse vector using a list of iterables (e.g. tuples) sparse_vector = [ [(27, 0.5), (100, 0.3), (5369, 0.6)], [(100, 0.1), (3, 0.8)] ]
Menentukan Skema Koleksi
Sebelum membuat koleksi, Anda perlu menentukan skema koleksi, yang mendefinisikan bidang dan secara opsional fungsi untuk mengubah bidang teks menjadi representasi vektor jarang yang sesuai.
Menambahkan bidang
Untuk menggunakan vektor jarang di Milvus, Anda perlu membuat koleksi dengan skema yang mencakup bidang-bidang berikut:
Bidang
SPARSE_FLOAT_VECTORyang disediakan untuk menyimpan vektor jarang, baik yang dibuat secara otomatis dari bidangVARCHARatau disediakan secara langsung di dalam data masukan.Biasanya, teks mentah yang diwakili oleh vektor jarang juga disimpan dalam koleksi. Anda dapat menggunakan bidang
VARCHARuntuk menyimpan teks mentah.
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="sparse_vector", datatype=DataType.SPARSE_FLOAT_VECTOR)
schema.add_field(field_name="text", datatype=DataType.VARCHAR, max_length=65535, enable_analyzer=True)
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());
schema.addField(AddFieldReq.builder()
.fieldName("text")
.dataType(DataType.VarChar)
.maxLength(65535)
.enableAnalyzer(true)
.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,
},
{
name: "text",
data_type: "VarChar",
enable_analyzer: true,
enable_match: true,
max_length: 65535,
},
];
import (
"context"
"fmt"
"github.com/milvus-io/milvus/client/v2/column"
"github.com/milvus-io/milvus/client/v2/entity"
"github.com/milvus-io/milvus/client/v2/index"
"github.com/milvus-io/milvus/client/v2/milvusclient"
)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
milvusAddr := "localhost:19530"
client, err := milvusclient.New(ctx, &milvusclient.ClientConfig{
Address: milvusAddr,
})
if err != nil {
fmt.Println(err.Error())
// handle error
}
defer client.Close(ctx)
schema := entity.NewSchema()
schema.WithField(entity.NewField().
WithName("pk").
WithDataType(entity.FieldTypeVarChar).
WithIsAutoID(true).
WithIsPrimaryKey(true).
WithMaxLength(100),
).WithField(entity.NewField().
WithName("sparse_vector").
WithDataType(entity.FieldTypeSparseVector),
).WithField(entity.NewField().
WithName("text").
WithDataType(entity.FieldTypeVarChar).
WithEnableAnalyzer(true).
WithMaxLength(65535),
)
export primaryField='{
"fieldName": "pk",
"dataType": "VarChar",
"isPrimary": true,
"elementTypeParams": {
"max_length": 100
}
}'
export vectorField='{
"fieldName": "sparse_vector",
"dataType": "SparseFloatVector"
}'
export textField='{
"fieldName": "text",
"dataType": "VarChar",
"elementTypeParams": {
"max_length": 65535,
"enable_analyzer": true
}
}'
export schema="{
\"autoID\": true,
\"fields\": [
$primaryField,
$vectorField,
$textField
]
}"
Dalam contoh ini, tiga bidang ditambahkan:
pk: Bidang ini menyimpan kunci utama menggunakan tipe dataVARCHAR, yang dibuat secara otomatis dengan panjang maksimum 100 byte.sparse_vector: Bidang ini menyimpan vektor jarang menggunakan tipe dataSPARSE_FLOAT_VECTOR.text: Bidang ini menyimpan string teks menggunakan tipe dataVARCHAR, dengan panjang maksimum 65535 byte.
Untuk mengaktifkan Milvus atau untuk menghasilkan penyematan vektor jarang dari bidang teks yang ditentukan selama penyisipan data, langkah tambahan yang melibatkan sebuah fungsi harus dilakukan. Untuk informasi lebih lanjut, silakan lihat Pencarian Teks Lengkap.
Mengatur Parameter Indeks
Proses pembuatan indeks untuk vektor jarang mirip dengan proses pembuatan indeks untuk vektor padat, tetapi dengan perbedaan pada jenis indeks yang ditentukan (index_type), metrik jarak (metric_type), dan parameter indeks (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={"inverted_index_algo": "DAAT_MAXSCORE"}, # or "DAAT_WAND" or "TAAT_NAIVE"
)
import io.milvus.v2.common.IndexParam;
import java.util.*;
List<IndexParam> indexes = new ArrayList<>();
Map<String,Object> extraParams = new HashMap<>();
extraParams.put("inverted_index_algo": "DAAT_MAXSCORE"); // Algorithm used for building and querying the index
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({
field_name: 'sparse_vector',
metric_type: MetricType.IP,
index_name: 'sparse_inverted_index',
index_type: IndexType.SPARSE_INVERTED_INDEX,
params: {
inverted_index_algo: 'DAAT_MAXSCORE',
},
});
idx := index.NewSparseInvertedIndex(entity.IP, 0.2)
indexOption := milvusclient.NewCreateIndexOption("my_collection", "sparse_vector", idx)
export indexParams='[
{
"fieldName": "sparse_vector",
"metricType": "IP",
"indexName": "sparse_inverted_index",
"indexType": "SPARSE_INVERTED_INDEX",
"params":{"inverted_index_algo": "DAAT_MAXSCORE"}
}
]'
Contoh ini menggunakan tipe indeks SPARSE_INVERTED_INDEX dengan IP sebagai metriknya. Untuk lebih jelasnya, lihat sumber-sumber berikut ini:
SPARSE_INVERTED_INDEX: Penjelasan indeks dan parameternya
Jenis Metrik: Jenis metrik yang didukung untuk berbagai jenis bidang
Pencarian Teks Lengkap: Tutorial mendetail tentang pencarian teks lengkap
Membuat Koleksi
Setelah pengaturan vektor jarang dan indeks selesai, Anda dapat membuat koleksi yang berisi vektor jarang. Contoh di bawah ini menggunakan metode create_collection untuk membuat koleksi bernama my_collection.
client.create_collection(
collection_name="my_collection",
schema=schema,
index_params=index_params
)
CreateCollectionReq requestCreate = CreateCollectionReq.builder()
.collectionName("my_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_collection',
schema: schema,
index_params: indexParams
});
err = client.CreateCollection(ctx,
milvusclient.NewCreateCollectionOption("my_collection", schema).
WithIndexOptions(indexOption))
if err != nil {
fmt.Println(err.Error())
// handle error
}
curl --request POST \
--url "${CLUSTER_ENDPOINT}/v2/vectordb/collections/create" \
--header "Authorization: Bearer ${TOKEN}" \
--header "Content-Type: application/json" \
--header "Request-Timeout: 10" \
-d "{
\"collectionName\": \"my_collection\",
\"schema\": $schema,
\"indexParams\": $indexParams
}"
Memasukkan data
Anda harus menyediakan data untuk semua bidang yang ditentukan selama pembuatan koleksi, kecuali untuk bidang yang dibuat secara otomatis (seperti kunci utama dengan auto_id yang diaktifkan). Jika Anda menggunakan fungsi BM25 bawaan untuk menghasilkan vektor jarang secara otomatis, Anda juga harus menghilangkan bidang vektor jarang saat memasukkan data.
data = [
{
"text": "information retrieval is a field of study.",
"sparse_vector": {1: 0.5, 100: 0.3, 500: 0.8}
},
{
"text": "information retrieval focuses on finding relevant information in large datasets.",
"sparse_vector": {10: 0.1, 200: 0.7, 1000: 0.9}
}
]
client.insert(
collection_name="my_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;
import java.util.ArrayList;
import java.util.List;
import java.util.SortedMap;
import java.util.TreeMap;
Gson gson = new Gson();
List<JsonObject> rows = new ArrayList<>();
{
JsonObject row = new JsonObject();
row.addProperty("text", "information retrieval is a field of study.");
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();
row.addProperty("text", "information retrieval focuses on finding relevant information in large datasets.");
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 insertResp = client.insert(InsertReq.builder()
.collectionName("my_collection")
.data(rows)
.build());
const data = [
{
text: 'information retrieval is a field of study.',
sparse_vector: {1: 0.5, 100: 0.3, 500: 0.8}
{
text: 'information retrieval focuses on finding relevant information in large datasets.',
sparse_vector: {10: 0.1, 200: 0.7, 1000: 0.9}
},
];
client.insert({
collection_name: "my_collection",
data: data
});
texts := []string{
"information retrieval is a field of study.",
"information retrieval focuses on finding relevant information in large datasets.",
}
textColumn := entity.NewColumnVarChar("text", texts)
// Prepare sparse vectors
sparseVectors := make([]entity.SparseEmbedding, 0, 2)
sparseVector1, _ := entity.NewSliceSparseEmbedding([]uint32{1, 100, 500}, []float32{0.5, 0.3, 0.8})
sparseVectors = append(sparseVectors, sparseVector1)
sparseVector2, _ := entity.NewSliceSparseEmbedding([]uint32{10, 200, 1000}, []float32{0.1, 0.7, 0.9})
sparseVectors = append(sparseVectors, sparseVector2)
sparseVectorColumn := entity.NewColumnSparseVectors("sparse_vector", sparseVectors)
_, err = client.Insert(ctx, milvusclient.NewColumnBasedInsertOption("my_collection").
WithColumns(
sparseVectorColumn,
textColumn
))
if err != nil {
fmt.Println(err.Error())
// handle err
}
curl --request POST \
--url "${CLUSTER_ENDPOINT}/v2/vectordb/entities/insert" \
--header "Authorization: Bearer ${TOKEN}" \
--header "Content-Type: application/json" \
--header "Request-Timeout: 10" \
-d '{
"data": [
{
"text": "information retrieval is a field of study.",
"sparse_vector": {"1": 0.5, "100": 0.3, "500": 0.8}
},
{
"text": "information retrieval focuses on finding relevant information in large datasets.",
"sparse_vector": {"10": 0.1, "200": 0.7, "1000": 0.9}
}
],
"collectionName": "my_collection"
}'
Melakukan Pencarian Kemiripan
Untuk melakukan pencarian kemiripan menggunakan vektor jarang, siapkan data kueri dan parameter pencarian.
# Prepare search parameters
search_params = {
"params": {"drop_ratio_search": 0.2}, # A tunable drop ratio parameter with a valid range between 0 and 1
}
# Query with sparse vector
query_data = [{1: 0.2, 50: 0.4, 1000: 0.7}]
import io.milvus.v2.service.vector.request.data.EmbeddedText;
import io.milvus.v2.service.vector.request.data.SparseFloatVec;
// Prepare search parameters
Map<String,Object> searchParams = new HashMap<>();
searchParams.put("drop_ratio_search", 0.2);
// Query with the sparse vector
SortedMap<Long, Float> sparse = new TreeMap<>();
sparse.put(1L, 0.2f);
sparse.put(50L, 0.4f);
sparse.put(1000L, 0.7f);
SparseFloatVec queryData = new SparseFloatVec(sparse);
// Prepare search parameters
annSearchParams := index.NewCustomAnnParam()
annSearchParams.WithExtraParam("drop_ratio_search", 0.2)
// Query with the sparse vector
queryData, _ := entity.NewSliceSparseEmbedding([]uint32{1, 50, 1000}, []float32{0.2, 0.4, 0.7})
// Prepare search parameters
const searchParams = {drop_ratio_search: 0.2}
// Query with the sparse vector
const queryData = [{1: 0.2, 50: 0.4, 1000: 0.7}]
# Prepare search parameters
export queryData='["What is information retrieval?"]'
# Query with the sparse vector
export queryData='[{1: 0.2, 50: 0.4, 1000: 0.7}]'
Kemudian, jalankan pencarian kemiripan menggunakan metode search:
res = client.search(
collection_name="my_collection",
data=query_data,
limit=3,
output_fields=["pk"],
search_params=search_params,
consistency_level="Strong"
)
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.response.SearchResp;
SparseFloatVec queryVector = new SparseFloatVec(sparse);
SearchResp searchR = client.search(SearchReq.builder()
.collectionName("my_collection")
.data(Collections.singletonList(queryData))
.annsField("sparse_vector")
.searchParams(searchParams)
.consistencyLevel(ConsistencyLevel.STRONG)
.topK(3)
.outputFields(Collections.singletonList("pk"))
.build());
System.out.println(searchR.getSearchResults());
// Output
//
// [[SearchResp.SearchResult(entity={pk=457270974427187729}, score=0.63, id=457270974427187729), SearchResp.SearchResult(entity={pk=457270974427187728}, score=0.1, id=457270974427187728)]]
await client.search({
collection_name: 'my_collection',
data: queryData,
limit: 3,
output_fields: ['pk'],
params: searchParams,
consistency_level: "Strong"
});
resultSets, err := client.Search(ctx, milvusclient.NewSearchOption(
"my_collection",
3, // limit
[]entity.Vector{queryData},
).WithANNSField("sparse_vector").
WithOutputFields("pk").
WithAnnParam(annSearchParams))
if err != nil {
fmt.Println(err.Error())
// handle err
}
for _, resultSet := range resultSets {
fmt.Println("IDs: ", resultSet.IDs.FieldData().GetScalars())
fmt.Println("Scores: ", resultSet.Scores)
fmt.Println("Pks: ", resultSet.GetColumn("pk").FieldData().GetScalars())
}
// Results:
// IDs: string_data:{data:"457270974427187705" data:"457270974427187704"}
// Scores: [0.63 0.1]
// Pks: string_data:{data:"457270974427187705" data:"457270974427187704"}
export params='{
"consistencyLevel": "Strong"
}'
curl --request POST \
--url "${CLUSTER_ENDPOINT}/v2/vectordb/entities/search" \
--header "Authorization: Bearer ${TOKEN}" \
--header "Content-Type: application/json" \
--header "Request-Timeout: 10" \
-d '{
"collectionName": "my_collection",
"data": $queryData,
"annsField": "sparse_vector",
"limit": 3,
"searchParams": $searchParams,
"outputFields": ["pk"],
"params": $params
}'
## {"code":0,"cost":0,"data":[{"distance":0.63,"id":"453577185629572535","pk":"453577185629572535"},{"distance":0.1,"id":"453577185629572534","pk":"453577185629572534"}]}
Untuk informasi lebih lanjut tentang parameter pencarian kemiripan, lihat Pencarian Vektor Dasar.