Topik ini menunjukkan cara membangun sistem pencarian multimodal dengan mengintegrasikan Vector Retrieval Service for Milvus (Milvus) dan model Qwen-VL Large Vision-Language Model (LVLM). Integrasi ini memungkinkan ekstraksi fitur gambar dan penggunaan model penyematan multimodal untuk pencarian yang efisien. Metode pencarian mencakup teks-ke-gambar, teks-ke-teks, gambar-ke-gambar, dan gambar-ke-teks.
Informasi latar belakang
Dalam pencarian multimodal, data tidak terstruktur seperti gambar dan teks diubah menjadi representasi vektor. Teknologi pengambilan vektor digunakan untuk menemukan konten serupa dengan cepat. Topik ini melibatkan penggunaan alat-alat berikut:
Vector Retrieval Service Milvus: Database vektor yang efisien untuk menyimpan dan mengambil vektor.
Qwen-VL: Mengekstrak deskripsi gambar dan kata kunci. Untuk informasi lebih lanjut, lihat Qwen-VL.
DashScope Embedding API: Mengonversi gambar dan teks menjadi vektor. Untuk informasi lebih lanjut, lihat Detail API Multimodal-Embedding.
Fitur-fiturnya mencakup hal-hal berikut:
Pencarian teks-ke-gambar: Masukkan kueri teks untuk mencari gambar yang paling mirip.
Pencarian teks-ke-teks: Masukkan kueri teks untuk mencari deskripsi gambar yang paling mirip.
Pencarian gambar-ke-gambar: Masukkan kueri gambar untuk mencari gambar yang paling mirip.
Pencarian gambar-ke-teks: Masukkan kueri gambar untuk mencari deskripsi gambar yang paling mirip.
Arsitektur sistem
Gambar berikut menunjukkan arsitektur keseluruhan dari sistem pencarian multimodal yang digunakan dalam topik ini.
Prasyarat
Buat instans Milvus. Untuk informasi lebih lanjut, lihat Buat instans Milvus dengan cepat.
Aktifkan Alibaba Cloud Model Studio dan dapatkan Kunci API. Untuk informasi lebih lanjut, lihat Dapatkan Kunci API Anda.
Instal paket dependensi yang diperlukan.
pip3 install dashscope pymilvus==2.5.0Contoh dalam topik ini berjalan di lingkungan
Python 3.9.Unduh dan ekstrak set data sampel.
wget https://github.com/milvus-io/pymilvus-assets/releases/download/imagedata/reverse_image_search.zip unzip -q -o reverse_image_search.zipSet data sampel berisi file CSV bernama
reverse_image_search.csvdan beberapa file gambar.CatatanTopik ini menggunakan set data sampel dan gambar dari proyek sumber terbuka Milvus.
Ikhtisar kode inti
Dalam contoh-contoh dalam topik ini, model Qwen-VL pertama-tama mengekstrak deskripsi gambar dan menyimpannya di bidang image_description. Kemudian, model penyematan multimodal mentransformasikan gambar dan deskripsinya menjadi representasi vektor, bernama image_embedding dan text_embedding. Proses ini memungkinkan pengambilan dan analisis lintas modalitas.
Untuk kesederhanaan, contoh ini hanya mengekstrak data dari 200 gambar pertama.
import base64
import csv
import dashscope
import os
import pandas as pd
import sys
import time
from tqdm import tqdm
from pymilvus import (
connections,
FieldSchema,
CollectionSchema,
DataType,
Collection,
MilvusException,
utility,
)
from http import HTTPStatus
import logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
class FeatureExtractor:
def __init__(self, DASHSCOPE_API_KEY):
self._api_key = DASHSCOPE_API_KEY # Simpan Kunci API dalam variabel lingkungan
def __call__(self, input_data, input_type):
if input_type not in ("image", "text"):
raise ValueError("Tipe input tidak valid. Harus 'image' atau 'text'.")
try:
if input_type == "image":
_, ext = os.path.splitext(input_data)
image_format = ext.lstrip(".").lower()
with open(input_data, "rb") as image_file:
base64_image = base64.b64encode(image_file.read()).decode("utf-8")
input_data = f"data:image/{image_format};base64,{base64_image}"
payload = [{"image": input_data}]
else:
payload = [{"text": input_data}]
resp = dashscope.MultiModalEmbedding.call(
model="multimodal-embedding-v1",
input=payload,
api_key=self._api_key,
)
if resp.status_code == HTTPStatus.OK:
return resp.output["embeddings"][0]["embedding"]
else:
raise RuntimeError(
f"Panggilan API gagal. Kode status: {resp.status_code}, Pesan kesalahan: {resp.message}"
)
except Exception as e:
logger.error(f"Pemrosesan gagal: {str(e)}")
raise
class FeatureExtractorVL:
def __init__(self, DASHSCOPE_API_KEY):
self._api_key = DASHSCOPE_API_KEY # Simpan Kunci API dalam variabel lingkungan
def __call__(self, input_data, input_type):
if input_type not in ("image"):
raise ValueError("Tipe input tidak valid. Harus 'image'.")
try:
if input_type == "image":
payload=[
{
"role": "system",
"content": [{"type":"text","text": "Anda adalah asisten yang membantu."}]
},
{
"role": "user",
"content": [
# {"image": "https://dashscope.oss-cn-beijing.aliyuncs.com/images/dog_and_girl.jpeg"},
{"image": input_data},
{"text": "Pertama, jelaskan gambar ini dalam kurang dari 50 kata, lalu berikan 5 kata kunci"}
],
}
]
resp = dashscope.MultiModalConversation.call(
model="qwen-vl-plus",
messages=payload,
api_key=self._api_key,
)
if resp.status_code == HTTPStatus.OK:
return resp.output["choices"][0]["message"].content[0]["text"]
else:
raise RuntimeError(
f"Panggilan API gagal. Kode status: {resp.status_code}, Pesan kesalahan: {resp.message}"
)
except Exception as e:
logger.error(f"Pemrosesan gagal: {str(e)}")
raise
class MilvusClient:
def __init__(self, MILVUS_TOKEN, MILVUS_HOST, MILVUS_PORT, INDEX, COLLECTION_NAME):
self._token = MILVUS_TOKEN
self._host = MILVUS_HOST
self._port = MILVUS_PORT
self._index = INDEX
self._collection_name = COLLECTION_NAME
self._connect()
self._create_collection_if_not_exists()
def _connect(self):
try:
connections.connect(alias="default", host=self._host, port=self._port, token=self._token)
logger.info("Berhasil terhubung ke Milvus.")
except Exception as e:
logger.error(f"Gagal terhubung ke Milvus: {str(e)}")
sys.exit(1)
def _collection_exists(self):
return self._collection_name in utility.list_collections()
def _create_collection_if_not_exists(self):
try:
if not self._collection_exists():
fields = [
FieldSchema(name="id", dtype=DataType.INT64, is_primary=True, auto_id=True),
FieldSchema(name="origin", dtype=DataType.VARCHAR, max_length=512),
FieldSchema(name="image_description", dtype=DataType.VARCHAR, max_length=1024),
FieldSchema(name="image_embedding", dtype=DataType.FLOAT_VECTOR, dim=1024),
FieldSchema(name="text_embedding", dtype=DataType.FLOAT_VECTOR, dim=1024)
]
schema = CollectionSchema(fields)
self._collection = Collection(self._collection_name, schema)
if self._index == 'IVF_FLAT':
self._create_ivf_index()
else:
self._create_hnsw_index()
logger.info("Koleksi berhasil dibuat.")
else:
self._collection = Collection(self._collection_name)
logger.info("Koleksi sudah ada.")
except Exception as e:
logger.error(f"Gagal membuat atau memuat koleksi: {str(e)}")
sys.exit(1)
def _create_ivf_index(self):
index_params = {
"index_type": "IVF_FLAT",
"params": {
"nlist": 1024, # Jumlah kluster untuk indeks
},
"metric_type": "L2",
}
self._collection.create_index("image_embedding", index_params)
self._collection.create_index("text_embedding", index_params)
logger.info("Indeks berhasil dibuat.")
def _create_hnsw_index(self):
index_params = {
"index_type": "HNSW",
"params": {
"M": 64, # Jumlah maksimum tetangga yang dapat dihubungkan oleh setiap node dalam grafik
"efConstruction": 100, # Jumlah tetangga kandidat yang dipertimbangkan untuk koneksi selama pembuatan indeks
},
"metric_type": "L2",
}
self._collection.create_index("image_embedding", index_params)
self._collection.create_index("text_embedding", index_params)
logger.info("Indeks berhasil dibuat.")
def insert(self, data):
try:
self._collection.insert(data)
self._collection.load()
logger.info("Data berhasil dimasukkan dan dimuat.")
except MilvusException as e:
logger.error(f"Gagal memasukkan data: {str(e)}")
raise
def search(self, query_embedding, field, limit=3):
try:
if self._index == 'IVF_FLAT':
param={"metric_type": "L2", "params": {"nprobe": 10}}
else:
param={"metric_type": "L2", "params": {"ef": 10}}
result = self._collection.search(
data=[query_embedding],
anns_field=field,
param=param,
limit=limit,
output_fields=["origin", "image_description"],
)
return [{"id": hit.id, "distance": hit.distance, "origin": hit.origin, "image_description": hit.image_description} for hit in result[0]]
except Exception as e:
logger.error(f"Pencarian gagal: {str(e)}")
return None
# Muat data dan hasilkan penyematan
def load_image_embeddings(extractor, extractorVL, csv_path):
df = pd.read_csv(csv_path)
image_embeddings = {}
for image_path in tqdm(df["path"].tolist()[:200], desc="Menghasilkan penyematan gambar"): # Gunakan hanya 200 gambar pertama untuk demo ini
try:
desc = extractorVL(image_path, "image")
image_embeddings[image_path] = [desc, extractor(image_path, "image"), extractor(desc, "text")]
time.sleep(1) # Kontrol frekuensi panggilan API
except Exception as e:
logger.warning(f"Gagal memproses {image_path}, melewati: {str(e)}")
return [{"origin": k, 'image_description':v[0], "image_embedding": v[1], 'text_embedding': v[2]} for k, v in image_embeddings.items()]
Di mana:
FeatureExtractor: Kelas ini memanggilDashScope Embedding APIuntuk mentransformasikan gambar atau teks menjadi representasi vektor.FeatureExtractorVL: Kelas ini memanggil model Qwen-VL untuk mengekstrak deskripsi teks dan kunci dari gambar.MilvusClient: Kelas ini mencakup operasi Milvus, termasuk membuat koneksi, mengelola koleksi, membangun indeks, menyisipkan data, dan melakukan pencarian.
Prosedur
Langkah 1: Muat set data
if __name__ == "__main__":
# Konfigurasikan Milvus dan DashScope APIs
MILVUS_TOKEN = "root:****"
MILVUS_HOST = "c-0aa16b1****.milvus.aliyuncs.com"
MILVUS_PORT = "19530"
COLLECTION_NAME = "multimodal_search"
INDEX = "IVF_FLAT" # IVF_FLAT ATAU HNSW
script_dir = os.path.dirname(os.path.abspath(__file__))
csv_path = os.path.join(script_dir, "reverse_image_search.csv")
# Langkah 1: Inisialisasi klien Milvus
milvus_client = MilvusClient(MILVUS_TOKEN, MILVUS_HOST, MILVUS_PORT, INDEX, COLLECTION_NAME)
# Langkah 2: Inisialisasi model besar Qwen-VL dan model penyematan multimodal
extractor = FeatureExtractor(DASHSCOPE_API_KEY)
extractorVL = FeatureExtractorVL(DASHSCOPE_API_KEY)
# Langkah 3: Hasilkan penyematan untuk set data gambar dan sisipkan ke Milvus
embeddings = load_image_embeddings(extractor, extractorVL, csv_path)
milvus_client.insert(embeddings)Ganti parameter berikut dengan nilai sebenarnya Anda.
Parameter | Deskripsi |
| Kunci API untuk DashScope. Digunakan untuk memanggil model Qwen-VL dan penyematan multimodal. |
| Kredensial akses untuk instans Milvus, dalam format |
| Titik akhir internal atau publik dari instans Milvus, seperti |
| Nomor port dari instans Milvus. Nilai defaultnya adalah |
| Nama koleksi Milvus yang digunakan untuk menyimpan data vektor gambar dan teks. |
Jalankan file Python. Jika output mencakup informasi berikut, data telah dimuat dengan sukses.
Menghasilkan penyematan gambar: 100%
INFO:__main__:Data berhasil dimasukkan dan dimuat.Anda juga dapat mengunjungi halaman Attu dan pergi ke tab Data untuk memverifikasi informasi set data.
Sebagai contoh, ketika model besar Qwen-VL menganalisis gambar, ia mengekstrak ringkasan teks yang secara hidup menggambarkan adegan tersebut: "Seseorang di pantai mengenakan celana jeans dan sepatu bot hijau. Pasir tertutup bekas air. Kata kunci: pantai, jejak kaki, pasir, sepatu, celana".
Deskripsi gambar menggunakan bahasa yang ringkas dan hidup untuk menyoroti fitur utama gambar, menciptakan gambar mental yang jelas tentang adegan tersebut.

