快來(lái)!建立你的第一個(gè)Python聊天機(jī)器人項(xiàng)目
利用Python,我們可以實(shí)現(xiàn)很多目標(biāo),比如說(shuō)建立一個(gè)你專(zhuān)屬的聊天機(jī)器人程序。
聊天機(jī)器人程序不光滿足個(gè)人需求,它對(duì)商業(yè)組織和客戶都非常有幫助。大多數(shù)人喜歡直接通過(guò)聊天室交談,而不是打電話給服務(wù)中心。
Facebook發(fā)布的數(shù)據(jù)證明了機(jī)器人的價(jià)值。每月有超過(guò)20億條信息在人和公司之間發(fā)送。HubSpot的研究顯示,71%的人希望從信息應(yīng)用程序獲得客戶支持。這是解決問(wèn)題的快速方法,因此聊天機(jī)器人在組織中有著光明的未來(lái)。
今天要做的是在Chatbot上建立一個(gè)令人興奮的項(xiàng)目。從零開(kāi)始完成一個(gè)聊天機(jī)器人,它將能夠理解用戶正在談?wù)摰膬?nèi)容并給出適當(dāng)?shù)幕貞?yīng)。
先決條件
為了實(shí)現(xiàn)聊天機(jī)器人,將使用一個(gè)深度學(xué)習(xí)庫(kù)Keras,一個(gè)自然語(yǔ)言處理工具包NLTK,以及一些有用的庫(kù)。運(yùn)行以下命令以確保安裝了所有庫(kù):
- pip installtensorflow keras pickle nltk
 
聊天機(jī)器人是如何工作的?
聊天機(jī)器人只是一個(gè)智能軟件,可以像人類(lèi)一樣與人互動(dòng)和交流。很有趣,不是嗎?現(xiàn)在來(lái)看看它們是如何工作的。
所有聊天機(jī)器人都基于自然語(yǔ)言處理(NLP)概念。NLP由兩部分組成:
- NLU(自然語(yǔ)言理解):機(jī)器理解人類(lèi)語(yǔ)言(如英語(yǔ))的能力。
 - NLG(自然語(yǔ)言生成):機(jī)器生成類(lèi)似于人類(lèi)書(shū)面句子的文本的能力。
 
想象一個(gè)用戶問(wèn)聊天機(jī)器人一個(gè)問(wèn)題:“嘿,今天有什么新聞?”
該聊天機(jī)器人就會(huì)將用戶語(yǔ)句分解為兩個(gè)部分:意圖和實(shí)體。這句話的目的可能是獲取新聞,因?yàn)樗傅氖怯脩粝M麍?zhí)行的操作。實(shí)體告訴了關(guān)于意圖的具體細(xì)節(jié),所以“今天”將是實(shí)體。因此,這里使用機(jī)器學(xué)習(xí)模型來(lái)識(shí)別聊天的意圖和實(shí)體。
項(xiàng)目文件結(jié)構(gòu)
項(xiàng)目完成后,將留下所有這些文件。快速瀏覽每一個(gè)。它將給開(kāi)發(fā)員一個(gè)如何實(shí)施該項(xiàng)目的想法。
- Train_chatbot.py-在本文件中,構(gòu)建和訓(xùn)練深度學(xué)習(xí)模型,該模型可以分類(lèi)和識(shí)別用戶向機(jī)器人提出的要求。
 - Gui_Chatbot.py-這個(gè)文件是構(gòu)建圖形用戶界面用來(lái)與訓(xùn)練后的聊天機(jī)器人聊天的地方。
 - Intents.json-Intents文件包含將用于訓(xùn)練模型的所有數(shù)據(jù)。它包含一組標(biāo)記及其相應(yīng)的模式和響應(yīng)。
 - Chatbot_model.h5-這是一個(gè)分層數(shù)據(jù)格式文件,其中存儲(chǔ)了訓(xùn)練模型的權(quán)重和體系結(jié)構(gòu)。
 - Classes.pkl-pickle文件可用于存儲(chǔ)預(yù)測(cè)消息時(shí)要分類(lèi)的所有標(biāo)記名。
 - Words.pkl-Words.pklpickle文件包含模型詞匯表中的所有唯一單詞。
 
下載源代碼和數(shù)據(jù)集:
mailto:https://drive.google.com/drive/folders/1r6MrrdE8V0bWBxndGfJxJ4Om62dJ2OMP?usp=sharing
如何建立自己的聊天機(jī)器人?
筆者將這個(gè)聊天機(jī)器人的構(gòu)建簡(jiǎn)化為5個(gè)步驟:
第一步:導(dǎo)入庫(kù)并加載數(shù)據(jù)
創(chuàng)建一個(gè)新的python文件并將其命名為train_chatbot,然后導(dǎo)入所有必需的模塊。之后,從Python程序中讀取JSON數(shù)據(jù)文件。
- importnumpy as np
 - fromkeras.models importSequential
 - fromkeras.layers importDense, Activation,Dropout
 - fromkeras.optimizers importSGD
 - importrandom
 - importnltk
 - fromnltk.stem importWordNetLemmatizer
 - lemmatizer = WordNetLemmatizer()
 - importjson
 - importpickle
 - intents_file = open('intents.json').read()
 - intents= json.loads(intents_file)
 
