如何使用tensorflow做一个人机对话机器人

一、前言

开始这个项目前,你需要具备什么知识:

1.tensorflow框架的使用

2.文本预处理

3.循环神经网络的理论

二、数据预处理

我们先来看看数据集是什么样子的。

两个E之间为一段对话,M为某个角色说的话。

第一步则是提取出每段对话的问题和回答。

代码如下:

import re

import numpy as np

datapath="source_data.conv"
data_list=[]
def filter_non_chinese(text):
    chinese_pattern = re.compile('[^\u4e00-\u9fa5]') # 匹配非汉字字符的正则表达式
    return chinese_pattern.sub('', text)
with open(datapath,'r',encoding='utf-8') as f:
    complete_dialog=[]
    for line in f:
        line=line.strip('\n')
        ls=line.split()
        if ls[0]=='E':
            data_list.append(complete_dialog)
            complete_dialog=[]
        if ls[0]=='M':
            s=''.join(ls[1:])
            s1=filter_non_chinese(s)
            # print(s1)
            complete_dialog.append(s1)

看看这个data_list最终的样子:

每个列表都是一段对话,且在提取的过程中我们还用正则表达式过滤掉了非中文字符。

第二步,每句话都需要一个开始标志和一个结束标志,不然计算机是不知道一句完整的话是什么样子的。

代码如下:

questions=[]
answers=[]
for dialog in data_list:
    if len(dialog)<2:
        continue
    if len(dialog)%2!=0:
        dialog=dialog[:-1]
    for i in range(len(dialog)):
        if i %2==0:
            questions.append("<start> "+" ".join(dialog[i])+" <end>")
        else:
            answers.append("<start> "+" ".join(dialog[i])+" <end>")

处理后的样子:

一共有两个列表,questions存放问题语句,answers存放回答语句,且都是一一对应的。

第三步则是文本编码了,传统的独热编码是满足不了文本编码的,在tensorflow中提供了一种文本编码类Tokenizer,在自然语言领域内非常实用。该类允许使用两种方法向量化一个文本语料库: 将每个文本转化为一个整数序列(每个整数都是词典中标记的索引); 或者将其转化为一个向量,其中每个标记的系数可以是二进制值、词频、TF-IDF权重等。

代码如下:

from tensorflow import keras
def tokenize(datas):
   tokenizer=keras.preprocessing.text.Tokenizer(filters="")
   tokenizer.fit_on_texts(datas)
   voc_li=tokenizer.texts_to_sequences(datas)
   voc_li=keras.preprocessing.sequence.pad_sequences(voc_li,padding='post')
   return voc_li,tokenizer

三、人机对话模型的构建

模型的关键在于编码器、注意力机制和编码器,接下来我们来构建它们吧。

在构建之前我们引入了循环神经网络:

传统的循环神经网络是满足不了的,随着时间的推移,前面的输入对后面的输出的影响可能不是很大,我们就此引入LSTM。

随着科学的发展,LSTM的优化版本GRU诞生了,它们有什么不同:

构建GRU的代码如下:

import tensorflow as tf


class Encoder(tf.keras.Model):
    def __init__(self, vocab_size, embedding_dim, enc_units, batch_sz):
        super(Encoder, self).__init__()
        self.batch_sz = batch_sz
        self.enc_units = enc_units
        self.embedding = tf.keras.layers.Embedding(vocab_size, embedding_dim)
        self.gru = tf.keras.layers.GRU(self.enc_units,
                                       return_sequences=True,
                                       return_state=True,
recurrent_initializer='glorot_uniform')
    @tf.function
    def call(self, x, hidden):
        x = self.embedding(x)
        output, state = self.gru(x, initial_state=hidden)
        return output, state

    def initialize_hidden_state(self):
        return tf.zeros((self.batch_sz, self.enc_units))

接下来是注意力机制的构建:

