0Pricing
AI Engineering Academy · Ders

BM25 Anahtar Sözcük Aramasını Uygulama

Python'da rank_bm25 kullanarak BM25'i kurun, belge derlemenizi dizinleyin ve tam terimleri, teknik jargonları ve ürün adlarını güvenilir biçimde işleyen anahtar sözcük aramaları çalıştırın.

BM25 Anahtar Sözcük Aramasını Uygulama, CoddyKit'te ücretsiz bir AI Engineering Academy dersidir. Bu, 4 dersinin 2. dersidir. Aşağıdan dersin tamamını ücretsiz okuyabilir, sonra tarayıcıda yerleşik kod editörü ve 7/24 yapay zeka koçu ile uygulamalı olarak pratik yapabilirsin. Bu, AI Engineering Academy öğrenme yolunun bir parçasıdır ve ilerlemeniz web ve CoddyKit uygulaması arasında senkronize olur. AI Engineering Academy kursu toplamda 4 dersten oluşur.

rank_bm25 Kurulumu

rank_bm25, BM25 algoritmasının BM25Okapi, BM25L ve BM25Plus varyantlarını sağlayan hafif bir Python kütüphanesidir. Harici hizmet gerektirmez, tamamen bellek içinde çalışır ve sıradan donanımlarda binlerce belgeyi saniyeler içinde dizine ekleyebilir. pip install rank-bm25 komutuyla kurduğunuzda herhangi bir altyapı kurulumu yapmadan anahtar kelime araması oluşturmaya hazırsınız.

# Install: pip install rank-bm25
from rank_bm25 import BM25Okapi

# BM25Okapi is the most common variant
# BM25L and BM25Plus handle very short documents better
# For most RAG use cases BM25Okapi is the right choice

corpus = [
    'Python decorator pattern explained with examples',
    'How to use context managers in Python',
    'JavaScript async await tutorial',
]
tokenized = [doc.lower().split() for doc in corpus]
bm25 = BM25Okapi(tokenized)
print('Index built with', len(corpus), 'documents')

Belirteçlere Ayırma: Kritik İlk Adım

BM25, ham dizelerle değil belirteç listeleriyle çalışır. Belirteçlere ayırma işleminizin kalitesi, arama kalitesini doğrudan etkiler. Basit boşluklara göre bölme; noktalama işaretlerini kaldırmayı, kök bulmayı ve durak kelimeleri çıkarmayı gözden kaçırır. Üretim sistemlerinde metni küçük harfe dönüştüren, noktalama işaretlerini kaldıran, durak kelimeleri çıkaran ve isteğe bağlı olarak 'run', 'runs' ve 'running' gibi biçimbilimsel varyantları eşleştirmek için kök bulma uygulayan uygun bir belirteçleyici kullanın.

import re
from nltk.corpus import stopwords
from nltk.stem import PorterStemmer

STOP_WORDS = set(stopwords.words('english'))
stemmer = PorterStemmer()

def tokenize(text: str) -> list[str]:
    text = text.lower()
    text = re.sub(r'[^a-z0-9\s]', ' ', text)
    tokens = text.split()
    tokens = [t for t in tokens if t not in STOP_WORDS and len(t) > 1]
    tokens = [stemmer.stem(t) for t in tokens]
    return tokens

print(tokenize('Running Python decorators efficiently in production!'))
# ['run', 'python', 'decor', 'effici', 'product']

BM25 Dizini Oluşturma

BM25 dizini oluşturmak, bir kez gerçekleştirilen çevrim dışı bir işlemdir. Belirteçlere ayrılmış derlemi BM25Okapi'ye verirsiniz; o da tüm terimler için ters belge sıklıklarını hesaplar ve normalleştirme amacıyla belge uzunluklarını saklar. Dizin hafiftir; on binlerce belge için bile birkaç megabayt yer kaplar. Derleminize yeni belgeler eklendiğinde dizini yeniden oluşturmalısınız.

from rank_bm25 import BM25Okapi

def build_bm25_index(documents: list[str]):
    tokenized = [tokenize(doc) for doc in documents]
    bm25 = BM25Okapi(tokenized)
    return bm25, tokenized

