用TensorFlow實現文本分析模型,做個聊天機器人


用TensorFlow實現文本分析模型,做個聊天機器人

聊天機器人的架構簡圖
用 TensorFlow 實現 Chatbot 的模型
如何准備 chatbot 的訓練數據
Chatbot 源碼解讀

1. 聊天機器人的架構簡圖

聊天機器人是可行的

我:chatbot,你好!

chatbot:你也好!

我:聊天機器人可行嗎?

chatbot:你不要懷疑這是天方夜譚,我不就在這里嗎?世界上還有很多跟我一樣聰明的機器人呢,你聽過IBM公司在2010年就研發出來了的Watson問答系統嗎?它可比我要聰明100倍呢

我:噢,想起來了,據說Watson在智力競賽中竟然戰勝了人類選手。但是我了解到它有一些缺陷:因為它還只是對信息檢索技術的綜合運用,並沒有進行各種語義關系的深刻計算,所以它能回答的問題也僅限於實事類的問題,所以它能贏得也就是知識類的智力競賽,如果你給它出個腦筋急轉彎,它就不行了

chatbot:是的呢,所以你任重道遠啊

聊天機器人工作原理是什么

我:chatbot,我問的每一句話,你都是怎么處理並回答我的呢?

chatbot:我身體里有三個重要模塊:提問處理模塊、檢索模塊、答案抽取模塊。三個模塊一起工作,就能回答你的問題啦

我:是嘛,那么這個提問處理模塊是怎么工作的呢?

chatbot:提問處理模塊要做三項重要工作:查詢關鍵詞生成、答案類型確定、句法和語義分析。

我:那么這個查詢關。。。

chatbot:別急別急,聽我一個一個講給你聽。查詢關鍵詞生成,就是從你的提問中提取出關鍵的幾個關鍵詞,因為我本身是一個空殼子,需要去網上查找資料才能回答你,而但網上資料那么多,我該查哪些呢?所以你的提問就有用啦,我找幾個中心詞,再關聯出幾個擴展詞,上網一搜,一大批資料就來啦,當然這些都是原始資料,我后面要繼續處理。再說答案類型確定,這項工作是為了確定你的提問屬於哪一類的,如果你問的是時間、地點,和你問的是技術方案,那我后面要做的處理是不一樣的。最后再說這個句法和語義分析,這是對你問題的深層含義做一個剖析,比如你的問題是:聊天機器人怎么做?那么我要知道你要問的是聊天機器人的研發方法

我:原來是這樣,提問處理模塊這三項工作我了解了,那么檢索模塊是怎么工作的呢?

chatbot:檢索模塊跟搜索引擎比較像,就是根據查詢關鍵詞所信息檢索,返回句子或段落,這部分就是下一步要處理的原料

我:那么答案抽取模塊呢?

chatbot:答案抽取模塊可以說是計算量最大的部分了,它要通過分析和推理從檢索出的句子或段落里抽取出和提問一致的實體,再根據概率最大對候選答案排序,注意這里是“候選答案”噢,也就是很難給出一個完全正確的結果,很有可能給出多個結果,最后還在再選出一個來

我:那么我只要實現這三個模塊,就能做成一個你嘍?

chatbot:是的

聊天機器人的關鍵技術

我:chatbot,小弟我知識匱乏,能不能告訴我都需要學哪些關鍵技術才能完成我的夢想

chatbot:小弟。。。我還沒滿月。說到關鍵技術,那我可要列一列了,你的任務艱巨了:

1)海量文本知識表示:網絡文本資源獲取、機器學習方法、大規模語義計算和推理、知識表示體系、知識庫構建;

2)問句解析:中文分詞、詞性標注、實體標注、概念類別標注、句法分析、語義分析、邏輯結構標注、指代消解、關聯關系標注、問句分類(簡單問句還是復雜問句、實體型還是段落型還是篇章級問題)、答案類別確定;

3)答案生成與過濾:候選答案抽取、關系推演(並列關系還是遞進關系還是因果關系)、吻合程度判斷、噪聲過濾

聊天機器人的技術方法

我:chatbot,我對聊天機器人的相關技術總算有所了解了,但是我具體要用什么方法呢?

