Archive for the 'Java' Category

POS-Tags und Lemmatisierung

Richtig zufrieden bin ich mit dem Porter-Stemmer-Algorithmus nicht. Er erkennt den gemeinsamen Wortstamm sehr gut, aber so habe ich es mir nicht vorgestellt. Er macht aus z.B. Töchterchen, Tochter, Töchter den Stamm tocht, und aus Brüder, Bruder, brüderlich, brüderlicherseits den Stamm brud, aber aus sehen macht er seh und aus sahen und sah wird sah. Ich wünsche mir aber Worte, die ich auch im Wörterbuch nachschlage und wenn ich nach sah oder sahen suche, schaue ich unter sehen. Oder wenn ich nach sprechen oder gesprochen oder sprach suche, schaue ich unter sprechen. Diese Grundformen nennt man aber Lemma und es existieren Programme, nämlich Lemmatisierer, um Wörter auf ihre Grundform, ihre Wörterbuchform, zurück zu führen. Außerdem will ich es jetzt wirklich wissen und mich interessieren auch die POS (Part of Speech)-Tags eines Satzes. Diese Tags sagen mir, ob ein Wort ein Nomen, ein Name oder eines von 10 Arten von Verben oder ein bestimmtes Adjektiv oder Adverb ist. Jetzt, wo ich schon am Thema NLP (Natural Language Processing) bin, kann ich mir das Gebiet ja auch zu Nutze machen. Als POS-Tagger, die für deutsche Sätze funktionieren habe ich nur den Stanford POS-Tagger und den IMS Tree Tagger gefunden. Nur der IMS Tree Tagger vermag es aber, mir auch das Lemma eines Wortes zu geben.

Im nachfolgenden Codebeispiel, zerlegt mir der Stanford POS-Tagger einen Satz in Tokens gibt dann für jedes Token dessen POS-Tag aus. Die Quellcodebeispiele sind alle in Java, welches die vorherrschende Sprache im NLP zu sein scheint. Es muss wohl doch keine so langsamen Sprache sein. Allerdings erledigen alle Programme, die ich gefunden habe, die Arbeit statisch. Sie sind nicht in andere Software eingebunden, sondern ein Text wird durch NLP-Software rechenintensiv verarbeitet und mit dem Ausgabetext kann man dann machen, was man will.

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;

import edu.stanford.nlp.ling.Sentence;
import edu.stanford.nlp.ling.TaggedWord;
import edu.stanford.nlp.ling.HasWord;
import edu.stanford.nlp.tagger.maxent.MaxentTagger;

class TaggerDemo {

  public static void main(String[] args) throws Exception {

    MaxentTagger tagger = new MaxentTagger("models/german-hgc.tagger");
	String s = "Aber alle Griechen nahmen Sosthenes, den Synagogenvorsteher, mit sich und schlugen ihn vor dem Richterstuhl. Und Gallio kümmerte sich um keinen dieser (Vorgänge).";
	InputStream is = new ByteArrayInputStream(s.getBytes());
	BufferedReader br = new BufferedReader(new InputStreamReader(is));

    List<List<HasWord>> sentences = tagger.tokenizeText(br);

    for (List<HasWord> sentence : sentences) {
      ArrayList<TaggedWord> tSentence = tagger.tagSentence(sentence);
      System.out.println(Sentence.listToString(tSentence, false));
    }
  }

}

Die Ausgabe:
Aber/KON alle/PIDAT Griechen/NN nahmen/VVFIN Sosthenes/NE ,/$, den/ART Synagogenvorsteher/NN ,/$, mit/APPR sich/PRF und/KON schlugen/VVFIN ihn/PPER vor/APPR dem/ART Richterstuhl/NN ./$.
Und/KON Gallio/NE kümmerte/VVFIN sich/PRF um/APPR keinen/PIAT dieser/PDAT -LRB-/TRUNC Vorgänge/NN -RRB-/TRUNC ./$.

