Whatsapp Telegram Telegram Call Anrufen

Intelligenter Chatbot mit NLP und Maschinellem Lernen


1. Importieren der notwendigen Bibliotheken

import random
import json
import pickle
import numpy as np
import nltk
from nltk.stem import WordNetLemmatizer
from nltk.stem.isri import ISRIStemmer
from keras.models import Sequential
from tensorflow.keras.optimizers import SGD
from keras.layers import Dense, Dropout
from keras.models import load_model

Beschreibung der importierten Module:

  • random: Wird verwendet, um Zufallszahlen zu generieren, was nützlich ist, um die Reihenfolge von Daten zu mischen.
  • json: Zum Laden und Speichern von Daten im JSON-Format, was hilfreich ist, um die Struktur der Chatbot-Intents zu definieren.
  • pickle: Ermöglicht das Speichern von Python-Objekten in Dateien und das spätere Laden dieser Objekte.
  • numpy: Eine Bibliothek für numerische Berechnungen in Python, die insbesondere für die Arbeit mit Arrays nützlich ist.
  • nltk: Natural Language Toolkit, eine Bibliothek für die Arbeit mit natürlicher Sprache (Textverarbeitung und -analyse).
    • WordNetLemmatizer: Ein Lemmatizer aus NLTK, der Wörter auf ihre Grundform reduziert.
    • ISRIStemmer: Ein Stemmer für die arabische Sprache aus NLTK, der Wörter auf ihre Wortstämme reduziert.
  • keras.models.Sequential: Ein Modell in Keras, das eine lineare Stapelung von Schichten ermöglicht.
  • tensorflow.keras.optimizers.SGD: Stochastic Gradient Descent, ein Optimierungsalgorithmus für das Training von neuronalen Netzen.
  • keras.layers.Dense: Eine dicht verbundene Schicht (vollständig verbunden) in einem neuronalen Netzwerk.
  • keras.layers.Dropout: Eine Schicht, die Dropout-An regularisierung verwendet, um Überanpassung zu verhindern.
  • keras.models.load_model: Ermöglicht das Laden eines zuvor gespeicherten Keras-Modells.

2. Initialisierung des arabischen Stemmers

