Pytorch
TensorFlow

#
使用 Keras 微調一個模型

完成上一節中的所有數據預處理工作後，您只剩下最後的幾個步驟來訓練模型。 但是請注意，`model.fit()` 命令在 CPU 上運行會非常緩慢。 如果您沒有GPU，則可以在 Google Colab 上使用免費的 GPU 或 TPU(需要梯子)。

這一節的代碼示例假設您已經執行了上一節中的代碼示例。 下面一個簡短的摘要，包含了在開始學習這一節之前您需要的執行的代碼：

    from datasets import load_dataset
    from transformers import AutoTokenizer, DataCollatorWithPadding
    import numpy as np

    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, return_tensors="tf")

    tf_train_dataset = tokenized_datasets["train"].to_tf_dataset(
        columns=["attention_mask", "input_ids", "token_type_ids"],
        label_cols=["labels"],
        shuffle=True,
        collate_fn=data_collator,
        batch_size=8,
    )

    tf_validation_dataset = tokenized_datasets["validation"].to_tf_dataset(
        columns=["attention_mask", "input_ids", "token_type_ids"],
        label_cols=["labels"],
        shuffle=False,
        collate_fn=data_collator,
        batch_size=8,
    )

### 訓練模型

從🤗 Transformers 導入的 TensorFlow 模型已經是 Keras 模型。 下面的視頻是對 Keras 的簡短介紹。

這意味著，一旦我們有了數據，就需要很少的工作就可以開始對其進行訓練。

和第二章使用的方法一樣, 我們將使用二分類的 `TFAutoModelForSequenceClassification`類:

    from transformers import TFAutoModelForSequenceClassification

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

您會注意到，與 第二章 不同的是，您在實例化此預訓練模型後會收到警告。 這是因為 BERT 沒有對句子對進行分類進行預訓練，所以預訓練模型的 head 已經被丟棄，而是插入了一個適合序列分類的新 head。 警告表明一些權重沒有使用（對應於丟棄的預訓練頭），而其他一些權重是隨機初始化的（新頭的權重）。 最後鼓勵您訓練模型，這正是我們現在要做的。

要在我們的數據集上微調模型，我們只需要在我們的模型上調用 `compile()` 方法，然後將我們的數據傳遞給 `fit()` 方法。 這將啟動微調過程（在 GPU 上應該需要幾分鐘）並輸出訓練loss，以及每個 epoch 結束時的驗證loss。

請注意🤗 Transformers 模型具有大多數 Keras 模型所沒有的特殊能力——它們可以自動使用內部計算的loss。 如果您沒有在 `compile()` 中設置損失函數，他們將默認使用內部計算的損失。 請注意，要使用內部損失，您需要將標籤作為輸入的一部分傳遞，而不是作為單獨的標籤（這是在 Keras 模型中使用標籤的正常方式）。 您將在課程的第 2 部分中看到這方面的示例，其中定義正確的損失函數可能很棘手。 然而，對於序列分類，標準的 Keras 損失函數可以正常工作，所以我們將在這裡使用它。

    from tensorflow.keras.losses import SparseCategoricalCrossentropy

    model.compile(
        optimizer="adam",
        loss=SparseCategoricalCrossentropy(from_logits=True),
        metrics=["accuracy"],
    )
    model.fit(
        tf_train_dataset,
        validation_data=tf_validation_dataset,
    )

請注意這裡有一個非常常見的陷阱——你只是可以將損失的名稱作為字符串傳遞給 Keras，但默認情況下，Keras 會假設你已經對輸出應用了 softmax。 然而，許多模型在應用 softmax 之前就輸出，也稱為 logits。 我們需要告訴損失函數我們的模型是否經過了softmax，唯一的方法是直接調用它，而不是用字符串的名稱。

### 提升訓練的效果

如果您嘗試上面的代碼，它肯定會運行，但您會發現loss只是緩慢或零星地下降。 主要原因是學習率。 與loss一樣，當我們將優化器的名稱作為字符串傳遞給 Keras 時，Keras 會初始化該優化器具有所有參數的默認值，包括學習率。 但是，根據長期經驗，我們知道Transformer 模型更適合使用比 Adam 的默認值（1e-3）也寫成為 10 的 -3 次方，或 0.001，低得多的學習率。 5e-5 (0.00005) 比默認值大約低 20 倍，是一個更好的起點。

