NLP基础:检索式问答系统实战

本文介绍了检索式问答系统的实现,包括基于TF-IDF的文本表示、倒排表优化以及词向量的使用。通过建立倒排表和利用预处理后的TF-IDF向量,减少了计算复杂度。实验结果显示,优化后的系统能有效返回最匹配的答案。
摘要由CSDN通过智能技术生成

1. 目的与思路

检索式问答系统所需要的数据已经提供,对于每一个问题都可以找得到相应的答案,所以可以理解为每一个样本数据是 <问题、答案>pair。 那系统的核心是当用户输入一个问题的时候,首先要找到跟这个问题最相近的已经存储在库里的问题,然后直接返回相应的答案即可。

  1. 最简单的思路是:将用户的输入与问题库中每个问题进行比较,找到与输入最相似的问题,并将该问题对应的答案返回给用户即可。这里衡量相似度通过计算输入与问题表示之间的欧式距离、余弦相似度实现。
  2. 上述的思路简单,但是操作复杂度高,因为要计算输入与库中的每一个问题进行相似度计算。因此要考虑优化,引入倒排表,通过层层过滤,将可选的问题范围逐步缩小。比如,可以先筛选出与用户输入有1个公共字符的问题,甚至是2个、3个…条件越严格,那么候选的问题数量就越少,计算量大大减小。
  3. 计算输入与问题的相似度时,需要得到它们的向量表示。在这里,首先采用TF-IDF文本表示,进行计算;其次,采用已经训练好的glove.6B 100维词向量,通过average操作,得到句子的整体向量表示进行计算。

2. 简单思路的实现

将用户的输入与问题库中每个问题进行比较,找到与输入最相似的Top5问题,并将Top5问题对应的答案返回给用户即可。

2.1 问题-答案 库的读取

采用的数据集是机器阅读理解数据集(SQuAD 2.0),一共86821个问题-答案 pair。

#读取数据
# 分数(5)
import json
import matplotlib.pyplot as plt
import numpy as np
from nltk.stem.porter import PorterStemmer
from sklearn.feature_extraction.text import TfidfVectorizer

def read_corpus():
    """
    读取给定的语料库,并把问题列表和答案列表分别写入到 qlist, alist 里面。 在此过程中,不用对字符换做任何的处理(这部分需要在 Part 2.3里处理)
    qlist = ["问题1", “问题2”, “问题3” ....]
    alist = ["答案1", "答案2", "答案3" ....]
    务必要让每一个问题和答案对应起来(下标位置一致)
    """
    qlist = []
    alist = []
    with open("././data/train-v2.0.json") as f:
        all_data = json.load(f)['data']
        for data in all_data:
            paragraphs = data['paragraphs']
            for paragraph in paragraphs:
                for qa in paragraph['qas']:
                    # print(qa['id'])
                    if qa['answers']:
                        qlist.append(qa['question'])
                        alist.append(qa['answers'][0]['text'])
    assert len(qlist) == len(alist)  # 确保长度一样
    print("Load question and answer success. The length :{}".format(len(qlist)))
    return qlist, alist

qlist, alist = read_corpus()

运行结果:

Load question and answer success. The length :86821

2.2 对数据的相关统计

2.2.1 单词统计

# TODO: 统计一下在qlist 总共出现了多少个单词? 总共出现了多少个不同的单词?
#       这里需要做简单的分词,对于英文我们根据空格来分词即可,其他过滤暂不考虑(只需分词)
word_voc = []
for question in qlist:
    question = question.replace('?', ' ?')
    line = question.strip().split()
    word_voc += line
word_voc = set(word_voc)
word_total = len(word_voc)
print("Num of total words:{}".format(word_total))#51930

运行结果:

Num of total words:51930

2.2.2 单词频率统计

# TODO: 统计一下qlist中每个单词出现的频率,并把这些频率排一下序,然后画成plot. 比如总共出现了总共7个不同单词,而且每个单词出现的频率为 4, 5,10,2, 1, 1,1
#       把频率排序之后就可以得到(从大到小) 10, 5, 4, 2, 1, 1, 1. 然后把这7个数plot即可(从大到小)
#       需要使用matplotlib里的plot函数。y轴是词频
word_freq = {
   }#统计qlist的单词频率
for question in qlist:
    question = question.replace('?', ' ?')
    line = question.strip().split()
    for word in line:
        if word in word_freq:
            word_freq[word] += 1
        else:
            word_freq[word] = 1
sort_word_freq = sorted(word_freq.items(), key=lambda x: x[1], reverse=True)
all_word = []
word_count = []
for word, count in sort_word_freq:
    all_word.append(word)
    word_count.append(count)
# scale_ls = range(len(all_word))
# plt.plot(scale_ls, word_count)
# plt.xticks(scale_ls, all_word)
plt.bar(range(20), word_count[:20], color='rgb', tick_label=all_word[:20])#画出前20个词
plt.show()

运行结果:
在这里插入图片描述
从上面的图中可以看, 这样的一个图的走势跟反比例函数形状很类似,也就是学术界的 Zipf’s law:第二常见的频率是最常见频率的出现次数的½,第三常见的频率是最

评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值