自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

利用 TensorFlow 實(shí)現(xiàn)上下文的 Chat-bots

人工智能 深度學(xué)習(xí) 機(jī)器學(xué)習(xí)
本文將帶你學(xué)會(huì)了如何去構(gòu)建一個(gè)聊天機(jī)器人框架,一個(gè)使它能記住上下文信息的機(jī)器人,已經(jīng)如何分析文本。未來(lái)的聊天機(jī)器人也都是能分析上下文語(yǔ)境的,這是一個(gè)大趨勢(shì)。

 在我們的日常聊天中,情景才是最重要的。我們將使用 TensorFlow 構(gòu)建一個(gè)聊天機(jī)器人框架,并且添加一些上下文處理機(jī)制來(lái)使得機(jī)器人更加智能。

[[195156]]

“Whole World in your Hand” — Betty Newman-Maguire (http://www.bettynewmanmaguire.ie/)

你是否想過(guò)一個(gè)問(wèn)題,為什么那么多的聊天機(jī)器人會(huì)缺乏會(huì)話情景功能?

鑒于上下文在所有的對(duì)話場(chǎng)景中的重要性,那么又該如何加入這個(gè)特性?

接下來(lái),我們將創(chuàng)建一個(gè)聊天機(jī)器人的框架,并且以一個(gè)島嶼輕便摩托車(chē)租賃店為例子,建立一個(gè)對(duì)話模型。這個(gè)小企業(yè)的聊天機(jī)器人需要處理一些關(guān)于租賃時(shí)間,租賃選項(xiàng)等的簡(jiǎn)單問(wèn)題。我們也希望這個(gè)機(jī)器人可以處理一些上下文的信息,比如查詢(xún)同一天的租賃信息。如果可以解決這個(gè)問(wèn)題,那么我們將節(jié)約很多的時(shí)間。

關(guān)于構(gòu)建聊天機(jī)器人,我們通過(guò)以下三部進(jìn)行:

  1. 我們會(huì)利用 TensorFlow 來(lái)編寫(xiě)對(duì)話意圖模型。
  2. 接下啦,我們將構(gòu)建一個(gè)處理對(duì)話的聊天機(jī)器人框架。
  3. 最后,我們將介紹如何將上下文信息合并到我們的響應(yīng)式處理器中。

在模型中,我們將使用 tflearn 框架,這是一個(gè) TensorFlow 的高層 API,并且我們將使用 IPython 作為開(kāi)發(fā)工具。

1. 我們會(huì)利用 TensorFlow 來(lái)編寫(xiě)對(duì)話意圖模型。

完整的 notebook 文檔,可以點(diǎn)擊這里。

對(duì)于一個(gè)聊天機(jī)器人框架,我們需要定義一個(gè)會(huì)話意圖的結(jié)構(gòu)。最簡(jiǎn)單方便的方式是使用一個(gè) JSON 格式的文件,如下所示:

chat-bot intents

每個(gè)會(huì)話意圖包含:

  • 標(biāo)簽(唯一的名稱(chēng))
  • 模式(我們的神經(jīng)網(wǎng)絡(luò)文本分類(lèi)器需要分類(lèi)的句子)
  • 回應(yīng)(一個(gè)將被用作回應(yīng)的句子)

稍后,我們也會(huì)添加一些基本的上下文元素。

首先,我們來(lái)導(dǎo)入一些我們需要的包:

# things we need for NLP
import nltk
from nltk.stem.lancaster import LancasterStemmer
stemmer = LancasterStemmer()

# things we need for Tensorflow
import numpy as np
import tflearn
import tensorflow as tf
import random

如果你還不了解 TensorFlow,那么可以學(xué)習(xí)一下這個(gè)教程或者這個(gè)教程。

# import our chat-bot intents file
import json
with open('intents.json') as json_data:
    intents = json.load(json_data)

代碼中的 JSON 文件可以這里下載,接下來(lái)我們可以開(kāi)始組織代碼的文件,數(shù)據(jù)和分類(lèi)器。

words = []
classes = []
documents = []
ignore_words = ['?']
# loop through each sentence in our intents patterns
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'])

# stem and lower each word and remove duplicates
words = [stemmer.stem(w.lower()) for w in words if w not in ignore_words]
words = sorted(list(set(words)))

# remove duplicates
classes = sorted(list(set(classes)))

print (len(documents), "documents")
print (len(classes), "classes", classes)
print (len(words), "unique stemmed words", words)

我們創(chuàng)建了一個(gè)文件列表(每個(gè)句子),每個(gè)句子都是由一些詞干組成,并且每個(gè)文檔都屬于一個(gè)特定的類(lèi)別。

27 documents
9 classes ['goodbye', 'greeting', 'hours', 'mopeds', 'opentoday', 'payments', 'rental', 'thanks', 'today']
44 unique stemmed words ["'d", 'a', 'ar', 'bye', 'can', 'card', 'cash', 'credit', 'day', 'do', 'doe', 'good', 'goodby', 'hav', 'hello', 'help', 'hi', 'hour', 'how', 'i', 'is', 'kind', 'lat', 'lik', 'mastercard', 'mop', 'of', 'on', 'op', 'rent', 'see', 'tak', 'thank', 'that', 'ther', 'thi', 'to', 'today', 'we', 'what', 'when', 'which', 'work', 'you']

比如,詞干 tak 將和 take,taking,takers 等匹配。在實(shí)際過(guò)程中,我們可以刪除一些無(wú)用的條目,但在這里已經(jīng)足夠了。

不幸的是,這種數(shù)據(jù)結(jié)構(gòu)不能在 TensorFlow 中使用,我們需要進(jìn)一步將這個(gè)數(shù)據(jù)進(jìn)行轉(zhuǎn)換:從單詞轉(zhuǎn)換到數(shù)字的張量。

# create our training data
training = []
output = []
# 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 = []
    # list of tokenized words for the pattern
    pattern_words = doc[0]
    # stem each word
    pattern_words = [stemmer.stem(word.lower()) for word in pattern_words]
    # create our bag of words array
    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
    output_row = list(output_empty)
    output_row[classes.index(doc[1])] = 1

    training.append([bag, output_row])

# shuffle our features and turn into np.array
random.shuffle(training)
training = np.array(training)

# create train and test lists
train_x = list(training[:,0])
train_y = list(training[:,1])

請(qǐng)注意,我們的數(shù)據(jù)順序已經(jīng)被打亂了。 TensorFlow 會(huì)選取其中的一些數(shù)據(jù)作為測(cè)試數(shù)據(jù),用來(lái)測(cè)試訓(xùn)練的模型的準(zhǔn)確度。

如果我們觀察單個(gè)的 x 向量和 y 向量,那么這就是一個(gè)詞袋模型,一個(gè)表示需要匹配的模式,一個(gè)表示匹配的目標(biāo)。

train_x example: [0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1] 
train_y example: [0, 0, 1, 0, 0, 0, 0, 0, 0]

接下來(lái),我們來(lái)構(gòu)建我們的模型。

# reset underlying graph data
tf.reset_default_graph()
# Build neural network
net = tflearn.input_data(shape=[None, len(train_x[0])])
net = tflearn.fully_connected(net, 8)
net = tflearn.fully_connected(net, 8)
net = tflearn.fully_connected(net, len(train_y[0]), activation='softmax')
net = tflearn.regression(net)

# Define model and setup tensorboard
model = tflearn.DNN(net, tensorboard_dir='tflearn_logs')
# Start training (apply gradient descent algorithm)
model.fit(train_x, train_y, n_epoch=1000, batch_size=8, show_metric=True)
model.save('model.tflearn')

這個(gè)模型使用的是 2 層神經(jīng)網(wǎng)絡(luò)模型,跟這篇文章中的是一樣的。

interactive build of a model in tflearn

我們完成了這部分的工作,現(xiàn)在需要保存我們的模型和文檔, 以便在后續(xù)的代碼中可以使用它們。

# save all of our data structures
import pickle
pickle.dump( {'words':words, 'classes':classes, 'train_x':train_x, 'train_y':train_y}, open( "training_data", "wb" ) )
[[195157]]

構(gòu)建我們的聊天機(jī)器人框架

這部分,完整的代碼在這里。

我們將構(gòu)建一個(gè)簡(jiǎn)單的狀態(tài)機(jī)來(lái)處理響應(yīng),并且使用我們的在上一部分中提到的意圖模型來(lái)作為我們的分類(lèi)器。如果你想了解聊天機(jī)器人的工作原理,那么可以點(diǎn)擊這里

我們需要導(dǎo)入和上一部分相同的包,然后 un-pickle 我們的模型和句子,正如我們?cè)谏弦徊糠种胁僮鞯摹U?qǐng)記住,我們的聊天機(jī)器人框架與我們的模型是分開(kāi)構(gòu)建的 —— 除非意圖模式改變了,那么我們需要重新運(yùn)行我們的模型,否則不需要重構(gòu)模型。如果擁有數(shù)百種意圖和數(shù)千種模式,模型可能需要幾分鐘的時(shí)間才能構(gòu)建完成。

