راه‌اندازی

pip install chromadb

ذخیره دائم داده‌های Chromadb

import chromadb

می‌توانید مسیر ذخیره‌سازی فایل پایگاه داده Chroma را مشخص کنید. اگر داده وجود داشته باشد، فایل پایگاه داده به‌طور خودکار هنگام شروع برنامه بارگذاری می‌شود.

client = chromadb.PersistentClient(path="/data/tizi365.db")

پارامتر path مسیر فایل پایگاه داده Chroma می‌باشد.

توجه: برای یک پایگاه داده Chroma، ایجاد یک شیء مشتری یک‌بار کافی است. بارگذاری و ذخیره کردن چند مشتری در همان مسیر ممکن است منجر به رفتارهای غیرمنتظره شود، از جمله حذف داده. به طور کلی، تنها باید یک مشتری Chroma در برنامه ایجاد شود.

تعدادی از توابع متداول شیء مشتری:

client.reset()  # پاک‌سازی و به‌طور کامل بازنشانی پایگاه داده

عملیات مجموعه

Chromadb از ابزار اصلی collection برای مدیریت مجموعه‌های داده برداری استفاده می‌کند، که می‌تواند به جداول در MYSQL شباهت داشته باشد.

ایجاد، مشاهده و حذف مجموعه‌ها

Chroma از نام مجموعه در URL استفاده می‌کند، بنابراین بعضی محدودیت‌های نام‌گذاری دارد:

  • طول نام باید بین 3 تا 63 نویسه باشد.
  • نام باید با یک حرف کوچک یا عدد شروع و ختم شود و می‌تواند شامل نقطه، خط‌تیره و زیرخط باشد.
  • نام نمی‌تواند دو نقطه متوالی داشته باشد.
  • نام نمی‌تواند یک آدرس IP معتبر باشد.

برای ایجاد یک مجموعه، باید نام مجموعه و یک تابع محاسبه بردار اختیاری (همچنین تابع توکرد به آن مربوط است) را مشخص کنید. اگر یک تابع توکرد مشخص شود، باید هر بار که به مجموعه دسترسی داشته باشید، ارائه شود.

توجه: هدف تابع محاسبه بردار (تابع توکرد) محاسبه بردار متن است.

collection = client.create_collection(name="my_collection", embedding_function=emb_fn)
collection = client.get_collection(name="my_collection", embedding_function=emb_fn)

تابع توکرد ورودی متن را می‌گیرد و بردار محاسبه شده را برمی‌گرداند.

توجه: مبتدیان می‌توانند درباره آموزش مدل توکرد بردار متن بیاموزند.

می‌توانید به یک مجموعه موجود با استفاده از .get_collection ارجاع داد و از .delete_collection برای حذف یک مجموعه استفاده نمایید. همچنین می‌توانید از .get_or_create_collection برای ارجاع به یک مجموعه (اگر وجود داشت) یا ایجاد آن در صورت عدم وجود استفاده نمایید.

collection = client.get_collection(name="tizi365")
collection = client.get_or_create_collection(name="tizi365")
client.delete_collection(name="tizi365")

عملیات تخصصی دیگر مجموعه:

collection.peek() # یک فهرست از 10 داده اول در مجموعه برمی‌گرداند
collection.count() # تعداد کل داده‌ها در مجموعه را برمی‌گرداند
collection.modify(name="new_name") # نام مجموعه را تغییر می‌دهد

مشخص کردن روش محاسبه فاصله برداری

تابع create_collection شامل پارامتر اختیاری metadata هم است. با تنظیم مقدار hnsw:space می‌توانید روش محاسبه فاصله فضای بردار را سفارشی کنید.

توجه: داده‌های بردار نمایانگر تشابه بین بردارها است با محاسبه فاصله فضایی بین بردارها. هر چه فاصله نزدیکتر باشد، تشابه بیشتری وجود دارد و بالعکس.

collection = client.create_collection(
        name="collection_name",
        metadata={"hnsw:space": "cosine"} # متد حسابش معیار پیوستگی پیش‌فرض است
    )

