Hugging face Transformers(4)—— Model

  • Hugging Face 是一家在 NLP 和 AI 領域具有重要影響力的科技公司,他們的開源工具和社區建設為NLP研究和開發提供了強大的支持。它們擁有當前最活躍、最受關注、影響力最大的 NLP 社區,最新最強的 NLP 模型大多在這里發布和開源。該社區也提供了豐富的教程、文檔和示例代碼,幫助用戶快速上手并深入理解各類 Transformer 模型和 NLP 技術
  • Transformers 庫是 Hugging Face 最著名的貢獻之一,它最初是 Transformer 模型的 pytorch 復現庫,隨著不斷建設,至今已經成為 NLP 領域最重要,影響最大的基礎設施之一。該庫提供了大量預訓練的模型,涵蓋了多種語言和任務,成為當今大模型工程實現的主流標準,換句話說,如果你正在開發一個大模型,那么按 Transformer 庫的代碼格式進行工程實現、將 check point 打包成 hugging face 格式開源到社區,對于推廣你的工作有很大的助力作用。本系列文章將介紹 Transformers 庫的基本使用方法
  • 參考:
    • 官方教程
    • 手把手帶你實戰HuggingFace Transformers

文章目錄

  • 1. Transformer Model
    • 1.1 基本架構
    • 1.2 模型類型
    • 1.3 Model Head
  • 2. Transformer 庫 Model 組件的基本使用
    • 2.1 模型加載
    • 2.2 模型調用
      • 2.2.1 不帶 Model Head 的模型調用
      • 2.2.2 帶 Model Head 的模型調用
  • 3. 下游任務訓練

1. Transformer Model

1.1 基本架構

  • Transformer model 代表了以 Transformer 為基礎的一系列模型
    • 原始的 Transformer 是 Encoder-Decoder 模型,用于自然語言翻譯任務。其 Encoder 部分接受原始序列輸入并構建其完整的特征表示,Decoder 部分基于 Encoder 提供的特征和當前已經翻譯的部分結果,自回歸地生成目標序列(翻譯結果)。 無論 Encoder 還是 Decoder,均由多個 Transformer Block 堆疊而成,每個 Transformer Block 由 Attention Layer 和 FFD Layer 組成
      在這里插入圖片描述
    • 由于 Transformer Encoder 具有序列特征提取能力,Transformer Decoder 具有自回歸序列生成能力,兩者之后都被獨立使用,Encoder-Only 衍生出屬于自編碼器的 BERT 類模型,Decoder-Only 衍生出屬于自回歸生成模型的 GPT 類模型
  • Attention 機制
    • Attention 機制是 Transformer 類模型的一個核心特征,在計算當前 token 的特征表示時,可以通過注意力機制有選擇性地告訴模型應該使用哪部分上下文
    • Encoder-Decoder / Encoder-Only / Decoder-Only 三類模型,可以歸結為 attention mask 設置的不同,詳見 1.2 節

1.2 模型類型

  • 目前主流的 Transformer 類模型可分為以下四類

    1. 自編碼模型:Encoder-Only 結構,擁有雙向的注意力機制,即計算每一個詞的特征時都看到完整上下文
    2. 自回歸模型:Decoder-Only / Causal Decoder 結構,擁有單向的注意力機制,即計算每一個詞的特征時都只能看到上文,無法看到下文:
    3. 序列到序列模型:Encoder-Decoder 結構,Encoder部分使用雙向的注意力,Decoder部分使用單向注意力
    4. 前綴模型:Prefix-Decoder 結構,它對輸入序列的前綴部分使用雙向注意力機制,后半部分使用單向注意力機制,前綴片段內部的所有 token 都能看到完整上下文,其他部分只能看到前文。這可以看作是 Encoder-Decoder 的一個變體
  • 以上 3 的結構示意圖已經在 1.1 節給出,它的 Encoder-Decoder 使用兩個獨立的 Transformer 結構,其中通過 cross attention 機制連接,1/2/4 都只使用一個 Transformer 結構,區別僅在于 attention mask 施加不同,使得序列中各個 token 能觀測到的上下文區域有所區別,如下所示
    在這里插入圖片描述

    • Prefix Decoder 和 Encoder-Decoder 的主要區別在于:前者對編碼部分的 attention 是在每一層 Transformer Block 內部施加的,即第任意一層 Block 中的解碼部分片段可以關注到該層的前綴片段;后者則是 Decoder 中每層 Block 都能只能關注到 Encoder 最后一層的編碼片段結果
    • Prefix Decoder 和 Decoder-Only 非常類似,它們能執行的任務類型也差不多,下圖更清晰地指示了二者的不同
      在這里插入圖片描述
  • 不同的模型結構適用不同的預訓練方法,主要有以下幾種
    在這里插入圖片描述

    1. FLM (full language modeling):就是訓練標準的語言模型,完整一段話從頭到尾基于上文預測下一個token。適用于 Decoder-Only 模型
    2. PLM (prefix language modeling) :一段話分成兩截,前一截作為輸入,預測后一截。適用于 Encoder-Decoder 模型和 Prefix Decoder 模型
    3. MLM (masked language modeling):遮蓋住文本中的一部分token,讓模型通過上下文猜測遮蓋部分的token。適用于 Encoder-Only 模型
      • 將任務改造成 text-to-text 形式(即 input 和 target 都是一段文本),可以適配 Encoder-Decoder 和 Prefix Decoder
      • 將 input 和 target 拼接起來,可以適配 Decoder-Only
  • 總結一下各類結構的經典模型和主要適用任務

    模型類型預訓練目標常用預訓練模型主要適用任務
    Encoder-onlyMLMALBERT,BERT,DistilBERT,RoBERTa文本分類、命名實體識別、閱讀理解
    Decoder-onlyFLMGPT,GPT-2,Bloom,LLaMA文本生成
    Encoder-DecoderPLMBART,T5,Marian,mBART文本摘要、機器翻譯
    Prefix-DecoderPLMChatGLM、ChatGLM2、U-PaLM文本摘要、機器翻譯、文本生成

    注意這里的適用任務并不絕對,比如 Decoder-only 配合指令微調,在參數規模大了之后其實什么都能做;用 MLM 目標預訓練的模型,經過 PLM 或 FLM 繼續訓練后,也能做翻譯和生成等任務,反之亦然。可以參考論文 What Language Model Architecture and Pretraining Objective Works Best for Zero-Shot Generalization?

  • 額外提一句,當前最流行的模型結構是 Decoder-only,其中可能包含多方面原因,可以參考 【大模型慢學】GPT起源以及GPT系列采用Decoder-only架構的原因探討