# restore all of our data structures
import pickle
data = pickle.load( open( "training_data", "rb" ) )
words = data['words']
classes = data['classes']
train_x = data['train_x']
train_y = data['train_y']

# import our chat-bot intents file
import json
with open('intents.json') as json_data:
    intents = json.load(json_data)

接下來(lái),我們需要導(dǎo)入剛剛利用 TensorFlow(tflearn 框架)訓(xùn)練好的模型。請(qǐng)注意,你第一步還是需要去定義 TensorFlow 模型結(jié)構(gòu),正如我們?cè)诘谝徊糠种凶龅哪菢印?/p>

# load our saved model
model.load('./model.tflearn')

在我們開(kāi)始處理對(duì)話意圖之前,我們需要一種從用戶輸入數(shù)據(jù)生詞詞袋的方法。而這個(gè)方法,跟我們前面所使用的方法是相同的。

def clean_up_sentence(sentence):
    # tokenize the pattern
    sentence_words = nltk.word_tokenize(sentence)
    # stem each word
    sentence_words = [stemmer.stem(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 sentence
def bow(sentence, words, show_details=False):
    # tokenize the pattern
    sentence_words = clean_up_sentence(sentence)
    # bag of words
    bag = [0]*len(words)  
    for s in sentence_words:
        for i,w in enumerate(words):
            if w == s: 
                bag[i] = 1
                if show_details:
                    print ("found in bag: %s" % w)

    return(np.array(bag))
p = bow("is your shop open today?", words)
print (p)
[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 1 0 0 0 0 0 1 0]

現(xiàn)在,我們可以開(kāi)始構(gòu)建我們的響應(yīng)處理器了。

ERROR_THRESHOLD = 0.25
def classify(sentence):
    # generate probabilities from the model
    results = model.predict([bow(sentence, words)])[0]
    # filter out predictions below a threshold
    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]], r[1]))
    # return tuple of intent and probability
    return return_list