گزینه‌های معتبر برای hnsw:space "l2"، "ip" یا "cosine" هستند. پیش‌فرض "l2" است.

افزودن داده به یک مجموعه

از متد .add برای افزودن داده به کروما استفاده کنید.

بدون مشخص کردن بردار سند، داده را مستقیما اضافه کنید:

collection.add(
    documents=["متن نمونه...", "سند2", "سند3", ...],
    metadatas=[{"فصل": "3", "آیه": "16"}, {"فصل": "3", "آیه": "5"}, {"فصل": "29", "آیه": "11"}, ...],
    ids=["شناسه1", "شناسه2", "شناسه3", ...]
)

اگر کروما یک لیست از اسناد دریافت کند، به طور خودکار از تابع تعبیه‌شده مجموعه برای محاسبه بردارهای سند استفاده می‌کند (در صورتی که تابع تعبیه‌شده در هنگام ایجاد مجموعه ارائه نشده باشد، مقدار پیش‌فرض استفاده خواهد شد). کروما همچنین سندها را نیز ذخیره می‌کند. اگر یک سند برای محاسبه با استفاده از تابع تعبیه‌شده انتخابی بسیار بزرگ باشد، یک استثنا رخ می‌دهد.

هر سند باید یک شناسه یکتا داشته باشد (ids). اضافه کردن یک شناسه یکسان دو بار موجب ذخیره کردن مقدار اولیه خواهد شد. اختیاری می‌توانید برای هر سند یک لیست از دیکشنری‌های متادیتا (metadatas) ارائه دهید، تا اطلاعات اضافی را که می‌تواند برای فیلتر کردن داده‌ها در زمان جستجوها استفاده شود، ذخیره کند.

به طور جایگزین، می‌توانید به طور مستقیم یک لیست از داده‌های مرتبط با بردار سند را فراهم کنید و کروما از داده بردار ارائه شده توسط شما بدون محاسبه خودکار بردارها استفاده خواهد کرد.

collection.add(
    documents=["سند1", "سند2", "سند3", ...],
    embeddings=[[1.1, 2.3, 3.2], [4.5, 6.9, 4.4], [1.1, 2.3, 3.2], ...],
    metadatas=[{"فصل": "3", "آیه": "16"}, {"فصل": "3", "آیه": "5"}, {"فصل": "29", "آیه": "11"}, ...],
    ids=["شناسه1", "شناسه2", "شناسه3", ...]
)

اگر ابعاد داده بردار فراهم شده (طول) با ابعاد مجموعه مطابقت نداشته باشد، یک استثنا رخ می‌دهد.

همچنین می‌توانید سندها را در جای دیگر ذخیره کرده و کروما را با داده بردار و لیست متادیتا فراهم کنید. می‌توانید با استفاده از شناسه‌ها برای ارتباط بردارها با اسناد ذخیره شده در جای دیگر استفاده کنید.

collection.add(
    embeddings=[[1.1, 2.3, 3.2], [4.5, 6.9, 4.4], [1.1, 2.3, 3.2], ...],
    metadatas=[{"فصل": "3", "آیه": "16"}, {"فصل": "3", "آیه": "5"}, {"فصل": "29", "آیه": "11"}, ...],
    ids=["شناسه1", "شناسه2", "شناسه3", ...]
)

توجه: عملکرد اصلی پایگاه داده برداری از روی هم‌معنایی بر اساس داده برداری است. برای کاهش اندازه پایگاه داده برداری و بهبود کارایی، می‌توانیم انتخاب کنیم که داده برداری و برخی ویژگی‌های فیلتر کننده ضروری را در پایگاه داده بردار ذخیره کنیم. داده‌های دیگری مانند محتوای مقاله می‌توانند در پایگاه داده‌های مانند MYSQL ذخیره شوند، تا زمانی که از طریق شناسه‌ها مرتبط شوند.

درخواست داده‌های مجموعه

روش .query می‌تواند برای پرس و جو در مجموعه‌های داده Chroma به چندین روش استفاده شود.

شما می‌توانید با استفاده از مجموعه‌ای از query_embeddings (داده‌های بردار) پرس و جو کنید.

