Artificial intelligence is no better than that. Based on the python 3 deep learning library keras / tensorflow, build your own chat robot

Time:2021-8-5

The original text is reproduced from “Liu Yue’s technology blog”https://v3u.cn/a_id_178

We are not unfamiliar with the concept of chat robot. Maybe you have flirted with Siri in boredom, or talked and laughed with Xiao AI’s classmates in your spare time. Anyway, we have to admit that artificial intelligence has gone deep into our life. At present, there are numerous robots providing three-party APIs on the market: Microsoft Xiaobing, Turing robot, Tencent chat, Qingyun guest robot, etc. we can access it on the app or web application at any time as long as we want. However, how to realize the underlying of these applications? Without network access, can we be like American dramasWest worldAs described in the description, the robot can communicate with humans only by storing the local “mental ball”. If you are not satisfied with being a “transfer man”, please follow our journey. This time, we will use the deep learning library keras / tensorflow for the first time to build our own local chat robot, which does not rely on any three-party interface and network.

Install related dependencies first:

pip3 install Tensorflow  
pip3 install Keras  
pip3 install nltk

Then write the script test\_ Bot.py import required libraries:

import nltk  
import ssl  
from nltk.stem.lancaster import LancasterStemmer  
stemmer = LancasterStemmer()  
  
import numpy as np  
from keras.models import Sequential  
from keras.layers import Dense, Activation, Dropout  
from keras.optimizers import SGD  
import pandas as pd  
import pickle  
import random

There is a hole here, that is, the natural language analysis library nltk will report an error:



Resource punkt not found

Normally, just add a line of downloader code

import nltk  
nltk.download('punkt')

However, due to academic Internet access, it is difficult to download normally through the python Downloader, so we play a curve to save the country and manually download the compressed package ourselves:

https://raw.githubusercontent.com/nltk/nltk_data/gh-pages/packages/tokenizers/punkt.zip

After decompression, put it in your user directory:

C:\Users\liuyue\tokenizers\nltk_data\punkt

OK, to get back to business, the main challenge in developing a chat robot is to classify the user input information and identify the correct intention of human beings (this can be solved by machine learning, but it’s too complex. I’m lazy, so I use deep learning keras). The second is how to maintain the context, that is, analyze and track the context. Generally, we don’t need to classify the user’s intention. We just need to take the information entered by the user as the answer to the chat robot’s question. Here, we use keras deep learning library to build the classification model.

The intention and learning pattern of chat robot are defined in a simple variable. There is no need to go to t’s corpus. We know that people who play with robots will be laughed at if they don’t have a corpus in their hands, but our goal is to build a specific chat robot for a specific context. Therefore, the classification model is created as a small vocabulary, which will only be able to identify a small group of patterns provided for training.

To put it bluntly, the so-called machine learning is that you repeatedly teach the machine to do one or several correct things. During the training, you keep demonstrating how to do it correctly, and then expect the machine to draw inferences from one instance in learning. However, this time we don’t teach it many things, just one thing, to test its response, Is it a bit like you train your pet dog at home? But the dog can’t talk to you.

I’ll give a simple example of the intention data variable here. If you like, you can expand the variable infinitely in the term library:

intents = {"intents": [  
        {"tag": "say hello",  
         "Patterns": ["hello", "hello", "excuse me", "hello", "master", "excuse me", "beauty", "handsome boy", "pretty girl", "Hi"],  
         "Responses": ["hello", "it's you again", "did you eat", "are you busy"],  
         "context": [""]  
        },  
        {"tag": "farewell",  
         "Patterns": ["goodbye", "Bye", "88", "see you later", "see you later"],  
         "Responses": ["goodbye", "Bon Voyage", "see you next time", "goodbye to you"],  
         "context": [""]  
        },  
   ]  
}

As you can see, I inserted two contextual tags, say hello and say goodbye, including user input information and machine response data.

Before we begin the training of classification model, we need to establish vocabulary. After the pattern is processed, the vocabulary is established. Each word will have a stem to produce a common root, which will help to match more combinations of user input.

for intent in intents['intents']:  
    for pattern in intent['patterns']:  
        # tokenize each word in the sentence  
        w = nltk.word_tokenize(pattern)  
        # add to our words list  
        words.extend(w)  
        # add to documents in our corpus  
        documents.append((w, intent['tag']))  
        # add to our classes list  
        if intent['tag'] not in classes:  
            classes.append(intent['tag'])  
  
words = [stemmer.stem(w.lower()) for w in words if w not in ignore_words]  
words = sorted(list(set(words)))  
  
classes = sorted(list(set(classes)))  
  
