Im ersten Teil habe ich über meinen Werdegang aus beruflicher Sicht geschrieben und bin darauf eingegangen, wie ich mich von einem einfachen Studenten zu einem erfolgreichen Mitarbeiter bei INNOQ entwickelt habe. In diesem zweiten Blogpost werde ich nun auf die Thematik meiner Masterarbeit eingehen, wobei der Fokus auf die für die Umsetzung wichtigen Teile der Arbeit gesetzt wird. Für eine ausführliche Beschreibung ist dabei auf die Masterarbeit zu verweisen.

Domain und Kontext der Arbeit

Das Thema der Arbeit ist die Erläuterung und Anwendung der agilen Softwareentwicklung mit dem Test-Driven Development (TDD) für die Entwicklung einer Conversational AI. Conversational AI ist dabei der akademische Begriff für Programme, welche umgangssprachlich meist als Chatbots bezeichnet werden. Softwareentwicklungsmethoden stellen einen wichtigen Aspekt in der Softwareentwicklung dar, da die Entwicklung von Software für Unternehmen oft mit einem großen finanziellen Aufwand verbunden ist. Daher besteht ein Bedarf an standardisierten Vorgehensweisen, wie dem TDD, da diese eine simple, strukturierte und zügige Bearbeitung der Projekte erleichtern. Die Anpassung der Softwareentwicklungsmethoden an Machine Learning (ML) ist daher auch ökonomisch sinnvoll, da ML immer häufiger in Software verwendet wird (vgl. [1]). Die Problematik bei dieser Anpassung ist im Wesentlichen, dass das TDD für die Entwicklung von gleichbleibenden, eher deterministischen Systemen ausgelegt ist. Dem gegenüber stehen die ML-Systeme, welche eine sich verändernde und statistische Natur aufweisen (vgl. [2]).

Ich habe mich für den Bereich Conversational AI entschieden, da mit einer Conversational AI eine Automatisierung im Kundensupport möglich ist, was zu einer zusätzlichen finanziellen Erleichterung für Unternehmen führen kann (vgl. [3]). Im praktischen Teil der Arbeit wurde daher ein Chatbot für eine Applikation, den Reisekosten-Gorilla, entwickelt, um dessen Kundensupport zu verbessern. Für den Reisekosten-Gorilla wurde ein Chatbot entwickelt, welcher Nutzer und Nutzerinnen mit natürlicher Sprache dabei unterstützt eine Reisekostenabrechnung zu tätigen. Dieser wurde mit dem Test-Driven-Development (TDD) umgesetzt, was den Kern der Masterarbeit darstellt. Die akademische Fragestellung bestand daraus zu beantworten, ob und wie das TDD anwendbar ist, um eine Conversational AI zu entwickeln. Hierbei galt es zu analysieren, welche Anpassungen gemacht werden müssen und wie sich diese auf die Vor- und Nachteile des TDD auswirken. Der Reisekosten-Gorilla stellt auf der Website ein Formular bereit, welches ausgefüllt direkt an die zuständige Abteilung des Unternehmens geschickt werden kann. Um den Prozess der Abrechnung zu erleichtern, wurde ein Chatbot implementiert, welcher einen Leitfaden für die Erstellung einer Abrechnung gibt, Fragen beantwortet und eine schnelle Abwicklung der Abrechnungen ermöglicht.

Grundlagen

Um den Chatbot mit TDD umzusetzen, wurden die folgenden 3 Themenbereiche behandelt:

  • Das Testen einer Conversational AI, da das Testen eine zentrale Rolle im TDD einnimmt
  • Das Test-Driven-Development, da dies die zentrale Methodik bei der Umsetzung ist
  • Grundlagen der Conversational AI, da dies dem umgesetzten Programm entspricht

Diese drei Grundlagen werden in den folgenden Abschnitten erklärt:

Testen