Schade, dass es für den Stanford POS Tagger keinen Lemmatisierer gibt. Den Tokenizer, welcher mir Sätze in Tokens zerlegt kann ich aber gebrauchen. So etwas muss ich ja nicht selbst programmieren. Im nachfolgenden Codeschnipsel tut der IMS Tree Tagger seinen Dienst und gibt sowohl Lemmas als auch POS-Tags aus.

import static java.util.Arrays.asList;

import org.annolab.tt4j.TokenHandler;
import org.annolab.tt4j.TreeTaggerWrapper;

public class tt4j {
        public static void main(String[] args) throws Exception {
                // Point TT4J to the TreeTagger installation directory. The executable is expected
                // in the "bin" subdirectory - in this example at "/opt/treetagger/bin/tree-tagger"
                System.setProperty("treetagger.home", "/home/bileser/Downloads/IMSTreeTagger/tree-tagger-linux-3.2");
                TreeTaggerWrapper<String> tt = new TreeTaggerWrapper<String>();
                try {
                        tt.setModel("/home/bileser/Downloads/IMSTreeTagger/tree-tagger-linux-3.2/german.par:iso8859-1");
                        tt.setHandler(new TokenHandler<String>() {
                                public void token(String token, String pos, String lemma) {
                                        System.out.println(token + "\t" + pos + "\t" + lemma);
                                }
                        });

                        String[] list = new String[] {
                        		"Und",
                        		"Gott",
                        		"sah",
                        		",",
                        		"dass",
                        		"es",
                        		"gut",
                        		"war",
                        		"."

                        };

                        tt.process(asList(list));
                }
                finally {
                        tt.destroy();
                }
        }
}

Die Ausgabe:
Und KON und
Gott NN Gott
sah VVFIN sehen
, $, ,
dass KOUS dass
es PPER es
gut ADJD gut
war VAFIN sein
. $. .

Durch POS-Tags und Lemmatisierung habe ich vor, Lückentexte zu generieren und rechnerisch die wichtigsten Sätze von Texten anhand der Häufigkeiten der Lemmas von Nomen, Verben und anderen Satzbestandteilen zu ermitteln. Zu den Themen automatische Generierung von Zusammenfassungen oder Fragen findet man im Netz allerhand Papers. So tief will ich denn in die Wissenschaft nicht eintauchen.

SGF Player mit Java und Batik – 2

Schönheit First

