02. PDF
Portable Document Format (PDF) (opens in a new tab), sebuah format file yang distandarisasi oleh ISO 32000, dikembangkan oleh Adobe pada tahun 1992 untuk menyajikan dokumen yang menyertakan format teks dan gambar dengan cara yang tidak bergantung pada perangkat lunak aplikasi, perangkat keras, dan sistem operasi.
Panduan ini mencakup cara memuat dokumen PDF
ke dalam format Dokumen (opens in a new tab) LangChain. Format ini digunakan di bagian hilir.
LangChain terintegrasi dengan berbagai pengurai PDF. Beberapa di antaranya sederhana dan relatif tingkat rendah, sementara yang lain mendukung OCR dan pemrosesan gambar atau melakukan analisis tata letak dokumen tingkat lanjut.
Pilihan yang tepat tergantung pada aplikasi Anda.
Catatan
Dokumentasi LangChain (opens in a new tab)
Eksperimen PDF tim AutoRAG
Papan peringkat berdasarkan eksperimen yang dilakukan di AutoRAG
Angka-angka di bawah ini menunjukkan kesetaraan (Semakin rendah, semakin baik)
PDFMiner | PDFPlumber | PyPDFium2 | PyMuPDF | PyPDF2 | |
---|---|---|---|---|---|
Medical | 1 | 2 | 3 | 4 | 5 |
Law | 3 | 1 | 1 | 3 | 5 |
Finance | 1 | 2 | 2 | 4 | 5 |
Public | 1 | 1 | 1 | 4 | 5 |
Sum | 5 | 5 | 7 | 15 | 20 |
Sumber: Blog Medium AutoRAG (opens in a new tab)
# File konfigurasi untuk mengelola API KEY sebagai variabel lingkungan
from dotenv import load_dotenv
# Memuat informasi KUNCI API
load_dotenv()
Dokumentasi praktis
Lembaga Penelitian Kebijakan Perangkat Lunak (SPRi) - Desember 2023
- Penulis : Jaeheung Yoo (Peneliti Utama, Pusat Penelitian Kebijakan AI), Jisoo Lee (Peneliti, Pusat Penelitian Kebijakan AI)
- Tautan : https://spri.kr/posts/view/23669 (opens in a new tab)
- Nama file : SPRI_AI_Brief_2023년12월호_F.pdf
Catatan : Silakan unduh file di atas di folder data
FILE_PATH = "./data/SPRI_AI_Brief_Desember 2023_F.pdf"
def show_metadata(docs):
if docs:
print("[metadata]")
print(list(docs[0].metadata.keys()))
print("\n[examples]")
max_key_length = max(len(k) for k in docs[0].metadata.keys())
for k, v in docs[0].metadata.items():
print(f"{k:<{max_key_length}} : {v}")
PyPDF
Di sini kita menggunakan pypdf
untuk memuat PDF sebagai sebuah larik dokumen, yang masing-masing berisi konten halaman dan metadata beserta nomor halaman.
# install
# !pip install -qU pypdf
from langchain_community.document_loaders import PyPDFLoader
# mengatur jalur berkas
loader = PyPDFLoader(FILE_PATH)
# Inisialisasi pemuat PDF
docs = loader.load()
# mencetak isi dokumen
print(docs[10].page_content[:300])
# keluaran metadata
show_metadata(docs)
PyPDF (OCR)
Beberapa PDF berisi gambar teks dalam dokumen atau gambar yang dipindai. Anda juga dapat menggunakan paket rapidocr-onnxruntime
untuk mengekstrak teks dari gambar.
# install
# !pip install -qU rapidocr-onnxruntime
# Inisialisasi pemuat PDF, aktifkan opsi ekstraksi gambar
loader = PyPDFLoader("https://arxiv.org/pdf/2103.15348.pdf" extract_images = True)
# memuat halaman PDF
docs = loader.load()
# mengakses konten halaman
print(docs[4].page_content[:300])
show_metadata(docs)
PyMuPDF
PyMuPDF dioptimalkan untuk kecepatan dan menyertakan metadata yang mendetail tentang PDF dan halaman-halamannya. Ini mengembalikan satu dokumen per halaman:
# install
# !pip install -qU pymupdf
from langchain_community.document_loaders import PyMuPDFLoader
# membuat sebuah contoh pemuat PyMuPDF
loader = PyMuPDFLoader(FILE_PATH)
# memuat dokumen
docs = loader.load()
# mencetak isi dokumen
print(docs[10].page_content[:300])
show_metadata(docs)
Tidak terstruktur
Unstructured (opens in a new tab) mendukung antarmuka umum untuk menangani format file yang tidak terstruktur atau semi-terstruktur seperti Markdown atau PDF.
UnstructuredPDFLoader (opens in a new tab) dari LangChain terintegrasi dengan Unstructured untuk mengurai dokumen PDF menjadi objek Dokumen (opens in a new tab) LangChain.
# install
# !pip install -qU unstructured
from langchain_community.document_loaders import UnstructuredPDFLoader
# membuat sebuah contoh dari UnstructuredPDFLoader
loader = UnstructuredPDFLoader(FILE_PATH)
# memuat data
docs = loader.load()
# mencetak isi dokumen
print(docs[0].page_content[:300])
show_metadata(docs)
Secara internal, unstructured menciptakan "elemen" yang berbeda untuk setiap potongan teks. Secara default, semua ini digabungkan, tetapi dapat dengan mudah dipisahkan dengan menentukan mode="elements"
.
# membuat sebuah contoh UnstructuredPDFLoader (mode = "elements")
loader = UnstructuredPDFLoader(FILE_PATH, mode = "elements")
# memuat data
docs = loader.load()
# mencetak isi dokumen
print(docs[0].page_content)
Lihat rangkaian lengkap jenis elemen untuk dokumentasi khusus ini
set(doc.metadata["category"] for doc in docs) # mengekstrak kategori data
show_metadata(docs)
PyPDFium2
from langchain_community.document_loaders import PyPDFium2Loader
# membuat sebuah contoh dari pemuat PyPDFium2
loader = PyPDFium2Loader(FILE_PATH)
# memuat data
docs = loader.load()
# mencetak isi dokumen
print(docs[10].page_content[:300])
show_metadata(docs)
PDFMiner
from langchain_community.document_loaders import PDFMinerLoader
# membuat sebuah contoh pemuat PDFMiner
loader = PDFMinerLoader(FILE_PATH)
# memuat data
docs = loader.load()
# mencetak isi dokumen
print(docs[0].page_content[:300])
show_metadata(docs)
Menggunakan PDFMiner untuk menghasilkan teks HTML
Metode ini memungkinkan Anda untuk mengurai konten HTML keluaran melalui BeautifulSoup
untuk mendapatkan informasi yang lebih terstruktur dan lebih kaya tentang ukuran font, nomor halaman, header/footer PDF, dll. yang dapat membantu Anda membagi teks secara semantik menjadi beberapa bagian.
from langchain_community.document_loaders import PDFMinerPDFasHTMLLoader
# membuat sebuah contoh dari PDFMinerPDFasHTMLLoader
loader = PDFMinerPDFasHTMLLoader(FILE_PATH)
# memuat dokumen
docs = loader.load()
# mencetak isi dokumen
print(docs[0].page_content[:300])
<html><head>
<meta http-equiv="Content-Type" content="text/html">
</head><body>
<span style="position:absolute; border: gray 1px solid; left:0px; top:50px; width:612px; height:858px;"></span>
<div style="position:absolute; top:50px;"><a name="1">Page 1</a></div>
<div style="position:absolute; border"></div>
show_metadata(docs)
from bs4 import BeautifulSoup
soup = BeautifulSoup(docs[0].page_content, "html.parser") # inisialisasi parser HTML
content = soup.find_all("div") # mencari semua tag div
import re
cur_fs = None
cur_text = ""
snippets = [] # kumpulkan semua potongan dengan ukuran font yang sama
for c in content:
sp = c.find("span")
if not sp:
continue
st = sp.get("style")
if not st:
continue
fs = re.findall("font-size:(\d+)px", st)
if not fs:
continue
fs = int(fs[0])
if not cur_fs:
cur_fs = fs
if fs == cur_fs:
cur_text += c.text
else
snippets.append((cur_text, cur_fs))
cur_fs = fs
cur_text = c.text
snippets.append((cur_text, cur_fs))
# Kemungkinan untuk menambahkan strategi untuk menghapus cuplikan duplikat (karena header/footer PDF muncul di beberapa halaman, duplikat dapat dianggap sebagai informasi duplikat ketika ditemukan)
from langchain_core.documents import Document
cur_idx = -1
semantic_snippets = []
# judul mengasumsikan: ukuran huruf besar
for s in snippets:
# tentukan judul baru: font cuplikan saat ini > font judul lama
if (
not semantic_snippets
or s[1] > semantic_snippets[cur_idx].metadata["heading_font"]
):
metadata = {"heading": s[0], "content_font": 0, "judul_font": s[1] }
metadata.update(docs[0].metadata)
semantic_snippets.append(Document(page_content="", metadata=metadata))
cur_idx += 1
continue
# tentukan konten bagian yang sama: font cuplikan saat ini <= font konten sebelumnya
if (
not semantic_snippets[cur_idx].metadata["content_font"]
or s[1] <= semantic_snippets[cur_idx].metadata["content_font"]
):
semantic_snippets[cur_idx].page_content += s[0]
semantic_snippets[cur_idx].metadata["content_font"] = max(
s[1], semantic_snippets[cur_idx].metadata["content_font"]
)
continue
# membuat kondisi bagian baru: font cuplikan saat ini > font konten sebelumnya, kurang dari font judul sebelumnya
metadata = {"heading": s[0], "content_font": 0, "heading_font": s[1] }
metadata.update(docs[0].metadata)
semantic_snippets.append(Document(page_content="", metadata=metadata))
cur_idx += 1
print(semantic_snippets[4])
Direktori PyPDF
Memuat PDF dari direktori
from langchain_community.document_loaders import PyPDFDirectoryLoader
# jalur direktori
loader = PyPDFDirectoryLoader("data/")
# memuat dokumen
docs = loader.load()
# mencetak jumlah dokumen
print(len(docs))
# mencetak isi dari dokumen
print(docs[50].page_content[:300])
# cetak metadata
print(docs[50].metadata)
PDFPlumber
Seperti PyMuPDF, dokumen keluaran berisi metadata terperinci tentang PDF dan halamannya, dan mengembalikan satu dokumen per halaman.
from langchain_community.document_loaders import PDFPlumberLoader
# membuat sebuah contoh pemuat dokumen PDF
loader = PDFPlumberLoader(FILE_PATH)
# memuat dokumen
docs = loader.load()
# mengakses data dokumen pertama
print(docs[10].page_content[:300])
show_metadata(docs)