def response(sentence, userID='123', show_details=False):
    results = classify(sentence)
    # if we have a classification then find the matching intent tag
    if results:
        # loop as long as there are matches to process
        while results:
            for i in intents['intents']:
                # find a tag matching the first result
                if i['tag'] == results[0][0]:
                    # a random response from the intent
                    return print(random.choice(i['responses']))

            results.pop(0)

傳遞給 response() 的每個(gè)句子都會(huì)被分類(lèi)。我們分類(lèi)器使用 model.predict() 函數(shù)來(lái)進(jìn)行類(lèi)別預(yù)測(cè),這個(gè)方法非???。模型返回的概率值和我們定義的意圖是一直的,用來(lái)生成潛在的響應(yīng)列表。

如果一個(gè)或多個(gè)分類(lèi)結(jié)果高于閾值,那么我們會(huì)選取出一個(gè)與意圖匹配的標(biāo)簽,然后處理。我們將我們的分類(lèi)列表作為一個(gè)堆棧,并從這個(gè)堆棧中尋找一個(gè)適合的匹配,直到找到一個(gè)最好的或者直到堆棧變空。

我們來(lái)舉一個(gè)例子,模型會(huì)返回最有可能的標(biāo)簽和其概率。

classify('is your shop open today?')
[('opentoday', 0.9264171123504639)]

請(qǐng)注意,“is your shop open today?” 不是這個(gè)意圖中的任何模式:“pattern : ["Are you open today?", "When do you open today?", "What are your hours today?"]”。但是,“open” 和 “today” 術(shù)語(yǔ)對(duì)我們的模式是非常有用的(他們?cè)谶x擇意圖時(shí),有決定性的作用)。

我們現(xiàn)在從用戶的輸入數(shù)據(jù)中產(chǎn)生一個(gè)結(jié)果:

response('is your shop open today?')
Our hours are 9am-9pm every day

再來(lái)一些例子:

response('do you take cash?')
We accept VISA, Mastercard and AMEX

response('what kind of mopeds do you rent?')
We rent Yamaha, Piaggio and Vespa mopeds

response('Goodbye, see you later')
Bye! Come back again soon.
[[195158]]

接下來(lái)讓我們結(jié)合一些基礎(chǔ)的語(yǔ)境來(lái)設(shè)計(jì)一個(gè)聊天機(jī)器人,比如拖車(chē)租賃聊天機(jī)器人。

語(yǔ)境

我們想處理的是一個(gè)關(guān)于租賃摩托車(chē)的問(wèn)題,并詢(xún)問(wèn)一些有關(guān)租金的事。對(duì)于用戶問(wèn)題的理解應(yīng)該是非常容易的,語(yǔ)境非常清晰。如果用戶詢(xún)問(wèn) “today”,那么上下文的租賃信息就是進(jìn)入時(shí)間框架,那么最好你還能指定是哪一個(gè)自行車(chē),這樣交流起來(lái)就不會(huì)浪費(fèi)時(shí)間。

