Upload files to "/"
This commit is contained in:
+187
@@ -0,0 +1,187 @@
|
|||||||
|
"""
|
||||||
|
Lab Terbimbing P-10: RAG Part 1 (Chunking, ChromaDB, Hit@k)
|
||||||
|
|
||||||
|
Persiapan:
|
||||||
|
pip install chromadb sentence-transformers pypdf
|
||||||
|
Taruh 2-3 PDF berbasis teks di folder dokumen/
|
||||||
|
|
||||||
|
Jalankan bertahap:
|
||||||
|
python lab_p10.py lab1
|
||||||
|
python lab_p10.py lab2
|
||||||
|
python lab_p10.py lab3
|
||||||
|
"""
|
||||||
|
import random
|
||||||
|
import sys
|
||||||
|
from datetime import datetime
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
import chromadb
|
||||||
|
from chromadb.utils import embedding_functions
|
||||||
|
from pypdf import PdfReader
|
||||||
|
|
||||||
|
BASE = Path(__file__).resolve().parent # folder tempat lab_p10.py berada
|
||||||
|
FOLDER = BASE / "dokumen"
|
||||||
|
# Setiap kali script dijalankan -> folder indeks BARU (tidak bentrok dengan run lain)
|
||||||
|
RUN_ID = datetime.now().strftime("%Y%m%d_%H%M%S")
|
||||||
|
DB_PATH = str(BASE / "chroma_db" / f"run_{RUN_ID}")
|
||||||
|
MODEL = "paraphrase-multilingual-MiniLM-L12-v2"
|
||||||
|
|
||||||
|
|
||||||
|
# ---------- fungsi dasar ----------
|
||||||
|
def baca_pdf(path):
|
||||||
|
reader = PdfReader(path)
|
||||||
|
return "\n".join((p.extract_text() or "") for p in reader.pages)
|
||||||
|
|
||||||
|
|
||||||
|
def potong(teks, ukuran=800, tumpang=100):
|
||||||
|
hasil, mulai = [], 0
|
||||||
|
while mulai < len(teks):
|
||||||
|
hasil.append(teks[mulai:mulai + ukuran])
|
||||||
|
mulai += ukuran - tumpang
|
||||||
|
return [h for h in hasil if h.strip()]
|
||||||
|
|
||||||
|
|
||||||
|
def daftar_pdf():
|
||||||
|
pdfs = sorted(FOLDER.glob("*.pdf"))
|
||||||
|
if not pdfs:
|
||||||
|
sys.exit(f"Tidak ada PDF di {FOLDER}. Letakkan 2-3 PDF dulu.")
|
||||||
|
return pdfs
|
||||||
|
|
||||||
|
|
||||||
|
def buat_koleksi(nama, ukuran, tumpang):
|
||||||
|
print(f"Indeks run ini disimpan di: {DB_PATH}")
|
||||||
|
emb = embedding_functions.SentenceTransformerEmbeddingFunction(model_name=MODEL)
|
||||||
|
client = chromadb.PersistentClient(path=DB_PATH)
|
||||||
|
koleksi = client.get_or_create_collection(
|
||||||
|
name=nama, embedding_function=emb, metadata={"hnsw:space": "cosine"}
|
||||||
|
)
|
||||||
|
for pdf in daftar_pdf():
|
||||||
|
chunks = potong(baca_pdf(pdf), ukuran, tumpang)
|
||||||
|
if not chunks:
|
||||||
|
print(f"PERINGATAN: {pdf.name} kosong (mungkin PDF scan), dilewati.")
|
||||||
|
continue
|
||||||
|
koleksi.upsert(
|
||||||
|
ids=[f"{pdf.stem}-{i}" for i in range(len(chunks))],
|
||||||
|
documents=chunks,
|
||||||
|
metadatas=[{"sumber": pdf.name, "chunk": i} for i in range(len(chunks))],
|
||||||
|
)
|
||||||
|
print(f"[{nama}] {pdf.name} -> {len(chunks)} chunk")
|
||||||
|
return koleksi
|
||||||
|
|
||||||
|
|
||||||
|
# ---------- LAB 1: eksperimen chunking ----------
|
||||||
|
def lab1():
|
||||||
|
pdf = daftar_pdf()[0]
|
||||||
|
teks = baca_pdf(pdf)
|
||||||
|
print(f"File: {pdf.name} | total {len(teks)} karakter")
|
||||||
|
print("=== 500 karakter pertama ===")
|
||||||
|
print(teks[:500])
|
||||||
|
print("\n(Jika kosong/aneh: PDF hasil scan, butuh OCR.)\n")
|
||||||
|
|
||||||
|
for ukuran in (500, 1000, 1500):
|
||||||
|
tumpang = int(ukuran * 0.15) # overlap 15%
|
||||||
|
chunks = potong(teks, ukuran, tumpang)
|
||||||
|
print(f"\n##### ukuran={ukuran}, overlap={tumpang} -> {len(chunks)} chunk #####")
|
||||||
|
for c in random.sample(chunks, min(3, len(chunks))):
|
||||||
|
print("-" * 50)
|
||||||
|
print(c)
|
||||||
|
print("-" * 50)
|
||||||
|
print("\nCatat: jumlah chunk tiap ukuran, dan apakah ada chunk "
|
||||||
|
"yang terpotong di tengah kalimat/tabel.")
|
||||||
|
|
||||||
|
|
||||||
|
# ---------- LAB 2: bangun indeks & cari ----------
|
||||||
|
PERTANYAAN_LAB2 = [
|
||||||
|
"Berapa jumlah gelas sampel untuk setiap merek air mineral?",
|
||||||
|
"Apa saja ukuran pemusatan data yang dihitung?",
|
||||||
|
"Apa tujuan dari praktikum statistik deskriptif ini?",
|
||||||
|
"Merek air mineral mana yang beratnya paling konsisten?",
|
||||||
|
"Data apa saja yang termasuk skala pengukuran rasio?",
|
||||||
|
]
|
||||||
|
PARAFRASE_LAB2 = [
|
||||||
|
"Seberapa lebar sebaran tinggi badan cowok dibanding cewek?",
|
||||||
|
"Pakai aplikasi apa data hasil timbangan diolah?",
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def tampil(koleksi, daftar):
|
||||||
|
for q in daftar:
|
||||||
|
hasil = koleksi.query(query_texts=[q], n_results=3)
|
||||||
|
print(f"\nQ: {q}")
|
||||||
|
for doc, meta, jarak in zip(hasil["documents"][0],
|
||||||
|
hasil["metadatas"][0],
|
||||||
|
hasil["distances"][0]):
|
||||||
|
print(f" [{meta['sumber']} · chunk {meta['chunk']}] jarak={jarak:.3f}")
|
||||||
|
print(" ", doc[:200].replace("\n", " "), "...")
|
||||||
|
|
||||||
|
|
||||||
|
def lab2():
|
||||||
|
koleksi = buat_koleksi("pedoman", 800, 100)
|
||||||
|
print("\n===== 5 PERTANYAAN BIASA =====")
|
||||||
|
tampil(koleksi, PERTANYAAN_LAB2)
|
||||||
|
print("\n===== 2 PERTANYAAN PARAFRASE =====")
|
||||||
|
tampil(koleksi, PARAFRASE_LAB2)
|
||||||
|
print("\nCatat: apakah chunk teratas relevan? Apakah parafrase tetap ketemu?")
|
||||||
|
|
||||||
|
|
||||||
|
# ---------- LAB 3: ukur Hit@k (+ MRR) ----------
|
||||||
|
UJI = [
|
||||||
|
("Berapa mean berat air mineral Alfa pada data tunggal?", "225,3"),
|
||||||
|
("Berapa standar deviasi tinggi badan laki-laki?", "6,65"),
|
||||||
|
("Berapa varians tinggi badan perempuan?", "8,97"),
|
||||||
|
("Software apa yang dipakai untuk mengolah data?", "Minitab"),
|
||||||
|
("Berapa gelas sampel yang ditimbang untuk tiap merek?", "40 gelas"),
|
||||||
|
("Merek air mineral apa saja yang diteliti?", "Aqua, Alfamart, dan Vit"),
|
||||||
|
("Apa kelebihan boxplot dibanding mean?", "tahan terhadap nilai ekstrim"),
|
||||||
|
("Data apa yang termasuk skala nominal?", "NRP, jenis kelamin"),
|
||||||
|
("Berapa skewness data kelompok merek Alfa?", "-0,1826"),
|
||||||
|
("Berapa IQR tinggi badan laki-laki?", "11,25"),
|
||||||
|
("Berapa mean berat air mineral Vit?", "201,75"),
|
||||||
|
("Apa itu presisi dalam pengukuran?", "konsistensi hasil pengukuran"),
|
||||||
|
("Berapa rata-rata jumlah SKS responden?", "20,765"),
|
||||||
|
("Untuk apa statistika inferensial digunakan?", "statistika inferensial digunakan"),
|
||||||
|
("Berapa range tinggi badan perempuan?", "8,20"),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def hit_at_k(koleksi, uji, k):
|
||||||
|
benar = 0
|
||||||
|
for q, kunci in uji:
|
||||||
|
docs = koleksi.query(query_texts=[q], n_results=k)["documents"][0]
|
||||||
|
if any(kunci.lower() in d.lower() for d in docs):
|
||||||
|
benar += 1
|
||||||
|
return benar / len(uji)
|
||||||
|
|
||||||
|
|
||||||
|
def mrr(koleksi, uji, k=5):
|
||||||
|
total = 0.0
|
||||||
|
for q, kunci in uji:
|
||||||
|
docs = koleksi.query(query_texts=[q], n_results=k)["documents"][0]
|
||||||
|
for rank, d in enumerate(docs, 1):
|
||||||
|
if kunci.lower() in d.lower():
|
||||||
|
total += 1 / rank
|
||||||
|
break
|
||||||
|
return total / len(uji)
|
||||||
|
|
||||||
|
|
||||||
|
def lab3():
|
||||||
|
if len(UJI) < 15:
|
||||||
|
print(f"PERINGATAN: baru {len(UJI)} pertanyaan uji; tugas meminta 15.\n")
|
||||||
|
pengaturan = {"chunk500": (500, 75), "chunk1000": (1000, 150)}
|
||||||
|
ringkasan = {}
|
||||||
|
for nama, (ukuran, tumpang) in pengaturan.items():
|
||||||
|
koleksi = buat_koleksi(nama, ukuran, tumpang)
|
||||||
|
ringkasan[nama] = {f"Hit@{k}": hit_at_k(koleksi, UJI, k) for k in (1, 3, 5)}
|
||||||
|
ringkasan[nama]["MRR"] = mrr(koleksi, UJI)
|
||||||
|
|
||||||
|
print("\n===== HASIL =====")
|
||||||
|
for nama, skor in ringkasan.items():
|
||||||
|
print(nama, " | ".join(f"{m}={v:.2f}" for m, v in skor.items()))
|
||||||
|
print("\nTulis kesimpulan 3-4 kalimat: ukuran terbaik, pertanyaan yang gagal, dugaan penyebab.")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
perintah = sys.argv[1] if len(sys.argv) > 1 else ""
|
||||||
|
{"lab1": lab1, "lab2": lab2, "lab3": lab3}.get(
|
||||||
|
perintah, lambda: print(__doc__)
|
||||||
|
)()
|
||||||
Reference in New Issue
Block a user