Dieses Tool nutzt moderne NLP-Technologien (Natural Language Processing) und eine benutzerfreundliche GUI basierend auf PyQt6. In diesem Blogbeitrag stelle ich Ihnen den Code vor, erkläre die Installation und zeige Ihnen, wie Sie das Tool in der Praxis einsetzen können.
Was macht der Frage-Antwort-Extraktor?
Der Frage-Antwort-Extraktor ist ein Python-Programm, das:
- Texte aus verschiedenen Dateiformaten einliest (PDF, TXT, DOCX, PPTX),
- Fragen aus einer Textdatei verarbeitet,
- mithilfe eines vortrainierten NLP-Modells (hier: deepset/gelectra-base-germanquad) Antworten findet,
- Ergebnisse in einer GUI anzeigt und Berichte in verschiedenen Formaten (CSV, Excel, JSON, PDF) generiert.
Dieses Tool eignet sich perfekt für Anwendungsfälle wie:
- Recherche: Schnelles Finden von Antworten in umfangreichen Dokumenten.
- Bildung: Automatische Auswertung von Lehrmaterialien.
- Dokumentenanalyse: Extraktion relevanter Informationen aus Berichten oder Präsentationen.
Der Code
Hier ist der vollständige Code für den Frage-Antwort-Extraktor:
python
import os
os.environ["OMP_NUM_THREADS"] = "1" # Begrenzt OpenMP-Threads
os.environ["MKL_NUM_THREADS"] = "1" # Begrenzt MKL-Threads
import fitz # PyMuPDF
from transformers import pipeline, AutoModelForQuestionAnswering, AutoTokenizer
import re
import pandas as pd
from reportlab.lib.pagesizes import letter
from reportlab.pdfgen import canvas
from io import BytesIO
import docx
import pptx
import mimetypes
from PyQt6.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout, QPushButton,
QLabel, QFileDialog, QProgressBar, QTextEdit, QMessageBox)
from PyQt6.QtCore import Qt
import torch
torch.set_num_threads(1) # Begrenzt PyTorch-Threads
class QuestionAnswerApp(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("Frage-Antwort Extraktor")
self.setGeometry(100, 100, 800, 600)
self.init_ui()
def init_ui(self):
main_widget = QWidget()
self.setCentralWidget(main_widget)
layout = QVBoxLayout(main_widget)
# Dateiauswahl
self.context_label = QLabel("Keine Kontextdatei ausgewählt")
layout.addWidget(self.context_label)
self.select_context_button = QPushButton("Kontextdatei auswählen")
self.select_context_button.clicked.connect(self.select_context)
layout.addWidget(self.select_context_button)
self.questions_label = QLabel("Keine Fragedatei ausgewählt")
layout.addWidget(self.questions_label)
self.select_questions_button = QPushButton("Fragedatei auswählen")
self.select_questions_button.clicked.connect(self.select_questions)
layout.addWidget(self.select_questions_button)
# Start-Button
self.start_button = QPushButton("Analyse starten")
self.start_button.clicked.connect(self.start_analysis)
self.start_button.setEnabled(False)
layout.addWidget(self.start_button)
# Fortschrittsbalken
self.progress_bar = QProgressBar()
self.progress_bar.setVisible(False)
layout.addWidget(self.progress_bar)
# Ausgabebereich
self.output_text = QTextEdit()
self.output_text.setReadOnly(True)
layout.addWidget(self.output_text)
# Export-Button
self.export_button = QPushButton("Als TXT exportieren")
self.export_button.clicked.connect(self.export_to_txt)
self.export_button.setEnabled(False)
layout.addWidget(self.export_button)
def select_context(self):
file_path = QFileDialog.getOpenFileName(self, "Kontextdatei auswählen", "",
"Unterstützte Dateien (*.pdf *.txt *.docx *.pptx)")[0]
if file_path:
self.context_path = file_path
self.context_label.setText(f"Kontext: {os.path.basename(file_path)}")
self.check_start_enabled()
def select_questions(self):
file_path = QFileDialog.getOpenFileName(self, "Fragedatei auswählen", "", "Text Dateien (*.txt)")[0]
if file_path:
self.questions_path = file_path
self.questions_label.setText(f"Fragen: {os.path.basename(file_path)}")
self.check_start_enabled()
def check_start_enabled(self):
self.start_button.setEnabled(hasattr(self, "context_path") and hasattr(self, "questions_path"))
def start_analysis(self):
self.output_text.clear()
self.progress_bar.setVisible(True)
self.start_button.setEnabled(False)
self.export_button.setEnabled(False)
# Ordner für Berichte erstellen
reporting_folder = "reporting"
base_filename = os.path.splitext(os.path.basename(self.context_path))[0]
self.report_folder = os.path.join(reporting_folder, base_filename)
os.makedirs(self.report_folder, exist_ok=True)
# Text und Fragen einlesen
contexts = read_text_from_file(self.context_path)
if not contexts:
QMessageBox.critical(self, "Fehler", "Konnte Kontext nicht einlesen.")
self.reset_ui()
return
questions = read_questions_from_file(self.questions_path)
if not questions:
QMessageBox.critical(self, "Fehler", "Konnte Fragen nicht einlesen.")
self.reset_ui()
return
# Modell laden
model_name = "deepset/gelectra-base-germanquad"
nlp = pipeline("question-answering", model=model_name, tokenizer=model_name)
# Fragen beantworten
answers = process_questions(questions, contexts, nlp, self.update_progress)
if not answers:
self.output_text.setText("Keine Antworten mit ausreichendem Score gefunden.")
else:
self.display_results(answers)
generate_reports(answers, self.report_folder)
self.export_button.setEnabled(True)
self.reset_ui()
def update_progress(self, value, total):
self.progress_bar.setMaximum(total)
self.progress_bar.setValue(value)
QApplication.processEvents()
def display_results(self, answers):
output = "=== Ergebnisse ===\n"
for answer in answers[:5]: # Erste 5 als Vorschau
output += (f"Frage: {answer[0]}\nAntwort: {answer[1]}\nSeite: {answer[2]}\n"
f"Score: {answer[3]:.2f}\nTextstelle: {answer[4]}\n\n")
self.output_text.setText(output)
self.answers = answers
def export_to_txt(self):
txt_path = QFileDialog.getSaveFileName(self, "Als TXT speichern", "", "Text Dateien (*.txt)")[0]
if txt_path:
with open(txt_path, "w", encoding="utf-8") as f:
f.write(self.output_text.toPlainText())
QMessageBox.information(self, "Erfolg", f"Ergebnisse in '{txt_path}' exportiert.")
def reset_ui(self):
self.progress_bar.setVisible(False)
self.start_button.setEnabled(True)
# Funktionen zum Einlesen von Text
def read_text_from_pdf(file_path):
try:
document = fitz.open(file_path)
pages = [(page.get_text(), i + 1) for i, page in enumerate(document)]
document.close()
return pages
except Exception as e:
print(f"Fehler beim Lesen der PDF: {e}")
return []
def read_text_from_txt(file_path):
try:
with open(file_path, 'r', encoding='utf-8') as file:
return [(file.read(), 1)]
except Exception as e:
print(f"Fehler beim Lesen der TXT: {e}")
return []
def read_text_from_docx(file_path):
try:
document = docx.Document(file_path)
return [('\n'.join(para.text for para in document.paragraphs), 1)]
except Exception as e:
print(f"Fehler beim Lesen der DOCX: {e}")
return []
def read_text_from_pptx(file_path):
try:
presentation = pptx.Presentation(file_path)
return [('\n'.join(shape.text for shape in slide.shapes if hasattr(shape, "text")), i + 1)
for i, slide in enumerate(presentation.slides)]
except Exception as e:
print(f"Fehler beim Lesen der PPTX: {e}")
return []
def read_text_from_file(file_path):
mime_type, _ = mimetypes.guess_type(file_path)
if mime_type == 'application/pdf':
return read_text_from_pdf(file_path)
elif mime_type == 'text/plain':
return read_text_from_txt(file_path)
elif mime_type == 'application/vnd.openxmlformats-officedocument.wordprocessingml.document':
return read_text_from_docx(file_path)
elif mime_type == 'application/vnd.openxmlformats-officedocument.presentationml.presentation':
return read_text_from_pptx(file_path)
else:
print(f"Unsupported file type: {mime_type}")
return []
def read_questions_from_file(file_path):
try:
with open(file_path, 'r', encoding='utf-8') as file:
return [q.strip() for q in file.readlines() if q.strip()]
except Exception as e:
print(f"Fehler beim Lesen der Fragen: {e}")
return []
def clean_number_format(text):
text = re.sub(r'(\d)(?=(\d{3})+(\.|\b))', r'\1.', text)
text = re.sub(r'(?<!\d)(\d+)(?=\.\d+)', r'\1.', text)
return text
def find_relevant_text(context, answer_start, answer_end):
start = max(answer_start - 100, 0)
end = min(answer_end + 100, len(context))
return context[start:end]
def process_questions(questions, contexts, nlp, progress_callback):
answers = []
total_steps = len(questions) * len(contexts)
step = 0
for question in questions:
for context, page_num in contexts:
cleaned_context = clean_number_format(context)
result = nlp(question=question, context=cleaned_context, max_answer_len=100)
if result['score'] > 0.5:
relevant_text = find_relevant_text(cleaned_context, result['start'], result['end'])
answers.append((question, result['answer'], page_num, result['score'], relevant_text))
step += 1
progress_callback(step, total_steps)
return answers
def generate_reports(answers, report_folder):
df = pd.DataFrame(answers, columns=["Frage", "Antwort", "Seite", "Score", "Textstelle"])
df.to_csv(os.path.join(report_folder, "report.csv"), index=False, encoding='utf-8')
df.to_excel(os.path.join(report_folder, "report.xlsx"), index=False, engine='openpyxl')
df.to_json(os.path.join(report_folder, "report.json"), orient='records', lines=True, force_ascii=False)
# PDF-Bericht
buffer = BytesIO()
c = canvas.Canvas(buffer, pagesize=letter)
text = c.beginText(40, letter[1] - 40)
text.setFont("Helvetica", 10)
for _, row in df.iterrows():
text.textLines([f"Frage: {row['Frage']}", f"Antwort: {row['Antwort']}",
f"Seite: {row['Seite']}", f"Score: {row['Score']:.2f}",
f"Textstelle: {row['Textstelle']}", ""])
c.drawText(text)
c.showPage()
c.save()
with open(os.path.join(report_folder, "report.pdf"), 'wb') as f:
f.write(buffer.getvalue())
def main():
app = QApplication([])
window = QuestionAnswerApp()
window.show()
app.exec()
if __name__ == "__main__":
main()
Installationsanweisungen
Um den Frage-Antwort-Extraktor zu nutzen, müssen Sie die folgenden Schritte ausführen:
1. Python installieren
Laden Sie die neueste Version von Python (mindestens 3.8) herunter und installieren Sie sie.
2. Virtuelle Umgebung erstellen (optional, aber empfohlen)
bash
python -m venv qa_env
source qa_env/bin/activate # Linux/Mac
qa_env\Scripts\activate # Windows
3. Abhängigkeiten installieren
Installieren Sie die erforderlichen Python-Bibliotheken mit pip:
bash
pip install PyMuPDF transformers torch pandas reportlab python-docx python-pptx PyQt6 openpyxl mimetypes
- PyMuPDF: Für die Verarbeitung von PDF-Dateien.
- Transformers: Für das NLP-Modell von Hugging Face.
- PyTorch: Als Backend für das Modell.
- Pandas: Für die Datenverarbeitung und Berichtgenerierung.
- ReportLab: Für die PDF-Berichtserstellung.
- python-docx: Zum Lesen von DOCX-Dateien.
- python-pptx: Zum Lesen von PPTX-Dateien.
- PyQt6: Für die grafische Benutzeroberfläche.
- openpyxl: Für die Excel-Datei-Generierung.
4. Code ausführen
Speichern Sie den Code in einer Datei (z. B. qa_extractor.py) und führen Sie ihn aus:
bash
python qa_extractor.py
Einsatzfelder
Dieses Tool ist vielseitig einsetzbar. Hier sind einige Beispiele:
1. Wissenschaftliche Recherche
Forscher können große PDF-Dokumente hochladen (z. B. Studien oder Berichte) und gezielte Fragen stellen, um relevante Informationen schnell zu finden, ohne das gesamte Dokument manuell durchsuchen zu müssen.
2. Bildung und Training
Lehrer können Lehrmaterialien (z. B. Präsentationen oder Handouts) analysieren und automatisch Antworten auf Prüfungsfragen generieren lassen.
3. Unternehmensdokumentation
Unternehmen können interne Berichte oder Protokolle analysieren, um wichtige Details wie Projektergebnisse oder KPI-Daten zu extrahieren.
4. Juristische Analyse
Anwälte könnten Schriftsätze oder Verträge hochladen und spezifische Fragen stellen, um Klauseln oder Bedingungen zu identifizieren.
Funktionsweise im Detail
- Dateiauswahl: Über die GUI wählen Sie eine Kontextdatei (PDF, TXT, DOCX, PPTX) und eine TXT-Datei mit Fragen aus.
- Textverarbeitung: Das Tool liest den Text aus der Kontextdatei Seite für Seite (bei PDFs/PPTX) oder als Ganzes (TXT/DOCX).
- NLP-Analyse: Das Modell deepset/gelectra-base-germanquad (optimiert für deutsche Texte) sucht nach Antworten mit einem Score > 0.5.
- Ergebnisse: Die Antworten werden in der GUI angezeigt und als Berichte in einem reporting-Ordner gespeichert.
Fazit
Der Frage-Antwort-Extraktor ist ein mächtiges Werkzeug für alle, die schnell und effizient Informationen aus Textdokumenten extrahieren möchten. Mit der Unterstützung mehrerer Dateiformate und der Integration eines modernen NLP-Modells ist es sowohl für den privaten als auch den professionellen Einsatz geeignet.
Schreibe einen Kommentar