تصویر از TippaPatt در Shutterstock.
تصویر از TippaPatt در Shutterstock.

جستجوی برداری بدون محدودیت: چرا توسعه‌دهندگان ClickHouse را دوست دارند

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

چندین پایگاه داده منبع باز - که تیم‌های توسعه می‌توانند بدون هزینه‌های راه‌اندازی زیاد یا دوره‌های قفل‌شدگی دردناک اتخاذ کنند - در حال حاضر در جستجوی برداری عالی هستند. در حالی که Apache Cassandra 5.0، PostgreSQL و OpenSearch همگی گزینه‌های خوبی هستند، یک جایگزین نوظهور به ویژه در حال حاضر ارزش توجه توسعه‌دهندگان را دارد: ClickHouse، یک پایگاه داده منبع باز که تجزیه و تحلیل با عملکرد بالا را با برخی از قابلیت‌های جستجوی برداری کاملاً چشمگیر ترکیب می‌کند.

ClickHouse از ابتدا برای پردازش تحلیلی آنلاین (OLAP) مجموعه‌های داده بزرگ ساخته شده است. این پایه و اساس برای عملیات جستجوی برداری، به ویژه در مقیاس، عالی است. در حالی که اکثر پایگاه‌های داده برداری تیم‌ها را مجبور می‌کنند زیرساخت‌های جداگانه‌ای برای جستجو و تجزیه و تحلیل بسازند، ClickHouse هر دو را به طور یکپارچه انجام می‌دهد، و آن را به عنوان بارهای کاری هوش مصنوعی پیچیده‌تر، ارزشمندتر می‌کند.

چرا ClickHouse برای جستجوی برداری برجسته است

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

اما داستان یکپارچه‌سازی آن، به نظر من، ClickHouse را به طور خاص جذاب می‌کند. این به طور مستقیم در خطوط لوله داده موجود با پشتیبانی بومی از Apache Kafka و Spark قرار می‌گیرد، در حالی که به خوبی با ابزارهای هوش مصنوعی مانند Hugging Face و LangChain نیز بازی می‌کند. و برخلاف راه‌حل‌های اختصاصی، می‌توانید بدون زیرساخت یا مجوز اضافی، مستقیماً به عملیات برداری بپردازید. همه اینها به طور مستقیم از جعبه بر روی همان معماری با عملکرد بالا پشتیبانی می‌شود.

ساخت یک موتور جستجوی ویکی‌پدیا با ClickHouse

قبل از پرداختن به کد، بیایید از اصطلاحات تخصصی عبور کنیم: جستجوی برداری با تبدیل محتوا (مانند متن، تصاویر یا صدا) به لیستی از اعداد به نام embeddings کار می‌کند. اینها را به عنوان مختصاتی در نظر بگیرید که نحوه مشابه بودن قطعات مختلف محتوا با یکدیگر را ترسیم می‌کنند. هنگامی که در حال ساخت برنامه‌های هوش مصنوعی هستید - به ویژه برنامه‌هایی که نیاز به درک زمینه یا یافتن اطلاعات مرتبط در زمان واقعی دارند - این embeddings سلاح مخفی شما هستند.

بیایید ببینیم این چگونه در عمل کار می‌کند با ساختن چیزی مفید: یک موتور جستجو که می‌تواند با استفاده از مقالات ویکی‌پدیا به عنوان پایگاه دانش خود به سوالات پاسخ دهد.

راه‌اندازی سریع: شروع سریع با Embeddings از پیش ساخته شده

در حالی که می‌توانید embeddings خود را با استفاده از Hugging Face یا LangChain تولید کنید (و من این رویکرد را برای تولید توصیه می‌کنم)، من مثال خود را با استفاده از یک مجموعه داده از پیش ساخته شده سریع‌تر خواهم کرد. انجمن Hugging Face قبلاً embeddings را برای میلیون‌ها مقاله ویکی‌پدیا ایجاد کرده است، که آنها را به صورت رایگان در دسترس قرار داده‌اند. این به ما امکان می‌دهد روی وظیفه اصلی تمرکز کنیم: راه‌اندازی ClickHouse برای جستجوی برداری.