Beim Testen von Machine Learning und somit auch einer Conversational AI müssen einige Besonderheiten beachtet werden. Der Grund für diese Besonderheiten ist häufig die veränderliche Natur der ML-Modelle. Diese ist durch das notwendige regelmäßige neue Trainieren der Modelle bedingt, da ML-Modelle sonst in den Concept Drift fallen. Der Concept Drift beschreibt eine Veränderung in der Verteilung der Daten, welche mit der Zeit eintreten kann. Durch diesen Drift wird ein Modell immer schlechtere Prognosen liefern, da das Modell auf einer anderen Datenverteilung trainiert wurde. Durch diese Notwendigkeit stellt das Modell keinen festen, gleichbleibenden Bestandteil des Systems dar. Somit wird auch das Testen zu einer dynamischen Instanz, die es erschwert Tests zu definieren, welche die Funktionalität dieses Teils des Systems gewährleisten können. Gleichzeitig können die Tests für eine Funktionalität keine absolute Sicherheit gewährleisten. Dies ist auf die statistische Natur der ML Modelle zurückzuführen. Da ein ML-Modell nach keinem festen Schema arbeitet, sondern jeden Input individuell verarbeitet, können keine Rückschlüsse von einem Input auf einen anderen gezogen werden. Dies ist ein Unterschied zum normalen Testen, da hierbei ein Test eine Funktionalität meist durch ein einzelnes Beispiel zuverlässig testet. In einem ML-System herrschen auch mehr Abhängigkeiten als in einem normalen Softwaresystem. Eine herkömmliche Software ist meist nur abhängig vom implementierten Code. Ein ML System ist hingegen abhängig vom Code, von den Daten und nicht zuletzt auch vom gewählten Modell.

Ein weiterer Unterschied kommt durch die Anwendung von Conversational AI. Beim Testen einer Conversational AI reichen auch die traditionellen Metriken des ML, wie eine Accuracy, zum Bewerten der ML-Modelle nicht aus. Ein Beispiel hierzu ist ein Modell mit 99%iger Genauigkeit, welches jedoch das Wort “Hallo” nicht versteht. Durch diese Fehlklassifikation könnte eine Konversation nicht gestartet werden, weshalb die anderen 99% nutzlos werden. Daher ist eine Genauigkeit als alleinige Metrik nicht ausreichend. Die Accuracy kann um diesen Fehler zu vermeiden mit empirischem Testen ergänzt werden, sodass ein Verständnis der am häufigsten verwendeten Inputs gewährleistet wird. Dies wird dann durch die allgemeine Genauigkeit unterstützt, sodass sowohl die wichtigsten Inputs sicher verstanden werden als auch eine allgemein gute Vorhersage gewährleistet werden kann.

Zu den Besonderheiten beim Testen zählen daher:

  • Das sich verändernde Modell
  • Einzelne Tests, welche nicht die gesamte Funktionalität gewährleisten können
  • Mehrere Abhängigkeiten, wie der Code, die Daten und das Modell
  • Die Accuracy, welche im NLP nicht ausreicht

Das Test-Driven-Development (TDD)

Das TDD folgt den zwei Grundregeln:

  • Schreibe neuen Code nur, wenn ein fehlgeschlagener Test diesen erfordert
  • Löse Duplikationen auf

Die erste Regel erfordert dabei das Markenzeichen des TDD: Die Tests vor dem funktionalen Code zu schreiben. Daher wird im TDD immer mit einem Test begonnen. Jeder Test wird dabei in einem sich wiederholenden Zyklus bearbeitet. Dieser Zyklus besteht aus den drei Phasen Red, Green und Refactor, welche in der folgenden Abbildung veranschaulicht sind:

Abb. 1: Der Zyklus im TDD
Abb. 1: Der Zyklus im TDD

In der Red Phase wird der zu bearbeitende Test gewählt und implementiert. In der Green Phase wird dann, da nun ein fehlschlagender Test existiert, der zugehörige Code geschrieben, der den Test besteht. Dabei wird ein Fokus darauf gelegt den Test mit so wenig Aufwand wie möglich zu bestehen. Zuletzt wird in der Refactor Phase der Code aufgeräumt, sodass dieser den Coding Konventionen entspricht, was in der Green Phase ignoriert werden durfte.

