暖啟動嵌入層矩陣

在 TensorFlow.org 上檢視 在 Google Colab 中執行 在 GitHub 上檢視 下載筆記本

本教學課程示範如何在使用 tf.keras.utils.warmstart_embedding_matrix API 變更詞彙表時,針對文字情緒分類進行「暖啟動」訓練。

您將從使用基本詞彙表訓練簡單的 Keras 模型開始,然後在更新詞彙表後,繼續訓練模型。這稱為「暖啟動」訓練,為此您需要為新詞彙表重新對應文字嵌入矩陣。

嵌入矩陣

嵌入提供一種使用有效率的密集表示法的方式,其中相似的詞彙表符記具有相似的編碼。它們是可訓練的參數 (模型在訓練期間學習的權重,與模型學習密集層權重的方式相同)。對於小型資料集,常見的嵌入維度為 8 維,而對於大型資料集,則高達 1024 維。較高維度的嵌入可以捕捉單字之間更精細的關係,但可能需要更多資料才能學習。

詞彙表

一組獨特的單字稱為詞彙表。若要建構文字模型,您需要選擇固定的詞彙表。通常,您會從資料集中最常見的單字建立詞彙表。詞彙表可讓我們將每段文字表示為 ID 序列,您可以在嵌入矩陣中查詢這些 ID。詞彙表可讓我們透過文字中出現的特定單字來表示每段文字。

為何暖啟動嵌入矩陣?

模型是使用一組代表指定詞彙表的嵌入來訓練的。如果模型需要更新或改進,您可以透過重複使用先前執行中的權重,顯著加快訓練達到收斂的速度。重複使用先前執行中的嵌入矩陣更困難。問題在於,詞彙表的任何變更都會使單字到 ID 的對應失效。

tf.keras.utils.warmstart_embedding_matrix 透過從基本詞彙表的嵌入矩陣為新詞彙表建立嵌入矩陣來解決此問題。在單字同時存在於兩個詞彙表中的情況下,基本嵌入向量會複製到新嵌入矩陣中的正確位置。這可讓您在詞彙表的大小或順序發生任何變更後,暖啟動訓練。

設定

pip install --pre -U "tensorflow>2.10"  # Requires 2.11
import io
import numpy as np
import os
import re
import shutil
import string
import tensorflow as tf

from tensorflow.keras import Model
from tensorflow.keras.layers import Dense, Embedding, GlobalAveragePooling1D
from tensorflow.keras.layers import TextVectorization

載入資料集

本教學課程使用大型電影評論資料集。您將在此資料集上訓練情緒分類器模型,並在此過程中從頭開始學習嵌入。請參閱載入文字教學課程以瞭解詳情。

使用 Keras 檔案公用程式下載資料集並檢閱目錄。

url = "https://ai.stanford.edu/~amaas/data/sentiment/aclImdb_v1.tar.gz"

dataset = tf.keras.utils.get_file(
    "aclImdb_v1.tar.gz", url, untar=True, cache_dir=".", cache_subdir=""
)

dataset_dir = os.path.join(os.path.dirname(dataset), "aclImdb")
os.listdir(dataset_dir)

train/ 目錄具有 posneg 資料夾,其中電影評論分別標記為正面和負面。您將使用 posneg 資料夾中的評論來訓練二元分類模型。

train_dir = os.path.join(dataset_dir, "train")
os.listdir(train_dir)

train 目錄也包含其他資料夾,應先移除這些資料夾,再建立訓練集。

remove_dir = os.path.join(train_dir, "unsup")
shutil.rmtree(remove_dir)

接下來,使用 tf.keras.utils.text_dataset_from_directory 建立 tf.data.Dataset。您可以在此文字分類教學課程中深入瞭解如何使用此公用程式。

使用 train 目錄建立訓練集和驗證集,並將 20% 分割用於驗證。

batch_size = 1024
seed = 123
train_ds = tf.keras.utils.text_dataset_from_directory(
    "aclImdb/train",
    batch_size=batch_size,
    validation_split=0.2,
    subset="training",
    seed=seed,
)
val_ds = tf.keras.utils.text_dataset_from_directory(
    "aclImdb/train",
    batch_size=batch_size,
    validation_split=0.2,
    subset="validation",
    seed=seed,
)