نکته: در سناریوهای واقعی توسعه، query_embeddings معمولاً ابتدا با محاسبه بردار پرس و جوی کاربر از طریق یک مدل توکیایی‌سازی متنی به دست می‌آیند و سپس از این بردار برای پرس و جوی محتوای مشابه استفاده می‌شود.

collection.query(
    query_embeddings=[[11.1, 12.1, 13.1],[1.1, 2.3, 3.2], ...],
    n_results=10,
    where={"metadata_field": "is_equal_to_this"},
    where_document={"$contains":"search_string"}
)

پرس و جو نتایج n_results را که بهترین مطابقت را با هر بردار پرس و جو (query_embedding) به ترتیب دارند، برمی‌گرداند. می‌توان یک دیکشنری فیلتر where اختیاری برای فیلتر کردن نتایج بر اساس متاداده‌های مرتبط با هر سند ارائه داد، همچنین یک دیکشنری فیلتر where_document اختیاری برای فیلتر کردن نتایج بر اساس محتوای سند ارائه داد.

اگر query_embeddings ارائه شده با ابعاد مجموعه سازگار نباشند، یک استثناء اتفاق خواهد افتاد. برای اطمینان از ابعاد برداری متناسب، از همان مدل توکیایی‌سازی متنی برای محاسبه بردارها استفاده کنید.

همچنین می‌توانید با استفاده از مجموعه‌ای از متون پرس و جو کنید. Chroma ابتدا برای هر متن پرس و جو بردار را با استفاده از تابع embedding مجموعه محاسبه می‌کند، سپس پرس و جو با استفاده از بردارهای متنی تولید شده انجام می‌دهد.

collection.query(
    query_texts=["doc10", "thus spake zarathustra", ...],
    n_results=10,
    where={"metadata_field": "is_equal_to_this"},
    where_document={"$contains":"search_string"}
)

همچنین می‌توانید از .get برای پرس و جو داده از مجموعه بر اساس شناسه استفاده کنید.

collection.get(
    ids=["id1", "id2", "id3", ...],
    where={"style": "style1"}
)

.get نیز از فیلترهای where و where_document پشتیبانی می‌کند. اگر هیچ شناسه‌ای ارائه نشود، تمام موارد موجود در مجموعه که با فیلترهای where و where_document مطابقت داشته باشند، برگردانده می‌شود.

تعیین فیلدهای بازگشتی

هنگام استفاده از get یا query، می‌توانید از پارامتر include برای تعیین داده‌های بازگشتی مانند embeddings, documents یا metadatas استفاده کنید، و برای پرس و جوها، داده‌های فاصله هم باید بازگردانده شوند. به طور پیش‌فرض، Chroma اسناد و متاداده‌ها را باز می‌گرداند و برای پرس و جوها داده‌های فاصله و برای "ids" همیشه برمی‌گرداند. شما می‌توانید با ارسال یک آرایه از نام‌های فیلد به پارامتر includes به متد query یا get، فیلدهایی که باید بازگردانده شوند را مشخص کنید.

collection.get(
    include=["documents"]
)

collection.query(
    query_embeddings=[[11.1, 12.1, 13.1],[1.1, 2.3, 3.2], ...],
    include=["documents"]
)

استفاده از فیلترهای Where

Chroma از فیلتر کردن پرس و جوها بر اساس متاداده و محتوای سند پشتیبانی می‌کند. فیلتر where برای فیلتر کردن متاداده‌ها استفاده می‌شود و فیلتر where_document برای فیلتر کردن محتوای سند استفاده می‌شود، و در زیر توضیح داده می‌شود که چگونه عبارات شرطی فیلتر را نوشت.

فیلتر کردن بر اساس متاداده

برای فیلتر کردن متاداده، شما باید یک دیکشنری فیلتر where را برای پرس و جو ارائه دهید. این دیکشنری باید ساختار زیر را داشته باشد:

{
    "metadata_field": {
        <عملگر>: <مقدار>
    }
}

