aiml简介+源代码解析+中文分词(java)

本文整合了看到的几篇博客并结合了笔者自己的尝试,记录下对aiml的浅显理解,想了解的更深入还是要靠自己探索呀!

目录

一、简介

二、原理+源代码解析

三、中文分词尝试

四、注意

五、优势与缺点(个人理解)

 

一、简介

AIML,全名为Artificial Intelligence Markup Language(人工智能标记语言),是一种创建自然语言软件代理的XML语言,最初来源于一个名为"A.L.I.C.E."的聊天机器人。

简言之,AIML就是用户通过定义的规则模板进行问答匹配,来实现聊天机器人自动问答的功能

下面展示了一个最基本的例子,仅包含最主要的<category><pattern><template>三种标签

<category>
    <pattern>你好</pattern>
    <template>您好,很高兴认识您。</template>
</category>

当用户输入问题“你好”时,机器人就会匹配到这个pattern,然后将<template>中的内容作为答案返回

标签的详细说明可以参考https://www.tutorialspoint.com/aiml/index.htm

二、原理+源代码解析

1.原理

原理参考了春雨里de太阳AIML知识库数据匹配原理解析这篇博客(感谢!)

一个通常流程是:

首先系统初始化,包括问句规范化配置、加载许多配置属性等然后将aiml问答知识库以树的形式加载到内存,将其拆分成单个词,结构类似trie tree,在java源码中就是Graphmaster对象(节点和子节点来存储知识节点)

然后接受用户输入,将问句规范化,并将其拆成一个个单词

进而查询匹配标签

最后根据最佳匹配,完善模板(比如*填充,index填充等)返回答案

 

2.源代码解析

代码逻辑:

(1)用Chat类实现会话,工厂创造bot对象

(2)解析aiml,在aiml包下 AimlHandler类,里面包含对多种标签的解析,生成contextgraphmaster对象

解析主入口在aliceBotParser的parse方法。

主要通过AimlHandler中的startElement和endElement方法调用pushTextNode()方法,将解析的一个完整标签内容压入stack

然后构造context对象(包含了所有aiml文件的节点、配置和上下文信息,节点存在transformation属性中,配置信息存储在properties属性中),其中transformations对象主要存储的是语句分离器,问句规范化处理信息

同时构造了graphmaster对象,通过newGraphmaster方法把解析的<category>加入到graphmaster对象中

handler.unload方法从读取xml生成的stack里面弹出元素

aiml中的基本知识单元<category></category>主要存储在category对象中,如上图所示,解析aiml文件后,返回存储问答对的Category类型的list,然后作为graphmaster对象的属性

bot要装载context对象和graphmaster对象,这样,bot就带着aiml中的配置和知识去回答用户提问

(3)用户输入,调用bot对象的respond方法

先利用用户的input生成request对象,request对象是请求对象,属性为originalsentences,original为问句,然后将sentence规范化,赋给request对象

(4)进一步调用重载的respond方法,调用graphmaster的match方法匹配sentence到对应的category,调用process方法,返回<template>标签的内容:value.toString

再调用response.append方法,将response对象的original(答案)设置为category的template回复内容

(5)把response添加进context的response链表中,这样会话历史就被存储到context对象中

(6)返回respond的最终返回值:response.original.toString()

三、中文分词尝试

参考了https://blog.csdn.net/zhang_hui_cs/article/details/22686951这篇博客

aiml默认是不支持中文的,如原理中知识节点图所示,知识节点都是按空格切分的,所以aiml支持默认按空格分割的英文,这样才能匹配,而不支持中文,所以支持中文的关键就是将中文分词后加空格分割

因而需要修改的地方有三个:

(1)解析aiml文件时<pattern>和<that>标签内容中文分词空格分割

对应于源码解析中的解析aiml时利用putNextNode方法将标签压栈,修改中文分词即可,我采用了Hanlp,可个性化定制

