---
title: 'Anlamsal ve Vektör Arama'
source: 'https://academia.sh/tr/kurslar/arama-motorlari/anlamsal-ve-vektor-arama'
course: 'Arama Motorları ve Metin Erişimi'
language: tr
updated: '2026-08-17T18:09:01+00:00'
license: 'CC BY-SA 4.0'
---

# Anlamsal ve Vektör Arama

Sözcüğün kendisi yerine geçtiği bağlamı karşılaştıran erişim: terim sayımından üretilen gömmelerin kurulması, eşanlamlı iki terimin hiç birlikte geçmeden yakın çıkması, vektör erişiminin terim erişimine göre kazandırdığı ve kaybettirdiği belgeler, karma aramanın iki listeyi birleştirmesi ve vektör dizininin bayt ile tarama maliyeti.

Buraya kadarki bütün düzenek tek bir varsayıma dayanıyor: sorgudaki sözcük ile belgedeki sözcük
aynı dizidir. Katalogda bu varsayım sık sık tutmaz. "hikâye" arayan okur, adında "öykü" geçen
kitapları göremez. Bu bir puanlama sorunu değildir: ters dizin için o iki dizgi arasında hiçbir bağ
yoktur, dolayısıyla alan ağırlığı da işlev tabanlı puan da o belgeleri getiremez — ikisi de yalnız
**eşleşmiş** bir terimin puanını değiştirir.

Bu ders başka bir erişim biçimi kurar. Her terim ve her belge, sabit uzunlukta bir **sayı dizisiyle**
temsil edilir; buna **gömme** denir. İki belgenin ya da bir sorgu ile bir belgenin yakınlığı, bu iki
dizi arasında hesaplanan bir **ölçüdür**. Sözcük eşleşmesi aranmaz; sayılar karşılaştırılır. Terim
"gömme" bu kursta iki anlamda geçer: belge modelinde bir belgenin başka bir belgenin içine
gömülmesini anlatıyordu, burada bir metnin sayı dizisine dönüştürülmüş halidir.

## Derlem ve Gömmenin Kurulumu

| Kod | Varsayım | Değer | Gerekçe |
|---|---|---|---|
| SI22 | gömmeye giren alan | ad ve özet | konu ve yazar alanları vektöre girmez |
| SI23 | bağlam penceresi | belgenin tamamı | aynı kayıtta geçen iki terim birlikte geçmiş sayılır |
| SI24 | gömme boyutu | 32; izdüşüm aynı tohumdan | vektörler her koşumda aynı çıkar |
| SI25 | eşanlamlı çift | "öykü" ve "hikâye" aynı bağlamda, hiçbir belgede birlikte değil | ölçülen kazanç bu yapıdan gelir, dilden çıkarılmaz |

SI25 açıkça yazılmalıdır: derlem, iki eşanlamlının aynı komşu sözcüklerle geçeceği biçimde
üretilmiştir. Ders bu yapının **ölçülebilir sonucunu** gösterir; gömmenin dildeki her eşanlamlıyı
bulacağını göstermez.

