"Telefon Kabı" Arayana "iPhone Kılıfı" Gösteremeyen Arama Kutusu: Contrastive Embedding ile Anlamsal Arama
Müşteriniz "telefon kabı" yazıyor, siteniz "sonuç bulunamadı" diyor; çünkü ürünün adı "kılıf". Embedding modellerinin anlamı contrastive öğrenmeyle nasıl kavradığını ve PostgreSQL + pgvector ile anlamsal aramayı adım adım anlatıyoruz.

İçindekiler 9
Bir e-ticaret sitesinin arama kutusuna müşteri "telefon kabı" yazıyor. Site "sonuç bulunamadı" diyor. Oysa depoda yüzlerce telefon kılıfı var; sadece hiçbirinin adında "kap" kelimesi geçmiyor. Müşteri sayfayı kapatıyor ve rakibin sitesine gidiyor.
Klasik arama motorları kelimeleri eşleştirir. "Kap" ile "kılıf"ın, "yağmurluk" ile "su geçirmez mont"un aynı şeyi kastettiğini bilmez. Embedding ile anlamsal arama, kelimelere değil anlama bakarak işte tam bu sorunu çözer. Bunu mümkün kılan da contrastive öğrenmeyle eğitilmiş embedding modelleri.
Contrastive language models yazımızda bu yaklaşımın genel fikrini, CLIP yazımızda da görseller ile metinlerin nasıl eşleştirildiğini anlatmıştık. Bu yazıda metinden metne aramaya odaklanıyoruz: bir model "anlamı" nasıl öğreniyor ve bunu kendi sitenizde nasıl kullanırsınız?
Kısaca:
- Embedding modeli, her metni bir sayı dizisine (vektöre) çevirir; anlamca yakın metinlerin vektörleri birbirine yakın düşer.
- Bu yakınlık, contrastive eğitimle öğrenilir: model, bir sorgunun doğru cevabını bir yığın yanlış aday arasından seçmeye zorlanır.
- Eğitimin kalitesini en çok "zor negatifler", yani doğru cevaba çok benzeyen yanlış örnekler belirler.
- PostgreSQL'in pgvector eklentisiyle ayrı bir veritabanı kurmadan anlamsal arama yapılabilir; en iyi sonuç ise kelime araması ile anlamsal aramayı birleştiren hibrit yaklaşımdan gelir.
Embedding nedir?
Embedding, bir metnin anlamını yüzlerce sayıdan oluşan bir vektöre sıkıştırmaktır. Örneğin bu yazıda kullanacağımız modelde her metin 768 sayıya dönüşür.
Bu sayıların tek tek bir anlamı yoktur. Önemli olan, vektörlerin birbirine göre konumudur. İyi eğitilmiş bir modelde:
- "telefon kabı" ile "iPhone kılıfı" birbirine çok yakın,
- "telefon kabı" ile "telefon şarj aleti" orta uzaklıkta,
- "telefon kabı" ile "mutfak robotu" çok uzak düşer.
İki vektörün ne kadar yakın olduğu genellikle kosinüs benzerliği ile ölçülür: aynı yöne bakan vektörlerin benzerliği 1'e yakındır.
Model "anlamı" nasıl öğreniyor?
Bir modelin "kap" ile "kılıf"ın aynı şey olduğunu öğrenmesi için kimse ona eş anlamlılar sözlüğü vermez. Öğrenme, contrastive bir oyunla gerçekleşir.
Pozitif çiftler
Eğitimin hammaddesi, birbiriyle ilişkili metin çiftleridir:
- Bir soru ve onun cevabı (ör. bir forum sorusu ve kabul edilen cevap),
- Bir arama sorgusu ve kullanıcının tıkladığı sayfa,
- Bir başlık ve makalenin gövdesi,
- Aynı cümlenin iki farklı ifadesi.
Bu çiftler pozitif örneklerdir: model bunların vektörlerini birbirine yaklaştırmalıdır.
Yığın içi negatifler
Peki modele neyi uzaklaştırması gerektiğini nasıl söyleriz? İşte contrastive öğrenmenin zarif hilesi burada: aynı eğitim yığınındaki diğer cevaplar, bir sorgu için doğal negatif örneklerdir.
Bir yığında 64 soru ve 64 cevap olduğunu düşünün. 1 numaralı soru için doğru cevap 1 numaralı cevaptır; diğer 63 cevap yanlıştır. Model, her soru için 64 aday arasından doğru cevabı seçmeye çalışır. Bu, bir çoktan seçmeli sınav gibidir ve modelin puanı InfoNCE adı verilen bir kayıp fonksiyonuyla ölçülür.
InfoNCE'yi formülsüz anlamak
InfoNCE'nin yaptığı şey sezgisel olarak şudur:
- Sorgu ile her aday arasındaki benzerliği hesapla.
- Bu benzerlikleri, toplamı 1 olan olasılıklara çevir (softmax).
- Doğru cevaba verilen olasılık ne kadar düşükse, cezayı o kadar büyük tut.
Model cezayı azaltmak için iki şeyi aynı anda yapmayı öğrenir: doğru cevabı sorguya yaklaştırmak ve yanlış cevapları uzaklaştırmak.
Bir ayar daha var: sıcaklık (temperature). Benzerlikler olasılığa çevrilmeden önce bu sayıya bölünür. Düşük sıcaklık, modeli doğru cevap ile en yakın yanlış cevap arasındaki küçük farklara karşı daha hassas yapar. Bu yüzden sıcaklık, contrastive eğitimin en kritik ayarlarından biridir.
Zor negatifler: Asıl öğrenme burada
Rastgele seçilen yanlış cevaplar çoğu zaman çok kolaydır. "iPhone 15 kılıfı" sorgusu için "mutfak robotu" açıklamasını elemek modele bir şey öğretmez. Model asıl, şu tür ayrımlarda öğrenir:
- "iPhone 15 kılıfı" ile "iPhone 15 ekran koruyucu"
- "iPhone 15 kılıfı" ile "iPhone 15 Pro kılıfı"
- "kadın koşu ayakkabısı" ile "erkek koşu ayakkabısı"
Doğru cevaba çok benzeyen bu yanlış örneklere zor negatifler (hard negatives) denir. Güçlü embedding modellerinin eğitiminde zor negatifler bilinçli olarak toplanır; örneğin bir arama motorunun üst sıralara getirdiği ama doğru olmayan sonuçlar kullanılır. Kendi verinizle bir modeli ince ayar yaparken de en çok fayda sağlayan şey genellikle iyi seçilmiş zor negatiflerdir.
Bu yaklaşım ürün dünyasında da tanıdık: E5 gibi modern embedding modelleri önce internetten toplanan çok büyük miktarda zayıf etiketli metin çiftiyle contrastive olarak ön eğitilir, ardından daha küçük ama kaliteli, zor negatifler içeren verilerle ince ayar yapılır. Bu yazıda kullanacağımız multilingual-e5-base, Türkçe dahil yaklaşık 100 dili destekleyen bu ailenin bir üyesi.
Uygulama: Ürünlerinizi vektöre çevirin
Önce gerekli kütüphaneyi kuralım:
pip install sentence-transformers
from sentence_transformers import SentenceTransformer
model = SentenceTransformer("intfloat/multilingual-e5-base")
urunler = [
"Silikon iPhone 15 kılıfı, darbeye dayanıklı, şeffaf",
"Temperli cam iPhone 15 ekran koruyucu",
"65W hızlı şarj adaptörü, USB-C",
"Su geçirmez kadın yağmurluğu, kapüşonlu",
"Paslanmaz çelik çay demliği, 2 litre",
]
# E5 modelleri belgelerin başına "passage: ", sorguların başına "query: " ister
urun_vekt = model.encode(["passage: " + u for u in urunler], normalize_embeddings=True)
def ara(sorgu, k=3):
s = model.encode(["query: " + sorgu], normalize_embeddings=True)
skor = (s @ urun_vekt.T)[0]
for i in skor.argsort()[::-1][:k]:
print(f"{skor[i]:.3f} {urunler[i]}")
ara("telefon kabı")
ara("yağmurda giyilecek bir şey")
İki ayrıntıya dikkat edin:
- Önekler: E5 ailesi, eğitimde sorgu ve belgeleri ayırt etmek için "query: " ve "passage: " önekleriyle eğitildi. Bunları unutmak sonuçları belirgin biçimde kötüleştirir. Kullandığınız modelin kartında böyle bir kural olup olmadığını her zaman kontrol edin.
- Normalizasyon: Vektörler normalize edildiğinde iki vektörün çarpımı doğrudan kosinüs benzerliğidir.
"telefon kabı" sorgusu, adında "kap" geçmeyen kılıfı üst sıraya getirir; "yağmurda giyilecek bir şey" ise yağmurluğu bulur. Kelime eşleşmesi yok, anlam eşleşmesi var.
PostgreSQL ve pgvector ile ölçeklemek
Birkaç yüz ürün için yukarıdaki kod yeter. Binlerce ya da milyonlarca kayıt için vektörleri bir veritabanında saklamak gerekir. Zaten PostgreSQL kullanıyorsanız ayrı bir vektör veritabanı kurmanıza gerek yok; pgvector eklentisi bu işi görür:
CREATE EXTENSION IF NOT EXISTS vector;
CREATE TABLE urunler (
id bigserial PRIMARY KEY,
ad text NOT NULL,
aciklama text,
embedding vector(768)
);
-- Yaklaşık en yakın komşu araması için HNSW indeksi
CREATE INDEX ON urunler USING hnsw (embedding vector_cosine_ops);
Arama, sorgunun vektörünü parametre olarak verip en yakın kayıtları istemekten ibaret:
SELECT ad, 1 - (embedding <=> $1) AS benzerlik
FROM urunler
ORDER BY embedding <=> $1
LIMIT 10;
<=> operatörü kosinüs uzaklığını verir; 1'den çıkarınca benzerlik elde edilir. HNSW indeksi, milyonlarca vektörde bile aramayı milisaniyeler seviyesinde tutar. Karşılığında sonuçlar "yaklaşık"tır: nadiren en yakın kayıtlardan biri atlanabilir. Çoğu arama senaryosu için bu takas fazlasıyla kabul edilebilir. İndekslerin genel mantığını merak ediyorsanız veritabanı indeksi yazımıza göz atabilirsiniz.
Hibrit arama: İki dünyanın en iyisi
Anlamsal arama her şeyi çözmez. Embedding modelleri anlamı yakalamakta iyidir ama tam eşleşmelerde zayıf kalabilir:
- Ürün kodları ve stok numaraları ("SKU-48213"),
- Model numaraları ("WH-1000XM5"),
- Nadir marka adları ve özel isimler.
Kullanıcı tam bir model numarası yazdığında, onu içeren ürünün ilk sırada çıkması gerekir. Klasik kelime araması bunu kusursuz yapar.
En iyi sonuç genellikle hibrit aramadan gelir: aynı sorgu hem kelime aramasında (PostgreSQL'in tam metin araması gibi) hem de anlamsal aramada çalıştırılır, iki sonuç listesi birleştirilir. Birleştirmede sık kullanılan basit ve sağlam bir yöntem Reciprocal Rank Fusion (RRF): her sonuca, iki listedeki sırasına göre 1 / (60 + sıra) gibi bir puan verilir ve puanlar toplanır. İki listede de üst sıralarda olan sonuçlar öne çıkar.
Kendi verinizle ince ayar
Hazır çok dilli modeller birçok iş için yeterlidir. Ama alanınız çok özelse (ör. hukuk metinleri, tıbbi ürünler, teknik yedek parçalar) kendi verinizle ince ayar yapmak sonuçları belirgin biçimde iyileştirebilir.
İyi haber şu: yukarıda anlattığımız contrastive eğitim, sentence-transformers kütüphanesinde hazır bir kayıp fonksiyonu olarak gelir: MultipleNegativesRankingLoss. Bu fonksiyon, yığın içi negatiflerle InfoNCE tarzı eğitim yapar. Sizden beklenen tek şey (sorgu, doğru sonuç) çiftleri. Bu çiftleri çoğu zaman zaten elinizde bulunan verilerden çıkarabilirsiniz:
- Site içi arama kayıtları: kullanıcının yazdığı sorgu ve ardından satın aldığı ürün,
- Destek kayıtları: müşterinin sorusu ve çözümü içeren yardım makalesi,
- SSS sayfaları: soru ve cevap.
Bu çiftlere bir de zor negatifler eklerseniz (mevcut aramanızın üst sıralara getirdiği ama yanlış olan sonuçlar), model tam olarak sizin müşterilerinizin ayrım yapmakta zorlandığı noktaları öğrenir.
Sonuçları nasıl ölçersiniz?
Anlamsal aramayı "denedim, iyi gibi" diyerek canlıya almayın. Basit bir değerlendirme seti hazırlayın:
- Gerçek arama kayıtlarınızdan 50-100 sorgu seçin.
- Her sorgu için doğru sonuçları elle işaretleyin.
- Eski aramanızı ve yeni aramayı aynı sorgularla çalıştırın.
- Doğru sonucun ilk 10'da çıkma oranını (recall@10) karşılaştırın.
Bu küçük set, hangi modelin, hangi öneklerin ve hangi hibrit ayarın sizin verinizde gerçekten daha iyi olduğunu tahminle değil, ölçüyle gösterir.
Sıkça sorulan sorular
Embedding ile anlamsal arama, ChatGPT gibi bir yapay zekâ mı kullanıyor?
Hayır, çok daha küçük ve ucuz bir model kullanıyor. Embedding modelleri metin üretmez, sadece metni vektöre çevirir. multilingual-e5-base gibi bir model kendi sunucunuzda, çoğu durumda ekran kartı olmadan bile çalışabilir ve her arama için dışarıya ücretli bir API çağrısı yapmanız gerekmez.
Anlamsal arama ile RAG arasındaki ilişki ne?
RAG (getirme destekli üretim), bir dil modelinin cevabını sizin belgelerinize dayandırmasıdır. "Getirme" adımında genellikle tam olarak bu yazıda anlattığımız anlamsal arama kullanılır: kullanıcının sorusuna en yakın belge parçaları bulunur ve modele verilir. Bu, yapay zekâ halüsinasyonlarını azaltmanın en etkili yollarından biri.
Ürün açıklamaları değiştiğinde ne yapmalıyım?
Değişen kaydın vektörünü yeniden hesaplayıp güncellemeniz yeterli. Modeli değiştirirseniz ise tüm vektörleri yeniden üretmeniz gerekir; farklı modellerin vektörleri birbiriyle karşılaştırılamaz.
Türkçe için hangi modeli seçmeliyim?
Çok dilli modeller (ör. multilingual-e5 ailesi) Türkçe için iyi bir başlangıç noktası. Seçimi ise yukarıdaki gibi küçük bir değerlendirme setiyle, kendi verinizde birkaç modeli karşılaştırarak yapmak en doğrusu.
Anlamsal arama, contrastive öğrenmenin en doğrudan ticari karşılığı: müşterinizin kelimeleriyle sizin ürün adlarınız arasındaki boşluğu kapatır. Sitenize ya da kurumsal belgelerinize anlamsal arama veya RAG tabanlı bir asistan eklemek isterseniz kurumsal yazılım geliştirme sayfamızdan bize ulaşabilirsiniz.
Kaynaklar
arXiv.orgText Embeddings by Weakly-Supervised Contrastive Pre-trainingThis paper presents E5, a family of state-of-the-art text embeddings that transfer well to a wide range of tasks. The model is trained in a contrastive manner with weak supervision signals from our curated large-scale text pair dataset (called CCPairs). E5 can be readily used as a general-purpose e…
arXiv.orgMultilingual E5 Text Embeddings: A Technical ReportThis technical report presents the training methodology and evaluation results of the open-source multilingual E5 text embedding models, released in mid-2023. Three embedding models of different sizes (small / base / large) are provided, offering a balance between the inference efficiency and embed…
arXiv.orgRepresentation Learning with Contrastive Predictive CodingWhile supervised learning has enabled great progress in many applications, unsupervised learning has not seen such widespread adoption, and remains an important and challenging endeavor for artificial intelligence. In this work, we propose a universal unsupervised learning approach to extract usefu…

