快來(lái)!建立你的第一個(gè)Python聊天機(jī)器人項(xiàng)目
利用Python,我們可以實(shí)現(xiàn)很多目標(biāo),比如說(shuō)建立一個(gè)你專屬的聊天機(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è)智能軟件,可以像人類一樣與人互動(dòng)和交流。很有趣,不是嗎?現(xiàn)在來(lái)看看它們是如何工作的。
所有聊天機(jī)器人都基于自然語(yǔ)言處理(NLP)概念。NLP由兩部分組成:
- NLU(自然語(yǔ)言理解):機(jī)器理解人類語(yǔ)言(如英語(yǔ))的能力。
- NLG(自然語(yǔ)言生成):機(jī)器生成類似于人類書(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í)模型,該模型可以分類和識(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í)要分類的所有標(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)添加與模式相關(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)目的詞匯表,類包含了要分類的所有實(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)創(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ù)量與類數(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è)很好的圖形用戶界面。可以將文件命名為gui_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)試試吧~