```js
// derlem.mjs — kutuphane katalogu: tohumlu derlem, ters dizin ve puanlayici.
export const N = 1200, TOHUM = 20260801, ALANLAR = ["ad", "ozet", "konu", "yazar"];
let d = TOHUM;                                            // 32 bit uretec, tasma yok
export const rast = () => { d = (d + 0x6D2B79F5) | 0; let t = Math.imul(d ^ (d >>> 15), 1 | d);
  t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t; return ((t ^ (t >>> 14)) >>> 0) / 2 ** 32; };
const sec = (a) => a[Math.floor(rast() * a.length)];
const TEMA = [["öykü|hikâye", "edebiyat", "kısa seçki derleme anlatı kurgu taşra"],
  ["masal", "çocuk edebiyatı", "çocuk resimli okul genç orman uyku"],
  ["tarih", "tarih", "osmanlı cumhuriyet arşiv belge kronik vakıf"],
  ["deniz", "gezi", "kıyı balıkçı liman gemi ada fener"],
  ["matematik", "bilim", "geometri sayı kanıt kuram çözüm olasılık"],
  ["şiir", "şiir", "dize toplu divan çeviri seçme aruz"]]
  .map(([k, konu, a]) => ({ k: k.split("|"), konu, ana: a.split(" ") }));
const ORTAK = "kitap cilt baskı yayın inceleme notlar giriş sözlük".split(" ");
const AD = "Ahmet Elif Selim Nuray Kemal Deniz Ayşe Ozan Meral Barış".split(" ");
const SOY = "Yıldız Aksu Demir Karaca Toprak Şen".split(" ");

export const belgeler = Array.from({ length: N }, (_, id) => {
  const t = sec(TEMA), cek = t.k[t.k.length > 1 && rast() < 0.5 ? 1 : 0], oz = [cek, cek];
  const ad = [...new Set([cek, sec(t.ana), sec(t.ana)])];
  t.ana.forEach((s, j) => { if (rast() < 1 / (1 + j * 0.42)) oz.push(s); });
  ORTAK.forEach((s, j) => { if (rast() < 0.55 / (1 + j * 0.28)) oz.push(s); });
  if (rast() < 0.5) oz.push(sec(sec(TEMA).ana));          // baska temadan sizan sozcuk
  for (let k = oz.length - 1; k > 0; k -= 1) { const j = Math.floor(rast() * (k + 1));
    [oz[k], oz[j]] = [oz[j], oz[k]]; }
  // konu etiketi yayinevi kararidir: belgelerin dortte birinde metinle ayni temada degil
  return { id, ad: ad.join(" "), ozet: oz.join(" "), konu: (rast() < 0.25 ? sec(TEMA) : t).konu,
    yazar: `${sec(AD)} ${sec(SOY)}`, yil: 1975 + Math.floor(rast() ** 0.6 * 50),
    odunc: Math.floor(rast() ** 3 * 400) };
});

export const belirtec = (s) => s.toLocaleLowerCase("tr").match(/[\p{L}\p{N}]+/gu) ?? [];
export function dizinKur(bs) {                            // alan basina ters dizin
  const dz = {};
  for (const a of ALANLAR) {
    const gonderi = new Map(), boy = new Float64Array(bs.length);
    for (const b of bs) {
      const ts = belirtec(b[a]), say = new Map();
      boy[b.id] = ts.length;
      for (const t of ts) say.set(t, (say.get(t) ?? 0) + 1);
      for (const [t, tf] of say) {
        if (!gonderi.has(t)) gonderi.set(t, []);
        gonderi.get(t).push({ id: b.id, tf });
      }
    }
    dz[a] = { gonderi, boy, ort: boy.reduce((x, y) => x + y, 0) / bs.length };
  }
  return dz;
}
export function ara(dz, sorgu) {          // esit alan agirligiyla puanlanmis sonuc listesi
  const terimler = belirtec(sorgu), k1 = 1.2, b = 0.75, p = new Map();
  for (const a of ALANLAR) for (const t of terimler) {
    const g = dz[a].gonderi.get(t);
    if (!g) continue;
    const idf = Math.log(1 + (N - g.length + 0.5) / (g.length + 0.5));
    for (const e of g) {
      const nrm = 1 - b + (b * dz[a].boy[e.id]) / dz[a].ort;
      p.set(e.id, (p.get(e.id) ?? 0) + (idf * e.tf * (k1 + 1)) / (e.tf + k1 * nrm));
    }
  }
  return [...p].sort((x, y) => y[1] - x[1] || x[0] - y[0]);
}
```

## Bağlamdan Vektör Üretmek

Gömme şöyle kurulur. Her terim için, o terimle aynı kayıtta geçen bütün terimlerin sayımı tutulur;
bu sayım o terimin bağlamıdır. Bağlam vektörü 51 sözcüklük sözlük boyundadır ve tohumlu bir izdüşümle
32 boyuta indirilir. Belgenin gömmesi, içindeki terimlerin gömmelerinin ağırlıklı ortalamasıdır.
Sorgu da aynı işlemden geçer ve benzerlik iki birim vektörün iç çarpımıdır.