Print (len (classes), "context", classes)  
  
Print (len (words), "number of words", words)

Output:

2 context ['Farewell', 'say hello']  
14 words ['88', 'sorry', 'hello', 'goodbye', 'see you later', 'see you later', 'handsome boy', 'master', 'hello', 'bye', 'hello', 'beauty', 'excuse me', 'pretty girl']

Training will not be analyzed according to vocabulary, because vocabulary has no meaning to the machine, which is also the misunderstanding of many Chinese word segmentation libraries. In fact, the machine does not understand whether you input English or Chinese. We only need to convert words or Chinese into a word bag containing an array of 0 / 1. The length of the array will be equal to the size of the vocabulary. When a word or vocabulary in the current mode is in a given location, it will be set to 1.

# create our training data  
training = []  
# create an empty array for our output  
output_empty = [0] * len(classes)  
# training set, bag of words for each sentence  
for doc in documents:  
    # initialize our bag of words  
    bag = []  
  
    pattern_words = doc[0]  
     
    pattern_words = [stemmer.stem(word.lower()) for word in pattern_words]  
  
    for w in words:  
        bag.append(1) if w in pattern_words else bag.append(0)  
      
   
    output_row = list(output_empty)  
    output_row[classes.index(doc[1])] = 1  
      
    training.append([bag, output_row])  
  
random.shuffle(training)  
training = np.array(training)  
  
train_x = list(training[:,0])  
train_y = list(training[:,1])

We started data training. The model was built with keras and was based on three layers. Due to the small data base, the classification output will be a multi class array, which will help to identify the coding intention. Use softmax activation to generate multi class classification output (the result returns a 0 / 1 array: \ [1,0,0,…, 0 \] — this array can identify the coding intention).

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'))  
  
  
sgd = SGD(lr=0.01, decay=1e-6, momentum=0.9, nesterov=True)  
model.compile(loss='categorical_crossentropy', optimizer=sgd, metrics=['accuracy'])  
  
  
model.fit(np.array(train_x), np.array(train_y), epochs=200, batch_size=5, verbose=1)

The training is performed in 200 iterations, with a batch processing capacity of 5. Because my test data sample is small, 100 times is OK, which is not the focus.

Start training:

