(1)基于检索的chatterbot
(2)基于生成模型的chatterbot
以前者为主,后者为辅;检索方法过程中当模型需要算法是,可以考虑加入深度学习。
(1)应答模式的匹配方法太粗暴
(2)特定领域+检索+合适的知识库能做到还不错。但开放域比较难
(1)匹配本身是一个模糊的场景
转成排序问题
(2)排序问题怎么处理?
转成能输出概率的01分类
(3)数据构建?
需要正样本(正确的答案)和负样本(不对的答案)
(4)Loss function
分类问题采用对数损失(二元的交叉熵损失)
)
IMPLEMENTING A RETRIEVAL-BASED MODEL IN TENSORFLOW,WILDML BLOG,2016
Microsoftz做法是,从其他不同的场景里,以相同的概率抽取答案,成为负样本。当前场景的问答作为正样本。
(1)训练集:
(2)验证集/测试集:
(3)评估准则:
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的数组
#定义随机预测函数
#从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
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,在队列里不断去数据,以跟的上入读数据的速度。
神经网络代码实现在下篇文章中具体阐述