让python aiml库全面支持中文(手把手修改源代码,debug,安装。附修改后代码)

python aiml库的中文支持问题


更新:
aiml库fork的是python-aiml库。于是我也去fork了一个python-aiml,然后添加上中文支持啦!
github地址
下载后如果要安装成library看最后哦:
不安装library,直接使用源码/Debug
安装修改后的源代码为library
注意:不管是安装的是aiml还是python-aiml,或者是我们改过的源代码,最后使用的时候都是import aiml


python aiml库对中文的支持不是很好,总会出现奇怪的问题。比如在打印kernel历史的时候,会发现句子中莫名其妙的多出了许多空格,导致无法成功匹配。这篇文章分享了我的解决办法。

阅读源码后,我们发现问题出在将句子分词和重组的逻辑。因为这个库主要是给英文使用的。我们知道,英文的单词是使用空格将一句话分为一个个单词的,而这也是aiml库对全英文句子分词的方法。

源代码的具体逻辑是:

在kernel学习.aiml文件(创建知识库)时会检查每一个<pattern>标签的text内容是否包含英文,如果包含英文就将text转为大写。

如果text不含英文怎么办呢?aiml库简单粗暴地用' '.join()在每个字符之间都插入一个空格。

最后,这些被处理过的句子将在匹配的时候直接被split()分成一个单词list。(也就是说,如果我们想成功匹配含有非英文的句子,就要在所有相邻字符间都加一个空格。要多难受有多难受)

我们来看几个例子:

"what are you":字符串只包含英文,将全部字符大写。结果为:"WHAT ARE YOU"。 结果符合期望。

"你好":字符串不包含英文,字符间插入空格。结果为:"你 好"。结果符合期望。

"你好hello":字符串包含英文,将所有字符大写。结果为:"你好HELLO"。结果符合期望。

  • 这一整个字符串将只作为一个单词进行匹配,这显然是有问题的。如果我们的pattern是"* hello",则无法成功匹配。

  • 我们希望得到的是三个单词:“你”,“好”,“hello”。所以结果应该是"你 好 HELLO"。这个结果在调用split()之后才会被分成三个部分。

相似的,在获得机器人回复之前,源代码也会将输入的内容做一次这样的处理。

在匹配通配符时也会出现类似的问题,通配符匹配的内容如果不含英文,则会在字符间插入空格。等我们想获取通配符的匹配内容时,就会发现里面全是不需要的空格。

另外,python aiml库对中文标点也同样不支持。

综上所述,我们需要对源代码进行一些修改。

修改源代码


版本信息:

python 3.7.1

aiml 0.9.2


修改后的两个文件已上传。

下面的路径为相对于aiml源代码根目录的相对路径
我们修改了源代码中的:

  • aiml/Kernel.py
  • aiml/PatternMgr.py

尝试解决的具体问题是:

  • 中英文混用时的分词预处理不合理
  • 中英文混用时,通配符内容在respond中的每一个字符间都有空格
  • 不支持中文标点

分词预处理

aiml/Kernel.py中添加新的分词预处理函数_split_cn_eng_sentence,来代替简单的' '.join()

def _split_cn_eng_sentence(self, sentence):
    # 将中文单字和英文单词用空格隔开 
    # 连续英文字母和连续的数字算作一个单词
    # 特殊字符如"*"也算作一个单词
    # 例:
    # >>> _split_cn_eng_sentence('*你*好* * *hel*lo')
    # * 你 * 好 * * * hel * lo
    # >>> _split_cn_eng_sentence('*你*,?!好* ,:* *12*345')
    # * 你 * 好 * * * 12 * 345
    special_chars = r'\*'
    regEx = re.compile(r'[^'+special_chars+r'\w]+') #非 连续单字/数字/特殊字符
    chinese = re.compile(r'(['+special_chars+r'\u4e00-\u9fa5])') #中文字符,加()来保留分隔符
    sub_sentences = regEx.split(sentence) #获得连续单字/数字/特殊字符
    res_list = []
    for s in sub_sentences:
        res_list += chinese.split(s) #将中文字符作为分隔符,但保留它们,就可以把每个汉字分开了
    return ' '.join(r for r in res_list if r) #去掉值为''的部分

修改aiml/Kernel.py中的learn函数:

if key and key[0] and key[1] and key[2] and em_ext == '.aiml' and (not self._check_contain_english(key[0])): 
    new_key = (' '.join(key[0]), key[1], key[2]) 