class BahdanauAttentionMechanism(tf.keras.layers.Layer):
    def __init__(self, units):
        super(BahdanauAttentionMechanism, self).__init__()
        self.W1 = tf.keras.layers.Dense(units)
        self.W2 = tf.keras.layers.Dense(units)
        self.V = tf.keras.layers.Dense(1)
    @tf.function
    def call(self, query, values):
        query_with_time_axis = tf.expand_dims(query, 1)
        score = self.V(tf.nn.tanh(
            self.W1(query_with_time_axis) + self.W2(values)))
        attention_weights = tf.nn.softmax(score, axis=1)
        context_vector = attention_weights * values
        context_vector = tf.reduce_sum(context_vector, axis=1)

        return context_vector, attention_weights

 解码器的构建:

class Decoder(tf.keras.Model):
    def __init__(self, vocab_size, embedding_dim, dec_units, batch_sz):
        super(Decoder, self).__init__()
        self.batch_sz = batch_sz
        self.dec_units = dec_units
        self.embedding = tf.keras.layers.Embedding(vocab_size, embedding_dim)
        self.gru = tf.keras.layers.GRU(self.dec_units,
                                       return_sequences=True,
                                       return_state=True,
                                       recurrent_initializer='glorot_uniform')
        self.fc = tf.keras.layers.Dense(vocab_size)
        self.attention = BahdanauAttentionMechanism(self.dec_units)
    @tf.function
    def call(self, x, hidden, enc_output):
        context_vector, attention_weights = self.attention(hidden, enc_output)
        x = self.embedding(x)
        x = tf.concat([tf.expand_dims(context_vector, 1), x], axis=-1)
        output, state = self.gru(x)
        output = tf.reshape(output, (-1, output.shape[2]))
        x = self.fc(output)
        return x, state, attention_weights

 到此模型以及构建完成。

四、模型的训练

模型训练需要损失函数,优化器,同样需要手动构建:

def loss(real,pred):
    mask=tf.math.logical_not(tf.math.equal(real,0))
    loss_obj=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True,reduction='none')
    loss_value=loss_obj(real,pred)
    mask=tf.cast(mask,dtype=loss_value.dtype)
    loss_value*=mask
    return tf.math.reduce_mean(loss_value)
def grad_loss(q,a,q_hidden,encoder,decoder,q_index,BATCH_SIZE):
    loss_value=0
    with tf.GradientTape() as tape:
        q_output,q_hidden=encoder(q,q_hidden)
        a_hidden=q_hidden
        a_input=tf.expand_dims([q_index.word_index["<strat>"]*BATCH_SIZE,1])
        for vector in range(1,a.shape[1]):
            predictions,a_hidden,_=decoder(a_input,a_hidden,q_output)
            a_input=tf.expand_dims(a[:,vector],1)
        batch_loss=(loss_value/int(a.shape[1]))
        variable=encoder.trainable_variable+decoder.trainable_variable
        return batch_loss,tape.gradient(loss_value,variable)
def optimize_loss(q,a,q_hidden,encoder,decoder,q_index,BATCH_SIZE,optimizer):
    batch_loss,grads=grad_loss(q,a,q_hidden,encoder,decoder,q_index,BATCH_SIZE)
    variables=encoder.trainable_variable+decoder.trainable_variable
    optimizer.apply_gradients(zip(grads,variables))
    return batch_loss

最后就是训练函数的编写,这部分是最重要的,写了这么多函数,我们该怎么把他们给组织起来:

import time


