python extract_convert.py对应代码解读抽取式提取+生成式提取摘要代码解读------摘要代码解读1

pytorch动态调整学习率

  • 抽取式生成的代码
    • convert数据转化的过程
    • 进入extract_convert.py中的extract_flow函数内容
    • 查看text_split函数的文本切割过程
    • snippets.py之中的text_segmentate函数的调用
    • 回到extract_convert.py的text_segmentate函数之中的内容
    • 进入extract_convert.py中的extract_matching函数内容
    • snippets.py之中查看compute_metrics函数的内容
    • snippets.py之中查看compute_rouge函数内容
    • 法研杯摘要学习snippets.py中compute_main_metrics函数中的调用部分
    • sinppets.py之中compute_metrics函数调用过程
    • sinppets.py之中的compute_rouge函数内容
    • 回到extract_convert.py之中的extract_convert.py的函数内容
    • extract_convert.py中extract_matching函数递归操作实现
    • extract_convert.py之中的extract_flow函数调用的操作
    • 回到extract_convert.py之中的convert函数中来
    • 回到extract_convert.py的main函数之中继续进行调查
    • 总结

抽取式生成的代码

最近拜读了苏大佬抽取式生成的代码,收获颇丰,这里解析一下对应内容
对应的github项目地址:
https://github.com/bojone/SPACES
苏大佬讲解的内容
https://spaces.ac.cn/archives/8046
首先输入数据的格式:

