«جستوجو» یکی از مفاهیم بنیادی در علوم کامپیوتر است؛ از پیدا کردن یک فایل در سیستم شخصی گرفته تا جستوجو در گوگل، همه به نوعی از موتورهای جستوجو استفاده میکنند. در گذشته، این سیستمها بر اساس تطبیق مستقیم کلیدواژهها کار میکردند اما با پیچیدهتر شدن زبان، این روشها محدودیتهای خود را نشان دادند چون فقط شباهت ظاهری واژهها را میفهمیدند، نه معنای پشت آنها. اما در جستجوی برداری، به جای تطبیق دقیق کلمات، مفاهیم و معانی با استفاده از بردارهای عددی در فضای چندبعدی مقایسه میشوند تا نتایجی مرتبطتر و هوشمندانهتر به دست آید.
در این مقاله، با وکتور سرچ آشنا میشویم؛ روشی که به جای تطبیق کلمات، معنا را مقایسه میکند. قدمبهقدم خواهیم دید که این فرایند چگونه کار میکند و چطور میتوان یک نمونه ساده از آن را در پایتون ساخت؛ از ایجاد بردارهای کلمه گرفته تا محاسبه شباهت و تجسم نتایج در فضا. با ما همراه باشید.
درک مفهوم Vector Search در یک نگاه
مشکل اصلی جستوجوی کلیدواژهای این بود که تنها بر پایه تطبیق دقیق واژهها کار میکرد. در نتیجه، اگر کاربر عبارتی مانند «automobile repair» را جستوجو میکرد، سیستمی که در متنش «car maintenance» آمده بود، نادیده گرفته میشد؛ چون معنا را نمیفهمید.
برای حل این محدودیت، جستجوی برداری (Vector Search) معرفی شد؛ روشی که بهجای تکیه بر تطبیق ظاهری کلمات، مفاهیم را بهصورت بردارهای عددی چندبعدی نمایش میدهد. در این مدل، هم اسناد و هم پرسوجو به بردارهایی در یک فضای معنایی تبدیل میشوند و سیستم نزدیکترین بردارها به پرسوجو را پیدا میکند. نتیجه، جستوجویی است که بر پایهی درک معنا و زمینه کار میکند، نه صرفا تکرار واژهها.
جستجوی برداری چگونه کار میکند؟