من از یک مجموعه داده استفاده خواهم کرد که شامل متن ویکی‌پدیا، بردارهای embedding و مقادیر فراداده است. embeddings بردارهای 768 بعدی هستند (اساساً لیست‌های طولانی از اعدادی که محتوای هر مقاله را نشان می‌دهند). بیایید قدم به قدم نحوه بارگیری این داده‌ها و شروع اجرای جستجوها را بررسی کنیم.

از مجموعه داده تا موتور جستجوی کارآمد: راهنمای گام به گام

ابتدا، بیایید بررسی کنیم که با چه چیزی کار می‌کنیم. مجموعه داده دارای چند ستون کلیدی است:

  • emb: بردارهای embedding (آرایه‌هایی از 768 شناور که هر مقاله را نشان می‌دهند)
  • text: محتوای واقعی مقاله ویکی‌پدیا
  • title: عناوین مقاله
  • فراداده اضافی مانند تعداد بازدید و اطلاعات زبان

من از دو دستور برای بررسی این داده‌ها در ClickHouse استفاده خواهم کرد:

  • DESCRIBE: برای درک ساختار ستون
  • SELECT: برای نگاهی دزدکی به محتوای واقعی

در اینجا کد برای بررسی مجموعه داده ما آمده است:

-- Describes the content of the parquet file
 DESCRIBE
 url('https://huggingface.co/datasets/Cohere/wikipedia-22-12-simple
 embeddings/resolve/refs%2Fconvert%2Fparquet/default/train/0000.parquet',
 'Parquet')
 SETTINGS enable_url_encoding = 0, max_http_get_redirects = 1;
 

 -- Select lines to get the data in the parquet files
 SELECT *
 FROM
 url('https://huggingface.co/datasets/Cohere/wikipedia-22-12-simple
 embeddings/resolve/refs%2Fconvert%2Fparquet/default/train/0000.parquet',
 'Parquet')
 LIMIT 2
 FORMAT Vertical
 SETTINGS enable_url_encoding = 0, max_http_get_redirects = 1;

نکته‌ای در مورد تنظیمات: من enable_url_encoding = 0 را تنظیم کردم زیرا URL از قبل رمزگذاری شده است، و max_http_get_redirects = 1 را برای اجازه دادن به یک پرش تغییر مسیر هنگام واکشی فایل تنظیم کردم.

اجرای این دستورات نتیجه می‌دهد:

ایجاد جدول جستجوی برداری ما

اکنون که ساختار داده خود را درک می‌کنیم، جدولی برای ذخیره آن ایجاد خواهم کرد. من از موتور MergeTree ClickHouse استفاده خواهم کرد، که برای بارهای کاری تحلیلی مانند جستجوی برداری بهینه شده است:

CREATE TABLE wiki_emb
(
 id UInt32,
 title String,
 text String,
 url String,
 wiki_id UInt32,
 views UInt32,
 paragraph_id UInt32,
 langs UInt32,
 emb Array(Float32)
)
ENGINE = MergeTree
ORDER BY id;

توجه: من در حال حاضر از ستون id به عنوان یک شاخص ساده استفاده می‌کنم. بعداً به بهینه‌سازی‌های عملکرد خواهم پرداخت.

بارگیری مجموعه داده ویکی‌پدیا

اکنون جدول را با داده‌های چندین فایل Parquet پر می‌کنم. چند تنظیم سریع ابتدا:

SET max_http_get_redirects = 1
SET enable_url_encoding = 0

