Leitfragen
Welche populären Ansätze zu deren Erfüllung gibt es? Was ist von ihnen zu halten?
Wann greift der Good-Turing-Schätzer? Was ist seine Grundidee, was muss erfüllt sein, damit sie gut ist?
ML-Schätzer sind notorisch schlecht für Sprachdaten, vor allem, weil nicht beobachtete Phänomene automatisch Wahrscheinlichkeit Null bekommen und beobachtete Phänomene überschätzt werden.
Wir diskutieren die Alternativen am Beispiel des Wörterzählens. Natürlich würde das ganz analog mit n-grammen oder was immer gehen.
Ein einfacher Trick ist adding one. Dabei berechnet man
Dabei ist N die Zahl der Wörter (word tokens) im Korpus, |wi| die Häufigkeit des betrachteten Wortes und B die Anzahl der word types (die ich hier mal ” Spezies“ nennen möchte, weil der Schätzer natürlich für alles mögliche funktioniert), die wir erwarten. Im Beispiel ist das schon etwas schwierig, denn die Anzahl verschiedener Wörter in einer Sprache ist keine zugängliche Größe (auch wenn ich argumentieren würde, dass sie finit ist). Einfacher ist das z.B. mit n-grammen über einer bekannten Anzahl grammatischer Kategorien o.ä.
Der Effekt ist, dass Wahrscheinlichkeit für die unbeobachteten Spezies reserviert wird. Beobachten wir etwa nur n < B verschiedene Wörter, so ist die Summe der Wahrscheinlichkeiten
Obwohl diese Variation des oben erwähnten ELE-Schätzers nicht so beliebig ist, wie es vielleicht scheinen mag (es ist das Ergebnis, wenn man nach Bayes schätzt und annimmt, dass alle Spezies a priori gleich wahrscheinlich sind), sind die Ergebnisse meist schlecht, zumal für beobachtete Spezies oft schlechter als ML-Schätzungen.
Besser ist der Good-Turing-Schätzer, der für typische Anwendungen der Computerlinguistik einen guten Kompromiss aus bias und mittlerem quadratischen Fehler bietet.
Die Situation: Wir haben s Spezies, die unabhängig voneinander mit Wahrscheinlichkeiten pi, i= 1,…,s beobachtet werden. Die Stichprobe besteht aus tatsächlich beobachteten Frequenzen r1,…,rs mit N= ∑ i=1sri.
Wir disktuieren hier ” Spezies“, weil, wie gesagt, bei weitem nicht nur Word Types in Frage kommen.
Wir fragen zunächst nach der Anzahl der gerade r Mal auftretenden Spezies, nr. Für diese lässt sich zeigen:
Der Beweis dieses Theorems ist zu aufwändig, als dass ich ihn hier breit diskutieren wollte – er braucht aber eigentlich keine schweren Geschütze. Ausgangspunkt ist dabei, dass die beobachteten Frequenzen mit pi Bernoulli-verteilt sind. Damit lässt sich E(nr) hinschreiben. Mit einigen Umformungen auch des Binomialkoeffizienten lässt sich der Beweis führen.
Übrigens verwende ich hier Sampsons Notation. Wir haben Schätzer bisher immer mit einem Zirkumflex geschrieben, die Größe oben
wäre dann also etwas wie , also ein Schätzer für die ” wahre“ Häufigkeit.
Für E(nr) könnten wir unsere Beobachtungen einsetzen. Für kleine r ist das auch gut (weil nr groß ist); für große r klappt das nicht mehr gut, weil nr klein oder gar Null wird.
Es ist die Lösung dieses Problems, das die Anwendung von Good-Turing im allgemeinen Rahmen etwas schwierig macht. Für praktisch alle interessanten Anwendungen in der Computerlinguistik gibt es aber eine recht einfache Lösung: Wir fitten Zipf’s Law auf die beobachtete Verteilung und schätzen E(nr) für kleine nr aus dem Fit. Das resultierende Verfahren wird meistens unter einem Titel wie Simplified Good-Turing (SGT) vorgestellt. Mehr darüber ist in dem sehr empfehlenswerten Buch ” Empirical Linguistics“ von Geoffrey Sampson zu erfahren.
Was ist ein Fit? Wenn man Beobachtungsdaten hat und vermutet, dass sie eigentlich durch eine Funktion einer bestimmten Form beschrieben werden sollten, versucht man, die Parameter der Funktion so zu bestimmen, dass sie die Beobachtung ” möglichst gut“ beschreiben, dass also ein irgendwie definierter Fehler möglichst klein wird. Wie wir das hier machen, sehen wir unten.
Ihr solltet euch wenigstens an den rötlich unterlegten Aufgaben versuchen
(1)
Zipf’s Law besagt in der Originalfassung, dass die Frequenz eines Wortes umgekehrt proportional zum Rang der Wortfrequenz ist (dabei hat das häufigste Wort Rang 1, das zweithäufigste Rang 2 usf). Wenn wir diesen Rang R nennen, heißt das, dass r= C∕R gelten muss, wobei r die Häufigkeit des R-t häufigsten Wortes und C eine irrelevante Konstante ist.
In der Tat will man solche Gesetzmäßigkeiten noch etwas allgemeiner haben und zulassen, dass am R noch eine Potenz steht, so dass die allgemeine Form des Gesetzes n= Cm-s ist – die Zipf-Beziehung oben ist dann der Spezialfall s= 1.
Zeigt, dass aus einem Gesetz wie n= Cm-s durch Ziehen des Logarithmus auf beiden Seiten der Gleichung ein Gesetz wie x= -sy+ z wird. Was sind x, y und z?
(2)
Holt euch von der Webseite das Programm checkZipf.py und, sofern ihr keine eigenen Texte ausreichender Länge habt, noch mar1810.txt. Werft einen kurzen Blick über das Programm. Unter der Zuweisung an fofDist (” Frequencies of frequency distribution“) stehen ein ganzer Haufen Funktionsaufrufe. Kommentiert alle bis auf den ersten aus.
Seht euch nochmal die Funktion getFoFDistribution an, bis ihr überzeugt seid, das sie tatsächlich ein Dictionary zurückgibt, in dem die Frequenzen die Schlüssel und die Frequenzen dieser Frequenzen als Werte stehen. Lasst sie euch dann per prettyPrintDist ausgeben.
Überprüft an ein paar Stichproben, ob rR wirklich in etwa konstant ist (wie Zipf’s Gesetz verlangt). Vorsicht: Die Verteilung, die ausgeben wird, ist r gegen nr, den Rang müsst ihr also auszählen (was aber nicht irre viel Arbeit ist, wenn man einmal gezählt hat, wie lang eine Spalte ist).
Auf Dauer ist das trotzdem mühsam, und drum gibt es die Funktion checkRankLaw. Seht sie euch an und lasst sie laufen. Versucht die Abweichungen von Zipf’s Law, die ihr (wahrscheinlich) seht, zu beschreiben.
(3)
Eine alternative Formulierung von Zipf’s Law (die so auch gern als Benford’s Law bezeichnet wird), ist nr = C∕rs mit s≈2. Da wir in unserer Behandlung des Good-Turing-Schätzers mit Sampson argumentieren und also mit Frequenzen und nicht mit Rängen arbeiten wollen, wird das die Formulierung sein, die wir verwenden. Die Funktion checkFreqLaw gibt nrr2 nach Rang aus. Versucht, die offensichtlichen Abweichungen von der Erwartung wieder zu kommentieren. Vielleicht wollt ihr auch etwas am Exponenten von freq in checkFreqLaw rumspielen, um der Erwartung eines flachen Verlaufs näher zu kommen (richtig flach werdet ihr es wohl nicht hinkriegen, eine ” Badewanne“ bleibt meistens).
(4)
Nach der Vorübung müsste log nr über log r aufgetragen eine Gerade sein, wenn die Behauptung aus der letzten Aufgabe stimmt(e), etwa mit Steigung -2. Um das zu sehen, wollen wir Plots machen. checkZipf ist auf eine Zusammenarbeit mit gnuplot vorbereitet – ich empfehle euch, euch das Programm, das auf allen auch nur irgendwie diskutablen Maschinen kostenlos verfügbar ist, zu installieren, wenn ihr es nicht ohnehin schon habt.
Die Funktion printGnuplotFile erzeugt Eingaben für Gnuplot aus einer Liste von Python-Werten (die Details sind nicht so interessant). Ihr seht zwei Aufrufe von printGnuplotFile am Ende des Skripts. Der erste erzeugt einen Plot von nr gegen r in linearem Maßstab. Seht ihn an und beschließt, dass ihr nichts erkennt.
Probiert danach den zweiten aus. Ihr solltet über weite Bereiche eine Gerade erkennen können. Hat sie in etwa Steigung zwei? Woher kommt das Auseinanderstreben der Punkte, wenn wir uns log nr = 0 nähern? Wie ist der Zusammenhang mit den Problemen, die wir oben festgestellt haben?
Technische Anmerkung: Am Bequemsten könnt ihr mit checkZipf und gnuplot ohne weitere Tricks arbeiten, indem ihr zwei Fenster offen haltet. In einem ruft ihr etwas wie checkZipf.py mar1810.txt > topl.gnu auf – damit ist in topl.gnu ein Skript, das gnuplot verarbeiten kann (vorausgesetzt, ihr habt alles außer dem einen printGnuplotFile-Aufruf wieder auskommentiert).
Im anderen Fenster habt ihr dann Gnuplot offen und könnt darin load "topl.gnu" sagen und solltet den Graphen sehen.
(5)
Erweitert das Programm, so dass es mehr als eine Datei verarbeiten kann und sammelt so viel Text wie ihr könnt. Seht euch die Kurven für möglichst große Datensammlungen an.