# Example with a small corpus
docs = [
    'Vector databases store dense embeddings for similarity search',
    'BM25 is a sparse keyword retrieval algorithm used in search engines',
    'Hybrid search combines dense and sparse retrieval for better recall',
    'PostgreSQL supports vector search via the pgvector extension',
]
bm25, tokenized = build_bm25_index(docs)
print(f'Index contains {bm25.corpus_size} documents')

BM25 Araması Gerçekleştirme

Arama yapmak için sorguyu, dizinde kullanılan aynı belirteçleyiciyle belirteçlere ayırın; tutarsız belirteçlere ayırma, kötü arama sonuçlarının yaygın bir nedenidir. Tüm belgelerin uygunluk puanlarını almak için get_scores işlevini, en yüksek N sonucu doğrudan almak içinse get_top_n işlevini çağırın. Hem dizin oluşturma hem de sorgulama için her zaman aynı ön işleme akışını kullanın.

def bm25_search(bm25, documents: list[str], query: str, top_k: int = 3):
    query_tokens = tokenize(query)
    scores = bm25.get_scores(query_tokens)

    # Get indices sorted by score descending
    ranked = sorted(enumerate(scores), key=lambda x: x[1], reverse=True)

    results = []
    for idx, score in ranked[:top_k]:
        results.append({
            'document': documents[idx],
            'score': round(score, 4),
            'rank': len(results) + 1,
        })
    return results

results = bm25_search(bm25, docs, 'sparse keyword search engine')
for r in results:
    print(f"Rank {r['rank']} (score {r['score']}): {r['document'][:60]}")

BM25 Hiperparametrelerini Ayarlama

BM25Okapi iki hiperparametre kabul eder: k1 terim sıklığının doygunluğunu denetler (daha yüksek değerler, sık geçen terimlerin daha yüksek puan almasını sağlar), b ise belge uzunluğunun normalleştirilmesini denetler (1.0 = tam normalleştirme, 0.0 = normalleştirme yok). k1=1.5, b=0.75 varsayılanları düzyazı için iyi çalışır. Kısa parçalar için (100 kelimenin altında) uzunluk yanlılığını azaltmak amacıyla 0.3 gibi daha düşük b değerlerini deneyin.

from rank_bm25 import BM25Okapi

# Default hyperparameters — good starting point
bm25_default = BM25Okapi(tokenized, k1=1.5, b=0.75)

# Tuned for short document chunks
bm25_short = BM25Okapi(tokenized, k1=1.2, b=0.3)

# Tuned for long documents
bm25_long = BM25Okapi(tokenized, k1=2.0, b=0.9)

# Always benchmark hyperparameters against a golden eval set
# before deploying to production

Teknik Jargon ve Kod Belirteçlerini İşleme

Kod tabanları ve teknik belgeler için belirteçleyiciniz, teknik belirteçleri aşırı derecede köklerine ayırmak yerine korumalıdır. BM25Okapi, pgvector ve LLM gibi terimler olduğu gibi kalmalıdır. Büyük harfli kısaltmalar, CamelCase veya snake_case tanımlayıcıları gibi kalıplarla eşleşen belirteçlerde kök bulmayı atlayan hibrit bir belirteçleyici, geliştiricilere yönelik aramalarda daha iyi sonuçlar verir.

import re

def technical_tokenize(text: str) -> list[str]:
    text = text.lower()
    # preserve underscores in snake_case and dots in version numbers
    text = re.sub(r'[^a-z0-9_.\s]', ' ', text)
    tokens = text.split()
    # keep tokens that look like identifiers (contain _ or .)
    tokens = [
        t for t in tokens
        if len(t) > 1 and t not in STOP_WORDS
    ]
    return tokens

print(technical_tokenize('Install pgvector 0.5.1 extension in PostgreSQL 16'))
# ['pgvector', '0.5.1', 'extension', 'postgresql', '16']

BM25 Dizinini Kalıcı Hâle Getirme

Yeniden dizine ekleme maliyetinden kaçınmak için BM25 dizinleri uygulama yeniden başlatmaları arasında diske kalıcı olarak kaydedilmelidir. rank_bm25 nesneleri sıradan Python nesneleri olduğundan pickle ile serileştirebilirsiniz. Daha büyük derlemler için hem dizini hem de puanlamadan sonra metni alabilmek üzere özgün belge listesini kaydedin. Güvenilmeyen girdilere karşı güvenli olmadıkları için hassas verileri pickle dosyalarında asla saklamayın.

