Suche
Teil 1: Sentence Component

Machine Learning Tutorial: Wie wir Maschinen die natürliche Sprache lehren

Masoud Amri

© Shutterstock / josefkubes

Im heutigen Alltag ist das Thema Human Machine Interaction aktueller denn je. Sei es am Bank- oder Ticketautomaten, beim Autofahren, beim Carsharing, beim Smart Home oder bei Apps auf Smartphones, Tablets, Wearables, Notebooks oder Spielekonsolen. Immer wieder kommt es zum Dialog zwischen Mensch und Maschine. Das Problem dabei ist, dass Menschen sich normalerweise in gesprochener oder geschriebener natürlicher Sprache unterhalten und Maschinen über APIs und Datenprotokolle.

Seit den 1960er Jahren wird zum Thema Künstliche Intelligenz intensiv geforscht, um den Austausch zwischen Mensch und Maschine in natürlicher Sprache zu ermöglichen. Gerade in der Spracherkennung gibt es heute sehr gute Algorithmen zur Umwandlung von akustischen Signalen in Text. Das Problem des maschinellen Verstehens und Lernens basierend auf natürlicher Sprache ist aber zu großen Teilen immer noch ungelöst. Die hier vorgestellte Lösung basiert auf einem Konzept, das wir Cogniology genannt haben. Wir meinen damit die Wissenschaft des kognitiven Verstehens und Lernens. Cogniology umfasst zwei miteinander verbundene Komponenten: einer Wissensbasis, bestehend aus Regeln und damit verbundenen Fakten, sowie einem Metaregelwerk, das definiert, wie aus vorhandenen Regeln und Fakten neue Regeln und Fakten entstehen. Sowohl Regeln als auch Metaregeln basieren dabei auf natürlicher Sprache.

Artikelserie
Teil 1: Sentence Component
Teil 2: Natürliche Datentypen (Natural Data Types)
Teil 3: Maschinelle Schlussfolgerung
Teil 4: Maschinelles Lernen und Verstehen
Teil 5: Programmierung in natürlicher Sprache I
Teil 6: Programmierung in natürlicher Sprache II

 

Durch ein Beispiel aus der Praxis möchten wir Schritt für Schritt die Anwendung der Technologie demonstrieren. Hierfür entwickeln wir einen Digitalassistenten für Zahnärzte. Der Digitalassistent soll während der Behandlung der Konversation zwischen Zahnarzt und Patienten folgen und sich die Behandlungsleistungen merken. Nach der Behandlung soll er aus den Behandlungsleistungen eine Rechnung oder einen Heil- und Kostenplan erstellen und per E-Mail an den Patienten senden. Zudem sollte er in der Lage sein, aus der Konversation zwischen Zahnarzt und Patienten herauszufinden, welchen Termin der Zahnarzt mit dem Patienten für die weitere Behandlung vereinbart hat. Der Termin soll in den Kalender eingetragen und per E-Mail an den Patienten gesendet werden. Dieses Beispiel werden wir in diesem und weiteren Artikeln ständig vertiefen.

Beginnen wir mit einem einfachen Hello-World-Beispiel. Damit wir unsere erste Sentence Component realisieren können, brauchen wir eine Klasse. Unter Package de.meine-firma erstellen wir eine neue Klasse und geben ihr einen Namen, z. B. Greeting. Wir fügen dann eine Methode mit dem Namen helloWorld ein und annotieren sie mit @Sentence. Als Argument der Annotation geben wir Hallo die Welt ein. Wenn wir soweit sind, sieht unsere Klasse wie in Listing 1 aus.

 
public class Greeting
{
  @Sentence("Hallo die Welt.")
  public String helloWorld()
  {
return "Die Welt grüßt dich!";
  }
}

Klassen- und Methodenname spielen dabei keine Rolle. Wichtig ist die Annotation @Sentence. Innerhalb der Annotation muss ein grammatikalisch korrekter Satz eingegeben werden.

Nun ist unsere Sentence Component soweit fertig. Sie muss lediglich als JAR-Datei exportiert und auf www.cognogy.com hochgeladen werden. Um die Sentence Component zu exportieren, wählen wir auf der Webseite im Menü Demo aus. Dort sehen wir eine Schaltfläche Upload Component zum Hochladen der JAR-Datei. Wir laden unsere JAR-Datei hoch und schreiben im Bereich Request Hallo die Welt. Anschließend klicken wir auf Run. Im Bereich Respond müssten wir Die Welt grüßt dich! sehen.

