Template
tugas10
This commit is contained in:
@@ -0,0 +1,155 @@
|
||||
"""Lab 1 - Eksperimen chunking (15 menit).
|
||||
|
||||
1. Baca satu PDF dengan baca_pdf() dan cetak 500 karakter pertama.
|
||||
2. Potong dengan potong() memakai tiga ukuran (500, 1000, 1500) dan overlap 10-20%.
|
||||
3. Lihat 3 chunk acak tiap ukuran: terpotong di tengah kalimat atau tabel?
|
||||
"""
|
||||
import os
|
||||
import random
|
||||
import sys
|
||||
|
||||
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
||||
from laporan_utils import Laporan
|
||||
from rag_utils import FOLDER_DOKUMEN, baca_pdf, norm, potong, potong_dengan_posisi
|
||||
|
||||
BASE = os.path.dirname(os.path.abspath(__file__))
|
||||
PDF = os.path.join(FOLDER_DOKUMEN, "pengantar_rag.pdf") # ganti dengan PDF Anda
|
||||
UKURAN = [500, 1000, 1500]
|
||||
OVERLAP = [0.10, 0.15, 0.20]
|
||||
OVERLAP_UTAMA = 0.15
|
||||
SEED = 42
|
||||
|
||||
|
||||
def wilayah_tabel(teks):
|
||||
"""Cari rentang karakter tabel ("Tabel N." sampai baris "Sumber:")."""
|
||||
hasil, pos = [], 0
|
||||
while True:
|
||||
a = teks.find("Tabel ", pos)
|
||||
if a < 0:
|
||||
return hasil
|
||||
b = teks.find("Sumber:", a)
|
||||
b = len(teks) if b < 0 else teks.find("\n", b)
|
||||
hasil.append((a, b))
|
||||
pos = b
|
||||
|
||||
|
||||
def awal_di_tengah_kalimat(teks, mulai):
|
||||
"""Chunk dimulai di tengah kalimat bila karakter bermakna sebelumnya bukan akhir kalimat/baris."""
|
||||
sebelum = teks[:mulai].rstrip(" ")
|
||||
return bool(sebelum) and sebelum[-1] not in ".!?:\n"
|
||||
|
||||
|
||||
def akhir_di_tengah_kalimat(teks, akhir):
|
||||
ujung = teks[:akhir].rstrip()
|
||||
return bool(ujung) and ujung[-1] not in ".!?"
|
||||
|
||||
|
||||
def memotong_tabel(wil, mulai, akhir):
|
||||
"""True bila chunk beririsan dengan tabel tetapi tidak memuat seluruh tabel."""
|
||||
for a, b in wil:
|
||||
beririsan = mulai < b and akhir > a
|
||||
utuh = mulai <= a and akhir >= b
|
||||
if beririsan and not utuh:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def main():
|
||||
# ---- Langkah 1
|
||||
teks = baca_pdf(PDF)
|
||||
n_huruf = sum(c.isalpha() for c in teks)
|
||||
kualitas = {
|
||||
"File": os.path.basename(PDF),
|
||||
"Total karakter": len(teks),
|
||||
"Karakter rusak (U+FFFD)": teks.count("\ufffd"),
|
||||
"Persentase huruf": f"{n_huruf / len(teks):.0%}",
|
||||
}
|
||||
print("=== Langkah 1: 500 karakter pertama ===")
|
||||
print(teks[:500])
|
||||
print(kualitas)
|
||||
|
||||
# ---- Langkah 2
|
||||
print("\n=== Langkah 2: jumlah chunk ===")
|
||||
tabel_jumlah = [["Ukuran (karakter)"] + [f"overlap {int(o * 100)}%" for o in OVERLAP]]
|
||||
for u in UKURAN:
|
||||
baris = [u]
|
||||
for o in OVERLAP:
|
||||
n = len(potong(teks, u, o))
|
||||
baris.append(n)
|
||||
print(f"ukuran={u:5d} overlap={o:.0%} -> {n} chunk")
|
||||
tabel_jumlah.append(baris)
|
||||
|
||||
# ---- Langkah 3
|
||||
wil = wilayah_tabel(teks)
|
||||
rnd = random.Random(SEED)
|
||||
cuplikan, statistik = {}, [["Ukuran", "Jumlah chunk", "Awal di tengah kalimat",
|
||||
"Akhir di tengah kalimat", "Memotong tabel"]]
|
||||
for u in UKURAN:
|
||||
pot = potong_dengan_posisi(teks, u, OVERLAP_UTAMA)
|
||||
n = len(pot)
|
||||
a = sum(awal_di_tengah_kalimat(teks, m) for m, _, _ in pot)
|
||||
z = sum(akhir_di_tengah_kalimat(teks, e) for _, e, _ in pot)
|
||||
t = sum(memotong_tabel(wil, m, e) for m, e, _ in pot)
|
||||
statistik.append([u, n, f"{a}/{n} ({a / n:.0%})", f"{z}/{n} ({z / n:.0%})", f"{t}/{n}"])
|
||||
pilih = sorted(rnd.sample(range(n), min(3, n)))
|
||||
cuplikan[u] = [(i + 1, pot[i][0], pot[i][1], pot[i][2],
|
||||
awal_di_tengah_kalimat(teks, pot[i][0]),
|
||||
akhir_di_tengah_kalimat(teks, pot[i][1]),
|
||||
memotong_tabel(wil, pot[i][0], pot[i][1])) for i in pilih]
|
||||
print(f"\n=== Langkah 3: ukuran {u} (3 chunk acak) ===")
|
||||
for no, m, e, isi, aw, ak, tb in cuplikan[u]:
|
||||
print(f"--- chunk #{no} [{m}:{e}] awal_tengah={aw} akhir_tengah={ak} potong_tabel={tb}")
|
||||
print(isi)
|
||||
|
||||
# ---- Kesimpulan otomatis dari angka
|
||||
rincian = "; ".join(
|
||||
f"ukuran {r[0]}: awal {r[2]}, akhir {r[3]}, memotong tabel {r[4]}" for r in statistik[1:])
|
||||
kesimpulan = (
|
||||
"Pemotongan per karakter tidak mengenal batas kalimat maupun tabel. Hasil seluruh chunk (overlap "
|
||||
f"{int(OVERLAP_UTAMA * 100)}%) - {rincian}. Jadi pada setiap ukuran, mayoritas chunk berakhir di tengah "
|
||||
"kalimat dan sebagian besar juga dimulai di tengah kalimat (hanya chunk pertama yang selalu bersih). "
|
||||
"Persentasenya tidak turun banyak saat ukuran diperbesar; yang berubah adalah jumlah chunk dan "
|
||||
"banyaknya topik per chunk. Tabel 1 terpotong oleh sedikitnya satu chunk pada tiap ukuran, dan teks "
|
||||
"tabel hasil ekstraksi sudah bercampur antar kolom, sehingga baris tabel yang terpisah dari baris "
|
||||
"judulnya sulit dipahami. Perbaikan yang layak dicoba: pemotongan berbasis kalimat/paragraf dan "
|
||||
"memperlakukan tabel sebagai satu chunk utuh."
|
||||
)
|
||||
print("\nKesimpulan:", kesimpulan)
|
||||
|
||||
# ---- Laporan PDF
|
||||
L = Laporan(os.path.join(BASE, "Lab1_Eksperimen_Chunking.pdf"),
|
||||
"Lab 1 - Eksperimen Chunking", "Dikerjakan dengan Python (pdfplumber + fungsi potong)")
|
||||
L.h1("Langkah 1. Membaca PDF dan memeriksa teks")
|
||||
L.p(f"File yang dibaca: {os.path.basename(PDF)}. Berikut 500 karakter pertama hasil baca_pdf():")
|
||||
L.kode(teks[:500])
|
||||
L.spasi()
|
||||
L.tabel([["Pemeriksaan", "Hasil"]] + [[k, v] for k, v in kualitas.items()], [6 * 28.35, 8 * 28.35])
|
||||
L.p("Teks terbaca bersih: tidak ada karakter rusak, kata utuh, dan urutan paragraf benar. "
|
||||
"Satu catatan: teks Tabel 1 hasil ekstraksi tercampur antar kolom karena sel yang lebih dari satu baris "
|
||||
"digabung per baris tampilan, bukan per sel.")
|
||||
L.h1("Langkah 2. Jumlah chunk tiap pengaturan")
|
||||
L.p("Fungsi potong(teks, ukuran, overlap) memakai jendela geser per karakter; langkah = ukuran x (1 - overlap).")
|
||||
L.tabel(tabel_jumlah)
|
||||
L.cap("Tabel 1. Jumlah chunk untuk 3 ukuran x 3 overlap. Makin besar ukuran, makin sedikit chunk; "
|
||||
"makin besar overlap, makin banyak chunk.")
|
||||
L.h1(f"Langkah 3. Tiga chunk acak per ukuran (overlap {int(OVERLAP_UTAMA * 100)}%, seed {SEED})")
|
||||
L.tabel(statistik)
|
||||
L.cap("Tabel 2. Statistik seluruh chunk pada tiap ukuran (bukan hanya yang dicuplik).")
|
||||
for u in UKURAN:
|
||||
L.h2(f"Ukuran {u} karakter")
|
||||
for no, m, e, isi, aw, ak, tb in cuplikan[u]:
|
||||
L.p(f"Chunk #{no} (karakter {m}-{e}) | awal di tengah kalimat: {'ya' if aw else 'tidak'} | "
|
||||
f"akhir di tengah kalimat: {'ya' if ak else 'tidak'} | memotong tabel: {'ya' if tb else 'tidak'}")
|
||||
L.kode(isi)
|
||||
L.spasi(4)
|
||||
L.h1("Kesimpulan")
|
||||
L.p(kesimpulan)
|
||||
L.halaman_baru()
|
||||
L.h1("Lampiran: kode lab1_chunking.py")
|
||||
L.kode(open(__file__, encoding="utf-8").read())
|
||||
L.simpan()
|
||||
print("\nLaporan: Lab1_Eksperimen_Chunking.pdf")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user