Assignment Chef icon Assignment Chef
All German tutorials

Programming lesson

Python-Information-Retrieval-System: Invertierten Index und TF-IDF-Suche aufbauen (CS 1656 Tutorial)

Lerne Schritt für Schritt, wie du mit Python einen einfachen Information-Retrieval-Engine baust – inklusive invertiertem Index, Stemming und TF-IDF-Ranking. Perfekt für Studierende, die CS 1656 oder ähnliche Assignments bearbeiten.

Python Information Retrieval invertierter Index Python TF-IDF Ranking CS 1656 Assignment Lösung pine-index.py pine-search.py NLTK Stemming Python Information Retrieval Tutorial Deutsch Suchmaschine selber bauen Python TF-IDF Formel Beispiel Dokumentenranking Python Python inverted index JSON Information Retrieval Studium Python Text Mining Tutorial RAG System Grundlagen Python Suchalgorithmus

Information Retrieval in der Praxis: Warum dieser Python-Ansatz aktuell ist

Stell dir vor, du entwickelst eine Suchfunktion für eine KI-gestützte Lernplattform oder analysierst Tweets zu den Olympischen Spielen 2026. Genau hier kommt Information Retrieval (IR) ins Spiel – die Kunst, aus einer Sammlung von Dokumenten die relevantesten Treffer zu finden. In diesem Tutorial zeigen wir dir, wie du mit Python einen eigenen IR-Engine baust, ähnlich wie in den Assignments 1 bis 5 des Kurses CS 1656 an der University of Pittsburgh. Du lernst, einen invertierten Index zu erstellen, Dokumente zu stemmen und mit der TF-IDF-Formel Relevanz-Scores zu berechnen. Das Ganze ist nicht nur ein Programmierprojekt, sondern auch eine Grundlage für moderne Suchmaschinen, Chatbots und sogar RAG-Systeme (Retrieval-Augmented Generation), die aktuell in der KI-Welt boomen.

Was ist ein invertierter Index und wie hilft er bei der Suche?

Ein invertierter Index ist wie das Stichwortverzeichnis eines Buches: Statt alle Seiten durchzublättern, schlägst du direkt nach, wo ein Begriff vorkommt. In der Informatik speicherst du für jedes Wort die Liste der Dokumente und die Häufigkeit pro Dokument. Das ist der Schlüssel für schnelle Suchen in großen Textmengen. In deinem Assignment wirst du pine-index.py schreiben, das genau das macht: Es liest Textdateien aus dem Ordner input/, normalisiert den Text (Kleinbuchstaben, Entfernung von Satzzeichen und Zahlen), wendet Stemming mit der NLTK-Bibliothek an und speichert den Index als JSON-Datei inverted-index.json.

Schritt 1: Textvorverarbeitung für den Index

Bevor du den Index erstellst, musst du den Text bereinigen. Das ist wichtig, damit „laufen“, „läuft“ und „gelaufen“ als das gleiche Wort erkannt werden – das erreicht man durch Stemming. Hier ein Beispielcode für die Vorverarbeitung:

import re
import nltk
from nltk.stem import PorterStemmer

stemmer = PorterStemmer()

def preprocess(text):
    text = text.lower()
    text = re.sub(r'[^a-zäöüß\s]', '', text)  # entfernt Zahlen und Satzzeichen
    words = text.split()
    words = [stemmer.stem(w) for w in words]
    return words

Beachte: In der Aufgabenstellung wird die Verwendung von nltk explizit erlaubt. Dein Programm sollte alle Zeichen außer Buchstaben und Leerzeichen entfernen – also auch Zahlen, die in vielen Dokumenten vorkommen. Das ist ein typischer Fehler, der in der Bewertung Punkte kosten kann.

Den invertierten Index aufbauen – Datenstruktur und JSON-Speicherung

Die empfohlene Datenstruktur ist ein Dictionary (dict). Der Schlüssel ist das Wort, der Wert ein weiteres Dictionary mit Dokumentnamen als Schlüssel und der Häufigkeit als Wert. Zusätzlich speicherst du die Anzahl der Dokumente, die ein Wort enthalten (document frequency) und die Gesamtzahl der Dokumente N. Am Ende exportierst du alles als JSON. Folgendes Grundgerüst zeigt dir, wie das aussehen könnte:

import os
import json
from collections import defaultdict

input_dir = 'input/'
index = defaultdict(lambda: {'freq': {}, 'df': 0})
N = 0

for filename in os.listdir(input_dir):
    if filename.endswith('.txt'):
        with open(os.path.join(input_dir, filename), 'r', encoding='utf-8') as f:
            text = f.read()
        words = preprocess(text)
        N += 1
        # Häufigkeit pro Dokument zählen
        word_counts = {}
        for w in words:
            word_counts[w] = word_counts.get(w, 0) + 1
        # In den Index eintragen
        for w, count in word_counts.items():
            index[w]['freq'][filename] = count
            index[w]['df'] = len(index[w]['freq'])  # aktualisiert df

