P-10 RAG Embedding dan Vector Database
This commit is contained in:
@@ -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.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -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()
|
||||||
@@ -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()
|
||||||
Reference in New Issue
Block a user