Mich interessiert zuerst, wie ich in SVG ein äußerlich passables Go-Brett mit Steinen hinbekomme. Im Netz finde ich ein mit Inkscape erzeugtes welches. Der SVG-Code ist gruselig: Die Koordinaten für die Gitterpunkte des Brettes haben etliche Nachkommastellen und die Datei enthält viel zu viele Knoten, welche ich nicht verstehe und nicht brauche. Unverständlicher, generierter Code, den ich nicht programmatisch manipulieren möchte, da ich an die Koordinaten und Knoten schlecht heran komme. Also schreibe ich mir meinen SVG-Code anhand von W3C-Spezifikation und Beispielen selber. Als erstes erzeuge ich mir mit dem SVG-Attribut ViewBox ein eigenes Koordinatensystem. Dieses Koordinatensystem lässt sich als Auflösung auffassen. Ich sage damit, wie groß meine Auflösung immer ist, egal, wieviel Platz ich tatsächlich zur Verfügung habe. Das macht mein SVG-Dokument skalierbar.
Für das Holzbrett zeichne ich nun ein Rechteck in hellbraun. Da ich unbedingt eine Art Holzmaserung sehen will und andernfalls nicht auf das Brett gucken kann, definiere ich mir für das Rechteck einen Filter, der aus den Effekten feTurbulence und feDiffuseLighting besteht.
Das Gitter zu zeichen ist trivial, doch das mache ich lieber in Java mit einer for-Schleife, da ich dann alle Abstände zentral ändern kann, statt 19 mal 19 Linien händisch zu programmieren. Um die Steine zu zeichnen definiere ich mir für schwarze und weiße Steine jeweils einen radialGradient-Knoten und einen Filter für den Schatten. Nachdem ich probeweise einige Steine gezeichnet habe, sehen diese zwar ganz reizend aus, aber wenn der Schatten zu groß ist, fällt er über den benachbarten Stein. Ich finde auf Anhieb keine Lösung, bin aber damit zufrieden, wenn der Schatten nur 1 Pixel breit ist und dieser bei einem Abstand eines Steines vom nächsten von ebenfalls 1 Pixel nur noch aufs Brett fällt.
Nun fange ich an, für ein Spiel Stein für Stein aufs Brett zu zeichnen, indem ich die ‘Logik’ ausblende. Die Logik besteht daraus, dass auch Steine vom Brett genommen werden müssen, wenn ein gesetzter Stein eine Gruppe gegnerischer Farbe berührt und dieser die letzte Freiheit nimmt. Ich möchte am liebsten pro Zug alles neu darstellen und alle Steinknoten dem SVG Dokument neu hinzufügen. Doch je mehr Steine ich hinzufüge, desto länger dauert es. Da die Verzögerung deutlich erkennbar ist, brauche ich eine Lösung. Im Netz kursiert der Vorschlag, vom SVG-Dokument ein Fragment abzuspalten und alles nötige zuerst dem Fragment anzuhängen, bevor das Fragment als Ganzes in das Dokument eingefügt wird. So müsse die Darstellung des Dokuments nur einmal gerechnet werden. Leider schafft auch dieser Vorschlag keine wesentliche Abhilfe. Deshalb gebe ich jedem Steinknoten eine eindeutige ID und füge nur einen neuen Knoten hinzu, wenn es ihn noch nicht gibt.

Logik

Da die Darstellung der kleinen Anwendung nun flüssig läuft und schön aussieht, kann ich mich nun der Logik zuwenden. Ein Zug verändert den Zustand des Brettes. Ein Zustand ist vom vorherigen Zustand abhängig. Wie kann ich es erreichen, dass ich wahllos zu irgendeinem Brettzustand eines Spiels springen kann, z.B. Zug n? Ich muss für Zug n den Zustand aus den voran gegangenen n-1 Zuständen errechnen. Meine eigenen Gedanken zum Thema Freiheiten von Steinen berechnen und diese Steine nahtlos vom Brett entfernen, erscheinen mir sehr kompliziert und ich schaue mir den offenen Quellcode von qgo an. Dieser Code ist in C++ geschrieben und ich finde sofort, was ich brauche. In dem qgo-Modell existieren Gruppen von Steinen. Beim Setzen eines Steins wird geprüft, ob er einer Gruppe angehört. Dabei wird Stein für Stein geschaut, ob der neue Stein von der Position her ein nördlicher, südlicher, westlicher oder östlicher Nachbar sein könnte. Gehört er zwei Gruppen an, werden die zwei Gruppen zu einer Gruppe verbunden und der Stein hinzugefügt. Gehört er keiner Gruppe an, wird aus ihm eine neue gebildet. Weiter wird pro Zug der Stand der Freiheiten der Gruppen anhand des Brettzustands berechnet. Hat mindestens ein Stein einer Gruppe einen benachbarten Punkt, der weder von einem weißen noch einem schwarzen Stein besetzt ist, besitzt die Gruppe Freiheit. Hat die Gruppe keine mehr, wird sie gelöscht, nachdem Stein für Stein dieser Gruppe vom Brett genommen wird. Es wird also pro Zug allerhand berechnet, aber das geht immer noch schneller, als ich klicken kann.
Zum Schluß hätte ich gerne noch alle 40.000 Spiele, die ich in einer viele MB großen Datei beherberge, in einer übersichtlichen Tabelle. Dort möchte ich Spiele anhand von Spielernamen, Turnier und Datum filtern können. Dies habe ich mir schon einmal mit F# in .NET programmiert. Aber wie ich festellen muss, sind es in Java mehr Zeilen und geht nicht so leicht von der Hand. Die string.split() Funktion in Java ist nämlich viel zu langsam, da sie einen regulären Ausdruck als Argument erwartet. Zum parsen des Strings, der alle Spiele enthält, muss ich ihn char für char begutachten, mir Positionen von Spieltrennern merken und pro Spiel einen Substring aus dem Gesamtstring extrahieren. Auch das läuft nach einigen Optimierungen flüssig, so dass der Import nur ein paar Sekunden dauert.

