Visualisasi Vektor
Dalam contoh ini, kami akan menunjukkan bagaimana memvisualisasikan embedding (vektor) dalam Milvus menggunakan t-SNE.
Teknik pengurangan dimensi, seperti t-SNE, sangat berharga untuk memvisualisasikan data yang kompleks dan berdimensi tinggi dalam ruang 2D atau 3D dengan tetap mempertahankan struktur lokal. Hal ini memungkinkan pengenalan pola, meningkatkan pemahaman tentang hubungan fitur, dan memfasilitasi interpretasi hasil model pembelajaran mesin. Selain itu, t-SNE membantu dalam evaluasi algoritme dengan membandingkan hasil pengelompokan secara visual, menyederhanakan presentasi data kepada audiens non-spesialis, dan dapat mengurangi biaya komputasi dengan bekerja dengan representasi dimensi yang lebih rendah. Melalui aplikasi ini, t-SNE tidak hanya membantu mendapatkan wawasan yang lebih dalam tentang kumpulan data tetapi juga mendukung proses pengambilan keputusan yang lebih tepat.
Persiapan
Ketergantungan dan Lingkungan
$ pip install --upgrade pymilvus openai requests tqdm matplotlib seaborn
Kita akan menggunakan model penyematan OpenAI dalam contoh ini. Anda harus menyiapkan kunci api OPENAI_API_KEY sebagai variabel lingkungan.
import os
os.environ["OPENAI_API_KEY"] = "sk-***********"
Menyiapkan data
Kami menggunakan halaman FAQ dari Dokumentasi Milvus 2.4.x sebagai pengetahuan pribadi dalam RAG kami, yang merupakan sumber data yang baik untuk pipeline RAG sederhana.
Unduh file zip dan ekstrak dokumen ke folder milvus_docs
.
$ wget https://github.com/milvus-io/milvus-docs/releases/download/v2.4.6-preview/milvus_docs_2.4.x_en.zip
$ unzip -q milvus_docs_2.4.x_en.zip -d milvus_docs
Kami memuat semua file penurunan harga dari folder milvus_docs/en/faq
. Untuk setiap dokumen, kita cukup menggunakan "# " untuk memisahkan konten dalam file, yang secara kasar dapat memisahkan konten dari setiap bagian utama dari file penurunan harga.
from glob import glob
text_lines = []
for file_path in glob("milvus_docs/en/faq/*.md", recursive=True):
with open(file_path, "r") as file:
file_text = file.read()
text_lines += file_text.split("# ")
Menyiapkan Model Penyematan
Kita menginisialisasi klien OpenAI untuk menyiapkan model penyematan.
from openai import OpenAI
openai_client = OpenAI()
Tentukan fungsi untuk menghasilkan penyematan teks menggunakan klien OpenAI. Kami menggunakan model penyematan-teks-3-besar sebagai contoh.
def emb_text(text):
return (
openai_client.embeddings.create(input=text, model="text-embedding-3-large")
.data[0]
.embedding
)
Hasilkan embedding uji dan cetak dimensi dan beberapa elemen pertama.
test_embedding = emb_text("This is a test")
embedding_dim = len(test_embedding)
print(embedding_dim)
print(test_embedding[:10])
3072
[-0.015370666049420834, 0.00234124343842268, -0.01011690590530634, 0.044725317507982254, -0.017235849052667618, -0.02880779094994068, -0.026678944006562233, 0.06816216558218002, -0.011376636102795601, 0.021659553050994873]
Memuat data ke dalam Milvus
Membuat Koleksi
from pymilvus import MilvusClient
milvus_client = MilvusClient(uri="./milvus_demo.db")
collection_name = "my_rag_collection"
Adapun argumen dari MilvusClient
:
- Menetapkan
uri
sebagai file lokal, misalnya./milvus.db
, adalah metode yang paling mudah, karena secara otomatis menggunakan Milvus Lite untuk menyimpan semua data dalam file ini. - Jika Anda memiliki data dalam skala besar, Anda dapat mengatur server Milvus yang lebih berkinerja pada docker atau kubernetes. Dalam pengaturan ini, silakan gunakan uri server, misalnya
http://localhost:19530
, sebagaiuri
. - Jika Anda ingin menggunakan Zilliz Cloud, layanan cloud yang dikelola sepenuhnya untuk Milvus, sesuaikan
uri
dantoken
, yang sesuai dengan kunci Public Endpoint dan Api di Zilliz Cloud.
Periksa apakah koleksi sudah ada dan hapus jika sudah ada.
if milvus_client.has_collection(collection_name):
milvus_client.drop_collection(collection_name)
Buat koleksi baru dengan parameter yang ditentukan.
Jika kita tidak menentukan informasi field apa pun, Milvus akan secara otomatis membuat field default id
untuk primary key, dan field vector
untuk menyimpan data vektor. Bidang JSON yang dicadangkan digunakan untuk menyimpan bidang yang tidak ditentukan skema dan nilainya.
milvus_client.create_collection(
collection_name=collection_name,
dimension=embedding_dim,
metric_type="IP", # Inner product distance
consistency_level="Strong", # Strong consistency level
)
Menyisipkan data
Lakukan perulangan melalui baris teks, buat penyematan, lalu masukkan data ke dalam Milvus.
Berikut ini adalah bidang baru text
, yang merupakan bidang yang tidak ditentukan dalam skema koleksi. Field ini akan secara otomatis ditambahkan ke field dinamis JSON yang dicadangkan, yang dapat diperlakukan sebagai field normal pada level tinggi.
from tqdm import tqdm
data = []
for i, line in enumerate(tqdm(text_lines, desc="Creating embeddings")):
data.append({"id": i, "vector": emb_text(line), "text": line})
milvus_client.insert(collection_name=collection_name, data=data)
Creating embeddings: 100%|██████████| 72/72 [00:20<00:00, 3.60it/s]
{'insert_count': 72, 'ids': [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71], 'cost': 0}
Memvisualisasikan Penyematan dalam Pencarian Vektor
Pada bagian ini, kita melakukan pencarian milvus dan kemudian memvisualisasikan vektor kueri dan vektor yang diambil bersama-sama dalam dimensi yang diperkecil.
Mengambil Data untuk Kueri
Mari siapkan pertanyaan untuk pencarian.
# Modify the question to test it with your own query!
question = "How is data stored in Milvus?"
Cari pertanyaan dalam koleksi dan ambil 10 kecocokan semantik teratas.
search_res = milvus_client.search(
collection_name=collection_name,
data=[
emb_text(question)
], # Use the `emb_text` function to convert the question to an embedding vector
limit=10, # Return top 10 results
search_params={"metric_type": "IP", "params": {}}, # Inner product distance
output_fields=["text"], # Return the text field
)
Mari kita lihat hasil pencarian dari kueri
import json
retrieved_lines_with_distances = [
(res["entity"]["text"], res["distance"]) for res in search_res[0]
]
print(json.dumps(retrieved_lines_with_distances, indent=4))
[
[
" Where does Milvus store data?\n\nMilvus deals with two types of data, inserted data and metadata. \n\nInserted data, including vector data, scalar data, and collection-specific schema, are stored in persistent storage as incremental log. Milvus supports multiple object storage backends, including [MinIO](https://min.io/), [AWS S3](https://aws.amazon.com/s3/?nc1=h_ls), [Google Cloud Storage](https://cloud.google.com/storage?hl=en#object-storage-for-companies-of-all-sizes) (GCS), [Azure Blob Storage](https://azure.microsoft.com/en-us/products/storage/blobs), [Alibaba Cloud OSS](https://www.alibabacloud.com/product/object-storage-service), and [Tencent Cloud Object Storage](https://www.tencentcloud.com/products/cos) (COS).\n\nMetadata are generated within Milvus. Each Milvus module has its own metadata that are stored in etcd.\n\n###",
0.7675539255142212
],
[
"How does Milvus handle vector data types and precision?\n\nMilvus supports Binary, Float32, Float16, and BFloat16 vector types.\n\n- Binary vectors: Store binary data as sequences of 0s and 1s, used in image processing and information retrieval.\n- Float32 vectors: Default storage with a precision of about 7 decimal digits. Even Float64 values are stored with Float32 precision, leading to potential precision loss upon retrieval.\n- Float16 and BFloat16 vectors: Offer reduced precision and memory usage. Float16 is suitable for applications with limited bandwidth and storage, while BFloat16 balances range and efficiency, commonly used in deep learning to reduce computational requirements without significantly impacting accuracy.\n\n###",
0.6210848689079285
],
[
"Does the query perform in memory? What are incremental data and historical data?\n\nYes. When a query request comes, Milvus searches both incremental data and historical data by loading them into memory. Incremental data are in the growing segments, which are buffered in memory before they reach the threshold to be persisted in storage engine, while historical data are from the sealed segments that are stored in the object storage. Incremental data and historical data together constitute the whole dataset to search.\n\n###",
0.585393488407135
],
[
"Why is there no vector data in etcd?\n\netcd stores Milvus module metadata; MinIO stores entities.\n\n###",
0.579704999923706
],
[
"How does Milvus flush data?\n\nMilvus returns success when inserted data are loaded to the message queue. However, the data are not yet flushed to the disk. Then Milvus' data node writes the data in the message queue to persistent storage as incremental logs. If `flush()` is called, the data node is forced to write all data in the message queue to persistent storage immediately.\n\n###",
0.5777501463890076
],
[
"What is the maximum dataset size Milvus can handle?\n\n \nTheoretically, the maximum dataset size Milvus can handle is determined by the hardware it is run on, specifically system memory and storage:\n\n- Milvus loads all specified collections and partitions into memory before running queries. Therefore, memory size determines the maximum amount of data Milvus can query.\n- When new entities and and collection-related schema (currently only MinIO is supported for data persistence) are added to Milvus, system storage determines the maximum allowable size of inserted data.\n\n###",
0.5655910968780518
],
[
"Does Milvus support inserting and searching data simultaneously?\n\nYes. Insert operations and query operations are handled by two separate modules that are mutually independent. From the client\u2019s perspective, an insert operation is complete when the inserted data enters the message queue. However, inserted data are unsearchable until they are loaded to the query node. If the segment size does not reach the index-building threshold (512 MB by default), Milvus resorts to brute-force search and query performance may be diminished.\n\n###",
0.5618637204170227
],
[
"What data types does Milvus support on the primary key field?\n\nIn current release, Milvus supports both INT64 and string.\n\n###",
0.5561620593070984
],
[
"Is Milvus available for concurrent search?\n\nYes. For queries on the same collection, Milvus concurrently searches the incremental and historical data. However, queries on different collections are conducted in series. Whereas the historical data can be an extremely huge dataset, searches on the historical data are relatively more time-consuming and essentially performed in series.\n\n###",
0.529681921005249
],
[
"Can vectors with duplicate primary keys be inserted into Milvus?\n\nYes. Milvus does not check if vector primary keys are duplicates.\n\n###",
0.528809666633606
]
]
Pengurangan dimensi menjadi 2-d dengan t-SNE
Mari kita kurangi dimensi sematan menjadi 2-d dengan t-SNE. Kita akan menggunakan pustaka sklearn
untuk melakukan transformasi t-SNE.
import pandas as pd
import numpy as np
from sklearn.manifold import TSNE
data.append({"id": len(data), "vector": emb_text(question), "text": question})
embeddings = []
for gp in data:
embeddings.append(gp["vector"])
X = np.array(embeddings, dtype=np.float32)
tsne = TSNE(random_state=0, max_iter=1000)
tsne_results = tsne.fit_transform(X)
df_tsne = pd.DataFrame(tsne_results, columns=["TSNE1", "TSNE2"])
df_tsne
TSNE1 | TSNE2 | |
---|---|---|
0 | -3.877362 | 0.866726 |
1 | -5.923084 | 0.671701 |
2 | -0.645954 | 0.240083 |
3 | 0.444582 | 1.222875 |
4 | 6.503896 | -4.984684 |
... | ... | ... |
69 | 6.354055 | 1.264959 |
70 | 6.055961 | 1.266211 |
71 | -1.516003 | 1.328765 |
72 | 3.971772 | -0.681780 |
73 | 3.971772 | -0.681780 |
74 baris × 2 kolom
Memvisualisasikan hasil pencarian Milvus pada bidang 2d
Kami akan memplot vektor kueri dengan warna hijau, vektor yang diambil dengan warna merah, dan vektor yang tersisa dengan warna biru.
import matplotlib.pyplot as plt
import seaborn as sns
# Extract similar ids from search results
similar_ids = [gp["id"] for gp in search_res[0]]
df_norm = df_tsne[:-1]
df_query = pd.DataFrame(df_tsne.iloc[-1]).T
# Filter points based on similar ids
similar_points = df_tsne[df_tsne.index.isin(similar_ids)]
# Create the plot
fig, ax = plt.subplots(figsize=(8, 6)) # Set figsize
# Set the style of the plot
sns.set_style("darkgrid", {"grid.color": ".6", "grid.linestyle": ":"})
# Plot all points in blue
sns.scatterplot(
data=df_tsne, x="TSNE1", y="TSNE2", color="blue", label="All knowledge", ax=ax
)
# Overlay similar points in red
sns.scatterplot(
data=similar_points,
x="TSNE1",
y="TSNE2",
color="red",
label="Similar knowledge",
ax=ax,
)
sns.scatterplot(
data=df_query, x="TSNE1", y="TSNE2", color="green", label="Query", ax=ax
)
# Set plot titles and labels
plt.title("Scatter plot of knowledge using t-SNE")
plt.xlabel("TSNE1")
plt.ylabel("TSNE2")
# Set axis to be equal
plt.axis("equal")
# Display the legend
plt.legend()
# Show the plot
plt.show()
png
Seperti yang dapat kita lihat, vektor kueri dekat dengan vektor yang diambil. Meskipun vektor yang diambil tidak berada dalam lingkaran standar dengan radius tetap yang berpusat pada kueri, kita dapat melihat bahwa vektor-vektor tersebut masih sangat dekat dengan vektor kueri pada bidang 2D.
Menggunakan teknik reduksi dimensi dapat memfasilitasi pemahaman vektor dan pemecahan masalah. Semoga Anda dapat memperoleh pemahaman yang lebih baik tentang vektor melalui tutorial ini.