1. <tr id="33chb"><label id="33chb"></label></tr>
  2. <pre id="33chb"></pre>
    當前位置:首頁 > 短網址資訊 > 正文內容

    利用 TensorFlow 實現上下文的 Chat-bots

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

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

    你是否想過一個問題,為什么那么多的聊天機器人會缺乏會話情景功能?

    鑒于上下文在所有的對話場景中的重要性,那么又該如何加入這個特性?

    接下來,我們將創建一個聊天機器人的框架,并且以一個島嶼輕便摩托車租賃店為例子,建立一個對話模型。這個小企業的聊天機器人需要處理一些關于租賃時間,租賃選項等的簡單問題。我們也希望這個機器人可以處理一些上下文的信息,比如查詢同一天的租賃信息。如果可以解決這個問題,那么我們將節約很多的時間。

    關于構建聊天機器人,我們通過以下三部進行:

    1. 我們會利用 TensorFlow 來編寫對話意圖模型。

    2. 接下啦,我們將構建一個處理對話的聊天機器人框架。

    3. 最后,我們將介紹如何將上下文信息合并到我們的響應式處理器中。

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

    1. 我們會利用 TensorFlow 來編寫對話意圖模型。

    完整的 notebook 文檔,可以點擊這里。

    對于一個聊天機器人框架,我們需要定義一個會話意圖的結構。最簡單方便的方式是使用一個 JSON 格式的文件,如下所示:

    chat-bot intents

    每個會話意圖包含:

    • 標簽(唯一的名稱)

    • 模式(我們的神經網絡文本分類器需要分類的句子)

    • 回應(一個將被用作回應的句子)

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

    首先,我們來導入一些我們需要的包:

    # things we need for NLPimport nltkfrom nltk.stem.lancaster import LancasterStemmerstemmer = LancasterStemmer()# things we need for Tensorflowimport numpy as npimport tflearnimport tensorflow as tfimport random

    如果你還不了解 TensorFlow,那么可以學習一下這個教程或者這個教程。

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

    代碼中的 JSON 文件可以這里下載,接下來我們可以開始組織代碼的文件,數據和分類器。

    words = []
    classes = []
    documents = []
    ignore_words = ['?']# loop through each sentence in our intents patternsfor 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 duplicateswords = [stemmer.stem(w.lower()) for w in words if w not in ignore_words]words = sorted(list(set(words)))# remove duplicatesclasses = sorted(list(set(classes)))
    
    print (len(documents), "documents")
    print (len(classes), "classes", classes)
    print (len(words), "unique stemmed words", words)

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

    27 documents9 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 等匹配。在實際過程中,我們可以刪除一些無用的條目,但在這里已經足夠了。

    不幸的是,這種數據結構不能在 TensorFlow 中使用,我們需要進一步將這個數據進行轉換:從單詞轉換到數字的張量。

    # 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 sentencefor 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])

    請注意,我們的數據順序已經被打亂了。 TensorFlow 會選取其中的一些數據作為測試數據,用來測試訓練的模型的準確度。

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

    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]

    接下來,我們來構建我們的模型。

    # reset underlying graph datatf.reset_default_graph()# Build neural networknet = 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 tensorboardmodel = 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')

    這個模型使用的是 2 層神經網絡模型,跟這篇文章中的是一樣的。


    interactive build of a model in tflearn

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

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

    構建我們的聊天機器人框架

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

    我們將構建一個簡單的狀態機來處理響應,并且使用我們的在上一部分中提到的意圖模型來作為我們的分類器。如果你想了解聊天機器人的工作原理,那么可以點擊這里。


    我們需要導入和上一部分相同的包,然后 un-pickle 我們的模型和句子,正如我們在上一部分中操作的。請記住,我們的聊天機器人框架與我們的模型是分開構建的 —— 除非意圖模式改變了,那么我們需要重新運行我們的模型,否則不需要重構模型。如果擁有數百種意圖和數千種模式,模型可能需要幾分鐘的時間才能構建完成。

    # restore all of our data structuresimport pickledata = 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 fileimport jsonwith open('intents.json') as json_data:
        intents = json.load(json_data)

    接下來,我們需要導入剛剛利用 TensorFlow(tflearn 框架)訓練好的模型。請注意,你第一步還是需要去定義 TensorFlow 模型結構,正如我們在第一部分中做的那樣。

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

    在我們開始處理對話意圖之前,我們需要一種從用戶輸入數據生詞詞袋的方法。而這個方法,跟我們前面所使用的方法是相同的。

    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 sentencedef 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]

    現在,我們可以開始構建我們的響應處理器了。

    ERROR_THRESHOLD = 0.25def 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_listdef 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() 的每個句子都會被分類。我們分類器使用 model.predict() 函數來進行類別預測,這個方法非???。模型返回的概率值和我們定義的意圖是一直的,用來生成潛在的響應列表。

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

    我們來舉一個例子,模型會返回最有可能的標簽和其概率。

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

    請注意,“is your shop open today?” 不是這個意圖中的任何模式:“pattern : ["Are you open today?", "When do you open today?", "What are your hours today?"]”。但是,“open” 和 “today” 術語對我們的模式是非常有用的(他們在選擇意圖時,有決定性的作用)。

    我們現在從用戶的輸入數據中產生一個結果:

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

    再來一些例子:

    response('do you take cash?')We accept VISA, Mastercard and AMEXresponse('what kind of mopeds do you rent?')We rent Yamaha, Piaggio and Vespa mopedsresponse('Goodbye, see you later')Bye! Come back again soon.

    接下來讓我們結合一些基礎的語境來設計一個聊天機器人,比如拖車租賃聊天機器人。

    語境

    我們想處理的是一個關于租賃摩托車的問題,并詢問一些有關租金的事。對于用戶問題的理解應該是非常容易的,語境非常清晰。如果用戶詢問 “today”,那么上下文的租賃信息就是進入時間框架,那么最好你還能指定是哪一個自行車,這樣交流起來就不會浪費時間。

    為了實現這一點,我們需要在框架中再加入一個概念 “state” 。這需要一個數據結構來維護這個新的概念和原來的意圖。

    因為我們需要我們的狀態機是一個非常容易的維護,恢復和復制等等操作,所以我們需要把數據都保存在一個諸如字典的數據結構中,這是非常重要的。

    接下來,我們給出基本語境的回復過程:

    # create a data structure to hold user contextcontext = {}
    
    ERROR_THRESHOLD = 0.25def 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_listdef 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)

    我們的上下文狀態是一個字典,它將包含每個用戶的狀態。我將為每個用戶使用一個唯一的標識(例如,cell#)。這允許我們的框架和狀態機同事維護多個用戶的狀態。

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

    我們在意圖處理流程中,添加了上下文信息,具體如下:

                    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']))

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

    {“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”
     }

    如果另一個意圖想要與上下文進行關聯,那么可以這樣做:

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

    以這種方法構建的信息庫,如果用戶只是輸入 "today" 而沒有上下文信息,那么這個 “today” 的用戶意圖是不會被處理的。如果用戶輸入的 "today" 是對我們的一個時間回應,即觸動了意圖標簽 "rental" ,那么這個意圖將會被處理。

    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" 意圖用來清除上下文語境信息,這就像我們打招呼一樣,標志著我們要開啟一個新的對話。我們還添加了 "show_details" 參數,用來幫助我們看到程序里面的信息。

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

    讓我們再次嘗試輸入 "今天" 這個詞,一些有趣的事情就發生了。

    response('today')
    We're open every day from 9am-9pm
    
    classify('today')
    [('today', 0.5322513580322266), ('opentoday', 0.2611265480518341)]

    首先,我們對沒有上下文信息的 "today" 的回應是不同的。我們的分類產生了 2 個合適的意圖,但 "opentoday" 被選中了。所以這個隨機性就比較大,上下文信息很重要!

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

    現在需要考慮的事情就是如何將對話放置到具體語境中了。

    狀態處理

    沒錯,你的機器人將會成為你的私人機器人了,不再是那么大眾化。除非你想要重建狀態,重新加載你的模型和文檔 —— 每次調用你的機器人框架,你都會需要加載一個模型狀態。

    這不是那么困難,你可以在自己的進程中運行一個有狀態的聊天機器人框架,并使用 RPC(遠程過程調用)或 RMI(遠程方法調用)調用它,我推薦使用 Pyro

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

    你的聊天機器人客戶端將通過 Pyro 函數進行調用,你的狀態服務將由它處理,是不是很贊。

    這里有一個手把手教你如何構建一個 Twilio SMS 機器人客戶端的方法,這里是一個構建 Facebook 機器人的方法。


    不要將狀態存儲在局部變量中

    所有狀態信息都必須放在諸如字典之類的數據結構中,易于持久化,重新加載或者以原子狀態進行復制。

    每個用戶的對話和上下文語境都會保存在用戶 ID 下面,這個ID必須是唯一的。

    我們會復制有些用戶的對話信息來進行場景分析,如果這些信息被保存在臨時變量中,那么就非常難來處理,這是一個最大的考慮。


    所以,現在你已經學會了如何去構建一個聊天機器人框架,一個使它能記住上下文信息的機器人,已經如何分析文本。未來的聊天機器人也都是能分析上下文語境的,這是一個大趨勢。

    我們聯想到意圖的構建會影響上下文的對話反應,所以我們可以創建各種各樣的會話環境。

    快去動手試試吧!



    作者:chen_h
    鏈接:http://www.virginiabusinesslawupdate.com/
    來源:FT12短網址
    著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。


    掃描二維碼推送至手機訪問。

    版權聲明:本文由短鏈接發布,如需轉載請注明出處。

    本文鏈接:http://www.virginiabusinesslawupdate.com/article_286.html

    分享給朋友:

    相關文章

    摩拜單車發布“摩拜+”戰略 過半新用戶來自微信小程序

    5月19日消息,摩拜單車在北京舉行發布會,正式宣布“摩拜+”開放平臺戰略,全面布局“生活圈”、“大數據”和“物聯網”三大開放平臺。中國聯通(微博)、招商銀行、中國銀聯、百度地圖等8大品牌成為摩拜“生活圈”的首批戰略合作伙伴?! 〈送?,摩拜單...

    如果時間倒退到畢業那年,我一定要選擇進大公司

    如果時間倒退到畢業那年,我一定要選擇進大公司

    01我是一個工科女,專業水平通常般,結業后到深圳作業,如今已是第四個年初。剛結業的時分,我歷來沒有想過要進大公司,我覺得大公司要求必定對比嚴厲,上班肯定沒有自在。我投的簡歷通常是面向小公司,那些超越100人的公司我是不投的,我就選定小公司了...

    直播行業亂象叢生,超過10家直播平臺被關停,3萬多表演者被處理,

     業界有人預測,2020年網絡直播市場規模將達到600億元,網絡直播及周邊行業市場最終將達到千億元級資金?! ?017年在監管層行業規范行動與“掃黃打非”政策的實施下,直播行業注定無法回到從前野蠻肆意的時期,而是迎來了一個漫長的整...

    摩拜ofo爭相進行新一輪融資 傳金額高達數億美元

    同享單車再次掀起融資競賽。騰訊科技從多個權威消息源獲悉,摩拜單車、ofo都在進行新一輪的融資,并且金額都到達數億美元。6月12日,一位接近摩拜單車高層的人士向騰訊科技透露,摩拜單車很快將宣告E輪融資。而此前據外媒《The informati...

    FT12短網址:給你的女朋友解釋為什么隨機播放歌曲并不隨機

    FT12短網址:給你的女朋友解釋為什么隨機播放歌曲并不隨機

    FT12短網址的小編在周末的時候開車帶著女朋友出去玩。小編車里面隨機播放著五月天的歌曲。當小編正沉浸在『得兒飄,得兒飄,得兒意的飄』中,幻想著自己是秋名山車神,突然,旁邊的豆腐,哦不,女朋友說話了。誒,你車上的歌曲是隨機播放的嗎?和短網址的...

    每天堅持不懈的寫軟文,得到的幾點心得感悟

    每天堅持不懈的寫軟文,得到的幾點心得感悟

    夜深了,我喜歡這樣寧靜的夜,它能讓人不用去想更多的事情,專注于做自己想做的事情,我認為是一種幸福的事情,拿著手機播放了今晚的《半夜聽》節目,聽這個節目已經有一段時間了,雖然每天只有那么短短的幾分鐘,但是那些字眼確實令我欲罷不能,聽完幾分鐘的...

    發表評論

    訪客

    ◎歡迎參與討論,請在這里發表您的看法和觀點。
    一本色综合网久久
    1. <tr id="33chb"><label id="33chb"></label></tr>
    2. <pre id="33chb"></pre>