第二步:數(shù)據(jù)預(yù)處理
模型無(wú)法獲取原始數(shù)據(jù)。為了使機(jī)器容易理解,必須經(jīng)過(guò)許多預(yù)處理。對(duì)于文本數(shù)據(jù),有許多預(yù)處理技術(shù)可用。第一種技術(shù)是標(biāo)記化,把句子分解成單詞。
通過(guò)觀察intents文件,可以看到每個(gè)標(biāo)記包含模式和響應(yīng)的列表。標(biāo)記每個(gè)模式并將單詞添加到列表中。另外,創(chuàng)建一個(gè)類(lèi)和文檔列表來(lái)添加與模式相關(guān)的所有意圖。
- words=[]
 - classes= []
 - documents= []
 - ignore_letters = ['!', '?', ',', '.']
 - forintent in intents['intents']:
 - forpattern in intent['patterns']:
 - #tokenize each word
 - word= nltk.word_tokenize(pattern)
 - words.extend(word)
 - #add documents in the corpus
 - documents.append((word, intent['tag']))
 - # add to our classes list
 - ifintent['tag'] notin classes:
 - classes.append(intent['tag'])
 - print(documents)
 
另一種技術(shù)是詞形還原。我們可以將單詞轉(zhuǎn)換成引理形式,這樣就可以減少所有的規(guī)范單詞。例如,單詞play、playing、playing、played等都將替換為play。這樣,可以減少詞匯表中的單詞總數(shù)。所以將每個(gè)單詞進(jìn)行引理,去掉重復(fù)的單詞。
- # lemmaztize and lower each word andremove duplicates
 - words= [lemmatizer.lemmatize(w.lower()) forw in words if w notinignore_letters]
 - words= sorted(list(set(words)))
 - # sort classes
 - classes= sorted(list(set(classes)))
 - # documents = combination betweenpatterns and intents
 - print(len(documents), "documents")
 - # classes = intents
 - print(len(classes), "classes", classes)
 - # words = all words, vocabulary
 - print(len(words), "unique lemmatized words", words)
 - pickle.dump(words,open('words.pkl','wb'))
 - pickle.dump(classes,open('classes.pkl','wb'))
 
最后,單詞包含了項(xiàng)目的詞匯表,類(lèi)包含了要分類(lèi)的所有實(shí)體。為了將python對(duì)象保存在文件中,使用pickle.dump()方法。這些文件將有助于訓(xùn)練完成后進(jìn)行預(yù)測(cè)聊天。
第三步:創(chuàng)建訓(xùn)練集和測(cè)試集
為了訓(xùn)練模型,把每個(gè)輸入模式轉(zhuǎn)換成數(shù)字。首先,對(duì)模式中的每個(gè)單詞進(jìn)行引理,并創(chuàng)建一個(gè)長(zhǎng)度與單詞總數(shù)相同的零列表。只將值1設(shè)置為那些在模式中包含單詞的索引。同樣,將1設(shè)置為模式所屬的類(lèi)輸入,來(lái)創(chuàng)建輸出。
- # create the training data
 - training= []
 - # create empty array for the output
 - output_empty = [0] * len(classes)
 - # training set, bag of words for everysentence
 - fordoc in documents:
 - # initializing bag of words
 - bag= []
 - # list of tokenized words for thepattern
 - word_patterns = doc[0]
 - # lemmatize each word - create baseword, in attempt to represent related words
 - word_patterns = [lemmatizer.lemmatize(word.lower()) for word in word_patterns]
 - # create the bag of words array with1, if word is found in current pattern
 - forword in words:
 - bag.append(1) if word inword_patterns 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 the features and make numpyarray
 - random.shuffle(training)
 - training= np.array(training)
 - # create training and testing lists. X- patterns, Y - intents
 - train_x= list(training[:,0])
 - train_y= list(training[:,1])
 - print("Training data is created")
 
第四步:訓(xùn)練模型
該模型將是一個(gè)由3個(gè)密集層組成的神經(jīng)網(wǎng)絡(luò)。第一層有128個(gè)神經(jīng)元,第二層有64個(gè),最后一層的神經(jīng)元數(shù)量與類(lèi)數(shù)相同。為了減少模型的過(guò)度擬合,引入了dropout層。使用SGD優(yōu)化器并對(duì)數(shù)據(jù)進(jìn)行擬合,開(kāi)始模型的訓(xùn)練。在200個(gè)階段的訓(xùn)練完成后,使用Kerasmodel.save(“chatbot_model.h5”)函數(shù)保存訓(xùn)練的模型。
- # deep neural networds model
 - 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'))
 - # Compiling model. SGD with Nesterovaccelerated gradient gives good results for this model
 - sgd= SGD(lr=0.01,decay=1e-6, momentum=0.9, nesterov=True)
 - model.compile(loss='categorical_crossentropy', optimizer=sgd, metrics=['accuracy'])
 - #Training and saving the model
 - hist= 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 is created")
 