INSERT INTO wiki_emb
SELECT *
FROM (
 SELECT * FROM url('https://huggingface.co/datasets/Cohere/wikipedia-22-12
 simple-embeddings/resolve/refs%2Fconvert%2Fparquet/default/train/0000.parquet',
 'Parquet')
 UNION ALL
 SELECT * FROM url('https://huggingface.co/datasets/Cohere/wikipedia-22-12
 simple-embeddings/resolve/refs%2Fconvert%2Fparquet/default/train/0001.parquet',
 'Parquet')
 UNION ALL
 SELECT * FROM url('https://huggingface.co/datasets/Cohere/wikipedia-22-12
 simple-embeddings/resolve/refs%2Fconvert%2Fparquet/default/train/0002.parquet',
 'Parquet')
 UNION ALL
 SELECT * FROM url('https://huggingface.co/datasets/Cohere/wikipedia-22-12
 simple-embeddings/resolve/refs%2Fconvert%2Fparquet/default/train/0003.parquet',
 'Parquet')
) AS data_sources;

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

قبل از شروع اجرای جستجوها، چند بهینه‌سازی: 1. ابتدا، بردارهای embedding را با استفاده از ZSTD فشرده می‌کنم، که به خوبی با اعداد ممیز شناور کار می‌کند:

ALTER TABLE wiki_emb MODIFY COLUMN emb Array(Float32) CODEC(ZSTD);

توجه داشته باشید که در حالی که روش‌های فشرده‌سازی سنتی مانند LZ4 واقعاً با embeddings به خوبی کار نمی‌کنند، ZSTD می‌تواند به طور قابل توجهی فضای ذخیره‌سازی را بدون تأثیر بر عملکرد کاهش دهد.

2. برای عملکرد درج بهتر، همیشه از درج دسته‌ای برای کاهش سربار استفاده کنید، ستون file_name را برای پیگیری منابع داده در نظر بگیرید و اگر نیاز به کاهش بیشتر فضای ذخیره‌سازی دارید، به کوانتیزاسیون نگاه کنید.

اجرای بردارهای مشابه

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

# Install the Cohere Python SDK
# pip install cohere
import cohere

# Initialize the Cohere client with your API key
api_key = 'your-api-key-here'
co = cohere.Client(api_key)

# Define the text you want to generate embeddings for
text = " Who created Unix " # Replace with your query

# Generate the embeddings using the multilingual-22-12 model
response = co.embed(
 texts=[text],
 model='multilingual-22-12'
)

# Extract the embedding from the response
embedding = response.embeddings[0]

# Print the embedding
print(embedding)

# Verify the length of the embedding

print(f'Length of embedding: {len(embedding)}')
Output:
[0.12451172, 0.20385742, -0.22717285, 0.39697266, -0.04095459
…
0.42578125, 0.23034668, 0.39160156, 0.116760254, 0.046661377, 0.1430664]
Length of embedding: 768

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

یافتن مقالات مشابه

هنگامی که embedding پرس و جو خود را داریم، می‌توانیم از توابع شباهت بردار داخلی ClickHouse برای یافتن مرتبط‌ترین مقالات ویکی‌پدیا استفاده کنیم:

SELECT
 title,
 url,
 paragraph_id,
 text,
 cosineDistance(emb, [Paste the embeddings]) AS distance
FROM wiki_emb
ORDER BY distance ASC
LIMIT 5
FORMAT Vertical;

این از cosineDistance استفاده می‌کند، اما ClickHouse از سایر معیارهای شباهت مانند L2Distance نیز پشتیبانی می‌کند اگر آنها بهتر با نیازهای شما مطابقت داشته باشند.

این پرس و جو مقالات را بر اساس میزان شباهت آنها به عبارت جستجوی ما "چه کسی یونیکس را ایجاد کرد" رتبه‌بندی می‌کند، با امتیازهای فاصله کمتر که نشان‌دهنده تطابق بهتر است.

عملکرد دنیای واقعی

اجرای این تنظیمات بر روی سخت‌افزار متوسط ​​(8 گیگابایت رم، 4 CPU) نتایج چشمگیری به دست آورد:

  • زمان پرس و جو: 0.633 ثانیه
  • اندازه مجموعه داده: 485,859 ردیف
  • بدون تنظیم یا بهینه‌سازی خاص

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