14/14 [==============================] - 0s 32ms/step - loss: 0.7305 - acc: 0.5000  
Epoch 2/200  
14/14 [==============================] - 0s 391us/step - loss: 0.7458 - acc: 0.4286  
Epoch 3/200  
14/14 [==============================] - 0s 390us/step - loss: 0.7086 - acc: 0.3571  
Epoch 4/200  
14/14 [==============================] - 0s 395us/step - loss: 0.6941 - acc: 0.6429  
Epoch 5/200  
14/14 [==============================] - 0s 426us/step - loss: 0.6358 - acc: 0.7143  
Epoch 6/200  
14/14 [==============================] - 0s 356us/step - loss: 0.6287 - acc: 0.5714  
Epoch 7/200  
14/14 [==============================] - 0s 366us/step - loss: 0.6457 - acc: 0.6429  
Epoch 8/200  
14/14 [==============================] - 0s 899us/step - loss: 0.6336 - acc: 0.6429  
Epoch 9/200  
14/14 [==============================] - 0s 464us/step - loss: 0.5815 - acc: 0.6429  
Epoch 10/200  
14/14 [==============================] - 0s 408us/step - loss: 0.5895 - acc: 0.6429  
Epoch 11/200  
14/14 [==============================] - 0s 548us/step - loss: 0.6050 - acc: 0.6429  
Epoch 12/200  
14/14 [==============================] - 0s 468us/step - loss: 0.6254 - acc: 0.6429  
Epoch 13/200  
14/14 [==============================] - 0s 388us/step - loss: 0.4990 - acc: 0.7857  
Epoch 14/200  
14/14 [==============================] - 0s 392us/step - loss: 0.5880 - acc: 0.7143  
Epoch 15/200  
14/14 [==============================] - 0s 370us/step - loss: 0.5118 - acc: 0.8571  
Epoch 16/200  
14/14 [==============================] - 0s 457us/step - loss: 0.5579 - acc: 0.7143  
Epoch 17/200  
14/14 [==============================] - 0s 432us/step - loss: 0.4535 - acc: 0.7857  
Epoch 18/200  
14/14 [==============================] - 0s 357us/step - loss: 0.4367 - acc: 0.7857  
Epoch 19/200  
14/14 [==============================] - 0s 384us/step - loss: 0.4751 - acc: 0.7857  
Epoch 20/200  
14/14 [==============================] - 0s 346us/step - loss: 0.4404 - acc: 0.9286  
Epoch 21/200  
14/14 [==============================] - 0s 500us/step - loss: 0.4325 - acc: 0.8571  
Epoch 22/200  
14/14 [==============================] - 0s 400us/step - loss: 0.4104 - acc: 0.9286  
Epoch 23/200  
14/14 [==============================] - 0s 738us/step - loss: 0.4296 - acc: 0.7857  
Epoch 24/200  
14/14 [==============================] - 0s 387us/step - loss: 0.3706 - acc: 0.9286  
Epoch 25/200  
14/14 [==============================] - 0s 430us/step - loss: 0.4213 - acc: 0.8571  
Epoch 26/200  
14/14 [==============================] - 0s 351us/step - loss: 0.2867 - acc: 1.0000  
Epoch 27/200  
14/14 [==============================] - 0s 3ms/step - loss: 0.2903 - acc: 1.0000  
Epoch 28/200  
14/14 [==============================] - 0s 366us/step - loss: 0.3010 - acc: 0.9286  
Epoch 29/200  
14/14 [==============================] - 0s 404us/step - loss: 0.2466 - acc: 0.9286  
Epoch 30/200  
14/14 [==============================] - 0s 428us/step - loss: 0.3035 - acc: 0.7857  
Epoch 31/200  
14/14 [==============================] - 0s 407us/step - loss: 0.2075 - acc: 1.0000  
Epoch 32/200  
14/14 [==============================] - 0s 457us/step - loss: 0.2167 - acc: 0.9286  
Epoch 33/200  
14/14 [==============================] - 0s 613us/step - loss: 0.1266 - acc: 1.0000  
Epoch 34/200  
14/14 [==============================] - 0s 534us/step - loss: 0.2906 - acc: 0.9286  
Epoch 35/200  
14/14 [==============================] - 0s 463us/step - loss: 0.2560 - acc: 0.9286  
Epoch 36/200  
14/14 [==============================] - 0s 500us/step - loss: 0.1686 - acc: 1.0000  
Epoch 37/200  
14/14 [==============================] - 0s 387us/step - loss: 0.0922 - acc: 1.0000  
Epoch 38/200  
14/14 [==============================] - 0s 430us/step - loss: 0.1620 - acc: 1.0000  
Epoch 39/200  
14/14 [==============================] - 0s 371us/step - loss: 0.1104 - acc: 1.0000  
Epoch 40/200  
14/14 [==============================] - 0s 488us/step - loss: 0.1330 - acc: 1.0000  
Epoch 41/200  
14/14 [==============================] - 0s 381us/step - loss: 0.1322 - acc: 1.0000  
Epoch 42/200  
14/14 [==============================] - 0s 462us/step - loss: 0.0575 - acc: 1.0000  
Epoch 43/200  
14/14 [==============================] - 0s 1ms/step - loss: 0.1137 - acc: 1.0000  
Epoch 44/200  
14/14 [==============================] - 0s 450us/step - loss: 0.0245 - acc: 1.0000  
Epoch 45/200  
14/14 [==============================] - 0s 470us/step - loss: 0.1824 - acc: 1.0000  
Epoch 46/200  
14/14 [==============================] - 0s 444us/step - loss: 0.0822 - acc: 1.0000  
Epoch 47/200  
14/14 [==============================] - 0s 436us/step - loss: 0.0939 - acc: 1.0000  
Epoch 48/200  
14/14 [==============================] - 0s 396us/step - loss: 0.0288 - acc: 1.0000  
Epoch 49/200  
14/14 [==============================] - 0s 580us/step - loss: 0.1367 - acc: 0.9286  
Epoch 50/200  
14/14 [==============================] - 0s 351us/step - loss: 0.0363 - acc: 1.0000  
Epoch 51/200  
14/14 [==============================] - 0s 379us/step - loss: 0.0272 - acc: 1.0000  
Epoch 52/200  
14/14 [==============================] - 0s 358us/step - loss: 0.0712 - acc: 1.0000  
Epoch 53/200  
14/14 [==============================] - 0s 4ms/step - loss: 0.0426 - acc: 1.0000  
Epoch 54/200  
14/14 [==============================] - 0s 370us/step - loss: 0.0430 - acc: 1.0000  
Epoch 55/200  
14/14 [==============================] - 0s 368us/step - loss: 0.0292 - acc: 1.0000  
Epoch 56/200  
14/14 [==============================] - 0s 494us/step - loss: 0.0777 - acc: 1.0000  
Epoch 57/200  
14/14 [==============================] - 0s 356us/step - loss: 0.0496 - acc: 1.0000  
Epoch 58/200  
14/14 [==============================] - 0s 427us/step - loss: 0.1485 - acc: 1.0000  
Epoch 59/200  
14/14 [==============================] - 0s 381us/step - loss: 0.1006 - acc: 1.0000  
Epoch 60/200  
14/14 [==============================] - 0s 421us/step - loss: 0.0183 - acc: 1.0000  
Epoch 61/200  
14/14 [==============================] - 0s 344us/step - loss: 0.0788 - acc: 0.9286  
Epoch 62/200  
14/14 [==============================] - 0s 529us/step - loss: 0.0176 - acc: 1.0000

