Pencarian dengan Menyematkan Daftar
Halaman ini menjelaskan cara menyiapkan sistem pengambilan teks ColBERT dan sistem pengambilan teks ColPali menggunakan larik struktur di Milvus, yang memungkinkan Anda untuk menyimpan dokumen beserta potongan vektornya dalam daftar penyematan.
Gambaran Umum
Untuk membangun sistem pencarian teks, Anda mungkin perlu membagi dokumen menjadi beberapa bagian dan menyimpan setiap bagian bersama dengan penyematannya sebagai entitas dalam basis data vektor untuk memastikan ketepatan dan keakuratan, terutama untuk dokumen yang panjang di mana penyematan teks lengkap dapat mengurangi kekhususan semantik atau melebihi batas masukan model.
Namun, menyimpan data dalam potongan-potongan akan menghasilkan hasil pencarian yang bersifat chunk-wise, yang berarti bahwa pencarian pada awalnya mengidentifikasi segmen-segmen yang relevan, bukan dokumen yang kohesif. Untuk mengatasi hal ini, Anda harus melakukan pemrosesan pasca-pencarian tambahan.
ColBERT (arXiv: 2004.12832) adalah sistem pencarian teks yang menawarkan pencarian bagian yang efisien dan efektif melalui interaksi akhir yang dikontekstualisasikan melalui BERT. Sistem ini memungkinkan pengkodean token-bijaksana yang independen untuk kueri dan dokumen dan menghitung kemiripannya.
Pengkodean dengan token-bijaksana
Selama pemasukan data di ColBERT, setiap dokumen dipecah menjadi token, yang kemudian divektorisasi dan disimpan sebagai daftar sematan, seperti pada d , , , ] . Ketika sebuah kueri tiba, kueri tersebut juga diberi token, divektor, dan disimpan sebagai sebuah daftar penyisipan, seperti pada q , , , ] .
Dalam rumus di atas,
d: sebuah dokumen
q: kueri
E: daftar sematan yang merepresentasikan dokumen.
E: daftar sematan yang merepresentasikan kueri.
,,,]: jumlah sematan vektor dalam daftar sematan yang mewakili dokumen berada dalam rentang R .
,,,]: jumlah sematan vektor dalam daftar sematan yang mewakili kueri berada dalam rentang R .
Interaksi akhir
Setelah vektorisasi selesai, daftar sematan kueri dibandingkan dengan daftar sematan dokumen, token demi token, untuk menentukan skor kesamaan akhir.
Interaksi Akhir
Seperti yang ditunjukkan pada diagram di atas, kueri berisi dua token, yaitu machine dan learning, dan dokumen di jendela memiliki empat token: neural, network, python, dan tutorial. Setelah token-token ini dibuat vektornya, vektor dari setiap token kueri dibandingkan dengan token-token yang ada di dalam dokumen untuk mendapatkan daftar skor kemiripan. Kemudian nilai tertinggi dari setiap daftar nilai dijumlahkan untuk menghasilkan nilai akhir. Proses untuk menentukan skor akhir dokumen dikenal sebagai kemiripan maksimum(MAX_SIM). Untuk detail tentang kemiripan maksimum, lihat Kemiripan maksimum.
Ketika mengimplementasikan sistem pengambilan teks seperti ColBERT di Milvus, Anda tidak dibatasi untuk membagi dokumen menjadi token.
Sebaliknya, Anda dapat membagi dokumen menjadi beberapa segmen dengan ukuran yang sesuai, menyematkan setiap segmen untuk membuat daftar penyematan, dan menyimpan dokumen bersama dengan segmen yang disematkan dalam sebuah entitas.
Ekstensi ColPali
Berdasarkan ColBERT, ColPali (arXiv: 2407.01449) mengusulkan pendekatan baru untuk pengambilan dokumen yang kaya secara visual yang memanfaatkan Vision-Language Models (VLM). Selama pengambilan data, setiap halaman dokumen dirender menjadi gambar beresolusi tinggi, kemudian dipecah menjadi beberapa bagian, bukannya diberi tanda. Sebagai contoh, gambar halaman dokumen berukuran 448 x 448 piksel dapat menghasilkan 1.024 tambalan, masing-masing berukuran 14 x 14 piksel.
Metode ini mempertahankan informasi non-tekstual, seperti tata letak dokumen, gambar, dan struktur tabel, yang hilang ketika menggunakan sistem pengambilan teks saja.
Ekstensi Copali
VLM yang digunakan dalam ColPali disebut PaliGemma (arXiv: 2407.07726), yang terdiri dari penyandi gambar(SigLIP-400M), model bahasa khusus dekoder(Gemma2-2B), dan lapisan linier yang memproyeksikan keluaran penyandi gambar ke dalam ruang vektor model bahasa, seperti yang ditunjukkan pada diagram di atas.
Selama konsumsi data, halaman dokumen, yang direpresentasikan sebagai gambar mentah, dibagi menjadi beberapa tambalan visual, yang masing-masing disematkan untuk menghasilkan daftar penyematan vektor. Kemudian mereka diproyeksikan ke dalam ruang vektor model bahasa untuk mendapatkan daftar penyematan akhir, seperti pada d , , , ] . Ketika sebuah kueri tiba, kueri tersebut diberi token, dan setiap token disematkan untuk menghasilkan sebuah daftar penyematan vektor, seperti pada q , , , ] . Kemudian, MAX_SIM telah diterapkan untuk membandingkan dua daftar sematan dan mendapatkan nilai akhir antara kueri dan halaman dokumen.
Sistem pengambilan teks ColBERT
Pada bagian ini, kita akan menyiapkan sistem pencarian teks ColBERT menggunakan Array of Structs dari Milvus. Sebelum itu, siapkan cluster Milvus v2.6.x instanceZilliz Cloud yang kompatibel dengan Milvus v2.6.x, dapatkan token akses Cohere.
Langkah 1: Instal dependensi
Jalankan perintah berikut untuk menginstal dependensi.
pip install --upgrade huggingface-hub transformers datasets pymilvus cohere
Langkah 2: Muat kumpulan data Cohere
Dalam contoh ini, kita akan menggunakan dataset Wikipedia Cohere dan mengambil 10.000 catatan pertama. Anda dapat menemukan informasi tentang dataset ini di halaman ini.
from datasets import load_dataset
lang = "simple"
docs = load_dataset(
"Cohere/wikipedia-2023-11-embed-multilingual-v3",
lang,
split="train[:10000]"
)
Menjalankan skrip di atas akan mengunduh dataset jika tidak tersedia secara lokal. Setiap catatan dalam dataset ini adalah sebuah paragraf dari halaman Wikipedia. Tabel berikut menunjukkan struktur dataset ini.
Nama Kolom |
Deskripsi |
|---|---|
|
ID rekaman |
|
URL dari catatan saat ini. |
|
Judul dokumen sumber. |
|
Paragraf dari dokumen sumber. |
|
Penyematan teks dari dokumen sumber. |
Langkah 3: Kelompokkan paragraf berdasarkan judul
Untuk mencari dokumen daripada paragraf, kita harus mengelompokkan paragraf berdasarkan judul.
df = docs.to_pandas()
groups = df.groupby('title')
data = []
for title, group in groups:
data.append({
"title": title,
"paragraphs": [{
"text": row['text'],
'emb': row['emb']
} for _, row in group.iterrows()]
})
Dalam kode ini, kita menyimpan paragraf yang dikelompokkan sebagai dokumen dan memasukkannya ke dalam daftar data. Setiap dokumen memiliki kunci paragraphs, yang merupakan daftar paragraf; setiap objek paragraf berisi kunci text dan emb.
Langkah 4: Membuat koleksi untuk kumpulan data Cohere
Setelah data siap, kita akan membuat koleksi. Di dalam koleksi tersebut, terdapat sebuah field bernama paragraphs, yang merupakan sebuah Array of Structs.
from pymilvus import MilvusClient, DataType
client = MilvusClient(
uri="http://localhost:19530",
token="root:Milvus"
)
# Create collection schema
schema = client.create_schema()
schema.add_field('id', DataType.INT64, is_primary=True, auto_id=True)
schema.add_field('title', DataType.VARCHAR, max_length=512)
# Create struct schema
struct_schema = client.create_struct_field_schema()
struct_schema.add_field('text', DataType.VARCHAR, max_length=65535)
struct_schema.add_field('emb', DataType.FLOAT_VECTOR, dim=512)
schema.add_field('paragraphs', DataType.ARRAY,
element_type=DataType.STRUCT,
struct_schema=struct_schema, max_capacity=200)
# Create index parameters
index_params = client.prepare_index_params()
index_params.add_index(
field_name="paragraphs[emb]",
index_type="AUTOINDEX",
metric_type="MAX_SIM_COSINE"
)
# Create a collection
client.create_collection(
collection_name='wiki_documents',
schema=schema,
index_params=index_params
)
Langkah 5: Masukkan kumpulan data Cohere ke dalam koleksi
Sekarang kita dapat memasukkan data yang telah disiapkan ke dalam koleksi yang telah kita buat di atas.
client.insert(
collection_name='wiki_documents',
data=data
)
Langkah 6: Cari di dalam kumpulan data Cohere
Menurut desain ColBERT, teks kueri harus diberi token dan kemudian disematkan ke dalam EmbeddingList. Pada langkah ini, kita akan menggunakan model yang sama dengan yang digunakan Cohere untuk menghasilkan sematan untuk paragraf dalam dataset Wikipedia.
import cohere
co = cohere.ClientV2("COHERE_API_KEY")
query_inputs = [
{
'content': [
{'type': 'text', 'text': 'Adobe'},
]
},
{
'content': [
{'type': 'text', 'text': 'software'}
]
}
]
embeddings = co.embed(
inputs=query_inputs,
model='embed-multilingual-v3.0',
input_type="classification",
embedding_types=["float"],
)
Di dalam kode, teks-teks kueri disusun menjadi token-token di query_inputs dan disematkan ke dalam sebuah daftar vektor float. Kemudian Anda dapat menggunakan EmbeddingList dari Milvus untuk melakukan pencarian kemiripan sebagai berikut.
from pymilvus.client.embedding_list import EmbeddingList
query_emb_list = EmbeddingList()
if (embeddings.embeddings.float):
query_emb_list.add_batch(embeddings.embeddings.float)
results = client.search(
collection_name="wiki_documents",
data=[query_emb_list],
anns_field="paragraphs[emb]",
search_params={
"metric_type": "MAX_SIM_COSINE"
},
limit=10,
output_fields=["title"]
)
for hit in results[0]:
print(f"Document {hit['entity']['title']}: {hit['distance']:.4f}")
Keluaran dari kode di atas mirip dengan yang berikut ini:
# Document Software: 2.3035
# Document Application: 2.1875
# Document Adobe Illustrator: 2.1167
# Document Open source: 2.0542
# Document Computer: 1.9811
# Document Microsoft: 1.9784
# Document Web browser: 1.9655
# Document Program: 1.9627
# Document Website: 1.9594
# Document Computer science: 1.9460
Skor kemiripan kosinus berkisar antara -1 hingga 1, dan skor kemiripan pada keluaran di atas dengan jelas menunjukkan jumlah skor kemiripan beberapa tingkat token.
Sistem pengambilan teks ColPali
Pada bagian ini, kita akan menyiapkan sistem pencarian teks berbasis ColPali menggunakan Array of Structs dari Milvus. Sebelum itu, siapkan cluster Milvus v2.6.x instanceZilliz Cloud yang kompatibel dengan Milvus v2.6.x.
Langkah 1: Instal dependensi
pip install --upgrade huggingface-hub transformers datasets pymilvus 'colpali-engine>=0.3.0,<0.4.0'
Langkah 2: Muat dataset Vidore
Pada bagian ini, kita akan menggunakan dataset Vidore bernama vidore_v2_finance_en. Dataset ini adalah korpus laporan tahunan dari sektor perbankan, yang ditujukan untuk tugas pemahaman dokumen panjang. Ini adalah salah satu dari 10 korpus yang terdiri dari Benchmark ViDoRe v3. Anda dapat menemukan detail tentang dataset ini di halaman ini.
from datasets import load_dataset
ds = load_dataset("vidore/vidore_v3_finance_en", "corpus")
df = ds['test'].to_pandas()
Menjalankan skrip di atas akan mengunduh dataset jika tidak tersedia secara lokal. Setiap catatan dalam dataset adalah halaman dari laporan keuangan. Tabel berikut ini menunjukkan struktur dari dataset ini.
Nama Kolom |
Deskripsi |
|---|---|
|
Catatan dalam korpus |
|
Gambar halaman dalam byte. |
|
ID dokumen deskriptif. |
|
Nomor halaman dari halaman saat ini dalam dokumen. |
Langkah 3: Hasilkan penyematan untuk gambar halaman
Seperti yang diilustrasikan di bagian Ikhtisar, model ColPali adalah VLM yang memproyeksikan gambar ke dalam ruang vektor model teks. Pada langkah ini, kita akan menggunakan model ColPali terbaru vidore/colpali-v1.3. Anda dapat menemukan detail tentang model ini di halaman ini.
import torch
from typing import cast
from colpali_engine.models import ColPali, ColPaliProcessor
model_name = "vidore/colpali-v1.3"
model = ColPali.from_pretrained(
model_name,
torch_dtype=torch.bfloat16,
device_map="cuda:0", # or "mps" if on Apple Silicon
).eval()
processor = ColPaliProcessor.from_pretrained(model_name)
Setelah modelnya siap, Anda dapat mencoba membuat tambalan untuk gambar tertentu sebagai berikut.
from PIL import Image
from io import BytesIO
# Use the iterrow() generator to get the first row
row = next(df.iterrows())[1]
# Include the image in the above row in a list
images = [ Image.open(row['image']['bytes'] ]
patches = processor.process_images(images).to(model.device)
patches_embeddings = model(**patches_in_pixels)[0]
# Check the shape of the embeddings generated for the patches
print(patches_embeddings.shape)
# [1031, 128]
Pada kode di atas, model ColPali mengubah ukuran gambar menjadi 448 x 448 piksel, kemudian membaginya menjadi patch, masing-masing berukuran 14 x 14 piksel. Terakhir, patch-patch ini disematkan ke dalam 1.031 embedding, masing-masing dengan 128 dimensi.
Anda bisa menghasilkan embedding untuk semua gambar dengan menggunakan perulangan sebagai berikut:
data = []
for index, row in df.iterrows():
row = next(df.iterrows())[1]
corpus_id = row['corpus_id']
images = [Image.open(BytesIO(row['image']['bytes']))]
batch_images = processor.process_images(images).to(model.device)
patches = model(**batch_images)[0]
doc_id = row['doc_id']
markdown = row['markdown']
page_number_in_doc = row['page_number_in_doc']
data.append({
"corpus_id": corpus_id,
"patches": [ {"emb": emb} for emb in patches ],
"doc_id": markdown,
"page_number_in_doc": row['page_number_in_doc']
})
Langkah ini relatif memakan waktu karena banyaknya data yang perlu disematkan.
Langkah 4: Buat koleksi untuk kumpulan data laporan keuangan
Setelah data siap, kita akan membuat sebuah koleksi. Di dalam koleksi tersebut, sebuah field bernama patches adalah sebuah Array of Structs.
from pymilvus import MilvusClient, DataType
client = MilvusClient(
uri=YOUR_CLUSTER_ENDPOINT,
token=YOUR_API_KEY
)
schema = client.create_schema()
schema.add_field(
field_name="corpus_id",
datatype=DataType.INT64,
is_primary=True
)
patch_schema = client.create_struct_field_schema()
patch_schema.add_field(
field_name="emb",
datatype=DataType.FLOAT_VECTOR,
dim=128
)
schema.add_field(
field_name="patches",
datatype=DataType.ARRAY,
element_type=DataType.STRUCT,
struct_schema=patch_schema,
max_capacity=1031
)
schema.add_field(
field_name="doc_id",
datatype=DataType.VARCHAR,
max_length=512
)
schema.add_field(
field_name="page_number_in_doc",
datatype=DataType.INT64
)
index_params = client.prepare_index_params()
index_params.add_index(
field_name="patches[emb]",
index_type="AUTOINDEX",
metric_type="MAX_SIM_COSINE"
)
client.create_collection(
collection_name="financial_reports",
schema=schema,
index_params=index_params
)
Langkah 5: Masukkan laporan keuangan ke dalam koleksi
Sekarang kita dapat memasukkan laporan keuangan yang telah disiapkan ke dalam koleksi.
client.insert(
collection_name="financial_reports",
data=data
)
Dari output, Anda dapat menemukan bahwa semua halaman dari kumpulan data Vidore telah dimasukkan.
Langkah 6: Cari di dalam laporan keuangan
Setelah data siap, kita dapat melakukan pencarian terhadap data di dalam koleksi sebagai berikut:
from pymilvus.client.embedding_list import EmbeddingList
queries = [
"quarterly revenue growth chart"
]
batch_queries = processor.process_queries(queries).to(model.device)
with torch.no_grad():
query_embeddings = model(**batch_queries)
query_emb_list = EmbeddingList()
query_emb_list.add_batch(query_embeddings[0].cpu())
results = client.search(
collection_name="financial_reports",
data=[query_emb_list],
anns_field="patches[emb]",
search_params={
"metric_type": "MAX_SIM_COSINE"
},
limit=10,
output_fields=["doc_id", "page_number_in_doc"]
)