Mundarija (32)
- Bu darsda
- 1. Nega bu kerak?
- 2. Vektor va masofa
- 2.1 Vektor
- 2.2 Uch xil masofa
- 2.3 Normallashtirish
- 3. pgvector
- 3.1 Tur va so'rov
- 3.2 Kanon ma'lumoti
- 4. Aniq va taxminiy qidiruv
- 4.1 Aniq qidiruv
- 4.2 Taxminiy qidiruv va recall
- 4.3 HNSW
- 4.4 IVFFlat
- 5. Kanon o'lchovi
- 5.1 Indekslar
- 5.2 Natijani halol o'qish
- 6. Filtrlangan qidiruv tuzog'i
- 6.1 0 ta natija
- 6.2 Nega aynan nol
- 6.3 iterative_scan
- 7. halfvec: ikki baravar kichik indeks
- 8. Vektor bazalar
- 9. Ko'p uchraydigan xatolar
- 10. Mashqlar
- 1-mashq (oson): bo'shliqlarni to'ldiring
- 2-mashq (o'rta): eng yaqin k ta
- 3-mashq (qiyin): mini IVFFlat va recall
- 11. Loyiha qadami: kanon tajribasi
- 12. Real ishda
- Xulosa
- Manbalar
Vektor bazalar va pgvector: ma'no bo'yicha o'xshashni topish
Qisqacha: Vektor qidiruv — har yozuvni sonlar massivi (vektor) sifatida saqlab, so'rov vektoriga eng yaqinlarini topish. Matnning ma'nosini aks ettiruvchi vektorlar bilan bu "ma'no bo'yicha qidiruv"ga aylanadi. PostgreSQL'da buni pgvector kengaytmasi beradi:
vector(768)turi,<=>masofa operatori va taxminiy indekslar. Kanon o'lchovida 30 000 vektorda aniq qidiruv 122 ms, HNSW indeksi bilan 1–2 ms oldi va natijalarning 98.5–100 % ini to'g'ri topdi. Lekin filtrli so'rovda HNSW 10 o'rniga 0 ta natija qaytardi — buning sababi va davosi shu darsda.
Bu darsda
- Vektor, o'lcham va masofa turlarini (kosinus, L2, skalyar ko'paytma) tushuntirasiz.
- Uchta masofani brauzerda hisoblab,
<=>,<->,<#>operatorlariga bog'laysiz. - Aniq qidiruv va taxminiy (ANN) indekslar — HNSW va IVFFlat — farqini bilasiz.
- Recall nima ekanini va kanon o'lchovini o'qiysiz.
- Filtrlangan qidiruvdagi "0 natija" tuzog'ini va
iterative_scanni bilasiz. - pgvector va alohida vektor bazalarni (Qdrant, Pinecone, Weaviate) solishtirasiz.
Oldin bilishingiz kerak: Extension'lar, Indeks turlari: GIN, GiST, BRIN va pg_trgm, Postgres'da full-text search, Qidiruv tizimlari qanday ishlaydi.
Oldingi darsda ma'lumotni himoyaladik. Endi butunlay yangi turdagi ma'lumotga o'tamiz — sonlar massivlari. Bu dars qismning AI bo'limiga ko'prik: keyingi darslarda shu vektorlarni sun'iy intellekt modeli hisoblaydi.
1. Nega bu kerak?
Aziz vazifalarida qidiradi: "mashina". Ro'yxatda "Avtomobilni texnik ko'rikdan o'tkazish" bor, lekin u topilmaydi. Postgres'da full-text search so'zlar bo'yicha qidiradi: "mashina" va "avtomobil" — boshqa-boshqa so'zlar. Sinonimlar lug'ati bilan buni qisman tuzatish mumkin, lekin "ota-onamga yordam" so'roviga "Onamga dori olib borish" vazifasini hech qanday lug'at ulamaydi.
Kerakli narsa — ma'no bo'yicha qidirish. G'oya shunday: maxsus model har matnni yuzlab sonlar massiviga aylantiradi. Ma'nosi yaqin matnlarning sonlari ham yaqin chiqadi. Bunday massiv embedding (vektor ko'rinishi) deyiladi. Uni qanday olish — Embedding va semantik qidiruv darsida, AI modeli bilan. Bu darsda esa savol boshqa: millionlab shunday massiv orasidan eng yaqinlarini tez qanday topamiz?
O'xshatish: shahar xaritasi. Har do'kon — xaritadagi nuqta (ikki son: kenglik va uzunlik). "Menga eng yaqin 5 ta do'kon" — vektor qidiruv, faqat ikki o'lchamda. Embedding'da o'lcham 2 emas, 768. Lekin "eng yaqin nuqtalar" g'oyasi aynan o'sha.
2. Vektor va masofa
2.1 Vektor
Vektor — tartiblangan sonlar ro'yxati: [0.12, -0.40, 0.88]. Sonlar soni — o'lcham (dimension). Kanonda o'lcham 768: har vazifa matni 768 ta son bilan ifodalanadi. Bitta float4 4 bayt, demak bitta vektor ~3 KB.
2.2 Uch xil masofa
Ikki vektor qanchalik "yaqin"? Uch asosiy o'lchov bor:
| O'lchov | Ma'nosi | pgvector operatori |
|---|---|---|
| L2 (Evklid) masofa | nuqtalar orasidagi to'g'ri chiziq uzunligi | <-> |
| Kosinus masofa | yo'nalishlar orasidagi burchak (1 − kosinus o'xshashlik) | <=> |
| Skalyar ko'paytma | ko'paytmalar yig'indisi; pgvector manfiy qiymatini qaytaradi | <#> |
Manfiy skalyar ko'paytma g'alati tuyuladi. Sabab: Postgres indeks bilan faqat o'sish tartibida (ORDER BY ... ASC) "eng kichik" qiymatlarni tez topadi. Eng katta ko'paytma kerak bo'lgani uchun pgvector uni teskari ishora bilan beradi.
Uchalasini brauzerda hisoblaymiz. Uchta "matn" — o'ylab topilgan uch o'lchamli vektorlar:
const vektorlar = {
"avtomobil ta'miri": [0.9, 0.1, 0.2],
"mashinani yuvish": [0.8, 0.2, 0.1],
"dars tayyorlash": [0.1, 0.9, 0.3],
};
const sorov = [0.85, 0.15, 0.1]; // "mashina"
const skalyar = (a, b) => a.reduce((s, x, i) => s + x * b[i], 0);
const uzunlik = (a) => Math.sqrt(skalyar(a, a));
const l2 = (a, b) => Math.sqrt(
a.reduce((s, x, i) => s + (x - b[i]) ** 2, 0));
const kosinusMasofa = (a, b) =>
1 - skalyar(a, b) / (uzunlik(a) * uzunlik(b));
for (const [nom, v] of Object.entries(vektorlar)) {
console.log(nom.padEnd(18),
"L2:", l2(sorov, v).toFixed(3),
"kosinus:", kosinusMasofa(sorov, v).toFixed(3),
"-skalyar:", (-skalyar(sorov, v)).toFixed(3));
}Konsolda:
avtomobil ta'miri L2: 0.122 kosinus: 0.007 -skalyar: -0.800
mashinani yuvish L2: 0.071 kosinus: 0.002 -skalyar: -0.720
dars tayyorlash L2: 1.079 kosinus: 0.698 -skalyar: -0.250Uchala o'lchovda ham ikki "mashina" mavzusidagi vektor yaqin, "dars tayyorlash" esa uzoq. -skalyar ustunida kichik son — yaqinroq: <#> ning teskari ishorasi shu. Lekin e'tibor bering: L2 va kosinus bo'yicha eng yaqini "mashinani yuvish", skalyar ko'paytma bo'yicha esa "avtomobil ta'miri". Sabab — vektorlar uzunligi har xil: skalyar ko'paytma uzunroq vektorni "yaqinroq" deb biladi. Keyingi bo'lim shu muammoni yechadi.
2.3 Normallashtirish
Vektorni uzunligiga bo'lsak, uning uzunligi 1 ga teng bo'ladi — bu normallashtirish. Normallangan vektorlarda uchala o'lchov bir xil tartib beradi: kosinus o'xshashlik skalyar ko'paytmaga teng bo'lib qoladi, L2 masofa esa unga bog'liq. Kanon hamma vektorni normallashtirdi (l2_normalize) va kosinus masofa (<=>, vector_cosine_ops) ishlatdi.
const skalyar = (a, b) => a.reduce((s, x, i) => s + x * b[i], 0);
const normalla = (a) => {
const u = Math.sqrt(skalyar(a, a));
return a.map((x) => x / u);
};
const a = normalla([0.9, 0.1, 0.2]);
const q = normalla([0.85, 0.15, 0.1]);
console.log("uzunlik:", Math.sqrt(skalyar(a, a)).toFixed(6));
console.log("kosinus o'xshashlik = skalyar:",
skalyar(a, q).toFixed(6));Konsolda:
uzunlik: 1.000000
kosinus o'xshashlik = skalyar: 0.992813Uzunlik 1, va normallangan vektorlarning skalyar ko'paytmasi aynan kosinus o'xshashlikning o'zi. Shuning uchun ko'p tizimlar vektorni yozishdan oldin normallashtiradi: keyin eng arzon hisob — skalyar ko'paytma — yetarli.
Tekshirib ko'ring: Ikki vektor:
[1, 0]va[10, 0]. Ularning kosinus masofasi va L2 masofasi qancha?
Javob
Kosinus masofa — 0: ikkalasi bir xil yo'nalishda. L2 masofa — 9: nuqtalar bir-biridan uzoq. Kosinus faqat yo'nalishga qaraydi, uzunlikka emas. Matn embedding'lari uchun odatda yo'nalish muhim — shuning uchun kosinus eng ko'p ishlatiladi. Normallangan vektorlarda bu farq yo'qoladi.
3. pgvector
3.1 Tur va so'rov
pgvector — PostgreSQL kengaytmasi. U vector(n) turini, masofa operatorlarini va ikki xil indeksni qo'shadi. Kanon pgvector/pgvector:pg18 image'ini ishlatdi — PostgreSQL 18.6 va pgvector 0.8.7. Bizning PostgreSQL 17 o'rnatmamizda kengaytma yo'q, shuning uchun bu darsdagi SQL — kanon skriptidan, chiqishlar ham kanonniki.
CREATE EXTENSION vector;
CREATE TABLE v (
id int,
egasi_id int,
e vector(768)
);
-- so'rov vektoriga eng yaqin 10 ta
SELECT id FROM v ORDER BY e <=> $1 LIMIT 10;ORDER BY e <=> $1 LIMIT 10 — vektor qidiruvning asosiy shakli: masofa bo'yicha tartiblab, birinchi 10 tasini olish. Bu k eng yaqin qo'shni (k-nearest neighbors, kNN) so'rovi.
3.2 Kanon ma'lumoti
Kanon 30 000 sintetik (o'ylab topilgan, modeldan olinmagan) vektor yaratdi (768 o'lcham, normallangan) va 300 ta "egasi"ga taqsimladi. Vektorlar tasodifiy emas, klasterli: 50 ta "markaz" va har birining atrofida shovqin. Bu yerda "klaster" — Postgres klasteri emas, balki bir-biriga yaqin nuqtalar to'dasi. Xaritadagi do'konlar misolida: bozor atrofida do'konlar zich, dala o'rtasida esa yo'q. Kanon skript izohida sababini yozgan: birinchi urinishda bir tekis tasodifiy vektorlar ishlatilgan va indeks natijalarning atigi 10–21 % ini topgan. Haqiqiy embedding'lar esa klasterli — o'xshash mavzular to'planib turadi.
Nega tasodifiy vektorlar yomon? Yuqori o'lchamda tasodifiy nuqtalar bir-biridan deyarli bir xil uzoqlikda bo'ladi. O'z modelimizda tekshiramiz: 1000 ta tasodifiy vektor va bitta so'rov, 2 va 768 o'lchamda. tasodif(31) — urug'li tasodif. Bir xil boshlang'ich son (urug') har safar bir xil "tasodifiy" ketma-ketlik beradi, shuning uchun natija siz bilan bizda bir xil chiqadi. Chiqishda "1-o'rin" ustuniga qarang — eng yaqin vektor qanchalik yaqin:
function tasodif(urug) {
return () => {
urug = (urug + 0x6d2b79f5) | 0;
let t = Math.imul(urug ^ (urug >>> 15), 1 | urug);
t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t;
return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
};
}
function kosinus(a, b) {
let s = 0, na = 0, nb = 0;
for (let i = 0; i < a.length; i++) {
s += a[i] * b[i]; na += a[i] ** 2; nb += b[i] ** 2;
}
return s / Math.sqrt(na * nb);
}
for (const d of [2, 768]) {
const r = tasodif(31);
const vektor = () => Array.from({ length: d }, () => r() - 0.5);
const q = vektor();
const oxshash = Array.from({ length: 1000 },
() => kosinus(q, vektor())).sort((x, y) => y - x);
console.log(`d=${d}: 1-o'rin ${oxshash[0].toFixed(3)}, ` +
`10-o'rin ${oxshash[9].toFixed(3)}, ` +
`o'rtadagi ${oxshash[500].toFixed(3)}`);
}Konsolda:
d=2: 1-o'rin 1.000, 10-o'rin 0.999, o'rtadagi 0.027
d=768: 1-o'rin 0.142, 10-o'rin 0.093, o'rtadagi -0.001Ikki o'lchamda eng yaqin qo'shni deyarli bir xil yo'nalishda (0.999 — 1.000). 768 o'lchamda esa "eng yaqin" ham atigi 0.142 — deyarli perpendikulyar (90 gradusga yaqin burchak), o'rtacha vektordan unchalik farq qilmaydi. Bunday ma'lumotda "eng yaqin 10 ta" — tasodif, indeks uchun ham, odam uchun ham ma'nosiz. Shuning uchun o'lchov klasterli ma'lumotda qayta qilindi.
4. Aniq va taxminiy qidiruv
4.1 Aniq qidiruv
Indekssiz pgvector har vektor bilan masofani hisoblaydi: 30 000 × 768 ta ko'paytirish. Bu — aniq qidiruv: natija 100 % to'g'ri. Kanon chiqishidan birinchi so'rov:
=== aniq (Seq Scan) — 1 sorov
id
-------
8107
5207
28957
16557
25007
25757
6407
17007
10457
26807
(10 rows)
Time: 232.530 ms232 ms — bitta so'rovga. 20 ta so'rov o'rtachasi keyinroq 122 ms chiqdi: birinchisi diskdan o'qidi. Million vektorda bu soniyalarga aylanadi.
4.2 Taxminiy qidiruv va recall
ANN (approximate nearest neighbors, taxminiy eng yaqin qo'shnilar) — hamma vektorni ko'rmasdan, ehtimoliy eng yaqinlarini topadigan usul. U tez, lekin ba'zan haqiqiy eng yaqinni o'tkazib yuboradi. Qanchalik yaxshi topganini recall o'lchaydi:
recall@10 = (taxminiy 10 talikdagi haqiqiy 10 talikka kirganlar) / 10Recall 100 % — taxminiy natija aniq bilan bir xil. 90 % — har 10 tadan bittasi noto'g'ri.
4.3 HNSW
HNSW (Hierarchical Navigable Small World) — vektorlar grafi. Har vektor o'ziga yaqin bir necha vektor bilan bog'langan. Qidiruv grafning yuqori, siyrak qavatidan boshlanadi va har qadamda so'rovga yaqinroq qo'shniga o'tadi, keyin pastki, zich qavatlarda aniqlashtiradi. Xuddi shahar bo'ylab yo'l topish: avval katta yo'llar bilan kerakli tumanga, keyin ko'chalar bilan uyga.
Ikki asosiy sozlama: m — har tugunning qo'shnilari soni, ef_construction — qurishda ko'riladigan nomzodlar. So'rovda — hnsw.ef_search: nechta nomzod ko'rib chiqiladi (sukut 40). Ko'p nomzod — yuqori recall, lekin sekinroq.
4.4 IVFFlat
IVFFlat — vektorlarni guruhlarga (ro'yxatlarga) bo'lish. Indeks qurilganda lists ta markaz topiladi, har vektor eng yaqin markazning ro'yxatiga yoziladi. So'rovda faqat ivfflat.probes ta eng yaqin ro'yxat ko'riladi (sukut 1).
flowchart LR
Q["so'rov vektori"] --> M{"eng yaqin markazlar (probes)"}
M --> L1["ro'yxat 17: 300 vektor"]
M -.->|"probes = 1 da ko'rilmaydi"| L2["ro'yxat 42: 300 vektor"]
L1 --> T["shu ro'yxatlar ichida aniq qidiruv"]
L2 -.-> TDiagrammaning xavfli joyi — uzuq strelka. Haqiqiy eng yaqin qo'shni qo'shni ro'yxatda bo'lsa-yu, probes = 1 faqat bitta ro'yxatni ko'rsa — u o'tkazib yuboriladi. IVFFlat'ning yana bir sharti: indeks ma'lumot yuklangandan keyin quriladi, aks holda markazlar noto'g'ri chiqadi.
5. Kanon o'lchovi
5.1 Indekslar
CREATE INDEX v_hnsw ON v USING hnsw (e vector_cosine_ops);
CREATE INDEX v_ivf ON v USING ivfflat (e vector_cosine_ops)
WITH (lists = 100);vector_cosine_ops — indeks kosinus masofasi (<=>) uchun. So'rovda boshqa operator (<->) ishlatilsa, bu indeks ishlatilmaydi. Natija (30 000 vektor, 20 ta so'rov, har biri aniq natija bilan solishtirildi):
| Indeks | Qurish | Hajm |
|---|---|---|
| HNSW (m = 16, ef_construction = 64) | 14.1 s | 117 MB |
| IVFFlat (lists = 100) | 1.4 s | 118 MB |
| So'rov usuli | recall@10 | O'rtacha so'rov |
|---|---|---|
| aniq (indekssiz) | 100 % | 122.14 ms |
| HNSW, ef_search = 40 | 98.5 % | 2.29 ms |
| HNSW, ef_search = 100 | 100 % | 1.21 ms |
| IVFFlat, probes = 1 | 74.5 % | 1.03 ms |
| IVFFlat, probes = 10 | 100 % | 3.58 ms |
Kanon chiqishidan ikki qator, misol uchun:
HNSW ef_search=40 recall@10 = 98.5%, o'rtacha 2.29 ms/so'rov
IVFFlat probes=1 recall@10 = 74.5%, o'rtacha 1.03 ms/so'rov- aniq100 %
- HNSW ef 4098,5 %
- HNSW ef 100100 %
- IVFFlat probes 174,5 %
- IVFFlat probes 10100 %
Manba: IlmHamroh kanon o'lchovi, 2026-11-03: PostgreSQL 18.6 + pgvector 0.8.7, 30 000 klasterli sintetik vektor (768 o'lcham), 20 so'rov; bitta mashina, bitta yugurish
Asosiy xulosalar: HNSW sukut sozlamasida ham deyarli hammasini topdi va aniq qidiruvdan ~50–100 baravar tez ishladi. IVFFlat 10 baravar tez quriladi, lekin probes = 1 da har to'rtinchi natija noto'g'ri. probes ni oshirsangiz recall qaytadi, tezlik esa kamayadi.
Tekshirib ko'ring: Telegram bot uchun "o'xshash savollar" qidiruvi qilyapsiz: 2 million vektor, ma'lumot har kuni qo'shilib boradi. HNSW yoki IVFFlat?
Javob
Ko'pincha HNSW. IVFFlat markazlari indeks qurilgan paytdagi ma'lumotdan topiladi. Ma'lumot ko'p o'zgarsa, markazlar eskiradi va recall tushadi — indeksni vaqti-vaqti bilan qayta qurish kerak bo'ladi. HNSW esa yangi vektorni grafga qo'shib boraveradi. Uning narxi — sekin qurish va ko'proq xotira. Lekin "qaysi biri" degan savolga eng to'g'ri javob — o'z ma'lumotingizda recall va vaqtni o'lchash, xuddi kanon qilganidek.
5.2 Natijani halol o'qish
HNSW ef_search = 40 ning 100 dan sekin chiqqani mantiqqa zid. Ko'proq nomzod ko'rish tezroq bo'lishi mumkin emas. Skriptni o'qisak, sabab ko'rinadi: ef 40 o'lchovi indeks qurilgandan keyin birinchi bo'lib bajarilgan — indeks sahifalari hali xotirada emas edi. Ikkinchi o'lchov issiq keshda ishladi. Ya'ni bu ikki ms qiymatni solishtirib bo'lmaydi, recall'larni esa bo'ladi.
Yugurishlar orasida vaqt 5 baravargacha o'zgargan. Kanon qayd etgan: yuklangan mashinada HNSW qurish uch yugurishda 14 dan 63 soniyagacha bo'lgan. Biz jadvalga bitta yugurishni oldik — nisbatlar barqaror, mutlaq soniyalar emas.
20 ta so'rov — kichik namuna. 98.5 % — 200 ta natijadan 197 tasi. Bitta-ikkita farq foizni sezilarli o'zgartiradi.
Ma'lumot sintetik. Klasterlar markazi va shovqin kanon tomonidan tanlangan. Haqiqiy embedding'larda recall boshqacha bo'ladi — o'z ma'lumotingizda o'lchang.
6. Filtrlangan qidiruv tuzog'i
6.1 0 ta natija
Real ilovada so'rov deyarli doim filtrli: "mening vazifalarim ichidan o'xshashlar". Kanon shuni sinadi: WHERE egasi_id = 7 — 300 egadan biri, 100 ta vektor. Rejalovchi bu holatda HNSW'ni o'zi tanlamaydi (skript izohida — Seq Scan tanlaydi), shuning uchun kanon enable_seqscan = off bilan indeksni majburladi:
SET enable_seqscan = off;
SELECT id FROM v WHERE egasi_id = 7
ORDER BY e <=> :'q1'::vector LIMIT 10;Reja (kanon chiqishi; 768 ta sondan iborat vektor qisqartirildi):
Limit
-> Index Scan using v_hnsw on v
Order By: (e <=> '[0.0100281555,-0.023947496,…]'::vector)
Filter: (egasi_id = 7)Natija:
iterative_scan=off: 0 ta natija (10 kutilgan)Nol. Egasining 100 ta vektori bor, so'rov 10 tasini so'radi, indeks hech nima qaytarmadi. Sabab rejada: Filter indeks skanidan keyin turibdi. HNSW ef_search = 40 ta eng yaqin nomzodni topdi, keyin filtr ulardan egasi 7 ga tegishlilarini qoldirdi. Qirqtasining birortasi ham egasi 7 niki emas edi.
6.2 Nega aynan nol
Kanon skriptidagi formulalarga qaraymiz. Vektor i ning egasi — 1 + i % 300, klasteri — 1 + i % 50. Egasi 7 bo'lishi uchun i % 300 = 6. 300 soni 50 ga bo'linadi, demak bunday i larda i % 50 ham doim 6. Ya'ni egasi 7 ning hamma 100 ta vektori bitta klasterda — 7-klasterda. So'rov vektori esa 8-klaster atrofida yaratilgan. HNSW 8-klasterdan 40 ta nomzod oldi — ularning hech biri egasi 7 niki emas.
Bu sintetik ma'lumotning "eng yomon holati". Haqiqiy hayotda ham shunday bo'ladi: foydalanuvchining vazifalari faqat ish haqida, u esa "oila" mavzusida so'raydi. Yaqin nomzodlar boshqa odamlarniki chiqadi.
6.3 iterative_scan
pgvector 0.8 dan boshlab yechim bor — iterativ skan: filtrdan keyin natija yetmasa, indeks yana nomzod qidirishda davom etadi.
SET hnsw.iterative_scan = relaxed_order;
SELECT id FROM v WHERE egasi_id = 7
ORDER BY e <=> :'q1'::vector LIMIT 10; iterative_scan=relaxed_order: 10 ta natija
Time: 86.042 ms10 ta — kutilgandek. Narxi — vaqt: 86 ms, oddiy HNSW so'rovidan ancha uzoq, chunki indeks ko'p nomzodni ko'rib chiqdi. relaxed_order — natijalar tartibi qat'iy bo'lmasligi mumkin (hujjatga ko'ra strict_order varianti ham bor). Kanon loyiha kodida ham aynan shu sozlamani har qidiruvdan oldin SET LOCAL bilan qo'yadi — buni Embedding va semantik qidiruv darsida ko'ramiz.
Boshqa yechimlar: egasi bo'yicha oddiy btree indeks (kichik egalar uchun rejalovchi aniq qidiruvni tanlaydi — 100 ta vektorni ko'rish arzon), egasi bo'yicha bo'limlash (Partitioning) yoki partial indeks.
Tekshirib ko'ring:
ef_searchni 40 dan 1000 ga ko'tarsak,iterative_scansiz ham natija chiqarmidi?
Javob
Bu ma'lumotda — ehtimol yo'q yoki juda kam. 1000 ta nomzod hamon 8-klaster va unga yaqin klasterlardan olinadi (har klasterda 600 ta vektor). Egasi 7 ning vektorlari esa boshqa klasterda. ef_search ni ko'tarish ehtimolni oshiradi, lekin kafolat bermaydi — va har so'rovni sekinlashtiradi. iterative_scan aynan "yetarli natija topilguncha davom et" deydi. Bu savolni kanon o'lchamagan — javob mantiqiy xulosa.
7. halfvec: ikki baravar kichik indeks
float4 — 4 bayt. halfvec — har son 2 baytli (16-bit) yarim aniqlikdagi son. Indeksni halfvec ga o'girib qurish mumkin, jadvaldagi asl vektor o'zgarmaydi:
CREATE INDEX v_half ON v
USING hnsw ((e::halfvec(768)) halfvec_cosine_ops);CREATE INDEX
Time: 16562.387 ms (00:16.562)
halfvec HNSW hajmi: 59 MB117 MB o'rniga 59 MB — aynan yarmi. Katta indeks xotiraga sig'masa, har so'rov diskka boradi; kichik indeks — tezroq. Halol eslatma: kanon halfvec indeksining recall'ini o'lchamagan. Aniqlik pasayishi odatda kichik deyiladi, lekin bu raqamsiz da'vo — o'z ma'lumotingizda tekshiring. Bunday indeksni ishlatish uchun so'rovda ham e::halfvec(768) <=> ... yozilishi kerak.
halfvec ning yana bir foydasi — o'lcham chegarasi. pgvector README'siga ko'ra (2026-11-03 holati) HNSW indeksi vector turida ko'pi bilan 2 000 o'lchamni, halfvec da esa 4 000 o'lchamni qabul qiladi. Ba'zi modellar to'liq o'lchamda 3 072 ta son qaytaradi. Bunday vektorga oddiy HNSW qurib bo'lmaydi: yo o'lchamni kichraytirasiz (kanon 768 ni tanladi), yo indeksni halfvec da qurasiz.
8. Vektor bazalar
pgvector — Postgres ichidagi yechim. Alohida vektor bazalar ham bor — faqat shu ish uchun qurilgan tizimlar. Quyidagilar rasmiy hujjat bo'yicha (hujjatlar 2026-11-03 da ochib ko'rildi; tizimlarning o'zi kursda sinalmagan):
| Tizim | Turi |
|---|---|
| Qdrant | ochiq manbali, o'zingiz o'rnatasiz yoki bulut |
| Pinecone | managed bulut xizmati (o'z serveringizga o'rnatilmaydi) |
| Weaviate | ochiq manbali, embedding modellariga tayyor ulanishlar bilan |
| Milvus | ochiq manbali, juda katta hajmlar uchun |
Qachon nima:
| pgvector | Alohida vektor baza | |
|---|---|---|
| Ma'lumot | vazifa va vektor bir bazada, bitta tranzaksiyada | ikki tizim, sinxronlash kerak |
| Huquqlar | RLS va rollar o'sha | o'z tizimi |
| Hajm | o'n millionlargacha qulay | yuz millionlar, maxsus optimallash |
| Operatsiya | yangi xizmat yo'q | yangi xizmat, zaxira, monitoring |
Kanon pgvector'ni tanladi: vazifalar allaqachon Postgres'da, embedding'lar o'sha foydalanuvchiga tegishli va RLS bilan himoyalanadi, hajm kichik. Alohida baza bo'lsa, vazifa o'chganda vektorni ham o'chirish — ikki tizimni sinxronlash muammosi (CDC va ma'lumot sinxronlash darsida).
9. Ko'p uchraydigan xatolar
| Belgi | Sabab | Davosi |
|---|---|---|
| Filtrli so'rov kam yoki 0 natija | filtr HNSW nomzodlaridan keyin | hnsw.iterative_scan, btree indeks, bo'limlash |
| Indeks ishlatilmayapti | operator indeks klassiga mos emas (<-> va vector_cosine_ops) |
bir xil masofa |
| IVFFlat recall past | probes = 1 yoki indeks bo'sh jadvalda qurilgan |
probes ni oshirish, ma'lumotdan keyin qurish |
| Natijalar "ma'nosiz" | vektorlar normallanmagan yoki boshqa model bilan yaratilgan | bitta model, normallash |
| Indeks qurish juda sekin | maintenance_work_mem kichik |
qurishda oshirish (kanon — 256 MB) |
| Vektor yozilmaydi | o'lcham mos emas (vector(768) ga 3 talik) |
model o'lchamini tekshirish |
Oxirgisidan oldingi qatordagi "boshqa model" — muhim tuzoq: ikki xil modelning vektorlarini solishtirib bo'lmaydi, ularning "koordinata tizimlari" boshqa. Kanon shuning uchun har vektor yonida model nomini saqlaydi (embedding darsida).
10. Mashqlar
Mashqlar kurs/mashqlar/31/08-pgvector/ da. 2- va 3-mashq yechimlari Node 24.21.0 da tekshirilgan (biz ishga tushirdik).
1-mashq (oson): bo'shliqlarni to'ldiring
- pgvector'da kosinus masofa operatori —
- Taxminiy qidiruvda haqiqiy eng yaqinlarning topilgan ulushi —
- Kanonda IVFFlat
probes = 1dagi recall@10 — % - Filtrli HNSW so'rovida kanon olgan natijalar soni —
- Ikki baravar kichik HNSW indeksi uchun vektor turi —
Yechim
- <=> —
<->L2,<#>manfiy skalyar ko'paytma. - recall — 100 % bo'lsa, natija aniq bilan bir xil.
- 74.5 —
probes = 10da 100 %. - 0 —
iterative_scanbilan 10. - halfvec — 117 MB o'rniga 59 MB.
2-mashq (o'rta): eng yaqin k ta
yaqinlar(sorov, yozuvlar, k) funksiyasini yozing: yozuvlar — { id, v } massivi. Hammasini kosinus masofa bo'yicha tartiblab, eng yaqin k tasining id sini qaytarsin — pgvector'dagi ORDER BY v <=> sorov LIMIT k ning aniq (indekssiz) varianti. Ishora: toSorted (toSorted va boshqalar), slice, map.
Yechim
const skalyar = (a, b) => a.reduce((s, x, i) => s + x * b[i], 0);
const masofa = (a, b) =>
1 - skalyar(a, b) / Math.sqrt(skalyar(a, a) * skalyar(b, b));
function yaqinlar(sorov, yozuvlar, k) {
return yozuvlar
.map((y) => ({ id: y.id, m: masofa(sorov, y.v) }))
.toSorted((a, b) => a.m - b.m)
.slice(0, k)
.map((y) => y.id);
}
const yozuvlar = [
{ id: 1, v: [0.9, 0.1, 0.2] },
{ id: 2, v: [0.1, 0.9, 0.3] },
{ id: 3, v: [0.8, 0.2, 0.1] },
{ id: 4, v: [0.2, 0.8, 0.5] },
{ id: 5, v: [0.7, 0.0, 0.6] },
];
console.log(yaqinlar([0.85, 0.15, 0.1], yozuvlar, 2));
console.log(yaqinlar([0.1, 1.0, 0.4], yozuvlar, 3));Konsolda:
[ 3, 1 ]
[ 2, 4, 3 ]Ikkinchi so'rov "dars" tomonga qaragan — 2 va 4 birinchi. Bu funksiya har so'rovda hamma yozuvni ko'radi — aynan kanondagi 122 ms lik "aniq" qidiruv, faqat kichik hajmda.
3-mashq (qiyin): mini IVFFlat va recall
Ikki o'lchamli 12 ta nuqta va 3 ta markaz berilgan. ivfQidir(sorov, k, probes): avval so'rovga eng yaqin probes ta markazni toping, keyin faqat shu markazlarga tegishli nuqtalar ichidan eng yaqin k tasini qaytaring (masofa — L2). Har nuqta o'ziga eng yaqin markazga tegishli. recall(aniq, taxminiy) — ikkala ro'yxatning umumiy elementlari ulushi. probes = 1 va probes = 2 ni solishtiring. Ishora: Set (Set), filter, toSorted.
Yechim
const nuqtalar = [
[1, 1], [2, 1], [1, 2], [2, 2],
[8, 1], [9, 2], [8, 2], [9, 1],
[5, 8], [6, 9], [5, 9], [4.4, 4.6],
];
const markazlar = [[1.5, 1.5], [8.5, 1.5], [5, 8.5]];
const l2 = (a, b) => Math.hypot(a[0] - b[0], a[1] - b[1]);
const eng = (p, ruyxat, k) => ruyxat
.map((x, i) => ({ i, m: l2(p, x) }))
.toSorted((a, b) => a.m - b.m).slice(0, k).map((x) => x.i);
const guruh = nuqtalar.map((p) => eng(p, markazlar, 1)[0]);
function ivfQidir(sorov, k, probes) {
const tanlangan = new Set(eng(sorov, markazlar, probes));
const nomzod = nuqtalar
.map((p, i) => ({ p, i }))
.filter((x) => tanlangan.has(guruh[x.i]));
return nomzod
.toSorted((a, b) => l2(sorov, a.p) - l2(sorov, b.p))
.slice(0, k).map((x) => x.i);
}
function recall(aniq, taxminiy) {
const a = new Set(aniq);
return taxminiy.filter((x) => a.has(x)).length / aniq.length;
}
const sorov = [3.4, 3.4];
const aniq = eng(sorov, nuqtalar, 3);
for (const probes of [1, 2]) {
const t = ivfQidir(sorov, 3, probes);
console.log(`probes=${probes}:`, t, "recall:", recall(aniq, t));
}
console.log("aniq:", aniq);Konsolda:
probes=1: [ 3, 1, 2 ] recall: 0.6666666666666666
probes=2: [ 11, 3, 1 ] recall: 1
aniq: [ 11, 3, 1 ]11-nuqta ([4.4, 4.6]) so'rovga eng yaqin, lekin u 3-markaz ([5, 8.5]) guruhiga tushgan — chunki ikkala markaz orasida 3-markazga biroz yaqinroq. So'rov esa 1-markazga eng yaqin. probes = 1 faqat 1-markaz ro'yxatini ko'radi va eng yaqin nuqtani o'tkazib yuboradi — recall uchdan ikki. probes = 2 qo'shni ro'yxatni ham ko'radi — recall 1. Kanondagi 74.5 % → 100 % ning kichik nusxasi.
11. Loyiha qadami: kanon tajribasi
Bu darsda vazifalar-api o'zgarmaydi va commit yo'q. Vektorlar hali yo'q: ularni hisoblaydigan AI modelini keyingi darslarda ulaymiz. Loyihaga pgvector'ni qismning AI bo'limida qo'shamiz — Embedding va semantik qidiruv da vazifa_embeddinglar jadvali va HNSW indeksi, RAG asoslari da shu qidiruv ustiga yordamchi quriladi. O'sha yerda bu darsdagi saboqlar kodga aylanadi. Vektorlar normallanadi, har qidiruvdan oldin iterative_scan yoqiladi. egasi_id bo'yicha oddiy btree indeks ham quriladi, jadval esa RLS bilan himoyalanadi va har vektor yonida model nomi saqlanadi. Kod o'sha darslarda. Partitioning darsidagi loyiha o'zgarishlari esa hali commit qilinmagan — ularni keyingi darsda statistika bilan birga commit qilamiz.
Kanon tajribasi: alohida baza, pgvector 0.8.7, 30 000 klasterli sintetik vektor, aniq qidiruv, HNSW va IVFFlat, 20 so'rovda recall, filtrli qidiruv va iterative_scan, halfvec indeksi. Oxirida baza o'chirildi. Qdrant, Pinecone, Weaviate — sinalmagan.
O'zingiz takrorlaydigan qism Docker'siz: 3-mashqdagi mini IVFFlat'da so'rovni [3.4, 3.4] dan [2.5, 2.5] ga o'zgartiring. probes = 1 da recall qancha bo'lishini oldindan ayting, keyin tekshiring. Keyin 11-nuqtani [4.4, 4.6] dan [3.8, 3.8] ga ko'chiring — u qaysi guruhga o'tadi va recall nima bo'ladi?
12. Real ishda
- Qayerda uchraydi: semantik qidiruv, tavsiyalar ("shunga o'xshash mahsulotlar"), AI yordamchilar (RAG), dublikat topish, rasm bo'yicha qidiruv.
- Intervyu savollari: "Kosinus va L2 masofa farqi?", "HNSW va IVFFlat qachon?", "Recall nima va uni qanday o'lchaysiz?", "Filtrli vektor qidiruvining muammosi?", "pgvector yoki alohida vektor baza?"
- Raqamlar bilan gapiring: "recall@10 98.5 %, so'rov 2 ms, indeks 117 MB" — vektor qidiruvni har doim shu uch o'lchov bilan baholang.
- Kod review'da: "bu so'rovda filtr bor — iterative_scan yoqilganmi?", "vektorlar qaysi model bilan, normallanganmi?", "indeks operatori so'rov operatoriga mosmi?"
Xulosa
- Vektor qidiruv — "eng yaqin k ta nuqta"; embedding'lar bilan bu ma'no bo'yicha qidiruvga aylanadi.
- pgvector:
vector(n),<=>kosinus,<->L2,<#>manfiy skalyar ko'paytma; normallangan vektorlarda tartib bir xil. - HNSW — graf, yuqori recall va tez so'rov, sekin qurish; IVFFlat — ro'yxatlar, tez qurish,
probesga sezgir. - Kanon: aniq 122 ms → HNSW 1–2 ms, recall 98.5–100 %; IVFFlat
probes = 1— 74.5 %. - Filtr HNSW nomzodlaridan keyin ishlaydi — kanonda 0 natija;
hnsw.iterative_scanbilan 10. halfvecindeksi — ikki baravar kichik; recall'ini o'zingiz o'lchang.
Keyingi dars: Time-series va analitik DB'lar — vazifa hodisalaridan faollik grafigini quramiz, statistika yo'lini qo'shamiz va loyihaning bu bosqichini commit qilamiz.
Manbalar
- pgvector README (operatorlar,
<#>manfiy ekani, HNSW sukutlari m = 16 / ef_construction = 64 / ef_search = 40, IVFFlat probes = 1, iterative index scans 0.8.0 dan, halfvec; joriy versiya 0.8.7): https://github.com/pgvector/pgvector (2026-11-03 holati) - Malkov va Yashunin, "Efficient and robust approximate nearest neighbor search using Hierarchical Navigable Small World graphs": https://arxiv.org/abs/1603.09320
- Qdrant hujjati: https://qdrant.tech/documentation/ (2026-11-03 holati)
- Pinecone hujjati: https://docs.pinecone.io/ (2026-11-03 holati)
- Weaviate hujjati: https://docs.weaviate.io/weaviate (2026-11-03 holati)
- Kanon o'lchovi (IlmHamroh, 2026-11-03): PostgreSQL 18.6 + pgvector 0.8.7 (
pgvector/pgvector:pg18), 30 000 sintetik vektor, 768 o'lcham. Brauzer modellari va mashq yechimlari — biz ishga tushirdik (2026-11-03, Node 24.21.0).
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!