Unterschiede
Hier werden die Unterschiede zwischen zwei Versionen der Seite angezeigt.
Beide Seiten, vorherige Überarbeitung Vorherige Überarbeitung Nächste Überarbeitung | Vorherige Überarbeitung | ||
talit:indexing [2024-08-08 08:06] – [Fragen] hof | talit:indexing [2025-02-15 13:38] (aktuell) – hof | ||
---|---|---|---|
Zeile 1: | Zeile 1: | ||
# Text Indexing | # Text Indexing | ||
- | Nachdem wir in [[talit: | + | <nodisp 1> |
+ | ++++Repo| | ||
+ | https:// | ||
+ | ++++ | ||
+ | </ | ||
+ | |||
+ | Nachdem wir in [[retrieval]] ein einzelnes Attribut indexiert haben, möchten wir dasselbe für einen ganzen Text erreichen. Statt der Geodatenbank verwenden wir dafür Artikel von Wikipedia. | ||
Natürlich benötigen wir auch wieder ein Toy-Dataset: | Natürlich benötigen wir auch wieder ein Toy-Dataset: | ||
Zeile 14: | Zeile 20: | ||
Anders als bei der Geodatenbank müssen wir hier noch den Text in einzelne Wörter (_en_. Tokens) zerlegen. | Anders als bei der Geodatenbank müssen wir hier noch den Text in einzelne Wörter (_en_. Tokens) zerlegen. | ||
- | |||
### Aufgabe 1 - Tokenizer | ### Aufgabe 1 - Tokenizer | ||
Schreibe eine Funktion `def tokenize(text)` die einen Text in einzelne Wörter zerlegt und diese Tokens mit `yield` zurückgibt. | Schreibe eine Funktion `def tokenize(text)` die einen Text in einzelne Wörter zerlegt und diese Tokens mit `yield` zurückgibt. | ||
- | Die Funktion soll im folgenden Text-Indexing-Code aufgerufen werden, der ganz ähnlich funktioniert wie der [[talit: | + | Die Funktion soll im folgenden Text-Indexing-Code aufgerufen werden, der ganz ähnlich funktioniert wie der [[retrieval# |
<code python> | <code python> | ||
Zeile 40: | Zeile 45: | ||
<code python> | <code python> | ||
def query_index(index, | def query_index(index, | ||
- | result_set = set() | ||
return index.get(query, | return index.get(query, | ||
query_index(toy_index, | query_index(toy_index, | ||
</ | </ | ||
+ | |||
#### Fragen | #### Fragen | ||
- Findet dein System einen Film über `' | - Findet dein System einen Film über `' | ||
Zeile 89: | Zeile 94: | ||
++++ | ++++ | ||
</ | </ | ||
+ | |||
+ | Wir sehen bereits: Einige Wörter (`antoine`) sind _wertvoller_ als andere (`film`), um eine Frage richtig zu beantworten. Im obigen Beispiel liefert das Token `film` überhaupt keine Information darüber, welches Dokument am besten zur Query passt, da es in jedem Dokument vorkommt. Wir werden das Problem später mit _Stop Wording_ behandeln. | ||
+ | |||
## Moar Data | ## Moar Data | ||
Zeile 98: | Zeile 106: | ||
Grössere Datasets gibt es auf https:// | Grössere Datasets gibt es auf https:// | ||
+ | |||
### Aufgabe 2 - Movie Index | ### Aufgabe 2 - Movie Index | ||
Erstelle einen Index aller WP-Artikel über Filme von 1985. Schreibe dazu eine Generator-Funktion, | Erstelle einen Index aller WP-Artikel über Filme von 1985. Schreibe dazu eine Generator-Funktion, | ||
Zeile 115: | Zeile 124: | ||
Wieviele Filme mit `' | Wieviele Filme mit `' | ||
- | |||
- | |||
## Stop Wording | ## Stop Wording | ||
Wenn du den erstellten Index untersuchst, | Wenn du den erstellten Index untersuchst, | ||
- | Normalerweise werden solche _Stop Words_ bereits bei der Indexierung ignoriert. Natürlich könnten wir eine Stop-Word-Liste aus dem Internet laden - aber eigentlich haben wir ja alle Informationen selber bereits zur Hand, um eine solche zu erstellen: Wir wissen ja für jedes Wort, in wievielen | + | Normalerweise werden solche _Stop Words_ bereits bei der Indexierung ignoriert. Natürlich könnten wir eine Stop-Word-Liste aus dem Internet laden - aber eigentlich haben wir ja alle Informationen selber bereits zur Hand, um eine solche zu erstellen: Wir kennen die Häufigkeit |
### Aufgabe 3 - Stop Words | ### Aufgabe 3 - Stop Words | ||
Zeile 153: | Zeile 160: | ||
Nehmen wir folgendes Beispiel: Wir indexieren alle Film-Artikel der 1980er Jahre und wollen möglichst gute Suchresultate für folgende Queries: | Nehmen wir folgendes Beispiel: Wir indexieren alle Film-Artikel der 1980er Jahre und wollen möglichst gute Suchresultate für folgende Queries: | ||
- | * '' | + | * '' |
+ | * '' | ||
+ | * '' | ||
Unsere Query-Funktion muss erstens mit mehreren Wörtern umgehen können - es bietet sich an, die `tokenize` Funktion auch hier anzuwenden und eine Suchanfrage für jedes Token in der Query durchzuführen. Die Frage ist allerdings, wie wir die einzelnen Resultatlisten kombinieren... | Unsere Query-Funktion muss erstens mit mehreren Wörtern umgehen können - es bietet sich an, die `tokenize` Funktion auch hier anzuwenden und eine Suchanfrage für jedes Token in der Query durchzuführen. Die Frage ist allerdings, wie wir die einzelnen Resultatlisten kombinieren... | ||
Zeile 162: | Zeile 171: | ||
Schreibe eine neue Query-Funktion, | Schreibe eine neue Query-Funktion, | ||
- | < | + | < |
++++Lösung| | ++++Lösung| | ||
Zeile 190: | Zeile 199: | ||
### Numerisches Ranking | ### Numerisches Ranking | ||
- | Die Schnittmenge scheint für die Query '' | + | Die Schnittmenge scheint für die Query '' |
- | #### Aufgabe 5: Ranking mit log IDF | + | |
- | Statt eine strikte AND-Verknüpfung aller Suchterme möchten wir ein Ranking erstellen, das alle Dokumente einschliesst, | + | |
- | Damit ein einzelner Wert nicht komplett dominiert, transformieren wir die IDF-Werte mit dem Logarithmus. | + | Die Vereinigungsmenge hingegen liefert sehr viele Resultate für eine Query wie '' |
- | {{:talit: | + | #### Wortfrequenzen |
+ | Zuerst werden die Inverse Document Frequencies in ein Dictionary überführt, | ||
+ | <code python> | ||
+ | word_frequencies = {word: freq for word, freq in freqs} | ||
+ | table = [[' | ||
+ | for token in tokenize(" | ||
+ | table.append([token, | ||
+ | |||
+ | %pip install tabulate | ||
+ | from tabulate import tabulate | ||
+ | tabulate(table, | ||
+ | </ | ||
+ | |||
+ | #### Inverse Document Frequency | ||
+ | |||
+ | Die erste Idee wäre, die Tokens nach ihrer _document frequency_ zu gewichten: Ein Wort, das nur in einem Dokument vorkommt, ist ein guter Hinweis dafür, dass dieses in die Resultatliste gehört. Ein Allerweltswort (soweit es noch nicht durch Stopwording herausgefiltert worden ist) ist hingegen kein sehr gewichtiges Indiz, dass ein es enthaltendes Dokument wichtig ist. | ||
+ | |||
+ | Wenn wir die Document Frequency plotten, sehen wir aber, dass die Frequenzen sehr ungleich verteilt sind: | ||
+ | |||
+ | <code python> | ||
+ | #!pip install matplotlib | ||
+ | |||
+ | def plot_terms(terms): | ||
+ | import matplotlib.pyplot as plt | ||
+ | import matplotlib as mpl | ||
+ | import math | ||
+ | |||
+ | labels = [x for x, y in terms.items()] | ||
+ | values = [y for x, y in terms.items()] | ||
+ | fig, ax = plt.subplots() | ||
+ | # | ||
+ | label_count = 10 | ||
+ | label_multiple = len(terms) // label_count | ||
+ | ax.xaxis.set_major_locator(mpl.ticker.MultipleLocator(label_multiple, | ||
+ | ax.set_xticklabels([None] + labels[0: | ||
+ | # | ||
+ | ax.plot(values) | ||
+ | |||
+ | plot_terms(word_frequencies) | ||
+ | </ | ||
+ | |||
+ | {{: | ||
+ | |||
+ | Das sieht nach einer exponentiellen Funktion aus ([[wpde> | ||
+ | |||
+ | <code python> | ||
+ | import math | ||
+ | log_terms = {term: math.log(1/ | ||
+ | print(f" | ||
+ | |||
+ | plot_terms(log_terms) | ||
+ | </ | ||
+ | |||
+ | {{: | ||
+ | |||
+ | #### Aufgabe 5: Ranking mit log IDF | ||
+ | Statt eine strikte AND-Verknüpfung aller Suchterme möchten wir ein Ranking erstellen, das alle Dokumente einschliesst, | ||
+ | |||
+ | <nodisp 1> | ||
+ | ++++Lösung| | ||
+ | <code python> | ||
+ | def query_index_weighted(index, | ||
+ | """ | ||
+ | result_set = dict() | ||
+ | for term in tokenize(query): | ||
+ | idf = log_terms[term] | ||
+ | results = index.get(term, | ||
+ | for result in results: | ||
+ | result_set[result] = result_set.get(result, | ||
+ | return dict(sorted(result_set.items(), | ||
+ | </ | ||
+ | ++++ | ||
+ | </ |