智能阅读模型的构建(第六届泰迪杯C题)

项目描述:

构建智能阅读模型主要通过两个方法来实现,第一个是TF-IDF的变种——TFC-ICF,TFC-ICF较于TF-IDF是将一个问题的所有答案看成一个整体,类比于TF-IDF文本分类的文件夹,问题的每一个回答类比于TF-IDF的文件夹里的每个文档。TFC是每个词所在句子的频率,叫类词频,ICF类比于逆文本率,叫倒类频率。作为衡量每个词在所有句子中的一个重要程度。利用sklearn里面的tfidfvectorizer先提取一个问题所对应的所有回答的特征词向量,用这个特征词向量,去和所有备选答案进行匹配和计算,最后得到一个TF-IDF值的矩阵,这个矩阵的行数是问题对应的备选答案的个数,列数就是特征词向量里特征词的个数。当对答案进行0、1判别时,先把问题分词,将这些词与每一句备选答案进行匹配,并把匹配到的词的TFC-ICF的权值提取出来。一个回答里匹配到几个词就提取几个权值,再把这些权值相加。这个相加的值作为该回答的得分。在下一步,设置阈值为0.31,哪句答案的得分大于等于0.31,这句答案的label判别为1,反之就判别为0。

第二个方法,首先构建了一个问题分类的特征词表,把问题分为地点类,人名类,时间类、未知类。利用pyltp库,对每个问题对应的所有答案,进行分词、词性标注、命名实体识别、句法依存分析、语义角色标注。流程就是:先将问题分词之后,用前三种问题的特征词向量判断问题属于这三种的哪一种,没有匹配到就属于第四种未知类,未知类的结果用第一种方法得到的结果来补充判断。问题分类之后,如果判断为地点类,就把答案的命名实体标注向量用re.match(‘S-Ns$’,word)判断是否有地点的命名实体,有判断label为1,无判断label为0;如果问题为人名类,把答案的命名实体向量用re.match(‘S-Nh$’,word)判断是否有人名的命名实体,有判断label为1,无判断label为0;如果判断问题为时间类,就把答案的语义标注向量用re.match(‘TMP$’,word)判断是否含有时间语义的词语,有判断label为1,无就判断label为0。

最后将上述两种方法分别得到的每个passage_id对应的label的值相加(这里选取第一种方法得到的label结果是通过设置阈值0.31进行判别后的, 其label值是0,1标记的),对每两个相加得到的值判断,大于等于1的值end_label标位1,小于1的值end_label标为0。


这个比赛因为组队的两个小伙伴临时有事(准备托福雅思啥的)前期没有参与进来,所以我 可以很自信得说:这比赛除了最后的文档编写从构思到代码实现全是我这个组长完成的。最后获得了一个三等奖,虽然有很多不甘心,因为它是按照参赛人数的比例来排序评奖,c题参赛的人数相对来说最少。

 


排序模型的代码:

import json
import jieba
import pandas as pd
import numpy as np
import os
import codecs
import sys
import re
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import confusion_matrix#导入计算混淆矩阵的库
from imp import reload 
reload(sys)
from tqdm import tqdm
from sklearn.feature_extraction.text import TfidfVectorizer


s = codecs.open('longwang.txt','r',encoding = 'utf-16')


def clean(text):
    #text = re.sub('[0-9]{2,}','',text)#消除两位以上的数字
    #text= re.sub('(?:(?:\d+,?)+(?:\.?\d+)?)','',text)#消除数字
    text = text.replace(' ','')#消除空格
    #text = re.sub('http[s]?://(?:[a-z]|[0-9]|[$-_@.&+]|[!*\(\),]|(?:%[0-9a-f][0-9a-f]))+','',text)#消除网页链接
    text = re.sub('[a-zA-Z]','',text)#消除英文字母
    words  = list(jieba.lcut(text))
    stopwords = [line.rstrip() for line in open('stopwords.txt','r',encoding="utf-8")]
    words_nostop = [w for w in words if w not in stopwords]
    return ' '.join(words_nostop)



Sents = [line.rstrip('\r\n') for line in s]#/r/n 回车加换行 


n = 2#设置两行为一句话
raw_Sents = ['\r\n'.join(Sents[i:i+n]) for i in range(len(Sents)-n+1)]


Sents = raw_Sents


Sents = [clean(i) for i in tqdm(iter(Sents))]#对每一句话进行清理