除了降低學習率，我們還有第二個技巧：我們可以慢慢降低學習率。在訓練過程中。 在文獻中，您有時會看到這被稱為 decaying 或 annealing學習率。 在 Keras 中，最好的方法是使用 learning rate scheduler。 一個好用的是`PolynomialDecay`——儘管有這個名字，但在默認設置下，它只是簡單地從初始值線性衰減學習率值在訓練過程中的最終值，這正是我們想要的。但是， 為了正確使用調度程序，我們需要告訴它訓練的次數。 我們將在下面為其計算“num_train_steps”。

    from tensorflow.keras.optimizers.schedules import PolynomialDecay

    batch_size = 8
    num_epochs = 3
    # 訓練步數是數據集中的樣本數除以batch size再乘以 epoch。
    # 注意這裡的tf_train_dataset是一個轉化為batch後的 tf.data.Dataset，
    # 不是原來的 Hugging Face Dataset，所以它的 len() 已經是 num_samples // batch_size。
    num_train_steps = len(tf_train_dataset) * num_epochs
    lr_scheduler = PolynomialDecay(
        initial_learning_rate=5e-5, end_learning_rate=0.0, decay_steps=num_train_steps
    )
    from tensorflow.keras.optimizers import Adam

    opt = Adam(learning_rate=lr_scheduler)

🤗 Transformers 庫還有一個 `create_optimizer()` 函數，它將創建一個具有學習率衰減的 `AdamW` 優化器。 這是一個便捷的方式，您將在本課程的後續部分中詳細瞭解。

現在我們有了全新的優化器，我們可以嘗試使用它進行訓練。 首先，讓我們重新加載模型，以重置我們剛剛進行的訓練運行對權重的更改，然後我們可以使用新的優化器對其進行編譯：

    import tensorflow as tf

    model = TFAutoModelForSequenceClassification.from_pretrained(checkpoint, num_labels=2)
    loss = tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True)
    model.compile(optimizer=opt, loss=loss, metrics=["accuracy"])

現在，我們再次進行fit：

    model.fit(tf_train_dataset, validation_data=tf_validation_dataset, epochs=3)

💡 如果您想在訓練期間自動將模型上傳到 Hub，您可以在 `model.fit()` 方法中傳遞 `PushToHubCallback`。 我們將在 第四章 中進行介紹

### 模型預測

訓練和觀察的loss下降都非常好，但是如果我們想從訓練後的模型中獲得輸出，或者計算一些指標，或者在生產中使用模型呢？ 為此，我們可以使用`predict()` 方法。 這將返回模型的輸出頭的logits數值，每個類一個。

    preds = model.predict(tf_validation_dataset)["logits"]

我們可以將這些 logit 轉換為模型的類別預測，方法是使用 argmax 找到最高的 logit，它對應於最有可能的類別：

    class_preds = np.argmax(preds, axis=1)
    print(preds.shape, class_preds.shape)

    (408, 2) (408,)

現在，讓我們使用這些 `preds` 來計算一些指標！ 我們可以像加載數據集一樣輕鬆地加載與 MRPC 數據集相關的指標，這次使用的是 `evaluate.load()` 函數。 返回的對象有一個 `compute()` 方法，我們可以使用它來進行度量計算：

    import evaluate

    metric = evaluate.load("glue", "mrpc")
    metric.compute(predictions=class_preds, references=raw_datasets["validation"]["label"])

    {'accuracy': 0.8578431372549019, 'f1': 0.8996539792387542}

您獲得的確切結果可能會有所不同，因為模型頭的隨機初始化可能會改變它獲得的指標。 在這裡，我們可以看到我們的模型在驗證集上的準確率為 85.78%，F1 得分為 89.97。 這些是用於評估 GLUE 基準的 MRPC 數據集結果的兩個指標。 BERT 論文 中的表格報告了基本模型的 F1 分數為 88.9。 那是 `uncased` 模型，而我們目前使用的是 `cased` 模型，這解釋了為什麼我們會獲得更好的結果。

使用 Keras API 進行微調的介紹到此結束。 第 7 章將給出對大多數常見 NLP 任務執行此操作的示例。如果您想在 Keras API 上磨練自己的技能，請嘗試使第二節所使用的的數據處理在 GLUE SST-2 數據集上微調模型。