Chat bot / Nltk

Stellt hier eure Projekte vor.
Internetseiten, Skripte, und alles andere bzgl. Python.
Antworten
lacreature
User
Beiträge: 17
Registriert: Dienstag 5. März 2019, 23:34

Moin zusammen,
ich habe heute mal versucht einen (sehr simplen) Chatbot zu coden,
die Begrüßung bekommt er nicht hin, danach kommt eine Fehlermeldung,
hat hier vielleicht jemand eine Idee wie ich das lösen könnte?

Code: Alles auswählen

import nltk
import numpy as np
import random
import string
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.metrics.pairwise import cosine_similarity


f = open('AVP.txt', 'r', errors='ignore')
raw = f.read()
text = raw.lower()

Token_sent = nltk.sent_tokenize(text)
Token_word = nltk.word_tokenize(text)


lemmer = nltk.stem.WordNetLemmatizer()

def LemToken(tokens):
    return [lemmer.lemmatize(token) for token in tokens]
remove_punct_dict = dict((ord(punct), None) for punct in string.punctuation)

def LemNormalize(text):
    return
LemToken(nltk.word_tokenize(text.lower().translate(remove_punct_dict)))

Greeting_input = ("hello", "hi")
Greeting_response = ["yo", "what's up", "what you want?"]

def greeting(sentence):
    for word in sentence.split():
        if word.lower() in Greeting_input:
            return random.choice(Greeting_response)

def response(user_response):
    robo_response = ' '
    Token_sent.append(user_response)

    TfidfVec = TfidfVectorizer(tokenizer=LemNormalize,
                               stop_words='english')
    tfidf = TfidfVec.fit_transform(Token_sent)
    vals = cosine_similarity(tfidf[-1], tfidf)
    idx = vals.argsort()[0][-2]
    flat = vals.flatten()
    flat.sort()
    req_tfidf = flat[-2]

    if(req_tfidf==0):
        robo_response = robo_response + "I don't know what you want from me and I simply don't care"
        return robo_response
    else:
        robo_response = robo_response + Token_sent[idx]
        return robo_response

flag = True
print("RadicalBot:  i can talk to you...try it")

while(flag==True):
    user_response = input()
    user_response = user_response.lower()
    if(user_response != 'fuck off'):
        if(user_response == 'yes'):
            flag = False
            print("RadicalBot:  ok ..")
        else:
            if(greeting(user_response) != None):
                print(greeting(user_response))
            else:
                print("RadicalBot: ")
                print(response(user_response))
                Token_sent.remove(user_response)
    else:
        flag = False
        print("RadicalBot:  i leave from here..")
lacreature
User
Beiträge: 17
Registriert: Dienstag 5. März 2019, 23:34

Das ist die Fehlermeldung:

Code: Alles auswählen

Traceback (most recent call last):
  File "/home/m1ghtfr3e/Chatbot.py", line 70, in <module>
    print(response(user_response))
  File "/home/m1ghtfr3e/Chatbot.py", line 41, in response
    tfidf = TfidfVec.fit_transform(Token_sent)
  File "/usr/local/lib/python3.7/dist-packages/sklearn/feature_extraction/text.py", line 1613, in fit_transform
    X = super(TfidfVectorizer, self).fit_transform(raw_documents)
  File "/usr/local/lib/python3.7/dist-packages/sklearn/feature_extraction/text.py", line 1031, in fit_transform
    self.fixed_vocabulary_)
  File "/usr/local/lib/python3.7/dist-packages/sklearn/feature_extraction/text.py", line 943, in _count_vocab
    for feature in analyze(doc):
  File "/usr/local/lib/python3.7/dist-packages/sklearn/feature_extraction/text.py", line 329, in <lambda>
    tokenize(preprocess(self.decode(doc))), stop_words)
  File "/usr/local/lib/python3.7/dist-packages/sklearn/feature_extraction/text.py", line 153, in _word_ngrams
    tokens = [w for w in tokens if w not in stop_words]
TypeError: 'NoneType' object is not iterable
Könnte es an dem Modul liegen, oder habe ich einfach nur was übersehen?
Benutzeravatar
__blackjack__
User
Beiträge: 13003
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@lacreature: Die Vermischung von Code und Funktionsdefinitionen ist sehr unübersichtlich. Auf Modulebene sollte nur Code stehen der Konstanten, Funktionen, und Klassen definiert. Das Hauptprogramm steht üblicherweise in einer Funktion die `main()` heisst. Wenn Du das aufräumst, sollte Dir auch der Fehler auffallen der in dem Code steckt und für `None`-Werte sorgt die dann zu der gezeigten Ausnahme führen.

