Skip to content
Open
18 changes: 13 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -150,9 +150,7 @@ Prepare the following artifacts for the ANN benchmark with `scripts/prepare_data
For the ANN benchmark, we provide two datasets via HuggingFace:
- `PUBMED768D400K`: [cryptolab-playground/pubmed-arxiv-abstract-embedding-gemma-300m](https://huggingface.co/datasets/cryptolab-playground/pubmed-arxiv-abstract-embedding-gemma-300m)
- `BLOOMBERG768D368K`: [cryptolab-playground/Bloomberg-Financial-News-embedding-gemma-300m](https://huggingface.co/datasets/cryptolab-playground/Bloomberg-Financial-News-embedding-gemma-300m)
- `PRODUCTS512D400K`
- `FASHION512D200K`
- `FOOD512D75K`
- `PRODUCTS512D400K`: [cryptolab-playground/amazon-products-clip-vit-b-32](https://huggingface.co/datasets/cryptolab-playground/amazon-products-clip-vit-b-32)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Food가 빠져있는 것 같습니다?!

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이제 추가했습니다!

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


Also, we provide centroids for the corresponding embedding model used in the ANN benchmark:
- GAS Centroids: [cryptolab-playground/gas-centroids](https://huggingface.co/datasets/cryptolab-playground/gas-centroids)
Expand All @@ -163,10 +161,20 @@ To prepare dataset, run the following command as example:
# Install dependencies for preparing dataset
pip install -r ./scripts/requirements.txt

# Prepare GAS dataset
# Prepare GAS dataset: PUBMED768D400K
python ./scripts/prepare_dataset.py \
-d cryptolab-playground/pubmed-arxiv-abstract-embedding-gemma-300m \
-e embeddinggemma-300m

# Prepare GAS dataset: BLOOMBERG768D368K
python ./scripts/prepare_dataset.py \
-d cryptolab-playground/Bloomberg-Financial-News-embedding-gemma-300m \
-e embeddinggemma-300m
Comment thread
euphoria0-0 marked this conversation as resolved.
Outdated

# Prepare GAS dataset: PRODUCTS512D400K
python ./scripts/prepare_dataset.py \
-d playground/amazon-products-clip-vit-b-32 \
Comment thread
euphoria0-0 marked this conversation as resolved.
Outdated
-e clip-vit-b-32
Comment thread
euphoria0-0 marked this conversation as resolved.
Outdated
```

Then, you can find the generated files as follows:
Expand Down Expand Up @@ -207,7 +215,7 @@ python -m vectordb_bench.cli.vectordbbench envectorivfflat \
... \
--train-centroids True \
--centroids-path "./centroids/embeddinggemma-300m/centroids.npy" \
--nlist 32768 \
--nlist 1024 \

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이거 embedding-gemma 쓸 때 는 32768 맞지않나요??

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

반영했습니다! 166e5a6