{"id": "5cec68aadcbea086a5b4b6eccb58a88f", "summary": "原告与被告侵权责任纠纷一案。原告提出诉求:判决被告赔偿原告医疗费、住院伙食补助费、营养费、护理费、误工费、残疾赔偿金、精神损害抚慰金、后续治疗费、被抚养人生活费、交通费、住宿费、鉴定费。被告辩称两被告对此不予认可,称两被告与涉案店铺没有关系,并未租用该店铺。经查明原告系受案外人粟顺良指示在涉案店铺进行装修,其主张两被告未尽到管理责任,但并未能提供充分的证据证明涉案商铺与两被告的关系,亦未能证实两被告存在过错行为且与原告受伤的结果之间有因果联系。根据《中华人民共和国民事诉讼法》第六十四条第一款、《最高人民法院关于民事诉讼证据的若干规定》第二条的规定,判决:驳回原告全部诉讼请求。", 
"text": [{"sentence": "唐鲜明与何伟华、深圳市华名威电汽车服务有限公司侵权责任纠纷一审民事判决书", "label": 1},
 {"sentence": "广东省深圳市宝安区人民法院", "label": 0}, {"sentence": "民 事 判 决 书", "label": 0}, {"sentence": "(2017)粤0306民初3474号", "label": 0}, 
 {"sentence": "原告唐鲜明", "label": 0}, {"sentence": "委托代理人孙光扬,泽丰(深圳)律师事务所律师。", "label": 0}, {"sentence": "被告何伟华", "label": 0}, 
 {"sentence": "被告深圳市华名威电汽车服务有限公司。", "label": 0}, {"sentence": "法定代表人何伟华。", "label": 0}, {"sentence": "两被告共同委托代理人刘征,广东同观律师事务所律师。", "label": 0}, {"sentence": "上列原告唐鲜明诉被告何伟华、深圳市华名威电汽车服务有限公司(以下简称华名威公司)侵权责任纠纷一案,原告诉请:1、判决被告赔偿原告163755.57元,赔偿明细如下:医疗费27086.37元、住院伙食补助费2500元、营养费4000元、护理费4598元、误工费26851元、残疾赔偿金46677.20元、精神损害抚慰金20000元、后续治疗费10000元、被抚养人生活费7787+9456元、交通费2000元、住宿费1000元、鉴定费1800元;", "label": 1}, {"sentence": "2、案件受理费等由被告承担。", "label": 1}, 
 ............
{"sentence": "5、住院期间留一人陪护。", "label": 0}, {"sentence": "2015年3月16日,广东南天司法鉴定所作出粤南(2015)临鉴字第817号《司法鉴定意见书》,鉴定意见为被鉴定人唐鲜明的伤残等级为玖级。", "label": 0}, 
{"sentence": "原告为此支付鉴定费1800元。", "label": 0}, {"sentence": "另,关于原告家庭情况,原告主张其无子女,兄弟三人,原告父亲唐近祥1948年12月16日出生,母亲李解英1951年10月18日出生,均为农业户口。", "label": 0}, 
{"sentence": "判决结果", "label": 0}, {"sentence": "本院认为,原告系受案外人粟顺良指示在涉案店铺进行装修,其主张两被告未尽到管理责任,但并未能提供充分的证据证明涉案商铺与两被告的关系,亦未能证实两被告存在过错行为且与原告受伤的结果之间有因果联系,故原告应承担举证不能的法律后果,对原告的诉讼请求,本院均不予支持。", "label": 1}, 
{"sentence": "综上,依照《中华人民共和国民事诉讼法》第六十四条第一款、《最高人民法院关于民事诉讼证据的若干规定》第二条的规定,判决如下:", "label": 1}, {"sentence": "驳回原告唐鲜明的全部诉讼请求。", "label": 1}, {"sentence": "案件受理费3575.12元,由原告唐鲜明负担。", "label": 1}, 
{"sentence": "如不服本判决,可在判决书送达之日起十五日内,向本院递交上诉状,并按对方当事人的人数提出副本,上诉于广东省深圳市中级人民法院。", "label": 0}, {"sentence": "审 判 长 赵  曼  琪", "label": 0}, {"sentence": "人民陪审员 陈  初  瑛", "label": 0}, 
{"sentence": "人民陪审员 王  嘉  义", "label": 0}, {"sentence": "二〇一七年十二月二十七日", "label": 0}, {"sentence": "书 记 员 高美琪(兼)", "label": 0}, 
{"sentence": "书 记 员 文  英  玲", "label": 0}, {"sentence": "附本案相关法律条文如下:", "label": 0}, {"sentence": "《中华人民共和国民事诉讼法》", "label": 0}, 
{"sentence": "第六十四条第一款当事人对自己提出的主张,有责任提供证据。", "label": 0}, {"sentence": "《最高人民法院关于民事诉讼证据的若干规定》", "label": 0}, 
{"sentence": "第二条当事人对自己提出的诉讼请求所依据的事实或者反驳对方诉讼请求所依据的事实有责任提供证据加以证明。", "label": 0}, {"sentence": "没有证据或者证据不足以证明当事人的事实主张的,由负有举证责任的当事人承担不利后果。", "label": 0}]}

可以看出这里有对应的text,由许多的sentence组成,同时也有对应的label。
接着进行载入数据

data = load_data(data_json)

加载完成之后得到的data

