مدلهای زبانی بزرگ (LLM) هر روز در حال تکامل هستند و این وضعیت به گسترش جستجوی معنایی یا semantic search کمک میکند. LLM ها در تجزیه و تحلیل متون و آشکارسازی شباهتهای معنایی برتری دارند. این وضعیت همچنین در موتورهای جستجو منعکس میشود زیرا موتورهای جستجوی معنایی میتوانند نتایج بیشتری را برای کاربران ارائه دهند.
با اینکه مدلهای زبانی بزرگ میتوانند نتایج نزدیک به معنا را دریافت کنند، پیادهسازی filter ها در نتایج جستجو برای بهبود تجربه کاربر ضروری است. به عنوان مثال، اضافه کردن filter های مبتنی بر تاریخ یا دستهبندیها میتواند به تجربه جستجوی بیشتری کمک کند. بنابراین، چگونه میتوانیم به طور موثر جستجوی معنایی را با filtering ترکیب کنیم؟
بیایید ابتدا با اتصال ElasticSearch و query های اولیه جستجو شروع کنیم:
from elasticsearch import Elasticsearch import config as cfg client = Elasticsearch( 'https://localhost:9200', ssl_assert_fingerprint=cfg.ES_FINGERPRINT, basic_auth=('elastic', cfg.ES_PASSWORD) )
من اطلاعات لازم برای connection را از یک config file میخوانم، و این جزئیات به صورت خودکار زمانی که Elasticsearch برای اولین بار راهاندازی میشود، ارائه میشوند.
[ { "title": "Data Structures and Algorithms", "date": "2023-08-02", "author": "Emily Johnson" }, { "title": "Artificial Intelligence Trends", "date": "2023-08-01", "author": "William Smith" }, ... ]
مجموعه دادهای که در این پست استفاده خواهم کرد توسط ChatGPT تولید شده و format ای را که در بالا توضیح داده شده است، رعایت میکند.
بیایید دادههایمان را با استفاده از این فایل JSON بخوانیم و یک ایندکس Elasticsearch را بر اساس این format ایجاد کنیم، سپس دادهها را به آن اضافه کنیم.
book_mappings = { "mappings": { "properties": { "title": {"type": "text"}, "author": {"type": "text"}, "date": {"type": "date"} } } } client.indices.create(index = "book_index", body=book_mappings) import json with open('data.json', 'r') as f: data = json.load(f) for each in data: client.index(index='book_index', document=each) client.indices.refresh()
در مجموعه دادهای که ایجاد کردهایم، سه فیلد وجود دارد، دو تای آنها به صورت متن و یکی به صورت تاریخ format شدهاند. سپس، از این mapping برای ایجاد یک index استفاده میکنیم، که آن را “book_index” مینامیم. از آنجا که دادهها و index ما در یک format هستند، در این مرحله نیازی به پردازش اضافی نیست.
بیایید با یک query شروع کنیم که تمام document های داخل index را بازیابی میکند:
برای اعمال filtering به document های داخل index، باید پارامتر “query” را تغییر دهیم. برای جستجوی کلمات درون متن، از “match” استفاده خواهیم کرد:
ما document هایی را در index لیست کردیم که کلمه “Data” در فیلد “title” آنها وجود دارد.
اگر میخواهید filtering را بر روی چندین فیلد اعمال کنید، میتوانید با استفاده از “bool” این کار را انجام دهید.
حالا، بیایید همان index را با document vector ها ایجاد کنیم. برای این پست، من از کتابخانه Sentence-Transformers و مدل ‘all-mpnet-base-v2’ استفاده خواهم کرد. محدودیتی در استفاده از مدل وجود ندارد بنابراین میتوانید هر مدلی که میخواهید را انتخاب کنید.
vector_mapping = { "mappings": { "properties": { "title": {"type": "text"}, "author": {"type": "text"}, "date": {"type": "date"}, "vector": { "type": "dense_vector", "dims": 768, "index": True, "similarity": "dot_product" } } } } client.indices.create(index='vector_index', body= vector_mapping)
در حالی که این بار “vector_index” را ایجاد میکنیم، یک فیلد اضافی از نوع “dense_vector” اضافه میکنیم و پارامترهای vector search را مشخص میکنیم:
from sentence_transformers import SentenceTransformer model = SentenceTransformer('all-mpnet-base-v2') for each in data: each['vector'] = model.encode(each['title']) client.index(index='vector_index', document=each) client.indices.refresh()
بیایید مدل را با استفاده از کتابخانه Sentence-Transformers بارگذاری کنیم و vector ها را از بخشهای "title" مجموعه داده استخراج کنیم. سپس این vector ها را به هر ورودی داده اضافه کرده و ادامه میدهیم تا این دادهها را به ایندکس "vector_index" اضافه کنیم.
برای انجام vector search در Elasticsearch، ابتدا به یک متن query و سپس نمایش برداری متناظر آن نیاز داریم.
توجه مهم: مدلی که برای به دست آوردن بردار query استفاده میشود باید همان مدل باشد که هنگام index کردن document ها استفاده شده است.
برای انجام vector search، تابع Elasticsearch.search() از پارامتر "knn" استفاده میکند. یک نمونه از query برای "knn" در تصویر زیر نشان داده شده است. مقدار "k" نشان میدهد که چه تعداد نتیجه میخواهید بازیابی کنید، در حالی که "num_candidates" مشخص میکند که چند document برای محاسبات در pool گرفته میشوند. "query_vector" نمایش برداری از متن query است (در مورد ما "HTML and CSS programming").
نتایج برای query نمونه در تصویر بالا قابل مشاهده است. با اینکه هیچ یک از نتایج بازگشتی دقیقاً همان کلمات را در بر ندارند، اما با موفقیت نتایج مشابه از نظر معنایی را جمعآوری کردهاند.
پس، اگر ما هم میخواهیم از این نتایج جستجوی معنایی به همراه filtering استفاده کنیم، چگونه باید کوئری “knn” را آماده کنیم؟
هر filter ای که اعمال میکنیم به عنوان یک “filter” درون پارامتر “knn” ارائه میشود. شما میتوانید در اینجا هر تعداد filter ای که میخواهید اضافه کنید و نتایج را بر اساس این filter ها ترکیب کنید.
توجه مهم: Elasticsearch پس از فرآیند vector search، عمل filtering را انجام میدهد، بنابراین ممکن است مواردی وجود داشته باشد که نتواند دقیقاً تعداد “k” نتایج را برگرداند. در تصویر بالا، با اینکه مقدار “k” به 5 تنظیم شده است، query تعداد 3 سند یا document را به عنوان نتیجه برگردانده است. این بخاطر آن است که در مجموعه دادهای که به عنوان مثال آماده شده است، فقط 3 سند معیارهای مشخص شده را برآورده میکنند.
باتشکر از
https://medium.com/@fatihsati/how-to-combine-vector-search-with-filtering-in-elasticsearch-b938ec78d179