Man kann also die folgenden Ziele der Phasen festhalten:

  • Red-Phase: Einen fehlschlagenden Test implementieren, welcher eine neue Funktionalität testet.
  • Green-Phase: Den Code so anpassen, dass der Test besteht, wobei so wenig Aufwand wie möglich aufgewendet wird.
  • Refactor: Den Code aufräumen, sodass dieser den Coding Konventionen entspricht. In diesen Phasen können verschiedene Patterns angewendet werden, um diese Ziele zu erreichen. Diese Patterns helfen z. B. dabei einen passenden Test in der Red-Phase zu wählen oder bei der Umsetzung des Tests im Code.

Grundlagen der Conversational AI

Im Allgemeinen beschreibt eine Conversational AI ein durch KI gestütztes automatisiertes Dialogsystem. Ein solches System wird meist als Chatbot bezeichnet. Die Entwicklung dieser Chatbots reichen dabei, abhängig von den Anforderungen, von simplen Benachrichtigungsbots bis hin zu personalisierten, animierten Avataren, welche sich über ein beliebiges Thema unterhalten können. Eine Übersicht der verschiedenen Arten der Chatbots ist in der folgenden Abbildung gegeben:

Abb. 2: Die verschiedenen Arten von Chatbots, Quelle: 4
Abb. 2: Die verschiedenen Arten von Chatbots, Quelle: 4

Im Allgemeinen erfüllt eine Conversational AI die folgenden zwei primären Funktionen:

  • Das Erkennen der natürlichen Sprache
  • Das Bestimmen der Aktionen des Chatbots

Für diese Tasks werden dabei zwei unterschiedliche ML-Modelle verwendet:

  • Das NLU-Modell zum Erkennen der natürlichen Sprache
  • Das Dialogmodell zum Bestimmen der unternommenen Aktionen

Bei diesen Modellen handelt es sich in vielen state-of-the-art Modellen um Sequenzmodelle, welche in sog. Transformern umgesetzt werden. Transformer sind dabei eine Art der neuronalen Netze, die vor allem zwei Vorteile gegenüber den bisher verwendeten Recurrent Neural Networks (RNNs) bieten: Zum einen, dass diese neuronalen Netze während des Trainings parallelisierbar sind, weshalb sie eine deutlich schnellere Trainingszeit aufweisen. Zum anderen können sie durch ihren Aufbau leichter zwischen relevanten und nicht relevanten Inputs unterscheiden. Die RNNs können diese Unterscheidung ebenfalls erlernen, jedoch wird hierzu meist eine sehr große Datengrundlage und langes Trainieren benötigt. Für eine genauere Beschreibung, wie Transformer funktionieren und welche Vorteile sie dadurch bieten wird an dieser Stelle auf die Masterarbeit verwiesen.

Wie durch Abbildung 2 deutlich wird, können mit Conversational AI viele Tasks erfüllt werden. Vor allem im Bereich des Kundensupports bietet Conversational AI viele Anwendungsmöglichkeiten. Die Verwendung von Conversational AI ist dabei ökonomisch lohnenswert, da Unternehmen daraus einen großen finanziellen Nutzen ziehen können. Ein wichtiger Nachteil bei der Verwendung von Chatbots in diesem Bereich ist jedoch, dass das Unternehmen bei der Verwendung mit ihrer Reputation dafür einstehen müssen. Dies bedeutet, dass ein Unternehmen bei der Entwicklung eines Chatbots eine große Aufmerksamkeit darauf legen sollte, dass die Conversational AI ordnungsgemäß funktioniert, da ein unzureichender Chatbot einen oftmals öffentlichen negativen Einfluss auf das Unternehmen haben wird. Um einen solchen Fehlschlag zu vermeiden, müssen unter anderem die bereits beschriebenen Besonderheiten beim Testen einer Conversational AI eingehalten werden.

TDD für die Entwicklung einer Conversational AI

Kommen wir nun zum praktischen Teil der Arbeit: Dem Anpassen und Anwenden des TDD für die Entwicklung einer Conversational AI. Hierfür mussten die Besonderheiten des ML in Einklang mit dem Vorgehen des TDD gebracht werden. Bei dem vorgestellten Vorgehen wurde sich an dem klassischen Zyklus des TDD orientiert, um somit möglichst viele der Vorteile des TDD beizubehalten. Um das Vorgehen zu beschreiben, werde ich ein kleines Beispiel aus der Thesis verwenden und anhand dessen auf die Besonderheiten und Anpassungen eingehen.