/**
   * 中文分词重载, 分词后中间加空格
   * 然后应用到that和pattern标签
   * @param isToChineseSegment
   */
  private void pushTextNode(Boolean isToChineseSegment)
  {
    String pushed = text.toString();
    text.delete(0, text.length());
    if (ignoreWhitespace)
      pushed = pushed.replaceAll("^[\\s\n]+|[\\s\n]{2,}|\n", " ");

    if(!"".equals(pushed.trim())){
      if(!isToChineseSegment){
        stack.push(new Text(pushed));
      }
      else{
        String result = "";
        List<Term> list = HanLP.segment(pushed);
        for(Term term:list){
          result += term.word + " ";
        }
        stack.push(new Text(result.trim()));
      }
    }
  }

然后在调用它的startElement和endElement方法中修改

//pushTextNode();  //英文分词
  pushTextNode(qname.toLowerCase().equals("pattern") ||qname.toLowerCase().equals("that") || qname.toLowerCase().equals("srai"));   //中文分词

(2)读取用户question中文分词空格分割

修改Transformation类的normalization方法

public void normalization(Sentence sentence)
  {
    //String input = breakWords(sentence.getOriginal());
    
    //---对用户输入中文分词---
    String result = breakWords(sentence.getOriginal());
    String input = "";
    List<Term> list = HanLP.segment(result);
    for(Term term:list){
      input += term.word + " ";
    }
    //------------------------

    input = ' ' + input + ' ';
    input = input.replaceAll("\\s{2,}", " ");
    sentence.setOriginal(input);

    Mapper mapper = new Mapper(input);
    input = substitute(input, mapper);
    input = fit(input, mapper);

    sentence.setMappings(mapper.toArray());
    sentence.setNormalized(input);
  }

(3)规范化处理类Transformations中需要修改正则表达式匹配,使其能够匹配中文

 //private final Pattern fitting = Pattern.compile("[^A-Z0-9]+");  //正则表达式匹配英文+数字
   private final Pattern fitting = Pattern.compile("[^A-Z0-9\u4e00-\u9FA5]+");  //添加中文字符

经笔者简单测试,可以进行中文模糊匹配

<category><pattern>_行不行_</pattern><template>不可以!</template></category>
<category><pattern>*能不能*</pattern><template>能!</template></category>

四、注意

1. aiml元素属性是大小写敏感的,修改时,pattern元素中的英文都要大写

2. 自定义标签:只需继承TemplateElement类,核心方法是process方法,它的返回值直接作为答案返回给用户

然后在aiml文件中添加

<category><pattern>测试</pattern><template><b/></template></category>

其中B就是自定义的标签类

public class B extends TemplateElement
{
  /*
  Constructors
  */

  public B(Attributes attributes)
  {
  }

  public B(Object... children)
  {
    super(children);
  }

  /*
  Methods
  */

  public String process(Match match)
  {
    return "user-defined";
  }
}

这样输入“测试”,机器人就会回复“user-defined”

3. bot.respond()往往需要一个session参数,因为面对不同用户时,能够进行区分

五、优势与缺点(个人理解)

1.优势:

第一部分展示的demo例子其实完全可以用问答对构建,然后检索的方式解决,不需要如此复杂。

aiml的优势有以下几点:

(1)支持通配符,即模糊匹配

这就使得aiml可以利用一个category解决很多相同句式的问题;而且,<srai>标签解决了检所匹配问答对扩充时答案的冗余

(2)多种功能标签的辅助

比如<random>,增加了回复的多样性

<if><condition>判断条件

<topic>根据主题匹配

<system>learn</system>学习功能,学习用户输入的正确答案

(3)会话机制

基于aiml构建的chatbot支持会话机制,使得机器人具有一定程度的基于上下文语境的多轮对话功能,而且机器人能够区分不同的用户角色。

比如同样的问句“你好”,机器人对于不同人就会回复“你好,张三”、“你好、李四”

 

2.缺点

因为是基于规则匹配,缺点也很明显

(1)依赖规则的构建复杂程度,以及aiml覆盖知识的多少,规则构建完善、语料大效果自然好。

当针对特定领域的问答时,采用aiml方式构建个性化的标签和采用其他方式比如检索匹配,知识图谱,深度学习技术等等需要评估可行性和效果,可以采用aiml进行辅助,多模块整合

(2)人工的费时费力

  • 3
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值