Intents.JSON mit Umlaute zeigt nicht richtig in Python

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
smaligardezi
User
Beiträge: 4
Registriert: Montag 6. September 2021, 07:21

data_file = open('intents.json').read()
intents = json.loads(data_file)
Intents hat deutsche Umlaute und diese werden in meinem Chatbot nicht korrekt angezeigt.
Ich Danke für die Hilfe.
Code
#!/usr/bin/python
# -*- coding: utf-8 -*-
import nltk
from nltk.stem import WordNetLemmatizer
lemmatizer = WordNetLemmatizer()
import pickle
import numpy as np


from tensorflow.keras.models import load_model
import json
import random

data_file = open('intents.json').read()
intents = json.load(data_file)

words = pickle.load(open('words.pkl','rb'))
classes = pickle.load(open('classes.pkl','rb'))
model = load_model('chatbot_model.h5')

def clean_up_sentence(sentence):
# tokenize the pattern - split words into array
sentence_words = nltk.word_tokenize(sentence)
# stem each word - create short form for word
sentence_words = [lemmatizer.lemmatize(word.lower()) for word in sentence_words]
return sentence_words

# return bag of words array: 0 or 1 for each word in the bag that exists in the sentence

def bow(sentence, words, show_details=True):
# tokenize the pattern
sentence_words = clean_up_sentence(sentence)
# bag of words - matrix of N words, vocabulary matrix
bag = [0]*len(words)
for s in sentence_words:
for i,w in enumerate(words):
if w == s:
# assign 1 if current word is in the vocabulary position
bag = 1
if show_details:
print ("found in bag: %s" % w)
return(np.array(bag))

def predict_class(sentence, model):
# filter out predictions below a threshold
p = bow(sentence, words,show_details=False)
res = model.predict(np.array([p]))[0]
ERROR_THRESHOLD = 0.25
results = [[i,r] for i,r in enumerate(res) if r>ERROR_THRESHOLD]
# sort by strength of probability
results.sort(key=lambda x: x[1], reverse=True)
return_list = []
for r in results:
return_list.append({"intent": classes[r[0]], "probability": str(r[1])})
return return_list