Der Chatbot hatte die folgenden Anforderungen:

  • Der Chatbot ermöglicht eine geführte Reisekostenabrechnung
  • Der Chatbot erklärt Fachbegriffe
  • Der Chatbot erlaubt Korrekturen vor dem Einreichen einer Abrechnung
  • Der Chatbot ermöglicht eine schnelle Abrechnung

Um mit dem Zyklus des TDD anzufangen, wird ein erster Test benötigt. Hierfür bietet sich an, eine Anforderung zunächst in kleinere, einzelne Funktionalitäten und diese dann in einzelne Tests zu splitten. Um also z. B. die Anforderung einer geführten Reisekostenabrechnung zu erfüllen, müssen mehrere aufeinander aufbauende Funktionalitäten erfüllt werden:

  1. Chatbot mit grundlegenden Intentionen, wie Begrüßen, Verabschieden, Bestätigen, Verneinen und Bedanken
  2. Eine Intention, um den Task zu triggern
  3. Die Funktion, um eine Abrechnung zu erstellen

Diese Funktionalitäten können nun durch einzelne Tests gewährleistet werden. Sollte das noch nicht möglich sein, kann dieses Aufteilen wiederholt werden, bis es dem Entwickler als machbar erscheint. Um z. B. die Funktionalität der grundlegenden Intentionen aufzuteilen, wird die folgende Test-Liste erzeugt:

  • MFTs für:

    • Begrüßungen
    • Verabschiedungen
    • Bestätigungen
    • Verneinungen
    • Bedanken
  • End-to-End Tests für Dialoge:

    • Begrüßung – Vorstellung, kann ich helfen? – Ja – Platzhalter, vielen Dank – Gerne, bis zum nächsten Mal – Verabschiedung
    • Begrüßung – Vorstellung, kann ich helfen? – Nein – Okay, bis zum nächsten mal – Verabschiedung

Diese Tests werden dann im Zyklus des TDDs bearbeitet. In der Red-Phase wird dabei das Ziel verfolgt einen fehlschlagenden Test zu erzeugen, für den beim Testen der Conversational AI sogenannte Minimum Functionality Tests (MFTs) verwendet werden. MFTs sind dabei empirische Tests, welche eine Menge von Daten gegen einen bestimmten Output testen, um somit das minimale Verständnis des Modells zu gewährleisten. In der Green-Phase wird daraufhin sowohl der Code, als auch das Modell angepasst und die Daten bereinigt. Das Ziel der Green-Phase ist dabei ein beliebiges, aber valides Modell zu finden, welches alle Tests besteht. Zuletzt wird dann in der Refactor-Phase das gesamte System optimiert. Nachdem ein valides Modell in der Green Phase gefunden wurde, wird nun beim Refactoring der geschriebene Code, aber auch die behandelten Daten und die Konfiguration bzw. auch Auswahl des Modells optimiert. Da dies ein sehr umfangreicher Schritt sein kann und ein Zyklus im TDD nur ein paar Minuten dauern soll, kann dieser Schritt verkürzt werden, wenn es die Situation zulässt. Anstatt alle möglichen passenden Modelle zu optimieren, kann lediglich das bereits gefundene Modell optimiert werden, um somit ein möglichst gutes Modell für den nächsten Zyklus zu finden. Hierdurch kann ein flüssigerer Arbeitsfluss gewährleistet werden. Das ausführliche Optimieren wird dann z. B. zu den Meilensteinen vorgenommen, sodass das Modell zu diesen bestimmten Zeitpunkten einen optimalen Stand aufweist.