```js
// gomme.mjs — terim sayimindan uretilen gomme, vektor erisimi ve karma arama.
import { belgeler, dizinKur, ara, belirtec, rast, N, TOHUM, ALANLAR } from "./derlem.mjs";
const D = 32, dz = dizinKur(belgeler), tr = (x) => Math.round(x).toLocaleString("tr-TR");
const metin = (b) => [...new Set([...belirtec(b.ad), ...belirtec(b.ozet)])];

const bs = new Map();                                     // sozluk: terim -> belge sikligi
for (const b of belgeler) for (const t of metin(b)) bs.set(t, (bs.get(t) ?? 0) + 1);
const V = [...bs.keys()], yer = new Map(V.map((t, i) => [t, i]));
const C = V.map(() => new Float64Array(V.length));        // birlikte gecis sayimi
for (const b of belgeler) { const ts = metin(b);
  for (const a of ts) for (const c of ts) if (a !== c) C[yer.get(a)][yer.get(c)] += 1; }

const idf = V.map((t) => Math.log(N / bs.get(t)));
const R = V.map(() => Float64Array.from({ length: D }, () => (rast() < 0.5 ? -1 : 1)));
const birle = (v) => { let s = 0; for (const x of v) s += x * x; s = Math.sqrt(s) || 1;
  for (let i = 0; i < v.length; i += 1) v[i] /= s; return v; };
const terimVek = C.map((satir) => {                       // baglam sayimi -> D boyutlu gomme
  const p = new Float64Array(D);
  for (let i = 0; i < satir.length; i += 1) if (satir[i]) { const w = satir[i] * idf[i];
    for (let j = 0; j < D; j += 1) p[j] += w * R[i][j]; }
  return birle(p);
});
const gomme = new Float32Array(N * D);                    // belge gommesi: terimlerin ortalamasi
for (const b of belgeler) { const p = new Float64Array(D);
  for (const t of metin(b)) { const v = terimVek[yer.get(t)], w = idf[yer.get(t)];
    for (let j = 0; j < D; j += 1) p[j] += w * v[j]; }
  gomme.set(birle(p), b.id * D); }
const sorguVek = (q) => { const p = new Float64Array(D);
  for (const t of belirtec(q)) if (yer.has(t)) { const v = terimVek[yer.get(t)];
    for (let j = 0; j < D; j += 1) p[j] += idf[yer.get(t)] * v[j]; }
  return birle(p); };
const vektorAra = (q) => { const s = sorguVek(q), r = [];
  for (let i = 0; i < N; i += 1) { let d = 0;
    for (let j = 0; j < D; j += 1) d += s[j] * gomme[i * D + j];
    r.push([i, d]); }
  return r.sort((a, b) => b[1] - a[1] || a[0] - b[0]); };
const karma = (t, v) => {                                 // sira temelli birlestirme, k = 60
  const p = new Map(), ek = (l) => l.slice(0, 100).forEach(([id], i) =>
    p.set(id, (p.get(id) ?? 0) + 1 / (60 + i + 1)));
  ek(t); ek(v);
  return [...p].sort((a, b) => b[1] - a[1] || a[0] - b[0]);
};

let giris = 0;
for (const a of ALANLAR) for (const g of dz[a].gonderi.values()) giris += g.length;
console.log(`derlem ${N} belge, tohum ${TOHUM}; sözlük ${V.length} terim, gömme boyutu ${D}`);
console.log(`vektör dizini ${tr(gomme.byteLength)} bayt; ters dizin ${tr(giris)} gönderi ` +
  `girişi x 8 bayt = ${tr(giris * 8)} bayt`);
console.log(`sorgu başına vektör taraması ${tr(N * D)} çarpma-toplama; küme sütunu terim yolunda ` +
  `eşleşen belgeyi, vektör yolunda benzerliği 0,5 ve üstü olan belgeyi sayar\n`);
const kos = (a, b) => { let s = 0; for (let i = 0; i < a.length; i += 1) s += a[i] * b[i]; return s; };
for (const t of ["hikâye", "masal"]) {
  const k = V.map((u, i) => [u, kos(terimVek[yer.get(t)], terimVek[i])])
    .sort((a, b) => b[1] - a[1]).slice(1, 5);
  console.log(`"${t}" gömmesine en yakın terimler: ` +
    k.map(([u, d]) => `${u} ${d.toFixed(3)}`).join(", "));
}
console.log(`belge 0 gömmesinin ilk dört bileşeni: ` +
  [...gomme.slice(0, 4)].map((x) => x.toFixed(3)).join(", ") + "\n");
console.log("sorgu".padEnd(11) + "yol".padEnd(8) + "küme".padStart(7) +
  "ilk10 terimli".padStart(15) + "ilk10 eşanlamlı".padStart(17) + "terim ilk10 ile ortak".padStart(23) +
  "  ilk sıradaki");
for (const [q, es] of [["hikâye", "öykü"], ["kısa öykü", "hikâye"]]) {
  const t = ara(dz, q), v = vektorAra(q), k = karma(t, v);
  const qt = new Set(belirtec(q)), tOn = new Set(t.slice(0, 10).map(([id]) => id));
  const esik = v.filter(([, d]) => d >= 0.5).length;
  for (const [ad, l, n] of [["terim", t, t.length], ["vektör", v, esik], ["karma", k, k.length]]) {
    const on = l.slice(0, 10).map(([id]) => belgeler[id]);
    const terimli = on.filter((b) => metin(b).some((x) => qt.has(x))).length;
    const esanlamli = on.filter((b) => metin(b).includes(es)).length;
    console.log(q.padEnd(11) + ad.padEnd(8) + tr(n).padStart(7) + `${terimli}`.padStart(15) +
      `${esanlamli}`.padStart(17) +
      `${on.filter((b) => tOn.has(b.id)).length}`.padStart(23) + `  ${on[0].ad}`);
  }
}
```