--nprobe 6
```

Expand Down
58 changes: 38 additions & 20 deletions scripts/prepare_dataset.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,31 +14,42 @@
from datasets import load_dataset


SUPPORTED_CASES = {
"pubmed768d400k": {
"dataset_name": "cryptolab-playground/pubmed-arxiv-abstract-embedding-gemma-300m",
"embedding_model": "embeddinggemma-300m"
},
"bloomberg768d368k": {
"dataset_name": "cryptolab-playground/Bloomberg-Financial-News-embedding-gemma-300m",
"embedding_model": "embeddinggemma-300m"
},
"products512d400k": {
"dataset_name": "cryptolab-playground/amazon-products-clip-vit-b-32",
"embedding_model": "clip-vit-b-32"
},
"food512d101k": {
"dataset_name": "cryptolab-playground/food101-clip-vit-b-32",
"embedding_model": "clip-vit-b-32"
}
}
SUPPORTED_EMBEDDING_MODELS = ["embeddinggemma-300m", "clip-vit-b-32"]


def get_args():
parser = argparse.ArgumentParser(description="Prepare dataset and ground truth neighbors for benchmarking.")
parser.add_argument(
"-d",
"--dataset-name",
type=str,
default="cryptolab-playground/pubmed-arxiv-abstract-embedding-gemma-300m",
default="pubmed768d400k",
help="Huggingface dataset name to download.",
choices=[
"cryptolab-playground/pubmed-arxiv-abstract-embedding-gemma-300m",
"cryptolab-playground/Bloomberg-Financial-News-embedding-gemma-300m",
],
choices=list(SUPPORTED_CASES.keys()),
)
parser.add_argument(
"--dataset-dir",
type=str,
default=os.path.join(os.environ.get("DATASET_LOCAL_DIR", "/tmp/vectordb_bench/dataset"), "pubmed768d400k"),
help="Dataset directory to save the dataset and neighbors. Default: 'pubmed768d400k' in DATASET_LOCAL_DIR.",
)
parser.add_argument(
"-e",
"--embedding-model",
type=str,
default="embeddinggemma-300m",
help="Embedding model name to download centroids for.",
default=None,
help="Dataset directory to save the dataset and neighbors. Default: <dataset_name> in DATASET_LOCAL_DIR.",
)
parser.add_argument(
"--centroids-dir",
Expand All @@ -52,7 +63,7 @@ def get_args():
def download_dataset(dataset_name: str, output_dir: str = "./dataset/pubmed768d400k") -> None:
"""Download dataset from Huggingface and save as Parquet files."""
# load dataset
ds = load_dataset(dataset_name)
ds = load_dataset(SUPPORTED_CASES[dataset_name]["dataset_name"])
train = ds["train"].to_pandas()
test = ds["test"].to_pandas()

Expand All @@ -62,6 +73,7 @@ def download_dataset(dataset_name: str, output_dir: str = "./dataset/pubmed768d4

test_table = pa.Table.from_pandas(test)
pq.write_table(test_table, f"{output_dir}/test.parquet")
print(f"Saved train and test parquet data to {output_dir}.")


def prepare_neighbors(
Expand Down Expand Up @@ -89,27 +101,33 @@ def prepare_neighbors(

table = pa.Table.from_pandas(df)
pq.write_table(table, f"{data_dir}/neighbors.parquet")
print(f"Saved neighbors data to {data_dir}.")


def download_centroids(embedding_model: str, dataset_dir: str) -> None:
"""Download pre-computed centroids and for IVF_GAS index."""

if embedding_model != "embeddinggemma-300m":
if embedding_model not in SUPPORTED_EMBEDDING_MODELS:
raise ValueError(f"Centroids for {embedding_model} currently not available.")

# BASE URL: https://huggingface.co/datasets/cryptolab-playground/gas-centroids
dataset_link = f"https://huggingface.co/datasets/cryptolab-playground/gas-centroids/resolve/main/{embedding_model}"
dataset_link = (
f"https://huggingface.co/datasets/cryptolab-playground/gas-centroids/resolve/main/{embedding_model}"
)

# download
os.makedirs(os.path.join(dataset_dir, embedding_model), exist_ok=True)
wget.download(f"{dataset_link}/centroids.npy", out=os.path.join(dataset_dir, embedding_model, "centroids.npy"))
print(f"\nDownloaded centroids to {os.path.join(dataset_dir, embedding_model)}")
print(f"\nSaved centroids data to {os.path.join(dataset_dir, embedding_model)}")


if __name__ == "__main__":
args = get_args()
os.makedirs(args.dataset_dir, exist_ok=True)

base_dataset_dir = os.environ.get("DATASET_LOCAL_DIR", "/tmp/vectordb_bench/dataset") if args.dataset_dir is None else args.dataset_dir
args.dataset_dir = os.path.join(base_dataset_dir, args.dataset_name)
os.makedirs(args.dataset_dir, exist_ok=True)

Comment thread
euphoria0-0 marked this conversation as resolved.
Outdated
download_dataset(args.dataset_name, args.dataset_dir)
prepare_neighbors(args.dataset_dir)
download_centroids(args.embedding_model, args.centroids_dir)
download_centroids(SUPPORTED_CASES[args.dataset_name]["embedding_model"], args.centroids_dir)
136 changes: 136 additions & 0 deletions scripts/prepare_random_dataset.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
import argparse
import os

import faiss
import numpy as np
import pandas as pd
import pyarrow as pa
import pyarrow.parquet as pq


def get_args():
parser = argparse.ArgumentParser(description="Prepare random dataset for benchmarking.")
parser.add_argument(
"--dataset-dir",
type=str,
default=os.path.join(os.environ.get("DATASET_LOCAL_DIR", "/tmp/vectordb_bench/dataset"), "random512d1m"),
help="Directory to save the random vectors.",
)
parser.add_argument(
"--dataset-size",
type=int,
default=1_000_000,
help="Number of dataset embeddings to use.",
)
parser.add_argument(
"--query-size",
type=int,
default=1_000,
help="Number of query embeddings to use. 1,000 is recommended in VectorDBBench.",
)
parser.add_argument(
"--dim",
type=int,
default=512,
help="Dimension of the embeddings.",
)

return parser.parse_args()


def get_random_data(num_data, dim, seed):
rng = np.random.default_rng(seed)

data = rng.uniform(low=-1.0, high=1.0, size=(num_data, dim))

# L2 normalize
norm = np.linalg.norm(data, axis=1, keepdims=True)
norm = np.maximum(norm, 1e-10)
data /= norm

print(data.shape)
return data.astype(np.float32)


def npy_to_parquet(
vector: np.ndarray,
dataset_dir: str = "./dataset/random512d1m",
mode: str = "train",
) -> None:
"""Convert downloaded .npy embeddings to Parquet format."""
print("Preparing embeddings from numpy array...")
os.makedirs(dataset_dir, exist_ok=True)

ids = np.arange(len(vector))
id_array = pa.array(ids, type=pa.int64())

list_arrays = [vector[i].tolist() for i in range(len(vector))]
vector_array = pa.array(list_arrays, type=pa.list_(pa.float64()))

assert len(id_array) == len(vector_array)

table = pa.Table.from_arrays([id_array, vector_array], names=["id", "emb"])

out_path = os.path.join(dataset_dir, f"{mode}.parquet")
print(f"Saving parquet to {out_path}")
pq.write_table(table, out_path)


def prepare_neighbors(
data_dir: str = "./dataset/random512d1m",
) -> None:
"""Prepare ground truth neighbors using brute-force flat search and save as Parquet."""
# load dataset
train = pd.read_parquet(f"{data_dir}/train.parquet")
test = pd.read_parquet(f"{data_dir}/test.parquet")

train = np.stack(train["emb"].to_list()).astype("float32")
test = np.stack(test["emb"].to_list()).astype("float32")
dim = train.shape[1]

# flat search
index = faiss.IndexFlatIP(dim)
index.add(train)

k = len(test)
distances, indices = index.search(test, k)
print(f"Distances: {distances.shape}, Indices: {indices.shape}")

assert all(indices[:, 0] == np.arange(len(test))) ### first N vectors

# save flat search result as neighbors
df = pd.DataFrame({"id": np.arange(len(indices)), "neighbors_id": indices.tolist()})

table = pa.Table.from_pandas(df)
pq.write_table(table, f"{data_dir}/neighbors.parquet")
print(f"Saving parquet to {data_dir}/neighbors.parquet")


if __name__ == "__main__":
args = get_args()

# generate random data and save as .npy
vectors = get_random_data(
num_data=args.dataset_size,
dim=args.dim,
seed=42,
)

# prepare train parquet file from numpy arrays
npy_to_parquet(
vector=vectors,
dataset_dir=args.dataset_dir,
mode="train",
)

# prepare test set
test_vectors = vectors[: args.query_size] ### first N vectors

npy_to_parquet(
vector=test_vectors,
dataset_dir=args.dataset_dir,
mode="test",
)

# prepare neighbors
prepare_neighbors(data_dir=args.dataset_dir)
46 changes: 46 additions & 0 deletions vectordb_bench/config-files/envector_food_config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# Custom Case
_base_dataset: &base_dataset
case_type: PerformanceCustomDataset
custom_case_name: FOOD512D101K
custom_case_description: FOOD512D101K benchmark (512D, 101K vectors)
custom_dataset_name: FOOD512D101K
custom_dataset_dir: ""
custom_dataset_size: 101000
custom_dataset_dim: 512
custom_dataset_file_count: 1
custom_dataset_use_shuffled: false
custom_dataset_with_gt: true
k: 10

# envector server settings
_base_envector: &base_envector
uri: localhost:50050
eval_mode: mm
drop_old: true
load: true

# FLAT
envectorflat:
<<: [*base_dataset, *base_envector]
index_name: food101_flat
db_label: FOOD512D101K-FLAT

# IVF-FLAT with trained k-means centroids
envectorivfflat:
<<: [*base_dataset, *base_envector]
index_name: food101_ivfflat
db_label: FOOD512D101K-IVFFLAT
nlist: 128
nprobe: 6
train_centroids: true
centroids_path: food/centroids/centroids_128.npy

# GAS: enVector-customized ANN
envectorivfgas:
<<: [*base_dataset, *base_envector]
index_name: food101_ivfgas
db_label: FOOD512D101K-IVFGAS
nlist: 1024
Comment on lines +33 to +43

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IVF FLAT / IVF GAS 에서의 nlist 값이 다른데 의도하신걸까요?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

의도했습니다!

nprobe: 6
train_centroids: true
centroids_path: centroids/clip-vit-b-32/centroids.npy
4 changes: 2 additions & 2 deletions vectordb_bench/config-files/envector_products_config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ envectorivfgas:
<<: [*base_dataset, *base_envector]
index_name: products_ivfgas
db_label: PRODUCTS512D400K-IVFGAS
nlist: 32768
nprobe: 6
nlist: 1024
nprobe: 16
train_centroids: true
centroids_path: centroids/clip-vit-b-32/centroids.npy
Loading