1.3 Model Head

  • 很多 NLP 任務是可以用相同的 model 骨干完成的,在 Transformers 庫的設計上,一個相同的模型骨干可以對應多個不同的任務,它們的區別僅在于最后的 model head 有所不同

    比如對于 “句子情感分類” 和 “句子自回歸生成” 兩個任務,前者可以看作是基于前驅序列特征做二分類任務(正面情感/負面情感),后者可以看作是基于前驅序列特征做多分類任務(從詞表中選擇一個token索引),兩個任務中 “前驅序列特征” 都是可以用 GPT 模型提取的,它們的區別僅在于前者最后接二分類頭,后者最后接多分類頭

    • Model Head 是連接在模型后的層,通常為1個或多個全連接層
    • Model Head 將模型的編碼的表示結果進行映射,以解決不同類型的任務
      在這里插入圖片描述

      以 BERT 模型情感二分類任務為例,設模型輸入長度 128,嵌入維度 768,則 Hidden states 尺寸 1x128x768。這時 Head 可能是一個輸入尺寸為 768,輸出尺寸為 2 的 MLP,最后一層 Hidden states 中 [CLS] 特殊 token 位置的 768 維向量將會輸入 Head,對 Head 輸出計算交叉熵損失來訓練模型

  • Transformer 庫中,模型類對象使用的 Model Head 可以從其類名后綴中觀察出來

    • *Model(模型本身,只返回編碼結果)
    • *ForCausalLM
    • *ForMaskedLM
    • *ForSeq2SeqLM
    • *ForMultipleChoice
    • *ForQuestionAnswering
    • *ForSequenceClassification
    • *ForTokenClassification

2. Transformer 庫 Model 組件的基本使用

