RAG Uygulamalarında Verimli Parçalama Stratejileri: 5 Yöntem ve Python Uygulamaları
Büyük Dil Modelleri (Large Language Models – LLM) çağında, kullanıcıların karmaşık sorularına doğru ve güncel yanıtlar verebilmek, bu modellerin yeteneklerini gerçek dünya bilgileriyle birleştirmekten geçiyor. Ancak LLM’lerin sınırlı bağlam pencereleri (context window) ve sürekli güncellenmeyen eğitim verileri, bu birleşimi zorlu bir görev haline getiriyor. İşte tam bu noktada, Geri Kazanım Artırılmış Üretim (Retrieval Augmented Generation – RAG) mimarisi devreye giriyor. RAG, bir sorguya yanıt vermeden önce ilgili bilgileri harici bir bilgi tabanından alarak (retrieve) LLM’e sunar ve böylece modelin daha bilinçli ve doğru yanıtlar üretmesini sağlar. Bu sürecin en kritik adımlarından biri ise “parçalama (chunking)” işlemidir. Veri parçalama, büyük metin belgelerini yönetilebilir, anlamlı ve LLM’in bağlam penceresine sığacak küçük parçalara ayırma sanatıdır. Peki, bu parçalama işlemini ne kadar verimli yapıyoruz? Doğru parçalama stratejisi seçimi, RAG sisteminizin performansı üzerinde doğrudan bir etkiye sahiptir. Bu makalede, RAG sistemlerinizin etkinliğini artıracak 5 farklı parçalama stratejisini detaylıca inceleyecek ve her biri için Python’da uygulanabilir kod örnekleri sunacağız.
RAG ve Parçalamanın Temel Kavramları: Neden Bu Kadar Önemli?
RAG, yani Geri Kazanım Artırılmış Üretim, modern yapay zeka uygulamalarında büyük dil modellerinin (LLM) bilgi eksikliklerini gidermek ve güncel, doğru yanıtlar üretmelerini sağlamak için kullanılan güçlü bir mimaridir. Temel olarak RAG, bir kullanıcının sorusuna yanıt vermeden önce, bu soruyla alakalı bilgileri önceden tanımlanmış bir bilgi tabanından veya veri kaynağından “geri kazanır” (retrieve) ve ardından bu geri kazanılan bilgileri LLM’e “bağlam (context)” olarak sunarak bir yanıt “üretmesini” (generate) ister. Bu sayede LLM, yalnızca kendi eğitim verileriyle sınırlı kalmaz, aynı zamanda dışarıdan alınan güncel ve özelleşmiş bilgilere de erişebilir.
Bu mimarinin etkinliği, geri kazanılan bilginin kalitesine doğrudan bağlıdır. Ancak harici bilgi tabanları genellikle çok büyük ve karmaşık metin belgelerinden oluşur. Bir kitabın tamamını, uzun bir makaleyi veya binlerce sayfalık bir teknik dokümanı doğrudan bir LLM’e sunmak mümkün değildir; çünkü LLM’lerin bağlam pencereleri sınırlıdır. Örneğin, bazı LLM’ler binlerce belirteç (token) işleyebilirken, bazıları daha azını işleyebilir. Bu bağlam penceresi sınırlaması, “parçalama (chunking)” işlemini zorunlu kılar.
Parçalama, büyük metin belgelerini, her biri belirli bir anlam bütünlüğünü koruyan ve LLM’in bağlam penceresine sığabilecek boyutta küçük “parçalara (chunks)” ayırma işlemidir. Bu parçalar daha sonra “embedding” adı verilen vektör gösterimlerine dönüştürülür. Embedding’ler, metin parçalarının anlamsal içeriğini yoğun bir sayı dizisi olarak temsil eder. Bu vektörler, bir vektör veritabanında saklanır. Kullanıcı bir sorgu sorduğunda, bu sorgu da embedding’e dönüştürülür ve vektör veritabanında en benzer (yani anlamsal olarak en yakın) metin parçaları aranır. Bulunan en alakalı parçalar, LLM’e sunularak yanıtın üretilmesine yardımcı olur.
Parçalama işlemi, RAG sisteminin omurgasını oluşturur. Yanlış veya yetersiz parçalama, aşağıdaki sorunlara yol açabilir:
- Bilgi Kaybı: Çok küçük parçalar, bir konunun bütünlüğünü bozabilir ve önemli bağlamın kaybolmasına neden olabilir. Örneğin, bir cümlenin yarısı bir parçada, diğer yarısı başka bir parçada kalırsa, her iki parça da anlamsız hale gelebilir.
- Alakasız Bilgi Getirme: Çok büyük parçalar, sorguyla doğrudan ilgili olmayan birçok gereksiz bilgiyi de beraberinde getirebilir. Bu, LLM’in dikkatini dağıtabilir ve istenen bilgiyi bulmasını zorlaştırabilir, hatta “hallüsinasyon” denilen yanlış veya uydurma yanıtlar üretmesine yol açabilir.
- Maliyet ve Gecikme: Büyük parçalar, LLM’e daha fazla belirteç göndermek anlamına gelir. Bu da API maliyetlerini artırır ve yanıt sürelerini uzatır.
- Bağlam Penceresi Aşımı: LLM’in bağlam penceresine sığmayacak kadar büyük parçalar, sistemin doğru çalışmasını engeller.
Doğru parçalama stratejisi, bu sorunları minimize ederek RAG sisteminizin hem daha doğru hem de daha verimli çalışmasını sağlar. Her belgenin ve kullanım senaryosunun kendine özgü ihtiyaçları olduğundan, tek bir “en iyi” parçalama yöntemi yoktur. Bu nedenle, farklı stratejileri anlamak ve doğru zamanda doğru olanı seçmek büyük önem taşır.
RAG Parçalama Stratejileri: 5 Etkili Yöntem ve Uygulamaları
RAG sistemlerinde bilgi geri kazanımının kalitesini doğrudan etkileyen parçalama işlemi için farklı yaklaşımlar mevcuttur. Her bir stratejinin kendine özgü avantajları ve dezavantajları bulunur ve en iyi seçim genellikle veri tipine, kullanım senaryosuna ve istenen performansa bağlıdır. Şimdi, bu 5 etkili parçalama stratejisini detaylıca inceleyelim ve her biri için Python’da çalışan kod örnekleri sunalım. Kod örneklerinde genellikle LangChain kütüphanesini kullanacağız, zira metin işleme ve parçalama konusunda oldukça kapsamlı araçlar sunmaktadır.
1. Sabit Boyutlu Parçalama (Fixed-size Chunking): Basit ve Hızlı Bir Başlangıç
Sabit boyutlu parçalama, en temel ve yaygın kullanılan yöntemlerden biridir. Bu stratejide, metin belgeleri önceden belirlenmiş sabit bir karakter veya belirteç (token) sayısına göre eşit büyüklükteki parçalara ayrılır. Örneğin, her parça 500 karakter uzunluğunda olabilir. Bu yöntem, uygulanması kolay ve hesaplama açısından oldukça verimlidir. Özellikle metinler arasında belirgin bir yapısal ayrım bulunmadığı veya hızlı bir prototipleme gerektiği durumlarda tercih edilebilir.
Avantajları:
- Basitlik: Uygulaması oldukça kolaydır. Sadece bir parça boyutu belirlemek yeterlidir.
- Hız: Metin üzerinde karmaşık analizler gerektirmediği için hızlı bir şekilde çalışır.
- Tekdüzelik: Tüm parçalar aynı boyutta olduğu için yönetimi kolaydır.
Dezavantajları:
- Bağlam Kaybı Riski: Bir cümlenin veya paragrafın ortasından kesilme olasılığı yüksektir. Bu, parçaların anlamsal bütünlüğünü bozabilir ve önemli bilgilerin kaybolmasına neden olabilir.
- Anlamsız Parçalar: Bir parçanın sonu ile bir sonraki parçanın başı arasında anlamsal bir bağlantı kopukluğu oluşabilir.
Gerçek Dünya Senaryosu: Büyük bir blog arşivi veya haber makaleleri koleksiyonu gibi yapısal olarak çok düzenli olmayan, ancak hızlıca indekslenmesi gereken metinler için uygun olabilir. Örneğin, bir haber portalının geçmiş makalelerini hızlıca RAG sistemine dahil etmek istediğinizde bu yöntemle başlayabilirsiniz.
Python Uygulaması:
LangChain’in CharacterTextSplitter sınıfı, bu tür bir parçalama için idealdir.
from langchain.text_splitter import CharacterTextSplitter
# Örnek uzun metin
uzun_metin = """
RAG (Retrieval Augmented Generation), büyük dil modellerinin (LLM) yeteneklerini artırmak için kullanılan güçlü bir mimaridir.
Bu yaklaşım, bir LLM'in belirli bir sorguya yanıt vermeden önce harici bir bilgi tabanından ilgili bilgileri geri kazanmasını sağlar.
Böylece model, sadece kendi eğitim verileriyle sınırlı kalmaz, aynı zamanda güncel ve özelleşmiş bilgilere de erişebilir.
RAG'ın temel amacı, LLM'lerin doğru, güncel ve bağlamla alakalı yanıtlar üretmesine yardımcı olmaktır.
Bu süreçte en kritik adımlardan biri, büyük belgeleri yönetilebilir parçalara ayırma işlemidir: parçalama (chunking).
Doğru parçalama stratejisi seçimi, RAG sisteminizin performansını doğrudan etkiler.
Çok küçük parçalar bağlamı bozabilirken, çok büyük parçalar alakasız bilgileri getirerek performansı düşürebilir.
Bu makalede farklı parçalama stratejilerini inceleyeceğiz.
"""
# Sabit boyutlu parçalayıcıyı oluşturma
# chunk_size: her parçanın maksimum karakter sayısı
# chunk_overlap: parçalar arasında örtüşen karakter sayısı (bu örnekte 0)
text_splitter = CharacterTextSplitter(
separator="\n\n", # Paragraf sonlarında bölmeye çalış
chunk_size=200,
chunk_overlap=0,
length_function=len,
is_separator_regex=False,
)
chunks = text_splitter.create_documents([uzun_metin])
print(f"Toplam parça sayısı: {len(chunks)}")
for i, chunk in enumerate(chunks):
print(f"\n--- Parça {i+1} (Uzunluk: {len(chunk.page_content)}) ---")
print(chunk.page_content)
Bu örnekte, metni 200 karakterlik parçalara ayırdık ve parçalar arasında herhangi bir örtüşme (overlap) bırakmadık. Gördüğünüz gibi, her parça belirlenen boyuta yakın olacak şekilde oluşturuldu, ancak anlamsal bütünlük bazı yerlerde bozulabilir.
2. Örtüşmeli Sabit Boyutlu Parçalama (Fixed-size Chunking with Overlap): Bağlamı Koruma
Sabit boyutlu parçalamanın en büyük dezavantajı olan bağlam kaybını azaltmak için geliştirilmiş bir yöntemdir. Bu stratejide, parçalar yine sabit bir boyutta oluşturulur, ancak her parça bir önceki parçanın sonundan belirli bir miktar karakteri veya belirteci “örtüşme” (overlap) olarak içerir. Bu örtüşme, parçalar arasındaki anlamsal geçişi yumuşatarak, bir bilginin bir parçanın sonunda başlayıp diğerinin başında devam etmesi durumunda bağlamın tamamen kaybolmasını engeller.
Avantajları:
- Bağlam Koruma: Parçalar arasındaki anlamsal sürekliliği artırır, böylece kritik bilgilerin bölünerek anlamsız hale gelme riskini azaltır.
- Daha İyi Geri Kazanım: Özellikle bir bilginin birden fazla parçaya yayıldığı durumlarda, örtüşme sayesinde her iki parçanın da ilgili sorgu için geri kazanılma olasılığı artar.
Dezavantajları:
- Veri Tekrarı: Örtüşme nedeniyle veritabanında daha fazla tekrar eden bilgi saklanır, bu da depolama alanını ve indeksleme süresini artırabilir.
- Hesaplama Maliyeti: Daha fazla parça oluşturulduğu için, embedding oluşturma ve vektör veritabanı sorguları biraz daha maliyetli olabilir.
Gerçek Dünya Senaryosu: Teknik dokümanlar, kılavuzlar veya uzun araştırma makaleleri gibi, belirli bir konunun farklı bölümlere yayılabileceği metinler için idealdir. Örneğin, bir şirket içi bilgi bankasında, bir prosedürün adımları farklı paragraflara dağılmışsa, örtüşmeli parçalama bu adımların bütünlüğünü korumaya yardımcı olur.
Python Uygulaması:
Yine CharacterTextSplitter kullanacağız, ancak bu sefer chunk_overlap parametresini sıfırdan büyük bir değere ayarlayacağız.
from langchain.text_splitter import CharacterTextSplitter
# Örnek uzun metin
uzun_metin_overlap = """
RAG (Retrieval Augmented Generation), büyük dil modellerinin (LLM) yeteneklerini artırmak için kullanılan güçlü bir mimaridir.
Bu yaklaşım, bir LLM'in belirli bir sorguya yanıt vermeden önce harici bir bilgi tabanından ilgili bilgileri geri kazanmasını sağlar.
Böylece model, sadece kendi eğitim verileriyle sınırlı kalmaz, aynı zamanda güncel ve özelleşmiş bilgilere de erişebilir.
RAG'ın temel amacı, LLM'lerin doğru, güncel ve bağlamla alakalı yanıtlar üretmesine yardımcı olmaktır.
Bu süreçte en kritik adımlardan biri, büyük belgeleri yönetilebilir parçalara ayırma işlemidir: parçalama (chunking).
Doğru parçalama stratejisi seçimi, RAG sisteminizin performansını doğrudan etkiler.
Çok küçük parçalar bağlamı bozabilirken, çok büyük parçalar alakasız bilgileri getirerek performansı düşürebilir.
Bu makalede farklı parçalama stratejilerini inceleyeceğiz.
"""
# Örtüşmeli sabit boyutlu parçalayıcıyı oluşturma
text_splitter_overlap = CharacterTextSplitter(
separator="\n\n",
chunk_size=200,
chunk_overlap=50, # 50 karakterlik örtüşme
length_function=len,
is_separator_regex=False,
)
chunks_overlap = text_splitter_overlap.create_documents([uzun_metin_overlap])
print(f"Toplam parça sayısı (örtüşmeli): {len(chunks_overlap)}")
for i, chunk in enumerate(chunks_overlap):
print(f"\n--- Parça {i+1} (Uzunluk: {len(chunk.page_content)}) ---")
print(chunk.page_content)
Bu örnekte 200 karakterlik parçalar oluşturduk ve her parçanın bir önceki parçayla 50 karakterlik bir örtüşmeye sahip olmasını sağladık. Bu, özellikle cümlelerin veya önemli ifadelerin parça sınırlarına denk gelmesi durumunda anlamsal bütünlüğün korunmasına yardımcı olacaktır.
3. Yapısal ve Özyinelemeli Karakter Bazlı Parçalama (Recursive Character Text Splitting): Belge Hiyerarşisini Koruma
Bu strateji, metinleri bölmek için önceden tanımlanmış bir dizi ayırıcı (separator) kullanır ve bu ayırıcıları hiyerarşik bir sırayla dener. Yani, metni önce en büyük ayırıcıya (örneğin, çift yeni satır \n\n ile paragraflar), sonra daha küçük ayırıcılara (tek yeni satır \n ile satırlar), sonra boşluklara ve son olarak karakterlere göre bölmeye çalışır. Eğer bir ayırıcı ile bölünen parça hala chunk_size değerinden büyükse, bir sonraki daha küçük ayırıcıya geçilir ve bu işlem parça boyutu uygun hale gelene kadar özyinelemeli olarak devam eder. Bu sayede, metnin doğal yapısı (paragraflar, başlıklar, cümleler) mümkün olduğunca korunur.
Avantajları:
- Anlamsal Bütünlük: Metnin doğal yapısını (paragraflar, cümleler) korumaya çalıştığı için anlamsal bütünlüğü sabit boyutlu parçalamaya göre çok daha iyi korur.
- Esneklik: Farklı türdeki metin belgeleri için (kod, markdown, düz metin) uygun ayırıcılar tanımlanabilir.
- Daha Az Bağlam Kaybı: Önemli bilgilerin cümle veya paragraf ortasında kesilme olasılığı daha düşüktür.
Dezavantajları:
- Karmaşıklık: Sabit boyutlu parçalamaya göre biraz daha karmaşıktır ve doğru ayırıcı dizisini seçmek deneme yanılma gerektirebilir.
- Performans: Bölme işlemi daha fazla mantık içerdiğinden, çok büyük metinler için biraz daha yavaş olabilir.
Gerçek Dünya Senaryosu: Markdown dosyaları, kod dosyaları, bilimsel makaleler veya yasal belgeler gibi belirgin yapısal öğelere (başlıklar, paragraflar, kod blokları) sahip metinler için mükemmeldir. Örneğin, bir yazılım projesinin dokümantasyonunu RAG sistemine dahil ederken, başlıkları ve kod bloklarını ayrı ayrı parçalar halinde tutmak, ilgili bilgiyi daha doğru geri kazanmayı sağlar.
Python Uygulaması:
LangChain’in RecursiveCharacterTextSplitter sınıfı bu stratejiyi uygular.
from langchain.text_splitter import RecursiveCharacterTextSplitter
# Örnek yapısal metin
yapısal_metin = """
# RAG Sistemlerinde Parçalama
## Giriş
Büyük Dil Modelleri (LLM) çağında, doğru ve güncel yanıtlar verebilmek için RAG mimarisi kritik öneme sahiptir.
RAG, harici bilgi tabanlarından veri çekerek LLM'in performansını artırır.
### Parçalamanın Önemi
Parçalama (chunking), RAG sistemlerinin temel taşıdır.
Doğru parçalama, geri kazanılan bilginin kalitesini doğrudan etkiler.
Yanlış parçalama bilgi kaybına veya alakasız veri getirmeye yol açabilir.
## Parçalama Stratejileri
Farklı belge türleri için farklı parçalama stratejileri uygulanmalıdır.
Örneğin, kod dosyaları için satır bazında, markdown için başlık bazında bölme daha mantıklıdır.
### RecursiveCharacterTextSplitter
Bu splitter, metni bölmek için bir dizi ayırıcı kullanır.
İlk olarak en büyük ayırıcı denenir, eğer parça hala büyükse bir sonraki ayırıcıya geçilir.
Bu sayede metnin doğal yapısı korunur.
"""
# Özyinelemeli karakter bazlı parçalayıcıyı oluşturma
# Ayırıcılar: önce çift yeni satır, sonra tek yeni satır, sonra boşluk, en son karakter
text_splitter_recursive = RecursiveCharacterTextSplitter(
chunk_size=150,
chunk_overlap=20,
separators=["\n\n", "\n", " ", ""], # Hiyerarşik ayırıcılar
length_function=len,
)
chunks_recursive = text_splitter_recursive.create_documents([yapısal_metin])
print(f"Toplam parça sayısı (özyinelemeli): {len(chunks_recursive)}")
for i, chunk in enumerate(chunks_recursive):
print(f"\n--- Parça {i+1} (Uzunluk: {len(chunk.page_content)}) ---")
print(chunk.page_content)
Bu örnekte, RecursiveCharacterTextSplitter‘ı farklı ayırıcılarla kullandık. Bu sayede, başlıklar ve paragraflar gibi yapısal öğeler mümkün olduğunca korunarak parçalar oluşturuldu.
4. Anlamsal Parçalama (Semantic Chunking): Anlam Bütünlüğünü Odak Noktasına Alma
Geleneksel parçalama yöntemleri genellikle karakter veya belirteç sayılarına ya da yapısal ayırıcılara odaklanırken, anlamsal parçalama metnin “anlam”ına odaklanır. Bu strateji, metni anlamsal olarak benzer cümle veya paragraf gruplarına ayırır. Temel fikir, aynı konu etrafında dönen cümleleri bir arada tutmak ve farklı konulara geçiş noktalarında bölmektir. Bu genellikle metin parçalarının embedding’leri (vektör gösterimleri) arasındaki benzerlik veya uzaklık ölçülerek yapılır. Eğer iki komşu cümlenin embedding’leri arasındaki benzerlik belirli bir eşiğin altına düşerse, bu bir bölme noktası olarak kabul edilir.
Avantajları:
- Yüksek Anlamsal Bütünlük: En yüksek anlamsal bütünlüğü koruyan parçaları üretir, çünkü aynı konuyu işleyen cümleleri bir arada tutar.
- Daha Doğru Geri Kazanım: Bir sorguyla anlamsal olarak en alakalı parçaların geri kazanılma olasılığı artar, çünkü parçalar konu bazında daha homojendir.
- Daha Az Gürültü: Alakasız cümlelerin aynı parça içinde olma olasılığı azalır.
Dezavantajları:
- Hesaplama Yoğunluğu: Her cümlenin veya küçük metin bloğunun embedding’ini oluşturmayı gerektirir, bu da özellikle büyük belgeler için zaman alıcı ve maliyetli olabilir.
- Model Bağımlılığı: Embedding modelinin kalitesine doğrudan bağlıdır. Zayıf bir embedding modeli, yanlış anlamsal ayrımlara yol açabilir.
- Karmaşıklık: Diğer yöntemlere göre daha karmaşık bir uygulama gerektirir.
Gerçek Dünya Senaryosu: Hukuki metinler, tıbbi raporlar, araştırma özetleri veya müşteri geri bildirimleri gibi, farklı konuların veya argümanların iç içe geçtiği ancak her bir konunun kendi içinde tutarlılık gösterdiği metinler için çok etkilidir. Örneğin, bir hukuki belgede farklı maddelerin yorumları aynı paragrafta geçse bile, anlamsal parçalama bu yorumları kendi bağlamlarında tutmaya çalışır.
Python Uygulaması:
LangChain’de doğrudan bir “SemanticChunker” bulunmasa da, bu konsepti uygulamak için cümle embedding’lerini ve benzerlik ölçümlerini kullanabiliriz. Aşağıdaki örnekte, cümleleri ayırıp her birinin embedding’ini oluşturarak anlamsal geçiş noktalarını tespit eden basit bir mekanizma gösterilmiştir. Gerçek bir uygulamada daha sofistike algoritmalar kullanılabilir. Bu örnek için sentence_transformers kütüphanesine ihtiyacınız olacak.
# pip install sentence-transformers
from sentence_transformers import SentenceTransformer
from sklearn.metrics.pairwise import cosine_similarity
import numpy as np
import re
# Örnek metin
anlamsal_metin = """
RAG sistemleri, LLM'lerin yeteneklerini harici bilgilerle zenginleştirir.
Bu sayede LLM'ler daha doğru ve güncel yanıtlar üretebilir.
Parçalama, RAG'ın kritik bir adımıdır.
Doğru parçalama stratejisi seçimi, sistemin performansını doğrudan etkiler.
Anlamsal parçalama, metnin anlam bütünlüğünü korumaya odaklanır.
Her cümlenin embedding'i oluşturulur ve benzerlik skorları hesaplanır.
Eğer iki cümle arasındaki benzerlik düşükse, bu bir bölme noktası olarak kabul edilebilir.
Bu yöntem, daha yüksek kaliteli geri kazanım sağlar.
Ancak hesaplama maliyeti diğer yöntemlere göre daha yüksektir.
Yine de, anlamsal bütünlük çoğu zaman bu maliyete değer.
"""
# Cümleleri ayırma fonksiyonu (basit bir regex ile)
def split_sentences(text):
sentences = re.split(r'(?<=[.!?])\s+', text)
return [s.strip() for s in sentences if s.strip()]
# Embedding modeli yükleme
model = SentenceTransformer('paraphrase-multilingual-MiniLM-L12-v2') # Türkçe destekli bir model
sentences = split_sentences(anlamsal_metin)
print(f"Ayrılan cümle sayısı: {len(sentences)}")
# Cümle embedding'lerini oluşturma
sentence_embeddings = model.encode(sentences)
# Benzerlik skorlarını hesaplama (komşu cümleler arasında)
similarity_scores = []
for i in range(len(sentence_embeddings) - 1):
score = cosine_similarity(
[sentence_embeddings[i]],
[sentence_embeddings[i+1]]
)[0][0]
similarity_scores.append(score)
# Bölme noktalarını belirleme (düşük benzerlik skorları)
# Eşik değeri deneme yanılma ile belirlenebilir
threshold = 0.5
chunk_boundaries = [0]
for i, score in enumerate(similarity_scores):
if score < threshold:
chunk_boundaries.append(i + 1)
chunk_boundaries.append(len(sentences))
# Parçaları oluşturma
anlamsal_chunks = []
for i in range(len(chunk_boundaries) - 1):
start = chunk_boundaries[i]
end = chunk_boundaries[i+1]
chunk_content = " ".join(sentences[start:end])
anlamsal_chunks.append(chunk_content)
print(f"\nToplam anlamsal parça sayısı: {len(anlamsal_chunks)}")
for i, chunk in enumerate(anlamsal_chunks):
print(f"\n--- Anlamsal Parça {i+1} ---")
print(chunk)
Bu kod, her cümleyi ayrı ayrı embedding'lere dönüştürür ve komşu cümleler arasındaki anlamsal benzerliği hesaplar. Belirlenen bir eşiğin altındaki benzerlik skorları, bir bölme noktası olarak kabul edilir. Bu sayede, anlamsal olarak birbiriyle ilişkili cümleler aynı parça içinde kalır.
5. Belge Odaklı ve Özel Parçalama (Document-Aware and Custom Chunking): Belge Yapısına Saygı
Bu strateji, belirli belge türlerinin kendine özgü yapılarını dikkate alarak parçalama yapar. Örneğin, bir PDF belgesini sayfa sayfa bölmek, bir Markdown belgesini başlıklarına göre bölmek veya bir kod dosyasını fonksiyonlarına veya sınıflarına göre bölmek bu kategoriye girer. Bu yaklaşım, belgenin doğal hiyerarşisini ve mantıksal bölümlerini koruyarak, geri kazanım sırasında çok daha alakalı ve bağlam açısından zengin parçalar elde etmeyi sağlar. Bu tür parçalayıcılar genellikle belirli dosya formatlarını veya yapısal işaretleyicileri (HTML etiketleri, Markdown başlıkları, kod yorumları) anlar ve bunlara göre bölme yapar.
Avantajları:
- Yüksek Doğruluk: Belgenin doğal yapısını ve içeriğin mantıksal akışını koruduğu için en doğru ve bağlam açısından zengin parçaları üretir.
- Hedeflenmiş Geri Kazanım: Bir sorgu, belgenin belirli bir bölümüyle (örneğin, bir başlık altındaki tüm bilgiler) eşleştiğinde, o bölümün tamamının geri kazanılması sağlanır.
- Minimum Bağlam Kaybı: Bilgi, belgenin doğal sınırları içinde kaldığı için bağlam kaybı riski çok düşüktür.
Dezavantajları:
- Uygulama Karmaşıklığı: Her belge türü için özel parçalama mantığı geliştirmeyi gerektirebilir, bu da zaman alıcı olabilir.
- Esneklik Sınırlaması: Genel bir çözümden ziyade, belirli bir belge formatına veya yapısına sıkıca bağlıdır.
- Ön İşleme İhtiyacı: Belgeyi önce belirli bir formata (örn. Markdown) dönüştürmek veya ayrıştırmak (parse etmek) gerekebilir.
Gerçek Dünya Senaryosu: Bir şirket içi bilgi tabanı, yasal sözleşmeler, kullanım kılavuzları, API dokümantasyonları veya teknik şartnameler gibi iyi yapılandırılmış belgeler için vazgeçilmezdir. Örneğin, bir API dokümanındaki her bir endpoint'in açıklamasını ayrı bir parça olarak tutmak, geliştiricilerin spesifik bir endpoint hakkında bilgi aradığında doğrudan ilgili bölüme ulaşmasını sağlar.
Python Uygulaması:
LangChain, belirli belge türleri için özel parçalayıcılar sunar. Örneğin, Markdown belgeleri için MarkdownHeaderTextSplitter kullanılabilir.
from langchain.text_splitter import MarkdownHeaderTextSplitter
# Örnek Markdown metni
markdown_metin = """
# Giriş
Bu belge, RAG sistemlerinde kullanılan parçalama stratejilerini açıklar.
## Genel Bakış
RAG, LLM'lere harici bilgi sağlar. Bu sayede LLM'ler güncel ve doğru yanıtlar üretebilir.
### Neden Parçalama?
LLM'lerin bağlam penceresi sınırlıdır. Bu nedenle belgelerin küçük parçalara ayrılması gerekir.
Parçalama, bilgi geri kazanımının etkinliğini artırır.
## Parçalama Yöntemleri
Çeşitli parçalama yöntemleri mevcuttur. Her birinin kendine özgü avantajları ve dezavantajları vardır.
### Sabit Boyutlu Parçalama
En basit yöntemdir. Belgeyi belirli bir karakter sayısına göre böler.
### Örtüşmeli Parçalama
Bağlam kaybını azaltmak için parçalar arasında örtüşme kullanılır.
### Anlamsal Parçalama
Metnin anlamsal bütünlüğüne odaklanır.
"""
# Markdown başlıklarına göre parçalayıcıyı oluşturma
# headers_to_split_on: Hangi başlık seviyelerine göre bölüneceğini belirtir
headers_to_split_on = [
("#", "Başlık 1"),
("##", "Başlık 2"),
("###", "Başlık 3"),
]
markdown_splitter = MarkdownHeaderTextSplitter(
headers_to_split_on=headers_to_split_on,
strip_headers=False # Başlıkları parçaların içinde tut
)
md_chunks = markdown_splitter.split_text(markdown_metin)
print(f"Toplam Markdown odaklı parça sayısı: {len(md_chunks)}")
for i, chunk in enumerate(md_chunks):
print(f"\n--- Markdown Parça {i+1} ---")
print(f"Metadata: {chunk.metadata}")
print(chunk.page_content)
Bu örnekte, Markdown belgesini başlık seviyelerine göre böldük. Her bir parça, ait olduğu başlık seviyelerini metadata olarak da içerir. Bu, daha sonra geri kazanım sırasında belirli bir başlık altındaki bilgileri filtrelemek veya önceliklendirmek için kullanılabilir.
Gelişmiş İpuçları ve En İyi Uygulamalar: Parçalama Sanatında Ustalaşmak
Parçalama, RAG sistemlerinin kalbinde yer alan ve optimize edilmesi gereken bir süreçtir. Yukarıda bahsedilen temel stratejilerin ötesine geçerek, RAG sistemlerinizin performansını daha da artırabilecek bazı gelişmiş ipuçları ve en iyi uygulamalar mevcuttur. Bu yaklaşımlar, özellikle karmaşık veri setleri ve yüksek performans beklentileri olan uygulamalar için kritik öneme sahiptir.
Dinamik Parçalama (Dynamic Chunking)
Tek bir sabit parça boyutu veya tek bir parçalama stratejisi, tüm belge türleri veya tüm sorgu tipleri için en iyi sonucu vermeyebilir. Dinamik parçalama, belgenin içeriğine, sorgunun karmaşıklığına veya hatta kullanıcının tercihlerine göre parçalama stratejisini veya parça boyutunu çalışma zamanında ayarlama yeteneğidir. Örneğin:
- Sorgu Odaklı Parçalama: Gelen sorgunun uzunluğuna veya anahtar kelimelerine göre daha küçük veya daha büyük parçalar oluşturulabilir. Kısa ve spesifik sorgular için daha küçük parçalar yeterli olabilirken, geniş kapsamlı sorgular için daha fazla bağlam sağlayan büyük parçalar gerekebilir.
- Belge İçeriği Odaklı Parçalama: Bir belgenin farklı bölümleri farklı parçalama mantığı gerektirebilir. Örneğin, bir teknik dokümanın giriş ve sonuç bölümleri daha genel parçalama ile işlenebilirken, detaylı teknik özelliklerin bulunduğu bölümler anlamsal veya yapısal parçalama ile daha hassas bir şekilde ayrılabilir.
Dinamik parçalama, genellikle daha karmaşık bir mühendislik gerektirir ancak RAG sisteminin adaptasyon yeteneğini ve genel performansını önemli ölçüde artırabilir.
Parça Boyutu ve Örtüşme Optimizasyonu
Doğru chunk_size (parça boyutu) ve chunk_overlap (parça örtüşmesi) değerlerini bulmak, deneme yanılma ve metrik tabanlı değerlendirme gerektirir.
- Deneme Yanılma: Farklı parça boyutları ve örtüşme değerleriyle denemeler yaparak, RAG sisteminizin geri kazanım doğruluğunu ve yanıt kalitesini test edin.
- Değerlendirme Metrikleri: Geri kazanım (recall), hassasiyet (precision), F1 skoru gibi metrikleri kullanarak farklı parçalama yapılandırmalarının performansını ölçün. Ayrıca, LLM tarafından üretilen yanıtların kalitesini (doğruluk, alaka düzeyi, akıcılık) insan değerlendirmesi veya otomatik değerlendirme araçlarıyla ölçmek de önemlidir.
- LLM Bağlam Penceresi: Seçtiğiniz LLM'in bağlam penceresini göz önünde bulundurun. Parçalarınızın toplam boyutu (geri kazanılan parça sayısı * ortalama parça boyutu) bu pencereyi aşmamalıdır. Genellikle, bağlam penceresinin %70-80'ini dolduracak şekilde parçalar seçmek iyi bir başlangıç noktası olabilir.
Zengin Metadata Kullanımı
Parçalama yaparken, her bir parçaya orijinal belgeden veya parçanın kendisinden türetilen ilgili metadata (üst veri) eklemek, geri kazanım sürecini büyük ölçüde iyileştirebilir. Metadata örnekleri:
- Kaynak Bilgisi: Orijinal belge adı, URL'si, yazarı, yayın tarihi.
- Konum Bilgisi: Belge içindeki sayfa numarası, bölüm başlığı, paragraf numarası.
- Belge Tipi: PDF, Markdown, HTML, kod.
- Özet Bilgisi: Parçanın kısa bir özeti veya anahtar kelimeleri.
Metadata, vektör aramasından sonra parçaları filtrelemek veya sıralamak için kullanılabilir. Örneğin, kullanıcı yalnızca "2023 yılındaki raporlardan" bilgi istiyorsa, metadata filtresi ile sadece o yıla ait belgelerden gelen parçalar LLM'e sunulabilir. Bu, alakasız bilgilerin LLM'e gitmesini engeller ve geri kazanım doğruluğunu artırır.
from langchain.text_splitter import CharacterTextSplitter
from langchain.docstore.document import Document
# Örnek metin ve metadata
metin_ile_metadata = "Bu, 2023 yılında yayınlanmış önemli bir şirket raporunun bir bölümüdür. Finansal performansımız beklentilerin üzerinde gerçekleşti."
metadata_ornegi = {"kaynak": "Şirket Raporu 2023", "yıl": 2023, "yazar": "AI Departmanı"}
text_splitter = CharacterTextSplitter(
separator=" ",
chunk_size=50,
chunk_overlap=0,
length_function=len
)
# Metni parçalarken metadata'yı ekleme
# LangChain'in create_documents metodu, Document nesneleri bekler
# Her parçaya orijinal belgenin metadata'sı otomatik olarak kopyalanır.
doc = Document(page_content=metin_ile_metadata, metadata=metadata_ornegi)
chunks_with_metadata = text_splitter.create_documents([doc])
print(f"Toplam parça sayısı (metadata ile): {len(chunks_with_metadata)}")
for i, chunk in enumerate(chunks_with_metadata):
print(f"\n--- Parça {i+1} ---")
print(f"İçerik: {chunk.page_content}")
print(f"Metadata: {chunk.metadata}")
Bu örnekte, orijinal belgeye ait metadata'nın parçalara nasıl eklendiğini görebilirsiniz. Bu metadata, daha sonra geri kazanım aşamasında filtreleme ve sıralama için kullanılabilir.
Parçalama, RAG sistemlerinin başarısı için temel bir adımdır ve tek bir "herkese uyan tek beden" çözümü yoktur. En iyi yaklaşım, veri setinizin özelliklerini, uygulamanızın gereksinimlerini ve kullandığınız LLM'in yeteneklerini dikkatlice değerlendirerek farklı stratejileri denemek ve optimize etmektir. Sürekli test ve iyileştirme, RAG sisteminizin zaman içinde daha akıllı ve daha doğru yanıtlar üretmesini sağlayacaktır.
Sonuç ve Sıkça Sorulan Sorular
RAG (Geri Kazanım Artırılmış Üretim) sistemleri, Büyük Dil Modellerinin (LLM) bilgi eksikliklerini gidererek ve güncel verilerle zenginleştirerek daha doğru ve bağlam odaklı yanıtlar üretmesini sağlayan devrim niteliğinde bir yaklaşımdır. Bu mimarinin temelinde yatan ve performansı doğrudan etkileyen en kritik adımlardan biri ise "parçalama (chunking)" işlemidir. Büyük metin belgelerini anlamlı, yönetilebilir ve LLM'in bağlam penceresine sığabilecek küçük parçalara ayırmak, geri kazanım kalitesini ve dolayısıyla nihai yanıtın doğruluğunu belirler.
Bu makalede, sabit boyutlu parçalamadan anlamsal ve belge odaklı yaklaşımlara kadar 5 farklı parçalama stratejisini detaylıca inceledik. Her bir stratejinin kendine özgü avantajları ve dezavantajları olduğunu, ve "en iyi" parçalama yönteminin veri setinizin yapısına, kullanım senaryonuza ve performans hedeflerinize göre değişebileceğini gördük. Python ve LangChain kütüphanesi ile sunulan kod örnekleri, bu stratejilerin pratikte nasıl uygulanabileceğine dair somut birer rehber niteliğindeydi.
Unutulmamalıdır ki, parçalama bir sanattır ve sürekli optimizasyon gerektirir. Dinamik parçalama, parça boyutu ve örtüşme oranlarının titizlikle ayarlanması ve zengin metadata kullanımı gibi ileri düzey teknikler, RAG sistemlerinizin yeteneklerini daha da ileriye taşıyabilir. Amacımız her zaman, LLM'e sunulacak en alakalı ve bağlam açısından zengin bilgiyi sağlamak olmalıdır. Bu sayede, kullanıcılarınıza daha güvenilir, doğru ve değerli yapay zeka deneyimleri sunabilirsiniz.
Sıkça Sorulan Sorular (SSS)
- 1. RAG sistemlerinde parçalama neden bu kadar önemlidir?
- Parçalama, büyük metin belgelerini LLM'lerin sınırlı bağlam pencerelerine sığabilecek küçük, anlamlı birimlere ayırır. Doğru parçalama, LLM'e sunulan bilginin alaka düzeyini ve doğruluğunu artırarak, modelin daha kaliteli ve doğru yanıtlar üretmesini sağlar. Yanlış parçalama ise bilgi kaybına, alakasız veri getirmeye veya bağlam penceresi aşımına yol açabilir.
- 2. Hangi parçalama stratejisi benim projem için en iyisidir?
- Tek bir "en iyi" strateji yoktur. Seçim, veri setinizin türüne, belgenin yapısına (düz metin, Markdown, kod vb.), sorguların karmaşıklığına ve LLM'inizin bağlam penceresi boyutuna bağlıdır. Yapısal belgelere sahipseniz (örn. Markdown),
RecursiveCharacterTextSplitterveya belge odaklı parçalama daha iyi olabilir. Anlamsal bütünlük kritikse, anlamsal parçalama denenebilir. Hızlı bir başlangıç için sabit boyutlu parçalama iyi bir seçenektir. - 3.
chunk_sizevechunk_overlapdeğerlerini nasıl belirlemeliyim? chunk_size, bir parçanın maksimum boyutunu belirler ve LLM'inizin bağlam penceresine uygun olmalıdır.chunk_overlap, parçalar arasındaki bağlam sürekliliğini sağlamak için kullanılır. Bu değerler genellikle deneme yanılma yoluyla ve RAG sisteminizin geri kazanım metrikleri (doğruluk, geri çağırma) ve LLM yanıt kalitesi değerlendirilerek optimize edilir. Genelliklechunk_overlapdeğeri,chunk_size'ın %10-20'si kadar olabilir.- 4. Metadata parçalama sürecinde nasıl bir rol oynar?
- Metadata (üst veri), her parçaya orijinal belge hakkında ek bağlam bilgisi (kaynak, yazar, tarih, sayfa numarası vb.) eklemenizi sağlar. Bu bilgiler, vektör veritabanından geri kazanılan parçaları daha sonra filtrelemek, sıralamak veya LLM'e sunmadan önce ek bağlam sağlamak için kullanılabilir. Örneğin, "sadece son bir yıldaki raporlardan bilgi getir" gibi sorgularda metadata filtrelemesi çok etkilidir.
- 5. Parçalama işlemi sadece metin belgeleri için mi geçerlidir?
- Hayır, parçalama konsepti sadece metin belgeleriyle sınırlı değildir. Görsel veriler (görüntülerin bölgelere ayrılması), ses verileri (ses kayıtlarının belirli zaman dilimlerine bölünmesi) veya yapılandırılmış veriler (tabloların satır veya sütunlara ayrılması) gibi farklı veri türleri için de benzer parçalama yaklaşımları uygulanabilir. Önemli olan, her veri türü için anlamlı ve geri kazanılabilir parçalar oluşturmaktır.
#RAG #Chunking #Python #LLM #YapayZeka #BilgiErişimi #Veriİşleme