2.5 实战演练之多项选择

目录

1 多项选择任务介绍

2 基于Transfromers的解决方案

2.1 数据预处理:

2.2 模型结构:

1)通过AutoModelForMultipleChoice定义后如何查看模型定义

 2)初始化模型__init__

3)前向传播过程 (forward)

2.3 数据集clue_C3

2.4 代码实战演练

1)导包

2)加载数据集

3)数据预处理

4)创建模型

5)创建评估函数

6)配置训练参数

7)创建训练器

8)模型训练

9)模型预测


1 多项选择任务介绍

不同于之前的机器阅读理解任务是“要从文本中总结答案”,多项选择任务的本质是“给定一个或多个文档,以及一个问题Q和对应的多个答案候选项,要求从候选项中输出一个答案”。

例子: 

不再需要像机器阅读理解任务一样定义起始位置和结束位置,本质是一个分类任务。

2 基于Transfromers的解决方案

2.1 数据预处理:

为了适应 BERT 的结构,每个问题每个选项 会被拼接在一起,作为一个序列输入。

这里,[CLS] 是 BERT 特殊的标记符,表示序列开始;[SEP] 是分隔符,用来区分问题和选项。

数据处理后我们得到的形状是 (batch_size, num_choices, sequence_length),表示每个问题有 num_choices 个选项,每个选项的输入序列长度为 sequence_length

2.2 模型结构:

1)通过AutoModelForMultipleChoice定义后如何查看模型定义

可以看到模型名称为BertForMultipleChoice。

方式一:直接导入模型,然后Crtl点击BertForMultipleChoice查看即可

方式二:我们只需要Crtl点击AutoModelForMultipleChoice,然后在上方路径中选择models\bert\modeling_bert.py即可查看源码

 2)初始化模型__init__

  • BERT主干模型self.bert = BertModel(config) 实例化了一个BERT模型,这部分负责处理输入的文本序列,生成上下文相关的词嵌入。
  • Dropout层self.dropout 用于防止模型过拟合,随机丢弃部分神经元,具体的丢弃概率通过 classifier_dropouthidden_dropout_prob 决定。
  • 分类头部self.classifier = nn.Linear(config.hidden_size, 1) 添加了一个线性分类器,它的输入是BERT模型的输出(hidden states),输出是每个选项的分数(logits),用于多项选择分类任务。由于任务是多选项分类,这个分类器输出一个值,表示当前选项被选择的概率。

3)前向传播过程 (forward)

输入数据的处理:在多项选择任务中,输入的数据格式是 (batch_size, num_choices, sequence_length),其中 num_choices 是选项的数量。模型需要将这种格式转化为 BERT 能处理的形状,即 (batch_size * num_choices, sequence_length)。这通过 input_ids.view(-1, input_ids.size(-1)) 来实现,同样的操作也适用于 attention_mask, token_type_ids 等。 

BERT模型处理

  • 将处理好的 input_ids, attention_mask, token_type_ids 等输入传给 self.bert() 方法。
  • BERT模型返回的是 outputs,其中包含了多个输出值。outputs[1] 通常是池化后的最后一层隐藏状态(pooled_output),表示整个序列的表示。

分类头部处理

  • 池化层输出:将 pooled_output 通过 Dropout 层,然后传给线性分类器。
  • 生成logits:线性分类器输出 logits,表示每个选项的得分。最后通过 logits.view(-1, num_choices) 将输出形状调整为 (batch_size, num_choices),方便后续计算损失。

损失计算

  • 如果 labels(正确答案标签)存在,则计算交叉熵损失(CrossEntropyLoss()),损失值用于训练过程中的反向传播。

2.3 数据集clue_C3

clue/clue · Datasets at Hugging Face

有的choice是4个,有的不足4个,不足4个的要填充一下

2.4 代码实战演练

1)导包

这块因为我们要对数据进行手动padding处理,所以就不需要导入DataCollator。

  • 使用 DataCollator:当你的输入数据长度不一致或需要批量化处理时,特别是在多项选择任务中,DataCollatorForMultipleChoice 可以帮助你处理动态填充和数据的批量化。
  • 不使用 DataCollator:如果你的数据已经经过填充且长度一致,可以直接将数据传递给模型。

2)加载数据集

简单查看一下,发现context是个列表形式,所以后续还要对其做拼接处理,至于test的answer是空的,所以需要pop出去

3)数据预处理

加载Tokenizer:

tokenizer = AutoTokenizer.from_pretrained("hfl/chinese-macbert-base")
tokenizer

数据处理:

这块要做的内容就是将原始的数据(包含["context", "quesiton", "choice", "answer"]的数据集)处理为符合Bert模型输入的样式。

按照我们之前提到过的Context+Question+Choice的方式,我们需要对其进行处理,加入examples包含1000条数据样本,我们对其处理先是得到了(4000,256)的shape,为了将其转换为符合Bert模型输入的形状(batch,choices,max_length),还需要对其进行转换得到(1000,4,256)的shape,至于其后续的(batch*choices,max_length)的shape是在模型内部完成的,我们就无需进行考虑了。

def process_function(examples):
    # examples, dict, keys: ["context", "quesiton", "choice", "answer"]
    # examples, 1000
    context = []
    question_choice = []
    labels = []
    for idx in range(len(examples["context"])):
        ctx = "\n".join(examples["context"][idx])
        question = examples["question"][idx]
        choices = examples["choice"][idx]
        for choice in choices:
            context.append(ctx)
            question_choice.append(question + " " + choice)
        if len(choices) < 4:
            for _ in range(4 - len(choices)):
                context.append(ctx)
                question_choice.append(question + " " + "不知道")
        labels.append(choices.index(examples["answer"][idx]))
    tokenized_examples = tokenizer(context, question_choice, truncation="only_first", max_length=256, padding="max_length")     # input_ids: 4000 * 256, 
    tokenized_examples = {k: [v[i: i + 4] for i in range(0, len(v), 4)] for k, v in tokenized_examples.items()}     # 1000 * 4 *256
    tokenized_examples["labels"] = labels
    return tokenized_examples
        

取前10条数据查看处理效果:

处理整体:

4)创建模型

5)创建评估函数

以准确率作为评估指标即可

6)配置训练参数

7)创建训练器

8)模型训练

9)模型预测

MultipleChoice 没有Pipeline来进行模型预测的,因此需要手写一个

from typing import Any
import torch


class MultipleChoicePipeline:

    def __init__(self, model, tokenizer) -> None:
        self.model = model
        self.tokenizer = tokenizer
        self.device = model.device

    def preprocess(self, context, quesiton, choices):
        cs, qcs = [], []
        for choice in choices:
            cs.append(context)
            qcs.append(quesiton + " " + choice)
        return tokenizer(cs, qcs, truncation="only_first", max_length=256, return_tensors="pt")

    def predict(self, inputs):
        inputs = {k: v.unsqueeze(0).to(self.device) for k, v in inputs.items()}
        return self.model(**inputs).logits

    def postprocess(self, logits, choices):
        predition = torch.argmax(logits, dim=-1).cpu().item()
        return choices[predition]

    def __call__(self, context, question, choices) -> Any:
        inputs = self.preprocess(context, question, choices)
        logits = self.predict(inputs)
        result = self.postprocess(logits, choices)
        return result

如果要涉及到多选等任务类型,就需要自己写决策的逻辑,需要改模型,一个是数据处理的标签,你要把正确的都放进去,另外就是Forward里面loss部分,具体选择什么loss需要确定下。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值