OK, after 200 times, the model has been trained. Now declare a method for word bag conversion:

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 = [stemmer.stem(word.lower()) for word in sentence_words]  
    return sentence_words

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[i] = 1  
                if show_details:  
                    print ("found in bag: %s" % w)  
    return(np.array(bag))

Test to see if you can hit the word bag:

P = bow ("hello", words)  
print (p)

Return value:

Found in bag: Hello  
[0 0 1 0 0 0 0 0 0 0 0 0 0 0]

Obviously, the match is successful and the word is in the bag.

Before we package the model, we can use the model.predict function to classify the user input and return the user intention according to the calculated probability (multiple intentions can be returned and output in reverse order according to the probability):

def classify_local(sentence):  
    ERROR_THRESHOLD = 0.25  
      
    # generate probabilities from the model  
    input_data = pd.DataFrame([bow(sentence, words)], dtype=float, index=['input'])  
    results = model.predict([input_data])[0]  
    # filter out predictions below a threshold, and provide intent index  
    results = [[i,r] for i,r in enumerate(results) 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((classes[r[0]], str(r[1])))  
    # return tuple of intent and probability  
      
    return return_list

Test:

print(classify_ Local ('Hello ')

Return value:

Found in bag:  
[('say hello', '0.999913')]  
liuyue:mytornado liuyue$

Retest:

print(classify_local('88'))

Return value:

found in bag: 88  
[('Farewell', '0.9995449')]

Perfect, match the context label of greeting. If you like, you can test more and improve the model.

After the test, we can package the trained model so that we don’t have to train before each call:

json_file = model.to_json()  
with open('v3ucn.json', "w") as file:  
   file.write(json_file)  
  
model.save_weights('./v3ucn.h5f')

Here, the model is divided into data file (JSON) and weight file (H5F). Save them and use them later.

Next, let’s build an API for the chat robot. Here, we use the popular framework fastapi, put the model file into the project directory, and write main.py:

import random  
import uvicorn  
from fastapi import FastAPI  
app = FastAPI()  
  
  
def classify_local(sentence):  
    ERROR_THRESHOLD = 0.25  
      
    # generate probabilities from the model  
    input_data = pd.DataFrame([bow(sentence, words)], dtype=float, index=['input'])  
    results = model.predict([input_data])[0]  
    # filter out predictions below a threshold, and provide intent index  
    results = [[i,r] for i,r in enumerate(results) 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((classes[r[0]], str(r[1])))  
    # return tuple of intent and probability  
      
    return return_list  
  
@app.get('/')  
async def root(word: str = None):  
      
    from keras.models import model_from_json  
    # # load json and create model  
    file = open("./v3ucn.json", 'r')  
    model_json = file.read()  
    file.close()  
    model = model_from_json(model_json)  
    model.load_weights("./v3ucn.h5f")  
  
    wordlist = classify_local(word)  
    a = ""  
    for intent in intents['intents']:  
        if intent['tag'] == wordlist[0][0]:  
            a = random.choice(intent['responses'])  
  
  
  
    return {'message':a}  
  
if __name__ == "__main__":  
    uvicorn.run(app, host="127.0.0.1", port=8000)

there:

from keras.models import model_from_json  
file = open("./v3ucn.json", 'r')  
model_json = file.read()  
file.close()  
model = model_from_json(model_json)  
model.load_weights("./v3ucn.h5f")

It is used to import the trained model library and then start the service:

uvicorn main:app --reload

The effect is as follows:

Artificial intelligence is no better than that. Based on the python 3 deep learning library keras / tensorflow, build your own chat robot

Conclusion: there is no doubt that science and technology have changed life. Chat robots can make us hear the singing of birds and swallows without the company of beauties. I believe that in the near future, the “mechanical girl” with laughter and beautiful clothes can also accompany me under the breeze and bright moon.

The original text is reproduced from “Liu Yue’s technology blog”https://v3u.cn/a_id_178