در هسته اصلی، وکتور سرچ سه مرحله دارد:
- نمایش برداری (Vector Representation): دادهها (مثل متن یا تصویر) به بردارهای عددی تبدیل میشوند. این کار معمولا با تکنیکهایی مثل word embeddings یا شبکههای عصبی انجام میشود. هر بردار، داده را در یک فضای چندبعدی نمایش میدهد.
- محاسبه شباهت (Similarity Calculation): سیستم بررسی میکند که بردار پرسوجو چقدر به سایر بردارهای موجود در دیتاست نزدیک است. این اندازهگیری معمولا با معیارهایی مثل شباهت کسینوسی یا فاصله اقلیدسی انجام میشود. هرچه بردارها نزدیکتر باشند، میزان شباهت بیشتر است.
- بازیابی (Retrieval): بر اساس نمرههای شباهت، k نتیجه برتر که بیشترین ارتباط را دارند بازگردانده میشوند.
برای مثال، اگر به دنبال اسنادی دربارهی «یادگیری ماشین» باشید، ابتدا عبارت «machine learning» به یک بردار تبدیل میشود و سیستم اسنادی را پیدا میکند که بردارهایشان به این پرسوجو نزدیکتر است، حتی اگر در متن آنها عباراتی مثل «هوش مصنوعی» یا «یادگیری عمیق» آمده باشد.
حالا بیایید یک سیستم جستجوی برداری ساده را از صفر با پایتون بسازیم. ما از یک دیتاست کوچک شامل چند جمله استفاده میکنیم، آنها را به بردار تبدیل میکنیم (با استفاده از میانگین بردارهای کلمات برای سادهسازی)، سپس یک تابع جستجو پیادهسازی میکنیم. در نهایت، با تجسم (Visualization) نشان میدهیم که این بردارها در فضا چطور خوشهبندی میشوند.
مرحله ۱: راهاندازی محیط
برای ساده نگه داشتن کار، از NumPy برای عملیات برداری و از Matplotlib برای تجسم دادهها استفاده میکنیم. عمدا از کتابخانههایی مثل FAISS یا spaCy صرفنظر میکنیم تا تمرکز روی پیادهسازی «از صفر» باقی بماند.
برای نمایش برداری کلمات هم بهجای مدلهای واقعی، از یک دیکشنری کوچک و از پیش تعریفشده استفاده میکنیم تا فرایند شبیهسازی شود. البته در عمل، شما معمولا از مدلهایی مثل Word2Vec، GloVe یا BERT استفاده خواهید کرد.
حالا بیایید بستههای لازم را (در صورت نیاز) نصب کنیم و ایمپورتها را تنظیم کنیم:
|
1 2 3 4 |
import numpy as np import matplotlib.pyplot as plt from collections import defaultdict import re |
ما از NumPy برای محاسبات برداری، Matplotlib برای رسم نمودارها و پایتون ساده برای پردازش متن استفاده میکنیم. همچنین ماژول re به ما کمک میکند تا متن را توکنایز (شکستن جملات به کلمات) کنیم.
مرحله ۲: ساخت دیتاست نمونه و امبدینگهای کلمه
در این مرحله، با یک دیتاست کوچک از جملههایی درباره فناوری کار میکنیم. برای نمایش کلمات بهصورت بردار، یک دیکشنری ساده از word embeddingها میسازیم که در آن هر کلمه به یک بردار دوبعدی نگاشت میشود (تا بتوان آن را بهسادگی تصویرسازی کرد).
بردارها بهصورت دلخواه تعریف میشوند اما طوری طراحی شدهاند که کلمات مرتبط در فضا بههم نزدیک باشند (مثلا «machine» و «neural» در نزدیکی هم قرار میگیرند).
در کاربردهای واقعی معمولا از مدلهای ازپیش آموزشدیده برای embedding استفاده میشود اما در این آموزش هدف ما این است که پیادهسازی ساده و مستقل از منابع بیرونی باشد.
|
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 |
# Toy dataset of sentences documents = [ “Machine learning is powerful”, “Artificial intelligence advances rapidly”, “Deep learning transforms technology”, “Data science drives innovation”, “Neural networks power AI” ] # Simplified 2D word embeddings (in practice, use pre-trained embeddings) word_embeddings = { “machine”: [0.8, 0.2], “learning”: [0.7, 0.3], “powerful”: [0.6, 0.4], “artificial”: [0.9, 0.1], “intelligence”: [0.85, 0.15], “advances”: [0.5, 0.5], “rapidly”: [0.4, 0.6], “deep”: [0.75, 0.25], “transforms”: [0.65, 0.35], “technology”: [0.7, 0.4], “data”: [0.3, 0.7], “science”: [0.35, 0.65], “drives”: [0.4, 0.6], “innovation”: [0.45, 0.55], “neural”: [0.8, 0.2], “networks”: [0.78, 0.22], “power”: [0.6, 0.4], “ai”: [0.9, 0.1] } |
مرحله ۳: تبدیل جملهها به بردار
برای جستوجو در میان متون، باید هر جمله را به یک بردار واحد تبدیل کنیم.
یک روش ساده برای این کار، میانگینگیری از بردارهای کلمات موجود در جمله است (بعد از توکنسازی و حذف کلمات توقف یا stopwords). این کار باعث میشود مفهوم کلی یا «میانگین معنایی» جمله در قالب یک بردار نمایش داده شود.
اگر کلمهای در دیکشنری word_embeddings وجود نداشته باشد، از بردار صفر برای آن استفاده میکنیم؛ هرچند در پروژههای واقعی معمولا برای کلمات ناشناخته روشهای دقیقتری بهکار میرود.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
def tokenize(text): “”“Convert text to lowercase and split into words.”“” return re.findall(r‘\b\w+\b’, text.lower()) def sentence_to_vector(sentence, embeddings): “”“Convert a sentence to a vector by averaging word embeddings.”“” words = tokenize(sentence) vectors = [embeddings.get(word, [0, 0]) for word in words] vectors = [v for v in vectors if sum(v) != 0] # Remove unknown words if not vectors: return np.zeros(2) # Return zero vector if no valid words return np.mean(vectors, axis=0) # Convert all documents to vectors doc_vectors = [sentence_to_vector(doc, word_embeddings) for doc in documents] |
مرحله ۴: پیادهسازی شباهت کسینوسی (Cosine Similarity)
شباهت کسینوسی یکی از متداولترین معیارها برای جستجوی برداری است، چون زاویه بین دو بردار را اندازهگیری میکند و بزرگی (magnitude) آنها را نادیده میگیرد. به همین دلیل، گزینهای عالی برای مقایسه شباهت معنایی در embeddingهای متنی محسوب میشود.
فرمول آن به این شکل است: حاصلضرب نقطهای (dot product) دو بردار تقسیم بر حاصلضرب اندازهی آنها (norms).
اگر یکی از بردارها صفر باشد (مثلا جمله هیچ کلمه معتبری نداشته باشد)، مقدار خروجی را صفر در نظر میگیریم تا از تقسیم بر صفر جلوگیری شود.
این تابع در نهایت برای مقایسه بردار عبارت جستوجو با بردارهای جملات (یا اسناد) استفاده میشود.
|
1 2 3 4 5 6 7 8 |
def cosine_similarity(vec1, vec2): “”“Compute cosine similarity between two vectors.”“” dot_product = np.dot(vec1, vec2) norm1 = np.linalg.norm(vec1) norm2 = np.linalg.norm(vec2) if norm1 == 0 or norm2 == 0: return 0.0 return dot_product / (norm1 * norm2) |
گام ۵: ساخت تابع جستوجوی برداری
حالا هسته جستوجو را میسازیم: تابعی که یک query دریافت میکند، آن را به بردار تبدیل میکند، شباهت کسینوسی را با هر بردار سند محاسبه میکند و در نهایت top-k سند برتر را همراه با امتیاز شباهت برمیگرداند. برای رتبهبندی از np.argsort استفاده میکنیم و نتایج با امتیاز صفر را (برای پرسوجوهای بیاعتبار) کنار میگذاریم.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
def vector_search(query, documents, embeddings, top_k=3): “”“Perform vector search and return top-k similar documents.”“” query_vector = sentence_to_vector(query, embeddings) similarities = [cosine_similarity(query_vector, doc_vec) for doc_vec in doc_vectors] # Get indices of top-k similarities ranked_indices = np.argsort(similarities)[::–1][:top_k] results = [ (documents[i], similarities[i]) for i in ranked_indices if similarities[i] > 0 ] return results # Example query query = “Machine learning technology” results = vector_search(query, documents, word_embeddings) print(“Query:”, query) print(“Top results:”) for doc, score in results: print(f“Score: {score:.3f}, Document: {doc}”) |
|
1 2 3 4 5 6 |
<strong>Output:</strong> Query: Machine learning technology Top results: Score: 1.000, Document: Machine learning is powerful Score: 0.999, Document: Deep learning transforms technology Score: 0.997, Document: Artificial intelligence advances rapidly |
همانطور که میبینید، این تابع بهخوبی مرتبطترین جملات از نظر معنایی را نسبت به پرسوجو بازیابی میکند.
با اینکه هیچکدام از آنها عبارت دقیق «machine learning technology» را در خود ندارند اما از نظر مفهومی بسیار نزدیکاند و این دقیقا همان قدرت جستوجوی مبتنی بر بردار است.
مرحله ۶: بصریسازی بردارها
برای درک بهترشوهی کار وکتور سرچ، بردارهای اسناد و بردار پرسوجو را در فضای دوبعدی ترسیم میکنیم. این کار نشان میدهد آیتمهای معناییِ مشابه چگونه در کنار هم خوشهبندی میشوند و فاصله زاویهایشان (مبنای شباهت کسینوسی) چه الگوی بصریای ایجاد میکند.
|
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 |
def plot_vectors(doc_vectors, documents, query, query_vector): “”“Plot document and query vectors in 2D space.”“” plt.figure(figsize=(8, 6)) # Plot document vectors doc_x, doc_y = zip(*doc_vectors) plt.scatter(doc_x, doc_y, c=‘blue’, label=‘Documents’, s=100) for i, doc in enumerate(documents): plt.annotate(doc[:20] + “…”, (doc_x[i], doc_y[i])) # Plot query vector plt.scatter(query_vector[0], query_vector[1], c=‘red’, label=‘Query’, s=200, marker=‘*’) plt.annotate(query[:20], (query_vector[0], query_vector[1]), color=‘red’) plt.title(‘Vector Search: Document and Query Vectors’) plt.xlabel(‘Dimension 1’) plt.ylabel(‘Dimension 2’) plt.legend() plt.grid(True) plt.show() plt.close() # Generate the plot query_vector = sentence_to_vector(query, word_embeddings) plot_vectors(doc_vectors, documents, query, query_vector) |