Abb. 1: Die Sentence Component in Aktion

Geschafft! Nun wissen wir, wie eine Sentence Component erstellt und getestet wird und können uns mit unserem Beispiel einer Anwendung für die Zahnarztpraxis beschäftigen. Bevor wir mit der Umsetzung anfangen, müssen wir uns Gedanken darüber machen, wie der allgemeine Ablauf in einer Zahnarztpraxis ist. Nehmen wir folgendes Szenario an:

Arzt (Dr. König): Guten Morgen, Frau Wagner, wir haben ja heute einen langen Tag vor uns.
Helferin: Das stimmt, aber das schaffen wir schon.
Arzt: Wer ist der erste Patient?
Helferin: Herr Müller.
Arzt: Okay, können Sie ihn bitte hereinrufen? Guten Morgen Herr Müller, schön Sie zu sehen. Schön sonnig heute, der Frühling kommt so langsam in Schwung.
Patient: Guten Morgen Herr Dr. König, ja, das stimmt.
Arzt: So, dann schauen wir uns mal die Zähne an. Das sieht beim Eckzahn leider gar nicht gut aus. Da brauchen wir einen Termin für eine Füllung. Wann hätten Sie denn Zeit?
Patient: Oh weh, das hört sich nicht gut an. Aber muss ja wohl leider sein. Ich komme nächsten Montagmorgen. Ginge das?
Arzt: Alles klar, dann sehen wir uns also nächste Woche. Auf Wiedersehen und einen schönen Tag.

Wir als Menschen merken sofort, dass in diesem Szenario Frau Wagner die Zahnarzthelferin, Dr. König der Zahnarzt und Herr Müller der Patient ist. Für unseren Digitalassistenten sieht die Welt aber ganz anderes aus. Anhand der Stimme kann er nicht erkennen, ob der Sprecher ein Mann oder eine Frau ist, und auch nicht, ob es sich um Dr. König oder Herrn Müller handelt, wenn sich die Gesprächspartner begrüßen. Der Digitalassistent kann sie auch nicht sehen. Die einzige Möglichkeit ist es also, all diese Informationen aus dem Text, der über Voice-to-Text generiert wurde, herauszufinden. Die obige Konversation wird für den Digitalassistenten aus einer Folge von Wörtern bestehen:

Lesen Sie auch: Artificial Intelligence: Miracle or Menace – wie AI unsere Zukunft verändert [VIDEO]

Guten Morgen Frau Wagner, wir haben ja einen langen Tag vor uns heute. Das stimmt, aber das schaffen wir schon. Wer ist der erste Patient? Herr Müller. Okay, können Sie ihn bitte hereinrufen. Guten Morgen Herr Müller, schön Sie zu sehen. Schön sonnig heute, der Frühling kommt so langsam in Schwung. Guten Morgen Herr Dr. König, ja, das stimmt. So, dann schauen wir uns mal die Zähne an. Das sieht beim Eckzahn leider gar nicht gut aus. Da brauchen wir einen Termin für eine Füllung. Wann könnten Sie denn? Oh weh, das hört sich nicht gut an. Aber muss ja dann wohl leider sein. Ich komme nächsten Montagmorgen. Ginge das? Alles klar, dann sehen wir uns also nächste Woche. Auf Wiedersehen und einen schönen Tag.

Wie wir dieser Konversation entnehmen können, ist der Satz „Ich komme nächsten Montagmorgen“ ausschlaggebend für die Terminvereinbarung. Daher fangen wir damit an. Auf die anderen Sätze kommen wir später zurück. Zur Implementierung dieses Satzes erstellen wir eine neue Klasse mit dem Namen Terminvereinbarung und schreiben eine Methode createCalenderEntry mit der Annotation @Sentence „Ich komme nächsten Montagmorgen“.

public class Terminvereinbarung
{	
  @Sentence("Ich komme nächsten Montagmorgen.")
  public String createCalenderEntry()
  {
return  "Wir haben Ihnen in unserer Terminkalender einen Termin für “ +
“morgen um 9 Uhr 30 eingestellt.";
  }
}

Wenn wir daraus eine JAR-Datei machen und sie wieder hochladen würden, wissen wir nun schon, wie das Ergebnis aussehen würde, daher sparen wir uns die Mühe. Dem Satz kann man entnehmen, dass „nächsten Montagmorgen“ eine Zeitangabe ist.
Man kann natürlich nicht für jede Zeitangabe eine neue Sentence Component schreiben. Die Zeitangaben können in natürlicher Sprache in Hunderten verschiedener Varianten ausgedrückt werden. Um das zu verhindern, fügen wir in der Klasse Terminvereinbarung noch eine neue Methode für die Zeitangaben hinzu (Listing 3).