為了實(shí)現(xiàn)這一點(diǎn),我們需要在框架中再加入一個(gè)概念 “state” 。這需要一個(gè)數(shù)據(jù)結(jié)構(gòu)來(lái)維護(hù)這個(gè)新的概念和原來(lái)的意圖。

因?yàn)槲覀冃枰覀兊臓顟B(tài)機(jī)是一個(gè)非常容易的維護(hù),恢復(fù)和復(fù)制等等操作,所以我們需要把數(shù)據(jù)都保存在一個(gè)諸如字典的數(shù)據(jù)結(jié)構(gòu)中,這是非常重要的。

接下來(lái),我們給出基本語(yǔ)境的回復(fù)過(guò)程:

# create a data structure to hold user context
context = {}

ERROR_THRESHOLD = 0.25
def classify(sentence):
    # generate probabilities from the model
    results = model.predict([bow(sentence, words)])[0]
    # filter out predictions below a threshold
    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]], r[1]))
    # return tuple of intent and probability
    return return_list

def response(sentence, userID='123', show_details=False):
    results = classify(sentence)
    # if we have a classification then find the matching intent tag
    if results:
        # loop as long as there are matches to process
        while results:
            for i in intents['intents']:
                # find a tag matching the first result
                if i['tag'] == results[0][0]:
                    # set context for this intent if necessary
                    if 'context_set' in i:
                        if show_details: print ('context:', i['context_set'])
                        context[userID] = i['context_set']

                    # check if this intent is contextual and applies to this user's conversation
                    if not 'context_filter' in i or \
                        (userID in context and 'context_filter' in i and i['context_filter'] == context[userID]):
                        if show_details: print ('tag:', i['tag'])
                        # a random response from the intent
                        return print(random.choice(i['responses']))

            results.pop(0)

我們的上下文狀態(tài)是一個(gè)字典,它將包含每個(gè)用戶的狀態(tài)。我將為每個(gè)用戶使用一個(gè)唯一的標(biāo)識(shí)(例如,cell#)。這允許我們的框架和狀態(tài)機(jī)同事維護(hù)多個(gè)用戶的狀態(tài)。

# create a data structure to hold user context
context = {}

我們?cè)谝鈭D處理流程中,添加了上下文信息,具體如下:

                if i['tag'] == results[0][0]:
                    # set context for this intent if necessary
                    if 'context_set' in i:
                        if show_details: print ('context:', i['context_set'])
                        context[userID] = i['context_set']

                    # check if this intent is contextual and applies to this user's conversation
                    if not 'context_filter' in i or \
                        (userID in context and 'context_filter' in i and i['context_filter'] == context[userID]):
                        if show_details: print ('tag:', i['tag'])
                        # a random response from the intent
                        return print(random.choice(i['responses']))

如果一個(gè)意圖想要設(shè)置上下文信息,那么我們可以這樣做:

{“tag”: “rental”,
 “patterns”: [“Can we rent a moped?”, “I’d like to rent a moped”, … ],
 “responses”: [“Are you looking to rent today or later this week?”],
 “context_set”: “rentalday”
 }

如果另一個(gè)意圖想要與上下文進(jìn)行關(guān)聯(lián),那么可以這樣做:

{“tag”: “today”,
 “patterns”: [“today”],
 “responses”: [“For rentals today please call 1–800-MYMOPED”, …],
“context_filter”: “rentalday”
 }

以這種方法構(gòu)建的信息庫(kù),如果用戶只是輸入 "today" 而沒(méi)有上下文信息,那么這個(gè) “today” 的用戶意圖是不會(huì)被處理的。如果用戶輸入的 "today" 是對(duì)我們的一個(gè)時(shí)間回應(yīng),即觸動(dòng)了意圖標(biāo)簽 "rental" ,那么這個(gè)意圖將會(huì)被處理。

response('we want to rent a moped')
Are you looking to rent today or later this week?

response('today')
Same-day rentals please call 1-800-MYMOPED

我們上下文信息也改變了:

context
{'123': 'rentalday'}

我們定義我們的 "greeting" 意圖用來(lái)清除上下文語(yǔ)境信息,這就像我們打招呼一樣,標(biāo)志著我們要開(kāi)啟一個(gè)新的對(duì)話。我們還添加了 "show_details" 參數(shù),用來(lái)幫助我們看到程序里面的信息。

response("Hi there!", show_details=True)
context: ''
tag: greeting
Good to see you again

