P-10 RAG Embedding dan Vector Database

This commit is contained in:
Raphael
2026-11-02 10:33:58 +07:00
commit 99fe6f0d76
19 changed files with 443 additions and 0 deletions
+122
View File
@@ -0,0 +1,122 @@
import chromadb
from chromadb.utils import embedding_functions
# ==========================================
# PENGATURAN
# ==========================================
FOLDER_DATABASE = "./chroma_db"
MODEL_EMBEDDING = "paraphrase-multilingual-MiniLM-L12-v2"
# ==========================================
# MEMBUAT KONEKSI
# ==========================================
def hubungkan_database(ukuran):
client = chromadb.PersistentClient(
path=FOLDER_DATABASE
)
embedding = (
embedding_functions.SentenceTransformerEmbeddingFunction(
model_name=MODEL_EMBEDDING
)
)
nama_koleksi = f"dokumen_chunk_{ukuran}"
koleksi = client.get_collection(
name=nama_koleksi,
embedding_function=embedding
)
return koleksi
# ==========================================
# MELAKUKAN PENCARIAN
# ==========================================
def cari_dokumen():
print("\n===== SISTEM PENCARIAN DOKUMEN RAG =====")
print("Ukuran chunk tersedia: 500, 1000, 1500")
try:
ukuran = int(input("Pilih ukuran chunk: "))
if ukuran not in [500, 1000, 1500]:
print("Ukuran chunk tidak tersedia.")
return
koleksi = hubungkan_database(ukuran)
jumlah_data = koleksi.count()
if jumlah_data == 0:
print("Indeks masih kosong. Jalankan indeks.py dahulu.")
return
print(f"\nJumlah chunk dalam indeks: {jumlah_data}")
while True:
pertanyaan = input(
"\nMasukkan pertanyaan (ketik 'keluar' untuk berhenti): "
).strip()
if pertanyaan.lower() == "keluar":
break
if not pertanyaan:
print("Pertanyaan tidak boleh kosong.")
continue
jumlah_hasil = min(3, jumlah_data)
hasil = koleksi.query(
query_texts=[pertanyaan],
n_results=jumlah_hasil
)
print("\n===== HASIL PENCARIAN =====")
for nomor, (dokumen, metadata, jarak) in enumerate(
zip(
hasil["documents"][0],
hasil["metadatas"][0],
hasil["distances"][0]
),
start=1
):
print(f"\nHasil ke-{nomor}")
print(f"Sumber : {metadata['sumber']}")
print(f"Halaman: {metadata['halaman']}")
print(f"Chunk : {metadata['chunk']}")
print(f"Jarak : {jarak:.4f}")
print("Isi dokumen:")
print(dokumen)
print("--------------------------------")
print(
"Catatan: nilai jarak lebih kecil "
"berarti hasil lebih dekat."
)
except ValueError:
print("Masukkan angka ukuran chunk yang benar.")
except Exception as error:
print("Terjadi kesalahan:", error)
print("Pastikan indeks sudah dibuat dengan indeks.py.")
# ==========================================
# PROGRAM UTAMA
# ==========================================
if __name__ == "__main__":
cari_dokumen()
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+147
View File
@@ -0,0 +1,147 @@
import chromadb
from chromadb.utils import embedding_functions
# ==========================================
# PENGATURAN
# ==========================================
FOLDER_DATABASE = "./chroma_db"
MODEL_EMBEDDING = "paraphrase-multilingual-MiniLM-L12-v2"
# ==========================================
# 15 PERTANYAAN PENGUJIAN
# GANTI PERTANYAAN DAN KATA KUNCI
# SESUAI ISI PDF YANG KAMU GUNAKAN.
# ==========================================
UJI = [
("Pertanyaan 1 dari dokumen", "kata kunci jawaban 1"),
("Pertanyaan 2 dari dokumen", "kata kunci jawaban 2"),
("Pertanyaan 3 dari dokumen", "kata kunci jawaban 3"),
("Pertanyaan 4 dari dokumen", "kata kunci jawaban 4"),
("Pertanyaan 5 dari dokumen", "kata kunci jawaban 5"),
("Pertanyaan 6 dari dokumen", "kata kunci jawaban 6"),
("Pertanyaan 7 dari dokumen", "kata kunci jawaban 7"),
("Pertanyaan 8 dari dokumen", "kata kunci jawaban 8"),
("Pertanyaan 9 dari dokumen", "kata kunci jawaban 9"),
("Pertanyaan 10 dari dokumen", "kata kunci jawaban 10"),
("Pertanyaan 11 dari dokumen", "kata kunci jawaban 11"),
("Pertanyaan 12 dari dokumen", "kata kunci jawaban 12"),
("Pertanyaan 13 dari dokumen", "kata kunci jawaban 13"),
("Pertanyaan 14 dari dokumen", "kata kunci jawaban 14"),
("Pertanyaan 15 dari dokumen", "kata kunci jawaban 15"),
]
# ==========================================
# MENGHUBUNGKAN KE CHROMADB
# ==========================================
def hubungkan_database(ukuran):
client = chromadb.PersistentClient(
path=FOLDER_DATABASE
)
embedding = (
embedding_functions.SentenceTransformerEmbeddingFunction(
model_name=MODEL_EMBEDDING
)
)
nama_koleksi = f"dokumen_chunk_{ukuran}"
koleksi = client.get_collection(
name=nama_koleksi,
embedding_function=embedding
)
return koleksi
# ==========================================
# MENGHITUNG HIT@K DAN MRR
# ==========================================
def evaluasi(koleksi, k):
benar = 0
total_reciprocal_rank = 0
jumlah_data = koleksi.count()
if jumlah_data == 0:
return 0, 0
jumlah_hasil = min(k, jumlah_data)
for pertanyaan, kata_kunci in UJI:
hasil = koleksi.query(
query_texts=[pertanyaan],
n_results=jumlah_hasil
)
daftar_dokumen = hasil["documents"][0]
peringkat_benar = None
for nomor, dokumen in enumerate(daftar_dokumen, start=1):
if kata_kunci.lower() in dokumen.lower():
peringkat_benar = nomor
break
if peringkat_benar is not None:
benar += 1
total_reciprocal_rank += 1 / peringkat_benar
hit = benar / len(UJI)
mrr = total_reciprocal_rank / len(UJI)
return hit, mrr
# ==========================================
# PROGRAM UTAMA EVALUASI
# ==========================================
def jalankan_evaluasi():
print("\n===== EVALUASI RETRIEVAL RAG =====")
print("Jumlah pertanyaan:", len(UJI))
print("\nHasil perbandingan ukuran chunk:")
print("-----------------------------------------------")
print("Ukuran | Hit@1 | Hit@3 | Hit@5 | MRR")
print("-----------------------------------------------")
for ukuran in [500, 1000, 1500]:
try:
koleksi = hubungkan_database(ukuran)
hasil_1, mrr_1 = evaluasi(koleksi, 1)
hasil_3, mrr_3 = evaluasi(koleksi, 3)
hasil_5, mrr_5 = evaluasi(koleksi, 5)
# MRR dihitung menggunakan hasil top-5.
print(
f"{ukuran:6} | "
f"{hasil_1:.2f} | "
f"{hasil_3:.2f} | "
f"{hasil_5:.2f} | "
f"{mrr_5:.2f}"
)
except Exception as error:
print(f"Chunk {ukuran}: gagal dievaluasi.")
print("Keterangan:", error)
print("-----------------------------------------------")
print("Evaluasi selesai.")
if __name__ == "__main__":
jalankan_evaluasi()
+174
View File
@@ -0,0 +1,174 @@
from pathlib import Path
from pypdf import PdfReader
import chromadb
from chromadb.utils import embedding_functions
# ==========================================
# PENGATURAN
# ==========================================
FOLDER_DOKUMEN = Path("dokumen")
FOLDER_DATABASE = "./chroma_db"
UKURAN_CHUNK = [500, 1000, 1500]
MODEL_EMBEDDING = "paraphrase-multilingual-MiniLM-L12-v2"
# ==========================================
# MEMBACA FILE PDF
# ==========================================
def baca_pdf(path):
reader = PdfReader(path)
daftar_halaman = []
for nomor, halaman in enumerate(reader.pages, start=1):
teks = halaman.extract_text() or ""
if teks.strip():
daftar_halaman.append({
"teks": teks,
"halaman": nomor
})
return daftar_halaman
# ==========================================
# MEMOTONG TEKS MENJADI CHUNK
# ==========================================
def potong(teks, ukuran=500, tumpang=50):
hasil = []
mulai = 0
while mulai < len(teks):
bagian = teks[mulai:mulai + ukuran]
if bagian.strip():
hasil.append(bagian)
mulai += ukuran - tumpang
return hasil
# ==========================================
# MEMBUAT INDEKS CHROMADB
# ==========================================
def buat_indeks():
if not FOLDER_DOKUMEN.exists():
print("Folder dokumen tidak ditemukan.")
return
daftar_pdf = list(FOLDER_DOKUMEN.glob("*.pdf"))
if not daftar_pdf:
print("Belum ada file PDF di folder dokumen.")
return
print("Menghubungkan ke ChromaDB...")
client = chromadb.PersistentClient(
path=FOLDER_DATABASE
)
embedding = (
embedding_functions.SentenceTransformerEmbeddingFunction(
model_name=MODEL_EMBEDDING
)
)
print("Model embedding siap digunakan.")
# Membuat koleksi untuk setiap ukuran chunk.
for ukuran in UKURAN_CHUNK:
nama_koleksi = f"dokumen_chunk_{ukuran}"
koleksi = client.get_or_create_collection(
name=nama_koleksi,
embedding_function=embedding,
metadata={"hnsw:space": "cosine"}
)
print("\n================================")
print(f"MEMBUAT INDEKS CHUNK {ukuran}")
print("================================")
total_chunk = 0
for pdf in daftar_pdf:
halaman_pdf = baca_pdf(pdf)
ids = []
documents = []
metadatas = []
for data in halaman_pdf:
teks = data["teks"]
overlap = int(ukuran * 0.10)
chunks = potong(
teks,
ukuran=ukuran,
tumpang=overlap
)
for nomor, chunk in enumerate(chunks):
id_chunk = (
f"{pdf.stem}_halaman_"
f"{data['halaman']}_chunk_"
f"{nomor}_ukuran_{ukuran}"
)
ids.append(id_chunk)
documents.append(chunk)
metadatas.append({
"sumber": pdf.name,
"halaman": data["halaman"],
"chunk": nomor,
"ukuran_chunk": ukuran
})
if ids:
# Simpan dalam kelompok agar lebih aman
# untuk dokumen dengan banyak chunk.
for awal in range(0, len(ids), 100):
akhir = awal + 100
koleksi.upsert(
ids=ids[awal:akhir],
documents=documents[awal:akhir],
metadatas=metadatas[awal:akhir]
)
print(
f"{pdf.name}: {len(ids)} chunk "
f"untuk ukuran {ukuran}"
)
total_chunk += len(ids)
else:
print(
f"{pdf.name}: tidak ada teks yang terbaca."
)
print(f"Total chunk: {total_chunk}")
print("\nSemua indeks berhasil dibuat!")
print(f"Lokasi database: {FOLDER_DATABASE}")
# ==========================================
# PROGRAM UTAMA
# ==========================================
if __name__ == "__main__":
buat_indeks()