def train_model(q_hidden, encoder, decoder, q_index, BATCH_SIZE,dataset,steps_per_epoch,optimizer,checkpoint,checkpoint_prefix,summary_writer):
    i=0
    EPOCHS = 200
    for epoch in range(EPOCHS):
        start=time.time()
        a_hidden = encoder.initalize_hidden_state()
        total_loss = 0
        for (batch,(q,a)) in enumerate(dataset.take(steps_per_epoch)):
            batch_loss=optimize_loss(q, a, q_hidden, encoder, decoder,q_index,BATCH_SIZE,optimizer)
            total_loss += batch_loss
            with summary_writer.as_default():
                tf.summary.scalar('batch_loss', batch_loss.numpy(), step=epoch)
            if batch%100==0:
                print("第{}次训练,第{}批数据损失值:{:.4f}".format(
                    epoch+1,
                    batch+1,
                    batch_loss.numpy()
                ))
            with summary_writer.as_default():
                tf.summary.scalar('total loss',total_loss/steps_per_epoch,step=epoch)
            if (epoch+1)%100==0:
                i+=1
                print("=====第{}次报存训练模型=====".format(i))
                checkpoint.save(file_prefix=checkpoint_prefix)
            print("第{}次训练,总损失值:{:.4f}".format(epoch+1,total_loss/steps_per_epoch))
            print("训练耗时:{:.1f}".format(time.time()-start))

五、编写主函数以及其他函数

def max_length(vectors):
    return max(len(x) for x in vectors)

def convert(index,vectors):
    for vector in vectors:
        if vector !=0:
            print("{}-->{}".format(vector,index.index_word[vector]))

def preprocess_question(question):
    question="<start> "+' '.join(question)+" <end>"
    return question

def answer_vector(question,a_max_len,q_max_len,q_index,a_index,encoder,decoder):
    attention_plot=np.zeros((a_max_len,q_max_len))
    question=preprocess_question(question)
    inputs=[q_index.word_index[i] for i in question.split(' ')]
    inputs=tf.keras.preprocessing.sequence.pad_sequences([inputs],maxlen=q_max_len,padding='post')
    inputs=tf.convert_to_tensor(inputs)
    result=''
    hidden=[tf.zeros((1,units))]
    q_out,q_hidden=encoder(inputs,hidden)
    a_hidden=q_hidden
    a_input=tf.expand_dims([a_index.word_index["<start>"]],0)
    for t in range(a_max_len):
        predictions,a_hidden,attention_weights=decoder(a_input,a_hidden,q_out)
        attention_weights=tf.reshape(attention_weights,(-1,))
        attention_plot[t]=attention_weights.numpy()
        predicted_id=tf.argmax(predictions[0]).numpy()
        result+=a_index.index_word[predicted_id]
        if a_index.index_word[predicted_id]=="<end>":
            result+=a_index.index_word[predicted_id]
        else:
            return result,question,attention_plot
        a_input=tf.expand_dims([predicted_id],0)
    return result,question,attention_plot

#对话函数
def chat(question,a_max_len,q_max_len,q_index,a_index,encoder,decoder):
    result,question,attention_plot=answer_vector(question,a_max_len,q_max_len,q_index,a_index,encoder,decoder)
    print('机器人:',result)

最终如何运行,需要编写下面这段代码:

import os
from datetime import datetime

