Mindspore 公开课 - GPT

GPT Task

在模型 finetune 中,需要根据不同的下游任务来处理输入,主要的下游任务可分为以下四类:

  • 分类(Classification):给定一个输入文本,将其分为若干类别中的一类,如情感分类、新闻分类等;
  • 蕴含(Entailment):给定两个输入文本,判断它们之间是否存在蕴含关系(即一个文本是否可以从另一个文本中推断出来);
  • 相似度(Similarity):给定两个输入文本,计算它们之间的相似度得分;
  • 多项选择题(Multiple choice):给定一个问题和若干个答案选项,选择最佳的答案。

Mindspore 公开课 - GPT_第1张图片
我们使用IMDb数据集,通过finetune GPT进行情感分类任务。

IMDb数据集是一个常用的情感分类数据集,其中包含50,000条影评文本,其中25,000条用作训练数据,另外25,000条用作测试数据。每个样本都有一个二元标签,表示影评的情感是正面还是负面。

import os

import mindspore
from mindspore.dataset import text, GeneratorDataset, transforms
from mindspore import nn

from mindnlp import load_dataset
from mindnlp.transforms import PadTransform, GPTTokenizer

from mindnlp.engine import Trainer, Evaluator
from mindnlp.engine.callbacks import CheckpointCallback, BestModelCallback
from mindnlp.metrics import Accuracy

数据预处理

# 加载数据集
imdb_train, imdb_test = load_dataset('imdb', shuffle=False)

通过load_dataset加载IMDb数据集后,我们需要对数据进行如下处理:

  • 将文本内容进行分词,并映射为对应的数字索引;
  • 统一序列长度:超过进行截断,不足通过占位符进行补全;
  • 按照分类任务的输入要求,在句首和句末分别添加Start与Extract占位符(此处用 表示);
  • 批处理。
import numpy as np

def process_dataset(dataset, tokenizer, max_seq_len=256, batch_size=32, shuffle=False):
    """数据集预处理"""
    def pad_sample(text):
        if len(text) + 2 >= max_seq_len:
            return np.concatenate(
                [np.array([tokenizer.bos_token_id]), text[: max_seq_len-2], np.array([tokenizer.eos_token_id])]
            )
        else:
            pad_len = max_seq_len - len(text) - 2
            return np.concatenate( 
                [np.array([tokenizer.bos_token_id]), text,
                 np.array([tokenizer.eos_token_id]),
                 np.array([tokenizer.pad_token_id] * pad_len)]
            )

    column_names = ["text", "label"]
    rename_columns = ["input_ids", "label"]

    if shuffle:
        dataset = dataset.shuffle(batch_size)

    dataset = dataset.map(operations=[tokenizer, pad_sample], input_columns="text")
    dataset = dataset.rename(input_columns=column_names, output_columns=rename_columns)
    dataset = dataset.batch(batch_size)

    return dataset

加载 GPT tokenizer,并添加上述使用到的 , , 占位符。

gpt_tokenizer = GPTTokenizer.from_pretrained('openai-gpt')

special_tokens_dict = {
    "bos_token": "",
    "eos_token": "",
    "pad_token": "",
}
num_added_toks = gpt_tokenizer.add_special_tokens(special_tokens_dict)

由于IMDb数据集本身不包含验证集,我们手动将其分割为训练和验证两部分,比例取0.7, 0.3。

imdb_train, imdb_val = imdb_train.split([0.7, 0.3])

dataset_train = process_dataset(imdb_train, gpt_tokenizer, shuffle=True)
dataset_val = process_dataset(imdb_val, gpt_tokenizer)
dataset_test = process_dataset(imdb_test, gpt_tokenizer)

模型训练

同BERT课件中的情感分类任务实现,这里我们依旧使用了混合精度。另外需要注意的一点是,由于在前序数据处理中,我们添加了3个特殊占位符,所以在token embedding中需要调整词典的大小(vocab_size + 3)。

from mindnlp.models import GPTForSequenceClassification
from mindnlp._legacy.amp import auto_mixed_precision

model = GPTForSequenceClassification.from_pretrained('openai-gpt', num_labels=2)
model.pad_token_id = gpt_tokenizer.pad_token_id
model.resize_token_embeddings(model.config.vocab_size + 3)
model = auto_mixed_precision(model, 'O1')

loss = nn.CrossEntropyLoss()
optimizer = nn.Adam(model.trainable_params(), learning_rate=2e-5)

metric = Accuracy()

ckpoint_cb = CheckpointCallback(save_path='checkpoint', ckpt_name='sentiment_model', epochs=1, keep_checkpoint_max=2)
best_model_cb = BestModelCallback(save_path='checkpoint', auto_load=True)

trainer = Trainer(network=model, train_dataset=dataset_train,
                  eval_dataset=dataset_val, metrics=metric,
                  epochs=3, loss_fn=loss, optimizer=optimizer, callbacks=[ckpoint_cb, best_model_cb], jit=True)

trainer.run(tgt_columns="label")

模型评估

evaluator = Evaluator(network=model, eval_dataset=dataset_test, metrics=metric)
evaluator.run(tgt_columns="label")

你可能感兴趣的:(gpt)