mindspore打卡22天之基于MindSpore通过GPT实现情感分类

基于MindSpore通过GPT实现情感分类

%%capture captured_output
# 实验环境已经预装了mindspore==2.2.14,如需更换mindspore版本,可更改下面mindspore的版本号
!pip uninstall mindspore -y
!pip install -i https://pypi.mirrors.ustc.edu.cn/simple mindspore==2.2.14
# 该案例在 mindnlp 0.3.1 版本完成适配,如果发现案例跑不通,可以指定mindnlp版本,执行`!pip install mindnlp==0.3.1`
!pip install mindnlp
!pip install jieba
%env HF_ENDPOINT=https://hf-mirror.com
imdb_ds = load_dataset('imdb', split=['train', 'test'])
imdb_train = imdb_ds['train']
imdb_test = imdb_ds['test']
Downloading readme: 0.00B [00:00, ?B/s]



Downloading data:   0%|          | 0.00/21.0M [00:00<?, ?B/s]



Downloading data:   0%|          | 0.00/20.5M [00:00<?, ?B/s]



Downloading data:   0%|          | 0.00/42.0M [00:00<?, ?B/s]



Generating train split:   0%|          | 0/25000 [00:00<?, ? examples/s]



Generating test split:   0%|          | 0/25000 [00:00<?, ? examples/s]



Generating unsupervised split:   0%|          | 0/50000 [00:00<?, ? examples/s]
imdb_train.get_dataset_size()
25000
import numpy as np

def process_dataset(dataset, tokenizer, max_seq_len=512, batch_size=4, shuffle=False):
    is_ascend = mindspore.get_context('device_target') == 'Ascend'
    def tokenize(text):
        if is_ascend:
            tokenized = tokenizer(text, padding='max_length', truncation=True, max_length=max_seq_len)
        else:
            tokenized = tokenizer(text, truncation=True, max_length=max_seq_len)
        return tokenized['input_ids'], tokenized['attention_mask']

    if shuffle:
        dataset = dataset.shuffle(batch_size)

    # map dataset
    dataset = dataset.map(operations=[tokenize], input_columns="text", output_columns=['input_ids', 'attention_mask'])
    dataset = dataset.map(operations=transforms.TypeCast(mindspore.int32), input_columns="label", output_columns="labels")
    # batch dataset
    if is_ascend:
        dataset = dataset.batch(batch_size)
    else:
        dataset = dataset.padded_batch(batch_size, pad_info={'input_ids': (None, tokenizer.pad_token_id),
                                                             'attention_mask': (None, 0)})

    return dataset
from mindnlp.transformers import GPTTokenizer
# tokenizer
gpt_tokenizer = GPTTokenizer.from_pretrained('openai-gpt')

# add sepcial token: <PAD>
special_tokens_dict = {
    "bos_token": "<bos>",
    "eos_token": "<eos>",
    "pad_token": "<pad>",
}
num_added_toks = gpt_tokenizer.add_special_tokens(special_tokens_dict)
---------------------------------------------------------------------------

JSONDecodeError                           Traceback (most recent call last)

Cell In[9], line 3
      1 from mindnlp.transformers import GPTTokenizer
      2 # tokenizer
----> 3 gpt_tokenizer = GPTTokenizer.from_pretrained('openai-gpt')
      5 # add sepcial token: <PAD>
      6 special_tokens_dict = {
      7     "bos_token": "<bos>",
      8     "eos_token": "<eos>",
      9     "pad_token": "<pad>",
     10 }


File ~/miniconda/envs/jupyter/lib/python3.9/site-packages/mindnlp/transformers/tokenization_utils_base.py:1723, in PreTrainedTokenizerBase.from_pretrained(cls, pretrained_model_name_or_path, cache_dir, force_download, local_files_only, token, mirror, *init_inputs, **kwargs)
   1720     else:
   1721         logger.info(f"loading file {file_path} from cache at {resolved_vocab_files[file_id]}")
-> 1723 return cls._from_pretrained(
   1724     resolved_vocab_files,
   1725     pretrained_model_name_or_path,
   1726     init_configuration,
   1727     *init_inputs,
   1728     cache_dir=cache_dir,
   1729     local_files_only=local_files_only,
   1730     _is_local=is_local,
   1731     **kwargs,
   1732 )


File ~/miniconda/envs/jupyter/lib/python3.9/site-packages/mindnlp/transformers/tokenization_utils_base.py:1923, in PreTrainedTokenizerBase._from_pretrained(cls, resolved_vocab_files, pretrained_model_name_or_path, init_configuration, token, cache_dir, local_files_only, _is_local, *init_inputs, **kwargs)
   1920 if "Fast" not in cls.__name__ and tokenizer_file is not None:
   1921     # This is for slow so can be done before
   1922     with open(tokenizer_file, encoding="utf-8") as tokenizer_file_handle:
-> 1923         tokenizer_file_handle = json.load(tokenizer_file_handle)
   1924         added_tokens = tokenizer_file_handle.pop("added_tokens")
   1925     for serialized_tokens in added_tokens:


File ~/miniconda/envs/jupyter/lib/python3.9/json/__init__.py:293, in load(fp, cls, object_hook, parse_float, parse_int, parse_constant, object_pairs_hook, **kw)
    274 def load(fp, *, cls=None, object_hook=None, parse_float=None,
    275         parse_int=None, parse_constant=None, object_pairs_hook=None, **kw):
    276     """Deserialize ``fp`` (a ``.read()``-supporting file-like object containing
    277     a JSON document) to a Python object.
    278 
   (...)
    291     kwarg; otherwise ``JSONDecoder`` is used.
    292     """
--> 293     return loads(fp.read(),
    294         cls=cls, object_hook=object_hook,
    295         parse_float=parse_float, parse_int=parse_int,
    296         parse_constant=parse_constant, object_pairs_hook=object_pairs_hook, **kw)


File ~/miniconda/envs/jupyter/lib/python3.9/json/__init__.py:346, in loads(s, cls, object_hook, parse_float, parse_int, parse_constant, object_pairs_hook, **kw)
    341     s = s.decode(detect_encoding(s), 'surrogatepass')
    343 if (cls is None and object_hook is None and
    344         parse_int is None and parse_float is None and
    345         parse_constant is None and object_pairs_hook is None and not kw):
--> 346     return _default_decoder.decode(s)
    347 if cls is None:
    348     cls = JSONDecoder


File ~/miniconda/envs/jupyter/lib/python3.9/json/decoder.py:337, in JSONDecoder.decode(self, s, _w)
    332 def decode(self, s, _w=WHITESPACE.match):
    333     """Return the Python representation of ``s`` (a ``str`` instance
    334     containing a JSON document).
    335 
    336     """
--> 337     obj, end = self.raw_decode(s, idx=_w(s, 0).end())
    338     end = _w(s, end).end()
    339     if end != len(s):


File ~/miniconda/envs/jupyter/lib/python3.9/json/decoder.py:355, in JSONDecoder.raw_decode(self, s, idx)
    353     obj, end = self.scan_once(s, idx)
    354 except StopIteration as err:
--> 355     raise JSONDecodeError("Expecting value", s, err.value) from None
    356 return obj, end


JSONDecodeError: Expecting value: line 1 column 1 (char 0)

GPTTokenizer.from_pretrained('openai-gpt') 这行代码是从Hugging Face的Transformers库中加载预训练的GPT(Generative Pretrained Transformer)模型的分词器。下面我会详细解释这一过程:

1. 检查本地缓存

首先,from_pretrained方法会检查是否有之前下载的预训练模型和分词器配置文件存在于本地缓存目录中。默认的缓存目录是~/.cache/huggingface/transformers(在Windows上可能是C:\Users\{username}\.cache\huggingface\transformers)。如果已经存在,它会直接加载这些文件。

2. 下载预训练资源

如果没有找到本地缓存,from_pretrained方法会从Hugging Face的Model Hub(模型仓库)下载预训练的GPT模型的分词器配置文件。这些文件通常包括:

  • vocab.json:包含了分词器的词汇表,每个token对应一个ID。
  • merges.txt:用于BPE(Byte Pair Encoding)分词算法,记录了字符对的合并规则。
  • config.json:包含了分词器的配置信息,如特殊token的定义等。

3. 构建分词器实例

一旦下载或读取了上述文件,GPTTokenizer类会使用这些文件初始化一个分词器实例。分词器会使用BPE算法将输入文本转化为一系列的token ID,这些ID可以直接用于预训练的GPT模型的输入。

4. 分词器方法

初始化后的GPTTokenizer实例提供了多种方法来处理文本,包括:

  • encode:将文本转化为token ID列表。
  • decode:将token ID列表转化为文本。
  • batch_encode_plus:批量处理多个文本,可以设置padding和truncation等参数。
  • prepare_for_model:为模型准备输入,包括添加特殊token、padding和truncation。

示例

假设我们要使用GPTTokenizer来处理一段文本:

from transformers import GPTTokenizer

# 加载预训练的GPT分词器
tokenizer = GPTTokenizer.from_pretrained('openai-gpt')

# 待分词的文本
text = "Hello, how are you doing today?"

# 对文本进行编码
encoded_input = tokenizer.encode(text, return_tensors='pt')

# 输出token ID列表
print(encoded_input)

# 解码token ID列表回文本
decoded_output = tokenizer.decode(encoded_input[0])
print(decoded_output)

在这个示例中,我们首先加载了预训练的GPT分词器,然后使用encode方法将一段文本转化为token ID列表,并使用decode方法将token ID列表还原为文本。通过这种方式,我们可以准备数据供GPT模型使用。

在自然语言处理(NLP)任务中,特别是在使用神经网络模型时,paddingtruncation是两个常见的预处理步骤,用于处理变长的文本序列,以便它们可以被模型以固定尺寸的批次(batches)处理。下面我将详细解释这两个概念:

Padding(填充)

定义:Padding 是在较短的序列末尾添加特殊标记(如 <PAD> 或 0),以使所有序列达到相同的长度。这是因为许多深度学习模型(如RNN、LSTM、Transformer等)要求输入具有固定的形状,而实际文本序列的长度往往是可变的。

作用

  • 允许模型以固定大小的批次处理不同长度的文本。
  • 防止模型需要为每个不同长度的序列动态调整其输入层。

使用场景

  • 当一个批次中的序列长度不一致时,需要进行padding。

Truncation(截断)

定义:Truncation 是在序列超出预定义的最大长度时,从序列的开头、中间或结尾裁剪掉多余的部分。这是为了避免过长的序列导致内存不足或计算成本过高。

作用

  • 控制输入序列的长度,以适应模型的输入限制。
  • 防止过长的序列占用过多的计算资源,特别是在GPU上。

使用场景

  • 当序列长度超过模型或硬件限制时,需要进行truncation。

结合使用

在实践中,paddingtruncation经常结合使用,以确保所有序列都符合模型的输入要求。例如,在处理一个批次的文本时,我们首先确定一个最大长度(通常是批次中最长序列的长度或一个预定义的值),然后对所有序列进行truncation,使其不超过这个长度,之后对所有较短的序列进行padding,直到它们达到这个最大长度。

实现示例

在使用Hugging Face的Transformers库时,GPTTokenizer和其他分词器提供了batch_encode_plusencode_plus方法,可以设置paddingtruncation参数。例如:

from transformers import GPTTokenizer

tokenizer = GPTTokenizer.from_pretrained('gpt2')
texts = ["This is a short sentence.", "This is a much longer sentence that might exceed the maximum length."]

