Model embedding yang digunakan hari ini bukan pilihan seumur hidup koleksi, dan pergantian model adalah operasi yang harus direncanakan sebagaimana migration pada skema basis data. Topik ini membahas mengapa vektor dari dua model tidak dapat dicampur, bagaimana dua set vektor disiapkan selama masa peralihan pada pgvector, Qdrant, dan Milvus, serta bagaimana backfill bertahap dijalankan dan dilacak. Simulasi backfill dalam topik ini menghitung progres per batch beserta sisa pekerjaannya.
Perubahan model sebagai migrasi#
Embedding dari model yang berbeda hidup di ruang vektor yang berbeda, sehingga jarak antara vektor lama dan vektor baru tidak bermakna apa pun. Pergantian model juga sering mengubah dimensi, sebagaimana terlihat pada model yang umum digunakan: all-MiniLM-L6-v2 menghasilkan 384 dimensi, bge-m3 menghasilkan 1024, sedangkan text-embedding-3-small dan text-embedding-3-large menghasilkan 1536 dan 3072. Kolom atau field yang menampung vektor lama tidak cukup diganti isinya, melainkan perlu diganti bentuknya pula.
Akibatnya, migrasi model menyerupai migrasi skema pada dua hal penting. Pertama, seluruh koleksi perlu di-re-embed dari teks aslinya, karena vektor lama tidak dapat dikonversi menjadi vektor model baru melalui perhitungan aritmetika, melainkan hanya dengan mengoperasikan model baru atas data sumber. Kedua, selama masa peralihan dua kelompok vektor hidup bersamaan, yaitu baris lama yang belum dioperasi ulang dan baris baru yang sudah menggunakan model baru. Query harus di-embed dengan model yang sama dengan vektor yang dibandingkannya, sehingga pasangan model antara query dan koleksi perlu diketahui pada setiap saat selama peralihan berlangsung.
Menyimpan versi dan dua set vektor#
Cara termudah menjaga kejelasan versi adalah menyimpan nama model per vektor atau per field, sehingga setiap baris selalu dapat ditanya model mana yang menghasilkan vektornya. Selama peralihan, dua set vektor hidup berdampingan, dan ketiga mesin utama menyediakan tempat untuk itu. pgvector menampungnya sebagai dua kolom, Qdrant menyediakan named vectors dalam satu titik, sedangkan Milvus mengizinkan beberapa vector field dalam satu collection. Skema berikut menyiapkan kolom untuk model baru di samping kolom lama pada pgvector.
-- tidak-dijalankan
-- Kolom lama menampung embedding model pertama berdimensi 384
ALTER TABLE dokumen ADD COLUMN embedding_v2 vector(1024);
-- Nama model dicatat per baris agar versinya selalu diketahui
ALTER TABLE dokumen ADD COLUMN model_embedding text NOT NULL DEFAULT 'all-MiniLM-L6-v2';Kolom embedding_v2 berdimensi 1024 untuk bge-m3, sedangkan kolom lama tetap berdimensi 384 untuk all-MiniLM-L6-v2. Pencarian selama peralihan memilih kolom sesuai model yang digunakan untuk meng-embed query, dan nilai model_embedding menunjukkan baris mana yang sudah berpindah. Pola yang sama berlaku pada Qdrant dan Milvus dengan mekanisme field-nya masing-masing, yaitu dua vektor dalam satu titik alih-alih dua kolom dalam satu baris. Setelah seluruh jalur query berpindah ke model baru, set vektor lama dihapus dari skema.
Simulasi backfill bertahap#
Re-embed seluruh koleksi sekaligus berisiko besar, baik karena biaya operasi model maupun karena gangguan di tengah proses, sehingga pekerjaan ini biasanya dipecah menjadi batch kecil yang statusnya terekam. Simulasi berikut menjalankan backfill atas 120 record yang seluruhnya menggunakan model v1 di awal, dengan 25 record dioperasi ulang per putaran. Status setiap record berubah dari v1 menjadi v2 tepat setelah batch-nya selesai, sehingga proses yang terputus dapat dilanjutkan dari posisi terakhir tanpa mengulang pekerjaan yang sudah dibayar.
"""Simulasi backfill embedding: progres per batch dan sisa pekerjaan."""
import numpy as np
# Koleksi kecil berisi 120 record; seluruhnya masih menggunakan model v1 di awal
total = 120
status = np.array(["v1"] * total)
# Backfill berjalan dalam batch 25 record per putaran
ukuran_batch = 25
putaran = 0
while np.count_nonzero(status == "v1") > 0:
sisa_indeks = np.where(status == "v1")[0]
batch = sisa_indeks[:ukuran_batch]
status[batch] = "v2" # re-embed selesai untuk batch ini
putaran += 1
selesai = np.count_nonzero(status == "v2")
print(f"Putaran {putaran}: {len(batch)} record di-re-embed, "
f"progres {selesai}/{total}, sisa {total - selesai}")Putaran 1: 25 record di-re-embed, progres 25/120, sisa 95
Putaran 2: 25 record di-re-embed, progres 50/120, sisa 70
Putaran 3: 25 record di-re-embed, progres 75/120, sisa 45
Putaran 4: 25 record di-re-embed, progres 100/120, sisa 20
Putaran 5: 20 record di-re-embed, progres 120/120, sisa 0Keluaran menunjukkan progres yang berjalan rapi dari 25 hingga 120 record, dengan sisa pekerjaan yang menyusut 95, 70, 45, 20, lalu 0. Putaran kelima menangani sisa 20 record yang tidak lagi penuh, dan pola ini berlaku umum karena batch terakhir jarang tepat berukuran sama. Dalam pekerjaan nyata, status per record disimpan di basis data sebagaimana kolom model_embedding pada contoh SQL, sehingga jumlah record dengan status v2 dapat dihitung kapan saja untuk mengetahui progres tanpa membaca log.
Backfill bertahap juga membuka jalan bagi strategi pelepasan yang aman. Query lama tetap berjalan penuh selama kolom lama ada, sementara sebagian kecil trafik mulai membaca kolom baru untuk record yang sudah siap, dan porsinya diperbesar seiring progres backfill. Peralihan berakhir ketika progres mencapai seluruh koleksi dan jalur query sudah membaca vektor baru di semua tempat.
Kesalahan umum#
Kesalahan pertama adalah membiarkan record lama dan baru berbagi satu kolom vektor tanpa penanda versi. Kolom tunggal memaksa vektor baru menempati bentuk yang sama dengan vektor lama, padahal dimensi dan ruangnya berbeda, sehingga hasil pencariannya campur aduk tanpa pesan error apa pun. Dua kolom, named vectors, atau field terpisah dengan penanda model adalah bentuk yang mencegah masalah ini.
Kesalahan kedua adalah menjalankan backfill tanpa status yang terekam. Proses yang terputus sebelum selesai tidak tahu record mana yang sudah dioperasi ulang, sehingga pilihan yang tersisa hanya mengulang dari awal dan membayar operasi model dua kali, atau menebak-nebak dan berisiko mencampur versi. Status per record yang disimpan bersama data menjadikan putaran berikutnya dapat dilanjutkan dengan aman.
Kesalahan ketiga adalah menghapus set vektor lama sebelum seluruh jalur query berpindah. Selama masih ada layanan yang meng-embed query dengan model lama, kolom lama masih diperlukan, dan penghapusannya membuat jalur tersebut gagal dengan error kolom yang tidak ada. Penghapusan dilakukan setelah inventaris jalur query menyatakan semua pembaca sudah berpindah.
Ringkasan#
- Pergantian model embedding diperlakukan sebagai migrasi karena ruang vektor berubah, dimensi dapat berubah, dan seluruh koleksi perlu di-re-embed dari teks asli.
- Selama peralihan, dua set vektor hidup bersama sebagai dua kolom pada pgvector, named vectors pada Qdrant, atau beberapa vector field pada Milvus, dengan nama model tersimpan per vektor.
- Backfill berjalan dalam batch kecil dengan status per record yang terekam, sehingga progres dapat dihitung dan proses yang terputus dapat dilanjutkan.
Referensi#
- pgvector — GitHub
— Dokumentasi pgvector untuk tipe
vectordengan dimensi tertentu dan pembuatan kolom baru melaluiALTER TABLE. - Qdrant Documentation — Qdrant — Dokumentasi Qdrant yang mencakup named vectors sebagai tempat dua set vektor dalam satu titik.
- Milvus Documentation — Milvus — Dokumentasi Milvus yang menjelaskan collection dengan beberapa vector field.