public class Terminvereinbarung
{
  Date datum = null;

  @Sentence("Ich komme an irgendeinem Datum.")
  public String createCalenderEntry()
  {
return "Wir haben Ihnen in unserer Terminkalender einen Termin für “ +
datum.toString();
“ eingetragen.";
  }

  @NaturalTypeMethod(naturalName = "an irgendeinem Datum", naturalType = "Datum")
  public void setDatum(Date datum){
    this.datum = datum;
  }
}

Mit der Annotation @NaturalTypeMethod kann man einen Teil des Satzes als Parameter definieren, in diesem Fall „an irgendeinem Datum“. Die Annotation naturalType gibt an, welchen natürlichen Datentyp dieser Parameter haben soll. Darüber hinaus haben wir den Satz „Ich komme nächsten Montagmorgen“ geändert in „Ich komme an irgendeinem Datum“. Es ist wichtig, dass der Satz in Normalform angegeben wird. Auch sollten z. B. keine Höflichkeitsausdrücke wie „bitte“, „danke“ oder „gern geschehen“ im Satz vorkommen. Der Satz muss einem präzisen Ausdruck entsprechen, der dem Sinn der Aussage entspricht. Die Plattform erkennt die Konjunktionen und Höflichkeitsausdrücke selbst und findet die richtige Übereinstimmung einer gesprochenen Aussage mit dem im @Sentence angegebenen Ausdruck.

Nun ist es soweit: Exportieren wir wieder die JAR-Datei und laden sie hoch. Wir geben dieses Mal den Satz aus unserem Szenario ein: „Ich komme nächsten Montagmorgen. Ginge das?“ Im Bereich „Response“ muss nun folgender Text erscheinen: „Wir haben Ihnen in unserem Terminkalender einen Termin für 2017-05-30 8:15 eingetragen.“

Was hat die Plattform gerade getan? Beim Hochladen der Sentence Component hat die Plattform gemerkt, dass ein Teil der Sentence Component als Parameter definiert ist. Sie merkt sich den natürlichen Datentyp dieses Parameters. Beim Vergleich des angegebenen Satzes mit dem in @Sentence annotiertem Satz werden zuerst die beiden Subjekte (ich) miteinander verglichen. Haben die beiden Sätze „Ich“ als Subjekt, wird mit dem Vergleich weiterer Merkmale fortgefahren. Als Nächstes kommt das Verb dran. Zuerst werden die beiden Verben in ihre Grundform konvertiert: Aus „komme“ wird „kommen“. Dann wird nach natürlichen Typen gesucht. In diesem Fall ist „nächsten Montagmorgen“ ein natürlicher Datentyp, also ein Datum. Das stimmt auch mit dem in @NaturalTypeMethod angegebenen natürlichen Datentyp Datum überein. So bleibt nichts mehr zu vergleichen, und alle Eingaben stimmen zu 100 Prozent mit dem Satz der Sentence Component überein. Danach führt die Plattform die Methode setDatum(Date datum) aus und weist das aus dem Ausdruck herausgenommene Datum als Datentyp Date der Methode zu. Die Methode speichert das Datum in der Variable datum. Die Plattform ruft anschließend die Methode createCalenderEntry() auf. Die Methode gibt dann den Text für den Eintrag in einem Terminkalender aus.

Der natürliche Datentyp im angegebenen Ausdruck „nächsten Montagmorgen“ entspricht nicht exakt dem technischen Datentyp Date. Wir Menschen machen bei unseren tagtäglichen Abläufen selten so exakte Angaben, wie ein Computerprogramm es von uns erwartet. In diesem Fall gibt der Ausdruck „nächsten Montagmorgen“ ein Datumsintervall an. Mit „Morgen“ ist z. B. eine Zeit zwischen 7:00 Uhr und 10:00 Uhr zu verstehen. Demnach müssen wir die Methode setDatum(Date datum) in setDatum(DateInterval datumBereich) ändern. Die Angabe des natürlichen Datentyps Datum bleibt unverändert, nur die technische Umsetzung ist anders (Listing 4).

public class Terminvereinbarung
{	
  Date datumStart = null;
  Date datumEnde = null;

