用 TensorFlow 做個聊天機器人

上一次提到了不錯的學(xué)習(xí)聊天機器人的資源,不知道小伙伴們有沒有去學(xué)習(xí)呢。
自己動手做聊天機器人教程
我最近每天都會學(xué)一點,拿出解讀來和大家分享一下。

本文結(jié)構(gòu):

    1. 聊天機器人的架構(gòu)簡圖
    1. 用 TensorFlow 實現(xiàn) Chatbot 的模型
    1. 如何準備 chatbot 的訓(xùn)練數(shù)據(jù)
    1. Chatbot 源碼解讀

1. 聊天機器人的架構(gòu)簡圖

學(xué)習(xí)資源:
[自己動手做聊天機器人 九-聊天機器人應(yīng)該怎么做]
(http://www.shareditor.com/blogshow/?blogId=73)

聊天機器人的工作流程大體為:提問-檢索-答案抽取。

提問:就是要分析主人的問句中關(guān)鍵詞,提問類型,還有真正想知道的東西。

檢索:根據(jù)前一步的分析,去找答案。

答案抽取:找到的答案,并不能直接應(yīng)用,還要整理成真正有用的,可以作為答案的回答。

涉及到的關(guān)鍵技術(shù)如圖中所示。

看不清圖的話,就是醬紫:

問句解析:
中文分詞、詞性標(biāo)注、實體標(biāo)注、概念類別標(biāo)注、句法分析、語義分析、邏輯結(jié)構(gòu)標(biāo)注、指代消解、關(guān)聯(lián)關(guān)系標(biāo)注、問句分類、答案類別確定;

海量文本知識表示:
網(wǎng)絡(luò)文本資源獲取、機器學(xué)習(xí)方法、大規(guī)模語義計算和推理、知識表示體系、知識庫構(gòu)建

答案生成與過濾:
候選答案抽取、關(guān)系推演、吻合程度判斷、噪聲過濾


2. 用 TensorFlow 實現(xiàn) Chatbot 的模型

之前有根據(jù) Siraj 的視頻寫過一篇《自己動手寫個聊天機器人吧》
文章里只寫了主函數(shù)的簡單過程:Data-Model-Training,是用 Lua 實現(xiàn)的,詳細的代碼可以去他的 github 上學(xué)習(xí)

下面這篇文章是用 TensorFlow + tflearn 庫 實現(xiàn),在 建模, 訓(xùn)練 和 預(yù)測 等環(huán)節(jié)可以學(xué)到更多細節(jié):

學(xué)習(xí)資源:自己動手做聊天機器人 三十八-原來聊天機器人是這么做出來的

兩篇的共同點是都用了 Seq2Seq 來實現(xiàn)。

LSTM的模型結(jié)構(gòu)為:

細節(jié)的話可以直接去看上面那篇原文,這里 po 出建立模型階段簡要的流程圖和過程描述:


  • 先將原始數(shù)據(jù) 300w chat 做一下預(yù)處理,即 切詞,分為 問答對。

  • 然后用 word2vec 訓(xùn)練出詞向量,生成二進制的詞向量文件。

作為 Input data X 傳入下面流程:

  • question 進入 LSTM 的 encoder 環(huán)節(jié),answer 進入 decoder 環(huán)節(jié),

  • 分別生成 output tensor。

  • 其中 decoder 是一個詞一個詞的生成結(jié)果,將所有結(jié)果加入到一個 list 中。

  • 最后和 encoder 的輸出,一起做為下一環(huán)節(jié) Regression 的輸入,并傳入 DNN 網(wǎng)絡(luò)。


3. 如何準備 chatbot 的訓(xùn)練數(shù)據(jù)

學(xué)習(xí)資源:
自己動手做聊天機器人 三十八-原來聊天機器人是這么做出來的

訓(xùn)練數(shù)據(jù)的生成過程如下:

  • 首先在 input file 里讀取每一行,并根據(jù) ‘|’ 拆分成 question 和 answer 句子。
  • 每個句子,都將 word 通過 word2vec 轉(zhuǎn)化成詞向量。
  • 每一句的向量序列都轉(zhuǎn)化成相同維度的形式:self.word_vec_dim * self.max_seq_len
  • 最后 answer 構(gòu)成了 y 數(shù)據(jù),question+answer 構(gòu)成了 xy 數(shù)據(jù),再被投入到 model 中去訓(xùn)練:
model.fit(trainXY, trainY, n_epoch=1000, snapshot_epoch=False, batch_size=1)

代碼如下:

def init_seq(input_file):
    """讀取切好詞的文本文件,加載全部詞序列
    """
    file_object = open(input_file, 'r')
    vocab_dict = {}
    while True:
        question_seq = []
        answer_seq = []
        line = file_object.readline()
        if line:
            line_pair = line.split('|')
            line_question = line_pair[0]
            line_answer = line_pair[1]
            for word in line_question.decode('utf-8').split(' '):
                if word_vector_dict.has_key(word):
                    question_seq.append(word_vector_dict[word])
            for word in line_answer.decode('utf-8').split(' '):
                if word_vector_dict.has_key(word):
                    answer_seq.append(word_vector_dict[word])
        else:
            break
        question_seqs.append(question_seq)
        answer_seqs.append(answer_seq)
    file_object.close()

def generate_trainig_data(self):
        xy_data = []
        y_data = []
        for i in range(len(question_seqs)):
            question_seq = question_seqs[i]
            answer_seq = answer_seqs[i]
            if len(question_seq) < self.max_seq_len and len(answer_seq) < self.max_seq_len:
                sequence_xy = [np.zeros(self.word_vec_dim)] * (self.max_seq_len-len(question_seq)) + list(reversed(question_seq))
                sequence_y = answer_seq + [np.zeros(self.word_vec_dim)] * (self.max_seq_len-len(answer_seq))
                sequence_xy = sequence_xy + sequence_y
                sequence_y = [np.ones(self.word_vec_dim)] + sequence_y
                xy_data.append(sequence_xy)
                y_data.append(sequence_y)
        return np.array(xy_data), np.array(y_data)

4. Chatbot 源碼解讀

學(xué)習(xí)資源:
自己動手做聊天機器人 三十八-原來聊天機器人是這么做出來的

這篇文章在 github 上的源碼:

提煉出步驟如下:

其中 2. 準備數(shù)據(jù), 3. 建立模型 就是上文著重說的部分。

    1. 引入包
    1. 準備數(shù)據(jù)
    1. 建立模型
    1. 訓(xùn)練
    1. 預(yù)測

1. 引入包

import sys
import math
import tflearn
import tensorflow as tf
from tensorflow.python.ops import rnn_cell
from tensorflow.python.ops import rnn
import chardet
import numpy as np
import struct

2. 準備數(shù)據(jù)

def load_word_set()
將 3000 萬語料,分成 Question 和 Answer 部分,提取出 word。

def load_word_set():
    file_object = open('./segment_result_lined.3000000.pair.less', 'r')
    while True:
        line = file_object.readline()
        if line:
            line_pair = line.split('|')
            line_question = line_pair[0]
            line_answer = line_pair[1]
            for word in line_question.decode('utf-8').split(' '):
                word_set[word] = 1
            for word in line_answer.decode('utf-8').split(' '):
                word_set[word] = 1
        else:
            break
    file_object.close()

def load_vectors(input)
從 vectors.bin 加載詞向量,返回一個 word_vector_dict 的詞典,key 是詞,value 是200維的向量。

def init_seq(input_file)
將 Question 和 Answer 中單詞對應(yīng)的詞向量放在詞向量序列中 question_seqs, answer_seqs

def init_seq(input_file):
    """讀取切好詞的文本文件,加載全部詞序列
    """
    file_object = open(input_file, 'r')
    vocab_dict = {}
    while True:
        question_seq = []
        answer_seq = []
        line = file_object.readline()
        if line:
            line_pair = line.split('|')
            line_question = line_pair[0]
            line_answer = line_pair[1]
            for word in line_question.decode('utf-8').split(' '):
                if word_vector_dict.has_key(word):
                    question_seq.append(word_vector_dict[word])
            for word in line_answer.decode('utf-8').split(' '):
                if word_vector_dict.has_key(word):
                    answer_seq.append(word_vector_dict[word])
        else:
            break
        question_seqs.append(question_seq)
        answer_seqs.append(answer_seq)
    file_object.close()

def vector_sqrtlen(vector)
用來求向量的長度。

def vector_sqrtlen(vector):
    len = 0
    for item in vector:
        len += item * item
    len = math.sqrt(len)
    return len

def vector_cosine(v1, v2)
用來求兩個向量間的距離。

def vector_cosine(v1, v2):
    if len(v1) != len(v2):
        sys.exit(1)
    sqrtlen1 = vector_sqrtlen(v1)
    sqrtlen2 = vector_sqrtlen(v2)
    value = 0
    for item1, item2 in zip(v1, v2):
        value += item1 * item2
    return value / (sqrtlen1*sqrtlen2)

def vector2word(vector)
給定一個詞向量,去 word-vector 字典中查找與此向量距離最近的向量,并記憶相應(yīng)的單詞,返回單詞和 cosine 值。

def vector2word(vector):
    max_cos = -10000
    match_word = ''
    for word in word_vector_dict:
        v = word_vector_dict[word]
        cosine = vector_cosine(vector, v)
        if cosine > max_cos:
            max_cos = cosine
            match_word = word
    return (match_word, max_cos)

3. 建立模型

class MySeq2Seq(object)
在前兩篇筆記中單獨寫了這兩塊。

def generate_trainig_data(self)
question_seqs, answer_seqs 得到 xy_data 和 y_data 的形式。

def model(self, feed_previous=False)
用 input data 生成 encoder_inputs 和帶GO頭的 decoder_inputs
將 encoder_inputs 傳遞給編碼器,返回一個輸出(預(yù)測序列的第一個值)和一個狀態(tài)(傳給解碼器)。
在解碼器中,用編碼器的最后一個輸出作為第一個輸入,預(yù)測過程用前一個時間序的輸出作為下一個時間序的輸入。

4. 訓(xùn)練

def train(self)
generate_trainig_data() 生成 X y 數(shù)據(jù),傳遞給 上面定義的 model,并訓(xùn)練 model.fit,再保存。

    def train(self):
        trainXY, trainY = self.generate_trainig_data()
        model = self.model(feed_previous=False)
        model.fit(trainXY, trainY, n_epoch=1000, snapshot_epoch=False, batch_size=1)
        model.save('./model/model')
        return model

5. 預(yù)測

generate_trainig_data() 生成數(shù)據(jù),用 model.predict 進行預(yù)測,predict 結(jié)果的每一個 sample 相當(dāng)于一句話的詞向量序列,每個 sample 中的每個 vector 在 word-vector 字典中找到與其最近的向量,并返回對應(yīng)的 word,及二者間的 cosine。

if __name__ == '__main__':
    phrase = sys.argv[1]
    if 3 == len(sys.argv):
        my_seq2seq = MySeq2Seq(word_vec_dim=word_vec_dim, max_seq_len=max_seq_len, input_file=sys.argv[2])
    else:
        my_seq2seq = MySeq2Seq(word_vec_dim=word_vec_dim, max_seq_len=max_seq_len)
    if phrase == 'train':
        my_seq2seq.train()
    else:
        model = my_seq2seq.load()
        trainXY, trainY = my_seq2seq.generate_trainig_data()
        predict = model.predict(trainXY)
        for sample in predict:
            print "predict answer"
            for w in sample[1:]:
                (match_word, max_cos) = vector2word(w)
                #if vector_sqrtlen(w) < 1:
                #    break
                print match_word, max_cos, vector_sqrtlen(w)

歷史技術(shù)博文鏈接匯總

我是 不會停的蝸牛 Alice
85后全職主婦
喜歡人工智能,行動派
創(chuàng)造力,思考力,學(xué)習(xí)力提升修煉進行中
歡迎您的喜歡,關(guān)注和評論!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,739評論 6 534
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,634評論 3 419
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,653評論 0 377
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,063評論 1 314
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 71,835評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,235評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,315評論 3 442
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,459評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,000評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 40,819評論 3 355
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,004評論 1 370
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,560評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,257評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,676評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,937評論 1 288
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,717評論 3 393
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 48,003評論 2 374

推薦閱讀更多精彩內(nèi)容