基于seq2seq的中文聊天机器人(三)

系列文章

1.基于seq2seq的中文聊天机器人(一)
2.基于seq2seq的中文聊天机器人(二)
3.基于seq2seq的中文聊天机器人(三)

5 可视化前端

5.1聊天机器人结构

设计一个聊天机器人需要:

  • 浏览器前端:为用户提供友好的机器聊天页面;
    机器聊天API服务:前后端通过异步的HTTP调用实现通讯,前端将对话请求提交给机器对话Web服务,完成后将结果返回前端进行展示;
  • 机器聊天后端:使用tf-seq2seq进行机器聊天模型训练,得到对应语言的模型文件。然后通过Python命令可以进行句级的聊天,机器聊天API服务调用该命令。

逻辑功能架构如图:
基于seq2seq的中文聊天机器人(三)_第1张图片

5.2 web分析与设计

想要用户体验良好,我们就需要一个好用的web界面给用户使用,屏蔽掉底层,这样用户才能简单的使用我们的产品。
(1)主要组成部分简介
Web界面的设计主要由HTML,CSS,JS文件构成,他们的主要功能如下。

  • HTML(HyperText Markup
    Language)超文本标记语言,决定网页的结构及内容,即“显示哪些内容”。可以把HTML说成是静态代码。“超文本”就是指页面内可以包含图片、链接,甚至音乐、程序等非文字元素。

  • CSS(Cascading Style
    Sheet)层叠样式表单,设计网页的表现样式,即“如何显示有关内容”,CSS是将样式信息与网页内容分离的一种标记语言,我们使用css为每个元素定义样式;它主要用于美化HTML页面。

  • JS(JavaScript)一种动态脚本语言,控制网页的行为(效果),即“内容应该如何对事件做出反应”,使用JavaScript代码可以让前台变的有交互(点击事件),常用来为网页添加各式各样的动态功能,为用户提供更流畅美观的浏览效果。通常JavaScript脚本是通过嵌入在HTML中来实现自身的功能的。

    JS的常用功能: ·嵌入动态文本于HTML页面 ·对浏览器事件作出响应 ·读写HTML元素 ·在数据被提交到服务器之前验证数据;检测访客的浏览器信息 ·控制cookies,包括创建和修改等

对于一个网页来说,HTML定义网页的结构,CSS描述网页的样子,而JavaScript控制网页行为,设置一个很经典的例子是说HTML就像 一个人的身体,而CSS就是人的衣服,Javascript就像人的思想和行为。

(2)分析与设计

这是一个会话界面,输入一句话,输出一句应答的话。需要设计提供对外调用的API,该API接受到用户输入的问答信息,根据接受到的信息调用模型预测应答会话并返回给服务器。

API的调用需要通过web服务器提供。Tornado是使用Python编写的一个强大的、可扩展的Web服务器。它在处理严峻的网络流量时表现得足够强健,在创建和编写时有着足够的轻量级,并能够被用在大量的应用和工具中。所以这里使用tornado绑定API对象,设置监听端口,循环监听用户的服务请求。

设计HTML的结构与内容,设置文章标题,对话框,文本输入框,发送信息,显示信息,还有背景以及头像的加载,引用js文件用于响应浏览器事件,读取数据和页面更新。

设计CSS用于美化HTML页面,选择器选择元素对其长宽高位置等进行布局设计。

编写js脚本用于页面事件的响应,页面动态更新,使用ajax实现异步通信效果,ajax可以实现动态不刷新(局部刷新)就是能在不更新整个页面的前提下维护数据。这使得Web应用程序更为迅捷地回应用户动作,并避免了在网络上发送那些没有改变过的信息。

最后得到的文件目录和逻辑结构如下图
基于seq2seq的中文聊天机器人(三)_第2张图片

5.3工作流程

RestfulAPI.py开启web容器,监听8000端口,聊天机器人前端网页通过ajax请求后端接口,返回机器人应答数据。具体流程如下

打开接口访问地址,本地服务器默认http://127.0.0.1:8000/api/chatbot
浏览器加载index.html,引用css文件进行渲染,在有引用js的地方执行js
基于seq2seq的中文聊天机器人(三)_第3张图片
页面加载好了之后如上图,页面加载完成之后,chat.js就开始执行了,这时可以进行会话,输入问答信息,点击发送,启动一个事件

  • chat.js响应事件,将输入信息作为参数,通过ajax向服务器发送信息
  • 服务器调用绑定的API,API通过传递的参数调用模型,生成预测会话,将会话返回给服务器 服务器返回给ajax引擎json格式文件
  • chat.js通过ajax的函数将接收到的数据插入DOM树,再调用show函数更新网页,一轮对话完成 可以继续输入信息进行对话,步骤如上