import pickle

def save_bm25_index(bm25, documents: list[str], path: str):
    with open(path, 'wb') as f:
        pickle.dump({'bm25': bm25, 'documents': documents}, f)
    print(f'Index saved to {path}')

def load_bm25_index(path: str):
    with open(path, 'rb') as f:
        data = pickle.load(f)
    return data['bm25'], data['documents']

save_bm25_index(bm25, docs, '/tmp/bm25_index.pkl')
bm25_loaded, docs_loaded = load_bm25_index('/tmp/bm25_index.pkl')

Artımlı Dizin Güncellemeleri

BM25 artımlı güncellemeleri desteklemez; yeni belgeler geldiğinde dizinin tamamını yeniden oluşturmanız gerekir. Sık değişen derlemler için pratik çözüm toplu güncellemelerdir: yeni belgeleri belirli bir zaman aralığında toplayın, ardından dizini kritik yolun dışında yeniden oluşturun. Bir dizin canlı trafiğe hizmet verirken diğeri yeniden oluşturulacak şekilde çift tamponlama düzenini kullanın; ardından bunları atomik olarak değiştirin.

import threading

class SwappableBM25Index:
    def __init__(self):
        self._index = None
        self._docs = []
        self._lock = threading.RLock()

    def rebuild(self, new_docs: list[str]):
        tokenized = [tokenize(d) for d in new_docs]
        new_index = BM25Okapi(tokenized)
        with self._lock:
            self._index = new_index
            self._docs = new_docs
        print(f'Index rebuilt with {len(new_docs)} documents')

    def search(self, query: str, top_k: int = 5):
        with self._lock:
            return bm25_search(self._index, self._docs, query, top_k)

BM25'yi LangChain ile Entegre Etme

LangChain, BM25 aramasını standart bir arama bileşeni arayüzüne entegre eden bir BM25Retriever sarmalayıcısı sağlar. Bu sayede BM25'yi LCEL zincirlerinde doğrudan kullanılabilen bir bileşen olarak kullanabilir ve EnsembleRetriever ile vektör arama bileşenlerini birleştirebilirsiniz. Ağırlık parametresi, son sıralamada BM25'nin yoğun arama bileşenine kıyasla ne kadar etkili olacağını denetler.

from langchain_community.retrievers import BM25Retriever
from langchain.retrievers import EnsembleRetriever
from langchain_core.documents import Document

langchain_docs = [Document(page_content=d) for d in docs]

bm25_retriever = BM25Retriever.from_documents(langchain_docs)
bm25_retriever.k = 5

# Combine with a vector retriever (assuming vector_retriever is already defined)
# ensemble = EnsembleRetriever(
#     retrievers=[bm25_retriever, vector_retriever],
#     weights=[0.4, 0.6],  # 40% BM25, 60% dense
# )

results = bm25_retriever.invoke('sparse keyword search')
for doc in results:
    print(doc.page_content[:80])

BM25 Kalitesini Değerlendirme

BM25 arama kalitesini ölçmek için sorguları bilinen ilgili belgelerle eşleştiren bir altın veri kümesi oluşturun. K'deki isabet oranını (ilgili belgenin en yüksek K sonuç arasında görünüp görünmediğini) ve MRR'yi (ortalama karşılıklı sıralamayı) hesaplayın. Hibrit sisteminizdeki en uygun ağırlıklandırmayı belirlemek için bu sayıları aynı test kümesindeki yoğun aramayla karşılaştırın.

def hit_rate_at_k(bm25, documents, queries, relevant_docs, k=5):
    hits = 0
    for query, relevant in zip(queries, relevant_docs):
        results = bm25_search(bm25, documents, query, top_k=k)
        retrieved = [r['document'] for r in results]
        if relevant in retrieved:
            hits += 1
    return hits / len(queries)