Langkah 2: Lakukan pengambilan vektor multimodal
Contoh 1: Pencarian teks-ke-gambar dan teks-ke-teks
Dalam contoh ini, kueri teksnya adalah "anjing coklat". Model vektor multimodal mengonversi kueri ini menjadi penyematan. Penyematan ini kemudian digunakan untuk melakukan pencarian teks-ke-gambar pada bidang image_embedding dan pencarian teks-ke-teks pada bidang text_embedding. Hasil untuk kedua pencarian dikembalikan.
Dalam file Python, ganti bagian main dengan kode berikut dan jalankan file tersebut.
if __name__ == "__main__":
MILVUS_HOST = "c-xxxxxxxxxxxx.milvus.aliyuncs.com"
MILVUS_PORT = "19530"
MILVUS_TOKEN = "root:****"
COLLECTION_NAME = "multimodal_search"
INDEX = "IVF_FLAT" # IVF_FLAT ATAU HNSW
DASHSCOPE_API_KEY = "<YOUR_DASHSCOPE_API_KEY >"
# Langkah 1: Inisialisasi klien Milvus
milvus_client = MilvusClient(MILVUS_TOKEN, MILVUS_HOST, MILVUS_PORT, INDEX, COLLECTION_NAME)
# Langkah 2: Inisialisasi model penyematan multimodal
extractor = FeatureExtractor(DASHSCOPE_API_KEY)
# Langkah 4: Contoh pencarian multimodal untuk pencarian teks-ke-gambar dan teks-ke-teks
text_query = "anjing coklat"
text_embedding = extractor(text_query, "text")
text_results_1 = milvus_client.search(text_embedding, field = 'image_embedding')
logger.info(f"Hasil pencarian teks-ke-gambar: {text_results_1}")
text_results_2 = milvus_client.search(text_embedding, field = 'text_embedding')
logger.info(f"Hasil pencarian teks-ke-teks: {text_results_2}")
Informasi berikut dikembalikan.
Output model besar bersifat non-deterministik, sehingga hasil Anda mungkin sedikit berbeda dari contoh ini.
INFO:__main__:Hasil pencarian teks-ke-gambar: [
{'id': 456882250782308942, 'distance': 1.338853359222412, 'origin': './train/Rhodesian_ridgeback/n02087394_9675.JPEG', 'image_description': 'Foto seekor anak anjing berdiri di atas karpet. Ia memiliki bulu coklat dan mata biru.\nKata kunci: anak anjing, karpet, mata, warna bulu, berdiri'},
{'id': 456882250782308933, 'distance': 1.3568601608276367, 'origin': './train/Rhodesian_ridgeback/n02087394_6382.JPEG', 'image_description': 'Ini adalah anjing hound coklat dengan telinga menjuntai dan kalung di lehernya. Ia sedang menatap lurus ke depan.\n\nKata kunci: anjing, coklat, hound, telinga, kalung'},
{'id': 456882250782308940, 'distance': 1.3838427066802979, 'origin': './train/Rhodesian_ridgeback/n02087394_5846.JPEG', 'image_description': 'Dua anak anjing sedang bermain di atas selimut. Salah satu anjing berbaring di atas yang lain, dengan boneka beruang di latar belakang.\n\nKata kunci: anak anjing, bermain, selimut, boneka beruang, interaksi'}]
INFO:__main__:Hasil pencarian teks-ke-teks: [
{'id': 456882250782309025, 'distance': 0.6969608068466187, 'origin': './train/mongoose/n02137549_7552.JPEG', 'image_description': 'Ini adalah foto close-up seekor hewan kecil berbulu coklat. Ia memiliki wajah bulat dan mata besar.\n\nKata kunci: hewan kecil, bulu coklat, wajah bulat, mata besar, latar belakang alami'},
{'id': 456882250782308933, 'distance': 0.7110348343849182, 'origin': './train/Rhodesian_ridgeback/n02087394_6382.JPEG', 'image_description': 'Ini adalah anjing hound coklat dengan telinga menjuntai dan kalung di lehernya. Ia sedang menatap lurus ke depan.\n\nKata kunci: anjing, coklat, hound, telinga, kalung'},
{'id': 456882250782308992, 'distance': 0.7725887298583984, 'origin': './train/lion/n02129165_19310.JPEG', 'image_description': 'Ini adalah foto close-up seekor singa. Ia memiliki surai tebal dan mata tajam.\n\nKata kunci: singa, mata, surai, lingkungan alami, hewan liar'}]Contoh 2: Pencarian gambar-ke-gambar dan gambar-ke-teks
Dalam contoh ini, pencarian kesamaan dilakukan menggunakan gambar singa dari direktori `test` (jalur: `test/lion/n02129165_13728.JPEG`).

