学了很多什么分词,维特比,ui-gram之类的,但是能用起来才算真的学懂
三天的时间做完了这个项目 结果和想象不能说是完全一致 只能说是毫无关系
总结一下项目经验就是:
试试做个子数据集,要不然需要跑很久还不知道哪里错了
一步一步来想清楚步骤,一定是可以做出来的
好的我们开始复盘!
2.1第一部分: 读取文件,并把内容分别写到两个list里
import json
def read_corpus():
"""
读取给定的语料库,并把问题列表和答案列表分别写入到 qlist, alist 里面。 在此过程中,不用对字符换做任何的处理(这部分需要在 Part 2.3里处理)
qlist = ["问题1", “问题2”, “问题3” ....]
alist = ["答案1", "答案2", "答案3" ....]
务必要让每一个问题和答案对应起来(下标位置一致)
"""
file="./data/train-v2.0.json"
qlist=[]
alist=[]
k=0
with open(file) as f:
jsf=json.load(f)
qadict=jsf['data']
for i in qadict:
qadict_para=i['paragraphs']
for j in qadict_para:
qadict_qas=j['qas']
for m in qadict_qas:
qlist.append(m['question'])
k+=1
if m['answers']==[]:
alist.append([])
else:
alist.append(m['answers'][0]['text'])
assert len(qlist) == len(alist) # 确保长度一样
return qlist, alist
qlist, alist=read_corpus()
2.2 理解数据(可视化分析/统计信息)
用到的是Counter计数器辅助包,可以把它当字典用,也顺带复习了对元组的操作
from collections import Counter
def count_word(list1):
word_list=[]
c=Counter()
for q in list1:
try:#这边主要是alist那边有很多的[]空列表,不能执行split
c.update(q.split())
except:
pass
word_total=sum(c.values())#出现的单词数
word_diff_total=len(c.keys())#不同的单词数
return word_total,word_diff_total,c
import matplotlib.pyplot as plt
import numpy as np
word_total,word_diff_total,q_c=count_word(qlist)
a_word_total,a_word_diff_total,a_c=count_word(alist)
#拿到counter的value频率
list2=sorted(q_c.values(),reverse=True)
print(list2[:10])
plt.plot(list2[:100])
#找出最高频的前10个 most_common返回的是元组列表
#用了列表解析式q[0]返回的是元组的第一个元素
q_top10=q_c.most_common(10)
q_top10=[q[0] for q in q_top10]
print(q_top10)
2.3 文本预处理
在这步要学习如何去除标点,string.punctuation记录了所有的标点
import string
#1.设置停用词
from nltk.corpus import stopwords
stop_words = set(stopwords.words('english'))
low_frequency_words=[]
#4.建立低频词词典 上一步词频统计用处只有建立高频低频词典
for key,value in q_c.items():
if value<2:
low_frequency_words.append(key)
#6.stemming
import nltk
porter = nltk.PorterStemmer()
# lancaster = nltk.LancasterStemmer() 都用的话会太慢
#辅助分词
from nltk.tokenize import word_tokenize
def text_preprocessing(input_list):
new_list = []
for sentence in input_list:
l_list = '' # 保存新的句子
if sentence==[]:
continue
l=nltk.word_tokenize(sentence)
for word in l:
# 转换小写+stemming
word = porter.stem(word)
# word = lancaster.stem(word)
# 3.去除所有标点符号
word = ''.join(c for c in word if c not in string.punctuation)
# punc = '~`!#$%^&*()_+-=|\';":/.,?><~·!@#¥%……&*()——+-=“:’;、。,?》《{}'
# word=re.sub(r"[%s]+" %punc, "",word)
# 5.处理数字
if word.isdigit():
word = word.replace(word,'#number') ##也可以写为 re.sub(r'\d', "#number", l[i])
if word not in stop_words and word not in low_frequency_words:
l_list += word + ' '
new_list.append(l_list)
return new_list
qlist2 = text_preprocessing(qlist) # 更新后的
2.4文本表示(文本转向量)
这步有三种tf-idf包
# TODO: 把qlist中的每一个问题字符串转换成tf-idf向量, 转换之后的结果存储在X矩阵里。 X的大小是: N* D的矩阵。 这里N是问题的个数(样本个数),
# D是字典库的大小。
from sklearn.feature_extraction.text import CountVectorizer, TfidfVectorizer, TfidfTransformer
tfidf_vec = TfidfVectorizer()
q_tfidf_matrix = tfidf_vec.fit_transform(qlist2)
q_tfidf_array=q_tfidf_matrix.toarray()
2.5 对于用户的输入问题,找到相似度最高的TOP5问题,并把5个潜在的答案做返回
def get_cos_similar_multi(v1: list, v2: list):
num = np.dot([v1], np.array(v2).T) # 向量点乘
denom = np.linalg.norm(v1) * np.linalg.norm(v2, axis=1) # 求模长的乘积
res = num / denom
res[np.isneginf(res)] = 0
return 0.5 + 0.5 * res
def top5results(input_q):
"""
给定用户输入的问题 input_q, 返回最有可能的TOP 5问题。这里面需要做到以下几点:
1. 对于用户的输入 input_q 首先做一系列的预处理,然后再转换成tf-idf向量(利用上面的vectorizer)
2. 计算跟每个库里的问题之间的相似度
3. 找出相似度最高的top5问题的答案
"""
# token=input_q.split()
# print(token)
tfidf_matrix = tfidf_vec.fit(qlist2).transform(input_q)
tfidf_array=tfidf_matrix.toarray()
res=get_cos_similar_multi(tfidf_array, q_tfidf_array)
top_idxs=[x[0] for x in sorted(enumerate(res[0][0]), key=lambda x: x[1])[-5:]]
return alist[top_idxs]
2.6利用倒排表的优化
# TODO: 基于倒排表的优化。在这里,我们可以定义一个类似于hash_map, 比如 inverted_index = {}, 然后存放包含每一个关键词的文档出现在了什么位置,
# 也就是,通过关键词的搜索首先来判断包含这些关键词的文档(比如出现至少一个),然后对于candidates问题做相似度比较。
#
inverted_idx = {} # 定一个一个简单的倒排表
high_frequency_words={}
def find_hf_words(dic):
for key,value in dic.items():
if value>10 and value<150:
high_frequency_words[key]=[]
return high_frequency_words
high_frequency_words=find_hf_words(q_c)
def create_rstable(qlist):
for idx,q in enumerate(qlist):
for word in q:
if word in high_frequency_words:
high_frequency_words[word].append(idx)
return high_frequency_words
high_frequency_words = create_rstable(qlist)
def find_match(input_q):
match_q=[]
token=input_q.split()
for t in token:
if t in high_frequency_words:
match_q+=high_frequency_words[t]
return set(match_q)
match_q=find_match("Do you know I love you so much ?")
print(match_q)
def top5results_invidx(input_q):
"""
给定用户输入的问题 input_q, 返回最有可能的TOP 5问题。这里面需要做到以下几点:
1. 利用倒排表来筛选 candidate
2. 对于用户的输入 input_q 首先做一系列的预处理,然后再转换成tf-idf向量(利用上面的vectorizer)
3. 计算跟每个库里的问题之间的相似度
4. 找出相似度最高的top5问题的答案
"""
# token=input_q.split()
# print(token)
tfidf_matrix = tfidf_vec.fit(qlist2).transform(input_q)
tfidf_array=tfidf_matrix.toarray()
res=get_cos_similar_multi(tfidf_array, q_tfidf_array[list(match_q)])
return res
2.7 基于词向量的文本表示
import numpy as np
with open('./data/glove.6B/glove.6B.100d.txt', 'r') as file1:
emb = []
vocab = []
for line in file1.readlines():
row = line.strip().replace("?",'').split(' ')
vocab.append(row[0])#词表
emb.append(row[1:])#词嵌入表
emb =np.asarray(emb)
#句子嵌入
import re
def sentence_embedding(sentence):
glove_sentence=[0]*100
for word in sentence.split():
word=word.lower()
punc = '~`!#$%^&*()_+-=|\';":/.,?><~·!@#¥%……&*()——+-=“:’;、。,?》《{}'
word=re.sub(r"[%s]+" %punc, "",word)
if word in vocab:
glove_sentence+=emb[vocab.index(word)].astype(float)
glove_sentence=glove_sentence/len(sentence.split())
return glove_sentence
sentence_embedding("Do you know I love you so much ?")
#做qlist的嵌入表
def list_embedding(list1):
glove_matrix = []
for l in list1:
glove_matrix.append(sentence_embedding(l))
return glove_matrix
q_emb = list_embedding(qlist)
q_emb=np.array(q_emb)
def top5results_emb(input_q):
"""
给定用户输入的问题 input_q, 返回最有可能的TOP 5问题。这里面需要做到以下几点:
1. 利用倒排表来筛选 candidate
2. 对于用户的输入 input_q,转换成句子向量
3. 计算跟每个库里的问题之间的相似度
4. 找出相似度最高的top5问题的答案
"""
input_glove=sentence_embedding(input_q)
match_q=find_match(input_q)
res=get_cos_similar_multi(input_glove, q_emb[list(match_q)])
return res
res=top5results_emb("Do you know I love you so much ?")
# print (top5results_emb(""))
top_idxs=[x[0] for x in sorted(enumerate(res[0]), key=lambda x: x[1])[-5:]]
print(top_idxs)