Dateien die man öffnet, sollte man auch wieder schliessen. Die ``with``-Anweisung ist in dem Zusammenhang nützlich.

Beim öffnen von Textdateien würde ich immer eine explizite Kodierung angeben.

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

Container-/Sequenzwerte werden üblicherweise in der Mehrzahl benannt, damit der Leser weiss, das es sich nicht um einen Einzelwert handelt.

Numpy wird importiert, aber nicht verwendet. `Token_word` wird definiert, aber nicht verwendet.

Namen sollten nicht zu weit von der Stelle entfernt definiert werden, wo man sie dann auch tatsächlich benötigt. `Token_sent` kann man bis vor die ``while``-Schleife verschieben, das muss nicht schon so weit am Anfang definiert werden. Wenn zwischen Definition und Verwendung viel anderer Code steht, macht es das Programm unübersichtlicher und Änderungen schwerer und fehleranfälliger.

Grunddatentypen haben in Namen nichts zu suchen. Wenn man den Typ ändert muss man sonst überall die betroffenen Namen anpassen, oder man hat falsche, irreführende Namen im Quelltext stehen.

Um Bedingungen bei ``while`` und ``if`` gehören keine Klammern. Wenn man dann auch noch kein Leerzeichen setzt, sieht das einem Funktionsaufruf sehr ähnlich.

`flag` ist überflüssig, weil man hier einfach eine ”Endlosschleife” schreiben kann, die an den entsprechenden Stellen mit ``break`` verlassen wird.

Man vergleicht auch nicht auf literale `True`/`False`-Werte mit ``==`` oder ``!=``. Da kommt ja nur wieder ein Wahrheitswert heraus. Entweder den, den man sowieso schon hatte, oder das Gegenteil. Im ersten Fall kann man gleich den Wert nehmen, den man schon hat. Für den zweiten Fall negiert man den mit ``not``.

Das `greeting()` potentiell zweimal aufgerufen wird, mit dem selben Argument, was zum selben Ergebnis führen wird, ist ineffizient. Man würde das einmal aufrufen und sich das Ergebnis merken. An der Stelle fällt dann auf, dass der Funktionsname ungünstig ist, denn das Ergebnis dieser Funktion würde sich als `greeting` ganz gut machen, weil der Wert ja eine Begrüssung darstellt. Deswegen benennt man Funktionen normalerweise nach der Tätigkeit die sie ausführen.

Bei der Funktion sollte man zudem am Ende explizit ein ``return None`` rein schreiben um klar zu machen, dass der Fall vorkommen kann, und das man da dann `None` als Rückgabe haben möchte.

Bei der Begrüssung scheint die einzige Stelle zu sein, wo der Bot nicht seinen Namen davor setzt – soll das so sein, oder wurde das nur übersehen?

Diese Wiederholung des Präfixes würde man auch besser irgendwie aus dem Code heraus ziehen, zum Beispiel durch eine eigene `print()`-Funktion die das immer vor die Ausgabe(n) setzt. Dann muss man das nur an *einer* Stelle im Programm ändern, wenn man diesen Botnamen mal anpassen will/muss.

Das bei `Token_sent` *in* der Funktion `response()` die Benutzereingabe hinzugefügt wird, und *nachdem* die Funktion ausgeführt wurde wieder entfernt wird, ist etwas assymmetrisch und kann leicht übersehen werden. Zudem wird beim hinzufügen `append()` verwendet und beim entfernen `remove()` – Dir ist klar das der Benutzer damit dauerhaft Einträge aus `Token_sent` entfernen kann wenn er etwas eingibt was in dieser Liste schon vorhanden ist‽ Ist das so gewollt, oder sollte das `remove()` eigentlich ein `pop()` sein?

Wenn man das aus der `response()`-Funktion heraus nimmt, dann braucht die auch das `user_response`-Argument nicht mehr. Dafür muss man ihr `Token_sent` übergeben. Funktionen und Methoden sollten alles was sie ausser Konstanten benötigen als Argument(e) übergeben bekommen. Wenn das Hauptprogramm in einer Funktion steht, gibt es so ein globales `Token_sent` ja auch gar nicht mehr.

Und wenn das `remove()` eigentlich ein `pop()` sein sollte, kann man sich `append()` und `pop()` ganz sparen in dem man `response()` eine Kopie der `Token_sent`-Liste + der Benutzereingabe übergibt. Das ist etwas mehr Rechen- und Speicheraufwand, wäre aber funktional gesehen die sauberere Lösung.

Ich sehe keinen Sinn in der `robo_response`-Variable in `response()`. Wenn man die entfernt, wird der Code einfacher.
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
Antworten