data1 = 
('唐鲜明与何伟华、深圳市华名威电汽车服务有限公司侵权责任纠纷一审民事判决书\n广东省深圳市宝安区人民法院\n民 事 判 决 书\n
(2017)粤0306民初3474号\n原告唐鲜明\n委托代理人孙光扬,泽丰(深圳)律师事务所律师。\n
被告何伟华\n被告深圳市华名威电汽车服务有限公司。\n法定代表人何伟华。\n两被告共同委托代理人刘征,广东同观律师事务所律师。\n
......
根据《中华人民共和国民事诉讼法》第六十四条第一款、《最高人民法院关于民事诉讼证据的若干规定》第二条的规定,判决:驳回原告全部诉讼请求。')

每一句中间用\n进行隔开,这样组装成一个对应的句子
接着进行数据转换操作:

data = convert(data)

获得对应的data

data2 = 
(['唐鲜明与何伟华、深圳市华名威电汽车服务有限公司侵权责任纠纷一审民事判决书', '1、判决被告赔偿原告163755.57元,', 
'赔偿明细如下:', '医疗费27086.37元、住院伙食补助费2500元、营养费4000元、护理费4598元、误工费26851元、残疾赔偿金46677.20元、
精神损害抚慰金20000元、后续治疗费10000元、被抚养人生活费7787+9456元、交通费2000元、住宿费1000元、鉴定费1800元;', 
'2、案件受理费等由被告承担。', '本院受理后,', '依法适用普通程序,', '公开开庭进行了审理。', '原告唐鲜明及其委托代理人孙光扬,',
'两被告共同委托代理人刘征到庭参加了诉讼。', '由负有举证责任的当事人承担不利后果。'], 
[12, 13, 16, 37, 38, 39, 73, 74, 75, 76, 81, 82, 83], 
'原告与被告侵权责任纠纷一案。原告提出诉求:判决被告赔偿原告医疗费、住院伙食补助费、营养费、护理费、误工费、残疾赔偿金、精神损害抚慰金、
后续治疗费、被抚养人生活费、交通费、住宿费、鉴定费。被告辩称两被告对此不予认可,称两被告与涉案店铺没有关系,并未租用该店铺。
经查明原告系受案外人粟顺良指示在涉案店铺进行装修,其主张两被告未尽到管理责任,但并未能提供充分的证据证明涉案商铺与两被告的关系《最高人民法院关于民事诉讼证据的若干规定》第二条的规定,判决:驳回原告全部诉讼请求。')

这里进入convert(data)之中查看具体的数据转换环节

convert数据转化的过程

def convert(data):
    """分句,并转换为抽取式摘要
    """
    D = parallel_apply(
        func=extract_flow,
        iterable=tqdm(data, desc=u'转换数据'),
        workers=100,
        max_queue_size=200
    )
    total_metric = sum([d[3] for d in D])
    D = [d[:3] for d in D]
    print(u'抽取结果的平均指标: %s' % (total_metric / len(D)))
    return D

这里并行转换为遍历读取相应的数据

def convert(data):
    """分句,并转换为抽取式摘要
    """
    for data1 in data:
        print('data1 = ')
        print(data1)
        result1 = extract_flow(data1)
        print('result1 = ')
        print(result1)
        break
    total_metric = sum([d[3] for d in D])
    D = [d[:3] for d in D]
    print(u'抽取结果的平均指标: %s' % (total_metric / len(D)))
    return D

这里对应的data1的内容为

data1 = 
('唐鲜明与何伟华、深圳市华名威电汽车服务有限公司侵权责任纠纷一审民事判决书\n
广东省深圳市宝安区人民法院\n民 事 判 决 书\n(2017)粤0306民初3474号\n
......
根据《中华人民共和国民事诉讼法》第六十四条第一款、《最高人民法院关于民事诉讼证据的若干规定》第二条的规定,判决:驳回原告全部诉讼请求。')

进入到extract_flow函数之中去

result1 = extract_flow(data1)

进入extract_convert.py中的extract_flow函数内容

查看extract_flow函数

def extract_flow(inputs):
    """单个样本的构建流(给parallel_apply用)
    """
    text, summary = inputs
    texts = text_split(text, True)  # 取后maxlen句
    summaries = text_split(summary, False)
    mapping = extract_matching(texts, summaries)
    #!!!extract_matching抽取关键!!!看上面的extract_matching函数的注释
    labels = sorted(set([i[1] for i in mapping]))
    pred_summary = ''.join([texts[i] for i in labels])
    metric = compute_main_metric(pred_summary, summary)
    return texts, labels, summary, metric

查看text_split函数的文本切割过程

def text_split(text, limited=True):
    """将长句按照标点分割为多个子句。
    """
    texts = text_segmentate(text, 1, u'\n。;:,')
    if limited:
        texts = texts[-maxlen:]
    return texts

首先看开头的切割部分

texts = text_segmentate(text,1,u'\n。;:,')

这里这段是将句子切分开,这里我从snippets.py之中调用出text_segmentate函数的内容

snippets.py之中的text_segmentate函数的调用

def text_segmentate(text, maxlen, seps='\n', strips=None):
    """将文本按照标点符号划分为若干个短句
    """
    text = text.strip().strip(strips)
    if seps and len(text) > maxlen:
        pieces = text.split(seps[0])
        text, texts = '', []
        for i, p in enumerate(pieces):
            if text and p and len(text) + len(p) > maxlen - 1:
                texts.extend(text_segmentate(text, maxlen, seps[1:], strips))
                text = ''
            if i + 1 == len(pieces):
                text = text + p
            else:
                text = text + p + seps[0]
        if text:
            texts.extend(text_segmentate(text, maxlen, seps[1:], strips))
        return texts
    else:
        return [text]

回到extract_convert.py的text_segmentate函数之中的内容

首先进行切分

texts = text_segmentate(text, 1, u'\n。;:,')

这里在调用text_segmentate函数,本质上是使用换行、分号、冒号、逗号、句号进行分割。
切分之前内容

texts = 
原告与被告侵权责任纠纷一案。
原告提出诉求:判决被告赔偿原告医疗费、住院伙食补助费、营养费、护理费、误工费、残疾赔偿金、精神损害抚慰金、后续治疗费、被抚养人生活费、交通费、住宿费、鉴定费。被告辩称两被告对此不予认可,称两被告与涉案店铺没有关系,并未租用该店铺。
经查明原告系受案外人粟顺良指示在涉案店铺进行装修,其主张两被告未尽到管理责任,但并未能提供充分的证据证明涉案商铺与两被告的关系,亦未能证实两被告存在过错行为且与原告受伤的结果之间有因果联系。
根据《中华人民共和国民事诉讼法》第六十四条第一款、《最高人民法院关于民事诉讼证据的若干规定》第二条的规定,判决:驳回原告全部诉讼请求。

切分完之后的内容

['原告与被告侵权责任纠纷一案。', '原告提出诉求:', '判决被告赔偿原告医疗费、住院伙食补助费、营养费、护理费、误工费、残疾赔偿金、精神损害抚慰金、后续治疗费、被抚养人生活费、交通费、住宿费、鉴定费。', '被告辩称两被告对此不予认可,', '称两被告与涉案店铺没有关系,', '并未租用该店铺。', '经查明原告系受案外人粟顺良指示在涉案店铺进行装修,', '其主张两被告未尽到管理责任,', '但并未能提供充分的证据证明涉案商铺与两被告的关系,', '亦未能证实两被告存在过错行为且与原告受伤的结果之间有因果联系。', '根据《中华人民共和国民事诉讼法》第六十四条第一款、《最高人民法院关于民事诉讼证据的若干规定》第二条的规定,', '判决:', '驳回原告全部诉讼请求。']

注意:这里的-maxlen意思为取出最后面的maxlen个对应的数值

texts = texts[-maxlen:]

进入extract_convert.py中的extract_matching函数内容

def extract_matching(texts, summaries, start_i=0, start_j=0):
    """在texts中找若干句子,使得它们连起来与summaries尽可能相似
    算法:texts和summaries都分句,然后找出summaries最长的句子,在texts
          中找与之最相似的句子作为匹配,剩下部分递归执行。
    """
    print('extract_matching')
    if len(texts) == 0 or len(summaries) == 0:
        return []
    r"""
    texts = ['唐鲜明与何伟华、深圳市华名威电汽车服务有限公司
    ......
    '由负有举证责任的当事人承担不利后果。']
    $$$summaries = $$$
    ['原告与被告侵权责任纠纷一案。', '原告提出诉求:', 
     ......
     '驳回原告全部诉讼请求。']
    i = 2,这里的summaries之中i = 2对应的长度是最长的
    """
    i = np.argmax([len(s) for s in summaries])
    print('i = ')
    print(i)
    j = np.argmax([compute_main_metric(t, summaries[i], 'char') for t in texts])
    print('j = ')
    print(j)
    lm = extract_matching(texts[:j + 1], summaries[:i], start_i, start_j)
    rm = extract_matching(
        texts[j:], summaries[i + 1:], start_i + i + 1, start_j + j
    )
    return lm + [(start_i + i, start_j + j)] + rm

这里首先调用判断summaries最长的语句
注意调用的为summaries之中的最长语句的内容

i = np.argmax([len(s) for s in summaries])

得到对应的i = 2,这里代表对应的summaries之中

summaries = 
['原告与被告侵权责任纠纷一案。', '原告提出诉求:', 
'判决被告赔偿原告医疗费、住院伙食补助费、营养费、护理费、误工费、
残疾赔偿金、精神损害抚慰金、后续治疗费、被抚养人生活费、交通费、住宿费、鉴定费。', 
......
'判决:', '驳回原告全部诉讼请求。']

第二个序列的句子

'判决被告赔偿原告医疗费、住院伙食补助费、营养费、护理费、误工费、
残疾赔偿金、精神损害抚慰金、后续治疗费、被抚养人生活费、交通费、住宿费、鉴定费。'

长度是最长的
接下来调用相应的

j = np.argmax([compute_main_metric(t, summaries[i], 'char') for t in texts])

这里对于每个texts中的内容,调用compute_main_metric函数进行计算
上面筛选出来了最长的长度为summaries[i],这里判断每一个texts之中的内容和summaries[i]的主要间隔
也就是说这里计算的是summaries之中的最长句子和texts之中的每个句子的内容构成的main_metric的最长长度

snippets.py之中查看compute_metrics函数的内容

def compute_metrics(source, target, unit='word'):
    """计算所有metrics
    """
    metrics = compute_rouge(source, target, unit)
    metrics['main'] = (
        metrics['rouge-1'] * 0.2 + metrics['rouge-2'] * 0.4 + metrics['rouge-l'] * 0.4
    )
    return metrics

def compute_main_metric(source, target, unit='word'):
    """计算主要metric
    """
    return compute_metrics(source, target, unit)['main']

snippets.py之中查看compute_rouge函数内容

def compute_rouge(source, target, unit='word'):
    """计算rouge-1、rouge-2、rouge-l
    """
    if unit == 'word':
        source = jieba.cut(source, HMM=False)
        target = jieba.cut(target, HMM=False)
    source, target = ' '.join(source), ' '.join(target)
    try:
        scores = rouge.get_scores(hyps=source, refs=target)
        return {
            'rouge-1': scores[0]['rouge-1']['f'],
            'rouge-2': scores[0]['rouge-2']['f'],
            'rouge-l': scores[0]['rouge-l']['f'],
        }
    except ValueError:
        return {
            'rouge-1': 0.0,
            'rouge-2': 0.0,
            'rouge-l': 0.0,
        }

由于上文找出来的summaries之中的最长长度为第二个索引,所以这里将第二个索引提取出来,依次将对应的文本中的内容和摘要列表之中的内容进行比较

j = np.argmax([compute_main_metric(t,summaries[i],'char') for t in texts])
判决被告赔偿原告医疗费、住院伙食补助费、
营养费、护理费、误工费、残疾赔偿金、精神损害抚慰金、
后续治疗费、被抚养人生活费、交通费、住宿费、鉴定费。

法研杯摘要学习snippets.py中compute_main_metrics函数中的调用部分

def compute_main_metric(source, target, unit='word'):
    """计算主要metric
    """
    return compute_metrics(source, target, unit)['main']

sinppets.py之中compute_metrics函数调用过程

接下来进入compute_metrics函数调用过程

def compute_metrics(source, target, unit='word'):
    """计算所有metrics
    """
    print('compute_metrics')
    print('source = ')
    print(source)
    print('target = ')
    print(target)
    metrics = compute_rouge(source, target, unit)
    print('metrics = ')
    print(metrics)
    metrics['main'] = (
        metrics['rouge-1'] * 0.2 + metrics['rouge-2'] * 0.4 +
        metrics['rouge-l'] * 0.4
    )
    print('new metrics = ')
    print(metrics)
    print('**************')
    return metrics

示例之中,我们采用

source = 
唐鲜明与何伟华、深圳市华名威电汽车服务有限公司侵权责任纠纷一审民事判决书
target = 
判决被告赔偿原告医疗费、住院伙食补助费、营养费、护理费、误工费、残疾赔偿金、精神损害抚慰金、后续治疗费、被抚养人生活费、交通费、住宿费、鉴定费。

进入到snippets.py之中的compute_rouge之中

sinppets.py之中的compute_rouge函数内容

def compute_rouge(source, target, unit='word'):
    """计算rouge-1、rouge-2、rouge-l
    """
    if unit == 'word':
        source = jieba.cut(source, HMM=False)
        target = jieba.cut(target, HMM=False)
    source, target = ' '.join(source), ' '.join(target)
    try:
        scores = rouge.get_scores(hyps=source, refs=target)
        return {
            'rouge-1': scores[0]['rouge-1']['f'],
            'rouge-2': scores[0]['rouge-2']['f'],
            'rouge-l': scores[0]['rouge-l']['f'],
        }
    except ValueError:
        return {
            'rouge-1': 0.0,
            'rouge-2': 0.0,
            'rouge-l': 0.0,
        }

这里的调用过程首先需要切分对应的单词内容

source = jieba.cut(source,HMM=False)
target = jieba.cut(target,HMM=False)

这里HMM调用的参数为是否开启HMM进行中文分词
然后进行分词之后的拼接并计算相应的分数内容

source = 
唐 鲜 明 与 何 伟 华 、 深 圳 市 华 名 威 电 汽 车 服 务 有 限 公 司 侵 权 责 任 纠 纷 一 审 民 事 判 决 书
target = 
判 决 被 告 赔 偿 原 告 医 疗 费 、 住 院 伙 食 补 助 费 、 营 养 费 、 护 理 费 、 误 工 费 、 残 疾 赔 偿 金 、 精 神 损 害 抚 慰 金 、 后 续 治 疗 费 、 被 抚 养 人 生 活 费 、 交 通 费 、 住 宿 费 、 鉴 定 费 。

计算分数时候调用相应的计算分数的包

try:
    scores = rouge.get_scores(hyps=source, refs=target)
    return {
        'rouge-1': scores[0]['rouge-1']['f'],
        'rouge-2': scores[0]['rouge-2']['f'],
        'rouge-l': scores[0]['rouge-l']['f'],
    }
except ValueError:
    return {
        'rouge-1': 0.0,
        'rouge-2': 0.0,
        'rouge-l': 0.0,
    }

rouge基于摘要中n元词的共现来评价摘要,rouge准则由一系列评价方法组成,包括rouge-N(N=1,2,3),
比如由机器产生的句子

the cat was found under the bed

与由人工产生的句子

the cat was under the bed

计算最终结果的时候

rouge1_recall = 6/6 = 1.0
rouge1_precision = 6/7

召回率看这个句子之中有几个被预测出来的,准确率也看这个句子之中有几个被预测出来的。
召回率
R e c a l l = 机 器 生 成 的 词 以 及 人 工 生 成 的 词 机 器 生 成 的 词 Recall = \frac{机器生成的词以及人工生成的词}{机器生成的词} Recall=
准确率
P r e c i s i o n = 机 器 生 成 的 词 以 及 人 工 生 成 的 词 机 器 生 成 的 词 Precision = \frac{机器生成的词以及人工生成的词}{机器生成的词} Precision=
最终综合得分综合考虑Recall分数和Precision分数的指标

metrics['main'] = (
    metrics['rouge-1'] * 0.2 + metrics['rouge-2'] * 0.4 +
    metrics['rouge-l'] * 0.4
)

回到extract_convert.py之中的extract_convert.py的函数内容

i = np.argmax([len(s) for s in summaries])
j = np.argmax([compute_main_metric(t,summaries[i],'char') for t in texts])

這里的取出summaries中最長的len(s),之後找到與這個summaries匹配最好的t
np.argmax用于返回一个numpy数组中最大值的索引值,当一组中同时出现几个最大值时,返回第一个最大值的索引值。这里切分的过程是按照最大索引值进行切割。
目前得到的這一波的匹配結果

i = 2
j = 16
texts[j] = 
医疗费27086.37元、住院伙食补助费2500元、营养费4000元、护理费4598元、误工费26851元、残疾赔偿金46677.20元、精神损害抚慰金20000元、后续治疗费10000元、被抚养人生活费7787+9456元、交通费2000元、住宿费1000元、鉴定费1800元;
-----------
summaries[i] = 
判决被告赔偿原告医疗费、住院伙食补助费、营养费、护理费、误工费、残疾赔偿金、精神损害抚慰金、后续治疗费、被抚养人生活费、交通费、住宿费、鉴定费。

extract_convert.py中extract_matching函数递归操作实现

进入到开始递归的操作

lm = extract_matching(texts[:j + 1], summaries[:i], start_i, start_j)

这里输入的递归的第一个部分

texts[:j+1] = 
['唐鲜明与何伟华、深圳市华名威电汽车服务有限公司侵权责任纠纷一审民事判决书', '广东省深圳市宝安区人民法院', '民 事 判 决 书', '(2017)粤0306民初3474号', 
'原告唐鲜明', '委托代理人孙光扬,', '泽丰(深圳)律师事务所律师。', '被告何伟华', '被告深圳市华名威电汽车服务有限公司。', '法定代表人何伟华。', 
'两被告共同委托代理人刘征,', '广东同观律师事务所律师。', '上列原告唐鲜明诉被告何伟华、深圳市华名威电汽车服务有限公司(以下简称华名威公司)侵权责任纠纷一案,', 
'原告诉请:', '1、判决被告赔偿原告163755.57元,', '赔偿明细如下:', '医疗费27086.37元、住院伙食补助费2500元、营养费4000元、护理费4598元、误工费26851元、
 残疾赔偿金46677.20元、精神损害抚慰金20000元、后续治疗费10000元、被抚养人生活费7787+9456元、交通费2000元、住宿费1000元、鉴定费1800元;']
summaries[:i] = 
['原告与被告侵权责任纠纷一案。', '原告提出诉求:']
start_i = 0
start_j = 0

输入的递归的第二个部分

texts[:j+1] = 
['唐鲜明与何伟华、深圳市华名威电汽车服务有限公司侵权责任纠纷一审民事判决书', '广东省深圳市宝安区人民法院', '民 事 判 决 书', '(2017)粤0306民初3474号', '原告唐鲜明', 
'委托代理人孙光扬,', '泽丰(深圳)律师事务所律师。', '被告何伟华', '被告深圳市华名威电汽车服务有限公司。', '法定代表人何伟华。', '两被告共同委托代理人刘征,', '广东同观律师事务所律师。', 
'上列原告唐鲜明诉被告何伟华、深圳市华名威电汽车服务有限公司(以下简称华名威公司)侵权责任纠纷一案,', '原告诉请:', '1、判决被告赔偿原告163755.57元,', '赔偿明细如下:', 
'医疗费27086.37元、住院伙食补助费2500元、营养费4000元、护理费4598元、误工费26851元、残疾赔偿金46677.20元、精神损害抚慰金20000元、后续治疗费10000元、
被抚养人生活费7787+9456元、交通费2000元、住宿费1000元、鉴定费1800元;']

summaries[:i] = 
['原告与被告侵权责任纠纷一案。', '原告提出诉求:']

感觉这里的texts到j就可以了,summaries到i,
因为texts是按照语义顺序的,summaries也是按照语义顺序的,之前找到texts[j]和summaries[i]匹配的最好,接下来只要去匹配texts和summaries前面的内容以及texts和summaries后面的内容即可

lm = extract_matching(texts[:j + 1], summaries[:i], start_i, start_j)
rm = extract_matching(
    texts[j:], summaries[i + 1:], start_i + i + 1, start_j + j
)

注意每次返回的时候,要加上当前选择的这对组合

return lm+[(start_i+i,start_j+j)]+rm

递归最终的终止条件写在函数前面

if len(texts) == 0 or len(summaries) == 0:
	return []

extract_convert.py之中的extract_flow函数调用的操作

此时这里的extract_matching函数调用完毕,回到extract_flow的函数之中

mapping = extract_matching(texts, summaries)
labels = sorted(set([i[1] for i in mapping]))

得到的结果

[(0, 12), (1, 13), (2, 16), (3, 37), (4, 38), (5, 39), (6, 73), (7, 74), (8, 75), (9, 76), (10, 81), (11, 82), (12, 83)]

第一个标签对应着labels的索引,第二个标签对应summaries的索引
接下来调用对应的labels之中的内容

labels = sorted(set([i[1] for i in mapping]))

得到对应的labels的结果

labels = [12,13,16,37,...,81,82,83]

之所以上面需要使用i[1]也就是texts文本中的顺序进行排序,是因为下面需要对texts文本之中的内容进行拼接
接下来需要从原始的texts文本之中将这些比较符合的文本提取出来

pred_summary = ''.join([texts[i] for i in labels])

将提取出来的rouge分数比较相近的文本拼接在一起
接下来计算拼接完成之后的得分

metric = compute_main_metric(pred_summary,summary)

回到extract_convert.py之中的convert函数中来

对于每一个data,这里并行式抽取数据并进行转化

D = parallel_apply(
    func=extract_flow,
    iterable=tqdm(data, desc=u'转换数据'),
    workers=100,
    max_queue_size=200
)

转化完成之后获得相应的D内容

D = 
(['唐鲜明与何伟华、深圳市华名威电汽车服务有限公司侵权责任纠纷一审民事判决书', '广东省深圳市宝安区人民法院', '民 事 判 决 书',
......
'没有证据或者证据不足以证明当事人的事实主张的,', '由负有举证责任的当事人承担不利后果。'],
[12,13,16,...81,82,83],
'原告与被告侵权责任纠纷一案。原告提出诉求:判决被告赔偿原告医疗费、住院伙食补助费、营养费、护理费、误工费、
......
《最高人民法院关于民事诉讼证据的若干规定》第二条的规定,判决:驳回原告全部诉讼请求。',
 0.7481025356642723)
 ......

这里的第一段文本为原始文本,标签为每个summaries能够匹配上的原始文本,最后一段为summaries对应的文本内容以及这次的评分。
这里计算相应的评分结果的平均值

total_metric = sum([d[3] for d in D])
D = [d[:3] for d in D]
print(u'抽取结果的平均指标: %s' % (total_metric / len(D)))
return D

回到extract_convert.py的main函数之中继续进行调查

if os.path.exists(data_random_order_json):
    idxs = json.load(open(data_random_order_json))
else:
    idxs = list(range(len(data)))
    np.random.shuffle(idxs)
    json.dump(idxs, open(data_random_order_json, 'w'))

如果之前已经写入到文件之中了,这里运行

os.path.exists(data_random_order_json)

从中读取出对应的idxs的内容
如果之前的id并未写入到相应的文件之中

idxs = list(range(len(data)))
np.random.shuffle(idxs)
json.dump(idxs, open(data_random_order_json, 'w'))

重写打乱并写入新的文件之中
接下来按照打乱的顺序依次读取并写入到相应的文件之中

data = [data[i] for i in idxs]
with open(data_extract_json, 'w', encoding='utf-8') as f:
    for d in data:
        f.write(json.dumps(d, ensure_ascii=False) + '\n')

总结

本质上extract_vectorize.py文件中的sequence_padding还是进行padding两次,第一次padding的过程出现在predict函数之中

batch_token_ids = sequence_padding(batch_token_ids)
batch_segment_ids = sequence_padding(batch_segment_ids)

这是当每一个句子长度没有补齐最长的长度时候,将句子长度补齐最长的长度
第二次padding出现在convert函数之中

def convert(data):
	......
	embeddings = sequence_padding(embeddings)
	return embeddings

这里的padding是将整个的list补齐到一个长度,比如每个句子分别是(12,768),(15,768),这里需要将这些句子都补齐到(15,768)

你可能感兴趣的:(文本摘要抽取代码解读,python)