Satzarten bestimmen

Auch Fragen wie “ist ministerarbeit schwer?” gibt seltsames aus: “ja, minerarbeit ist schwer.”

String satzMitte = frage.replaceAll((satzBeginn) + "|" + (satzEnde), "");

Das Problem ist, dass ReplaceAll eben alles ersetzt. ReplaceFirst hilft auch nicht: “ja, ministerarbeit schwerist schwer.”

Was gaebe es fuer eine Alternative?

Nicht dass ich wüßte, aber solche Sachen kann man sich relativ leicht als static utility methods selbst bauen.

Nebenbei, nochmal: Es gibt da viele ausgefeilte Modelle dafür. Mit reinem String-Matching und dem Umstellen von Worten wird man da nicht sooo weit kommen. Allerdings kenne ich mich damit auch nicht im Detail aus, nur aus einem Kapitel (Kap. 22) aus Artificial Intelligence: A Modern Approach , wo das mal kurz angerissen wurde… gerade genug, um zu merken, dass man, selbst wenn man das Kapitel durchgearbeitet hat (was ich nichtmal getan habe) nur an der Oberfläche gekratzt hat :wink: Aber vielleicht hilft es, um die richtigen Stichworte zu finden: Dieses Buch wird in nahezu allen Vorlesungen verwendet, wo es irgendwie um KI geht :wink: Ansonsten wäre schon die Frage interessant, was da am Ende rauskommen soll (also „wenn es fertig ist“ :smiley: )

@Marco13 schau dir mal das an. http://vimeo.com/53221560

Ab Minute 41. Was 40 Sekunden später dabei rauskommt ist sehr sehr amüsant.

Da baut einer einen Chat-Bot in Go. Dafür werden Markov Ketten verwendet. Kurz: Zu einer Eingabe werden aus alten Antworten auf ähnliche Eingaben, eine möglichst wahrscheinliche Antwort generiert/gesucht.

Vielleicht sagt er das vorher oder nachher, aber… das sieht jetzt nicht aus, als ginge es darum, das Eingegebene wirklich zu verstehen. FALLS ich das richtig interpretiere wird dieser Bot nie etwas sagen, was nicht vorher schonmal jemand gesagt hat. Bei den Verfahren aus dem verlinkten Buch geht es eher in die Richtung eines Aufbaus einer Wissensbasis über die Welt und so… (ist lange her, müßte es mir nochmal durchlesen…)

Ja, das siehst du mehr oder weniger richtig.

Wenn du auf eine riesige Wissensbasis hast die

{(Eingabe) -> Antwort}

mappt, dann brauchst du es aber auch nicht verstehen.

Es ist ein Selbstlernendes System. Je länger der Bot sich mit jemandem “Unterhält”, desto schlauer wird er. Irgendwann fängt er an selbst Fragen zu stellen und bekommt dann neue Antworten, die er dann wieder verwenden kann.

Wenn du das Manuell auseinanderfriemeln wolltest, dann schreibst du dir einen Ast.

In der Präsentation geht es auch eher um die Netzwerkfähigkeiten von GO. Websockets und so.

Aber es spricht ja auch nichts dagegen, beides miteinander zu verbinden. In AIMA werden in Kapitel 20 Markov Models besprochen und in Kapitel 23. Natural Language for Communication, gibt es einen Punkt Comparing context-free and Markov models.

Es muß ja, so wie ich das im Moment sehe, nicht nur Verstanden werden was gefragt wird, sondern auch die passende Antwort gefunden und formuliert werden.

Hallo,

zur Zeit versuche ich mich daran die Sätze zu unterteilen, damit das Programm Sätze wie “Hallo. Wie geht es Dir?” Einzeln beantworten kann. Doch gerade hänge ich an einem Problem mit Abkürzungen. Mein Plan ist eine Art Replace über eine Liste. So etwas muesse doch mittels Regular Expressions funktionieren oder? Denn eine nicht mehr übersichtliche Liste mit unzähligen Abkürzungen scheint mir nicht praktikabel.

Vielen Dank im Voraus.

eine Liste ist eine Liste,
ob 10 oder 1000 Elemente drin, das sollte der Übersichtlichkeit nicht schaden,

Ersetzung klingt eher nach einfachen String-Möglichkeiten, nicht unbedingt Regular Expressions,
aber denkbar schon, wenn alles so allgemein gesprochen,