  @Sentence("Ich komme an irgendeinem Datum.")
  public String createCalenderEntry()
  {
return "Wir haben Ihnen wunschgemäß einen Termin zwischen “ +
datumStart.toString() +
“ und ";
datumEnde.toString() +
“ erstellt. Ihr Termin ist: " +
datumEnde.toString();
  }

  @NaturalTypeMethod(naturalName = " an irgendeinem Datum", naturalType = "Datum")
  public void setDatum(DateInterval datumBereich){
    this.datumStart = datumBereich.from;
    this.datumEnd = datumBereich.to;
  }
}

Für Menschen ist es selbstverständlich, dass „morgen um 10 Uhr“ und „nächsten Montagmorgen“ jeweils Angaben zum Datum sind. Aus der Sicht der Computerprogramme sind sie jedoch verschiedene Datentypen. Wenn wir einen natürlichen Datentyp für unsere Sentence Component benötigen, der mehrere technische Varianten unterstützt, wählen wir einen passenden technischen Datentyp, der zu unseren Bedürfnissen passt. Die Plattform ruft unsere Methode mit einem passenden Wert auf. Auf die natürlichen Datentypen werden wir im nächsten Artikel ausführlich eingehen. Für den ersten Schritt werden wir uns mehr auf die Sentence Component selbst konzentrieren.

Wie bei den Datumsangaben formulieren Menschen ihre Absicht auf verschiedene Arten. Zum Beispiel drückt „Ich habe morgen Zeit“ eine ähnliche Absicht wie „Ich komme morgen“ aus. Aus der Sicht der deutschen Grammatik bestimmt das Verb die Absicht des Satzes. In der Terminologie der Cogniology bezeichnet man solche Sätze als synonyme Sätze, obwohl sie grammatikalisch völlig unterschiedlich sind. Somit ändert sich unsere Sentence Component wie in Listing 5.

public class Terminvereinbarung
{	
  Date datumStart = null;
  Date datumEnde = null;

  @Sentence
(
"Ich komme an irgendeinem Datum.",
    synonyms =
      {
        "Ich habe an irgendeinem Datum Zeit."
      }
)
  public String createCalenderEntry()
  {
return "Wir haben Ihnen wunschgemäß einen Termin zwischen “ +
datumStart.toString() +
“ und ";
datumEnde.toString() +
“ erstellt. Ihr Termin ist: " +
datumEnde.toString();
  }

  @NaturalTypeMethod(naturalName = "an irgendeinem Datum", naturalType = "Datum")
  public void setDatum(DateInterval datumBereich){
    this.datumStart = datumBereich.from;
    this.datumEnd = datumBereich.to;
  }
}

Um alle möglichen Fälle für eine Terminvereinbarung abzudecken, sollten wir in die Liste der synonymen Sätze eintragen, was uns als Synonym einfällt. Dabei müssen wir unbedingt darauf achten, dass der Parameteranteil des Satzes im synonymen Satz exakt wiedergegeben wird. Sonst können wir mit einer Fehlermeldung rechnen. Nun können wir alle möglichen Arten, die uns einfallen, eingeben und die Reaktion des Systems ansehen. Zum Beispiel folgende Sätze:

• Nächsten Donnerstag zwischen 8 Uhr 15 und 20 Uhr 45 hätte ich Zeit.
• Ich würde gerne ab Mitte Januar montags kommen.
• Immer Montagvormittag um 10 Uhr 30 kann ich.

Fazit

In diesem Artikel haben wir den Ansatz der Cogniology eingeführt und beschrieben, auf welche Weise wir eine konkrete Umsetzung der Regeln und Metaregeln in natürlicher Sprache, basierend auf Sentence Component, angehen. In den folgenden Artikeln werden wir schrittweise die weiteren nötigen Komponenten und Umsetzungen erläutern, um dem Ziel der kompletten Umsetzung des Cogniology-Ansatzes auf einer Java-basierten Plattform näherzukommen. In der nächsten Folge geht es konkret um die Umsetzung von natürlichsprachlichen Datentypen. Cogniology ermöglicht den Sentence Components, wie Polymorphismus in der objektorientierten Programmierung, abhängig von ihrer Verwendung unterschiedliche natürliche Datentypen anzunehmen.

Geschrieben von
Masoud Amri
Masoud Amri ist Informatiker mit über zwanzig Jahren Erfahrung als Softwareentwickler und Softwarearchitekt bei namhaften Unternehmen (IBM, Mercedes, Volksbank, Fraunhofer etc.) Er arbeitet seit 2012 an der Theorie und Umsetzung der Cogniology.
Kommentare

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht.