MindSpore学习实践——基于MindSpore的GPT2实现文本摘要

前言

GPT-2由GPT-1的结构改造(其实改造不大,还是transformer的decoder结构)而来并且将将参数量从1亿增加到15亿(和现在比还算小的)。其也是用于文本生成预测,根据输入的句子转换成向量然后再预测各个词的出现频率,最后使用argmax将其转换成argmax。该模型基于大量文本数据进行无监督训练,具备了生成高质量自然语言的能力,适合多种下游任务,包括文本生成、对话系统和文本分类等。

本节课我学到了什么?

  1. zero shot learning

本次课程讲到了zero-shot learning(零样本学习),本人不太了解,故查了一些资料学习。所谓zero-shot learning,就是根本没有提供任何示例,模型根据给定的指令理解任务。这使模型能够识别、分类或理解在训练过程中未见过的概念或类别。相比之下对于一些罕见或未知类别的样本,传统监督学习方法可能难以处理。也是因此,GPT-2可以在没有特定训练数据的情况下生成高质量的文本,即可以理解和生成从未见过的主题或风格的文本。

  1. task conditioning

语言模型能够使用相同的无监督模型学习多个任务。在传统的语言模型训练中,训练目标通常被表述为:
P(output∣input) P(\text{output}|\text{input}) P(outputinput)
然而,GPT-2的目标是使用相同的无监督模型学习多个任务。为了实现这一点,学习目标需要被修改为:
P(output∣input,task) P(\text{output}|\text{input}, \text{task}) P(outputinput,task)
其中模型被期望在相同的输入下由于任务的不同产生不同的输出。

task conditioning在GPT-2中的实现可以通过在输入序列的开始添加指示任务类型的标记、将任务信息编码为嵌入向量并注入在模型的某些层中(*可以帮助模型根据不同的任务调整其输出)、在训练过程中让模型学习到不同任务之间的共同点和差异,方便其区分并以及根据任务类型动态调整模型内部的信息流动,使得模型的不同部分专注于不同的任务等方式。

  1. GPT-2模型的结构方面

GPT-2最高有48层,同样使用了使用字节对(BPE)编码构建字典,字典的大小为50257,滑动窗口大小为1-24,batch_size为512,Layer Normalization移动到了每一块的输入部分,在每个self-attention之后额外添加了一个Layer Normalization。(层规范化(Layer Normalization)是一种在深度学习中常用的规范化技术,它的主要目的是帮助神经网络更快、更稳定地收敛)

代码实训

  1. 环境配置
#安装mindnlp 0.4.0套件
!pip install mindnlp==0.4.0
!pip uninstall soundfile -y
!pip install https://ms-release.obs.cn-north-4.myhuaweicloud.com/2.3.1/MindSpore/unified/aarch64/mindspore-2.3.1-cp39-cp39-linux_aarch64.whl --trusted-host ms-release.obs.cn-north-4.myhuaweicloud.com -i https://pypi.tuna.tsinghua.edu.cn/simple
  1. 数据集的加载与预处理
from mindnlp.utils import http_get
from mindspore.dataset import TextFileDataset
import json
import numpy as np
from mindnlp.transformers import BertTokenizer

# download dataset
url = 'https://download.mindspore.cn/toolkits/mindnlp/dataset/text_generation/nlpcc2017/train_with_summ.txt'
path = http_get(url, './')

# load dataset
dataset = TextFileDataset(str(path), shuffle=False)
dataset.get_dataset_size()

# split into training and testing dataset
mini_dataset, _ = dataset.split([0.001, 0.999], randomize=False)
train_dataset, test_dataset = mini_dataset.split([0.9, 0.1], randomize=False)

# preprocess dataset
def process_dataset(dataset, tokenizer, batch_size=4, max_seq_len=1024, shuffle=False):
    def read_map(text):
        data = json.loads(text.tobytes())
        return np.array(data['article']), np.array(data['summarization'])

    def merge_and_pad(article, summary):
        # tokenization
        # pad to max_seq_length, only truncate the article
        tokenized = tokenizer(text=article, text_pair=summary,
                              padding='max_length', truncation='only_first', max_length=max_seq_len)
        return tokenized['input_ids'], tokenized['input_ids']
    
    dataset = dataset.map(read_map, 'text', ['article', 'summary'])
    # change column names to input_ids and labels for the following training
    dataset = dataset.map(merge_and_pad, ['article', 'summary'], ['input_ids', 'labels'])

    dataset = dataset.batch(batch_size)
    if shuffle:
        dataset = dataset.shuffle(batch_size)

    return dataset

# GPT2无中文tokenizer,改用BertTokenizer替代。
tokenizer = BertTokenizer.from_pretrained('bert-base-chinese')
len(tokenizer)

train_dataset = process_dataset(train_dataset, tokenizer, batch_size=1)

next(train_dataset.create_tuple_iterator())
  1. 模型构建:构建GPT2ForSummarization模型
from mindnlp.core.nn import functional as F
from mindnlp.transformers import GPT2LMHeadModel

class GPT2ForSummarization(GPT2LMHeadModel):
    def forward(
        self,
        input_ids = None,
        attention_mask = None,
        labels = None,
    ):
        outputs = super().forward(input_ids=input_ids, attention_mask=attention_mask)
        shift_logits = outputs.logits[..., :-1, :]
        shift_labels = labels[..., 1:]
        # Flatten the tokens
        loss = F.cross_entropy(shift_logits.view(-1, shift_logits.shape[-1]), shift_labels.view(-1), ignore_index=tokenizer.pad_token_id)
        return (loss,)
  1. 模型训练
from mindspore import nn
from mindnlp.transformers import GPT2Config, GPT2LMHeadModel
from mindnlp.engine import Trainer
from mindnlp.engine import TrainingArguments

num_epochs = 1
warmup_steps = 100
learning_rate = 1.5e-4
max_grad_norm = 1.0
num_training_steps = num_epochs * train_dataset.get_dataset_size()

config = GPT2Config(vocab_size=len(tokenizer))
model = GPT2ForSummarization(config)

# 记录模型参数数量
print('number of model parameters: {}'.format(model.num_parameters()))

training_args = TrainingArguments(
    output_dir="gpt2_summarization",
    save_steps=train_dataset.get_dataset_size(),
    save_total_limit=3,
    logging_steps=1000,
    max_steps=num_training_steps,
    learning_rate=learning_rate,
    max_grad_norm=max_grad_norm,
    warmup_steps=warmup_steps
    
)

trainer = Trainer(
    model=model,
    args=training_args,
    train_dataset=train_dataset,
)

trainer.train()
  1. 模型推理
def process_test_dataset(dataset, tokenizer, batch_size=1, max_seq_len=1024, max_summary_len=100):
    def read_map(text):
        data = json.loads(text.tobytes())
        return np.array(data['article']), np.array(data['summarization'])

    def pad(article):
        tokenized = tokenizer(text=article, truncation=True, max_length=max_seq_len-max_summary_len)
        return tokenized['input_ids']

    dataset = dataset.map(read_map, 'text', ['article', 'summary'])
    dataset = dataset.map(pad, 'article', ['input_ids'])
    
    dataset = dataset.batch(batch_size)

    return dataset
    
batched_test_dataset = process_test_dataset(test_dataset, tokenizer, batch_size=1)

print(next(batched_test_dataset.create_tuple_iterator(output_numpy=True)))

model = GPT2LMHeadModel.from_pretrained('./gpt2_summarization/checkpoint-45', config=config)

model.set_train(False)
model.config.eos_token_id = model.config.sep_token_id
i = 0
for (input_ids, raw_summary) in batched_test_dataset.create_tuple_iterator():
    output_ids = model.generate(input_ids, max_new_tokens=50, num_beams=5, no_repeat_ngram_size=2)
    output_text = tokenizer.decode(output_ids[0].tolist())
    print(output_text)
    i += 1
    if i == 1:
        break

实训结果:

请添加图片描述

Logo

昇腾计算产业是基于昇腾系列(HUAWEI Ascend)处理器和基础软件构建的全栈 AI计算基础设施、行业应用及服务,https://devpress.csdn.net/organization/setting/general/146749包括昇腾系列处理器、系列硬件、CANN、AI计算框架、应用使能、开发工具链、管理运维工具、行业应用及服务等全产业链

更多推荐