This commit is contained in:
Cliff Julyano S
2026-11-02 11:24:52 +07:00
parent 5cfa138cf4
commit db453afcf9
23 changed files with 46 additions and 160 deletions
@@ -0,0 +1 @@
invalid type: string "vention\n \nis\n \nnot\n \nused\n \nin\n \nevery\n \ncase\n \nbecause\n \nsometimes\n \nthe\n \nargument\n \nname\n \nand\n \nthe\n \nvariable\n \nname\n \nare\n \ndifferent.\n \nFor\n \nexample:\n \ntext\n \ncontext:\n \nshortUrlBase\n \nHere,\n \n`context`\n \nis\n \nthe\n \nargument\n \nname\n \nand\n \n`shortUrlBase`\n \nis\n \nthe\n \nvariable\n \nbeing\n \npassed\n \nto
Binary file not shown.
Binary file not shown.
+45 -160
View File
@@ -1,224 +1,109 @@
from pathlib import Path from pathlib import Path
from pypdf import PdfReader from pypdf import PdfReader
import chromadb import chromadb
from chromadb.utils import embedding_functions from chromadb.utils import embedding_functions
# ========================================== # ==========================================
# 1. MEMBACA DOKUMEN PDF # FUNGSI BANTU
# ========================================== # ==========================================
def baca_pdf(path): def baca_pdf(path):
reader = PdfReader(path) reader = PdfReader(path)
return "\n".join((p.extract_text() or "") for p in reader.pages) return "\n".join((p.extract_text() or "") for p in reader.pages)
# ==========================================
# 2. MEMBAGI DOKUMEN MENJADI CHUNK
# ==========================================
def potong(teks, ukuran=800, tumpang=100): def potong(teks, ukuran=800, tumpang=100):
hasil = [] hasil, mulai = [], 0
mulai = 0
while mulai < len(teks): while mulai < len(teks):
hasil.append(teks[mulai:mulai + ukuran]) hasil.append(teks[mulai:mulai + ukuran])
mulai += ukuran - tumpang mulai += ukuran - tumpang
return [h for h in hasil if h.strip()] return [h for h in hasil if h.strip()]
# ========================================== # ==========================================
# 3. PENGATURAN EMBEDDING DAN DATABASE # SETUP EMBEDDING & CLIENT
# ========================================== # ==========================================
emb = embedding_functions.SentenceTransformerEmbeddingFunction( emb = embedding_functions.SentenceTransformerEmbeddingFunction(
model_name="paraphrase-multilingual-MiniLM-L12-v2" model_name="paraphrase-multilingual-MiniLM-L12-v2")
)
client = chromadb.PersistentClient(path="./chroma_db") client = chromadb.PersistentClient(path="./chroma_db")
# ==========================================
# 4. MEMBUAT KOLEKSI
# ==========================================
def buat_koleksi(nama, ukuran, tumpang): def buat_koleksi(nama, ukuran, tumpang):
try: try:
client.delete_collection(nama) client.delete_collection(nama)
except Exception: except Exception:
pass pass
kol = client.get_or_create_collection( kol = client.get_or_create_collection(
name=nama, name=nama, embedding_function=emb,
embedding_function=emb, metadata={"hnsw:space": "cosine"})
metadata={"hnsw:space": "cosine"}
)
for pdf in Path("dokumen").glob("*.pdf"): for pdf in Path("dokumen").glob("*.pdf"):
chunks = potong(baca_pdf(pdf), ukuran, tumpang)
teks = baca_pdf(pdf)
chunks = potong(teks, ukuran, tumpang)
if not chunks:
continue
kol.upsert( kol.upsert(
ids=[ ids=[f"{nama}-{pdf.stem}-{i}" for i in range(len(chunks))],
f"{nama}-{pdf.stem}-{i}"
for i in range(len(chunks))
],
documents=chunks, documents=chunks,
metadatas=[ metadatas=[{"sumber": pdf.name, "chunk": i} for i in range(len(chunks))],
{"sumber": pdf.name, "chunk": i}
for i in range(len(chunks))
]
) )
print(f" {nama}: {pdf.name} -> {len(chunks)} chunk")
return kol return kol
# ========================================== # ==========================================
# 5. 15 PERTANYAAN DAN KATA KUNCI # 15 PERTANYAAN + KATA KUNCI
# (disesuaikan dengan isi PDF probabilitas)
# ========================================== # ==========================================
UJI = [ UJI = [
("What is a sample space?", "sample space"), ("What is a sample space?", "sample space"),
("What is classical probability?", "classical probability"), ("What is classical probability?", "classical probability"),
("What is empirical probability?", "empirical probability"), ("What is empirical probability?", "empirical probability"),
("What is subjective probability?", "subjective probability"), ("What is subjective probability?", "subjective probability"),
("What is a simple event?", "simple event"), ("What is a simple event?", "simple event"),
("What is the multiplication rule?", "multiplication rule"), ("What is the multiplication rule?", "multiplication rule"),
("What is the addition rule?", "addition rule"), ("What is the addition rule?", "addition rule"),
("What are mutually exclusive events?", "mutually exclusive"), ("What are mutually exclusive events?", "mutually exclusive"),
("What is conditional probability?", "conditional probability"), ("What is conditional probability?", "conditional probability"),
("What is the law of large numbers?", "law of large numbers"), ("What is the law of large numbers?", "law of large numbers"),
("What is a discrete probability distribution?", "discrete probability distribution"), ("What is a discrete probability distribution?", "discrete probability distribution"),
("What is a binomial experiment?", "binomial experiment"), ("What is a binomial experiment?", "binomial experiment"),
("What is the fundamental counting principle?", "fundamental counting principle"), ("What is the fundamental counting principle?", "fundamental counting principle"),
("What is the difference between permutations and combinations?", "permutation"), ("What is the difference between permutations and combinations?", "permutation"),
("What is a random variable?", "random variable"), ("What is a random variable?", "random variable"),
] ]
# ==========================================
# 6. FUNGSI HIT@K
# ==========================================
def hit_at_k(koleksi, uji, k): def hit_at_k(koleksi, uji, k):
benar = 0 benar = 0
jumlah = min(k, koleksi.count())
if jumlah == 0:
return 0
for pertanyaan, kunci in uji: for pertanyaan, kunci in uji:
docs = koleksi.query(query_texts=[pertanyaan], n_results=k)["documents"][0]
docs = koleksi.query(
query_texts=[pertanyaan],
n_results=jumlah
)["documents"][0]
if any(kunci.lower() in d.lower() for d in docs): if any(kunci.lower() in d.lower() for d in docs):
benar += 1 benar += 1
return benar / len(uji) return benar / len(uji)
# ========================================== # ==========================================
# 7. MEMBUAT DUA KOLEKSI BERBEDA # JALANKAN: BUAT 2 KOLEKSI
# ========================================== # ==========================================
kol_500 = buat_koleksi("chunk_500", 500, 75) print("=== MEMBUAT KOLEKSI ===")
kol_500 = buat_koleksi("chunk_500", 500, 75)
kol_1500 = buat_koleksi("chunk_1500", 1500, 225) kol_1500 = buat_koleksi("chunk_1500", 1500, 225)
print("\n=== CEK ISI KOLEKSI ===")
print(f" chunk_500 : {kol_500.count()} dokumen")
print(f" chunk_1500 : {kol_1500.count()} dokumen")
# ========================================== # ==========================================
# 8. MENAMPILKAN TABEL HASIL HIT@K # HITUNG HIT@K
# ========================================== # ==========================================
print("\n========== TABEL HIT@K ==========") print("\n=== TABEL HIT@K ===")
print(f"{'k':<4}{'Chunk 500':<14}{'Chunk 1500':<14}")
print(f"{'k':<5}{'Chunk 500':<15}{'Chunk 1500':<15}")
hasil = {}
for k in (1, 3, 5): for k in (1, 3, 5):
print(f"{k:<4}{hit_at_k(kol_500, UJI, k):<14.2f}{hit_at_k(kol_1500, UJI, k):<14.2f}")
h500 = hit_at_k(kol_500, UJI, k) print("\n=== PERTANYAAN GAGAL di k=3 ===")
h1500 = hit_at_k(kol_1500, UJI, k)
hasil[k] = (h500, h1500)
print(f"{k:<5}{h500:<15.2f}{h1500:<15.2f}")
# ==========================================
# 9. MENAMPILKAN PERTANYAAN YANG GAGAL
# ==========================================
print("\n========== PERTANYAAN GAGAL DI k=3 ==========")
gagal = []
for pertanyaan, kunci in UJI: for pertanyaan, kunci in UJI:
ok_500 = any(kunci.lower() in d.lower()
for d in kol_500.query(query_texts=[pertanyaan], n_results=3)["documents"][0])
ok_1500 = any(kunci.lower() in d.lower()
for d in kol_1500.query(query_texts=[pertanyaan], n_results=3)["documents"][0])
if not ok_500 and not ok_1500:
print(f"X {pertanyaan} (kunci: '{kunci}')")
docs_500 = kol_500.query( print("\n=== SELESAI ===")
query_texts=[pertanyaan],
n_results=min(3, kol_500.count())
)["documents"][0]
docs_1500 = kol_1500.query(
query_texts=[pertanyaan],
n_results=min(3, kol_1500.count())
)["documents"][0]
ok_500 = any(kunci.lower() in d.lower() for d in docs_500)
ok_1500 = any(kunci.lower() in d.lower() for d in docs_1500)
if not ok_500 or not ok_1500:
gagal.append(pertanyaan)
print(f"\nPertanyaan: {pertanyaan}")
print(f"Kata kunci: {kunci}")
print(f"Chunk 500 : {'Berhasil' if ok_500 else 'Gagal'}")
print(f"Chunk 1500: {'Berhasil' if ok_1500 else 'Gagal'}")
if not gagal:
print("Semua pertanyaan berhasil ditemukan pada kedua koleksi.")
# ==========================================
# 10. KESIMPULAN OTOMATIS
# ==========================================
print("\n========== KESIMPULAN ==========")
rata_500 = sum(v[0] for v in hasil.values()) / len(hasil)
rata_1500 = sum(v[1] for v in hasil.values()) / len(hasil)
print(f"Rata-rata Hit@K Chunk 500 : {rata_500:.2f}")
print(f"Rata-rata Hit@K Chunk 1500 : {rata_1500:.2f}")
if rata_500 > rata_1500:
print("Chunk 500 memiliki hasil pencarian lebih baik.")
elif rata_1500 > rata_500:
print("Chunk 1500 memiliki hasil pencarian lebih baik.")
else:
print("Kedua ukuran chunk memiliki hasil rata-rata yang sama.")
print(
f"Jumlah pertanyaan yang gagal pada minimal satu koleksi "
f"di k=3: {len(gagal)} dari {len(UJI)}."
)
print(
"Kegagalan pencarian dapat disebabkan oleh perbedaan istilah, "
"informasi yang tersebar di beberapa chunk, atau hasil embedding "
"yang kurang relevan."
)
print("\n========== PENGUJIAN SELESAI ==========")