5.4.技术实现

(1) api文件
提供对外调用的接口,根据接收到的信息调用seq2seq模型返回预测结果,如果输入为空格,返回请输入聊天信息。

def chatbot_api(infos):
    du = DataProcessing.DataUnit(**data_config)
    save_path = os.path.join(BASE_MODEL_DIR, MODEL_NAME)
    batch_size = 1
    tf.reset_default_graph()
    model = Seq2Seq(batch_size=batch_size,
                    encoder_vocab_size=du.vocab_size,
                    decoder_vocab_size=du.vocab_size,
                    mode='decode',
                    **model_config)
    # 创建session的时候允许显存增长
    config = tf.ConfigProto()
    config.gpu_options.allow_growth = True

    with tf.Session(config=config) as sess:
        init = tf.global_variables_initializer()
        sess.run(init)
        model.load(sess, save_path)

        while True:
            q = infos
            #判断接受到的参数是否为空格字符串
            if q is None or q.strip() == '':
                return "请输入聊天信息"
                continue
            q = q.strip()

            indexs = du.transform_sentence(q)
            x = np.asarray(indexs).reshape((1, -1))
            xl = np.asarray(len(indexs)).reshape((1,))
            pred = model.predict(
                sess, np.array(x),
                np.array(xl)
            )
            result = du.transform_indexs(pred[0])
            return result

在resultful.py中创建应用对象,使用tornado服务器,绑定监听端口8000,监听端口的连接。

f __name__ == '__main__':
    # 创建一个应用对象
    app = tornado.web.Application([(r'/api/chatbot', IndexHandler)])
    # 绑定一个监听端口
    app.listen(8000)
    # 启动web程序,开始监听端口的连接
    tornado.ioloop.IOLoop.current().start()

服务器增加解决跨域名请求访问和异常处理的功能,若调用api过程中出现了异常,则向用户返回服务器内部错误。

class BaseHandler(RequestHandler):
    """解决JS跨域请求问题"""
    def set_default_headers(self):
        self.set_header('Access-Control-Allow-Origin', '*')
        self.set_header('Access-Control-Allow-Methods', 'POST, GET')
        self.set_header('Access-Control-Max-Age', 1000)
        self.set_header('Access-Control-Allow-Headers', '*')
        # self.set_header('Content-type', 'application/json')
class IndexHandler(BaseHandler):
    # 添加一个处理get请求方式的方法
    def get(self):
        # 向响应中,添加数据
        infos = self.get_query_argument("infos")
        print("Q:", infos)
        # 捕捉服务器异常信息
        try:
            result = chatbot_api(infos=infos)
        except:
            result = "服务器内部错误"
        print("A:", "".join(result))
        self.write("".join(result))

(2) HTML文件

<html lang="en">

   <head>
      <meta charset="UTF-8" />
       <link rel="shortcut icon" href="chatImages/favicon.ico" />
      <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
      <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />
      <title>聊天界面</title>
      <link rel="stylesheet" type="text/css" href="chatCss/chat.css" />

      <script src="chatJs/jquery.min.js"></script>
      <script src="chatJs/flexible.js"></script>
   </head>

   <body>
      <header class="header" align="center">
         <span style="font-size:18px">聊天机器人</span>
         <input type="hidden" style="font-size:15px;text-align: center;" type="text" id="picker" placeholder="参数选择"  readonly="readonly"
         value='LSTM 2层 25轮'>
      </header>
      <div class="message">

          <div class="send">
            <div class="msg">
               <img src="chatImages/man.png" alt="" />
               <p><i class="msg_input"></i>请输入内容开始对话</p>
            </div>
         </div>
         <div class="send">      
         </div>
      </div>
      <div class="footer">
         <input type="text" id='inputVal' placeholder='请输入对话内容'/>
         <p>发送</p>
      </div>
   <script src="chatJs/chat.js" type="text/javascript" charset="utf-8"></script>
   
   <script src="chatJs/picker.min.js"></script>
   <script type="text/javascript" src="chatJs/city.js"></script>
   <script type="text/javascript" src="chatJs/index.js"></script>
   
   </body>
</html>

分析:

IE使用最新的引擎渲染网页,chrome=1则可以激活Chrome Frame. html文档开头加载了chatJs/jquery.min.js这个文件, 可以使用丰富的jQuery函数来做开发,做更多的事情(Write less, Do more),主要是方便 DOM操作,当然它的功能挺多的。
<input type="hidden" style="font-size:15px;text-align: center;" type="text" id="picker" placeholder="参数选择"  readonly="readonly"
value='LSTM 2层 25轮'>

