从头实现深度学习的对话系统--简单chatbot代码实现

从头实现深度学习的对话系统–简单chatbot代码实现

本文的代码都可以到我的github中下载:https://github.com/lc222/seq2seq_chatbot

预训练好的模型可以到我的百度云网盘中下载:

链接:https://pan.baidu.com/s/1hrNxaSk 密码:d2sn

前面几篇文章我们已经介绍了seq2seq模型的理论知识,并且从tensorflow源码层面解析了其实现原理,本篇文章我们会聚焦于如何调用tf提供的seq2seq的API,实现一个简单的chatbot对话系统。这里先给出几个参考的博客和代码:

  1. tensorflow官网API指导
  2. Chatbots with Seq2Seq Learn to build a chatbot using TensorFlow
  3. DeepQA
  4. Neural_Conversation_Models

经过一番调查发现网上现在大部分chatbot的代码都是基于1.0版本之前的tf实现的,而且都是从tf官方指导文档nmt上进行迁移和改进,所以基本上大同小异,但是在实际使用过程中会发现一个问题,由于tf版本之间的兼容问题导致这些代码在新版本的tf中无法正常运行,常见的几个问题主要是:

  • seq2seq API从tf.nn迁移到了tf.contrib.legacy_seq2seq;
  • rnn目前也大都使用tf.contrib.rnn下面的RNNCell;
  • embedding_attention_seq2seq函数中调用deepcopy(cell)这个函数经常会爆出TypeError: can't pickle _thread.lock objects

关于上面第三个错误这里多说几句,因为确实困扰了我很久,基本上我在网上找到的每一份代码都会有这个错(DeepQA除外)。首先来讲一种最简单的方法是将tf版本换成1.0.0,这样问题就解决了。

然后说下不想改tf版本的办法,我在网上找了很久,自己也尝试着去找bug所在,错误定位在embedding_attention_seq2seq函数中调用deepcopy函数,于是就有人尝试着把deepcopy改成copy,或者干脆不进行copy直接让encoder和decoder使用相同参数的RNNcell,但这明显是不正确的做法。我先想出了一种解决方案就是将embedding_attention_seq2seq的传入参数中的cell改成两个,分别是encoder_cell和decoder_cell,然后这两个cell分别使用下面代码进行初始化:

encoCell = tf.contrib.rnn.MultiRNNCell([create_rnn_cell() for _ in range(num_layers)],)
decoCell = tf.contrib.rnn.MultiRNNCell([create_rnn_cell() for _ in range(num_layers)],)

这样做不需要调用deepcopy函数对cell进行复制了,问题也就解决了,但是在模型构建的时候速度会比较慢,我猜测是因为需要构造两份RNN模型,但是最后训练的时候发现速度也很慢,就先放弃了这种做法。

然后我又分析了一下代码,发现问题并不是单纯的出现在embedding_attention_seq2seq这个函数,而是在调用module_with_buckets的时候会构建很多个不同bucket的seq2seq模型,这就导致了embedding_attention_seq2seq会被重复调用很多次,后来经过测试发现确实是这里出现的问题,因为即便不使用model_with_buckets函数,我们自己为每个bucket构建模型时同样也会报错,但是如果只有一个bucket也就是只调用一次embedding_attention_seq2seq函数时就不会报错,其具体的内部原理我现在还没有搞清楚,就看两个最简单的例子:

import tensorflow as tf
import copy

cell = tf.contrib.rnn.BasicLSTMCell(10)
cell1 = copy.deepcopy(cell)#这句代码不会报错,可以正常执行

a = tf.constant([1,2,3,4,5])
b = copy.deepcopy(a)#这句代码会报错,就是can't pickle _thread.lock objects。可以理解为a已经有值了,而且是tf内部类型,导致运行时出错???还是不太理解tf内部运行机制,为什么cell没有线程锁,但是a有呢==

所以先忽视原因,只看解决方案的话就是,不适用buckets构建模型,而是简单的将所有序列都padding到统一长度,然后直接调用一次embedding_attention_seq2seq函数构建模型即可,这样是不会抱错的。(希望看到这的同学如果对这里比较理解可以指点一二,或者互相探讨一下)

最后我也是采用的这种方案,综合了别人的代码实现了一个embedding+attention+beam_search等多种功能的seq2seq模型,训练一个基础版本的chatbot对话机器人,tf的版本是1.4。写这份代码的目的一方面是为了让自己对tf的API接口的使用方法更熟悉,另一方面是因为网上的一些代码都很繁杂,想DeepQA这种,里面会有很多个文件还实现了前端,然后各种封装,显得很复杂,不适合新手入门,所以就想写一个跟textcnn相似风格的代码,只包含四个文件,代码读起来也比较友好。接下来就让我们看一下具体的代码实现吧。最终的代码我会放在github上

数据处理

这里我们借用DeepQA里面数据处理部分的代码,省去从原始本文文件构造对话的过程直接使用其生成的dataset-cornell-length10-filter1-vocabSize40000.pkl文件。有了该文件之后数据处理的代码就精简了很多,主要包括:

  1. 读取数据的函数loadDataset()
  2. 根据数据创建batches的函数getBatches()和createBatch()
  3. 预测时将用户输入的句子转化成batch的函数sentence2enco()

具体的代码含义在注释中都有详细的介绍,这里就不赘述了,见下面的代码:

padToken, goToken, eosToken, unknownToken = 0, 1, 2, 3

class Batch:
    #batch类,里面包含了encoder输入,decoder输入,decoder标签,decoder样本长度mask
    def __init__(self):
        self.encoderSeqs = []
        self.decoderSeqs = []
        self.targetSeqs = []
        self.weights = []

def loadDataset(filename):
    '
  • 2
    点赞
  • 42
    收藏
    觉得还不错? 一键收藏
  • 10
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值