from sklearn.feature_extraction.text import TfidfTransformer#导入计算tfidf的库
from sklearn.feature_extraction.text import CountVectorizer#导入计算词频矩阵的库

corpu = Sents
vectorizer=CountVectorizer()#该类会将文本中的词语转换为词频矩阵,矩阵元素a[i][j] 表示j词在i类文本下的词频
transformer=TfidfTransformer()#该类会统计每个词语的tf-idf权值
tfidf=transformer.fit_transform(vectorizer.fit_transform(corpu))#第一个fit_transform是计算tf-idf,第二个fit_transform是将文本转为词频矩阵
word=vectorizer.get_feature_names()#获取词袋模型中的所有词语
weight=tfidf.toarray()#将tf-idf矩阵抽取出来,元素a[i][j]表示j词在i类文本中的tf-idf权重

from collections import Counter

def search(s):
    result = {}
    answer = []
    score = []
    _ = jieba.lcut(s)
    for q in _:
        if q in word:
            for i in range(len(weight)):
                for j in range(len(word)):
                    if q == word[j]:
                        result[i] = result.get(i,0) + weight[i][j]
    result = Counter(result).most_common(10)
    for n in range(len(result)):
        answer.append(raw_Sents[result[n][0]])
        score.append(result[n][1])
    c = {
        "answer" : answer,
        "score"  : score}
    tf = pd.DataFrame(c)
    return(tf)



print (search(u'神级机甲有多强大'))

'''
#输出结果:
                                              answer     score
0           神级机甲师。在下不才,是传灵学院机甲学院副院长、神级机甲师蒙特,特来请\r\n教  1.042843
1  神级机甲,那可是神级机甲啊,主动上门挑战,丢了一台神级机甲不说,还\r\n让人家一个人就给击退了。  1.026891
2  机甲被对方缴获了,对方明显没有要把神级机甲还回来的意思。\r\n正在两台神级机甲内的机甲师不...  1.021547
3           但神级机甲对机甲师的保护是非常好的,他并没有受伤,然后机甲就被人打\r\n开了。  1.020787
4              \r\n红色机甲是什么?那可是神级机甲啊,机甲世界中的顶级机甲,号称能够以  0.988498
5                                 \r\n地朝着两台神级机甲打了过去。  0.987706
6  什么?”千古丈亭得到消息后,鼻子都快被气歪了。\r\n神级机甲,那可是神级机甲啊,主动上门挑...  0.959375
7  在攻击到来之前,他确实是被两台神级机甲护在了后面,可两台神级机甲也\r\n承受了最多的攻击,...  0.923912
8  红色机甲是什么?那可是神级机甲啊,机甲世界中的顶级机甲,号称能够以\r\n已之力改变战争格局...  0.877193
9  体积明显要比其他颜色的机甲小一些,但谁都知道神级机甲有多强大,那可是和\r\n四字斗铠比肩的...  0.876901
'''

# In[76]:


print (search(u'魔鬼群岛的七位前辈'))
'''
#输出结果:
                                              answer     score
0  你可知道,当初他也去过魔鬼群岛,但是,他被魔鬼群岛的七位前辈誉为自从有\r\n群岛以后最难搞...  1.428616
1  乎所有人都喜欢他,这不是简单的事情。这孩子性格温和,但韧性十足。\r\n你可知道,当初他也去...  1.310614
2  听到这句“玩得高兴”,唐舞麟不禁打了个寒战,不知道是内院的哪些学员\r\n在被他们折磨呢。七...  0.802956
3  当初,在魔鬼群岛上,他用毅力忍受了毁灭之力带来的痛苦,收获最多,也\r\n开始找到了属于自己的路。  0.646335
4  在被他们折磨呢。七位老魔自从来了这边之后,那兴奋劲比当初在魔鬼群岛的时\r\n候增加了几倍,...  0.626933
5  骆桂星忍不住道:“贪婪老….,…·老前辈,为什么当初在魔鬼群岛,你们不是\r\n这样给我们上...  0.592013
6  至连体重都没有办法控制,但他可以掌控自己的实力\r\n当初,在魔鬼群岛上,他用毅力忍受了毁灭...  0.570836
7  激发年轻人的斗志,毕竟没有什么能比初代史莱克七怪的传奇更令人震撼了\r\n骆桂星忍不住道:“...  0.536613
8                     乎没有可行性的方法。\r\n贪梦老魔前辈,您有什么办法吗?”  0.444977
9                  这实在是太可怕了。\r\n传灵塔加上战神殿,都凑不出七位极限斗罗。  0.416765
'''

 