So sieht der Player aus:
SGF_Player_Batik_Swing

Sogar mein 4-jähriger Sohn kann auf diese Weise Profi-Partien nachspielen! Züge können in der geschwindigkeitsverstellbaren Animation äußerst gut gefunden werden, statt in einem PDF-Dokument, welches 50 Züge als Zahlen beinhaltet, suchen zu müssen.

SGF Player mit Java und Batik – 1

Ich schaute mir ja so gerne Amatuerpartien von hochrangigen Go-Spielern auf dem KGS-Server an. Doch jetzt schaue ich mir, seit dem ich mir einen SGF Player entwickelt habe, lieber Profi-Partien an. Als ich mit einem Kollegen über mein Vorhaben sprach, war sein erster Rat: SVG. Dieses Format findet demnächst seinen Weg in den Internet Explorer 9 und in den Firefox ist es jetzt schon integriert. Erste Erkundungen waren beeindruckend. SVG öffnet einen Weg, Spiele in den Browser zu bringen, indem SVG-Inhalte, die sich im DOM befinden, über Javasript manipuliert werden können. Doch wie viele Enwtickler machte ich um Javascript bisher einen weiten Bogen. Lange gab es für die Entwicklung mit Javascript keine IDE, was sich jetzt eigentlich geändert haben dürfte. Aber eine neue Technologie, nämlich SVG, ist mir erst einmal genug. Ich will Schritt für Schritt vorgehen und nur eine neue Sache auf einmal lernen. Und schließlich muss es nicht direkt ins Web. Mein Blick fällt auf die Bibliothek Batik, welche die Manipulation von SVG mittels der mir vertrauten Sprache Java ermöglicht. Das Beispiel sieht denn auch sehr vielversprechend aus: Es ist sogar möglich, ein Applet zu entwickeln, welches sehr wohl übers Web ausgeführt werden kann.
Als nächstes entscheide ich mich für die NetBeans IDE und binde die Batik-Archive in mein Projekt ein. NetBeans behagt mir auf Anhieb. Die Bedienung ist sehr ähnlich zum Visual Studio. Im Gegensatz zur C++-Unterstützung von Visual Studio kann ich hier jedoch sehr bequem Klassen oder das Projekt umbennennen, Code refaktorisieren und fehlende Importe einfügen. Da ich jetzt doch wieder einen Haufen neuer Technologien auf einmal vorfinde, will ich zunächst die Batik Beispielanwendung in eine Swing-Anwendung überführen.
Das Ergebnis:

Batik-Beispiel in Java Swing - klein

Batik-Beispiel in Java Swing - klein


Batik-Beispiel in Java Swing - groß

Batik-Beispiel in Java Swing - groß

Das Ergebnis stellt mich sehr zufrieden und entspannt lehne ich mich zurück. Mit Java unter Linux bin ich zufrieden. Swing schnurrt schnell und Anwendung und IDE, die ebenfalls in Java programmiert ist, laufen flüssig. Das SVG-Dokument ist interaktiv über Java manipulierbar und die Manipulation verläuft ohne Flackern. Das Ergebnis ist frei skalierbar, ohne, dass ich hierfür Aufwand betreiben müsste. Die Technologien sind gefunden, um meinen ersten SGF-Player zu verwirklichen.