設定資料集以提升效能

您可以深入瞭解 Dataset.cacheDataset.prefetch,以及如何在資料效能指南中將資料快取到磁碟。

AUTOTUNE = tf.data.AUTOTUNE

train_ds = train_ds.cache().prefetch(buffer_size=AUTOTUNE)
val_ds = val_ds.cache().prefetch(buffer_size=AUTOTUNE)

文字預處理

接下來,定義情緒分類模型所需的資料集預處理步驟。使用所需的參數初始化 layers.TextVectorization 層,以向量化電影評論。您可以在文字分類教學課程中深入瞭解如何使用此層。

# Create a custom standardization function to strip HTML break tags '<br />'.
def custom_standardization(input_data):
    lowercase = tf.strings.lower(input_data)
    stripped_html = tf.strings.regex_replace(lowercase, "<br />", " ")
    return tf.strings.regex_replace(
        stripped_html, "[%s]" % re.escape(string.punctuation), ""
    )


# Vocabulary size and number of words in a sequence.
vocab_size = 10000
sequence_length = 100

# Use the text vectorization layer to normalize, split, and map strings to
# integers. Note that the layer uses the custom standardization defined above.
# Set maximum_sequence length as all samples are not of the same length.
vectorize_layer = TextVectorization(
    standardize=custom_standardization,
    max_tokens=vocab_size,
    output_mode="int",
    output_sequence_length=sequence_length,
)

# Make a text-only dataset (no labels) and call `Dataset.adapt` to build the
# vocabulary.
text_ds = train_ds.map(lambda x, y: x)
vectorize_layer.adapt(text_ds)

建立分類模型

使用 Keras Sequential API 定義情緒分類模型。

embedding_dim = 16
text_embedding = Embedding(vocab_size, embedding_dim, name="embedding")
text_input = tf.keras.Sequential(
    [vectorize_layer, text_embedding], name="text_input"
)
classifier_head = tf.keras.Sequential(
    [GlobalAveragePooling1D(), Dense(16, activation="relu"), Dense(1)],
    name="classifier_head",
)

model = tf.keras.Sequential([text_input, classifier_head])

編譯和訓練模型

您將使用 TensorBoard 來視覺化指標,包括損失和準確度。建立 tf.keras.callbacks.TensorBoard

tensorboard_callback = tf.keras.callbacks.TensorBoard(log_dir="logs")

使用 Adam 優化器和 BinaryCrossentropy 損失編譯和訓練模型。

model.compile(
    optimizer="adam",
    loss=tf.keras.losses.BinaryCrossentropy(from_logits=True),
    metrics=["accuracy"],
)
model.fit(
    train_ds,
    validation_data=val_ds,
    epochs=15,
    callbacks=[tensorboard_callback],
)

使用此方法,模型可達到約 85% 的驗證準確度

您可以查看模型摘要以深入瞭解模型的每一層。

model.summary()

在 TensorBoard 中視覺化模型指標。

# docs_infra: no_execute
%load_ext tensorboard
%tensorboard --logdir logs

詞彙表重新對應

現在您將更新詞彙表並繼續進行暖啟動訓練。

首先,取得基本詞彙表和嵌入矩陣。

embedding_weights_base = (
    model.get_layer("text_input").get_layer("embedding").embeddings
)
vocab_base = vectorize_layer.get_vocabulary()

定義新的向量化層以產生更大的新詞彙表

# Vocabulary size and number of words in a sequence.
vocab_size_new = 10200
sequence_length = 100

vectorize_layer_new = TextVectorization(
    standardize=custom_standardization,
    max_tokens=vocab_size_new,
    output_mode="int",
    output_sequence_length=sequence_length,
)

# Make a text-only dataset (no labels) and call adapt to build the vocabulary.
text_ds = train_ds.map(lambda x, y: x)
vectorize_layer_new.adapt(text_ds)

