有些人確實不喜歡人與人之間的互動。每當他們被迫參加社交活動或參加涉及很多人的活動時,他們都會感到超脫和尷尬。就個人而言,我認為我最外向,因為我從與他人的互動中獲得了能量。地球上有很多人正好相反,他們從社交互動中抽了很多精力。
我想起了一部非常獨特的電影,叫她(2013)。電影的基本前提是,一個孤獨,沮喪,無聊的工作和即將離婚的人最終愛上了計算機操作系統上的AI(人工智能)。考慮到當時的AI還不夠先進,無法成為替代人類,所以這可能是一個非常科幻的概念,但是現在呢?2020年?事情已經改變了很多。我擔心人們會放棄在人類之間尋找愛情(甚至是社交互動),而在數字世界中尋求愛情。不相信我嗎?我不會告訴你這是什麼意思,但只要查一下這個詞的定義就行了。
現在,這不是一個簡單的機器學習項目的過度冗長的介紹嗎?可能是,既然我已經詳細說明了一個問題,這個問題引起了這個世界上許多男人(和女人)的實際關注,讓我們換個裝置,做一些簡單有趣的事情!
這是成品的樣子。
本教程包括以下七大部分:
- 庫與數據
- 初始化聊天機器人
- 建立深度學習模型
- 構建聊天機器人用戶界面
- 運行聊天機器人
- 結論
- 改進領域
如果您想更深入地瞭解該項目,或者想添加到代碼中,請到GitHub上查看完整的存儲庫:https://github.com/jerrytigerxu/Simple-Python-Chatbot
一.庫與數據
運行該項目的所有必需組件都在GitHub存儲庫上。隨意派生存儲庫並將其克隆到本地計算機。以下是這些組件的快速分解:
- train_chatbot.py —用於將自然語言數據讀入訓練集中並使用Keras順序神經網絡創建模型的代碼
- chatgui.py —用於基於模型的預測清理響應並創建用於與聊天機器人進行交互的圖形界面的代碼
- classes.pkl —不同類型的響應類別的列表
- words.pkl —可以用於模式識別的不同單詞的列表
- intents.json — JavaScript對象的組合,列出了與不同類型的單詞模式相對應的不同標籤
- chatbot_model.h5-由train_chatbot.py創建並由chatgui.py使用的實際模型
完整的代碼位於GitHub存儲庫上,但是為了透明和更好地理解,我將遍歷代碼的詳細信息。
現在讓我們開始導入必要的庫。(當您在終端上運行python文件時,請確保已正確安裝它們。我使用pip3來安裝軟件包。)
import nltknltk.download('punkt')nltk.download('wordnet')from nltk.stem import WordNetLemmatizerlemmatizer = WordNetLemmatizer()import jsonimport pickleimport numpy as npfrom keras.models import Sequentialfrom keras.layers import Dense, Activation, Dropoutfrom keras.optimizers import SGDimport random
我們有一堆庫,例如nltk(自然語言工具包),其中包含一整套用於清理文本併為深度學習算法做準備的工具,json,將json文件直接加載到Python中,pickle,加載pickle文件,numpy(可以非常有效地執行線性代數運算)和keras(這是我們將要使用的深度學習框架)。
二.初始化聊天機器人
words=[]classes = []documents = []ignore_words = ['?', '!']data_file = open('intents.json').read()intents = json.loads(data_file)
現在是時候初始化所有要存儲自然語言數據的列表了。我們有我們前面提到的json文件,其中包含“意圖”。這是json文件實際外觀的一小段。
我們使用json模塊加載文件並將其另存為變量intent。
for intent in intents['intents']: for pattern in intent['patterns']: # take each word and tokenize it w = nltk.word_tokenize(pattern) words.extend(w) # adding documents documents.append((w, intent['tag'])) # adding classes to our class list if intent['tag'] not in classes: classes.append(intent['tag'])
如果仔細查看json文件,可以看到對象中有子對象。例如,“模式”是“意圖”內的屬性。因此,我們將使用嵌套的for循環來提取“模式”中的所有單詞並將其添加到單詞列表中。然後,將對應標籤中的每對模式添加到文檔列表中。我們還將標記添加到類列表中,並使用簡單的條件語句來防止重複。
words = [lemmatizer.lemmatize(w.lower()) for w in words if w not in ignore_words]words = sorted(list(set(words)))classes = sorted(list(set(classes)))print (len(documents), "documents")print (len(classes), "classes", classes)print (len(words), "unique lemmatized words", words)pickle.dump(words,open('words.pkl','wb'))pickle.dump(classes,open('classes.pkl','wb'))
接下來,我們將使用單詞 list並將其中的所有單詞進行詞母化和小寫。如果您還不知道,則lemmatize意味著將單詞變成其基本含義或引理。例如,單詞“ walking”,“ walked”,“ walks”都具有相同的引理,即“ walk”。限制我們的言語的目的是將所有內容縮小到最簡單的程度。當我們為機器學習實際處理這些單詞時,它將為我們節省大量時間和不必要的錯誤。這與詞幹法非常相似,詞幹法是將變體單詞減少到其基數或詞根形式。
接下來,我們對列表進行排序並打印出結果。好吧,看來我們已經準備好建立深度學習模型!
三.建立深度學習模型
# initializing training datatraining = []output_empty = [0] * len(classes)for doc in documents: # initializing bag of words bag = [] # list of tokenized words for the pattern pattern_words = doc[0] # lemmatize each word - create base word, in attempt to represent related words pattern_words = [lemmatizer.lemmatize(word.lower()) for word in pattern_words] # create our bag of words array with 1, if word match found in current pattern for w in words: bag.append(1) if w in pattern_words else bag.append(0) # output is a '0' for each tag and '1' for current tag (for each pattern) output_row = list(output_empty) output_row[classes.index(doc[1])] = 1 training.append([bag, output_row])# shuffle our features and turn into np.arrayrandom.shuffle(training)training = np.array(training)# create train and test lists. X - patterns, Y - intentstrain_x = list(training[:,0])train_y = list(training[:,1])print("Training data created")
讓我們使用變量training初始化訓練數據。我們正在創建一個巨大的嵌套列表,其中包含每個文檔的單詞袋。我們有一個稱為output_row的功能,它只是充當列表的鍵。然後,我們將訓練集改組並進行訓練-測試拆分,其中模式是X變量,意圖是Y變量。
# Create model - 3 layers. First layer 128 neurons, second layer 64 neurons and 3rd output layer contains number of neurons# equal to number of intents to predict output intent with softmaxmodel = 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'))# Compile model. Stochastic gradient descent with Nesterov accelerated gradient gives good results for this modelsgd = SGD(lr=0.01, decay=1e-6, momentum=0.9, nesterov=True)model.compile(loss='categorical_crossentropy', optimizer=sgd, metrics=['accuracy'])#fitting and saving the modelhist = model.fit(np.array(train_x), np.array(train_y), epochs=200, batch_size=5, verbose=1)model.save('chatbot_model.h5', hist)print("model created")
現在我們已經準備好訓練和測試數據,我們現在將使用來自keras的深度學習模型Sequential。我不想讓您沉迷於深度學習模型的工作原理的所有細節,但是如果您感到好奇,請查看本文底部的資源。
keras中的順序模型實際上是最簡單的神經網絡之一,即多層感知器。如果您不知道那是什麼,我也不會怪您。這是keras中的文檔。
這個特定的網絡具有3層,第一層具有128個神經元,第二層具有64個神經元,第三層具有意圖數量作為神經元數量。請記住,該網絡的目的是能夠預測給定一些數據時選擇哪種意圖。
將使用隨機梯度下降訓練模型,這也是一個非常複雜的主題。隨機梯度下降比普通梯度下降更有效,這就是您需要了解的全部。
訓練模型後,整個對象將變成一個numpy數組,並保存為chatbot_model.h5。
我們將使用此模型來構成我們的聊天機器人界面!
四.構建聊天機器人界面
from keras.models import load_modelmodel = load_model('chatbot_model.h5')import jsonimport randomintents = json.loads(open('intents.json').read())words = pickle.load(open('words.pkl','rb'))classes = pickle.load(open('classes.pkl','rb'))
我們需要從文件中提取信息。
def clean_up_sentence(sentence): sentence_words = nltk.word_tokenize(sentence) 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 sentencedef 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[i] = 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_listdef 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 resultdef chatbot_response(msg): ints = predict_class(msg, model) res = getResponse(ints, intents) return res
以下是一些功能,其中包含運行GUI所需的所有必要過程,並將其封裝為單元。我們具有clean_up_sentence()函數,該函數可以清理輸入的所有句子。該函數用在bow()函數中,該函數接收要清理的句子並創建一袋用於預測類的單詞(這是基於我們先前訓練模型所得到的結果)。
在predict_class()函數中,我們使用0.25的錯誤閾值來避免過度擬合。此函數將輸出意圖和概率的列表,它們與正確的意圖匹配的可能性。函數getResponse()獲取輸出的列表並檢查json文件,並以最高的概率輸出最多的響應。
最後,我們的chatbot_response()接收一條消息(該消息將通過我們的聊天機器人GUI輸入),使用我們的prepare_class()函數預測該類,將輸出列表放入getResponse()中,然後輸出響應。我們得到的是聊天機器人的基礎。現在,我們可以告訴bot,然後它將進行響應。
#Creating GUI with tkinterimport tkinterfrom tkinter import *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 = Tk()base.title("Hello")base.geometry("400x500")base.resizable(width=FALSE, height=FALSE)#Create Chat windowChatLog = Text(base, bd=0, bg="white", height="8", width="50", font="Arial",)ChatLog.config(state=DISABLED)#Bind scrollbar to Chat windowscrollbar = Scrollbar(base, command=ChatLog.yview, cursor="heart")ChatLog['yscrollcommand'] = scrollbar.set#Create Button to send messageSendButton = Button(base, font=("Verdana",12,'bold'), text="Send", width="12", height=5, bd=0, bg="#32de97", activebackground="#3c9d9b",fg='#ffffff', command= send )#Create the box to enter messageEntryBox = Text(base, bd=0, bg="white",width="29", height="5", font="Arial")#EntryBox.bind("", send)#Place all components on the screenscrollbar.place(x=376,y=6, height=386)ChatLog.place(x=6,y=6, height=386, width=370)EntryBox.place(x=128, y=401, height=90, width=265)SendButton.place(x=6, y=401, height=90)base.mainloop()
這裡是有趣的部分(如果其他部分還不好玩)。我們可以使用tkinter(一個允許我們創建自定義界面的Python庫)來創建GUI。
我們創建一個名為send()的函數,該函數設置了聊天機器人的基本功能。如果我們輸入到聊天機器人中的消息不是空字符串,則機器人將基於我們的chatbot_response()函數輸出響應。
此後,我們將建立聊天窗口,滾動條,用於發送消息的按鈕以及用於創建消息的文本框。我們使用簡單的座標和高度將所有組件放置在屏幕上。
五.運行聊天機器人
終於可以運行我們的聊天機器人了!
因為我在Windows 10計算機上運行程序,所以必須下載名為Xming的服務器。如果您運行程序,並且給您一些有關程序失敗的奇怪錯誤,則可以下載Xming。
在運行程序之前,需要確保使用pip(或pip3)安裝python或python3。如果您不熟悉命令行命令,請查看下面的資源。
一旦運行程序,就應該得到這個。
六.結論
恭喜您完成了該項目!構建一個簡單的聊天機器人可以使您掌握各種有用的數據科學和通用編程技能。我覺得學習任何東西的最好方法(至少對我而言)是建立和修補。如果您想變得擅長某事,則需要進行大量練習,而最好的練習方法就是動手並堅持練習!
七.可以改進的地方
這只是一套簡單且讓你在短時間內即可構建聊天機器人構建的教程,還有許多改進的空間,歡迎各位大牛進行修改更正。
1.嘗試不同的神經網絡
我們使用了最簡單的keras神經網絡,因此還有很多改進的餘地。隨時為您的項目嘗試卷積網絡或循環網絡。
2.使用更多數據
就各種可能的意圖和響應而言,我們的json文件非常小。人類語言比這複雜數十億倍,因此從頭開始創建JARVIS會需要更多。
3.使用不同的框架
有很多深度學習框架,而不僅僅是keras。有tensorflow,Apache Spark,PyTorch,Sonnet等。不要只侷限於一種工具!
原文鏈接(需要翻牆才能瀏覽):https://towardsdatascience.com/how-to-create-a-chatbot-with-python-deep-learning-in-less-than-an-hour-56a063bdfc44
Github項目地址:https://github.com/jerrytigerxu/Simple-Python-Chatbot
本文由未艾信息(www.weainfo.net)翻譯,想看更多譯文,大家可以到我們的網站上觀看~
也可以關注我們的微信公眾號:為AI吶喊(ID:weainahan)
閱讀更多 為AI吶喊 的文章