讓我們?cè)俅螄L試輸入 "今天" 這個(gè)詞,一些有趣的事情就發(fā)生了。

response('today')
We're open every day from 9am-9pm

classify('today')
[('today', 0.5322513580322266), ('opentoday', 0.2611265480518341)]

首先,我們對(duì)沒(méi)有上下文信息的 "today" 的回應(yīng)是不同的。我們的分類(lèi)產(chǎn)生了 2 個(gè)合適的意圖,但 "opentoday" 被選中了。所以這個(gè)隨機(jī)性就比較大,上下文信息很重要!

response("thanks, your great")
Happy to help!
[[195159]]

現(xiàn)在需要考慮的事情就是如何將對(duì)話放置到具體語(yǔ)境中了。

狀態(tài)處理

沒(méi)錯(cuò),你的機(jī)器人將會(huì)成為你的私人機(jī)器人了,不再是那么大眾化。除非你想要重建狀態(tài),重新加載你的模型和文檔 —— 每次調(diào)用你的機(jī)器人框架,你都會(huì)需要加載一個(gè)模型狀態(tài)。

這不是那么困難,你可以在自己的進(jìn)程中運(yùn)行一個(gè)有狀態(tài)的聊天機(jī)器人框架,并使用 RPC(遠(yuǎn)程過(guò)程調(diào)用)或 RMI(遠(yuǎn)程方法調(diào)用)調(diào)用它,我推薦使用 Pyro

用戶界面(客戶端)通常是無(wú)狀態(tài)的,例如:HTTP 或 SMS。

你的聊天機(jī)器人客戶端將通過(guò) Pyro 函數(shù)進(jìn)行調(diào)用,你的狀態(tài)服務(wù)將由它處理,是不是很贊。

這里有一個(gè)手把手教你如何構(gòu)建一個(gè) Twilio SMS 機(jī)器人客戶端的方法,這里是一個(gè)構(gòu)建 Facebook 機(jī)器人的方法。

不要將狀態(tài)存儲(chǔ)在局部變量中

所有狀態(tài)信息都必須放在諸如字典之類(lèi)的數(shù)據(jù)結(jié)構(gòu)中,易于持久化,重新加載或者以原子狀態(tài)進(jìn)行復(fù)制。

每個(gè)用戶的對(duì)話和上下文語(yǔ)境都會(huì)保存在用戶 ID 下面,這個(gè)ID必須是唯一的。

我們會(huì)復(fù)制有些用戶的對(duì)話信息來(lái)進(jìn)行場(chǎng)景分析,如果這些信息被保存在臨時(shí)變量中,那么就非常難來(lái)處理,這是一個(gè)最大的考慮。

[[195160]]

所以,現(xiàn)在你已經(jīng)學(xué)會(huì)了如何去構(gòu)建一個(gè)聊天機(jī)器人框架,一個(gè)使它能記住上下文信息的機(jī)器人,已經(jīng)如何分析文本。未來(lái)的聊天機(jī)器人也都是能分析上下文語(yǔ)境的,這是一個(gè)大趨勢(shì)。

我們聯(lián)想到意圖的構(gòu)建會(huì)影響上下文的對(duì)話反應(yīng),所以我們可以創(chuàng)建各種各樣的會(huì)話環(huán)境。

快去動(dòng)手試試吧!

責(zé)任編輯:林師授 來(lái)源: 簡(jiǎn)書(shū)
相關(guān)推薦

2017-05-11 14:00:02

Flask請(qǐng)求上下文應(yīng)用上下文

2022-09-15 08:01:14

繼承基礎(chǔ)設(shè)施基礎(chǔ)服務(wù)

2012-12-31 10:01:34

SELinuxSELinux安全

2022-09-14 13:13:51

JavaScript上下文

2024-09-05 08:24:09

2023-07-11 10:02:23

2022-04-24 15:37:26

LinuxCPU

2025-04-07 01:02:00

GoAPI語(yǔ)言

2012-08-10 13:32:08

.NETAOP架構(gòu)

2024-02-21 19:56:48

??filterA并發(fā)計(jì)算

2024-09-30 14:10:00

2017-12-17 17:01:23

限界上下文系統(tǒng)模型

2022-10-28 16:24:33

Context上下文鴻蒙

2025-03-18 08:14:05

2024-03-14 08:11:45

模型RoPELlama

2024-01-29 08:49:36

RAG模型檢索

2012-07-18 11:39:18

ibmdw

2022-09-26 23:36:33

Linux系統(tǒng)CPU

2022-04-25 11:27:34

LinuxCPU

2020-07-24 10:00:00

JavaScript執(zhí)行上下文前端
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)