python构建基于知识图谱的京东商城问答系统-用户问题预处理

这篇主要介绍对用户问题的处理,也就是从获取用户问题到明白用户意图这个过程,主要涉及到命名实体识别(这个任务简单,我就用词性标注来代替了),问题分类,以及填充问题模板这几个部分。介绍的时候,可能会用一些代码来说明,但是下面列出来的代码并不完整,完整的代码请参照github。这些代码只是辅助理解整个过程,这样去看代码的时候才容易理清函数之间的来龙去脉。
 

关键信息抽取

这部分就是通过命名实体识别来获取用户问题中的电脑名称,而在实验过程中,在这一部分我们其实用的词性标注,因为词性标注工具可以把电脑名称标注出来,如下使用jieba进行词性标注时的含义:

nm 评论

所以,只要我们把问题词性标注后,找到对应的nr对应的单词,那就是电脑名称了。这就是这部分的主要思路,接下来就看下代码是怎么实现的。

  • 用户问题词性标注

功能的实现就是下面这个函数:

    def question_posseg(self):
        jieba.load_userdict("./data/userdict3.txt")
        clean_question = re.sub("[\s+\.\!\/_,$%^*(+\"\')]+|[+——()?【】“”!,。?、~@#¥%……&*()]+","",self.raw_question)
        self.clean_question=clean_question
        question_seged=jieba.posseg.cut(str(clean_question))
        result=[]
        question_word, question_flag = [], []
        for w in question_seged:
            temp_word=f"{w.word}/{w.flag}"
            result.append(temp_word)
            # 预处理问题
            word, flag = w.word,w.flag
            question_word.append(str(word).strip())
            question_flag.append(str(flag).strip())
        assert len(question_flag) == len(question_word)
        self.question_word = question_word
        self.question_flag = question_flag
        print(result)
        return result

jieba的词性标注和分词是同步进行的,所以如果分词不准确的话,那么词性标注往往也会出错,比如说电脑拯救者,被jieba分词分为:

拯  救  者

这样肯定就不能识别出拯救者这个实体了啊,于是我就加了一个自定义字典,把所涉及到的所有电脑名称都加到了这个字典中,这样以来,jieba分词的时候就会参考字典里面的词来进行分词和词性标注了,自定义词典的格式如下:

联想 小新14 15 nm
联想Yoga Pro14s 15 nm
联想小新Air14 15 nm
联想小新Pro14 15 nm
联想Yoga Pro 15 nm
联想小新Pro16 15 nm
联想拯救者Y9000P 15 nm
联想Yoga Pro 15 nm
联想Yoga 15 nm
联想拯救者R9000X 15 nm
联想Yoga 15 nm
联想拯救者Y9000X 15 nm
联想拯救者Y9000P 15 nm
联想Yoga Pro14s 15 nm
联想拯救者Y9000X 15 nm
拯救者 15 nm
小新 15 nm
联想拯救者Y7000P nm
联想小新Pro 13 15 nm
联想拯救者Y7000 15 nm
联想Yoga Pro14s 15 nm
联想小新Air14Plus 15 nm
联想Yoga 13s nm
小新 15 nm
联想拯救者Y7000P

这样涉及到的电脑名称就不会被jieba分词分开了,此外还需要去掉问题中的特殊字符,这些操作搞完了后就可以进行分词和词性标注了,处理后把结果返回即可。

  • 问题分类和模板填充

接下来是对用户的问题进行分类,获取对应问题模板,从而明白用户的意图,首先要使用前面介绍的根据用户习惯构造的各种各样的问题来训练一个分类器,我这里使用的sklearn里面的贝叶斯分类器。
首先是组织训练数据:

    def read_train_data(self):
        train_x=[]
        train_y=[]
        file_list=getfilelist("./data/question/")
        # 遍历所有文件
        for one_file in file_list:
            # 获取文件名中的数字
            num = re.sub(r'\D', "", one_file)
            # 如果该文件名有数字,则读取该文件
            if str(num).strip()!="":
                # 设置当前文件下的数据标签
                label_num=int(num)
                # 读取文件内容
                with(open(one_file,"r",encoding="utf-8")) as fr:
                    data_list=fr.readlines()
                    for one_line in data_list:
                        word_list=list(jieba.cut(str(one_line).strip()))
                        # 将这一行加入结果集
                        train_x.append(" ".join(word_list))
                        train_y.append(label_num)
        return train_x,train_y

接着是训练多分类贝叶斯分类器模型,并返回:

    # 训练并测试模型-NB
    def train_model_NB(self):
        X_train, y_train = self.train_x, self.train_y
        self.tv = TfidfVectorizer()

        train_data = self.tv.fit_transform(X_train).toarray()
        clf = MultinomialNB(alpha=0.01)
        clf.fit(train_data, y_train)
        return clf

利用训练好的模型来对新问题进行分类:

    # 预测
    def predict(self,question):
        question=[" ".join(list(jieba.cut(question)))]
        test_data=self.tv.transform(question).toarray()
        y_predict = self.model.predict(test_data)[0]
        print("question type:",y_predict)
        return y_predict

返回用户问题所属的类别编号,这个编号也就对应一个问题模板:

0:nm 评论
1:nm 价格
2:nm 类型
3:nm 简介
4:nm 配置
5:nnt 介绍
6:nnt 电脑重量
7:nnt 电脑评论数 大于 x
8:nnt 电脑好评度 大于 x
9:nnt 颜色
10:nnt 处理器
11:nnt 显卡
12:nnt 包邮
13:nnt 有货
14:nnt 快递
15:nnt 屏幕刷新率
16:nnt 固态
17:nnt 产地
18:nnt 厚度
19:nnt 屏幕色域

数字和模板的对应关系可以提前存到一个字典中,当预测出编号后,直接通过这个编号作为字典的key值,这样就查询出问题模板了。比如预测的结果是2,则对应的问题模板 nm 类型,表示询问某个电脑的类型,再结合前一阶段获取的电脑名字,则可以组成一个新的问题,比如:

拯救者  类型

上面对涉及到的重点部分进行了介绍,这里小结一下,当遇到一个新问题时:

1、得到原始问题;
2、抽取问题中的关键信息,比如从问题“拯救者Y9000P介绍”中抽取出拯救者Y9000P这个电脑名称;
3、使用分类模型来预测问题类别,预测出模板编号,查询字典得到问题模板;如dict[5]:nnt 介绍;
4、替换模板中的抽象内容,得到:拯救者Y9000P 介绍。

猜你喜欢

转载自blog.csdn.net/weixin_45897172/article/details/130998530
今日推荐