chatbot:看你這么好學,那我就多給你講一講。聊天機器人的技術可以分成四種類型:1)基於檢索的技術;2)基於模式匹配的技術;3)基於自然語言理解的技術;4)基於統計翻譯模型的技術。這幾種技術並不是都要實現,而是選其一,聽我給你說說他們的優缺點,你就知道該選哪一種了。基於檢索的技術就是信息檢索技術,它簡單易實現,但無法從句法關系和語義關系給出答案,也就是搞不定推理問題,所以直接pass掉。基於模式匹配的技術就是把問題往已經梳理好的幾種模式上去靠,這種做推理簡單,但是模式我們涵蓋不全,所以也pass掉。基於自然語言理解就是把淺層分析加上句法分析、語義分析都融入進來做的補充和改進。基於統計翻譯就是把問句里的疑問詞留出來,然后和候選答案資料做配對,能對齊了就是答案,對不齊就對不起了,所以pass掉。選哪個知道了嗎?

我:知道了!基於自然語言理解的技術!so easy!媽媽再也不用擔心我的學習!o(╯□╰)o

chatbot:好,那我的任務結束啦,我該走了,再見!

我:不要走——不要走——ka ji ma——

在呼喚chatbot的聲音中,我從夢中醒來,這時我才意識到這是一個夢,揉揉雙眼,突然發現床邊有一張紙,上面寫着:

你會成功的!讀了這篇文章的人也會成功的!加油!

--------------------------------------------

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

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

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

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

涉及到的關鍵技術如圖中所示。

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

問句解析: 
中文分詞、詞性標注、實體標注、概念類別標注、句法分析、語義分析、邏輯結構標注、指代消解、關聯關系標注、問句分類、答案類別確定;

海量文本知識表示: 
網絡文本資源獲取、機器學習方法、大規模語義計算和推理、知識表示體系、知識庫構建

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


2. 用 TensorFlow 實現 Chatbot 的模型

之前有根據 Siraj 的視頻寫過一篇《自己動手寫個聊天機器人吧》,

http://www.jianshu.com/p/d0f4a751012b

文章里只寫了主函數的簡單過程:Data-Model-Training,是用 Lua 實現的。

下面這篇文章是用 TensorFlow + tflearn 庫 實現,在 建模, 訓練 和 預測 等環節可以學到更多細節:

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

http://www.shareditor.com/blogshow/?blogId=121

兩篇的共同點是都用了 Seq2Seq 來實現。

LSTM的模型結構為: 

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

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

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

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

  • question 進入 LSTM 的 encoder 環節,answer 進入 decoder 環節,

  • 分別生成 output tensor。

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

  • 最后和 encoder 的輸出,一起做為下一環節 Regression 的輸入,並傳入 DNN 網絡。


3. 如何准備 chatbot 的訓練數據

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

http://www.shareditor.com/blogshow/?blogId=121

訓練數據的生成過程如下:

  • 首先在 input file 里讀取每一行,並根據 ‘|’ 拆分成 question 和 answer 句子。

  • 每個句子,都將 word 通過 word2vec 轉化成詞向量。

  • 每一句的向量序列都轉化成相同維度的形式:self.word_vec_dim * self.max_seq_len

  • 最后 answer 構成了 y 數據,question+answer 構成了 xy 數據,再被投入到 model 中去訓練:

 

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 源碼解讀

提煉出步驟如下:

其中 2. 准備數據, 3. 建立模型 就是上文着重說的部分。

    1. 引入包

    1. 准備數據

    1. 建立模型

    1. 訓練

    1. 預測

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. 准備數據

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 中單詞對應的詞向量放在詞向量序列中 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 字典中查找與此向量距離最近的向量,並記憶相應的單詞,返回單詞和 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 傳遞給編碼器,返回一個輸出(預測序列的第一個值)和一個狀態(傳給解碼器)。 

在解碼器中,用編碼器的最后一個輸出作為第一個輸入,預測過程用前一個時間序的輸出作為下一個時間序的輸入。

4. 訓練

def train(self) 

用 generate_trainig_data() 生成 X y 數據,傳遞給 上面定義的 model,並訓練 model.fit,再保存。

5. 預測

用 generate_trainig_data() 生成數據,用 model.predict 進行預測,predict 結果的每一個 sample 相當於一句話的詞向量序列,每個 sample 中的每個 vector 在 word-vector 字典中找到與其最近的向量,並返回對應的 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)

 

------------------------------

 本人微信公眾帳號: 心禪道(xinchandao)

 

本人微信公眾帳號:雙色球預測合買(ssqyuce)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM