zyl311620
NLP菜鸡
采纳率0%
2020-06-20 17:08

一个简单问答系统遇到的问题

  • 自然语言处理

只用tf-idf的时候输出的结果基本正确,用了倒排表优化后输出的答案就完全不正确了。不知道哪里出问题了,求大神解答

# 分数(5)
import json
from collections import Counter
import matplotlib.pyplot as plt
from nltk.stem import PorterStemmer
from nltk.tokenize import word_tokenize
from sklearn.feature_extraction.text import TfidfVectorizer
import numpy as np
from sklearn.metrics.pairwise import cosine_similarity
import math
from collections import defaultdict
def read_corpus(filepath):
    """
    读取给定的语料库,并把问题列表和答案列表分别写入到 qlist, alist 里面。 在此过程中,不用对字符换做任何的处理(这部分需要在 Part 2.3里处理)
    qlist = ["问题1", “问题2”, “问题3” ....]
    alist = ["答案1", "答案2", "答案3" ....]
    务必要让每一个问题和答案对应起来(下标位置一致)
    """
    qlist = []
    alist = []
    with open(filepath) as f:
        file_array = json.load(f)['data']
        for file in file_array:
            paragraph = file['paragraphs']
            for paragraphs in paragraph:
                paragraph = paragraphs['qas']
                for qa in paragraph:
                    qlist.append(qa['question'])
                    try:
                        alist.append(qa['answers'][0]['text'])
                    except IndexError:
                        qlist.pop()
    assert len(qlist) == len(alist)  # 确保长度一样
    return qlist, alist



# TODO: 对于qlist, alist做文本预处理操作。 可以考虑以下几种操作:
#       1. 停用词过滤 (去网上搜一下 "english stop words list",会出现很多包含停用词库的网页,或者直接使用NLTK自带的)
#       2. 转换成lower_case: 这是一个基本的操作
#       3. 去掉一些无用的符号: 比如连续的感叹号!!!, 或者一些奇怪的单词。
#       4. 去掉出现频率很低的词:比如出现次数少于10,20....
#       5. 对于数字的处理: 分词完只有有些单词可能就是数字比如44,415,把所有这些数字都看成是一个单词,这个新的单词我们可以定义为 "#number"
#       6. stemming(利用porter stemming): 因为是英文,所以stemming也是可以做的工作
#       7. 其他(如果有的话)
#       请注意,不一定要按照上面的顺序来处理,具体处理的顺序思考一下,然后选择一个合理的顺序
#  hint: 停用词用什么数据结构来存储? 不一样的数据结构会带来完全不一样的效率!
def text_preprocessing(text):
    # 生成停用词和标准化
    stopfile_path = r'C:\Users\Administrator\nltk_data\corpora\stopwords\baidu_stopwords.txt'
    with open(stopfile_path, 'r', encoding='UTF-8') as f:
        sw = set(f.read())
    sw -= {'when', 'who', 'why', 'what', 'how', 'where', 'which'}
    ps = PorterStemmer()

    seg = list()
    #用nltk分词
    for word in word_tokenize(text):
        #小写化,次干提取
        word = ps.stem(word.lower())
        #数值归一
        word = '#number' if word.isdigit() else word
        #去停用词
        if len(word)>1 and word not in sw:
            seg.append(word)
    return seg
def qlist_preprocessing(qlist):
    word_cnt = Counter()
    qlist_seg = list()
    for text in qlist:
        seg = text_preprocessing(text)
        qlist_seg.append(seg)
        word_cnt.update(seg)

    value_sort = sorted(word_cnt.values(),reverse=True)
    min_tf = value_sort[int(math.exp(0.99*math.log(len(word_cnt))))]
    for cur in range(len(qlist_seg)):
        qlist_seg[cur] = [word for word in qlist_seg[cur] if word_cnt[word] > min_tf]
    return qlist_seg

# 分数(10)

def top5results_invidx(input_q):
    """
    给定用户输入的问题 input_q, 返回最有可能的TOP 5问题。这里面需要做到以下几点:
    1. 对于用户的输入 input_q 首先做一系列的预处理,然后再转换成tf-idf向量(利用上面的vectorizer)
    2. 计算跟每个库里的问题之间的相似度
    3. 找出相似度最高的top5问题的答案
    """
    qlist, alist = read_corpus(r'C:\Users\Administrator\Desktop\train-v2.0.json')
    alist = np.array(alist)
    # 分数(10)
    # TODO: 把qlist中的每一个问题字符串转换成tf-idf向量, 转换之后的结果存储在X矩阵里。 X的大小是: N* D的矩阵。 这里N是问题的个数(样本个数),
    #       D是字典库的大小。
    vectorizer = TfidfVectorizer()  # 定义一个tf-idf的vectorizer
    qlist_seg = qlist_preprocessing(qlist)  #对qlist进行处理
    seg = text_preprocessing(input_q)   #对输入的问题进行处理
    X = vectorizer.fit_transform([' '.join(se) for se in qlist_seg])  # 结果存放在X矩阵


    #定义一个简单的倒排表
    inversed_idx = defaultdict(set)
    for cur in range(len(qlist_seg)):
        for word in qlist_seg[cur]:
            inversed_idx[word].add(cur)
    candiates = set()
    for word in seg:
        #取所有包含任意一词的文档的并集
        candiates = candiates | inversed_idx[word]
    candiates = list(candiates)

    input_vec = vectorizer.transform([' '.join(seg)])   #对输入的问题进行分词预处理和转换成tf-idf向量
    sim = cosine_similarity(input_vec,X[candiates])[0]     #计算余弦相似度
    top_idx = np.argsort(sim)[-5:].tolist()     #找出相似度最高的top5下标
    top_idx.reverse()
    print([x for x in sim[top_idx]])
    return [alist[i] for i in top_idx] # 返回相似度最高的问题对应的答案,作为TOP5答案
print(top5results_invidx("What government blocked aid after Cyclone Nargis?"))
  • 点赞
  • 写回答
  • 关注问题
  • 收藏
  • 复制链接分享
  • 邀请回答

1条回答

相关推荐