Huggingface Transformers(1)-Hugging Face官方课程

文章目录

*
课程介绍
1. [Transformer models](https://huggingface.co/course/chapter1?fw=pt)

+ 什么是自然语言处理?
+ pipeline(不常用 )
+ Transformers
+
* Transformer 模型由两部分组成:
* 语言模型:
+ Architectures vs. checkpoints
2. [Using 🤗 Transformers](https://huggingface.co/course/chapter2?fw=pt)

+ pipeline的背后原理
+
* 使用tokenizer进行预处理
* Full model
* AutoModel架构的输出
* from transformers import AutoModelForSequenceClassification
+ Model heads: Making sense out of numbers
+
* 处理model的输出,转化为概率
+ models
+
* 加载model
* 保存model
+ Tokenizers
+
* word-based
* character-based
* subword-based
* tokenizer
* 处理多个sequences
* tokenizer处理完整示例
3. [Fine-tuning a pretrained model](https://huggingface.co/course/chapter3?fw=pt)

+ 处理数据
+
* 使用tokenizer处理一对句子(模型需要一对输入,比如判断这对句子是否意思相同)
* 使用tokenizer处理多对句子
* 数据预处理完整代码
+ [Fine-tuning a model with the Trainer API](https://huggingface.co/course/chapter3/3?fw=pt)
+ 使用pytorch训练
+ 使用🤗 Accelerate加速
4. [Sharing models and tokenizers](https://huggingface.co/course/chapter4?fw=pt)

课程介绍

官方链接

Huggingface Transformers(1)-Hugging Face官方课程

经典模型的处理能力:

  1. RNN 10-40
  2. LSTM 50-100
  3. Transformer BERT 512 GPT 1024
  4. Transformer-XL,LXNet 3000-5000

word embedding

预训练模型 fine tune 方法小结

什么是自然语言处理?

  • Classifying whole sentences:评论情感分析,检测电子邮件是否为垃圾邮件,确定句子在语法上是否正确或判断两个句子在逻辑上是否相关。
  • Classifying each word in a sentence:识别句子的语法成分(名词、动词、形容词)或命名实体(人、地点、组织)。
  • Generating text content::用自动生成的文本完成提示,用屏蔽词填充文本中的空白。
  • Extracting an answer from a text: 给定问题和上下文,根据上下文中提供的信息提取问题的答案。
  • Generating a new sentence from an input text: 将文本翻译成另一种语言,文本摘要。

pipeline(不常用 )

官方示例代码

🤗 Transformers library中最基本的对象是pipeline. 它将模型与其必要的预处理和后处理步骤连接起来,使我们能够直接输入任何文本并获得可理解的答案:

from transformers import pipeline

classifier = pipeline("sentiment-analysis")
classifier("I've been waiting for a HuggingFace course my whole life.")

[{'label': 'POSITIVE', 'score': 0.9598047137260437}]

默认情况下,pipeline会选择一个特定的预训练模型,该模型已针对英语情感分析进行了微调。创建classifier对象时,将下载并缓存模型。如果您重新运行该命令,则将使用缓存的模型,无需再次下载模型。

将文本输入给pipeline,pipeline的处理涉及到以下三个主要步骤:

*
– 文本被预处理为model可以理解的格式。
– 预处理的输入被传递给model。
*
– model的预测是经过处理,转化为人可以理解的形式。

现在可以使用的pipelines

  • feature-extraction (get the vector representation of a text)fill-mask
  • ner (named entity recognition)
  • question-answering
  • sentiment-analysis
  • summarization
  • text-generation
  • translation
  • zero-shot-classification

Transformers

Transformer architecture是在2017年六月推出,当初的研究用于翻译任务。随后推出了几个有影响力的模型,如下图:

Huggingface Transformers(1)-Hugging Face官方课程

预训练模型的详细介绍,它们可以分为三类:

类 GPT(也称为 auto-regressive Transformer 模型)

类 BERT(也称为 auto-encoding Transformer 模型)

BART/T5 类(也称为 equence-to-sequence Transformer 模型)

; Transformer 模型由两部分组成:

  • 编码器(左):编码器接收输入并构建其表示(其特征)。这意味着模型经过优化以从输入中获取理解。
  • 解码器(右):解码器使用编码器的表示(特征)以及其他输入来生成目标序列。这意味着模型针对生成输出进行了优化。

encoder models
auto-encoding models

使用 Transformer 模型的编码器。在每个阶段,注意力层都可以访问初始句子中的所有单词。这些模型通常被描述为具有”双向”注意力。这些模型的预训练通常围绕着以某种方式破坏给定的句子(例如,通过随机屏蔽其中的单词)并让模型找到或重建初始句子。ALBERT, BERT, DistilBERT, ELECTRA, RoBERTa句子分类,命名实体识别(以及更一般的单词分类)和提取式问答。decoder modles
auto-regressive models

使用 Transformer 模型的解码器。在每个阶段,对于给定的单词,注意力层只能访问句子中位于它之前的单词。解码器模型的预训练通常围绕预测句子中的下一个单词。CTRL, GPT, GPT-2, Transformer XL文本生成。encoder-decoder models
sequence-to-sequence models

使用 Transformer 架构的两个部分。在每个阶段,编码器的注意力层可以访问初始句子中的所有单词,而解码器的注意力层只能访问位于输入中给定单词之前的单词。这些模型的预训练可以使用编码器或解码器模型的目标来完成,但通常涉及一些更复杂的东西。例如,T5是通过用一个掩码特殊单词替换随机范围的文本(可以包含几个单词)进行预训练的,然后目标是预测这个掩码单词替换的文本。。BART, T5, Marian, mBART摘要、翻译或生成式问答。

语言模型:

MLM: masked language modeling 预测句子中的掩码词。

Huggingface Transformers(1)-Hugging Face官方课程

CLM: causal language modeling 根据当前的文本预测下一个词。

Huggingface Transformers(1)-Hugging Face官方课程

; Architectures vs. checkpoints

  • Architecture: 这是模型的骨架—模型中每一层和每个操作的定义。
  • Checkpoints: 这些是将在给定架构中加载的权重。
  • Model: 这是一个总称,不像”架构”或”检查点”那样精确:它可以同时表示两者。

官方示例代码

pipeline的背后原理

pipeline将三个步骤组合在一起:预处理、通过模型传递输入和后处理:流程见下图

from transformers import pipeline

classifier = pipeline("sentiment-analysis")
classifier([
    "I've been waiting for a HuggingFace course my whole life.",
    "I hate this so much!",
])

[{'label': 'POSITIVE', 'score': 0.9598047137260437},
 {'label': 'NEGATIVE', 'score': 0.9994558095932007}]

Huggingface Transformers(1)-Hugging Face官方课程

使用tokenizer进行预处理

与其他神经网络一样,Transformer 模型不能直接处理原始文本,因此我们管道的第一步是将文本输入转换为模型可以理解的数字(如下图流程)。为此,我们使用了一个tokenizer,它将负责:

  • 将输入拆分为称为标记的单词、子词或符号(如标点符号)。
  • 将每个标记映射到一个整数。
  • 添加可能对模型有用的其他输入。

Huggingface Transformers(1)-Hugging Face官方课程

所有这些预处理都需要以与模型预训练时完全相同的方式完成,因此我们首先需要从Model Hub下载该信息。为此,我们使用 AutoTokenizer类及其 from_pretrained方法。使用我们模型的检查点名称,它将自动获取与模型的标记器关联的数据并将其缓存。

由于 sentiment-analysis管道的默认检查点是 distilbert-base-uncased-finetuned-sst-2-english(您可以在此处查看其模型卡),我们运行以下命令:

from transformers import AutoTokenizer

checkpoint = "distilbert-base-uncased-finetuned-sst-2-english"
tokenizer = AutoTokenizer.from_pretrained(checkpoint)

raw_inputs = [
    "I've been waiting for a HuggingFace course my whole life.",
    "I hate this so much!",
]

inputs = tokenizer(raw_inputs, padding=True, truncation=True, return_tensors="pt")
print(inputs)

{
    'input_ids': tensor([
        [  101,  1045,  1005,  2310,  2042,  3403,  2005,  1037, 17662, 12172, 2607,  2026,  2878,  2166,  1012,   102],
        [  101,  1045,  5223,  2023,  2061,  2172,   999,   102,     0,     0,     0,     0,     0,     0,     0,     0]
    ]),
    'attention_mask': tensor([
        [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
        [1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0]
    ])
}

{'input_ids': [[101, 1045, 1005, 2310, 2042, 3403, 2005, 1037, 17662, 12172, 2607, 2026, 2878, 2166, 1012, 102], [101, 1045, 5223, 2023, 2061, 2172, 999, 102, 0, 0, 0, 0, 0, 0, 0, 0]], 'attention_mask': [[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0]]}

输出本身是一个包含两个键的字典,input_ids和attention_mask。

input_ids包含两行整数(每个句子一个),它们是每个句子中标记的唯一标识符。attention_mask是具有与input_ids张量完全相同形状的张量,填充0和1:1表示应注意的相应位置的标记,0表示不应注意的相应位置的标记(即,模型的 attention layers 应忽略它们)。

Full model

Huggingface Transformers(1)-Hugging Face官方课程

; AutoModel架构的输出

注意:🤗 Transformers 模型的输出表现得像namedtuples 或字典。您可以通过属性(就像我们所做的那样)或键 ( outputs[“last_hidden_state”])访问元素,如果您确切地知道要查找的内容在哪里,甚至可以通过索引访问元素outputs[0]。

AutoModel这个架构只包含基本的 Transformer 模块:给定一些输入,它输出我们称之为隐藏层的东西,也称为 features。对于每个模型输入,我们将得到一个高维向量,表示Transformer 模型对该输入的上下文理解,可以理解成是做了word embedding。

补充:对于不同架构输出会不一样:

  1. model = AutoModel.from_pretrained("bert-base-chinese"),输出为BaseModelOutputWithPoolingAndCrossAttentions,包含’last_hidden_state’和’pooler_output’两个元素。其中’last_hidden_state’的形状是(batch size,sequence length,768),’pooler_output’的形状是(batch size,768)。

pooler output是取[CLS]标记处对应的向量后面接个全连接再接tanh激活后的输出。

虽然这些隐藏状态本身就很有用,但它们通常是模型另一部分(称为head )的输入。在pipeline那一节中,可以使用相同的体系结构执行不同的任务,是因为这些任务中的每一个都有与之关联的不同头。

  1. model = AutoModelForMaskedLM.from_pretrained("bert-base-chinese"),输出为MaskedLMOutput,包含’logits’元素,形状为[batch size,sequence length,21128],21128是’vocab_size’。
  2. model = AutoModelForTokenClassification.from_pretrained( "bert-base-chinese"),输出为TokenClassifierOutput,包含’logits’元素,形状为[batch size,sequence length,2]。
from transformers import AutoModel

checkpoint = "distilbert-base-uncased-finetuned-sst-2-english"
model = AutoModel.from_pretrained(checkpoint)

outputs = model(**inputs)

print(outputs)

BaseModelOutput(last_hidden_state=tensor([[[-0.1798,  0.2333,  0.6321,  ..., -0.3017,  0.5008,  0.1481],
         [ 0.2758,  0.6497,  0.3200,  ..., -0.0760,  0.5136,  0.1329],
         [ 0.9046,  0.0985,  0.2950,  ...,  0.3352, -0.1407, -0.6464],
         ...,
         [ 0.1466,  0.5661,  0.3235,  ..., -0.3376,  0.5100, -0.0561],
         [ 0.7500,  0.0487,  0.1738,  ...,  0.4684,  0.0030, -0.6084],
         [ 0.0519,  0.3729,  0.5223,  ...,  0.3584,  0.6500, -0.3883]],

        [[-0.2937,  0.7283, -0.1497,  ..., -0.1187, -1.0227, -0.0422],
         [-0.2206,  0.9384, -0.0951,  ..., -0.3643, -0.6605,  0.2407],
         [-0.1536,  0.8987, -0.0728,  ..., -0.2189, -0.8528,  0.0710],
         ...,
         [-0.3017,  0.9002, -0.0200,  ..., -0.1082, -0.8412, -0.0861],
         [-0.3338,  0.9674, -0.0729,  ..., -0.1952, -0.8181, -0.0634],
         [-0.3454,  0.8824, -0.0426,  ..., -0.0993, -0.8329, -0.1065]]],
       grad_fn=<NativeLayerNormBackward>), hidden_states=None, attentions=None)

print(outputs.last_hidden_state.shape)

torch.Size([2, 16, 768])

Huggingface Transformers(1)-Hugging Face官方课程

注意:🤗 Transformers 模型的输出表现得像namedtuples 或字典。您可以通过属性(就像我们所做的那样)或键 ( outputs[“last_hidden_state”])访问元素,如果您确切地知道要查找的内容在哪里,甚至可以通过索引访问元素outputs[0]。

from transformers import AutoModelForSequenceClassification

Model heads: Making sense out of numbers

模型头将隐藏状态的高维向量作为输入,并将它们投影到不同的维度上。它们通常由一个或几个线性层组成:

Huggingface Transformers(1)-Hugging Face官方课程

Transformer 模型的输出直接送到模型头部进行处理。

在此图中,transformer network由其嵌入层和后续层表示。嵌入层将标记化输入中的每个输入 ID 转换为表示关联标记的向量。随后的层使用注意力机制操纵这些向量以产生句子的最终表示。

&#x1F917; Transformers &#x4E2D;&#x6709;&#x8BB8;&#x591A;&#x4E0D;&#x540C;&#x7684;&#x67B6;&#x6784;&#x53EF;&#x7528;&#xFF0C;&#x6BCF;&#x4E00;&#x79CD;&#x67B6;&#x6784;&#x90FD;&#x56F4;&#x7ED5;&#x7740;&#x5904;&#x7406;&#x7279;&#x5B9A;&#x4EFB;&#x52A1;&#x800C;&#x8BBE;&#x8BA1;&#x3002;&#x8FD9;&#x662F;&#x4E00;&#x4E2A;&#x975E;&#x8BE6;&#x5C3D;&#x5217;&#x8868;&#xFF1A;
  • *Model (retrieve the hidden states)
  • *ForCausalLM
  • *ForMaskedLM
  • *ForMultipleChoice
  • *ForQuestionAnswering
  • *ForSequenceClassification
  • *ForTokenClassification
  • and others 🤗 对于我们上文的示例,我们将需要一个带有序列分类头的模型(能够将句子分类为正面或负面)。所以,我们不会使用这个AutoModel类,而是使用AutoModelForSequenceClassification:
from transformers import AutoModelForSequenceClassification

checkpoint = "distilbert-base-uncased-finetuned-sst-2-english"
model = AutoModelForSequenceClassification.from_pretrained(checkpoint)
outputs = model(**inputs)

print(outputs.logits.shape)

torch.Size([2, 2])

print(outputs)

SequenceClassifierOutput(loss=None, logits=tensor([[-1.5607,  1.6123],
        [ 4.1692, -3.3464]], grad_fn=<AddmmBackward>), hidden_states=None, attentions=None)

处理model的输出,转化为概率

我们的模型预测第一句为[-1.5607,1.6123],第二句为[4.1692,-3.3464]。这些不是概率,而是logits,即模型最后一层输出的原始的、非标准化的分数。要转换为概率,它们需要经过softmax(所有🤗transformers模型都会输出logits,因为用于训练的损失函数通常会将最后一个激活函数(如SoftMax)与实际损失函数(如交叉熵)融合在一起):

import torch

predictions = torch.nn.functional.softmax(outputs.logits, dim=-1)

print(predictions)

tensor([[4.0195e-02, 9.5980e-01],
        [9.9946e-01, 5.4418e-04]], grad_fn=<SoftmaxBackward>)

现在我们可以看到模型预测[0.0402, 0.9598]第一个句子和[0.9995, 0.0005]第二个句子。这些是可识别的概率分数。

要获得每个位置对应的标签,我们可以检查id2label模型配置的属性

model.config.id2label

{0: 'NEGATIVE', 1: 'POSITIVE'}

models

Huggingface Transformers(1)-Hugging Face官方课程

; 加载model

我们可以使用 from_pretrained方法,来加载一个已经训练好的 Transformer 模型。

from transformers import BertModel

model = BertModel.from_pretrained("bert-base-cased")

保存model

保存模型就像加载模型一样简单——我们使用save_pretrained方法,它类似于from_pretrained方法:

model.save_pretrained("directory_on_my_computer")

该命令会将 config.json pytorch_model.bin两个文件保存在硬盘。

  • config.json文件,构建model architecture所需的属性。此文件还包含一些元数据,例如上次保存 checkpoint时使用的🤗Transformers版本。文件内容如下:
{
  "_name_or_path": "bert-base-cased",
  "architectures": [
    "BertModel"
  ],
  "attention_probs_dropout_prob": 0.1,
  "gradient_checkpointing": false,
  "hidden_act": "gelu",
  "hidden_dropout_prob": 0.1,
  "hidden_size": 768,
  "initializer_range": 0.02,
  "intermediate_size": 3072,
  "layer_norm_eps": 1e-12,
  "max_position_embeddings": 512,
  "model_type": "bert",
  "num_attention_heads": 12,
  "num_hidden_layers": 12,
  "pad_token_id": 0,
  "position_embedding_type": "absolute",
  "transformers_version": "4.8.1",
  "type_vocab_size": 2,
  "use_cache": true,
  "vocab_size": 28996
}
  • pytorch_model.bin文件称为状态字典;它包含模型的所有权重。configuration是了解model architecture所必需的,而模型权重是模型的参数。

Tokenizers

参考链接

Tokenizers:将文本转换为模型可以处理的数据。模型只能处理数字,因此Tokenizers需要将我们的文本输入转换为数字数据。tokenizer 主要分为如图所示三类:

Huggingface Transformers(1)-Hugging Face官方课程

; word-based

Huggingface Transformers(1)-Hugging Face官方课程

采用word-based vocabulary,英语有17W的单词。中文有2W个字,超过37.5W个词。

character-based

Huggingface Transformers(1)-Hugging Face官方课程

word-based和character-based tokenization的缺点

Huggingface Transformers(1)-Hugging Face官方课程

; subword-based

Huggingface Transformers(1)-Hugging Face官方课程

原则:

  • 经常使用的words不应该被切分成更小的subwords。
  • 罕见的words应该被分解成有意义的subwords。

Huggingface Transformers(1)-Hugging Face官方课程

tokenizers小结(差不多就这样吧)

其他技术

  • Byte-level BPE, as used in GPT-2
  • WordPiece, as used in BERT
  • SentencePiece or Unigram, as used in several multilingual models

Huggingface Transformers(1)-Hugging Face官方课程

; tokenizer

from transformers import AutoTokenizer

tokenizer = AutoTokenizer.from_pretrained("bert-base-cased")

tokenizer("Using a Transformer network is simple")

{'input_ids': [101, 7993, 170, 11303, 1200, 2443, 1110, 3014, 102],
 'token_type_ids': [0, 0, 0, 0, 0, 0, 0, 0, 0],
 'attention_mask': [1, 1, 1, 1, 1, 1, 1, 1, 1]}

tokenizer将文本转换为数字称为编码。编码分两步完成:标记化,然后转换为输入 ID。

第一步是将文本拆分为单词(或单词的一部分、标点符号等),通常称为标记。有多个规则可以管理该过程,这就是为什么我们需要使用模型名称来实例化分词器,以确保我们使用模型预训练时使用的相同规则。

第二步是将这些标记转换为数字,这样我们就可以用它们构建一个张量并将它们提供给模型。为此,分词器有一个词汇表,这是我们在使用from_pretrained方法实例化它时下载的部分。同样,我们需要使用模型预训练时使用的相同词汇。

Huggingface Transformers(1)-Hugging Face官方课程

tokenizer方法实现了tokenization,处理special tokens和转化为input ids三个过程。

我们可以使用 tokenizer.tokenize(sequence)来实现tokenization这个过程,输出是 list of strings, or tokens。使用 tokenizer.convert_tokens_to_ids(tokens)来实现将tokens转换为ids。

注意:tokenizer(sequence)方法,可能会在开头添加[CLS]结尾添加[SEP]等特殊词。这是因为模型是用这些进行预训练的,所以为了获得相同的推理结果,我们还需要添加它们。注意有些ckeckpoint不加特殊词,或者加不同的词;模型也可以仅在开头或仅在结尾添加这些特殊词。在任何情况下,tokenizer()都会自动处理这些。。。而使用tokenizer.tokenize(sequence)则不会处理这些。

from transformers import AutoTokenizer
tokenizer = AutoTokenizer.from_pretrained("bert-base-cased")

sequence = "Using a Transformer network is simple"
tokens = tokenizer.tokenize(sequence)

print(tokens)

['Using', 'a', 'Trans', '##former', 'network', 'is', 'simple']

ids = tokenizer.convert_tokens_to_ids(tokens)
print(ids)

[7993, 170, 13809, 23763, 2443, 1110, 3014]

解码:从词汇索引中,我们想要得到一个字符串。这可以通过以下decode方法完成:

decoded_string = tokenizer.decode([7993, 170, 11303, 1200, 2443, 1110, 3014])
print(decoded_string)

Using a transformer network is simple

请注意,该decode方法不仅将索引转换回标记,还将属于相同单词的标记组合在一起以生成可读的句子。当我们使用预测新文本的模型(从提示生成的文本,或序列到序列问题(如翻译或摘要))时,这种行为将非常有用。

处理多个sequences

import torch
from transformers import AutoTokenizer, AutoModelForSequenceClassification

checkpoint = "distilbert-base-uncased-finetuned-sst-2-english"
tokenizer = AutoTokenizer.from_pretrained(checkpoint)
model = AutoModelForSequenceClassification.from_pretrained(checkpoint)

sequence = "I've been waiting for a HuggingFace course my whole life."

tokens = tokenizer.tokenize(sequence)
ids = tokenizer.convert_tokens_to_ids(tokens)
input_ids = torch.tensor(ids)

model(input_ids)

Huggingface Transformers(1)-Hugging Face官方课程

报错原因是我们向model发送了单个序列,而 🤗 Transformers model默认需要多个句子即2维张量。

修改方法:

添加一个维度 input_ids = torch.tensor([ids])

对于不同长度的序列,需要进行截断或者填充。可以使用 tokenizer.pad_token_id找到padding token ID 。

tokenizer处理完整示例

官方代码

tokenizer能将输出处理为特定框架的tensor— “pt” returns PyTorch tensors, “tf” returns TensorFlow tensors, and “np” returns NumPy arrays。

import torch
from transformers import AutoTokenizer, AutoModelForSequenceClassification

checkpoint = "distilbert-base-uncased-finetuned-sst-2-english"
tokenizer = AutoTokenizer.from_pretrained(checkpoint)
model = AutoModelForSequenceClassification.from_pretrained(checkpoint)
sequences = [
  "I've been waiting for a HuggingFace course my whole life.",
  "So have I!"
]

tokens = tokenizer(sequences, padding=True, truncation=True, return_tensors="pt")
output = model(**tokens)

处理数据

官方代码

下面是我们如何在 PyTorch 中训练一个批次的序列分类器:

import torch
from transformers import AdamW, AutoTokenizer, AutoModelForSequenceClassification

checkpoint = "bert-base-uncased"
tokenizer = AutoTokenizer.from_pretrained(checkpoint)
model = AutoModelForSequenceClassification.from_pretrained(checkpoint)
sequences = [
    "I've been waiting for a HuggingFace course my whole life.",
    "This course is amazing!",
]
batch = tokenizer(sequences, padding=True, truncation=True, return_tensors="pt")

batch["labels"] = torch.tensor([1, 1])

optimizer = AdamW(model.parameters())
loss = model(**batch).loss
loss.backward()
optimizer.step()

当然,仅仅在两个句子上训练模型不会产生很好的结果。为了获得更好的结果,您需要准备更大的数据集。

在下文中在本节中,我们将使用MRPC(Microsoft Research Paraphase Corpus)数据集作为示例,该数据集在William B.Dolan和Chris Brockett的一篇论文中介绍。该数据集由5801对句子组成,并有一个标签表明它们是否是意译(即,如果两个句子的意思是相同的)。我们选择了它,因为它是一个很小的数据集,因此很容易在上面进行训练。通过以下代码获取该数据集:

from datasets import load_dataset

raw_datasets = load_dataset("glue", "mrpc")
raw_datasets

DatasetDict({
    train: Dataset({
        features: ['idx', 'label', 'sentence1', 'sentence2'],
        num_rows: 3668
    })
    validation: Dataset({
        features: ['idx', 'label', 'sentence1', 'sentence2'],
        num_rows: 408
    })
    test: Dataset({
        features: ['idx', 'label', 'sentence1', 'sentence2'],
        num_rows: 1725
    })
})

raw_train_dataset = raw_datasets["train"]
raw_train_dataset[0]

{'idx': 0,
 'label': 1,
 'sentence1': 'Amrozi accused his brother , whom he called " the witness " , of deliberately distorting his evidence .',
 'sentence2': 'Referring to him as only " the witness " , Amrozi accused his brother of deliberately distorting his evidence .'}

raw_train_dataset.features

{'idx': Value(dtype='int32', id=None),
 'label': ClassLabel(num_classes=2, names=['not_equivalent', 'equivalent'], names_file=None, id=None),
 'sentence1': Value(dtype='string', id=None),
 'sentence2': Value(dtype='string', id=None)}

我们得到一个DatasetDict包含训练集、验证集和测试集的对象。每一个都包含几列(sentence1、sentence2、label和idx)和可变数量的行,它们是每个集合中元素的数量(因此,训练集中有 3668 对句子,验证集中有 408 对,还有 1725在测试集中)。

使用tokenizer处理一对句子(模型需要一对输入,比如判断这对句子是否意思相同)

Huggingface Transformers(1)-Hugging Face官方课程

Huggingface Transformers(1)-Hugging Face官方课程

token_type_ids是告诉模型输出的哪一部分是第一句,哪一部分是第二句。

请注意,如果选择不同的checkpoint,则在标记化的输入中不一定有 token_type_ids(例如,如果您使用DistilBERT模型,则不会返回它们)。只有当模型知道如何处理它们时,它们才会被return,因为它在预训期间看到了它们。在这里,Bert预先接受了token type IDs的训练,在前文谈到的 masked language modeling objective目标之上,Bert还有一个额外的目标,称为下一句预测。这项任务的目标是对句子对之间的关系进行建模。对于下一句预测,模型被提供句子对(带有随机掩蔽的标记),并被要求预测第二句是否在第一句之后。

通常,我们不需要担心标记化输入中是否有 token_type_ids:只要您对标记器和模型使用相同的检查点,一切都会自动处理。

; 使用tokenizer处理多对句子

Huggingface Transformers(1)-Hugging Face官方课程

注意:送入tokenizer的两个list的对应关系。如上图的代码,’my name is sylvain’和’i work at hugging face’是一对。输出解释看下面两个图。

Huggingface Transformers(1)-Hugging Face官方课程

Huggingface Transformers(1)-Hugging Face官方课程
from datasets import load_dataset

raw_datasets = load_dataset("glue", "mrpc")

tokenized_dataset = tokenizer(
    raw_datasets["train"]["sentence1"],
    raw_datasets["train"]["sentence2"],
    padding=True,
    truncation=True,
    return_tensors = 'pt'
)

上面的示例代码在内存使用上有缺点,为此我们使用Dataset.map()方法,该方法提供了更大的灵活性。如果我们需要完成更多的预处理而不仅仅是tokenization。该map方法通过在数据集的每个元素上应用一个函数来工作,所以让我们定义一个函数tokenizes our inputs:

from datasets import load_dataset

raw_datasets = load_dataset("glue", "mrpc")

def tokenize_function(example):
    return tokenizer(example["sentence1"], example["sentence2"], truncation=True)

tokenized_datasets = raw_datasets.map(tokenize_function, batched=True)

🤗 Datasets library 应用这种处理的方式是向数据集添加新字段,预处理函数返回的字典中的每个键对应一个字段。处理后的raw_datasets内容如下:

DatasetDict({
    train: Dataset({
        features: ['attention_mask', 'idx', 'input_ids', 'label', 'sentence1', 'sentence2', 'token_type_ids'],
        num_rows: 3668
    })
    validation: Dataset({
        features: ['attention_mask', 'idx', 'input_ids', 'label', 'sentence1', 'sentence2', 'token_type_ids'],
        num_rows: 408
    })
    test: Dataset({
        features: ['attention_mask', 'idx', 'input_ids', 'label', 'sentence1', 'sentence2', 'token_type_ids'],
        num_rows: 1725
    })
})

Dataset.map通过传递num_proc参数来应用预处理函数时,您甚至可以使用多线程。我们在这里没有这样做,因为 🤗 Tokenizers 库已经使用多个线程来更快地处理我们的样本,但是如果您没有使用由该库支持的tokenizer,可以通过num_proc参数加快预处理。

数据预处理完整代码

from datasets import load_dataset

raw_datasets = load_dataset("glue", "mrpc")

def tokenize_function(example):
    return tokenizer(example["sentence1"], example["sentence2"], truncation=True)

tokenized_datasets = raw_datasets.map(tokenize_function, batched=True)

tokenized_datasets = tokenized_datasets.remove_columns(
    ["sentence1", "sentence2", "idx"]
)

tokenized_datasets = tokenized_datasets.rename_column("label", "labels")

tokenized_datasets.set_format("torch")

tokenized_datasets["train"].column_names

[ 'attention_mask','input_ids','labels','token_type_ids' ]

在 PyTorch 中,负责将一批样本放在一起的函数称为collate_fn函数。这是您在构建DataLoader时可以传递的参数,默认值是一个函数,它只会将您的样本转换为 PyTorch 张量并连接它们(如果您的元素是列表、元组或字典,则递归)。在我们的情况下这是不可能的,因为我们拥有的输入不会全部具有相同的大小。我们特意推迟了填充,只在每批次必要时应用它,避免过长的输入和大量的填充。这将大大加快训练速度,但请注意,如果您在 TPU 上进行训练,它可能会导致问题——TPU 更喜欢固定形状。

Huggingface Transformers(1)-Hugging Face官方课程

为了能动态填充,我们在tokenize_function函数中没有做padding处理,在之后的data_collator函数做了padding。

from transformers import DataCollatorWithPadding

data_collator = DataCollatorWithPadding(tokenizer=tokenizer)

Huggingface Transformers(1)-Hugging Face官方课程

huggingface transformers使用指南1

huggingface transformers使用指南2

Huggingface Transformers(1)-Hugging Face官方课程
from datasets import load_dataset
from transformers import AutoTokenizer, DataCollatorWithPadding
from transformers import TrainingArguments
from transformers import AutoModelForSequenceClassification
from transformers import Trainer

raw_datasets = load_dataset("glue", "mrpc")
checkpoint = "bert-base-uncased"
tokenizer = AutoTokenizer.from_pretrained(checkpoint)

def tokenize_function(example):
    return tokenizer(example["sentence1"], example["sentence2"], truncation=True)

tokenized_datasets = raw_datasets.map(tokenize_function, batched=True)
data_collator = DataCollatorWithPadding(tokenizer=tokenizer)

training_args = TrainingArguments("test-trainer")

model = AutoModelForSequenceClassification.from_pretrained(checkpoint, num_labels=2)

trainer = Trainer(
    model,
    training_args,
    train_dataset=tokenized_datasets["train"],
    eval_dataset=tokenized_datasets["validation"],
    data_collator=data_collator,
    tokenizer=tokenizer,
)

trainer.train()

上述代码每500步报告一次训练损失,但是不会报告性能如何。这是因为:

  1. 我们没有通过将 evaluation_strategy设置为“steps” (评估每个评估步骤)或“epoch”(在每个时期结束时评估)来告诉 Trainer在训练期间进行评估。
  2. 我们没有为 Trainer提供 compute_metrics函数来在评估期间计算指标(否则评估只会打印loss,这不是一个非常直观的数字)。

代码进行如下修改:

def compute_metrics(eval_preds):
    metric = load_metric("glue", "mrpc")
    logits, labels = eval_preds
    predictions = np.argmax(logits, axis=-1)
    return metric.compute(predictions=predictions, references=labels)

training_args = TrainingArguments("test-trainer", evaluation_strategy="epoch")
model = AutoModelForSequenceClassification.from_pretrained(checkpoint, num_labels=2)

trainer = Trainer(
    model,
    training_args,
    train_dataset=tokenized_datasets["train"],
    eval_dataset=tokenized_datasets["validation"],
    data_collator=data_collator,
    tokenizer=tokenizer,
    compute_metrics=compute_metrics
)

使用训练的模型预测:

predictions = trainer.predict(tokenized_datasets["validation"])
print(predictions.predictions.shape, predictions.label_ids.shape)

使用pytorch训练

Huggingface Transformers(1)-Hugging Face官方课程

官方代码

from datasets import load_dataset
from transformers import AutoTokenizer, DataCollatorWithPadding
from torch.utils.data import DataLoader
from transformers import AutoModelForSequenceClassification
from transformers import AdamW
from transformers import get_scheduler
import torch
from tqdm.auto import tqdm
from datasets import load_metric

device = torch.device("cuda") if torch.cuda.is_available() else torch.device("cpu")

raw_datasets = load_dataset("glue", "mrpc")
checkpoint = "bert-base-uncased"
tokenizer = AutoTokenizer.from_pretrained(checkpoint)

def tokenize_function(example):
    return tokenizer(example["sentence1"], example["sentence2"], truncation=True)

tokenized_datasets = raw_datasets.map(tokenize_function, batched=True)
data_collator = DataCollatorWithPadding(tokenizer=tokenizer)

tokenized_datasets = tokenized_datasets.remove_columns(
    ["sentence1", "sentence2", "idx"]
)
tokenized_datasets = tokenized_datasets.rename_column("label", "labels")
tokenized_datasets.set_format("torch")
tokenized_datasets["train"].column_names

train_dataloader = DataLoader(
    tokenized_datasets["train"], shuffle=True, batch_size=8, collate_fn=data_collator
)
eval_dataloader = DataLoader(
    tokenized_datasets["validation"], batch_size=8, collate_fn=data_collator
)

model = AutoModelForSequenceClassification.from_pretrained(checkpoint, num_labels=2)

optimizer = AdamW(model.parameters(), lr=5e-5)

num_epochs = 3
num_training_steps = num_epochs * len(train_dataloader)
lr_scheduler = get_scheduler(
    "linear",
    optimizer=optimizer,
    num_warmup_steps=0,
    num_training_steps=num_training_steps
)

model.to(device)

progress_bar = tqdm(range(num_training_steps))

model.train()
for epoch in range(num_epochs):
    for batch in train_dataloader:
        batch = {k: v.to(device) for k, v in batch.items()}
        outputs = model(**batch)
        loss = outputs.loss
        loss.backward()

        optimizer.step()
        lr_scheduler.step()
        optimizer.zero_grad()
        progress_bar.update(1)

metric= load_metric("glue", "mrpc")
model.eval()
for batch in eval_dataloader:
    batch = {k: v.to(device) for k, v in batch.items()}
    with torch.no_grad():
        outputs = model(**batch)

    logits = outputs.logits
    predictions = torch.argmax(logits, dim=-1)
    metric.add_batch(predictions=predictions, references=batch["labels"])

metric.compute()

model.save_pretrained('./my_model/')

使用🤗 Accelerate加速

Huggingface Transformers(1)-Hugging Face官方课程

Huggingface Transformers(1)-Hugging Face官方课程

accelerate官方地址

from datasets import load_dataset
from transformers import AutoTokenizer, DataCollatorWithPadding
from torch.utils.data import DataLoader
from transformers import AutoModelForSequenceClassification
from transformers import AdamW
from transformers import get_scheduler
import torch
from tqdm.auto import tqdm
from datasets import load_metric
from accelerate import Accelerator
import os
from multiprocessing import cpu_count

cpu_num = cpu_count()
os.environ ['OMP_NUM_THREADS'] = str(cpu_num)
os.environ ['OPENBLAS_NUM_THREADS'] = str(cpu_num)
os.environ ['MKL_NUM_THREADS'] = str(cpu_num)
os.environ ['VECLIB_MAXIMUM_THREADS'] = str(cpu_num)
os.environ ['NUMEXPR_NUM_THREADS'] = str(cpu_num)
torch.set_num_threads(cpu_num)

raw_datasets = load_dataset("glue", "mrpc")
checkpoint = "bert-base-uncased"
tokenizer = AutoTokenizer.from_pretrained(checkpoint)

def tokenize_function(example):
    return tokenizer(example["sentence1"], example["sentence2"], truncation=True)

tokenized_datasets = raw_datasets.map(tokenize_function, batched=True)
data_collator = DataCollatorWithPadding(tokenizer=tokenizer)

tokenized_datasets = tokenized_datasets.remove_columns(
    ["sentence1", "sentence2", "idx"]
)
tokenized_datasets = tokenized_datasets.rename_column("label", "labels")
tokenized_datasets.set_format("torch")
tokenized_datasets["train"].column_names

train_dataloader = DataLoader(
    tokenized_datasets["train"], shuffle=True, batch_size=8, collate_fn=data_collator
)
eval_dataloader = DataLoader(
    tokenized_datasets["validation"], batch_size=8, collate_fn=data_collator
)

accelerator = Accelerator()
model = AutoModelForSequenceClassification.from_pretrained(checkpoint, num_labels=2)

optimizer = AdamW(model.parameters(), lr=5e-5)

num_epochs = 3
num_training_steps = num_epochs * len(train_dataloader)
lr_scheduler = get_scheduler(
    "linear",
    optimizer=optimizer,
    num_warmup_steps=0,
    num_training_steps=num_training_steps
)

progress_bar = tqdm(range(num_training_steps))

model.train()
for epoch in range(num_epochs):
    for batch in train_dataloader:
        outputs = model(**batch)
        loss = outputs.loss
        accelerator.backward(loss)

        optimizer.step()
        lr_scheduler.step()
        optimizer.zero_grad()
        progress_bar.update(1)

model.save_pretrained('./my_model/')

启动方式:


accelerate config

accelerate launch train.py

Original: https://blog.csdn.net/weixin_45783552/article/details/122286050
Author: hihehe
Title: Huggingface Transformers(1)-Hugging Face官方课程

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/530502/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球