Metode pencarian gambar-ke-gambar dan gambar-ke-teks mengambil konten terkait gambar target dari perspektif visual dan teks. Ini memungkinkan pencocokan kesamaan multi-dimensi.
if __name__ == "__main__":
# Konfigurasikan Milvus dan DashScope APIs
MILVUS_TOKEN = "root:****"
MILVUS_HOST = "c-0aa16b1****.milvus.aliyuncs.com"
MILVUS_PORT = "19530"
COLLECTION_NAME = "multimodal_search"
INDEX = "IVF_FLAT" # IVF_FLAT ATAU HNSW
DASHSCOPE_API_KEY = "<YOUR_DASHSCOPE_API_KEY >"
# Langkah 1: Inisialisasi klien Milvus
milvus_client = MilvusClient(MILVUS_TOKEN, MILVUS_HOST, MILVUS_PORT, INDEX, COLLECTION_NAME)
# Langkah 2: Inisialisasi model penyematan multimodal
extractor = FeatureExtractor(DASHSCOPE_API_KEY)
# Langkah 5: Contoh pencarian multimodal untuk pencarian gambar-ke-gambar dan gambar-ke-teks
image_query_path = "./test/lion/n02129165_13728.JPEG"
image_embedding = extractor(image_query_path, "image")
image_results_1 = milvus_client.search(image_embedding, field = 'image_embedding')
logger.info(f"Hasil pencarian gambar-ke-gambar: {image_results_1}")
image_results_2 = milvus_client.search(image_embedding, field = 'text_embedding')
logger.info(f"Hasil pencarian gambar-ke-teks: {image_results_2}")Output berikut dikembalikan.
Output model besar bersifat acak hingga tingkat tertentu. Hasil Anda mungkin berbeda dari contoh ini.
INFO:__main__:Hasil pencarian gambar-ke-gambar: [
{'id': 456882250782308987, 'distance': 0.23892249166965485, 'origin': './train/lion/n02129165_19953.JPEG', 'image_description': 'Seekor singa megah berdiri di samping batu, dengan pohon dan semak-semak di latar belakang. Sinar matahari bersinar padanya.\n\nKata kunci: singa, batu, hutan, sinar matahari, kebuasan'},
{'id': 456882250782308989, 'distance': 0.4113130569458008, 'origin': './train/lion/n02129165_1142.JPEG', 'image_description': 'Seekor singa beristirahat di vegetasi hijau yang lebat. Latar belakang terdiri dari bambu dan pohon.\n\nKata kunci: singa, rumput, tanaman hijau, batang pohon, lingkungan alami'},
{'id': 456882250782308984, 'distance': 0.5206397175788879, 'origin': './train/lion/n02129165_16.JPEG', 'image_description': 'Gambar menunjukkan sepasang singa berdiri di atas rumput. Singa jantan memiliki surai tebal, sementara singa betina tampak lebih ramping.\n\nKata kunci: singa, rumput, jantan, betina, lingkungan alami'}]
INFO:__main__:Hasil pencarian gambar-ke-teks:
[{'id': 456882250782308989, 'distance': 1.0935896635055542, 'origin': './train/lion/n02129165_1142.JPEG', 'image_description': 'Seekor singa beristirahat di vegetasi hijau yang lebat. Latar belakang terdiri dari bambu dan pohon.\n\nKata kunci: singa, rumput, tanaman hijau, batang pohon, lingkungan alami'},
{'id': 456882250782308987, 'distance': 1.2102885246276855, 'origin': './train/lion/n02129165_19953.JPEG', 'image_description': 'Seekor singa megah berdiri di samping batu, dengan pohon dan semak-semak di latar belakang. Sinar matahari bersinar padanya.\n\nKata kunci: singa, batu, hutan, sinar matahari, kebuasan'},
{'id': 456882250782308992, 'distance': 1.2725986242294312, 'origin': './train/lion/n02129165_19310.JPEG', 'image_description': 'Ini adalah foto close-up seekor singa. Ia memiliki surai tebal dan mata tajam.\n\nKata kunci: singa, mata, surai, lingkungan alami, hewan liar'}]