elif key and key[0] and key[1] and key[2] and em_ext == '.aiml' and self._check_contain_english(key[0]): 
    new_key=(key[0].upper(), key[1], key[2])

改为

if key and key[0] and key[1] and key[2] and em_ext == '.aiml':
    new_key = (self._split_cn_eng_sentence(key[0]).upper(),
            key[1], key[2])

aiml/Kernel.py中的respond函数,注释这两行:

if not self._check_contain_english(s): 
    s = ' '.join(s) 

aiml/Kernel.py中的_respond函数开头使用_split_cn_eng_sentence处理输入字符串:

def _respond(self, input_, sessionID):
    """Private version of respond(), does the real work."""
    if len(input_) == 0:
        return u""
    input_ = self._split_cn_eng_sentence(input_) #添加这一行

中文标点

aiml/PatternMgr.py:在_puncStripRE变量中添加中文标点:

punctuation = "\"`~!@#$%^&*()-_=+[{]}\\|;:',<.>/?" 
chinese_punctuation = "、“”;:,《。》?【】·!¥…()—" #添加这一行
self._puncStripRE = re.compile("[" + re.escape(punctuation+chinese_punctuation) + "]") # re.escape(punctuation) 改为 re.escape(punctuation+chinese_punctuation)

通配符内容合并

我们还需要在处理通配符匹配到的内容时,将list中的单词合理地合并成句子。源代码中同样中是直接使用' '.join()

aiml/PatternMgr.py中添加新的helper函数_combine_cn_eng_sentence

def _combine_cn_eng_sentence(self, word_list):
    #MODIFIED
    # 将split()后的中文单字和英文单词合并为字符串空格隔开 
    # 中文和中文用''连接,英文和英文用' '连接,中文和英文用''连接
    # 例:
    # >>> print(_combine_cn_eng_sentence(['hi', '你', '觉', '得', 'what', '电', '影', '好', '看']))
    # hi你觉得what电影好看
    def is_chinese_word(word):
        # 仅用于Unicode编码
        # 用于被_split_cn_eng_sentence(string)+string.split()处理后的单词列表
        if len(word) != 1: 
            return False # 如果单词长度大于1,则不是中文
        return u'\u4e00' <= word and word <= u'\u9fa5' #如果不在中文编码范围内,则不是中文
    res = ''
    is_prev_word_chinese = True # 设为True,开头不加空格
    for w in word_list:
        is_chinese = is_chinese_word(w)
        if is_chinese:
            res += w
        else:
            if is_prev_word_chinese:
                res += w
            else:
                res += ' '+w
        is_prev_word_chinese = is_chinese
    return res  

源代码中aiml/PatternMgr.pystar函数底部,

    if starType == 'star': return ' '.join(pattern.split()[start:end+1])
    elif starType == 'thatstar': return ' '.join(that.split()[start:end+1])
    elif starType == 'topicstar': return ' '.join(topic.split()[start:end+1])

改为

    if starType == 'star': return self._combine_cn_eng_sentence(pattern.split()[start:end+1]) #MODIFIED
    elif starType == 'thatstar': return self._combine_cn_eng_sentence(that.split()[start:end+1]) #MODIFIED
    elif starType == 'topicstar': return self._combine_cn_eng_sentence(topic.split()[start:end+1]) #MODIFIED
            

Debug

如果想debug,可以在根目录下创建一个空的`__init__.py`文件,然后像import一个普通的.py文件一样导入就可以了。比如这样一个目录结构: ``` aaa |--------aiml_0_9_2 |--------test.py ``` 源代码根目录是`aiml_0_9_2`(注意文件夹的名字不要包含`.`)。在test.py里面就可以这样导入 ``` from aiml_0_9_2.aiml import Kernel ```

安装修改后的源代码

在源代码根目录中运行

python setup.py install

然后就可以当做正常的python库import啦:

import aiml

aiml库的其他问题

aiml库不完全支持最新的AIML语言。目前仅支持*作为通配符,_^#均不支持

  • _*用法相同,表示一个或多个词 (one+ wildcards)
  • ^#用法相同,表示零或多个词 (zero+ wildcards)

python aiml库一些细节上值得注意的地方:

  • 标点符号不参与匹配

写在最后

码字不易,觉得有帮助就给我点个赞吧!我会继续努力的!

评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值