# 对文本进行编码,设置padding和truncation
encoded_texts = tokenizer.batch_encode_plus(
    texts,
    padding='max_length',
    truncation=True,
    max_length=10,
    return_tensors='pt'
)

# 输出将包括输入ID和注意力掩码
print(encoded_texts['input_ids'])
print(encoded_texts['attention_mask'])

在这个示例中,padding='max_length'意味着所有的序列都将被填充到最大长度(在这里是10),而truncation=True则意味着任何超过10个token的序列将被截断。return_tensors='pt'表示返回的将是PyTorch的张量格式。

# split train dataset into train and valid datasets
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)
next(dataset_train.create_tuple_iterator())
from mindnlp.transformers import GPTForSequenceClassification
from mindspore.experimental.optim import Adam

# set bert config and define parameters for training
model = GPTForSequenceClassification.from_pretrained('openai-gpt', num_labels=2)
model.config.pad_token_id = gpt_tokenizer.pad_token_id
model.resize_token_embeddings(model.config.vocab_size + 3)

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

metric = Accuracy()

# define callbacks to save checkpoints
ckpoint_cb = CheckpointCallback(save_path='checkpoint', ckpt_name='gpt_imdb_finetune', epochs=1, keep_checkpoint_max=2)
best_model_cb = BestModelCallback(save_path='checkpoint', ckpt_name='gpt_imdb_finetune_best', auto_load=True)

trainer = Trainer(network=model, train_dataset=dataset_train,
                  eval_dataset=dataset_train, metrics=metric,
                  epochs=1, optimizer=optimizer, callbacks=[ckpoint_cb, best_model_cb],
                  jit=False)
trainer.run(tgt_columns="labels")
evaluator = Evaluator(network=model, eval_dataset=dataset_test, metrics=metric)
evaluator.run(tgt_columns="labels")

这段代码展示了如何使用MindNLP和MindSpore框架对IMDB电影评论数据集进行情感分析的端到端训练过程。下面是对代码逻辑的详细解析:

  1. 导入依赖库和数据集

    • 导入MindSpore、MindNLP的相关模块,包括数据加载、预处理、模型定义、优化器、训练器、评估器等。
    • 从MindNLP加载IMDB数据集,将其分为训练集和测试集。
  2. 数据预处理

    • 定义process_dataset函数,该函数接收一个数据集、一个分词器、最大序列长度和批大小作为参数。
    • 根据设备类型(Ascend或GPU/CPU)选择不同的分词方式。
    • 数据集进行shuffle、tokenization、类型转换和batching处理。
    • 使用GPTTokenizer进行文本的tokenization,添加特殊token,并对tokenizer进行更新。
    • 训练集和验证集通过split函数从原训练集中分离出来。
  3. 模型定义与配置

    • 从预训练的GPTForSequenceClassification模型创建一个实例,用于序列分类任务。
    • 更新模型配置,设置pad_token_id,并调整嵌入层大小以适应新加入的特殊token。
  4. 训练配置

    • 定义Adam优化器,设置学习率为2e-5。
    • 创建训练器Trainer,传入模型、训练数据集、评估数据集、评估指标、训练轮数、优化器和回调函数。
    • 设置两个回调函数:CheckpointCallback用于保存训练过程中的模型检查点,BestModelCallback用于保存最佳模型。
    • 设置训练器的jit模式为False,禁用自动图优化。
  5. 模型训练与评估

    • 使用trainer.run函数开始模型训练,指定目标列(标签)为"labels"。
    • 训练结束后,使用Evaluator类对测试数据集进行评估,同样指定目标列为"labels"。

这段代码的主要功能是利用预训练的GPT模型对IMDB数据集进行微调,以执行二分类情感分析任务。通过数据增强、模型微调、评估和模型保存,实现了一个完整的情感分析模型训练和评估流程。

print("yanggemindspore打卡22天之基于MindSpore通过GPT实现情感分类   2024  07 11")
yanggemindspore打卡22天之基于MindSpore通过GPT实现情感分类   2024  07 11

  • 24
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值