نقاط آبی نشاندهنده بردارهای اسناد هستند و ستاره قرمز، بردار پرسوجو را نمایش میدهد. برچسبها (annotationها) شامل ۲۰ نویسه اول هر جمله و پرسوجو هستند.
همانطور که مشاهده میکنید، اسنادی که به بردار پرسوجو نزدیکترند، از نظر معنایی نیز شباهت بیشتری دارند و این بهصورت بصری تایید میکند که جستوجوی برداری چگونه عمل میکند.
چرا این موضوع برای RAG اهمیت دارد
در معماری RAG، جستوجوی برداری ستون فقرات مرحلهی بازیابی (Retrieval) است. با تبدیل اسناد و پرسوجوها به بردار، RAG میتواند اطلاعات مرتبط از نظر زمینه و مفهوم را حتی در پرسوجوهای پیچیده پیدا کند.
پیادهسازی سادهای که در این آموزش دیدیم، همین فرایند را شبیهسازی میکند: بردار پرسوجو، اسنادی را بازیابی میکند که از نظر معنایی به آن نزدیکتر هستند و سپس مدل زبانی میتواند از این اسناد برای تولید پاسخ استفاده کند.در کاربردهای واقعی، این فرایند با embeddingهای پُربعدتر و الگوریتمهای جستوجوی بهینهتر (مثل HNSW یا IVF) مقیاسپذیر میشود اما ایده اصلی همان است.
جمعبندی
در این آموزش، جستوجوی برداری را از صفر با پایتون پیادهسازی کردیم. میتوانید این پیادهسازی را گسترش دهید؛ مثلا با استفاده از embeddingهای واقعی (مانند مدلهای موجود در کتابخانه Transformers از Hugging Face) یا با بهکارگیری روشهای جستوجوی تقریبی نزدیکترین همسایهها (Approximate Nearest Neighbor).
پیشنهاد میکنیم با پرسوجوها یا دیتاستهای مختلف آزمایش کنید تا درک عمیقتری ازشوهی کار جستوجوی برداری و نقش آن در سیستمهای هوشمند امروزی به دست آورید.
منابع
سوالات متداول
در جستوجوی کلیدواژهای، سیستم فقط بهدنبال تطابق دقیق واژهها میگردد، در حالیکه جستوجوی برداری بر پایه معناست. در روش برداری، پرسوجو و اسناد به بردارهایی در فضای عددی تبدیل میشوند و سیستم مواردی را پیدا میکند که از نظر معنایی نزدیکترند، حتی اگر از کلمات متفاوتی استفاده شده باشد.
در معماری RAG، هدف این است که مدل زبانی بتواند پاسخهایی بر پایه دادههای مرتبط و واقعی تولید کند. جستوجوی برداری به RAG کمک میکند تا اسنادی را بازیابی کند که از نظر مفهومی به پرسوجو نزدیکاند، نه فقط از نظر ظاهری و همین دقت پاسخها را بهطور چشمگیری افزایش میدهد.
بله، همانطور که در این مقاله دیدیم، میتوان منطق پایه جستوجوی برداری را با چند خط کد پایتون و استفاده از NumPy پیادهسازی کرد. البته برای مقیاسهای بزرگ یا دادههای واقعی، باید از embeddingهای چندبعدیتر و کتابخانههایی مثل FAISS، Annoy یا HNSWlib استفاده کرد تا کارایی و سرعت افزایش پیدا کند.




دیدگاهتان را بنویسید