第二个通过命名实体识别和角色标注分类的方法:


# -*- coding: utf-8 -*-
import json
import jieba
import pandas as pd
import numpy as np
import os
import codecs
import sys
import re
from pyltp import SentenceSplitter
from pyltp import Segmentor
from pyltp import Postagger
from pyltp import SementicRoleLabeller
from pyltp import NamedEntityRecognizer
from pyltp import Parser


# 导入用于分析的各种库
import os
LTP_DATA_DIR = '/root/Jupyter Notebook/TeddyCup/ltp_data_v3.4.0'  # ltp模型目录的路径
cws_model_path = os.path.join(LTP_DATA_DIR, 'cws.model')  # 分词模型路径,模型名称为`cws.model`
pos_model_path = os.path.join(LTP_DATA_DIR, 'pos.model')  # 词性标注模型路径,模型名称为`pos.model`
ner_model_path = os.path.join(LTP_DATA_DIR, 'ner.model')  # 命名实体识别模型路径,模型名称为`pos.model`
par_model_path = os.path.join(LTP_DATA_DIR, 'parser.model')  # 依存句法分析模型路径,模型名称为`parser.model`
pisrl_model_path = os.path.join(LTP_DATA_DIR, 'pisrl.model')  # 语义角色标注模型目录路径,模型目录为`srl`。注意该模型路径是一个目录,而不是一个文件。


# 分句函数
def segmentor(sentence):#分词的输入参数是句子

    segmentor = Segmentor()  # 初始化实例
    segmentor.load(cws_model_path)  # 加载模型
    words = segmentor.segment(sentence)  # 分词
    print ('\t'.join(words))
    words_list = list(words)
    segmentor.release()  # 释放模型
    return words_list



sentence = '元芳你怎么看'

#先将句子分词
words = jieba.lcut(sentence)


# 词性标注的函数:
def posttagger(words):#词性标注输入参数是列表

    postagger = Postagger() # 初始化实例
    postagger.load(pos_model_path)  # 加载模型

    #words = ['元芳', '你', '怎么', '看']  # 分词结果
    postags = postagger.postag(words)  # 词性标注

    #print ('\t'.join(postags))
    postagger.release()  # 释放模型
    return postags


#命名实体识别函数
def ner(words, postags):#输入的参数都是列表

    recognizer = NamedEntityRecognizer() # 初始化实例
    recognizer.load(ner_model_path)  # 加载模型

    #words = ['元芳', '你', '怎么', '看']
    #postags = ['nh', 'r', 'r', 'v']
    netags = recognizer.recognize(words, postags)  # 命名实体识别

    #print ('\t'.join(netags))
    recognizer.release()  # 释放模型
    return netags



# 句法依存分析函数
def parse(words, postags):

    parser = Parser() # 初始化实例
    parser.load(par_model_path)  # 加载模型

    #words = ['元芳', '你', '怎么', '看']
    #ostags = ['nh', 'r', 'r', 'v']
    arcs = parser.parse(words, postags)  # 句法分析

    #print ("\t".join("%d:%s" % (arc.head, arc.relation) for arc in arcs))
    parser.release()  # 释放模型
    return arcs


# 语义角色标注的函数:
def role_label(words, postags, arcs):

    labeller = SementicRoleLabeller() # 初始化实例
    labeller.load(pisrl_model_path)  # 加载模型

    #words = ['元芳', '你', '怎么', '看']
    #postags = ['nh', 'r', 'r', 'v']
    # arcs 使用依存句法分析的结果
    roles = labeller.label(words, postags, arcs)  # 语义角色标注
    role_name = []
    # 打印结果
    #for role in roles:
        #print (role.index, "".join(
            #["%s:(%d,%d)" % (arg.name, arg.range.start, arg.range.end) for arg in role.arguments]))
    for role in roles:
        for arg in role.arguments:
            role_name.append(arg.name)
    
    labeller.release()  # 释放模型
    return role_name

#命名实体识别的函数重新封装
def mmst(a):
    b = jieba.lcut(a)
    c = posttagger(b)
    d = ner(b,c)
    g = []
    for i in d:
        g.append(i)
    #print(g)
    return g

sss = mmst(sentence)


#语义角色标注函数重新封装
def jsbz(a):
    b = jieba.lcut(a)
    c = posttagger(b)
    d = parse(b,c)
    e = role_label(b,c,d)
    #print(e)
    return e

b = jsbz(sentence)


# 读入json数据
file = codecs.open('test_data_sample.json','r',encoding='utf-8')
#file = open(r"test_data_sample.json")

f =file


#把属于同一个question的content组合在一起,根据不同的question分开,里面的建立一个字典
fileJson = json.load(f)
a = len(fileJson)
print(a)
#label=[]
#ct={}
m = {}#内容的字典
passage_id = {}
for i in range(a):

    field = fileJson[i]
    b = len(field['passages'])


    for j in range(b):
        
        
        h = []
        g = []
        
        g.append(field['passages'][j]['content'])
        h.append(0)
        
        m[i] = m.get(i,[]) + g
        passage_id[i] = passage_id.get(i,[]) + h


# 导入用于问句分类的特征词:

#导入地点类问题分类的特征词
loc_words = [line.rstrip() for line in open('loc.txt','r',encoding = 'utf-8')]


#导入人名类问题分类的特征词
peo_words = [line.rstrip() for line in open('peo.txt','r',encoding = 'utf-8')]


# 导入用于时间类问题分类的特征词
tmp_words = [line.rstrip() for line in open('tmp.txt','r',encoding = 'utf-8')]


# In[29]:


#核心代码
qs = {}
score = []
a = len(fileJson)
for i in range(a):

    field = fileJson[i]
    b = len(field['passages'])
    qs[i]= field['question']
    for j in range(b):


        s1 = mmst(m[i][j])
        s2 = jsbz(m[i][j])#命名实体标注
        print(s1)


        _ = jieba.lcut(qs[i])#对问题进行分词
        print(_)
        for q in _:
            if q in loc_words:#对问题进分类,如果条件句为真,问题就分为地点类问题
                n =0
                for word in s1:
                    
                    if re.match('S-Ns$',word):#对回答中的
                        
                        n = n+1
                if n <=2 :
                    passage_id[i][j] = 1
                    print(1)


        for q in _:
            if q in peo_words:
                n =0
                for word in s1:
                    
                    if re.match('S-Nh$',word):
                        
                        n = n+1
                if n ==1 :
                    passage_id[i][j] = 1
                    print(2)

        for q in _:
            if q in tmp_words:
                n =0
                for word in s2:
                    
                    if re.match('TMP$',word):
                        
                        n = n+1
                if n <=2 :
                    passage_id[i][j] = 1
                    print(3)



pid = []#将存放命名实体匹配模型得到的结果passage_id里面的0,1编号
g = []#将存放原数据的passage_id,像17148201这样的


# 将数据添加到列表里面
a = len(fileJson)
for i in range(a):
    field = fileJson[i]
    b = len(field['passages'])
    for j in range(b):
        pid.append(passage_id[i][j])
        g.append(field['passages'][j]['passage_id'])


c = {
    "label" : pid,
    "passage_id" : g
    }
tf = pd.DataFrame(c)
#tf['score'] = tf.score.apply(judge_score)
#tf['label'] = tf.score.apply(judge_score)



# 保存数据
tf.to_csv( 'pyltp_result_00001.csv', sep=',', escapechar='\\',index=False)#命名实体识别模型输出的0,1label


# 读入数据
test_df = pd.read_csv("test_result_001.csv",encoding = 'utf-8',delimiter=",")#读取前面tfc-icf模型计算的结果

# 定义一个0 1 分类的函数 
def judge_score1(x):
    if x >=0.31 :
        return 1
    else:
        return 0

# 对数据框里面的数据 进行分类
test_df['score'] = test_df.score.apply(judge_score1)



# 保存数据
test_df.to_csv( 'test_result_002.csv', sep=',', escapechar='\\',index=False)


# 定义一个新的列表,将两个方法的到的结果的值加起来
ppp = []
for i in range(len(tf.label)):
    ppp.append(tf.label[i]+test_df.score[i])


# 定义第二个分类的函数
def judge_score2(x):
    if x >= 1:
        return 1
    else:
        return 0


# 将列表转化为数据框的形式
cccc = {
    "end_label" : ppp,
    "passage_id" : g
    }
end_tf = pd.DataFrame(cccc)
#tf['score'] = tf.score.apply(judge_score)
end_tf['end_label'] = end_tf.end_label.apply(judge_score2)



# 保存最后的结果
end_tf.to_csv( 'end_pyltp_result_00002.csv', sep=',', escapechar='\\',index=False)

 

 

你可能感兴趣的:(python数据分析)