def getResponse(ints, intents_json):
tag = ints[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

def chatbot_response(msg):
ints = predict_class(msg, model)
res = getResponse(ints, intents)
return res


#Creating GUI with tkinter
import tkinter
from tkinter import *
from PIL import Image, ImageTk


def send():
msg = EntryBox.get("1.0",'end-1c').strip()
EntryBox.delete("0.0",END)

if msg != '':
ChatLog.config(state=NORMAL)
ChatLog.insert(END, "You: " + msg + '\n\n')
ChatLog.config(foreground="#442265", font=("Verdana", 12 ))

res = chatbot_response(msg)
ChatLog.insert(END, "Bot: " + res + '\n\n')

ChatLog.config(state=DISABLED)
ChatLog.yview(END)



base = tkinter.Tk()
base.title("Borat")
base.geometry("400x500")
base.resizable(width=TRUE, height=TRUE)

#Create Chat window

ChatLog = Text(base, bd=0, bg="white",font="Arial")

ChatLog.config(state=DISABLED)
ChatLog.pack(side="right", fill="y", expand=True, padx=5, pady=5)


#Bind scrollbar to Chat window
scrollbar = Scrollbar(base, command=ChatLog.yview, cursor="heart")
ChatLog['yscrollcommand'] = scrollbar.set

image = Image.open(r"C:\Users\pavilion\Desktop\ChatBot\mail.png")
resize_image = image.resize((68,65))
img = ImageTk.PhotoImage(resize_image)

#Create Button to send message
SendButton = tkinter.Button(base, image=img, cursor="dot",
relief=FLAT, command= send)
##font=("Arial",12,'bold'), text="send", width="10", height=2, highlightcolor="#e1c4c5", bg="#32de97", activebackground="#3c9d9b",fg='#ffffff',##


#Create the box to enter message
EntryBox = Text(base, bd=0, bg="white", font="Arial")
EntryBox.bind('<Return>', send)
EntryBox.pack(side="top", fill="both", expand=True, padx=0, pady=0)


#Place all components on the screen
ChatLog.place(x=6,y=6, height=386, width=370)
EntryBox.place(x=80, y=401, height=90, width=265)
scrollbar.place(x=376,y=6, height=386)
SendButton.place(x=5, y=410, height=71, width=70)

base.mainloop()
Sirius3
User
Beiträge: 17710
Registriert: Sonntag 21. Oktober 2012, 17:20

Der Fehler wurde ja schon in Deinem anderen Thread beantwortet.

Ansonsten brich das Programm auf ein Minimalbeispiel herunter, das den Fehler zeigt.
Benutzeravatar
__blackjack__
User
Beiträge: 13003
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@smaligardezi: Anmerkungen zum Quelltext: Importe gehören nicht mitten in das Modul, sondern an den Anfang, damit man leicht erkennen kann was die Abhängigkeiten von dem Modul sind.

Sternchen-Importe sind Böse™. Da holt man sich gerade bei `tkinter` fast 200 Namen ins Modul von denen nur ein kleiner Bruchteil verwendet wird. Auch Namen die gar nicht in `tkinter` definiert werden, sondern ihrerseits von woanders importiert werden. Das macht Programme unnötig unübersichtlicher und fehleranfälliger und es besteht die Gefahr von Namenskollisionen.

Zum Beispiel ist dann plötzlich die Reihenfolge wichtig in der man alles aus `tkinter` und `Image` aus `PIL` importiert, weil es auch ein `tkinter.Image` gibt.

Auf Modulebene sollte nur Code stehen der Konstanten, Funktionen, und Klassen definiert. Das Hauptprogramm steht üblicherweise in einer Funktion die `main()` heisst.

Daraus folgt dann auch das Funktionen und Methoden alles was sie ausser Konstanten benötigen als Argument(e) übergeben bekommen, weil auf Modulebene keine Variablen existieren.

Namen werden in Python klein_mit_unterstrichen geschrieben. Ausnahmen sind Konstanten (KOMPLETT_GROSS) und Klassen (PascalCase).

Namen sollte nicht kryptisch abgekürzt werden. Wenn man `message` meint, sollte man nicht nur `msg` schreiben. Einbuchstabige Namen gehen in der Regel gar nicht. Ausnahmen Laufindexe (`i`, `j`, `k`) und `x`, `y`, `z` für Koordinaten. Speziell `i` als Laufvariable einer Schleife für etwas anderes als eine ganze Zahl zu verwenden ist sehr irreführend.

Funktionsnamen beschreiben üblicherweise die Tätigkeit, die von der Funktion durchgeführt wird, um sie leichter von eher passiven Werten unterscheiden zu können. `chatbot_response()` ist keine Tätigkeit. Das wäre ein guter Name für die Antwort eines Chat-Bot. `res` ist kein guter Name an der Stelle, weil Abkürzung. Man weiss nicht mal so genau ob nun für `response` oder für `result`.

`send()` funktioniert so nicht mit dem `bind()` weil da ein `Event`-Objekt übergeben wird mit dem `send()` nichts anfangen kann.

Dateien die man öffnet, sollte man auch wieder schliessen. Dazu sollte man die ``with``-Anweisung verwenden, wenn möglich.

Man muss nicht jedes zwischenergebnis an einen Namen binden. Dann muss man sich auch nicht Namen ausdenken die ähnlich klingen und wo man am Ende nicht wirklich weiss was dahinter steckt. Zum Beispiel kann man an `image` und `img` nicht unterscheiden welches das bearbeitete und welches das unbearbeitete Bild ist.

Warum wird von der Eingabe das letzte Zeichen ignoriert?

Wenn man sich die `getResponse()`-Funktion anschaut, scheint die "intents.json" die Informationen in einem falschen Format zu enthalten, beziehungsweise sollte man das im Programm *einmal* in ein passendes Wörterbuch überführen, statt in `getResponse()`.

Grunddatentypen haben nichts in Namen verloren. Den Typen ändert man gar nicht so selten mal während der Programmentwicklung und dann muss man überall im Programm die betroffenen Namen ändern, oder man hat falsche, irreführende Namen im Quelltext.

`predict_class()` wandelt eine Wahrscheinlichkeit in eine Zeichenkette um. Warum? Im Programm selbst werden diese Werte dann auch überhaupt gar nicht mehr verwendet, das kann man also entfernen.

Und es wird auch nur das erste Ergebnis aus der Liste verwendet, was wegen der Sortierung das mit der grössten Wahrscheinlichkeit ist. Da könnte man dann auch `max()` verwenden, statt alles zu sortieren.

`bow()` hat wieder einen schlechten Namen.

Es ist ineffizient da für jedes Wort/Lemma aus dem Satz wieder linear alle Worte durchzugehen. Sinnvoller wäre es aus dem Satz eine Menge von Lemmata zu machen und dann zu schauen ob ein Wort da drin enthalten ist, oder nicht.

Ungetestet:

Code: Alles auswählen

#!/usr/bin/env python3
import json
import pickle
import random
import tkinter as tk
from functools import partial

import nltk
import numpy as np
from nltk.stem import WordNetLemmatizer
from PIL import Image, ImageTk
from tensorflow.keras.models import load_model


def deserialize(deserializer, filename):
    with open(filename, "rb") as file:
        return deserializer.load(file)


load_json = partial(deserialize, json)
load_pickle = partial(deserialize, pickle)


def get_lemmata(lemmatizer, sentence):
    # stem each word - create short form for word
    return {
        lemmatizer.lemmatize(word.lower())
        for word in nltk.word_tokenize(sentence)
    }


def create_word_bitmap(lemmatizer, words, sentence):
    lemmata = get_lemmata(lemmatizer, sentence)
    return np.array([int(word in lemmata) for word in words])


def predict_class(lemmatizer, words, classes, model, sentence):
    return classes[
        max(
            (probability, index)
            for index, probability in enumerate(
                model.predict(
                    create_word_bitmap(lemmatizer, words, sentence).reshape(
                        (1, -1)
                    )
                )[0]
            )
            if probability > 0.25
        )[1]
    ]


def communicate(lemmatizer, words, classes, model, tag_to_responses, message):
    return random.choice(
        tag_to_responses[
            predict_class(lemmatizer, words, classes, model, message)
        ]
    )


def send(
    entry_box,
    chat_log_widget,
    lemmatizer,
    words,
    classes,
    model,
    tag_to_responses,
    _event=None,
):
    message = entry_box.get("1.0", "end-1c").strip()
    entry_box.delete("0.0", tk.END)
    if message:
        response = communicate(
            lemmatizer, words, classes, model, tag_to_responses, message
        )
        chat_log_widget.config(state=tk.NORMAL)
        try:
            chat_log_widget.insert(tk.END, f"You: {message}\n\n")
            chat_log_widget.config(foreground="#442265", font=("Verdana", 12))
            chat_log_widget.insert(tk.END, f"Bot: {response}\n\n")
        finally:
            chat_log_widget.config(state=tk.DISABLED)
        chat_log_widget.yview(tk.END)


def main():
    root = tk.Tk()
    root.title("Borat")
    root.geometry("400x500")  # FIXME Keine absolute Fenstergrösse vorgeben.
    root.resizable(width=True, height=True)

    chat_log_widget = tk.Text(
        root, bd=0, bg="white", font="Arial", state=tk.DISABLED
    )
    chat_log_widget.pack(side=tk.RIGHT, fill=tk.Y, expand=True, padx=5, pady=5)

    scrollbar = tk.Scrollbar(
        root, command=chat_log_widget.yview, cursor="heart"
    )
    chat_log_widget["yscrollcommand"] = scrollbar.set

    image = ImageTk.PhotoImage(
        Image.open(R"C:\Users\pavilion\Desktop\ChatBot\mail.png").resize(
            (68, 65)
        )
    )
    send_button = tk.Button(root, image=image, cursor="dot", relief=tk.FLAT)

    entry_box = tk.Text(root, bd=0, bg="white", font="Arial")
    entry_box.pack(side=tk.TOP, fill=tk.BOTH, expand=True, padx=0, pady=0)
    #
    # FIXME Kein `place()` verwenden!
    #
    chat_log_widget.place(x=6, y=6, height=386, width=370)
    entry_box.place(x=80, y=401, height=90, width=265)
    scrollbar.place(x=376, y=6, height=386)
    send_button.place(x=5, y=410, height=71, width=70)

    on_send = partial(
        send,
        entry_box,
        chat_log_widget,
        WordNetLemmatizer(),
        load_pickle("words.pkl"),
        load_pickle("classes.pkl"),
        load_model("chatbot_model.h5"),
        {
            intent["tag"]: intent["responses"]
            for intent in load_json("intents.json")["intents"]
        },
    )
    send_button["command"] = on_send
    entry_box.bind("<Return>", on_send)

    root.mainloop()


if __name__ == "__main__":
    main()
Die nächsten Schritte wären, da Klassen einzuführen, um nicht so viele Argumente herumreichen zu müssen, und `place()`/absolute Grössen los zu werden.
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
Antworten