Nachdem diese ersten drei Funktionalitäten erfüllt werden, wird ein erster Meilenstein erreicht, da der Chatbot in der Lage ist eine Abrechnung zu tätigen. Der Konversationsverlauf der mit diesem Meilenstein erreicht wird, ist dabei an typische Konversationen gekoppelt, welchen der Chatbot in seiner Produktionsumgebung begegnen wird. Diese Konversation besteht aus einer Begrüßung, worauf der Chatbot mit einer Vorstellung reagiert. Darauf folgt die Frage, wie er behilflich sein kann, worauf ausgedrückt werden kann, dass man eine Abrechnung tätigen will, welche daraufhin ausgeführt wird. Nachdem eine Abrechnung getätigt wurde, bedankt sich der Bot und schließt die Konversation mit einer Verabschiedung ab. Diese Konversation beinhaltet einige grundlegende Intentionen, wie z. B. Begrüßungen, Verabschiedungen, Bestätigungen, Verneinungen und Bedanken, welche in den meisten Konversationen vorkommen. Mit diesen Intentionen der ersten Funktionalität wird daher ein allgemeines Grundkonstrukt der Konversationen geschaffen. Die zweite Funktionalität, dem Einführen des Triggers von dem Task, erweitert dieses Grundkonstrukt, um die Möglichkeit einen Platzhalter auszuführen, welcher anstelle der Abrechnung ausgeführt wird. In der dritten Funktionalität, dem konkreten Anlegen von Abrechnungen, wird dann die richtige Funktionalität hinter den Platzhalter gebaut, um sich somit mit dem Chatbot unterhalten zu können, mit dem Ziel eine Abrechnung zu tätigen. Wenn diese Funktionalitäten erfüllt werden, wird auch diese Anforderung erfüllt und der Entwickler kann sich den weiteren Anforderungen widmen.

Fazit der Arbeit

Das Ergebnis der Arbeit ist, dass der Chatbot erfolgreich mit dem vorgestellten Verfahren umgesetzt werden konnte. Um das Fazit der Arbeit in Kurzform wiederzugeben, werde ich auf die wichtigsten Vorteile eingehen, welche mir bei der Umsetzung stärksten aufgefallen sind. Ein wichtiger Vorteil bei der Nutzung des TDD ist die strukturierte Arbeitsweise. Durch das klare schrittweise Aufteilen von Anforderungen in Funktionalitäten und diese in einzelne Tests, liefert das Vorgehen vor allem unerfahrenen Programmierern einen sehr strukturierten Arbeitsplan nach dem sie sich richten können. Ein weiterer Vorteil ist die variable Schwierigkeit. Dadurch, dass der Programmierer selbst entscheiden kann, wie lange er die Aufteilungen der Funktionalitäten macht, kann dieser auch selbst entscheiden, wie umfangreich ein Zyklus wird. Somit kann dieser die Schwierigkeit, bis zu einem gewissen Grad, individuell anpassen. Der wohl offensichtliche Vorteil ist das sorgenfreie Testen. Dadurch, dass die Tests beim TDD vor dem funktionalen Code geschrieben werden, müssen diese nicht im Nachhinein implementiert werden. Ein daraus resultierender Vorteil ist, dass die Tests auch während der Implementierung Auskunft darüber liefern können, ob der geschriebene Code funktioniert, was dem Programmierer Sicherheit gibt, ob der aktuelle Code funktioniert. Ein weiterer Vorteil liegt in der Kommunikation. Durch die Fokussierung auf Tests kann anhand dieser ohne ML-spezifisches Vorwissen kommuniziert werden, sodass die Erfüllung programmspezifischer Anforderungen vor allem bei ML-basierten Anwendungen besser koordiniert werden kann.

  1. Z. Carmon, K. Wertenbroch, H. Yang, and R. Schrift, “Designing ai systems that customers won’t hate,” MIT Sloan Management Review, 12 2019.  ↩

  2. J. M. Zhang, M. Harman, L. Ma, and Y. Liu, “Machine learning testing: Survey, lands– capes and horizons,” IEEE Transactions on Software Engineering, 2020.  ↩

  3. X. Luo, S. Tong, Z. Fang, and Z. Qu, “Frontiers: Machines vs. humans: The impact of artificial intelligence chatbot disclosure on customer purchases,” Marketing Science, vol. 38, no. 6, pp. 937–947, 2019.  ↩

TAGS