```
derlem 1200 belge, tohum 20260801; sözlük 51 terim, gömme boyutu 32
vektör dizini 153.600 bayt; ters dizin 16.050 gönderi girişi x 8 bayt = 128.400 bayt
sorgu başına vektör taraması 38.400 çarpma-toplama; küme sütunu terim yolunda eşleşen belgeyi, vektör yolunda benzerliği 0,5 ve üstü olan belgeyi sayar

"hikâye" gömmesine en yakın terimler: öykü 0.997, anlatı 0.891, seçki 0.854, kurgu 0.847
"masal" gömmesine en yakın terimler: çocuk 0.864, uyku 0.839, okul 0.812, orman 0.800
belge 0 gömmesinin ilk dört bileşeni: -0.175, -0.092, -0.125, 0.231

sorgu      yol        küme  ilk10 terimli  ilk10 eşanlamlı  terim ilk10 ile ortak  ilk sıradaki
hikâye     terim       108             10                0                     10  hikâye taşra
hikâye     vektör      383              4                6                      2  hikâye anlatı kısa
hikâye     karma       153             10                0                      5  hikâye anlatı kısa
kısa öykü  terim       245             10                0                     10  öykü kısa
kısa öykü  vektör      452             10                4                      1  hikâye anlatı kısa
kısa öykü  karma       156             10                0                      5  öykü taşra kısa
```

İlk satırlar gömmenin ne olduğunu somutlaştırıyor: belge 0'ın gömmesi otuz iki ondalık sayıdan
oluşur ve ilk dördü ekranda duruyor. "hikâye" ile "öykü" arasındaki yakınlık 0,997 — bu iki terim
derlemde **hiçbir belgede birlikte geçmiyor**, yakınlıkları yalnız aynı komşularla (anlatı, seçki,
kurgu) geçmelerinden geliyor. Ters dizinde bu iki dizgi arasında ölçülebilir hiçbir ilişki yoktur.

## Kazanılan, Kaybedilen ve İkisini Birleştiren

"hikâye" sorgusunun üç satırı bu dersin özetidir. Terim yolu 108 belge eşleştiriyor ve ilk onun onu
da gerçekten "hikâye" taşıyor: kesinlik tam, ama "öykü" taşıyan tek bir belge bile yok — bunlar
kümenin dışında ve hiçbir ağırlıkla içeri giremez.

Vektör yolu ilk onun **altısını** "öykü" taşıyan belgelerden getiriyor. Kazanç budur ve terim
yolunun yapısal olarak veremeyeceği bir kazançtır. Aynı satırda kayıp da duruyor: ilk onun yalnız
dördü "hikâye" taşıyor, terim yolunun ilk onuyla ortak belge sayısı ikiye düşmüş. Kütüphaneci tam
o sözcüğü arıyorsa altı satır işine yaramaz.

Küme sütunu ikinci bir farkı gösteriyor. Terim yolunda "eşleşen belge" tanımlıdır: 108. Vektör
yolunda böyle bir şey yoktur; her belgenin bir benzerliği vardır ve küme ancak bir **eşikle**
seçilir. 0,5 eşiğinde 383 belge kalıyor; eşik 0,6 seçilseydi başka bir sayı çıkardı. Vektör aramada
kümenin sınırı veriden değil, seçilen eşikten gelir.