上面一段隐藏域在页面中对于用户是不可见的,在表单中插入隐藏域的目的在于收集或发送信息,以利于被处理表单的程序所使用。浏览者单击发送按钮发送表单的时候,隐藏域的信息也被一起发送到服务器。用来对参数进行选择,这里想要通过picker实现网络属性的三级联动选择,选择编码器和解码器的神经元是LSTM还是GRU,网络层数,以及训练的轮数,这里默认参数是LSTM 225轮。
加载移动端适配js  chatJs/flexible.js  在移动设备上使用时自适应屏幕。
中间为显示对话内容,显示发送的信息和接受到的服务器返回的信息。
下部为输入文本框和发送框,发送框绑定一个事件,引入了chat.js响应发送事件。
Picker.min.js 以及index.js实现模型属性的联动选择,为筛选器组件,这里没有使用到。
city.js为模型属性的三级联动库,用于模型的属性选择。
(3)js文件
js文件主要功能是通过ajax实现DOM树的更新。
Upview函数的功能是选择message插入html内容,并且实现页面的对话自动滚动到底部。
Show函数定义了发送和接受信息的html变量,并且调用了upview函数更新视图
*聊天信息,发送和接受的信息*/
function show(headSrc, str, className) {
   var html = "
+ className + ">
+ headSrc + " />" + "

" + str + "

"
; upView(html); } /*更新视图*/ function upView(html) { $('.message').append(html); //选择message插入html内容 $('body,html').animate({ scrollTop: $('.message').outerHeight() - window.innerHeight }, 200); // 自动将页面移动到最底部 }

jQuery主要函数,DOM加载完成之后,jQuery获取输入框焦点为inputval赋值,并且需要绑定键盘的up状态进行信息输入,输入框的p标签(发送)绑定信息获取事件,调用getMessage函数与服务器的数据交换和页面更新。

getMessage函数执行数据交换和网页更新,首先判断输入是否为空,为空则返回,什么也不做,不空则为发送框上锁,防止连续提交,将输入信息显示到对话框,清空输入框。通过ajax异步执行请求,与服务器交换数据,先向服务器发送数据,服务器调用绑定的API返回数据。请求服务成功之后,再调用show函数将接收到的数据用于更新DOM ,再显示到对话框中。

//页面DOM文档加载完成后加载执行
$(function () {
   $('#inputVal').focus();//点击获得输入框焦点
   $('.footer').on('keyup', 'input', function () {//绑定输入框键盘up
      if ($(this).val().length > 0) {               //状态
         $(this).next().css('background', '#114F8E');

      } else {
         $(this).next().css('background', '#ddd');
      }
   });

   $('.footer p').click(getMessage);//绑定信息获取事件
   $(document).keyup(function (ev) {  //文本框按下回车键执行事件
      if (ev.keyCode == 13) {
         getMessage();
      }
   })
      //获取输入的文本信息
   function getMessage() {
      var val = $('#inputVal').val();  //返回inputVal的值给val
      if (val == '')
         return;    //若为空,则直接返回
      if (flag) {
         flag = false;   //上锁,防止连续提交
            //输入信息显示到对话框
         show("./chatImages/woman.png", $(".footer input").val(), "show");
         // 替换为各自的接口地址
         var url = "http://127.0.0.1:8000/api/chatbot";
         //清空input
         $(".footer input").val("").next().css('background', '#ddd');
         $.ajax({   //异步执行请求,与服务器交换数据
            type: "get",       //请求的方式,有POST和GET两种,默认是GET,数据类型是String
            dataType: "json",  //预期服务器返回的数据类型json
            async: true,       //异步请求
            url: url,          //请求的服务器地址,默认是当前页面,数据类型是String
            data: {
               infos: val,    //发送到服务器的数据,必须为key/value格式
            },
            complete: function (data) {  //请求成功之后前端处理的代码
               flag = true;
               message = data.responseText
                  setTimeout(function () {   //浏览器会在合适的时间,将代码插入任务队列
                     show("chatImages/man.png", message, "send");  //显示接受到的信息
                  }, 500);   //500毫秒后调用function函数
            }
         });
      }
   }
});

5.5部署测试

我们已将项目部署到阿里云服务器上,地址http://47.100.77.54:8088/(如果不回复就是我把服务关掉了)
输入空格则会提示返回请输入聊天信息,输入简单直接的问题基本上可以得到预期的效果。
基于seq2seq的中文聊天机器人(三)_第4张图片

基于seq2seq的中文聊天机器人(三)_第5张图片
基于seq2seq的中文聊天机器人(三)_第6张图片
基于seq2seq的中文聊天机器人(三)_第7张图片

 
 
 
最后附上github地址,欢迎star~
https://github.com/daniellibin/seq2seq-chatbot

你可能感兴趣的:(nlp)