第五步:與聊天機(jī)器人互動(dòng)
模型已經(jīng)準(zhǔn)備好聊天了,現(xiàn)在在一個(gè)新文件中為聊天機(jī)器人創(chuàng)建一個(gè)很好的圖形用戶界面??梢詫⑽募麨間ui_chatbot.py
在GUI文件中,使用Tkinter模塊構(gòu)建桌面應(yīng)用程序的結(jié)構(gòu),然后捕獲用戶消息,并在將消息輸入到訓(xùn)練模型之前,再次執(zhí)行一些預(yù)處理。
然后,模型將預(yù)測(cè)用戶消息的標(biāo)簽,從intents文件的響應(yīng)列表中隨機(jī)選擇響應(yīng)。
這是GUI文件的完整源代碼。
- importnltk
 - fromnltk.stem importWordNetLemmatizer
 - lemmatizer = WordNetLemmatizer()
 - importpickle
 - importnumpy as np
 - fromkeras.models importload_model
 - model= load_model('chatbot_model.h5')
 - importjson
 - importrandom
 - intents= json.loads(open('intents.json').read())
 - words= pickle.load(open('words.pkl','rb'))
 - classes= pickle.load(open('classes.pkl','rb'))
 - defclean_up_sentence(sentence):
 - # tokenize the pattern - splittingwords into array
 - sentence_words = nltk.word_tokenize(sentence)
 - # stemming every word - reducing tobase form
 - sentence_words = [lemmatizer.lemmatize(word.lower()) for word in sentence_words]
 - returnsentence_words
 - # return bag of words array: 0 or 1for words that exist in sentence
 - defbag_of_words(sentence, words,show_details=True):
 - # tokenizing patterns
 - sentence_words = clean_up_sentence(sentence)
 - # bag of words - vocabulary matrix
 - bag= [0]*len(words)
 - fors in sentence_words:
 - fori,word inenumerate(words):
 - ifword == s:
 - # assign 1 if current word is in thevocabulary position
 - bag[i] = 1
 - ifshow_details:
 - print("found in bag:%s" % word)
 - return(np.array(bag))
 - defpredict_class(sentence):
 - # filter below thresholdpredictions
 - p= bag_of_words(sentence,words,show_details=False)
 - res= model.predict(np.array([p]))[0]
 - ERROR_THRESHOLD = 0.25
 - results= [[i,r] fori,r inenumerate(res) ifr>ERROR_THRESHOLD]
 - # sorting strength probability
 - results.sort(key=lambdax: x[1],reverse=True)
 - return_list = []
 - forr in results:
 - return_list.append({"intent": classes[r[0]],"probability": str(r[1])})
 - returnreturn_list
 - defgetResponse(ints, intents_json):
 - tag= ints[0]['intent']
 - list_of_intents = intents_json['intents']
 - fori in list_of_intents:
 - if(i['tag']== tag):
 - result= random.choice(i['responses'])
 - break
 - returnresult
 - #Creating tkinter GUI
 - importtkinter
 - fromtkinter import *
 - defsend():
 - msg= EntryBox.get("1.0",'end-1c').strip()
 - EntryBox.delete("0.0",END)
 - ifmsg != '':
 - ChatBox.config(state=NORMAL)
 - ChatBox.insert(END, "You: " + msg+ '\n\n')
 - ChatBox.config(foreground="#446665", font=("Verdana", 12 ))
 - ints= predict_class(msg)
 - res= getResponse(ints,intents)
 - ChatBox.insert(END, "Bot: " + res+ '\n\n')
 - ChatBox.config(state=DISABLED)
 - ChatBox.yview(END)
 - root= Tk()
 - root.title("Chatbot")
 - root.geometry("400x500"
 - root.resizable(width=FALSE, height=FALSE)
 - #Create Chat window
 - ChatBox= Text(root, bd=0, bg="white",height="8", width="50", font="Arial",)
 - ChatBox.config(state=DISABLED)
 - #Bind scrollbar to Chat window
 - scrollbar= Scrollbar(root,command=ChatBox.yview, cursor="heart")
 - ChatBox['yscrollcommand'] = scrollbar.set
 - #Create Button to send message
 - SendButton = Button(root,font=("Verdana",12,'bold'),text="Send", width="12", height=5,
 - bd=0,bg="#f9a602",activebackground="#3c9d9b",fg='#000000',
 - command=send )
 - #Create the box to enter message
 - EntryBox= Text(root, bd=0, bg="white",width="29", height="5", font="Arial")
 - #EntryBox.bind("<Return>",send)
 - #Place all components on the screen
 - scrollbar.place(x=376,y=6, height=386)
 - ChatBox.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)
 - root.mainloop()
 
運(yùn)行聊天機(jī)器人
現(xiàn)在有兩個(gè)獨(dú)立的文件,一個(gè)是train_chatbot.py,首先使用它來(lái)訓(xùn)練模型。
- pythontrain_chatbot.py
 
快來(lái)試試吧~















 
 
 





 
 
 
 