一、檢索式與生成式聊天機器人對比
1、基於檢索的chatterbot
2、基於生成的chatterbot
3、聊天機器人的一些思考:
(1)基於檢索的chatterbot
- 根據input和context,結合知識庫的算法得到合適回覆。
- 從一個固定的數據集中找到合適的內容作爲回覆
- 檢索和匹配的方式有很多種(可以基於機器學習判斷屬於那種類型的匹配,利用關鍵字+word2vec進行文本相似度匹配)
- 數據和匹配方法對質量有很大影響
(2)基於生成模型的chatterbot
- 典型的是用seq2seq方法
- 生成的結果需要考慮通暢度和準確度
- 深度學習是學習數據的特徵。對於認爲很重要的特徵,可以最後在全連接層時進行矩陣拼接加入,而不參與特徵之間的學習,以免造成影響。
以前者爲主,後者爲輔;檢索方法過程中當模型需要算法是,可以考慮加入深度學習。
4、chatterbot的問題
(1)應答模式的匹配方法太粗暴
- 編輯距離無法捕獲深層語義信息
- 核心詞+word2vec無法捕獲整句話語義(對於我 愛 你和你 愛 我,詞向量表示是一樣的)
- LSTM等RNN模型能捕獲序列信息
- 用深度學習來提高匹配階段準確率
(2)特定領域+檢索+合適的知識庫能做到還不錯。但開放域比較難
5、轉化爲機器學習或深度學習能夠解決的問題,應該怎麼做
(1)匹配本身是一個模糊的場景
轉成排序問題
(2)排序問題怎麼處理?
轉成能輸出概率的01分類
(3)數據構建?
需要正樣本(正確的答案)和負樣本(不對的答案)
(4)Loss function
分類問題採用對數損失(二元的交叉熵損失)
)
二、使用深度學習完成問答:
1、論文
IMPLEMENTING A RETRIEVAL-BASED MODEL IN TENSORFLOW,WILDML BLOG,2016
2、論文框架圖
三、深度學習問答數據
1、中文:
Microsoftz做法是,從其他不同的場景裏,以相同的概率抽取答案,成爲負樣本。當前場景的問答作爲正樣本。
2、Ubuntu對話語料庫:
(1)訓練集:
- Ubuntu對話數據集,來自Ubuntu的IRC網絡上的對話日誌
- 訓練集1000000條實例,一半是正例(label爲1),一半是負例(label爲0,負例爲隨機生成)
- 樣本包括上下文信息(context,即Query)和一段可能的回覆內容,即Response;Label爲1表示Response和Query的匹配,Label爲0表示不匹配
- query的平均長度爲86個word,而response的平均長度爲17個word。
(2)驗證集/測試集:
- 每個樣本有一個正樣本和9個負樣本(也稱爲干擾樣本)
- 建模的目標是給正例的得分儘可能高(排序越靠前),而負例的得分儘可能低(有點類似分類問題)
- 語料做過分詞、stemmed、lemmatized等文本預處理
- 用NER(命名實體識別)將文本中的實體,如姓名、地點、組織、URL等替換成特殊字符。
(3)評估準則:
- recall@k(在前k個位置,能夠找回標準答案的概率有多高)
- 經模型對候選的response排序後,前k個候選中存在正例數據(正確的那個)的佔比
- k值越高,指標值越高,對模型性能的要求越松。
四、基線模型代碼實現:
import pandas as pd
import numpy as np
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.feature_extraction.text import TfidfTransformer
#導入數據
train_df = pd.read_csv("../data/train.csv")
test_df = pd.read_csv("../data/test.csv")
validation_df = pd.read_csv("../data/valid.csv")
y_test = np.zeros(len(test_df))
#定義評估指標
def evaluate_recall(y,y_test,k=n):
num_examples = float(len(y))
num_correct = 0
for predictions,label in zip(y,y_test):
if label in predictions[:k]:
num_correct += 1
return num_correct/num_examples
#從len(utterances)中隨機抽取數字,生成size=10的數組
1、基線模型:
#定義隨機預測函數
#從len(utterances)中隨機抽取數字,生成size=10的數組
#replace:True表示可以取相同數字,False表示不可以取相同數字
def predict_random(context,utterances):
return np.random.choice(len(utterances),10,replace=False)
#生成隨機預測結果
y_random = [predict_random(test_df.Context[x],test_df.iloc[x,1:].values) for x in range(len(test_df))]
#對前k=1,2,5,10分別進行評存在的正樣本概率評估
for n in [1,2,5,10]:
print(f'Recall @ ({n},10):{evaluate_recall(y_random,y_test,n):g}')
輸出結果:
Recall @ (1,10):0.0992072
Recall @ (2,10):0.199313
Recall @ (5,10):0.50037
Recall @ (10,10):1
2、基線模型:TF-IDF檢索
class TFIDFPredictor():
def __init__(self):
self.vectorizer = TfidfVectorizer()
def train(self, data):
self.vectorizer.fit(np.append(data.Context.values,data.Utterance.values))
def predict(self,context,utterances):
#將輸入問題Q轉化爲向量
vector_context = self.vectorizer.transform([context])
#將回答A轉化爲向量
vector_doc = self.vectorizer.transform(utterances)
#將回答向量與問題向量做矩陣相乘
result = np.dot(vector_doc,vector_context.T).todense()
result = np.asarray(result).flatten()
##將result中的元素從小到大排列,提取其對應的index(索引)。再將索引進行倒敘排列(越在前面,概率越大)
#argsort
return np.argsort(result,axis=0)[::-1]
pred = TFIDFPredictor()
pred.train(train_df)
y = [pred.predict(test_df.Context[x],test_df.iloc[x,1:].values) for x in range(len(test_df))]
for n in [1,2,5,10]:
print(f'Recall @ ({n},10):{evaluate_recall(y,y_test,n):g}')
輸出結果:
Recall @ (1,10):0.485624
Recall @ (2,10):0.586681
Recall @ (5,10):0.762474
Recall @ (10,10):1
五、神經網絡建模原理
1、Query 和Response都是經過分詞和embedding映射的。初始向量使用GloVe/word2vec
2、分詞且向量化的Query和Response經過相同的RNN(word by word)(同一組參數)。RNN最終生成一個向量表示,捕捉了Query和Reponse之間的【語義聯繫】(圖中的c和r);這個
向量的維度是可以指定的,這裏指定爲256維。
3、將向量c與一個矩陣M相乘,來預測一個可能的回覆r‘。如果c爲一個256維的向量,M維是256*256的矩陣,兩者相乘的結果爲另一個256維向量,我們可以將其解釋爲【一個生成式的回覆向量】。矩陣M是需要訓練的參數
4、通過點乘的方式來預測生成的回覆r’和候選的回覆r之間的相似程度,點乘結果越大表示候選回覆最爲回覆的可信度越高;之後通過sigmoid函數歸一化,轉成概率形式。(sigmoid作爲壓縮函數經常使用)
5、損失函數:二元的交叉熵函數/對數函數。回想邏輯迴歸,交叉熵損失函數爲L = -y * ln(y’)- (1 - y) * ln(1 - y’)。(公式的意義是直觀的,即當y=1時,L=-ln(y’),我們希望y’儘量接近1,使得損失韓式的值越小‘反之亦然。)
使用Tensorflow的話訓練速度主要受2方面影響。一、讀數據(例如,訓練數據是512兆的文本數據,100w個長文本。是很大的數據集),開銷會很大。可以使用Tensorflow能都讀進去的格式tdrecords。可以用Tensorflow自帶的工具進行讀入和處理。如一個batch或構建一個隊列。二、GPU處理的速度。可以構建一個隊列 query,在隊列裏不斷去數據,以跟的上入讀數據的速度。
神經網絡代碼實現在下篇文章中具體闡述