if __name__ == "__main__":
    stamp = datetime.now().strftime("%Y%m%d-%H:%M:%S")
    # source_path = "./data/source_data.conv"
    # 下载文件
    path_to_zip = tf.keras.utils.get_file(
    'spa-eng.zip', origin='http://storage.googleapis.com/download.tensorflow.org/data/spa-eng.zip',
    extract=True)
    path_to_file = os.path.dirname(path_to_zip)+"/spa-eng/spa.txt"
    # answers, questions  = create_dataset(path_to_file, 24000)
    # q_vec, q_index = tokenize(questions)
    # a_vec, a_index = tokenize(answers)
    # questions, answers = source_data(source_path)
    q_vec, q_index = tokenize(questions)
    a_vec, a_index = tokenize(answers)
    print("voc:", q_vec)
    print("tokenize:", q_index.index_word)
    print("voc:", a_vec)
    print("tokenize:", a_index.index_word)

    q_max_len = max_length(q_vec)
    a_max_len = max_length(a_vec)
    convert(q_index, q_vec[0])
    BUFFER_SIZE = len(q_vec)
    print("buffer size:", BUFFER_SIZE)
    BATCH_SIZE = 64
    steps_per_epoch = len(q_vec)//BATCH_SIZE
    embedding_dim = 256
    units = 1024
    q_vocab_size = len(q_index.word_index)+1
    a_vocab_size = len(a_index.word_index)+1
    dataset = tf.data.Dataset.from_tensor_slices(
        (q_vec, a_vec)
    ).shuffle(BUFFER_SIZE)
    dataset = dataset.batch(BATCH_SIZE, drop_remainder=True)
    # 数据遍历测试
    # for(batch,(q, a)) in enumerate(dataset.take(steps_per_epoch)):
    #     print("batch:",batch)
    #     print("question:",q)
    #     print("answer:",a)
    # 正常训练
    q_batch, a_batch = next(iter(dataset))
    print("question batch:",q_batch.shape)
    print("answer batch:", a_batch.shape)
    log_path = "./logs/chat"+stamp
    summary_writer = tf.summary.create_file_writer(log_path)
    tf.summary.trace_on(graph=True, profiler=True)
    encoder = Encoder(
        q_vocab_size,
        embedding_dim,
        units,
        BATCH_SIZE)
    q_hidden = encoder.initialize_hidden_state()
    q_output, q_hidden = encoder(q_batch, q_hidden)
    with summary_writer.as_default():
        tf.summary.trace_export(name="chat-en", step=0, profiler_outdir=log_path)

    tf.summary.trace_on(graph=True, profiler=True)
    attention_layer = BahdanauAttentionMechanism(10)
    attention_result, attention_weights = attention_layer(
        q_hidden, q_output
    )
    with summary_writer.as_default():
        tf.summary.trace_export(name="chat-atten", step=0, profiler_outdir=log_path)

    tf.summary.trace_on(graph=True, profiler=True)
    decoder = Decoder(
        a_vocab_size,
        embedding_dim,
        units,
        BATCH_SIZE
    )
    a_output, _, _ = decoder(
        tf.random.uniform((64,1)),
        q_hidden,
        q_output
    )
    with summary_writer.as_default():
        tf.summary.trace_export(name="chat-dec", step=0, profiler_outdir=log_path)
    optimizer = tf.keras.optimizers.Adam()
    checkpoint_dir = "./models"
    checkpoint_prefix = os.path.join(checkpoint_dir, "ckpt")
    checkpoint = tf.train.Checkpoint(
        optimizer=optimizer,
        encoder=encoder,
        decoder=decoder
    )
    # 训练模型
    train_model(q_hidden, encoder, decoder, q_index, BATCH_SIZE, dataset, steps_per_epoch, optimizer, checkpoint, checkpoint_prefix,summary_writer)
    # 恢复模型,进行预测
    checkpoint.restore(tf.train.latest_checkpoint(checkpoint_dir))
    # 对话预测
    print("====机器人1号为您服务====")
    while True:
        inputs = input("用户:")
        if inputs == "q":
            exit()
        chat(inputs,a_max_len, q_max_len, q_index, a_index, encoder, decoder)
        # chat_image(inputs,a_max_len, q_max_len, q_index, a_index, encoder, decoder)

运行这段代码,会进行200轮的训练,最后调用对话函数,就可以和机器人对话啦。

五、总结

本文到此结束啦,作者不会提供代码,只提供数据集,因为代码全都在代码块里哈哈哈。

获取数据集的方式:

1.点赞+收藏+评论区留言邮箱

2.点赞+收藏+私信发邮箱

  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