# Example evaluation
test_queries = ['BM25 algorithm', 'hybrid search systems']
test_relevant = [
    'BM25 is a sparse keyword retrieval algorithm used in search engines',
    'Hybrid search combines dense and sparse retrieval for better recall',
]
hit_rate = hit_rate_at_k(bm25, docs, test_queries, test_relevant, k=3)
print(f'Hit rate @3: {hit_rate:.2%}')

Ölçekli Üretim BM25 Kullanımı

Milyonlarca belge içeren derlemlerde saf Python ile çalışan rank_bm25 çok yavaş kalır. Üretim ölçeğinde BM25; Elasticsearch ve OpenSearch'te (ikisi de varsayılan puanlama işlevi olarak BM25 kullanır), Typesense'te ve Qdrant'ın seyrek vektör kipinde kullanılabilir. Bu sistemler ters dizinleri diskte tutar, kısmi güncellemeleri destekler ve dizinin tamamını yeniden oluşturmadan eşzamanlı sorguları işler.

Hızlı Kontrol

Bu derste ele alınan BM25 anahtar kelime araması uygulamasını anlayıp anlamadığınızı test edin.

Ders Özeti

Bu derste şunları öğrendiniz: rank_bm25, belirteçlere ayrılmış girdi gerektiren bellek içi bir BM25 dizini sağlar; dizin oluşturma ve sorgulama arasındaki tutarlı belirteçlere ayırma, doğru puanlama için zorunludur; k1 ve b hiperparametreleri ise belge uzunluklarının dağılımına göre ayarlanabilir. Ölçekli üretim kullanımı için bellek içi BM25 yerine Elasticsearch veya OpenSearch kullanın. Sırada BM25 ve yoğun arama sonuçlarını birleştirmek için karşılıklı sıralama birleştirmesini uygulayacağız.

Sıkça Sorulan Sorular

“BM25 Anahtar Sözcük Aramasını Uygulama” dersi ücretsiz mi?

Evet — “BM25 Anahtar Sözcük Aramasını Uygulama” dersin tüm metni burada web'de ücretsiz olarak okunabilir. Etkileşimli olarak pratik yapmak (yerleşik kod editörü ve 7/24 yapay zeka koçu) ve AI Engineering Academy kursunun geri kalanını açmak için CoddyKit PRO'ya yükselt. AI Engineering Academy kursu toplamda 4 dersten oluşur.

“BM25 Anahtar Sözcük Aramasını Uygulama” dersinde ne öğreneceğim?

Python'da rank_bm25 kullanarak BM25'i kurun, belge derlemenizi dizinleyin ve tam terimleri, teknik jargonları ve ürün adlarını güvenilir biçimde işleyen anahtar sözcük aramaları çalıştırın. AI Engineering Academy ile uygulamalı kodu tarayıcıda doğrudan çalıştırarak pratik yaparsın ve 7/24 yapay zeka koçu dersi çalışırken sorularını yanıtlar.

AI Engineering Academy öğrenmeye başlamak için deneyim gerekli mi?

Önceden deneyim gerekmez. CoddyKit'te AI Engineering Academy, başlangıçtan ileri seviyeye kadar yapılandırıldığı için buradan başlayabilir veya başından başlayıp kendi hızında ilerleme yapabilirsin. Bu, 4 dersinin 2. dersidir.

“BM25 Anahtar Sözcük Aramasını Uygulama” dersi ne kadar sürer?

Çoğu CoddyKit dersi yaklaşık 5–10 dakika sürer. Her biri kısa ve etkileşimli olduğu için sabit ilerleme yaparsın ve web ile uygulama arasında tam olarak bıraktığın yerden devam edebilirsin.

Bu AI Engineering Academy dersinde kod yazıp çalıştırabilir miyim?

Evet. Her AI Engineering Academy dersi yerleşik bir kod editörü içerir, bu sayede tarayıcıda gerçek kod yazıp çalıştırabilir ve anlık yapay zeka geri bildirimi alırsın — yerel kurulum gerekli değildir.

Bu kursun tüm dersleri

  1. Yoğun ve Seyrek Getirme: Ödünleşimler
  2. BM25 Anahtar Sözcük Aramasını Uygulama
  3. Puan Birleştirme İçin Karşılıklı Sıra Birleştirme
  4. Pinecone ve pgvector'da Karma Arama
← AI Engineering Academy Sayfasına Dön