# Get the new vocabulary
vocab_new = vectorize_layer_new.get_vocabulary()
# View the new vocabulary tokens that weren't in `vocab_base`
set(vocab_base) ^ set(vocab_new)

使用 keras.utils.warmstart_embedding_matrix 公用程式產生更新的嵌入。

# Generate the updated embedding matrix
updated_embedding = tf.keras.utils.warmstart_embedding_matrix(
    base_vocabulary=vocab_base,
    new_vocabulary=vocab_new,
    base_embeddings=embedding_weights_base,
    new_embeddings_initializer="uniform",
)
# Update the model variable
updated_embedding_variable = tf.Variable(updated_embedding)

如果您有想要用來初始化新嵌入矩陣的嵌入矩陣,請使用 keras.initializers.Constant 作為 new_embeddings 初始化工具。將以下程式碼區塊複製到程式碼儲存格以試用。當您有更好的嵌入矩陣初始化工具可用於詞彙表中的新單字時,這會很有幫助。

# generate updated embedding matrix
new_embedding = np.random.rand(len(vocab_new), 16)
updated_embedding = tf.keras.utils.warmstart_embedding_matrix(
            base_vocabulary=vocab_base,
            new_vocabulary=vocab_new,
            base_embeddings=embedding_weights_base,
            new_embeddings_initializer=tf.keras.initializers.Constant(
                new_embedding
            )
        )
# update model variable
updated_embedding_variable = tf.Variable(updated_embedding)

驗證嵌入矩陣的形狀是否已變更以反映新詞彙表。

updated_embedding_variable.shape

現在您已取得更新的嵌入矩陣,下一步是更新層權重。

text_embedding_layer_new = Embedding(
    vectorize_layer_new.vocabulary_size(), embedding_dim, name="embedding"
)
text_embedding_layer_new.build(input_shape=[None])
text_embedding_layer_new.embeddings.assign(updated_embedding)
text_input_new = tf.keras.Sequential(
    [vectorize_layer_new, text_embedding_layer_new], name="text_input_new"
)
text_input_new.summary()

# Verify the shape of updated weights
# The new weights shape should reflect the new vocabulary size
text_input_new.get_layer("embedding").embeddings.shape

修改模型架構以使用新的文字向量化層。

您也可以從檢查點載入模型,並如下所示更新模型架構。

warm_started_model = tf.keras.Sequential([text_input_new, classifier_head])
warm_started_model.summary()

您已成功更新模型以接受新的詞彙表。嵌入層已更新,可將舊詞彙表單字對應至舊嵌入,並初始化要學習的新詞彙表單字的嵌入。模型其餘部分的已學習權重將保持不變。模型已暖啟動,可從上次停止的位置繼續訓練。

您現在可以驗證重新對應是否成功。取得基本詞彙表和新詞彙表中都存在的詞彙表單字「the」的索引,並比較嵌入值。它們應該相等。

# New vocab words
base_vocab_index = vectorize_layer("the")[0]
new_vocab_index = vectorize_layer_new("the")[0]
print(
    warm_started_model.get_layer("text_input_new").get_layer("embedding")(
        new_vocab_index
    )
    == embedding_weights_base[base_vocab_index]
)

繼續進行暖啟動訓練

請注意訓練如何暖啟動。第一個週期的準確度約為 85%。這與先前訓練結束時的準確度相近。

model.compile(
    optimizer="adam",
    loss=tf.keras.losses.BinaryCrossentropy(from_logits=True),
    metrics=["accuracy"],
)
model.fit(
    train_ds,
    validation_data=val_ds,
    epochs=15,
    callbacks=[tensorboard_callback],
)

視覺化暖啟動訓練

# docs_infra: no_execute
%reload_ext tensorboard
%tensorboard --logdir logs

後續步驟

在本教學課程中,您已學會如何

  • 在小型詞彙表資料集上從頭開始訓練情緒分類模型。
  • 在詞彙表大小變更時,更新模型架構並暖啟動嵌入矩陣。
  • 透過擴展的資料集持續改進模型準確度

若要深入瞭解嵌入,請查看Word2VecTransformer 模型語言理解教學課程。