yoitsmeyusuf/turkce-tibbi-vektor-veri-seti
Turkce Tibbi Makale Vektor Veri Seti umutertugrul/turkish-hospital-medical-articles veri setinden secilen guven, medipol, florence hastane kaynaklarindan 402 makale parcalanip magibu/embeddingmagibu-200m ile vektorlestirildi. Toplam 3138 chunk. Sutunlar sutun tip aciklama chunk_id string Parcanin kimligi (hastane-makale-bolum-parca) url string Parcanin ait oldugu orijinal makalenin kaynak baglantisi chunk_text string Parcalanmis metin icerigi (ham;… See the full description on the dataset page: https://huggingface.co/datasets/yoitsmeyusuf/turkce-tibbi-vektor-veri-seti.
Turkce Tibbi Makale Vektor Veri Seti
umutertugrul/turkish-hospital-medical-articles veri setinden secilen guven, medipol, florence hastane kaynaklarindan 402 makale parcalanip `magibu/embeddingmagibu-200m` ile vektorlestirildi. Toplam 3138 chunk.
Sutunlar
1. Chunking stratejisi
Yontem: bolum tabanli (yapisal) parcalama.
1. metni \n ile bloklara ayir
2. baslik isaretle: len<120 & basinda "- " yok & sonunda ":" yok
& ("?" ile bitiyor | "." ya da "!" ile bitmiyor)
3. icindekiler menusunu sil (govdede baslik olarak da gecen "- X" satirlari)
4. bolum = baslik + kendisinden sonraki icerik bloklari
5. chunk uret:
bolum <= 320 token -> oldugu gibi (~%89)
bolum > 320 token -> blok sinirindan bol, 48 overlap
tek blok asiyorsa cumleden bol
bolum < 48 token -> komsu bolume yapistirNeden bu yontem secildi. Once sabit token + overlap denendi, olcum sonucu iki varsayim da cokdu:
- `\n\n` yok. Kaynak veri setinin 14 split'i olculdu;
livdisinda hicbirinde cift newline gecmiyor. Ayrac tek\n. Ama metin duz blok da degil: makaleler "baslik + o basligin altindaki icerik" seklinde diziliyor. Baslik tespiti kurali 14 split'in hepsinde bolumlerin %84-94'unde isabet ediyor. - 350 token hedefi cok buyuktu. Modelin tokenizer'i Turkce'ye ozel oldugu icin 5,77 karakter/token veriyor (cok dilli modellerde ~3,2). Bolumlerin medyani sadece ~90 token, p99'u ~400. 350 token'lik hedef bolumlerin %99'undan buyuk oldugu icin 3-5 alakasiz bolumu tek chunk'a tikardi.
Sonucta chunk sinirini tokenizer degil, belgenin kendi yapisi belirliyor. Overlap chunk'larin yalnizca ~%11'inde devreye giriyor: overlap keyfi noktadan kesince anlamin ikiye bolunmesini telafi eden bir yamadir, biz keyfi noktadan degil baslik sinirindan kestigimiz icin sadece istisnai buyuk bolumlerde gerekiyor.
Neden 320 token tavan. Teknik zorunluluk degil - model 8192 token aliyor. Sebep arama kalitesi: bir chunk ne kadar cok konu icerirse vektoru o kadar ortalamaya kayar, pozitif ve negatif sorularin skorlari birbirine yaklasir, esik ayirt edemez hale gelir. Tavan konu safligini koruyan sinir.
Sonuc: 402 makale -> 3138 chunk, medyan 106 token, p90 244, max 320 (tavani asan: 0), %89'i baslik tasiyor.
2. Embedding modeli
Neden secildi: Turkce'ye ozel uyarlanmis (cross-lingual tokenizer surgery + offline distillation), 8K context uzun bolumleri kesmeden aliyor, 200M parametre 8 GB VRAM'e rahat sigiyor - 3138 chunk 18 saniyede gomuldu. Ciktilar L2-normalize oldugu icin kosinus benzerligi = nokta carpimi.
Model asimetrik - prompt kullanimi zorunlu. config_sentence_transformers.json:
"query": "task: search result | query: "
"document": "title: none | text: "Chunk'lar document, sorular query prompt'u ile gomuluyor. Bu atlanirsa model yine calisir ama skor dagilimi kayar ve esik analizi anlamsizlasir.
Baslik enjeksiyonu: document prompt'undaki title: none alanina gercek baslik konuyor. Hastane metinleri anafora dolu ("Bu hastalikta...", "Tedavi sureci..."); tek basina chunk cogu zaman neyden bahsettigini soylemiyor. chunk_text sutunu ham kaliyor - baslik yalnizca vektor uretiminde kullanildi.
float32 tercih edildi: model varsayilan olarak bfloat16 yukleniyor ve o hassasiyette L2 normlar 0,996-1,004 arasinda kayiyor. normalize_embeddings=True de cozmuyor (normalizasyon da bf16 icinde). float32'de normlar tam 1,0.
Kullanim
from datasets import load_dataset
import numpy as np
from sentence_transformers import SentenceTransformer
veri = load_dataset("yoitsmeyusuf/turkce-tibbi-vektor-veri-seti", split="train")
vektorler = np.array(veri["chunk_vector"], dtype=np.float32)
model = SentenceTransformer("magibu/embeddingmagibu-200m")
soru = model.encode(["safra kesesi tasi nasil tedavi edilir"], prompt_name="query")[0]
skorlar = vektorler @ soru # L2-normalize -> nokta carpimi = kosinus
en_iyi = int(np.argmax(skorlar))
if skorlar[en_iyi] < 0.56:
print("Bu sorunun cevabi dokumanlarimda yer almamaktadir.")
else:
print(veri[en_iyi]["chunk_text"], veri[en_iyi]["url"])3. Esik (threshold) analizi
Secilen esik: 0.56 — 30 soruluk test setinde 28/30 (20/20 pozitif, 8/10 negatif).
Esik bastan secilmedi, 0,30-0,90 arasi 0,01 adimla supuruldu. Her esik degeri iki hata turunu dengeliyor: esik cok yuksekse cevabi olan soruya "bilmiyorum" denir, cok dusukse alakasiz soruya uydurma cevap uretilir.
Test seti. 20 pozitif soru chunk'lardan turetildi (soru yazip cevabini aramak degil): her sorunun gold_chunk_id'si kayitli, boylece erisim goz karari degil olcum. 10 negatif soru bilerek uc gruba ayrildi - hepsi "Fenerbahce kac sampiyonluk aldi" tipinde olsaydi esik analizi sahte cikardi, 0,2 de 0,6 da ayni sonucu verirdi.

Neden tam 0.56. Plato ortasi secildi; bu ayni zamanda karar bosluğunun ortasi:
en yuksek dogru elenen negatif : 0.5438
esik : 0.5600
en dusuk pozitif : 0.5696Erisim (recall): gold chunk 20/20 soruda ilk 5 sonuc icinde (top-1'de 13/20). Olcut bilerek "ilk 5" - LLM'e ilk K chunk birden veriliyor ve ayni bolumun kardes chunk'lari cogu zaman gold kadar gecerli kaynak.
Vektor veritabani: ChromaDB, hnsw:space=cosine. Iki tuzak: Chroma'nin varsayilan mesafesi L2'dir (acikca cosine verilmeli) ve Chroma distance dondurur, benzerlik degil -> benzerlik = 1 - distance. HNSW yaklasik arama yaptigi icin 30 sorunun tamami numpy ile kesin kosinus hesabiyla karsilastirildi: sapma 0/30.
Esigin yapisal siniri
Esikle elenemeyen 2 negatif soru var ve sebepleri ogretici:
- "Sıtma hastalığı hangi sivrisinek türüyle bulaşır?" — 0.6267
- "Laparoskopik kolesistektomi ameliyatı Medipol'de kaç TL tutuyor?" — 0.6095
Birincisinde korpusta sitma yok ama "Sivrisinek Isirigina Ne Iyi Gelir?" makalesi var - vektor konu yakinligini goruyor. Ikincisinde kolesistektomi korpusta var, sadece fiyat bilgisi yok.
Yani esik konu yoklugunu filtreliyor, bilgi yoklugunu filtrelemiyor. Kosinus benzerligi "bu metin bu soruyla ayni konuda mi" sorusunu cevapliyor, "bu metin bu sorunun cevabini iceriyor mu" sorusunu degil. Bu vektor aramanin yapisal siniri, ayar hatasi degil; kapatmanin yolu ikinci bir kapi (LLM'e "verilen metinde cevap yoksa bilmiyorum de" talimati + ciktinin kaynaga karsi dogrulanmasi).
Kod
Veri setini ureten ve olcen kodlar bu reponun kod/ klasorunde:
python 1_veri_hazirla.py && python 2_gom_ve_indeksle.py && python 4_esik_analizi.py
python ara.py "safra kesesi tasi nasil tedavi edilir"Kaynak veri seti gated: once dataset sayfasinda "Agree and access" tiklanmali.