an Code und Beispielen wird es sich letztlich klären

Hallo SlaterB,

vielen Dank fuer Deine Antwort. Vezeih, wenn ich mich nicht exakt ausgedrueckt habe.

Ich moechte eine Eingabe (Z.B. „Hubertus wird Prof. Dr. fuer Keksologie! Das ist evtl. nicht schlecht u. bringt Geld.“). Dafuer verwende ich den BreakIterator. Der funktioniert bei Satzzeichen hervorragend und sofern nach den Punkt Klein geschrieben wird, erkennt er es sogar als Satz. Doch folgender Code:

String eingabe = "Hubertus wird Prof. Dr. fuer Keksologie! Damit ist er evtl. Einzigartig.";    
        
     BreakIterator iterator = BreakIterator.getSentenceInstance(Locale.GERMAN);
        iterator.setText(eingabe);
        int start = iterator.first();
            for (int end = iterator.next();
                end != BreakIterator.DONE;
            start = end, end = iterator.next()) {
                System.out.println(eingabe.substring(start,end));
            }

Gib das folgende aus:

Hubertus wird Prof.
Dr. fuer Keksologie!
Damit ist er evtl.
Einzigartig.

Meine Ueberlegung war es nun, den Ausgangsstring String eingabe zuvor durch eine zweite Klasse laufen zu lassen, die alle Abkuerzungen durch die richtigen Worte ersetzt.

Die erste Moeglichkeit die mir eingefalle ist, ist das Standartmaessige eingabe.replaceAll, doch

String eingabe1 = eingabe.replaceAll("a.", "auch");
...
String eingabe390 = eingabe.replaceAll("zzgl.", "zuzüglich");

Waere wohl nicht umbedingt ein sinnvoller Programmierstil. Daher bemuehte ich Google und fand immer wieder Hinweise darauf, dass dies mit Regular Expressions zu loesen sei. Jedoch fand ich nach ca. 2 Stunden rumgegoogle nichts Konkretes fuer meine Fragestellung. Dies bedeutet im Umkehrschluss, dass Regular Expressions entweder nicht das richtige ist, dass ich nicht richtig googlen kann oder das mir das richtige Suchstichwort fehlt.

Aber nicht unüblich. Abkürzungen sind einfach ein Knackpunkt. Der TreeTagger macht das mWn auch mit statischen Listen.

packe alle Kombinationen in eine Liste (also eine Java-Liste, ArrayList, bekannt?), und per Schleife jede Kombination abarbeiten,

replaceAll() verwendet sogar RegEx, der Punkt steht in diesem Fall für beliebige Zeichen,
insofern immer noch ohne RegEx stark zu bedenken, das normalere replace() etwa,
oder auch

..  = eingabe.replaceAll(Pattern.quote("a."), "auch");

bei zig einzelnen Kommandos wäre es unschön, auf diese Weise noch komplizierter zu werden,
bei Schleife mit nur einer Code-Zeile ist das fast egal


bedenke auch Sätze wie
“Hubertus wird Prof. Damit ist er evtl. einzigartig.”

zwei Punkte hintereinander werden kaum kommen,
ist leider nicht leicht, sondern Sprache

Hallo SlaterB,

leider kann ich Deiner Erklaerung nicht folgen. Koenntest Du es mir anschaulicher erklaeren?

for (Ersetzung e : listeMitErsetzungen) {
   eingabe = eingabe.replace(e.kurz, e.lang);
}

immer noch die Frage: kennst du eine ArrayList?
darin Objekte vom Typ Ersetzung, eigene neue Klasse, die zwei Strings je gespeichert,
statt for-each geht auch normalere Schleife mit i-Index,

