Dieses Tutorial beschreibt die Erstellung eines einfachen, regelbasierten Chatbots, der mithilfe eines Feedforward-Neuronalen Netzes trainiert wird. Ziel ist es, auf vordefinierte Eingaben passende Antworten zu generieren. Als Grundlage dienen sogenannte Intents – Muster von Eingaben und zugehörige Antwortmöglichkeiten. Dieser Chatbot ist wesentlich einfacher als ein LLM (Large Language Model, wie ChatGPT):
Vorteile:
+ Schnelles Training
+ Volle Kontrolle über die Antworten durch den Chatbot
(es sind keine Halluzinationen möglich, die bei kommerziellen Anwendung eventuell zu rechtlichen Problemen führen können)
+ Leichte Erweiterbarkeit durch Datenpflege ohne das Programmierfähigkeiten notwendig sind
Nachteile:
– Eine wirklich natürlich Kommunikation ist nicht möglich.
– Emergente Fähigkeiten wie die eines LLMs entstehen nicht, d.h. es kann nicht mit Ironie umgegangen werden oder neue Gedichte geschrieben werden
Die Verarbeitung von Texteingaben erfolgt mit Hilfe der Bibliothek NLTK (Natural Language Toolkit), insbesondere durch Tokenisierung und Stemming.
1. Setup: Import und NLTK vorbereiten
pythonCopyEditimport nltk
nltk.download('punkt_tab') # Punkt-Tokenizer herunterladen (wichtig für Worttrennung)
import random
import numpy as np
import tensorflow as tf
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense
from nltk.stem.lancaster import LancasterStemmer
stemmer = LancasterStemmer() # Initialisierung eines Stemmers
Erläuterung:
- NLTK: Eine umfangreiche Bibliothek zur Verarbeitung natürlicher Sprache.
- punkt bzw. punkt_tab: Ein Tokenizer-Modell zur Worttrennung (Tokenisierung) für verschiedene Sprachen.
- LancasterStemmer: Ein Stemming-Verfahren, das Wörter auf ihre Grundform reduziert, z. B. „geht“ → „geh“.
2. Definition der Intents (Absichten und Antworten)
pythonCopyEditintents = {
"intents": [
{"tag": "Begrüßung",
"patterns": ["Hallo", "Hi", "Guten Tag", "Servus", "Hey"],
"responses": ["Hallo!", "Guten Tag!", "Wie kann ich helfen?"]},
{"tag": "Verabschiedung",
"patterns": ["Tschüss", "Auf Wiedersehen", "Bis bald", "Mach's gut"],
"responses": ["Tschüss!", "Auf Wiedersehen!", "Bis zum nächsten Mal."]},
{"tag": "Hilfe",
"patterns": ["Was kannst du?", "Wobei hilfst du?", "Was machst du?"],
"responses": ["Ich beantworte einfache Fragen.", "Ich bin ein kleiner Chatbot zur Demonstration."]},
{"tag": "Witz",
"patterns": ["Erzähl mir einen Witz", "Witz", "Ich will lachen"],
"responses": ["Warum konnte der Computer nicht aufhören zu lachen? Weil er ein Byte genommen hat!",
"Was ist ein Keks unter einem Baum? Ein schattiges Plätzchen."]},
{"tag": "Wetter",
"patterns": ["Wie ist das Wetter?", "Wird es regnen?", "Ist es sonnig?"],
"responses": ["Ich bin leider kein Wetterdienst, aber schau mal aus dem Fenster!"]},
{"tag": "Zeit",
"patterns": ["Wie spät ist es?", "Welche Uhrzeit haben wir?", "Zeit?"],
"responses": ["Ich habe leider keine Uhr, aber dein Gerät bestimmt."]},
{"tag": "Name",
"patterns": ["Wie heißt du?", "Wer bist du?", "Was ist dein Name?"],
"responses": ["Ich bin ein einfacher Chatbot ohne Namen."]},
{"tag": "Dank",
"patterns": ["Danke", "Vielen Dank", "Ich danke dir"],
"responses": ["Gern geschehen!", "Immer gerne.", "Kein Problem!"]}
]
}
Erläuterung:
- Jeder Intent besteht aus Patterns (Eingaben des Users) und möglichen Responses (Antworten).
- Der Tag dient zur Klassifikation – das Modell lernt, welcher Tag zu einer Eingabe gehört.
Da der User normalerweise den Intent nicht buchstabengetreu eingibt, wird das Tokenizieren, Stemming und das Neuronale Netz benötigt. In dieser Kombination reicht es aus wenn der User seinen Eingabentext nur ungefähr so wie ein Pattern formuliert. Das neuronale Netz prüft dann welches Intent-Pattern dem Eingabetext am „ähnlichsten ist“ und gibt eine Antwort aus der Intent-Liste.
Dies ermöglicht eine natürliche Frage-Antwort-Kommunikation mit dem Chatbot (sofern es genügend Patterns gibt). Dazu lässt sich z.B. eine Intents-Datenbank integrieren, auf die ein Chatbot einer Firmenwebsite zugreift, der potentielle Kunden berät.
3. Textverarbeitung: Tokenisierung, Stemming, Daten vorbereiten
pythonCopyEditwords = [] # Wortliste für das Vokabular
classes = [] # Liste der Kategorien (Tags)
documents = [] # Paare aus Token-Liste und zugehörigem Tag
ignore_words = ['?', '!'] # Satzzeichen, die ignoriert werden
for intent in intents["intents"]:
for pattern in intent["patterns"]:
w = nltk.word_tokenize(pattern) # Tokenisierung in Wörter
words.extend(w) # Wörter zum Vokabular hinzufügen
documents.append((w, intent["tag"])) # Satz und Tag speichern
if intent["tag"] not in classes:
classes.append(intent["tag"])
# Stemming und Kleinbuchstaben
words = [stemmer.stem(w.lower()) for w in words if w not in ignore_words]
words = sorted(list(set(words))) # Duplikate entfernen, sortieren
classes = sorted(list(set(classes))) # Tags sortieren
Begriffe erklärt:
- Tokenisierung: Zerlegung eines Textes in Wörter (Tokens), z. B. „Wie geht es dir?“ → [„Wie“, „geht“, „es“, „dir“, „?“]
- Stemming: Rückführung eines Wortes auf den Wortstamm, z. B. „gegangen“, „geht“ → „geh“.
- Vokabular: Alle eindeutigen Wortstämme der Patterns.
4. Bag-of-Words-Modell erstellen, One-Hot-Encoding durchführen und Trainingsdaten vorbereiten
pythonCopyEdittraining = []
output_empty = [0] * len(classes) # Leere One-Hot-Vektoren
for doc in documents:
bag = []
pattern_words = [stemmer.stem(word.lower()) for word in doc[0]]
for w in words:
bag.append(1 if w in pattern_words else 0)
output_row = list(output_empty)
output_row[classes.index(doc[1])] = 1 # One-Hot-Encoding des Tags
training.append([bag, output_row])
random.shuffle(training)
training = np.array(training, dtype=object)
train_x = np.array(list(training[:,0])) # Eingabedaten
train_y = np.array(list(training[:,1])) # Ziel-Labels
Erläuterung:
Bag-of-Words (BoW) – Erklärung und Beispiel
Grundidee:
Jeder Satz wird in einen Vektor umgewandelt, der anzeigt, ob bestimmte Wörter vorhanden sind. Das Bag-of-Words-Modell ist eine der einfachsten Methoden, um Textdaten in eine für Maschinen lernbare Form zu bringen. Es ignoriert die Reihenfolge der Wörter und betrachtet nur deren Vorkommen.
Vorgehensweise:
- Alle eindeutigen Wörter aus dem gesamten Textkorpus werden gesammelt (das sogenannte Vokabular).
- Für jeden Satz/Text wird ein Vektor erstellt, dessen Länge der Anzahl an Wörtern im Vokabular entspricht.
- Jedes Element des Vektors steht für ein Wort im Vokabular und ist:
1
, wenn das Wort im aktuellen Satz enthalten ist.0
, wenn das Wort nicht enthalten ist.
Beispiel:
Gegeben sei folgendes Vokabular aus drei Trainingssätzen:
- Sätze (Patterns):
- Satz 1: „Hallo wie geht es“
- Satz 2: „Geht es gut“
- Satz 3: „Hallo gut“
Nach Tokenisierung und Stemming könnte das Vokabular folgendermaßen aussehen:
Satz 1 → Token: ["hall", "wie", "geh", "es"]
→ Bag-of-Words-Vektor: [0, 1, 1, 1, 1]
Satz 2 → Token: ["geh", "es", "gut"]
→ Vektor: [1, 1, 0, 0, 1]
Satz 3 → Token: ["hall", "gut"]
→ Vektor: [1, 0, 1, 0, 0]
One-Hot-Encoding – Erklärung und Beispiel
Grundidee:
One-Hot-Encoding ist eine Technik, um Kategorien (z. B. Tags, Klassen) in numerische Vektoren zu überführen. Es wird genau ein Element auf 1
gesetzt, der Rest bleibt 0
.
Beispiel:
Angenommen es gibt drei Intents:
pythonCopyEditclasses = ["Begrüßung", "Witz", "Verabschiedung"]
Das One-Hot-Encoding für diese Klassen ist:
- „Begrüßung“ →
[1, 0, 0]
- „Witz“ →
[0, 1, 0]
- „Verabschiedung“ →
[0, 0, 1]
Anwendung:
Im Chatbot werden alle Intents so codiert. Beim Training lernt das Modell, zu jedem Bag-of-Words-Vektor die passende One-Hot-Klasse vorherzusagen.
Beispiel: Trainingsdatensatz
Satz | Bag-of-Words-Vektor | One-Hot-Ziel (Tag) |
---|---|---|
„Hallo wie geht es“ | [0, 1, 1, 1, 1] | [1, 0, 0] |
„Erzähl mir einen Witz“ | [1, 0, 0, 0, 0] | [0, 1, 0] |
„Tschüss und bis bald“ | [0, 0, 0, 0, 0] | [0, 0, 1] |
(Hinweis: Die Vektoren sind nur exemplarisch. Tatsächlich enthält der Vektor alle Wörter aus dem Vokabular.)
5. Einfaches neuronales Netz mit TensorFlow erstellen und trainieren
pythonCopyEditmodel = Sequential()
model.add(Dense(8, input_shape=(len(train_x[0]),), activation='relu'))
model.add(Dense(8, activation='relu'))
model.add(Dense(len(train_y[0]), activation='softmax')) # Ausgabe = Wahrscheinlichkeiten
model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy'])
model.fit(train_x, train_y, epochs=1000, batch_size=8, verbose=1)
Modellstruktur:
- Dense-Schichten: Vollständig verbundene Neuronen, Aktivierungsfunktion ReLU.
- Softmax-Ausgabe: Wahrscheinlichkeitsverteilung über alle möglichen Tags.
- Training: Optimierung mit Adam, Verlustfunktion: kategorische Kreuzentropie.
6. Funktion zur Verarbeitung neuer Benutzereingaben
pythonCopyEditdef clean_up_sentence(sentence):
sentence_words = nltk.word_tokenize(sentence)
sentence_words = [stemmer.stem(word.lower()) for word in sentence_words]
return sentence_words
def bow(sentence, words):
sentence_words = clean_up_sentence(sentence)
bag = [0] * len(words)
for s in sentence_words:
for i, w in enumerate(words):
if w == s:
bag[i] = 1
return np.array(bag)
Zweck:
- Neue Sätze werden wie im Training verarbeitet: Tokenisierung → Stemming → Bag-of-Words-Vektor.
- Der Vektor wird dem Modell übergeben, das dann einen Tag vorhersagt.
7. Chatfunktion zum Testen des Chatbots
pythonCopyEditdef chat():
print("Start Chatbot (Tippe 'exit' zum Beenden)")
while True:
inp = input("Sie: ")
if inp.lower() == "exit":
print("Chatbot: Tschüss!")
break
input_data = bow(inp, words)
results = model.predict(np.array([input_data]))[0]
results_index = np.argmax(results)
tag = classes[results_index]
for intent in intents["intents"]:
if intent["tag"] == tag:
responses = intent["responses"]
print("Chatbot:", random.choice(responses))
chat()
Nun können Nutzeranfragen, die nur ungefähr den Patterns entsprechen, durch den Chatbot beantwortet werden:
Sie: hallo
Chatbot: Guten Tag!
Sie: was kannst du?
Chatbot: Ich beantworte einfache Fragen.
Sie: wie heißt du?
Chatbot: Ich bin ein einfacher Chatbot ohne Namen.
Sie: wie ist name
Chatbot: Ich bin ein einfacher Chatbot ohne Namen.
Sie: servus
Chatbot: Hallo!
Sie: welche uhrzeit haben wir jetzt
Chatbot: Ich habe leider keine Uhr, aber dein Gerät bestimmt.
Dieser Chatbot bietet eine einfache Möglichkeit, Grundlagen der NLP (Natural Language Processing) und neuronaler Netze zu erlernen. Dank Tokenisierung und Stemming werden Eingaben sprachlich vereinfacht und effizient verarbeitet. Das Modell erkennt anhand der Intents passende Antworten und kann leicht erweitert werden.
Mögliche Erweiterungen:
- Nutzung echter Datensätze
- Erweiterung der Modellarchitektur (z. B. LSTM, Transformer)
- GUI oder Web-Integration