深度学习TensorFlow对话机器人实战全系列精品课 :一、课程优势本课程有陈敬雷老师的清华大学出版社配套书籍教材《分布式机器学习实战》人工智能科学与技术丛书,新书配合此实战课程结合学习,一静一动,互补高效学习!本课程由互联网一线知名大牛陈敬雷老师全程亲自授课,技术前沿热门,这个《深度学习TensorFlow对话机器人实战全系列精品课》来自陈敬雷在一线大型互联网公司的多年实战经验总结,实实在在的重量级干货分享!二、课程简介       对话机器人一个用来模拟人类对话或聊天的计算机程序,本质上是通过机器学习和人工智能等技术让机器理解人的语言。它包含了诸多学科方法的融合使用,是人工智能领域的一个技术集中演练营。在未来几十年,人机交互方式将发生变革。越来越多的设备将具有联网能力,这些设备如何与人进行交互将成为一个挑战。自然语言成为适应该趋势的新型交互方式,对话机器人有望取代过去的网站、如今的APP,占据新一代人机交互风口。在未来对话机器人的产品形态下,不再是人类适应机器,而是机器适应人类,基于人工智能技术的对话机器人产品逐渐成为主流。      对话机器人从对话的产生方式,可以分为基于检索的模型(Retrieval-Based Models)和生成式模型(Generative Models),基于检索我们可以使用搜索引擎的方式来,基于生成式模型我们可以使用TensorFlow或MXnet深度学习框架的Seq2Seq算法来实现,同时我们可以加入强化学习的思想来优化Seq2Seq算法。      我们这个《深度学习TensorFlow对话机器人实战全系列精品课》从TensorFlow深度学习框架原理以及主流的神经网络算法讲起,逐步由浅入深的给大家详细讲解对话机器人项目的原理以及代码实现、并在公司服务器上演示如何实际操作和部署的全过程!! !深度学习TensorFlow对话机器人实战全系列精品课大纲如下:一、主流深度学习框架1、Tensorflow深度学习框架2、mxnet多GPU深度学习框架二、神经网络算法3、MLP多层感知机算法4、CNN卷积神经网络5、RNN循环神经网络,6、LSTM长短期记忆神经网络7、Seq2Seq端到端神经网络【可试听】8、GAN生成对抗网络9、深度强化学习DQN三、对话机器人实战10、对话机器人原理与介绍11、基于TensorFlow对话机器人项目实战【可试听】12、基于TensorFlow对话机器人模型训练前数据准备和处理13、基于TensorFlow对话机器人项目实战源码解析和Linux服务器训练模型过程操作实战14、基于TensorFlow对话机器人项目服务工程化和在Linux服务器上操作实战【可试听】15、基于MXNet对话机器人项目实战16、基于MXNet对话机器人项目实战源码解析17、基于MXNet对话机器人项目服务工程化和在Linux服务器上操作实战18、基于深度强化学习机器人19、基于搜索引擎对话机器人20、对话机器人的Web服务工程化三、老师介绍陈敬雷  充电了么创始人,CEO兼CTO陈敬雷,北京充电了么科技有限公司创始人,CEO兼CTO,十几年互联网从业经验,曾就职于用友、中软、凡客、乐蜂网(唯品会)、猎聘网、人民日报(灵思云途)、北京万朝科技,曾任架构师、首席技术官、首席科学家等职务,对业务领域B端、C端、电商、职场社交招聘、内容文娱、营销行业都有着丰富的经验,在技术领域,尤其在大数据和人工智能方向有丰富的算法工程落地实战经验,其中在猎聘网任职期间主导的推荐算法系统项目获得公司优秀项目奖,推荐效果得到5倍的提升。陈敬雷著有清华大学出版社两本人工智能书籍,分别是《分布式机器学习实战(人工智能科学与技术丛书)》、《自然语言处理原理与实战(人工智能科学与技术丛书)》。目前专注于大数据和人工智能驱动的上班族在线教育行业,研发了充电了么app和网站,用深度学习算法、nlp、推荐引擎等技术来高效提升在线学习效率。 

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值