栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Python

【创新实训】问答系统-Question Generation模块-【博客5:预测生成模块】

Python 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

【创新实训】问答系统-Question Generation模块-【博客5:预测生成模块】

前面四篇博客主要探究了QG(Question Generation)任务的基本策略、评价指标;描述了我的初步探索;以及给出了数据处理的方法以及训练模型的构建方法:

  • 博客1:基本策略
  • 博客2:评价指标、初步探索
  • 博客3:训练数据和预测数据预处理的部分
  • 博客4:模型的构建
  • 博客5:生成“问答对”
  • 博客6:问答对有效性过滤(基于文本分类任务)
  • 博客7:用问题库检索服务
一、模型的预测–生成问题

这一部分主要将“训练好的模型”应用在数据预处理部分得到的教材数据上,期望能够对教材生成一系列问题。

为了测试生成的模型的性能,我选取部分测试数据,将其放在my_test.json文件中,其格式和训练数据导入时的格式相同。
生成答案的时候,infer.py程序主要完成下面几个步骤:

1.加载模型权重;
2.构建问题生成器;
3.加载预测数据,并对篇章、答案数据清洗,对长文本数据进行截断;
4.生成每一个问题
5.保存生成的结果到result.json中

相关的代码如下:

def do_infer(input_path, output_path):
    # 加载模型权重
    models = []
    for i in range(1, n + 1):
        i = i-1
        model, train_model = build_model()
        train_model.load_weights(f'../user_data/model_data/fold-{i}.h5')
        models.append(model)

    # 问题生成器
    qag = QuestionGeneration(
        models, start_id=None, end_id=tokenizer._token_dict['?'],
        maxlen=max_q_len
    )

    # 加载并遍历预测数据
    data = json.load(open(input_path))[0:10:1] #预测部分数据
    func = lambda m: '' if len(m.group(0)) > 5 else m.group(0)
    for d in tqdm(data, desc=u'正在预测(共%s条样本)' % len(data)):
        # 篇章数据清洗
        passage = re.sub(r's', '', d['text'])
        passage = re.sub(r'\n', '', passage)
        passage = re.sub(r'([A-Za-z]+)', func, passage)
        passage = re.sub(r'([A-Za-z]+)', func, passage)
        for qa in d['annotations']:
            # 答案数据清洗
            answer = re.sub(r's', '', qa['A'])
            answer = re.sub(r'\n', '', answer)
            answer = re.sub(r'([A-Za-z]+)', func, answer)
            answer = re.sub(r'([A-Za-z]+)', func, answer)
            answer = answer[:-1] if answer.endswith('.') else answer
            # 长文本数据截断
            if len(passage) < max_p_len - 2 and len(answer) < max_a_len - 1:
                a = answer
                p = passage
            else:
                a = answer[:max_a_len - 1] if len(
                    answer) > max_a_len - 1 else answer
                try:
                    idx = passage.index(a)
                    if len(passage[idx:]) < (max_p_len - 2 - head):
                        p = passage[-(max_p_len - 2):]
                    else:
                        p = passage[max(0, idx - head):]
                        p = p[:max_p_len - 2]
                except ValueError:
                    p = passage[:max_p_len - 2]
            qa['Q'] = qag.generate(p, a)  # 问题生成

    # 保存结果
    json.dump(data, open(output_path, 'w'), ensure_ascii=False, indent=2)

其余部分的代码如下:

import json
import os
import re

import numpy as np
from bert4keras.models import build_transformer_model
from bert4keras.snippets import AutoRegressiveDecoder
from bert4keras.tokenizers import Tokenizer, load_vocab
from keras.layers import Input
from keras.models import Model
from tqdm import tqdm

os.environ['CUDA_VISIBLE_DEVICES'] = '0'    # 指定GPU

# 基本参数
n = 1               # 交叉验证
max_p_len = 194     # 篇章最大长度
max_q_len = 131     # 问题最大长度
max_a_len = 65      # 答案最大长度
head = 64           # 篇章截取中,取答案id前head个字符


# nezha配置
config_path = '../user_data/model_data/NEZHA-Large-WWM/bert_config.json'
checkpoint_path = '../user_data/model_data/NEZHA-Large-WWM/model.ckpt-346400'
dict_path = '../user_data/model_data/NEZHA-Large-WWM/vocab.txt'


# 加载并精简词表,建立分词器
token_dict, keep_tokens = load_vocab(
    dict_path=dict_path,
    simplified=True,
    startswith=['[PAD]', '[UNK]', '[CLS]', '[SEP]'],
)
tokenizer = Tokenizer(token_dict, do_lower_case=True)


def build_model():
    """构建模型。"""
    model = build_transformer_model(
        config_path,
        checkpoint_path,
        model='nezha',
        application='unilm',
        keep_tokens=keep_tokens,  # 只保留keep_tokens中的字,精简原字表
    )

    o_in = Input(shape=(None, ))
    train_model = Model(model.inputs + [o_in], model.outputs + [o_in])

    return model, train_model


class QuestionGeneration(AutoRegressiveDecoder):
    """通过beam search来生成问题。"""
    def __init__(self, models, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.models = models

    @AutoRegressiveDecoder.wraps(default_rtype='probas')
    def predict(self, inputs, output_ids, states):
        token_ids, segment_ids = inputs
        token_ids = np.concatenate([token_ids, output_ids], 1)
        segment_ids = np.concatenate([segment_ids, np.ones_like(output_ids)], 1)
        probas = list()
        for i in range(n):
            proba = self.models[i].predict([token_ids, segment_ids])[:, -1]
            probas.append(proba)

        return np.mean(np.concatenate(probas), axis=0, keepdims=True)

    def generate(self, passage, answer, topk=5):
        p_token_ids, _ = tokenizer.encode(passage, maxlen=max_p_len)
        a_token_ids, _ = tokenizer.encode(answer, maxlen=max_a_len)
        token_ids = p_token_ids + a_token_ids[1:]
        segment_ids = [0] * (len(p_token_ids) + len(a_token_ids[1:]))
        q_ids = self.beam_search([token_ids, segment_ids], topk)  # 基于beam search
        return tokenizer.decode(q_ids)

生成结果可以看出,采用中医药数据集经过五折交叉验证后生成的问题,在中医药领域表现良好,但是在《经济法教辅语料》的数据集上,生成的问题有一些并不能够被人读懂。这是因为在一个数据集上的训练数据具有一定的领域局限性。


而采用多数据集混合的数据训练的模型,模型生成的问题表现能力更佳。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/958397.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号