isstemmer = ISRIStemmer()
  • ISRIStemmer ist eine Klasse aus der NLTK-Bibliothek (Natural Language Toolkit) speziell für das Stemming der arabischen Sprache.
  • Ein Stemmer reduziert Wörter auf ihre Wurzelformen oder Stämme. Zum Beispiel wird das Wort "laufen" auf "lauf" reduziert.
  • Der ISRIStemmer basiert auf einem algorithmischen Ansatz und ist darauf ausgelegt, gängige Präfixe und Suffixe in der arabischen Sprache zu entfernen.
  • 3. Herunterladen von NLTK-Resourcen

    nltk.download('omw-1.4')
    nltk.download("punkt")
    nltk.download("wordnet")
  • nltk.download('omw-1.4'):
    • Lädt das Open Multilingual Wordnet (OMW) in der Version 1.4 herunter.
    • OMW ist eine Sammlung von Wortnetzen für viele verschiedene Sprachen, die mit dem englischen WordNet verknüpft sind.
    • Dies ist nützlich, wenn Sie mehrsprachige Sprachverarbeitung durchführen möchten oder Zugang zu Wortnetzen in verschiedenen Sprachen benötigen.
  • nltk.download("punkt"):
    • Lädt den Punktuation Tokenizer (Punkt) herunter.
    • Punkt ist ein vortrainiertes Modell zur Satz- und Worttokenisierung.
    • Tokenisierung ist der Prozess der Aufteilung von Text in kleinere Einheiten wie Sätze oder Wörter. Dies ist ein wichtiger Schritt in der Textvorverarbeitung.
  • nltk.download("wordnet"):
    • Lädt das WordNet herunter, eine große lexikalische Datenbank der englischen Sprache.
    • In WordNet sind Nomen, Verben, Adjektive und Adverbien in Gruppen von kognitiven Synonymen (Synsets) organisiert, die jeweils unterschiedliche Konzepte darstellen.
    • WordNet wird häufig für Aufgaben wie Lemmatisierung, Synonym-Erkennung und semantische Textanalyse verwendet.
  • 4. Laden der JSON-Daten (Absichten des Chatbots)

    data_file = open("intents_arabic.json", encoding='utf-8', errors='ignore').read()
    intents = json.loads(data_file)

    Die Datei intents_arabic.json enthält die Daten wie folgt:

    {
        "intents": [{
                          "tag": "greetings",
                          "patterns": ["مرحبًا هناك", "مرحبًا", "السلام عليكم ", "ياو", "شو فيه", "كيف حالك؟", "مساء الخير", "هولا", "صباح الخير "],
    	              "responses": ["مرحبًا شكرًا للتحقق", "مرحبًا، كيف يمكنني مساعدتك"]
                         },
                        {
                          "tag": "events",
                           "patterns": ["ما هي الأحداث القادمة", "الأحداث القادمة"],
    			"responses": ["لا توجد حاليًا أحداث قادمة"]
                          }
            
    
       ]
    }
    


  • open("intents_arabic.json", encoding='utf-8', errors='ignore'):
    • Diese Funktion öffnet die Datei intents_arabic.json im Lese-Modus ('r' ist standardmäßig der Lese-Modus).
    • encoding='utf-8': Die Datei wird mit UTF-8-Kodierung geöffnet, um sicherzustellen, dass alle Zeichen, insbesondere arabische Zeichen, korrekt gelesen werden.
    • errors='ignore': Diese Option ignoriert Fehler, die während des Lesens auftreten könnten, z.B. aufgrund von ungültigen Zeichen.
  • .read():
    • Diese Methode liest den gesamten Inhalt der geöffneten Datei als einen einzigen String.
    • Der Inhalt der Datei wird in der Variable data_file als String gespeichert.
    • json.loads(data_file):
      • Diese Funktion wandelt den JSON-formatierten String data_file in ein entsprechendes Python-Objekt um, meistens ein Dictionary.
      • Der JSON-Inhalt wird in der Variable intents gespeichert.

    5. Initialisierung von Listen

    words = []
    classes = []
    documents = []
    ignore_words = ["?", "!", ".", ","]
    

    words = []:

    • Diese leere Liste wird später verwendet, um alle Wörter zu speichern, die in den Mustern der Chatbot-Intents vorkommen.
    • Diese Wörter werden tokenisiert (d.h. in einzelne Wörter zerlegt) und später verarbeitet (z.B. durch Stemming oder Lemmatisierung).

    classes = []:

    • Diese leere Liste wird verwendet, um die verschiedenen Klassen oder Tags zu speichern, die den unterschiedlichen Absichten (Intents) des Chatbots zugeordnet sind.
    • Jede Klasse repräsentiert eine Kategorie von Benutzeranfragen, wie z.B. "Begrüßung", "Hilfe", "Bestellung aufgeben", etc.

    documents = []:

    • Diese leere Liste wird verwendet, um Tupel zu speichern, die aus tokenisierten Wörtern und den zugehörigen Klassen bestehen.
    • Jedes Dokument besteht aus einem Muster (eine Liste von Wörtern) und dem entsprechenden Tag (die Klasse), z.B. (["Hallo", "wie", "geht", "es", "dir"], "Begrüßung").

    ignore_words = ["?", "!", ".", ","]:

  • Diese Liste enthält Satzzeichen und andere Sonderzeichen, die während der Textverarbeitung ignoriert werden sollen.
    • Diese Zeichen tragen in der Regel nicht zur Bedeutung des Textes bei und werden daher nicht in die Wortliste aufgenommen.


    6. Verarbeitung der Muster und Tags aus den JSON-Daten

    for intent in intents["intents"]:
        for pattern in intent["patterns"]:
            w = nltk.word_tokenize(pattern)
            words.extend(w)
            documents.append((w, intent["tag"]))
            if intent["tag"] not in classes:
                classes.append(intent["tag"])

    for intent in intents["intents"]:

    Diese Schleife geht durch jeden Intent in der intents["intents"]-Liste, die aus dem JSON-Objekt geladen wurde. Jeder Intent ist ein Dictionary, das Informationen wie tag, patterns und responses enthält.


    for pattern in intent["patterns"]:

    Diese innere Schleife geht durch jedes Muster (pattern) im aktuellen Intent. Muster sind Beispiele für Benutzeranfragen, die dem Intent zugeordnet sind.


    w = nltk.word_tokenize(pattern):

    Die nltk.word_tokenize(pattern)-Funktion zerlegt das Muster in einzelne Wörter (Tokens). Zum Beispiel wird der Satz "Wie geht es dir?" in die Tokens ["Wie", "geht", "es", "dir", "?"] zerlegt.


    words.extend(w):

    Die words-Liste wird mit den tokenisierten Wörtern (w) erweitert. Diese Liste enthält schließlich alle Wörter, die in den Mustern aller Intents vorkommen.

    words

    Ausgabe:

    ['مرحبًا',
     'هناك',
     'مرحبًا',
     'السلام',
     'عليكم',
     'ياو',
     'شو',
     'فيه',
     'كيف',
     'حالك؟',
     'مساء',
     'الخير',
     'هولا',
     'صباح',
     'الخير',
     'وداعًا',
    .
    .
    .
    ]


    documents.append((w, intent["tag"])):

    Jedes Dokument besteht aus einem Tupel von tokenisierten Wörtern (w) und dem zugehörigen Intent-Tag (intent["tag"]). Diese Dokumente werden verwendet, um den Zusammenhang zwischen den Mustern und ihren Intents zu speichern und später zum Training des Modells zu verwenden.

    documents

    Ausgabe:

    [(['مرحبًا', 'هناك'], 'greetings'),
     (['مرحبًا'], 'greetings'),
     (['السلام', 'عليكم'], 'greetings'),
     (['ياو'], 'greetings'),
     (['شو', 'فيه'], 'greetings'),
     (['كيف', 'حالك؟'], 'greetings'),
     (['مساء', 'الخير'], 'greetings'),
     (['هولا'], 'greetings'),
     (['صباح', 'الخير'], 'greetings'),
     (['وداعًا'], 'goodbye'),
     (['باي'], 'goodbye'),
     (['أراك', 'لاحقًا'], 'goodbye'),
     (['شكرا'], 'thanks'),
     (['حسنًا'], 'thanks'),
     (['شكرًا', 'لك'], 'thanks'),
     (['شكرًا', 'جزيلا'], 'thanks')
    .
    .
    .
    ]


    if intent["tag"] not in classes:

    Überprüft, ob das Intent-Tag (intent["tag"]) bereits in der classes-Liste vorhanden ist.

    classes.append(intent["tag"]):

    Wenn das Tag noch nicht in der Liste ist, wird es hinzugefügt. Die classes-Liste enthält alle unterschiedlichen Tags (Intents), die in den Daten vorkommen.

    classes

    Ausgabe:

    ['greetings',
     'goodbye',
     'thanks',
     'noanswer',
     'name1',
     'name',
     'date',
     'fav',
     'need',
     'AI',
     'sentiment',
     'sapient',
     'abbr',
     'lang',
     'sound',
     'artificial',
    .
    .
    .
    ]

    7. Stemming der Wörter und Ignorieren von Sonderzeichen

    processed_words = []
    for w in words:
        stemmed_word = isstemmer.stem(w)
        if w not in ignore_words:
            processed_words.append(stemmed_word)
    words = sorted(list(set(processed_words)))
    classes = sorted(list(classes))

    processed_words = []:

    Diese Zeile initialisiert eine leere Liste processed_words, die verwendet wird, um die gestemmten Wörter zu speichern.


    for w in words:

    Diese Schleife durchläuft jedes Wort in der words-Liste, die zuvor erstellt wurde.


    stemmed_word = isstemmer.stem(w):

    Das aktuelle Wort w wird mit dem ISRIStemmer gestemmt und das Ergebnis in der Variablen stemmed_word gespeichert. Der ISRIStemmer reduziert das Wort auf seinen Wortstamm.


    if w not in ignore_words:

    Diese Bedingung überprüft, ob das aktuelle Wort w nicht in der ignore_words-Liste enthalten ist, die Satzzeichen und andere irrelevante Zeichen enthält.


    processed_words.append(stemmed_word):

    Wenn das Wort w nicht in der ignore_words-Liste ist, wird das gestemmte Wort stemmed_word zur processed_words-Liste hinzugefügt.


    words = sorted(list(set(processed_words))):

    Die processed_words-Liste wird in ein Set umgewandelt, um Duplikate zu entfernen, dann zurück in eine Liste umgewandelt und schließlich alphabetisch sortiert. Das Ergebnis wird der Variablen words zugewiesen.


    classes = sorted(list(classes)):

    Die classes-Liste wird alphabetisch sortiert und das Ergebnis wird der Variablen classes zugewiesen.

    8. Erstellung des Trainingsdatensatzes

    training = []
    output_empty = [0] * len(classes)
    for doc in documents:
        bag = []
        pattern_words = doc[0]
        pattern_words = [isstemmer.stem(word) for word in pattern_words]
        for w in words:
            bag.append(1) if w in pattern_words else bag.append(0)
        output_row = list(output_empty)
        output_row[classes.index(doc[1])] = 1
        training.append([bag, output_row])
    • training = []: Diese Zeile initialisiert eine leere Liste training, die später die Trainingsdaten enthalten wird.

    • output_empty = [0] * len(classes): Diese Zeile erstellt eine Liste output_empty, die aus Nullen besteht und deren Länge der Anzahl der Klassen entspricht. Diese Liste wird verwendet, um die Ausgabezeilen zu initialisieren.

    • for doc in documents:: Diese Schleife durchläuft jedes Dokument in der documents-Liste, die zuvor erstellt wurde.

    • bag = []: Diese Zeile initialisiert eine leere Liste bag, die verwendet wird, um die Wortpräsenz in einem Muster zu speichern.

    • pattern_words = doc[0]: Diese Zeile extrahiert die Wörter des aktuellen Musters aus dem Dokument und speichert sie in pattern_words.

    • pattern_words = [isstemmer.stem(word) for word in pattern_words]: Diese Zeile stemmt jedes Wort im Muster mit dem ISRIStemmer und speichert die gestemmten Wörter zurück in pattern_words.

    • for w in words:: Diese Schleife durchläuft jedes Wort in der words-Liste, die alle einzigartigen gestemmten Wörter enthält.

    • bag.append(1) if w in pattern_words else bag.append(0): Diese Zeile fügt 1 zur bag-Liste hinzu, wenn das Wort w im pattern_words-Muster vorkommt, andernfalls fügt sie 0 hinzu. Dies erstellt einen sogenannten "Bag of Words"-Vektor, der angibt, welche Wörter im Muster vorhanden sind.

    • output_row = list(output_empty): Diese Zeile erstellt eine neue Liste output_row, die eine Kopie von output_empty ist. Diese Liste wird verwendet, um die Zielausgabe für das aktuelle Dokument zu erstellen.

    • output_row[classes.index(doc[1])] = 1: Diese Zeile setzt den Wert der entsprechenden Klasse in output_row auf 1. doc[1] enthält das Tag des aktuellen Dokuments, und classes.index(doc[1]) gibt den Index dieser Klasse in der classes-Liste zurück.

    • training.append([bag, output_row]): Diese Zeile fügt das Paar aus bag (dem "Bag of Words"-Vektor) und output_row (der Zielausgabe) zur training-Liste hinzu. Die training-Liste enthält somit die Trainingsdaten für das Modell.

    9. Überprüfung und Korrektur von Inkonsistenzen in Längen und Typen

    bag_length = len(words)
    output_length = len(classes)
    fixed_training = []
    for item in training:
        bag, output_row = item
        if len(bag) != bag_length:
            if len(bag) < bag_length:
                bag = bag + [0] * (bag_length - len(bag))
            else:
                bag = bag[:bag_length]
        if len(output_row) != output_length:
            if len(output_row) < output_length:
                output_row = output_row + [0] * (output_length - len(output_row))
            else:
                output_row = output_row[:output_length]
        bag = list(bag)
        output_row = list(output_row)
        fixed_training.append([bag, output_row])
    • bag_length = len(words): Diese Zeile speichert die Länge der words-Liste (die Anzahl der einzigartigen Wörter) in der Variablen bag_length.

    • output_length = len(classes): Diese Zeile speichert die Länge der classes-Liste (die Anzahl der verschiedenen Klassen) in der Variablen output_length.

    • fixed_training = []: Diese Zeile initialisiert eine leere Liste fixed_training, die später die korrigierten Trainingsdaten enthalten wird.

    • for item in training:: Diese Schleife durchläuft jedes Element item in der training-Liste.

    • bag, output_row = item: Diese Zeile entpackt das item in die beiden Variablen bag und output_row. bag ist der "Bag of Words"-Vektor und output_row ist die Zielausgabe.

    • if len(bag) != bag_length:: Diese Bedingung überprüft, ob die Länge der bag-Liste nicht gleich bag_length ist.

    • if len(bag) < bag_length:: Diese innere Bedingung überprüft, ob die Länge der bag-Liste kleiner als bag_length ist.

    • bag = bag + [0] * (bag_length - len(bag)): Wenn die bag-Liste zu kurz ist, wird sie durch Hinzufügen der notwendigen Anzahl von Nullen (0) auf bag_length verlängert.

    • else:: Diese Bedingung wird ausgeführt, wenn die bag-Liste länger als bag_length ist.

    • bag = bag[

      ]: Wenn die bag-Liste zu lang ist, wird sie auf bag_length gekürzt.

    • if len(output_row) != output_length:: Diese Bedingung überprüft, ob die Länge der output_row-Liste nicht gleich output_length ist.

    • if len(output_row) < output_length:: Diese innere Bedingung überprüft, ob die Länge der output_row-Liste kleiner als output_length ist.

    • output_row = output_row + [0] * (output_length - len(output_row)): Wenn die output_row-Liste zu kurz ist, wird sie durch Hinzufügen der notwendigen Anzahl von Nullen (0) auf output_length verlängert.

    • else:: Diese Bedingung wird ausgeführt, wenn die output_row-Liste länger als output_length ist.

    • output_row = output_row[ ]: Wenn die output_row-Liste zu lang ist, wird sie auf output_length gekürzt.

    • bag = list(bag): Diese Zeile stellt sicher, dass bag eine Liste ist (falls es versehentlich in ein anderes Format umgewandelt wurde).

    • output_row = list(output_row): Diese Zeile stellt sicher, dass output_row eine Liste ist (falls es versehentlich in ein anderes Format umgewandelt wurde).

    • fixed_training.append([bag, output_row]): Diese Zeile fügt das korrigierte Paar aus bag und output_row zur fixed_training-Liste hinzu. Die fixed_training-Liste enthält somit die korrigierten Trainingsdaten für das Modell.

    10. Umwandeln des Trainingsdatensatzes in ein NumPy-Array

    random.shuffle(fixed_training)
    training_array = np.array(fixed_training, dtype=object)
    train_x = list(training_array[:,0])
    train_y = list(training_array[:,1])
    • random.shuffle(fixed_training): Diese Zeile mischt die fixed_training-Liste zufällig durch. Dies ist wichtig, um sicherzustellen, dass das Modell nicht in einer bestimmten Reihenfolge der Daten trainiert wird, was zu einer besseren Generalisierung des Modells führt.

    • training_array = np.array(fixed_training, dtype=object): Diese Zeile konvertiert die fixed_training-Liste in ein NumPy-Array. Das Argument dtype=object stellt sicher, dass das Array Elemente unterschiedlicher Typen aufnehmen kann. In diesem Fall bestehen die Elemente aus Listen (Bag-of-Words-Vektoren und Zielausgaben).

    • train_x = list(training_array[:,0]): Diese Zeile extrahiert die erste Spalte des training_array-Arrays (die Bag-of-Words-Vektoren) und konvertiert sie zurück in eine Liste. Diese Liste wird in der Variablen train_x gespeichert.

    • train_y = list(training_array[:,1]): Diese Zeile extrahiert die zweite Spalte des training_array-Arrays (die Zielausgaben) und konvertiert sie zurück in eine Liste. Diese Liste wird in der Variablen train_y gespeichert.

    print(train_y)

    Ausgabe:

    [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    .
    .
    ]



    11. Erstellen des neuronalen Netzwerks

    model = Sequential()
    model.add(Dense(128, input_shape=(len(train_x[0]),), activation="relu"))
    model.add(Dropout(0.5))
    model.add(Dense(64, activation="relu"))
    model.add(Dropout(0.5))
    model.add(Dense(len(train_y[0]), activation="softmax"))
    model.summary()
    • model = Sequential(): Diese Zeile erstellt ein sequentielles Modell, das bedeutet, dass die Schichten des neuronalen Netzes nacheinander hinzugefügt werden.

    • model.add(Dense(128, input_shape=(len(train_x[0]),), activation="relu")):

      • Diese Zeile fügt dem Modell eine dichte (vollständig verbundene) Schicht mit 128 Neuronen hinzu.
      • Dense(128): Gibt die Anzahl der Neuronen in dieser Schicht an.
      • input_shape=(len(train_x[0]),): Definiert die Form der Eingabedaten. In diesem Fall entspricht die Eingabe der Länge des ersten Elements in train_x, was die Anzahl der Merkmale (Wörter im Bag-of-Words-Vektor) darstellt.
      • activation="relu": Verwendet die ReLU-Aktivierungsfunktion (Rectified Linear Unit), die für die meisten versteckten Schichten in neuronalen Netzen verwendet wird, um nichtlineare Beziehungen zu modellieren.
    • model.add(Dropout(0.5)): Diese Zeile fügt eine Dropout-Schicht mit einer Dropout-Rate von 0.5 hinzu. Dropout wird verwendet, um Überanpassung (Overfitting) zu verhindern, indem es zufällig 50% der Neuronen während des Trainings deaktiviert.

    • model.add(Dense(64, activation="relu")): Diese Zeile fügt eine weitere dichte Schicht mit 64 Neuronen und der ReLU-Aktivierungsfunktion hinzu.

    • model.add(Dropout(0.5)): Diese Zeile fügt eine weitere Dropout-Schicht mit einer Dropout-Rate von 0.5 hinzu.

    • model.add(Dense(len(train_y[0]), activation="softmax")):

      • Diese Zeile fügt die Ausgabeschicht hinzu, die eine dichte Schicht mit so vielen Neuronen wie es Klassen in train_y gibt.
      • Dense(len(train_y[0])): Die Anzahl der Neuronen entspricht der Anzahl der Klassen (Intents).
      • activation="softmax": Verwendet die Softmax-Aktivierungsfunktion, die für die Mehrklassen-Klassifikation geeignet ist, da sie die Wahrscheinlichkeiten für jede Klasse ausgibt.
    • model.summary(): Diese Zeile gibt eine Zusammenfassung des Modells aus, einschließlich der Anzahl der Schichten, der Form der Eingaben und Ausgaben und der Anzahl der zu trainierenden Parameter.


    12. Kompilieren und Trainieren des Modells

    sgd = SGD(learning_rate=0.01, momentum=0.9, nesterov=True)
    model.compile(loss="categorical_crossentropy", optimizer=sgd, metrics=["accuracy"])
    save_model = model.fit(np.array(train_x), np.array(train_y), epochs=200, batch_size=5, verbose=1)
    • sgd = SGD(learning_rate=0.01, momentum=0.9, nesterov=True): Diese Zeile erstellt einen Stochastic Gradient Descent (SGD) Optimierer mit den folgenden Parametern:

      • learning_rate=0.01: Legt die Lernrate des Optimierers fest, die bestimmt, wie große Schritte der Optimierer bei der Anpassung der Gewichte macht.
      • momentum=0.9: Fügt einen Momentum-Term hinzu, der die Konvergenz beschleunigt und dabei hilft, lokale Minima zu vermeiden.
      • nesterov=True: Aktiviert das Nesterov Momentum, eine Variante des Standard-Momentums, die vorsieht, dass die Gradientenschätzung auf den zukünftigen Positionen berechnet wird.
    • model.compile(loss="categorical_crossentropy", optimizer=sgd, metrics=["accuracy"]): Diese Zeile kompiliert das Modell mit den folgenden Parametern:

      • loss="categorical_crossentropy": Setzt die Verlustfunktion auf Kategorische Kreuzentropie, die für Mehrklassenklassifikationsprobleme verwendet wird.
      • optimizer=sgd: Verwendet den zuvor definierten SGD-Optimierer.
      • metrics=["accuracy"]: Fügt die Metrik "accuracy" hinzu, um die Genauigkeit des Modells während des Trainings zu überwachen.
    • save_model = model.fit(np.array(train_x), np.array(train_y), epochs=200, batch_size=5, verbose=1): Diese Zeile trainiert das Modell mit den folgenden Parametern:

      • np.array(train_x): Konvertiert die Trainingsdaten in ein NumPy-Array.
      • np.array(train_y): Konvertiert die Zielwerte in ein NumPy-Array.
      • epochs=200: Bestimmt die Anzahl der Epochen (vollständige Durchläufe durch den Trainingsdatensatz), in denen das Modell trainiert wird.
      • batch_size=5: Bestimmt die Größe der Mini-Batches, die in jedem Trainingsschritt verwendet werden.
      • verbose=1: Setzt die Ausführlichkeit auf 1, was bedeutet, dass während des Trainings Fortschrittsmeldungen angezeigt werden.

    Ausgabe:

    Model: "sequential"
    
    ┃ Layer (type)                         ┃ Output Shape                ┃         Param #
    ┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
    │ dense (Dense)                        │ (None, 128)                 │          17,664 
    ├──────────────────────────────────────┼─────
    │ dropout (Dropout)                    │ (None, 128)                 │               0 
    ├──────────────────────────────────────┼─────
    │ dense_1 (Dense)                      │ (None, 64)                  │           8,256 
    ├──────────────────────────────────────┼─────
    │ dropout_1 (Dropout)                  │ (None, 64)                  │               0 
    ├──────────────────────────────────────┼─────
    │ dense_2 (Dense)                      │ (None, 55)                  │           3,575 
    └──────────────────────────────────────┴────
     Total params: 29,495 (115.21 KB)
     Trainable params: 29,495 (115.21 KB)
     Non-trainable params: 0 (0.00 B)
    

    Auch Ausgabe:

    Epoch 1/200
    21/21 ━━━━━━━━━━━━━━━━━━━━ 1s 2ms/step - accuracy: 0.0395 - loss: 4.0371   
    Epoch 2/200
    21/21 ━━━━━━━━━━━━━━━━━━━━ 0s 1ms/step - accuracy: 0.0186 - loss: 3.9789     
    Epoch 3/200
    21/21 ━━━━━━━━━━━━━━━━━━━━ 0s 1ms/step - accuracy: 0.0994 - loss: 3.8372     
    Epoch 4/200
    21/21 ━━━━━━━━━━━━━━━━━━━━ 0s 1ms/step - accuracy: 0.1152 - loss: 3.7986 
    Epoch 5/200
    21/21 ━━━━━━━━━━━━━━━━━━━━ 0s 1ms/step - accuracy: 0.0510 - loss: 3.7852   
    .
    .
    .
    Epoch 199/200
    21/21 ━━━━━━━━━━━━━━━━━━━━ 0s 1ms/step - accuracy: 0.9562 - loss: 0.2005 
    Epoch 200/200
    21/21 ━━━━━━━━━━━━━━━━━━━━ 0s 1ms/step - accuracy: 0.9315 - loss: 0.2120 

    13. Speichern des Modells und der Daten

    model.save("chatbot_model.h5", save_model)
    pickle.dump(words, open("words.pkl", "wb"))
    pickle.dump(classes, open("classes.pkl", "wb"))
    • model.save("chatbot_model.h5", save_model): Diese Zeile speichert das trainierte Modell in einer Datei mit dem Namen chatbot_model.h5.

      • "chatbot_model.h5": Der Name der Datei, in der das Modell gespeichert wird. Das .h5-Format ist ein HDF5-Format, das häufig zum Speichern von Modellen in Keras verwendet wird.
      • save_model: Dies ist die Variable, die das trainierte Modell enthält. Das Argument save_model ist optional, da das Modell direkt gespeichert wird, aber es wird häufig verwendet, um sicherzustellen, dass das richtige Modellobjekt gespeichert wird.
    • pickle.dump(words, open("words.pkl", "wb")): Diese Zeile speichert die words-Liste in einer Datei mit dem Namen words.pkl mithilfe der Pickle-Bibliothek.

      • words: Die Liste der einzigartigen gestemmten Wörter, die während der Vorverarbeitung erstellt wurde.
      • open("words.pkl", "wb"): Öffnet eine Datei mit dem Namen words.pkl im Schreibmodus (wb steht für "write binary"), um die Daten im Binärformat zu speichern.
      • pickle.dump(words, ...): Speichert die words-Liste in der geöffneten Datei.
    • pickle.dump(classes, open("classes.pkl", "wb")): Diese Zeile speichert die classes-Liste in einer Datei mit dem Namen classes.pkl mithilfe der Pickle-Bibliothek.

      • classes: Die Liste der unterschiedlichen Klassen (Intents), die während der Vorverarbeitung erstellt wurde.
      • open("classes.pkl", "wb"): Öffnet eine Datei mit dem Namen classes.pkl im Schreibmodus (wb steht für "write binary"), um die Daten im Binärformat zu speichern.
      • pickle.dump(classes, ...): Speichert die classes-Liste in der geöffneten Datei.

    14. Laden des gespeicherten Modells und der Daten

    words = pickle.load(open('words.pkl', 'rb'))
    classes = pickle.load(open('classes.pkl', 'rb'))
    model = load_model('chatbot_model.h5')
    • words = pickle.load(open('words.pkl', 'rb')): Diese Zeile lädt die gespeicherte words-Liste aus der Datei words.pkl mithilfe der Pickle-Bibliothek.

      • open('words.pkl', 'rb'): Öffnet die Datei words.pkl im Lesemodus (rb steht für "read binary"), um die Daten im Binärformat zu lesen.
      • pickle.load(...): Lädt die Daten aus der geöffneten Datei und weist sie der Variablen words zu. Die words-Liste enthält die einzigartigen gestemmten Wörter, die während der Vorverarbeitung erstellt wurden.
    • classes = pickle.load(open('classes.pkl', 'rb')): Diese Zeile lädt die gespeicherte classes-Liste aus der Datei classes.pkl mithilfe der Pickle-Bibliothek.

      • open('classes.pkl', 'rb'): Öffnet die Datei classes.pkl im Lesemodus (rb steht für "read binary"), um die Daten im Binärformat zu lesen.
      • pickle.load(...): Lädt die Daten aus der geöffneten Datei und weist sie der Variablen classes zu. Die classes-Liste enthält die unterschiedlichen Klassen (Intents), die während der Vorverarbeitung erstellt wurden.
    • model = load_model('chatbot_model.h5'): Diese Zeile lädt das gespeicherte Modell aus der Datei chatbot_model.h5 mithilfe der Keras-Bibliothek.

      • load_model('chatbot_model.h5'): Lädt das trainierte Modell aus der Datei chatbot_model.h5 und weist es der Variablen model zu. Das Modell wurde zuvor trainiert und gespeichert, und durch das Laden kann es nun verwendet werden, um neue Eingaben zu klassifizieren.

    15. Definieren von Funktionen zur Verarbeitung von Eingabesätzen

    def clean_up_sentence(sentence):
        sentence_words = nltk.word_tokenize(sentence)
        sentence_words = [isstemmer.stem(word) for word in sentence_words]
        return sentence_words
    
    def bag_of_words(sentence):
        sentence_words = clean_up_sentence(sentence)
        bag = [0] * len(words)
        for w in sentence_words:
            for i, word in enumerate(words):
                if word == w:
                    bag[i] = 1
        return np.array(bag)
    

    Funktion clean_up_sentence

    • def clean_up_sentence(sentence): Definiert eine Funktion clean_up_sentence, die einen Satz als Eingabe annimmt und eine Liste von gestemmten Wörtern zurückgibt.

    • sentence_words = nltk.word_tokenize(sentence): Diese Zeile tokenisiert den Eingabesatz in einzelne Wörter (Tokens) mithilfe der word_tokenize-Funktion von NLTK. Zum Beispiel wird der Satz "Wie geht es dir?" in die Tokens ["Wie", "geht", "es", "dir", "?"] zerlegt.

    • sentence_words = [isstemmer.stem(word) for word in sentence_words]: Diese Zeile stemmt jedes Wort in der Liste sentence_words mithilfe des ISRIStemmer. Der Stemmer reduziert die Wörter auf ihre Grundformen.

    • return sentence_words: Gibt die Liste der gestemmten Wörter zurück.

    Funktion bag_of_words

    • def bag_of_words(sentence): Definiert eine Funktion bag_of_words, die einen Satz als Eingabe annimmt und einen Bag-of-Words-Vektor zurückgibt.

    • sentence_words = clean_up_sentence(sentence): Diese Zeile ruft die Funktion clean_up_sentence auf, um den Eingabesatz zu tokenisieren und zu stemmen. Das Ergebnis ist eine Liste von gestemmten Wörtern.

    • bag = [0] * len(words): Diese Zeile initialisiert eine Liste bag mit Nullen. Die Länge der Liste entspricht der Anzahl der einzigartigen Wörter (words) im Trainingsdatensatz.

    • for w in sentence_words:: Diese Schleife durchläuft jedes Wort in der Liste sentence_words.

    • for i, word in enumerate(words):: Diese verschachtelte Schleife durchläuft jedes Wort in der Liste words und gibt dessen Index i und das Wort word zurück.

    • if word == w:: Diese Bedingung überprüft, ob das aktuelle Wort word aus der words-Liste mit dem aktuellen Wort w aus der sentence_words-Liste übereinstimmt.

    • bag[i] = 1: Wenn das Wort word im Satz vorkommt, wird der entsprechende Index in der bag-Liste auf 1 gesetzt.

    • return np.array(bag): Konvertiert die bag-Liste in ein NumPy-Array und gibt es zurück. Dieser Vektor repräsentiert das Vorkommen der Wörter aus dem Eingabesatz im Trainingsdatensatz.

    16. Definieren der Funktion zur Vorhersage der Klasse eines Satzes

    def predict_class(sentence):
        bow = bag_of_words(sentence)
        res = model.predict(np.array([bow]))[0]
        ERROR_THRESHOLD = 0.25
        results = [[i, r] for i, r in enumerate(res) if r > ERROR_THRESHOLD]
        return_list = []
        for r in results:
            return_list.append({'intent': classes[r[0]], 'probability': str(r[1])})
        return return_list

    Funktion predict_class

    • def predict_class(sentence): Definiert eine Funktion predict_class, die einen Satz als Eingabe annimmt und eine Liste von vorhergesagten Klassen (Intents) mit deren Wahrscheinlichkeiten zurückgibt.

    • bow = bag_of_words(sentence): Diese Zeile ruft die Funktion bag_of_words auf, um den Eingabesatz in einen Bag-of-Words-Vektor (bow) zu konvertieren.

    • res = model.predict(np.array([bow]))[0]:

      • Diese Zeile verwendet das trainierte Modell (model), um eine Vorhersage für den Eingabesatz zu machen.
      • Der Bag-of-Words-Vektor (bow) wird in ein NumPy-Array konvertiert und als Eingabe für die Vorhersage verwendet.
      • Das Modell gibt eine Liste von Wahrscheinlichkeiten für jede Klasse zurück. Das [0] am Ende extrahiert das erste (und einzige) Element aus der Liste der Vorhersagen.
    • ERROR_THRESHOLD = 0.25: Setzt einen Schwellenwert (ERROR_THRESHOLD) auf 0.25. Nur Klassen mit einer Wahrscheinlichkeit über diesem Schwellenwert werden berücksichtigt.

    • results = [[i, r] for i, r in enumerate(res) if r > ERROR_THRESHOLD]:

      • Diese Zeile erstellt eine Liste von Indizes und Wahrscheinlichkeiten für die Klassen, deren Wahrscheinlichkeit über dem Schwellenwert liegt.
      • enumerate(res): Gibt sowohl den Index (i) als auch die Wahrscheinlichkeit (r) für jede Klasse in res zurück.
      • if r > ERROR_THRESHOLD: Filtert die Klassen heraus, deren Wahrscheinlichkeit unter dem Schwellenwert liegt.
    • return_list = []: Initialisiert eine leere Liste return_list, die die vorhergesagten Klassen (Intents) mit deren Wahrscheinlichkeiten speichern wird.

    • for r in results:: Diese Schleife durchläuft jedes Element (r) in der results-Liste.

    • return_list.append({'intent': classes[r[0]], 'probability': str(r[1])}):

      • Diese Zeile fügt ein Dictionary mit den Schlüsseln intent und probability zur return_list hinzu.
      • classes[r[0]]: Verwendet den Index (r[0]) aus der results-Liste, um den entsprechenden Klassennamen aus der classes-Liste zu erhalten.
      • str(r[1]): Konvertiert die Wahrscheinlichkeit (r[1]) in einen String.
    • return return_list: Gibt die return_list zurück, die die vorhergesagten Klassen und deren Wahrscheinlichkeiten enthält.


    17. Definieren der Funktion zur Generierung der Antwort basierend auf der Vorhersage

    def get_response(intents_list, intents_json):
        tag = intents_list[0]['intent']
        list_of_intents = intents_json['intents']
        for i in list_of_intents:
            if i['tag'] == tag:
                result = random.choice(i['responses'])
                break
        return result

    Funktion get_response

    • def get_response(intents_list, intents_json): Definiert eine Funktion get_response, die eine Liste von Intents (intents_list) und die JSON-Daten der Intents (intents_json) als Eingabe annimmt und eine entsprechende Antwort zurückgibt.

    • tag = intents_list[0]['intent']: Diese Zeile extrahiert das Intent-Tag aus dem ersten Element der intents_list. Die intents_list ist eine Liste von vorhergesagten Intents, und hier wird das Intent-Tag des wahrscheinlichsten Intents ausgewählt.

    • list_of_intents = intents_json['intents']: Diese Zeile extrahiert die Liste aller Intents aus den JSON-Daten (intents_json). Dies ist eine Liste von Dictionaries, wobei jedes Dictionary einen Intent mit seinen Mustern und Antworten enthält.

    • for i in list_of_intents:: Diese Schleife durchläuft jedes Intent-Dictionary (i) in der list_of_intents.

    • if i['tag'] == tag:: Diese Bedingung überprüft, ob das Tag des aktuellen Intents (i['tag']) mit dem extrahierten Tag (tag) übereinstimmt.

    • result = random.choice(i['responses']):

      • Wenn das Tag übereinstimmt, wird eine zufällige Antwort aus der Liste der Antworten (i['responses']) des aktuellen Intents ausgewählt.
      • random.choice(i['responses']): Wählt zufällig eine Antwort aus der Liste der Antworten des aktuellen Intents.
    • break: Diese Zeile beendet die Schleife, sobald eine passende Antwort gefunden wurde.

    • return result: Gibt die ausgewählte Antwort (result) zurück.

    18. Erstellen einer Endlosschleife für die Chatbot-Interaktion

    while True:
        message = input("")
        ints = predict_class(message)
        res = get_response(ints, intents)
        print(res)

    Unendliche Schleife

    • while True:: Diese Zeile startet eine unendliche Schleife, die kontinuierlich ausgeführt wird, bis sie explizit gestoppt wird (z.B. durch eine Unterbrechung des Benutzers).

    Benutzer-Eingabe

    • message = input(""):
      • Diese Zeile wartet auf eine Eingabe des Benutzers.
      • input(""): Fordert den Benutzer auf, eine Nachricht einzugeben. Der leere String ("") bedeutet, dass keine zusätzliche Aufforderung angezeigt wird.
      • Die eingegebene Nachricht wird der Variablen message zugewiesen.

    Klassenvorhersage

    • ints = predict_class(message):
      • Diese Zeile ruft die Funktion predict_class auf, um die Klasse (Intent) der eingegebenen Nachricht vorherzusagen.
      • predict_class(message): Verarbeitet die eingegebene Nachricht und gibt eine Liste von vorhergesagten Klassen (Intents) mit ihren Wahrscheinlichkeiten zurück.
      • Das Ergebnis wird der Variablen ints zugewiesen.

    Antwort generieren

    • res = get_response(ints, intents):
      • Diese Zeile ruft die Funktion get_response auf, um eine passende Antwort basierend auf den vorhergesagten Klassen (Intents) zu generieren.
      • get_response(ints, intents): Verarbeitet die vorhergesagten Klassen (ints) und die JSON-Daten der Intents (intents), um eine passende Antwort zu finden.
      • Die generierte Antwort wird der Variablen res zugewiesen.

    Antwort ausgeben

    • print(res):
      • Diese Zeile gibt die generierte Antwort (res) auf der Konsole aus.
      • print(res): Zeigt die Antwort an, die von der Funktion get_response zurückgegeben wurde.

    19. Der gesamte Code

    import random
    import json
    import pickle
    import numpy as np
    
    import nltk
    # english:
    from nltk.stem import WordNetLemmatizer
    # Arabic
    from nltk.stem.isri import ISRIStemmer
    
    from keras.models import Sequential
    from tensorflow.keras.optimizers import SGD
    from keras.layers import Dense, Dropout
    from keras.models import load_model
    
    isstemmer = ISRIStemmer()
    
    nltk.download('om-1.4')
    nltk.download("punkt")
    nltk.download("wordnet")
    
    
    data_file = open("intents_arabic.json", encoding='utf-8', errors= 'ignore').read()
    intents = json.loads(data_file)
    
    words = []
    classes = []
    documents = []
    ignore_words = ["?","!",".",","]
    
    for intent in intents["intents"]:
        for pattern in intent["patterns"]:
            w = nltk.word_tokenize(pattern)
            words.extend(w)
            
            documents.append((w, intent["tag"]))
            
            if intent["tag"] not in classes:
                classes.append(intent["tag"])
    
    
    processed_words = []
    for w in words:
        stemmed_word = isstemmer.stem(w)
        
        if w not in ignore_words:
            processed_words.append(stemmed_word)
            
    words = sorted(list(set(processed_words)))
    classes = sorted(list(classes))
    
    
    import random
    import numpy as np
    
    training = []
    output_empty = [0] * len(classes)
    
    for doc in documents:
        bag = []
        pattern_words = doc[0]
        
        # Stem the pattern words
        pattern_words = [isstemmer.stem(word) for word in pattern_words]
        
        for w in words:
            bag.append(1) if w in pattern_words else bag.append(0)
        
        output_row = list(output_empty)
        output_row[classes.index(doc[1])] = 1
        
        training.append([bag, output_row])
    
    # Check and fix inconsistencies in lengths and types
    bag_length = len(words)
    output_length = len(classes)
    
    fixed_training = []
    for item in training:
        bag, output_row = item
        
        # Ensure bag is the correct length
        if len(bag) != bag_length:
            if len(bag) < bag_length:
                bag = bag + [0] * (bag_length - len(bag))
            else:
                bag = bag[:bag_length]
        
        # Ensure output_row is the correct length
        if len(output_row) != output_length:
            if len(output_row) < output_length:
                output_row = output_row + [0] * (output_length - len(output_row))
            else:
                output_row = output_row[:output_length]
        
        # Ensure both bag and output_row are lists (not NumPy arrays)
        bag = list(bag)
        output_row = list(output_row)
        
        fixed_training.append([bag, output_row])
    
    # Convert fixed_training to a NumPy array
    random.shuffle(fixed_training)
    training_array = np.array(fixed_training, dtype=object)
    
    print("Training data successfully converted to NumPy array after fixing inconsistencies.")
    
    train_x = list(training_array[:,0])
    train_y = list(training_array[:,1])
    print("train data created")
    
    model = Sequential()
    model.add(Dense(128, input_shape=(len(train_x[0]),), activation="relu"))
    model.add(Dropout(0.5))
    model.add(Dense(64, activation="relu"))
    model.add(Dropout(0.5))
    model.add(Dense(len(train_y[0]), activation="softmax"))
    model.summary()
    
    sgd = SGD(learning_rate=0.01,momentum = 0.9, nesterov=True)
    model.compile(loss="categorical_crossentropy", optimizer = sgd, metrics=["accuracy"]) 
    
    
    save_model = model.fit(np.array(train_x), np.array(train_y),epochs=200, batch_size = 5, verbose=1)
    
    model.save("chatbot_mode.h5", save_model)
    
    pickle.dump(words, open("words.pkl","wb"))
    pickle.dump(classes, open("classes.pkl","wb"))
    
    
    words= pickle.load(open('words.pkl', 'rb'))
    classes= pickle.load(open('classes.pkl', 'rb'))
    
    model = load_model('chatbot_mode.h5')
    
    
    def clean_up_sentence(sentence):
        sentence_words = nltk.word_tokenize(sentence)
        sentence_words = [isstemmer.stem(word) for word in sentence_words]
        #print("cleaned up sentence =", sentence_words)
        return sentence_words
    
    def bag_of_words(sentence):
        sentence_words = clean_up_sentence(sentence)
        bag = [0]*len(words)
        
        for w in sentence_words:
            for i, word in enumerate(words):
                if word ==w:
                    bag[i]=1
                    
        return np.array(bag)
    
    def predict_class(sentence):
        bow = bag_of_words(sentence)
        res = model.predict(np.array([bow]))[0]
        #print("row result",res)
        
        ERROR_THRESHOLD = 0.25
        results = [[i,r] for i,r in enumerate(res) if r>ERROR_THRESHOLD]
        #print("results after THRESHOLDING", results)
        
        return_list = []
        
        for r in results:
            return_list.append({'intent':classes[r[0]], 'probability':str(r[1])})
            
        #print("final probabile results list", return_list)
        
        return return_list
    
    def get_response(intents_list, intents_json):
        tag= intents_list[0]['intent']
        list_of_intents = intents_json['intents']
        
        for i in list_of_intents:
            if i['tag'] == tag:
                result = random.choice(i['responses'])
                break
        return result
    
    
    while True:
        message = input("")
        ints = predict_class(message)
        #print("ints", ints)
        res = get_response(ints, intents)
        
        print(res)
    




    CEO Image

    Ali Ajjoub

    info@ajjoub.com

    Adresse 0049-15773651670

    Adresse Jacob-winter-platz,1 01239 Dresden

    Buchen Sie jetzt Ihren Termin für eine umfassende und individuelle Beratung.

    Termin Buchen

    Kontaktieren Sie uns

    Lassen Sie uns K o n t a k t aufnehmen!