"kısa öykü" satırları farkın ne kadar derin olabileceğini gösteriyor: iki yolun ilk onunda **tek**
ortak belge var. İki liste de savunulabilir, biri sözcüğü tutuyor, öteki bağlamı; ve neredeyse
ayrıklar.

Karma satırları iki listeyi sıra temelli birleştiriyor: her belgenin puanı, iki listedeki
sıralarının tersinden gelir. Sonuç ilk onda tam eşlemeyi geri alıyor — onda onu "hikâye" taşıyor —
ve terim listesiyle ortaklık ikiden beşe çıkıyor. Ama eşanlamlı sütunu sıfıra düşüyor: birleştirme,
vektör yolunun kazandırdığı belgeleri ilk onun dışına itiyor. Karma arama iki listenin kesişimini
öne çıkarır; kazanç ilk onda değil, listenin daha derininde durur. Küme de daralıyor: 153 belge,
çünkü birleştirmeye her iki listeden yalnız ilk yüz belge giriyor.

Bedel tarafında iki sayı var. Vektör dizini 153.600 bayt tutuyor, ters dizin 128.400; vektör dizini
belge sayısıyla **doğrusal** büyür (belge başına 128 bayt, sabit), ters dizin ise belgelerin
uzunluğuna ve terim çeşitliliğine bağlıdır. Asıl fark sorgu anında: terim yolu yalnız sorgu
terimlerinin gönderi listelerini okur, "hikâye" için 108 giriş; vektör yolu **her belgeye** dokunur
ve 38.400 çarpma-toplama yapar. Vektör aramada eşleşmeyen belge diye bir şey olmadığı için
elenecek belge de yoktur; taramayı kısaltmak ayrı bir yapı gerektirir ve bu ders o yapıyı kurmaz.

## Özet

- Gömme sabit uzunlukta bir sayı dizisidir, benzerlik iki dizi arasındaki bir ölçüdür: "hikâye" ile
  "öykü" hiçbir belgede birlikte geçmeden 0,997 yakınlıkta çıkar, çünkü aynı komşularla geçerler.
- "hikâye" sorgusunda terim yolu 108 belge ve ilk onda on tam eşleme verir; vektör yolu ilk onun
  altısını "öykü" taşıyan belgelerden doldurur ve yalnız dördünde sorgu terimi bulunur.
- Vektör aramada eşleşen küme yoktur; küme bir eşikle seçilir: 0,5 eşiğinde 383 belge.
- Karma arama tam eşlemeyi ilk onda geri alır (onda on) ve terim listesiyle ortaklığı ikiden beşe
  çıkarır, ama eşanlamlı belgeleri ilk ondan düşürür.
- Vektör dizini belge başına sabit 128 bayttır (toplam 153.600) ve sorgu başına bütün derlemi
  gezerek 38.400 çarpma-toplama ister; terim yolu "hikâye" için yalnız 108 gönderi girişi okur.

## Sonraki Adım

Bu konu sorgunun bütün yüzeyini kurdu: sorgu dili, eşleşme, süzme, puanlama, ağırlık, toplama,
sunum ve son olarak sözcük yerine bağlamı karşılaştıran erişim. Ölçülen her sayının altında
söylenmemiş bir koşul var: bütün bu sorgular **tek bir süreçte ve tek bir dizin üzerinde** koştu.
Ters dizin bir bilgisayarın belleğine sığdı, puanlayıcı bütün gönderi listelerini yerel olarak
okudu, toplama bütün kovaları tek yerde topladı, sayfalama parçaları aynı süreçte birleştirdi ve
vektör taraması bütün belgelere sırayla dokundu. Katalog bir milyon kayda çıktığında ve dizin
birden çok makineye yayıldığında bu sorguların hiçbiri aynı biçimde koşamaz: ters belge sıklığı
artık kimin bildiği bir sayıdır, kova toplaması nerede birleşir, aynı sorgu iki makinede aynı
sırayı verir mi. Sonraki konu dizinin makinelere dağıtılmasını ve aynı sorgunun orada ne olduğunu
ele alır.