das Einfügen in die Liste ist freilich auch noch immer eine Zeile pro Paar:
liste.add(new Ersetzung("“a.”, “auch”);
liste.add(…

kürzer gehts nicht recht, außer mit neuen Umwegen wie ein langes Array oder schlicht ein String
“a. auch zzgl. zuzüglich …”
welches selber nach Regeln durchgearbeitet wird, je zwei Wörter bilden ein Paar

Ich habe mir auf Deine letzte Antwort hin die Erklaerung zu ArrayLists durchgelesen, bin allerdings nicht so recht daraus schlau geworden. Aber jetzt habe ich es durch Dein Beispiel, glaube ich, verstanden. Ich danke Dir.

Bei den Ersetzungen, sortiere sie nach Länge. Ersetze erst die langen.

Sonst wird vielleicht erst ein Teil einer längeren Abkürzung ersetzt.

a. -> auch finde ich problematisch.

An der sogenannten Satztrennung (das meint genau das, was ist ein Satzende, was ist ein Abkürzungs- TausenderPunkt, Akronym…) hat hier bei uns in der Firma auch jemand sehr lange gesessen, dennoch macht sie nicht immer alles richtig.

Auch können normale Worte am Satzende wie eine Abkürzung wirken, aber keine sein.

wenn man Regeln einbauen will wie ‚vor der Kurzform ein Leerzeichen oder Klammer Anfang des Strings‘,
damit eben nicht a. in ca. ersetzt wird, dann hilft RegEx doch wieder gut mit

aber bitte nicht mich fragen ‚und wie genau?‘, ich will nicht alles erzählen :wink:

Hallo,

mittlerweile bin ich ein kleines Bisschen weiter gekommen. Allerdings nicht sehr. Ich wollte ersteinmal die Problem loesen die bei mehr als einem Satz auftreten. Das Programm sollte jetzt eigentlich auf jeden Satz den entsprechenden Antwortsatz liefern. Doch leider will das nicht so wie ich das moechte. Er liefert zwar die entsprechende Anzahl an Saetzen zurueck, jedoch nur in der Satzart des letzten Satzes. Was ist in meinem Code falsch?

public class Satzarten {
    
    public static void main (String[] args){
        
        String eingabe = "Ich mag Kekse. Wie geht es Dir?";
        String sentence;
        
        //ToDo auf Namen Checken
         String eingabeBereinigt = Wortignorierungen.unnoetigeWoerter(eingabe);
         String text = Wortersetzung.abkuerzungen(eingabeBereinigt);
         //ToDo Saetze Umbrechen - Probleme beseitigen.
         //ToDo Saetze zusammenfuegen
         
          BreakIterator breaker = BreakIterator.getSentenceInstance();
        breaker.setText(text);
        int index = 0;
        while (breaker.next() != BreakIterator.DONE) {
            sentence = text.substring(index, breaker.current());
                           System.out.println(satzart(sentence, eingabe));
            index = breaker.current();         
                }

    }   
   static String satzart(String sentence, String eingabe){
              
       String satzZeichen = eingabe.substring(eingabe.length()-1);
       String[] satzteile = eingabe.split("\\s");
       String satzAnfang = satzteile[0];
                             
       Collection<String> wFragen= Arrays.asList("wessen", "wer", "wie", "welche", "was", "wem", "wann", "wo", "wohin", "weshalb", "wieso", "warum", "weswegen", "inwiefern", "inwieweit");
       Collection<String> verb= Arrays.asList("geh\u00f6rt", "ist", "hat" );
       
       String satzOhneZeichen = eingabe.replaceAll("[\\\\.,-/]", "");
       
       String satzart;
              
       if (eingabe.equals(satzAnfang)){
                satzart = Wort.wort(satzOhneZeichen);
        }
        
        else if (satzZeichen.equals("?")){
        
               if (wFragen.contains( satzAnfang )){
                 satzart = Frage.ergaenzungsfrage();
                }
                else {
                 satzart = Frage.jaNeinFrage(eingabe);  
                }
        }
        else 
        {
            if (verb.contains( satzteile[1] )){  
                satzart = Aussage.aussage(satzAnfang); 
             }
            else if ((verb.contains( satzAnfang ))) {
                         
            satzart = Aufforderung.aufforderung(satzAnfang);
            }
            else {
            satzart = Wunsch.wunsch(satzAnfang);    
            } 
             
      }
       return satzart;
     }
}

PS: Gibt es irgendwie die Moeglichkeit das Arrray anstatt es mittels System.out auszugeben wieder als einen String zurueck zu geben und diesen weiterzuverarbeiten?

Auf die Inhalte eines Arrays greifst du über den Index zu. Um aus einem String-Array z.B. einen String zu basteln empfiehlt sich eine Schleife und ein StringBuffer der die einzelnen Strings zusammenpackt.

Danke timbeau.

Doch mein Kernproblem ist, dass mein Programm die Ausgabe nicht so macht wie ich es moechte.

Eigentliche sollte er den Text in Saetze zerlegen und dann fuer jeden Satz die entsprechende Ausgabe ausgeben. Zerlegen tut er es dann zwar, die Satzanzahl ist immer richtig, jedoch gibt er nur die Satzart des erste Satzes aus (dies dann allerdings in der Anzahl der Gesamtsaetze). Kannst Du mir sagen wo im Quelltext mein Fehler ist?

der Code ist nicht ausführbar, es fehlen viele Klassen,
ihn im Kopf durchzugehen ist eher mühsam,

du kannst alles mit Log-Ausgaben vollpflastern, Fehler zu finden ist einfach,
zunächst die Schleife mit BreakIterator, wie oft wird sie durchlaufen, welche Indexe, welche Substrings, untersuche das,

dann jeden Ablauf der satzart-Methode untersuchen, durchaus auch in einem zweiten Test mit jedem Satz nur einzeln,
bzw. mit beiden Sätzen ohne Schleife, ohne Gesamtstring, hintereinander aufgerufen

funktioniert es einzeln, aber zwei Sätze hintereinander gehen schief? dann werden vielleicht Informationen vom ersten Durchlauf gemerkt,
alles Dinge, die du herausfinden kannst

letztlich aber schlicht Log in der Methode, welches if wird betreten, welches return kommt letztlich dran, was sind die Entscheidungen dazu,
direkt davor oder bisschen weiter zurück, notfalls jeden Schritt in der Verarbeitung beleuchten

du solltest eigentlich froh sein, noch so ein überschaubares abgeschlossenes kleines Programm zu haben,
ohne Datenbank oder Datei von Festplatte, gar Netzwerk, ohne mehrere Threads und viele noch schlimmere Dinge,

jeden Millimeter, jede Entscheidung des Programmes kannst du komfortabel beobachten, mache das auch

Hallo SlaterB,

jetzt muesste der Code ausfuehrbar sein.

Der BreakIterator allein gibt die Saetze richtig aus, wenn ich System.out.println(satzart(sentence, eingabe)); in
System.out.println(sentence);; aendere teilt es mir die beiden Saetze und gibt sie mir einzeln aus. Wenn ich Nur einen Satz verwende, gibt mir das Programm die entsprechende Antwort. Dies geht also auch. Es muss also irgendwo beim durchlaufen von String satzart zu dem unerwuenschten Verhalten kommen.

Ich komme nicht darauf wo der Fehler ist.


package Main;

import java.util.*;
import java.text.BreakIterator;
import java.util.*;

public class Satzarten {
    
    public static void main (String[] args){
        
        String eingabe = "ist Panama weit weg? wo ist es?";
        
        String text = eingabe;
        
        String sentence;
        
       
         
          BreakIterator breaker = BreakIterator.getSentenceInstance();
        breaker.setText(text);
        int index = 0;
        while (breaker.next() != BreakIterator.DONE) {
            sentence = text.substring(index, breaker.current());
                           System.out.println(satzart(sentence, eingabe));
            index = breaker.current();         
                }

    }   
   static String satzart(String sentence, String eingabe){
              
       String satzZeichen = eingabe.substring(eingabe.length()-1);
       String[] satzteile = eingabe.split("\\s");
       String satzAnfang = satzteile[0];
                             
       Collection<String> wFragen= Arrays.asList("wessen", "wer", "wie", "welche", "was", "wem", "wann", "wo", "wohin", "weshalb", "wieso", "warum", "weswegen", "inwiefern", "inwieweit");
       Collection<String> verb= Arrays.asList("geh\u00f6rt", "ist", "hat" );
       
       String satzOhneZeichen = eingabe.replaceAll("[\\\\.,-/]", "");
       
       String satzart;
              
       if (eingabe.equals(satzAnfang)){
                satzart = "Wort";
        }
        
        else if (satzZeichen.equals("?")){
        
               if (wFragen.contains( satzAnfang )){
                 satzart = "Ergenzungsfrage";
                }
                else {
                 satzart = "Ja/Nein";  
                }
        }
        else 
        {
            if (verb.contains( satzteile[1] )){  
                satzart = "Aussage";
             }
            else if ((verb.contains( satzAnfang ))) {
                         
            satzart ="Aufforderung";
            }
            else {
            satzart = "Wunsch";    
            } 
             
      }
       return satzart;
     }
}