# Gesamtzahl N speichern
index['_N'] = N

with open('inverted-index.json', 'w') as f:
    json.dump(index, f, indent=2)

Wichtig: Die df (document frequency) muss die Anzahl der Dokumente sein, die das Wort enthalten – nicht die Summe der Vorkommen. In vielen Lösungen wird das falsch gemacht, also achte darauf.

Das Suchprogramm: pine-search.py – TF-IDF-Ranking verstehen

Nachdem der Index erstellt ist, schreibst du pine-search.py. Dieses Programm liest den Index aus inverted-index.json und eine Liste von Suchanfragen aus keywords.txt. Jede Zeile in der Datei enthält mehrere Suchbegriffe (z.B. „pittsburgh steelers“). Für jede Anfrage berechnest du für jedes Dokument einen Relevanzscore nach der Formel:

w(key, doc) = (1 + log2(freq(key, doc))) * log2(N / n(doc))

Dabei ist freq(key, doc) die Häufigkeit des Wortes im Dokument, n(doc) die Anzahl der Dokumente, die das Wort enthalten (df), und N die Gesamtzahl der Dokumente. Der Score eines Dokuments ist die Summe der Gewichte aller Suchbegriffe. Dokumente mit gleichem Score erhalten den gleichen Rang und werden alphabetisch nach Dateinamen sortiert.

Beispiel: Ranking für „pittsburgh steelers“

Angenommen, doc3.txt enthält „steelers“ 3-mal und „pittsburgh“ 1-mal. N = 9, df(steelers)=2, df(pittsburgh)=5. Dann berechnest du:

w(steelers) = (1 + log2(3)) * log2(9/2) = (1 + 1.585) * 2.1699 = 2.585 * 2.1699 ≈ 5.61
w(pittsburgh) = (1 + log2(1)) * log2(9/5) = (1 + 0) * 0.848 = 0.848
Score = 5.61 + 0.848 = 6.458

Das Ergebnis wird im Format [1] file=doc3.txt score=6.458 weight(pittsburgh)=0.848 weight(steelers)=5.610 ausgegeben. Achte darauf, dass die Gewichte auf 6 Nachkommastellen genau sind.

Häufige Fehler und Tipps für die Abgabe

In der Bewertung wird streng auf die Formatvorgaben geachtet. Hier sind die häufigsten Fallstricke:

  • Falsche Stemming-Ergebnisse: Teste deinen Stemmer mit Wörtern wie „running“ → „run“. NLTK’s PorterStemmer ist in Ordnung.
  • Vergessen, Zahlen zu entfernen: Die Anweisung sagt eindeutig „eliminate numbers“. Entferne also Ziffern, nicht nur Satzzeichen.
  • JSON-Struktur falsch: Der Index muss als JSON-Objekt gespeichert werden, nicht als Liste. Der Schlüssel _N ist nicht zwingend, aber praktisch.
  • Ausgabeformat: Die erste Zeile der Ausgabe muss exakt so aussehen: ———————————————————— (40 Bindestriche). Danach kommt keywords = pittsburgh steelers und dann die Ergebnisse.

Erweiterungsideen: Von der Uni-Aufgabe zur echten Anwendung

Der hier gelernte Ansatz ist die Basis für viele moderne Technologien. Du könntest deinen IR-Engine mit einer Flask-Weboberfläche verbinden, um eine Mini-Suchmaschine zu bauen. Oder du integrierst ihn in einen Chatbot, der aus einer Wissensdatenbank antwortet – ähnlich wie bei RAG-Modellen, die 2026 in vielen KI-Apps stecken. Auch die Verarbeitung von Social-Media-Daten (Tweets zu aktuellen Events wie der Fußball-WM 2026) ist ein spannendes Feld. Wenn du die TF-IDF-Werte durch neuronale Embeddings ersetzt, landest du bei semantischer Suche – ein heißes Thema in der KI-Forschung.

Zusammenfassung: Dein Fahrplan für das Assignment

  1. pine-index.py: Textdateien einlesen, vorverarbeiten, invertierten Index erstellen, als JSON speichern.
  2. pine-search.py: JSON laden, Keywords parsen, TF-IDF-Scores berechnen, nach Rang und Dateinamen sortieren, formatierte Ausgabe.
  3. Testen: Verwende die beigelegten Beispieldateien und vergleiche mit der Musterausgabe output.txt.
  4. Abgabe: Commit auf GitHub (privates Repository) – achte auf die Deadlines (keine Abgabe nach 48 Stunden Verspätung).

Mit diesem Tutorial bist du bestens gerüstet, um die Assignments 1 bis 5 von CS 1656 zu lösen. Viel Erfolg!