2.1 模型加載

  • 使用 AutoModel 類,可以用 from_pretrained 方法直接從模型地址下載模型和權重檢查點,并返回 model 對象。這類似前文介紹過的 AutoTokenizer 類。這里我們加載一個小規模的中文情感分類模型

    from transformers import AutoConfig, AutoModel, AutoTokenizer
    # 在線加載
    # 若下載失敗,也可以在倉庫 https://huggingface.co/hfl/rbt3/tree/main 手動下載,然后在from_pretrained方法中傳入本地文件夾加載
    model = AutoModel.from_pretrained("hfl/rbt3")
    model
    
    BertModel((embeddings): BertEmbeddings((word_embeddings): Embedding(21128, 768, padding_idx=0)(position_embeddings): Embedding(512, 768)(token_type_embeddings): Embedding(2, 768)(LayerNorm): LayerNorm((768,), eps=1e-12, elementwise_affine=True)(dropout): Dropout(p=0.1, inplace=False))(encoder): BertEncoder((layer): ModuleList((0): BertLayer((attention): BertAttention((self): BertSelfAttention((query): Linear(in_features=768, out_features=768, bias=True)(key): Linear(in_features=768, out_features=768, bias=True)(value): Linear(in_features=768, out_features=768, bias=True)(dropout): Dropout(p=0.1, inplace=False))(output): BertSelfOutput((dense): Linear(in_features=768, out_features=768, bias=True)(LayerNorm): LayerNorm((768,), eps=1e-12, elementwise_affine=True)(dropout): Dropout(p=0.1, inplace=False)))(intermediate): BertIntermediate(
    ...(pooler): BertPooler((dense): Linear(in_features=768, out_features=768, bias=True)(activation): Tanh())
    )
    

    可以看到這是一個 BertModel

  • 可以通過 model.config 訪問該模型的參數

    # 查看模型配置參數
    model.config
    
    BertConfig {"_name_or_path": "hfl/rbt3","architectures": ["BertForMaskedLM"],"attention_probs_dropout_prob": 0.1,"classifier_dropout": null,"directionality": "bidi","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": 3,"output_past": true,"pad_token_id": 0,"pooler_fc_size": 768,"pooler_num_attention_heads": 12,"pooler_num_fc_layers": 3,"pooler_size_per_head": 128,"pooler_type": "first_token_transform",
    ..."transformers_version": "4.41.2","type_vocab_size": 2,"use_cache": true,"vocab_size": 21128
    }
    

    可見,Bert 類模型的參數使用一個 BertConfig 類對象管理,查看其源碼定義,可以看到參數的解釋

    class BertConfig(PretrainedConfig):r"""This is the configuration class to store the configuration of a [`BertModel`] or a [`TFBertModel`]. It is used toinstantiate a BERT model according to the specified arguments, defining the model architecture. Instantiating aconfiguration with the defaults will yield a similar configuration to that of the BERT[google-bert/bert-base-uncased](https://huggingface.co/google-bert/bert-base-uncased) architecture.Configuration objects inherit from [`PretrainedConfig`] and can be used to control the model outputs. Read thedocumentation from [`PretrainedConfig`] for more information.Args:vocab_size (`int`, *optional*, defaults to 30522):Vocabulary size of the BERT model. Defines the number of different tokens that can be represented by the`inputs_ids` passed when calling [`BertModel`] or [`TFBertModel`].hidden_size (`int`, *optional*, defaults to 768):Dimensionality of the encoder layers and the pooler layer.num_hidden_layers (`int`, *optional*, defaults to 12):Number of hidden layers in the Transformer encoder.num_attention_heads (`int`, *optional*, defaults to 12):Number of attention heads for each attention layer in the Transformer encoder.intermediate_size (`int`, *optional*, defaults to 3072):Dimensionality of the "intermediate" (often named feed-forward) layer in the Transformer encoder.hidden_act (`str` or `Callable`, *optional*, defaults to `"gelu"`):The non-linear activation function (function or string) in the encoder and pooler. If string, `"gelu"`,`"relu"`, `"silu"` and `"gelu_new"` are supported.hidden_dropout_prob (`float`, *optional*, defaults to 0.1):The dropout probability for all fully connected layers in the embeddings, encoder, and pooler.attention_probs_dropout_prob (`float`, *optional*, defaults to 0.1):The dropout ratio for the attention probabilities.max_position_embeddings (`int`, *optional*, defaults to 512):The maximum sequence length that this model might ever be used with. Typically set this to something largejust in case (e.g., 512 or 1024 or 2048).type_vocab_size (`int`, *optional*, defaults to 2):The vocabulary size of the `token_type_ids` passed when calling [`BertModel`] or [`TFBertModel`].initializer_range (`float`, *optional*, defaults to 0.02):The standard deviation of the truncated_normal_initializer for initializing all weight matrices.layer_norm_eps (`float`, *optional*, defaults to 1e-12):The epsilon used by the layer normalization layers.position_embedding_type (`str`, *optional*, defaults to `"absolute"`):Type of position embedding. Choose one of `"absolute"`, `"relative_key"`, `"relative_key_query"`. Forpositional embeddings use `"absolute"`. For more information on `"relative_key"`, please refer to[Self-Attention with Relative Position Representations (Shaw et al.)](https://arxiv.org/abs/1803.02155).For more information on `"relative_key_query"`, please refer to *Method 4* in [Improve Transformer Modelswith Better Relative Position Embeddings (Huang et al.)](https://arxiv.org/abs/2009.13658).is_decoder (`bool`, *optional*, defaults to `False`):Whether the model is used as a decoder or not. If `False`, the model is used as an encoder.use_cache (`bool`, *optional*, defaults to `True`):Whether or not the model should return the last key/values attentions (not used by all models). Onlyrelevant if `config.is_decoder=True`.classifier_dropout (`float`, *optional*):The dropout ratio for the classification head.Examples:```python>>> from transformers import BertConfig, BertModel>>> # Initializing a BERT google-bert/bert-base-uncased style configuration>>> configuration = BertConfig()>>> # Initializing a model (with random weights) from the google-bert/bert-base-uncased style configuration>>> model = BertModel(configuration)>>> # Accessing the model configuration>>> configuration = model.config```"""model_type = "bert"def __init__()...
    
  • 注意到 BertConfig 類繼承自 PretrainedConfig,這意味著之前從 model.config 打印的參數并不完整,進一步查看 PretrainedConfig 類的源碼,可以看到模型使用的所有參數。了解模型使用的全部參數是重要的,因為我們修改模型時主要就是從修改參數入手

2.2 模型調用

2.2.1 不帶 Model Head 的模型調用

  • 像 2.1 節那樣加載,得到的 model 是不帶 model head 的,這一點可以從打印從模型結構中看出,它以一個 BertPooler 塊結尾

    ...
    (pooler): BertPooler((dense): Linear(in_features=768, out_features=768, bias=True)(activation): Tanh())
    ...
    

    可見輸出特征還是 768 維,這意味著沒有接調整到目標維度的 model head。當我們想把預訓練的模型作為序列特征提取器時,這種裸模型是有用的,可以通過加載模型時傳入參數 output_attentions=True 來獲得模型所有層的 attention 張量

    # 構造測試輸入
    sen = "弱小的我也有大夢想!"
    tokenizer = AutoTokenizer.from_pretrained("hfl/rbt3")  # 加載 tokenizer
    inputs = tokenizer(sen, return_tensors="pt")           # return_tensors="pt" 要求返回 tensor 張量# 不帶 model head 的模型調用
    model = AutoModel.from_pretrained("hfl/rbt3", output_attentions=True) # 要求輸出 attention 張量
    output = model(**inputs)print(output.keys())	# odict_keys(['last_hidden_state', 'pooler_output', 'attentions'])
    assert output.last_hidden_state.shape[1] == len(inputs['input_ids'][0]) # 輸出尺寸和輸入尺寸相同
    

    查看最后一層 hidden state

    # 不帶 model head 做下游任務時,通常我們是需要 model 提取的特征,即最后一層的 last_hidden_state
    output.last_hidden_state      # torch.Size([1, 12, 768])
    
    tensor([[[ 0.6804,  0.6664,  0.7170,  ..., -0.4102,  0.7839, -0.0262],[-0.7378, -0.2748,  0.5034,  ..., -0.1359, -0.4331, -0.5874],[-0.0212,  0.5642,  0.1032,  ..., -0.3617,  0.4646, -0.4747],...,[ 0.0853,  0.6679, -0.1757,  ..., -0.0942,  0.4664,  0.2925],[ 0.3336,  0.3224, -0.3355,  ..., -0.3262,  0.2532, -0.2507],[ 0.6761,  0.6688,  0.7154,  ..., -0.4083,  0.7824, -0.0224]]],grad_fn=<NativeLayerNormBackward0>)
    

2.2.2 帶 Model Head 的模型調用

  • 使用帶有 1.3 節所述 model head 類名后綴的模型類加載模型,即可得到帶 head 的模型

    from transformers import AutoModelForSequenceClassificationclz_model = AutoModelForSequenceClassification.from_pretrained("hfl/rbt3")  # 加載帶多分類頭的模型
    clz_model # 注意模型結構最后多了 (classifier): Linear(in_features=768, out_features=2, bias=True)
    
    Some weights of BertForSequenceClassification were not initialized from the model checkpoint at hfl/rbt3 and are newly initialized: ['classifier.bias', 'classifier.weight']
    You should probably TRAIN this model on a down-stream task to be able to use it for predictions and inference.
    BertForSequenceClassification((bert): BertModel((embeddings): BertEmbeddings((word_embeddings): Embedding(21128, 768, padding_idx=0)(position_embeddings): Embedding(512, 768)(token_type_embeddings): Embedding(2, 768)(LayerNorm): LayerNorm((768,), eps=1e-12, elementwise_affine=True)(dropout): Dropout(p=0.1, inplace=False))(encoder): BertEncoder((layer): ModuleList((0): BertLayer((attention): BertAttention((self): BertSelfAttention((query): Linear(in_features=768, out_features=768, bias=True)(key): Linear(in_features=768, out_features=768, bias=True)(value): Linear(in_features=768, out_features=768, bias=True)(dropout): Dropout(p=0.1, inplace=False))(output): BertSelfOutput((dense): Linear(in_features=768, out_features=768, bias=True)(LayerNorm): LayerNorm((768,), eps=1e-12, elementwise_affine=True)(dropout): Dropout(p=0.1, inplace=False)))
    ...))(dropout): Dropout(p=0.1, inplace=False)(classifier): Linear(in_features=768, out_features=2, bias=True)
    )
    

    注意模型現在變成了一個 BertForSequenceClassification 對象,其結構最后多了一個由 dropoutclassifier 線性層組成的 head,而且這里提示我們 Some weights of BertForSequenceClassification were not initialized...,說明這個線性層的參數 ckpt 中沒有提供,需要我們針對下游任務特別訓練

  • 注意到分類頭默認輸出維度(類別數為2),這個可以通過參數 num_labels 控制,從模型類 BertForSequenceClassification 定義進去檢查。下面修改 model head 的輸出維度看看

    # 分類頭默認輸出維度(類別數為2),可以通過參數 num_labels 控制
    from transformers import AutoModelForSequenceClassification, BertForSequenceClassificationclz_model = AutoModelForSequenceClassification.from_pretrained("hfl/rbt3", num_labels=10)  # 指定10個類
    clz_model # 注意模型結構最后多了 (classifier): Linear(in_features=768, out_features=10, bias=True)
    
    Some weights of BertForSequenceClassification were not initialized from the model checkpoint at hfl/rbt3 and are newly initialized: ['classifier.bias', 'classifier.weight']
    You should probably TRAIN this model on a down-stream task to be able to use it for predictions and inference.
    BertForSequenceClassification((bert): BertModel((embeddings): BertEmbeddings((word_embeddings): Embedding(21128, 768, padding_idx=0)(position_embeddings): Embedding(512, 768)(token_type_embeddings): Embedding(2, 768)(LayerNorm): LayerNorm((768,), eps=1e-12, elementwise_affine=True)(dropout): Dropout(p=0.1, inplace=False))(encoder): BertEncoder((layer): ModuleList((0): BertLayer((attention): BertAttention((self): BertSelfAttention((query): Linear(in_features=768, out_features=768, bias=True)(key): Linear(in_features=768, out_features=768, bias=True)(value): Linear(in_features=768, out_features=768, bias=True)(dropout): Dropout(p=0.1, inplace=False))(output): BertSelfOutput((dense): Linear(in_features=768, out_features=768, bias=True)(LayerNorm): LayerNorm((768,), eps=1e-12, elementwise_affine=True)(dropout): Dropout(p=0.1, inplace=False)))
    ...))(dropout): Dropout(p=0.1, inplace=False)(classifier): Linear(in_features=768, out_features=10, bias=True)
    )
    
  • 使用以上模型做前向傳播試試

    clz_model(**inputs)
    
    SequenceClassifierOutput(loss=None, logits=tensor([[ 0.1448,  0.1539, -0.1112,  0.1182,  0.2485,  0.4370,  0.3614,  0.5981,0.5442, -0.2900]], grad_fn=<AddmmBackward0>), hidden_states=None, attentions=None)
    

    可見輸出結構中存在一個 loss 成員,說明前向過程中就有計算 loss 的結構了,不妨看一下 BertForSequenceClassification 類的定義

    class BertForSequenceClassification(BertPreTrainedModel):def __init__(self, config):super().__init__(config)self.num_labels = config.num_labelsself.config = configself.bert = BertModel(config)classifier_dropout = (config.classifier_dropout if config.classifier_dropout is not None else config.hidden_dropout_prob)self.dropout = nn.Dropout(classifier_dropout)self.classifier = nn.Linear(config.hidden_size, config.num_labels)# Initialize weights and apply final processingself.post_init()@add_start_docstrings_to_model_forward(BERT_INPUTS_DOCSTRING.format("batch_size, sequence_length"))@add_code_sample_docstrings(checkpoint=_CHECKPOINT_FOR_SEQUENCE_CLASSIFICATION,output_type=SequenceClassifierOutput,config_class=_CONFIG_FOR_DOC,expected_output=_SEQ_CLASS_EXPECTED_OUTPUT,expected_loss=_SEQ_CLASS_EXPECTED_LOSS,)def forward(self,input_ids: Optional[torch.Tensor] = None,attention_mask: Optional[torch.Tensor] = None,token_type_ids: Optional[torch.Tensor] = None,position_ids: Optional[torch.Tensor] = None,head_mask: Optional[torch.Tensor] = None,inputs_embeds: Optional[torch.Tensor] = None,labels: Optional[torch.Tensor] = None,output_attentions: Optional[bool] = None,output_hidden_states: Optional[bool] = None,return_dict: Optional[bool] = None,) -> Union[Tuple[torch.Tensor], SequenceClassifierOutput]:r"""labels (`torch.LongTensor` of shape `(batch_size,)`, *optional*):Labels for computing the sequence classification/regression loss. Indices should be in `[0, ...,config.num_labels - 1]`. If `config.num_labels == 1` a regression loss is computed (Mean-Square loss), If`config.num_labels > 1` a classification loss is computed (Cross-Entropy)."""return_dict = return_dict if return_dict is not None else self.config.use_return_dictoutputs = self.bert(input_ids,attention_mask=attention_mask,token_type_ids=token_type_ids,position_ids=position_ids,head_mask=head_mask,inputs_embeds=inputs_embeds,output_attentions=output_attentions,output_hidden_states=output_hidden_states,return_dict=return_dict,)pooled_output = outputs[1]pooled_output = self.dropout(pooled_output)logits = self.classifier(pooled_output)loss = Noneif labels is not None:if self.config.problem_type is None:if self.num_labels == 1:self.config.problem_type = "regression"elif self.num_labels > 1 and (labels.dtype == torch.long or labels.dtype == torch.int):self.config.problem_type = "single_label_classification"else:self.config.problem_type = "multi_label_classification"if self.config.problem_type == "regression":loss_fct = MSELoss()if self.num_labels == 1:loss = loss_fct(logits.squeeze(), labels.squeeze())else:loss = loss_fct(logits, labels)elif self.config.problem_type == "single_label_classification":loss_fct = CrossEntropyLoss()loss = loss_fct(logits.view(-1, self.num_labels), labels.view(-1))elif self.config.problem_type == "multi_label_classification":loss_fct = BCEWithLogitsLoss()loss = loss_fct(logits, labels)if not return_dict:output = (logits,) + outputs[2:]return ((loss,) + output) if loss is not None else outputreturn SequenceClassifierOutput(loss=loss,logits=logits,hidden_states=outputs.hidden_states,attentions=outputs.attentions,)

    從 forward 方法中可見,如果傳入了 labels 參數,則會進一步根據輸出尺寸 num_labels 自動識別任務類型,并使用相應的損失函數計算 loss 作為返回的一部分

3. 下游任務訓練

  • 在 2.2.2 節,我們構造了一個 BertForSequenceClassification 模型,它的 Bert 骨干加載了預訓練的 ckpt 權重,而分類頭權重是隨機初始化的。本節我們使用 ChnSentiCorp_htl_all數據集對它做下游任務訓練,該數據集由 7000 多條酒店評論數據,包括 5000 多條正向評論,2000 多條負向評論,用這些數據繼續訓練,可以得到一個文本情感分類模型。由于模型中絕大部分參數都有良好的初始權重,且模型規模很小,訓練成本并不高
  • 我們這里不使用 Transformers 庫的 pipeline、evaluate、trainer 和 dataset,盡量手動實現全部代碼,細節請參考注釋
    import os
    import sys
    base_path = os.path.abspath(os.path.join(os.path.dirname(__file__)))
    sys.path.append(base_path)from transformers import AutoTokenizer, AutoModelForSequenceClassification
    import pandas as pd
    import torch
    from torch.utils.data import Dataset, DataLoader, random_split
    from torch.optim import Adamclass MyDataset(Dataset):def __init__(self) -> None:super().__init__()self.data = pd.read_csv(f"{base_path}/ChnSentiCorp_htl_all.csv")    # 加載原始數據self.data = self.data.dropna()                                      # 去掉 nan 值def __getitem__(self, index):text:str = self.data.iloc[index]["review"]label:int = self.data.iloc[index]["label"]return text, labeldef __len__(self):return len(self.data)def collate_func(batch):# 對 dataloader 得到的 batch data 進行后處理# batch data 是一個 list,其中每個元素是 (sample, label) 形式的元組texts, labels = [], []for item in batch:texts.append(item[0])labels.append(item[1])# 對原始 texts 列表進行批量 tokenize,通過填充或截斷保持 token 長度為 128,要求返回的每個字段都是 pytorch tensorglobal tokenizerinputs = tokenizer(texts, max_length=128, padding="max_length", truncation=True, return_tensors="pt")# 增加 label 字段,這樣之后模型前向傳播時可以直接計算 lossinputs["labels"] = torch.tensor(labels)return inputsdef evaluate(model):model.eval()acc_num = 0with torch.inference_mode():for batch in validloader:if torch.cuda.is_available():batch = {k: v.cuda() for k, v in batch.items()}output = model(**batch)pred = torch.argmax(output.logits, dim=-1)acc_num += (pred.long() == batch["labels"].long()).float().sum()return acc_num / len(validset)def train(model, optimizer, epoch=3, log_step=100):global_step = 0for ep in range(epoch):model.train()for batch in trainloader:if torch.cuda.is_available():batch = {k: v.cuda() for k, v in batch.items()}optimizer.zero_grad()output = model(**batch) # batch 是一個字典,其中包含 model forward 方法所需的字段,每個字段 value 是 batch tensoroutput.loss.backward()  # batch 字典中包含 labels 時會計算損失,詳見源碼optimizer.step()if global_step % log_step == 0:print(f"ep: {ep}, global_step: {global_step}, loss: {output.loss.item()}")global_step += 1acc = evaluate(model)print(f"ep: {ep}, acc: {acc}")if __name__ == "__main__":# 構造訓練集/測試集以及對應的 Dataloaderdataset = MyDataset()train_size = int(0.9*len(dataset))vaild_size = len(dataset) - train_sizetrainset, validset = random_split(dataset, lengths=[train_size, vaild_size])trainloader = DataLoader(trainset, batch_size=32, shuffle=True, collate_fn=collate_func)validloader = DataLoader(validset, batch_size=64, shuffle=False, collate_fn=collate_func)# 構造 tokenizer、model 和 optimizertokenizer = AutoTokenizer.from_pretrained("hfl/rbt3")model = AutoModelForSequenceClassification.from_pretrained("hfl/rbt3")  # 從 AutoModelForSequenceClassification 加載標準初始化模型,從 AutoModel.from_pretrained("hfl/rbt3") 加載 ckpt 權重模型if torch.cuda.is_available():model = model.cuda()optimizer = Adam(model.parameters(), lr=2e-5)# 訓練train(model, optimizer)# 測試sen = "我覺得這家酒店不錯,飯很好吃!"id2_label = {0: "差評!", 1: "好評!"}model.eval()with torch.inference_mode():inputs = tokenizer(sen, return_tensors="pt")inputs = {k: v.cuda() for k, v in inputs.items()}logits = model(**inputs).logitspred = torch.argmax(logits, dim=-1)print(f"輸入:{sen}\n模型預測結果:{id2_label.get(pred.item())}")
    Some weights of BertForSequenceClassification were not initialized from the model checkpoint at hfl/rbt3 and are newly initialized: ['classifier.bias', 'classifier.weight']
    You should probably TRAIN this model on a down-stream task to be able to use it for predictions and inference.
    ep: 0, global_step: 0, loss: 0.6289803385734558
    ep: 0, global_step: 200, loss: 0.17686372995376587
    ep: 0, acc: 0.8944659233093262
    ep: 1, global_step: 300, loss: 0.18355882167816162
    ep: 1, global_step: 400, loss: 0.27272453904151917
    ep: 1, acc: 0.8957529067993164
    ep: 2, global_step: 500, loss: 0.18500971794128418
    ep: 2, global_step: 600, loss: 0.08873294293880463
    ep: 2, acc: 0.8918918967247009
    輸入:我覺得這家酒店不錯,飯很好吃!
    模型預測結果:好評!
    

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/43366.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/43366.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/43366.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【Bug優化】支付寶支付中“交易訂單處理失敗,請稍后再試”問題

引言 近期&#xff0c;一位友友問&#xff1a;他在集成支付寶支付功能時遇到了一個棘手的問題&#xff0c;當用戶在支付過程中選擇放棄支付&#xff0c;嘗試重新支付同一訂單時&#xff0c;前端會顯示“交易訂單處理失敗&#xff0c;請稍后再試”。 這個問題的核心在于支…

文章SameStr(一):圖1代碼

“Publication Figure 1” 百度云盤鏈接: https://pan.baidu.com/s/15g7caZp354zIWktpnWzWhQ 提取碼: 4sh7 Libraries Standard Import library(tidyverse) library(cowplot) library(scales) library(ggpubr)Special # devtools::install_github("pmartinezarbizu/…

linux 代理export

export http_proxyhttp://10.67.11.138:7890 export https_proxyhttp://10.67.11.138:7890

大/小端模式與位操作

文章目錄 1. 大小端模式 2. 大端模式&#xff08;Big-endian&#xff09; 3. 小端模式&#xff08;Little Endian&#xff09; 4. 判斷和轉換大小端模式 5. 位操作 5.1 移位操作 5.2 取反操作 5.3 位與操作 5.4 位或操作 5.5 置位操作 5.6 清位操作 1. 大小端模式 …

大數據學習之 scala基礎(補充)

scala基礎&#xff1a; hello world: 寫scala可運行文件的注意事項1、如果一個scala文件要運行&#xff0c;class要改成object2、如果是class&#xff0c;就僅單純代表一個類&#xff0c;如果是object代表的是單例對象3、scala語法中&#xff0c;一句話結束不需要加分號4、sca…

Spring的AOP基礎以及AOP的核心概念

2. AOP基礎 學習完spring的事務管理之后&#xff0c;接下來我們進入到AOP的學習。 AOP也是spring框架的第二大核心&#xff0c;我們先來學習AOP的基礎。 在AOP基礎這個階段&#xff0c;我們首先介紹一下什么是AOP&#xff0c;再通過一個快速入門程序&#xff0c;讓大家快速體…

Ubuntu配置GitHub(第一次clone/push)

文章目錄 1. 安裝Git&檢查連接2. 注冊GitHub3. 生成&GitHub添加SSH3.1. 檢查&刪除已有id_rsa3.2. 生成SSH3.3. GitHub添加id_rsa.pub SSH3.4. 檢查SSH 4. 繼續開發可以參考參考 1. 安裝Git&檢查連接 安裝 sudo apt-get install git檢查SSH連接 ssh -T gitgi…

【工具分享】零零信安攻擊面管理平臺

文章目錄 00SEC-ASM?功能介紹功能演示 最近閑來無事&#xff0c;到處網上沖浪&#xff0c;無意間發現了長亭云圖攻擊面管理平臺&#xff0c;無奈需要授權才能使用&#xff0c;于是就找到了平替&#xff1a;零零信安攻擊面管理平臺。 長亭云圖攻擊面管理平臺&#xff1a;https:…

vue2封裝向上滾動組件

目錄 1.代碼2.使用 1.代碼 <template><div class"marquee-wrap" :style"{height: height px}"><ul class"marquee-list":style"animateUpStyle"v-on:mouseover"myMouseover"v-on:mouseout"myMouseout…

工廠方法模式在金融業務中的應用及其框架實現

引言 工廠方法模式&#xff08;Factory Method Pattern&#xff09;是一種創建型設計模式&#xff0c;它定義了一個創建對象的接口&#xff0c;但由子類決定實例化哪一個類。工廠方法模式使得類的實例化延遲到子類。在金融業務中&#xff0c;工廠方法模式可以用于創建不同類型…

惠普Z系列AI臺式工作站家族

聚焦智能制造、數據科學及人工智能、3D圖形圖像等高精尖領域&#xff0c;為客戶提供高性能、強穩定、強拓展的臺式工作站產品及解決方案。 HP Z1 Tower G9 VR創作 / 設計建模 適用于VR內容創作及設計建模等工作可支持128G DDR5內存&#xff0c;第十四代Intel Core? vPro? CPU…

Windows下cmd快速到達指定文件位置(三種方法)

一、圖形界面 第一步&#xff1a; 點擊此圖標 在搜索框輸入“cmd”&#xff0c;打開命令提示符 或者&#xff1a; 快捷鍵windowR&#xff0c;點擊“確定” 第二步&#xff1a; 先進盤 比如d盤&#xff0c;輸入 d: &#xff08;注意英文輸入法&#xff09;&#xff0c;再進文件…

前沿重器[53] | 聊聊搜索系統6:精排

前沿重器 欄目主要給大家分享各種大廠、頂會的論文和分享&#xff0c;從中抽取關鍵精華的部分和大家分享&#xff0c;和大家一起把握前沿技術。具體介紹&#xff1a;倉頡專項&#xff1a;飛機大炮我都會&#xff0c;利器心法我還有。&#xff08;算起來&#xff0c;專項啟動已經…

充分利用視覺信息多問多答合成數據,提升多模態大模型數學推理能力

©PaperWeekly 原創 作者 | 史文浩 單位 | 電子科技大學 論文題目&#xff1a; Math-LLaVA: Bootstrapping Mathematical Reasoning for Multimodal Large Language Models 論文鏈接&#xff1a; https://arxiv.org/abs/2406.17294 開源鏈接&#xff1a; https://github.c…

最新國內免費使用GPT4o、4.0、3.5 的方法

為了方便大家對GPT有更好的了解&#xff0c;這里特地整理了一個表格做對比 這些模型展示了OpenAI在自然語言處理領域的持續進步&#xff0c;每一代模型都在理解和生成能力、效率和適用性方面進行了顯著提升。 網站匯總 這里順便給大家匯總一下國內同類型的網站&#xff0c;有…

物聯網設計競賽_10_Jetson Nano中文轉漢語語音

在windows中pyttsx3可以讓漢字文本輸出中文語音&#xff0c;但是在jetson上只能用英文說話 import pyttsx3def hanyu(test):engine pyttsx3.init()rate engine.getProperty(rate)engine.setProperty(rate,125)engine.say(test)engine.runAndWait() hanyu(你好) #engine.save…

qt 按鈕鏈接一個槽函數

在Qt中&#xff0c;按鈕&#xff08;比如QPushButton&#xff09;可以通過信號和槽的機制來連接到一個槽函數。這樣&#xff0c;當按鈕被點擊時&#xff0c;槽函數就會被執行。下面是如何將一個按鈕鏈接到一個槽函數的基本步驟&#xff1a; 創建按鈕和槽函數&#xff1a; 創建…

每日一練全新考試模式解鎖|考試升級

&#x1f64b;頻繁有小伙伴咨詢&#xff1a;我想舉辦一場歷時一個月的答題活動&#xff0c;學生可以每天打開答題&#xff0c;活動完結后可以導出每天的答題成績 此前我們都會讓小伙伴創建30場考試&#xff0c;然后使用批量分享功能組合起來&#xff0c;對外分享一個鏈接就可以…

【chatgpt消費者偏好】是什么驅動了游客持續旅游意愿?推文分享—2024-07-08

今天推文的主題是【chatgpt&消費者意愿】 第一篇&#xff1a;文章主要研究了什么因素驅動旅游者繼續使用ChatGPT進行旅行服務&#xff0c;并從人類擬態的角度探討了旅游者對ChatGPT的感知和使用意圖。第二篇&#xff1a;本文探討了ChatGPT-4在生成針對TripAdvisor上發布的…

速盾:cdn防御cc

CDN&#xff08;Content Delivery Network&#xff09;是指在分布式網絡中分布服務器群&#xff0c;通過就近訪問用戶、提供快速可靠的內容傳輸與加速服務。而CC&#xff08;Challenge Collapsar&#xff09;攻擊則是一種常見的網絡攻擊手段&#xff0c;通過發送大量的請求來超…