فیلتر کردن متاداده از عملگرهای زیر پشتیبانی می‌کند:

  • $eq - مساوی با (رشته، عدد صحیح، اعشاری)
  • $ne - نامساوی با (رشته، عدد صحیح، اعشاری)
  • $gt - بزرگتر از (عدد صحیح، اعشاری)
  • $gte - بزرگتر مساوی (عدد صحیح، اعشاری)
  • $lt - کوچکتر از (عدد صحیح، اعشاری)
  • $lte - کوچکتر مساوی (عدد صحیح، اعشاری)

استفاده از عملگر $eq معادل با استفاده از فیلتر where است.

{
    "metadata_field": "search_string"
}

{
    "metadata_field": {
        "$eq": "search_string"
    }
}

فیلتر کردن محتوای سند

برای فیلتر کردن محتوای سند، شما باید یک دیکشنری فیلتر where_document را برای پرس و جو ارائه دهید. این دیکشنری باید ساختار زیر را داشته باشد:

{
    "$contains": "search_string"
}

استفاده از اپراتورهای منطقی

شما همچنین می‌توانید از اپراتورهای منطقی $and و $or برای ترکیب چند فیلتر استفاده کنید.

اپراتور $and نتایجی را که با تمام فیلترهای موجود در لیست مطابقت دارند، برمی‌گرداند.

{
    "$and": [
        {
            "فیلد_متادیتا": {
                <اپراتور>: <مقدار>
            }
        },
        {
            "فیلد_متادیتا": {
                <اپراتور>: <مقدار>
            }
        }
    ]
}

اپراتور $or نتایجی را که با هر یک از شرایط فیلتر در لیست مطابقت دارند، برمی‌گرداند.

{
    "$or": [
        {
            "فیلد_متادیتا": {
                <اپراتور>: <مقدار>
            }
        },
        {
            "فیلد_متادیتا": {
                <اپراتور>: <مقدار>
            }
        }
    ]
}

به‌روزرسانی داده در یک مجموعه

استفاده از .update به شما امکان می‌دهد تا هر یک از خصوصیت‌های داده در یک مجموعه را به‌روز کنید.

collection.update(
    ids=["id1", "id2", "id3", ...],
    embeddings=[[1.1, 2.3, 3.2], [4.5, 6.9, 4.4], [1.1, 2.3, 3.2], ...],
    metadatas=[{"chapter": "3", "verse": "16"}, {"chapter": "3", "verse": "5"}, {"chapter": "29", "verse": "11"}, ...],
    documents=["doc1", "doc2", "doc3", ...],
)

اگر یک شناسه در مجموعه یافت نشود، یک خطا ثبت خواهد شد و به‌روزرسانی نادیده گرفته خواهد شد. اگر سند ارائه شده بردار متناظری نداشته باشد، تابع تعبیه مجموعه برای محاسبه بردار استفاده خواهد شد.

اگر داده بردار فراهم شده از ابعاد مختلفی نسبت به مجموعه باشد، یک استثناء رخ خواهد داد.

کروما همچنین عملیات upsert را پشتیبانی می‌کند، که می‌تواند داده‌های موجود را به‌روز کند و در صورت عدم وجود، داده جدید را درج کند.

collection.upsert(
    ids=["id1", "id2", "id3", ...],
    embeddings=[[1.1, 2.3, 3.2], [4.5, 6.9, 4.4], [1.1, 2.3, 3.2], ...],
    metadatas=[{"chapter": "3", "verse": "16"}, {"chapter": "3", "verse": "5"}, {"chapter": "29", "verse": "11"}, ...],
    documents=["doc1", "doc2", "doc3", ...],
)

حذف داده‌های مجموعه

کروما پشتیبانی می‌کند که از .delete برای حذف داده‌ها از یک مجموعه بر اساس شناسه استفاده کنید. بردارها، اسناد و متادیتاهای مرتبط با هر داده نیز حذف خواهند شد.

collection.delete(
    ids=["id1", "id2", "id3",...],
    where={"chapter": "20"}
)

.delete همچنین یک فیلتر where را پشتیبانی می‌کند. اگر شناسه‌ای ارائه نشود، همه موارد مطابق با فیلتر where را از مجموعه حذف خواهد کرد.