Files
fchollet--deep-learning-wit…/chapter15_language-models-and-the-transformer.ipynb
2026-07-13 13:25:23 +08:00

1193 lines
32 KiB
Plaintext

{
"cells": [
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text"
},
"source": [
"This is a companion notebook for the book [Deep Learning with Python, Third Edition](https://www.manning.com/books/deep-learning-with-python-third-edition). For readability, it only contains runnable code blocks and section titles, and omits everything else in the book: text paragraphs, figures, and pseudocode.\n\n**If you want to be able to follow what's going on, I recommend reading the notebook side by side with your copy of the book.**\n\nThe book's contents are available online at [deeplearningwithpython.io](https://deeplearningwithpython.io)."
]
},
{
"cell_type": "code",
"execution_count": 0,
"metadata": {
"colab_type": "code"
},
"outputs": [],
"source": [
"!pip install keras keras-hub --upgrade -q"
]
},
{
"cell_type": "code",
"execution_count": 0,
"metadata": {
"colab_type": "code"
},
"outputs": [],
"source": [
"import os\n",
"os.environ[\"KERAS_BACKEND\"] = \"jax\""
]
},
{
"cell_type": "code",
"execution_count": 0,
"metadata": {
"cellView": "form",
"colab_type": "code"
},
"outputs": [],
"source": [
"# @title\n",
"import os\n",
"from IPython.core.magic import register_cell_magic\n",
"\n",
"@register_cell_magic\n",
"def backend(line, cell):\n",
" current, required = os.environ.get(\"KERAS_BACKEND\", \"\"), line.split()[-1]\n",
" if current == required:\n",
" get_ipython().run_cell(cell)\n",
" else:\n",
" print(\n",
" f\"This cell requires the {required} backend. To run it, change KERAS_BACKEND to \"\n",
" f\"\\\"{required}\\\" at the top of the notebook, restart the runtime, and rerun the notebook.\"\n",
" )"
]
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text"
},
"source": [
"## Language models and the Transformer"
]
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text"
},
"source": [
"### The language model"
]
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text"
},
"source": [
"#### Training a Shakespeare language model"
]
},
{
"cell_type": "code",
"execution_count": 0,
"metadata": {
"colab_type": "code"
},
"outputs": [],
"source": [
"import keras\n",
"\n",
"filename = keras.utils.get_file(\n",
" origin=(\n",
" \"https://storage.googleapis.com/download.tensorflow.org/\"\n",
" \"data/shakespeare.txt\"\n",
" ),\n",
")\n",
"shakespeare = open(filename, \"r\").read()"
]
},
{
"cell_type": "code",
"execution_count": 0,
"metadata": {
"colab_type": "code"
},
"outputs": [],
"source": [
"shakespeare[:250]"
]
},
{
"cell_type": "code",
"execution_count": 0,
"metadata": {
"colab_type": "code"
},
"outputs": [],
"source": [
"import tensorflow as tf\n",
"\n",
"sequence_length = 100\n",
"\n",
"def split_input(input, sequence_length):\n",
" for i in range(0, len(input), sequence_length):\n",
" yield input[i : i + sequence_length]\n",
"\n",
"features = list(split_input(shakespeare[:-1], sequence_length))\n",
"labels = list(split_input(shakespeare[1:], sequence_length))\n",
"dataset = tf.data.Dataset.from_tensor_slices((features, labels))"
]
},
{
"cell_type": "code",
"execution_count": 0,
"metadata": {
"colab_type": "code"
},
"outputs": [],
"source": [
"x, y = next(dataset.as_numpy_iterator())\n",
"x[:50], y[:50]"
]
},
{
"cell_type": "code",
"execution_count": 0,
"metadata": {
"colab_type": "code"
},
"outputs": [],
"source": [
"from keras import layers\n",
"\n",
"tokenizer = layers.TextVectorization(\n",
" standardize=None,\n",
" split=\"character\",\n",
" output_sequence_length=sequence_length,\n",
")\n",
"tokenizer.adapt(dataset.map(lambda text, labels: text))"
]
},
{
"cell_type": "code",
"execution_count": 0,
"metadata": {
"colab_type": "code"
},
"outputs": [],
"source": [
"vocabulary_size = tokenizer.vocabulary_size()\n",
"vocabulary_size"
]
},
{
"cell_type": "code",
"execution_count": 0,
"metadata": {
"colab_type": "code"
},
"outputs": [],
"source": [
"dataset = dataset.map(\n",
" lambda features, labels: (tokenizer(features), tokenizer(labels)),\n",
" num_parallel_calls=8,\n",
")\n",
"training_data = dataset.shuffle(10_000).batch(64).cache()"
]
},
{
"cell_type": "code",
"execution_count": 0,
"metadata": {
"colab_type": "code"
},
"outputs": [],
"source": [
"embedding_dim = 256\n",
"hidden_dim = 1024\n",
"\n",
"inputs = layers.Input(shape=(sequence_length,), dtype=\"int\", name=\"token_ids\")\n",
"x = layers.Embedding(vocabulary_size, embedding_dim)(inputs)\n",
"x = layers.GRU(hidden_dim, return_sequences=True)(x)\n",
"x = layers.Dropout(0.1)(x)\n",
"outputs = layers.Dense(vocabulary_size, activation=\"softmax\")(x)\n",
"model = keras.Model(inputs, outputs)"
]
},
{
"cell_type": "code",
"execution_count": 0,
"metadata": {
"colab_type": "code"
},
"outputs": [],
"source": [
"model.summary(line_length=80)"
]
},
{
"cell_type": "code",
"execution_count": 0,
"metadata": {
"colab_type": "code"
},
"outputs": [],
"source": [
"model.compile(\n",
" optimizer=\"adam\",\n",
" loss=\"sparse_categorical_crossentropy\",\n",
" metrics=[\"sparse_categorical_accuracy\"],\n",
")\n",
"model.fit(training_data, epochs=20)"
]
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text"
},
"source": [
"#### Generating Shakespeare"
]
},
{
"cell_type": "code",
"execution_count": 0,
"metadata": {
"colab_type": "code"
},
"outputs": [],
"source": [
"inputs = keras.Input(shape=(1,), dtype=\"int\", name=\"token_ids\")\n",
"input_state = keras.Input(shape=(hidden_dim,), name=\"state\")\n",
"\n",
"x = layers.Embedding(vocabulary_size, embedding_dim)(inputs)\n",
"x, output_state = layers.GRU(hidden_dim, return_state=True)(\n",
" x, initial_state=input_state\n",
")\n",
"outputs = layers.Dense(vocabulary_size, activation=\"softmax\")(x)\n",
"generation_model = keras.Model(\n",
" inputs=(inputs, input_state),\n",
" outputs=(outputs, output_state),\n",
")\n",
"generation_model.set_weights(model.get_weights())"
]
},
{
"cell_type": "code",
"execution_count": 0,
"metadata": {
"colab_type": "code"
},
"outputs": [],
"source": [
"tokens = tokenizer.get_vocabulary()\n",
"token_ids = range(vocabulary_size)\n",
"char_to_id = dict(zip(tokens, token_ids))\n",
"id_to_char = dict(zip(token_ids, tokens))\n",
"\n",
"prompt = \"\"\"\n",
"KING RICHARD III:\n",
"\"\"\""
]
},
{
"cell_type": "code",
"execution_count": 0,
"metadata": {
"colab_type": "code"
},
"outputs": [],
"source": [
"input_ids = [char_to_id[c] for c in prompt]\n",
"state = keras.ops.zeros(shape=(1, hidden_dim))\n",
"for token_id in input_ids:\n",
" inputs = keras.ops.expand_dims([token_id], axis=0)\n",
" predictions, state = generation_model.predict((inputs, state), verbose=0)"
]
},
{
"cell_type": "code",
"execution_count": 0,
"metadata": {
"colab_type": "code"
},
"outputs": [],
"source": [
"import numpy as np\n",
"\n",
"generated_ids = []\n",
"max_length = 250\n",
"for i in range(max_length):\n",
" next_char = int(np.argmax(predictions, axis=-1)[0])\n",
" generated_ids.append(next_char)\n",
" inputs = keras.ops.expand_dims([next_char], axis=0)\n",
" predictions, state = generation_model.predict((inputs, state), verbose=0)"
]
},
{
"cell_type": "code",
"execution_count": 0,
"metadata": {
"colab_type": "code"
},
"outputs": [],
"source": [
"output = \"\".join([id_to_char[token_id] for token_id in generated_ids])\n",
"print(prompt + output)"
]
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text"
},
"source": [
"### Sequence-to-sequence learning"
]
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text"
},
"source": [
"#### English-to-Spanish translation"
]
},
{
"cell_type": "code",
"execution_count": 0,
"metadata": {
"colab_type": "code"
},
"outputs": [],
"source": [
"import pathlib\n",
"\n",
"zip_path = keras.utils.get_file(\n",
" origin=(\n",
" \"http://storage.googleapis.com/download.tensorflow.org/data/spa-eng.zip\"\n",
" ),\n",
" fname=\"spa-eng\",\n",
" extract=True,\n",
")\n",
"text_path = pathlib.Path(zip_path) / \"spa-eng\" / \"spa.txt\""
]
},
{
"cell_type": "code",
"execution_count": 0,
"metadata": {
"colab_type": "code"
},
"outputs": [],
"source": [
"with open(text_path) as f:\n",
" lines = f.read().split(\"\\n\")[:-1]\n",
"text_pairs = []\n",
"for line in lines:\n",
" english, spanish = line.split(\"\\t\")\n",
" spanish = \"[start] \" + spanish + \" [end]\"\n",
" text_pairs.append((english, spanish))"
]
},
{
"cell_type": "code",
"execution_count": 0,
"metadata": {
"colab_type": "code"
},
"outputs": [],
"source": [
"import random\n",
"random.choice(text_pairs)"
]
},
{
"cell_type": "code",
"execution_count": 0,
"metadata": {
"colab_type": "code"
},
"outputs": [],
"source": [
"import random\n",
"\n",
"random.shuffle(text_pairs)\n",
"val_samples = int(0.15 * len(text_pairs))\n",
"train_samples = len(text_pairs) - 2 * val_samples\n",
"train_pairs = text_pairs[:train_samples]\n",
"val_pairs = text_pairs[train_samples : train_samples + val_samples]\n",
"test_pairs = text_pairs[train_samples + val_samples :]"
]
},
{
"cell_type": "code",
"execution_count": 0,
"metadata": {
"colab_type": "code"
},
"outputs": [],
"source": [
"import string\n",
"import re\n",
"\n",
"strip_chars = string.punctuation + \"\u00bf\"\n",
"strip_chars = strip_chars.replace(\"[\", \"\")\n",
"strip_chars = strip_chars.replace(\"]\", \"\")\n",
"\n",
"def custom_standardization(input_string):\n",
" lowercase = tf.strings.lower(input_string)\n",
" return tf.strings.regex_replace(\n",
" lowercase, f\"[{re.escape(strip_chars)}]\", \"\"\n",
" )\n",
"\n",
"vocab_size = 15000\n",
"sequence_length = 20\n",
"\n",
"english_tokenizer = layers.TextVectorization(\n",
" max_tokens=vocab_size,\n",
" output_mode=\"int\",\n",
" output_sequence_length=sequence_length,\n",
")\n",
"spanish_tokenizer = layers.TextVectorization(\n",
" max_tokens=vocab_size,\n",
" output_mode=\"int\",\n",
" output_sequence_length=sequence_length + 1,\n",
" standardize=custom_standardization,\n",
")\n",
"train_english_texts = [pair[0] for pair in train_pairs]\n",
"train_spanish_texts = [pair[1] for pair in train_pairs]\n",
"english_tokenizer.adapt(train_english_texts)\n",
"spanish_tokenizer.adapt(train_spanish_texts)"
]
},
{
"cell_type": "code",
"execution_count": 0,
"metadata": {
"colab_type": "code"
},
"outputs": [],
"source": [
"batch_size = 64\n",
"\n",
"def format_dataset(eng, spa):\n",
" eng = english_tokenizer(eng)\n",
" spa = spanish_tokenizer(spa)\n",
" features = {\"english\": eng, \"spanish\": spa[:, :-1]}\n",
" labels = spa[:, 1:]\n",
" sample_weights = labels != 0\n",
" return features, labels, sample_weights\n",
"\n",
"def make_dataset(pairs):\n",
" eng_texts, spa_texts = zip(*pairs)\n",
" eng_texts = list(eng_texts)\n",
" spa_texts = list(spa_texts)\n",
" dataset = tf.data.Dataset.from_tensor_slices((eng_texts, spa_texts))\n",
" dataset = dataset.batch(batch_size)\n",
" dataset = dataset.map(format_dataset, num_parallel_calls=4)\n",
" return dataset.shuffle(2048).cache()\n",
"\n",
"train_ds = make_dataset(train_pairs)\n",
"val_ds = make_dataset(val_pairs)"
]
},
{
"cell_type": "code",
"execution_count": 0,
"metadata": {
"colab_type": "code"
},
"outputs": [],
"source": [
"inputs, targets, sample_weights = next(iter(train_ds))\n",
"print(inputs[\"english\"].shape)"
]
},
{
"cell_type": "code",
"execution_count": 0,
"metadata": {
"colab_type": "code"
},
"outputs": [],
"source": [
"print(inputs[\"spanish\"].shape)"
]
},
{
"cell_type": "code",
"execution_count": 0,
"metadata": {
"colab_type": "code"
},
"outputs": [],
"source": [
"print(targets.shape)"
]
},
{
"cell_type": "code",
"execution_count": 0,
"metadata": {
"colab_type": "code"
},
"outputs": [],
"source": [
"print(sample_weights.shape)"
]
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text"
},
"source": [
"#### Sequence-to-sequence learning with RNNs"
]
},
{
"cell_type": "code",
"execution_count": 0,
"metadata": {
"colab_type": "code"
},
"outputs": [],
"source": [
"embed_dim = 256\n",
"hidden_dim = 1024\n",
"\n",
"source = keras.Input(shape=(None,), dtype=\"int32\", name=\"english\")\n",
"x = layers.Embedding(vocab_size, embed_dim, mask_zero=True)(source)\n",
"rnn_layer = layers.GRU(hidden_dim)\n",
"rnn_layer = layers.Bidirectional(rnn_layer, merge_mode=\"sum\")\n",
"encoder_output = rnn_layer(x)"
]
},
{
"cell_type": "code",
"execution_count": 0,
"metadata": {
"colab_type": "code"
},
"outputs": [],
"source": [
"target = keras.Input(shape=(None,), dtype=\"int32\", name=\"spanish\")\n",
"x = layers.Embedding(vocab_size, embed_dim, mask_zero=True)(target)\n",
"rnn_layer = layers.GRU(hidden_dim, return_sequences=True)\n",
"x = rnn_layer(x, initial_state=encoder_output)\n",
"x = layers.Dropout(0.5)(x)\n",
"target_predictions = layers.Dense(vocab_size, activation=\"softmax\")(x)\n",
"seq2seq_rnn = keras.Model([source, target], target_predictions)"
]
},
{
"cell_type": "code",
"execution_count": 0,
"metadata": {
"colab_type": "code"
},
"outputs": [],
"source": [
"seq2seq_rnn.summary(line_length=80)"
]
},
{
"cell_type": "code",
"execution_count": 0,
"metadata": {
"colab_type": "code"
},
"outputs": [],
"source": [
"seq2seq_rnn.compile(\n",
" optimizer=\"adam\",\n",
" loss=\"sparse_categorical_crossentropy\",\n",
" weighted_metrics=[\"accuracy\"],\n",
")\n",
"seq2seq_rnn.fit(train_ds, epochs=15, validation_data=val_ds)"
]
},
{
"cell_type": "code",
"execution_count": 0,
"metadata": {
"colab_type": "code"
},
"outputs": [],
"source": [
"import numpy as np\n",
"\n",
"spa_vocab = spanish_tokenizer.get_vocabulary()\n",
"spa_index_lookup = dict(zip(range(len(spa_vocab)), spa_vocab))\n",
"\n",
"def generate_translation(input_sentence):\n",
" tokenized_input_sentence = english_tokenizer([input_sentence])\n",
" decoded_sentence = \"[start]\"\n",
" for i in range(sequence_length):\n",
" tokenized_target_sentence = spanish_tokenizer([decoded_sentence])\n",
" inputs = [tokenized_input_sentence, tokenized_target_sentence]\n",
" next_token_predictions = seq2seq_rnn.predict(inputs, verbose=0)\n",
" sampled_token_index = np.argmax(next_token_predictions[0, i, :])\n",
" sampled_token = spa_index_lookup[sampled_token_index]\n",
" decoded_sentence += \" \" + sampled_token\n",
" if sampled_token == \"[end]\":\n",
" break\n",
" return decoded_sentence\n",
"\n",
"test_eng_texts = [pair[0] for pair in test_pairs]\n",
"for _ in range(5):\n",
" input_sentence = random.choice(test_eng_texts)\n",
" print(\"-\")\n",
" print(input_sentence)\n",
" print(generate_translation(input_sentence))"
]
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text"
},
"source": [
"### The Transformer architecture"
]
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text"
},
"source": [
"#### Dot-product attention"
]
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text"
},
"source": [
"#### Transformer encoder block"
]
},
{
"cell_type": "code",
"execution_count": 0,
"metadata": {
"colab_type": "code"
},
"outputs": [],
"source": [
"class TransformerEncoder(keras.Layer):\n",
" def __init__(self, hidden_dim, intermediate_dim, num_heads):\n",
" super().__init__()\n",
" key_dim = hidden_dim // num_heads\n",
" self.self_attention = layers.MultiHeadAttention(num_heads, key_dim)\n",
" self.self_attention_layernorm = layers.LayerNormalization()\n",
" self.feed_forward_1 = layers.Dense(intermediate_dim, activation=\"relu\")\n",
" self.feed_forward_2 = layers.Dense(hidden_dim)\n",
" self.feed_forward_layernorm = layers.LayerNormalization()\n",
"\n",
" def call(self, source, source_mask):\n",
" residual = x = source\n",
" mask = source_mask[:, None, :]\n",
" x = self.self_attention(query=x, key=x, value=x, attention_mask=mask)\n",
" x = x + residual\n",
" x = self.self_attention_layernorm(x)\n",
" residual = x\n",
" x = self.feed_forward_1(x)\n",
" x = self.feed_forward_2(x)\n",
" x = x + residual\n",
" x = self.feed_forward_layernorm(x)\n",
" return x"
]
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text"
},
"source": [
"#### Transformer decoder block"
]
},
{
"cell_type": "code",
"execution_count": 0,
"metadata": {
"colab_type": "code"
},
"outputs": [],
"source": [
"class TransformerDecoder(keras.Layer):\n",
" def __init__(self, hidden_dim, intermediate_dim, num_heads):\n",
" super().__init__()\n",
" key_dim = hidden_dim // num_heads\n",
" self.self_attention = layers.MultiHeadAttention(num_heads, key_dim)\n",
" self.self_attention_layernorm = layers.LayerNormalization()\n",
" self.cross_attention = layers.MultiHeadAttention(num_heads, key_dim)\n",
" self.cross_attention_layernorm = layers.LayerNormalization()\n",
" self.feed_forward_1 = layers.Dense(intermediate_dim, activation=\"relu\")\n",
" self.feed_forward_2 = layers.Dense(hidden_dim)\n",
" self.feed_forward_layernorm = layers.LayerNormalization()\n",
"\n",
" def call(self, target, source, source_mask):\n",
" residual = x = target\n",
" x = self.self_attention(query=x, key=x, value=x, use_causal_mask=True)\n",
" x = x + residual\n",
" x = self.self_attention_layernorm(x)\n",
" residual = x\n",
" mask = source_mask[:, None, :]\n",
" x = self.cross_attention(\n",
" query=x, key=source, value=source, attention_mask=mask\n",
" )\n",
" x = x + residual\n",
" x = self.cross_attention_layernorm(x)\n",
" residual = x\n",
" x = self.feed_forward_1(x)\n",
" x = self.feed_forward_2(x)\n",
" x = x + residual\n",
" x = self.feed_forward_layernorm(x)\n",
" return x"
]
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text"
},
"source": [
"#### Sequence-to-sequence learning with a Transformer"
]
},
{
"cell_type": "code",
"execution_count": 0,
"metadata": {
"colab_type": "code"
},
"outputs": [],
"source": [
"hidden_dim = 256\n",
"intermediate_dim = 2048\n",
"num_heads = 8\n",
"\n",
"source = keras.Input(shape=(None,), dtype=\"int32\", name=\"english\")\n",
"x = layers.Embedding(vocab_size, hidden_dim)(source)\n",
"encoder_output = TransformerEncoder(hidden_dim, intermediate_dim, num_heads)(\n",
" source=x,\n",
" source_mask=source != 0,\n",
")\n",
"\n",
"target = keras.Input(shape=(None,), dtype=\"int32\", name=\"spanish\")\n",
"x = layers.Embedding(vocab_size, hidden_dim)(target)\n",
"x = TransformerDecoder(hidden_dim, intermediate_dim, num_heads)(\n",
" target=x,\n",
" source=encoder_output,\n",
" source_mask=source != 0,\n",
")\n",
"x = layers.Dropout(0.5)(x)\n",
"target_predictions = layers.Dense(vocab_size, activation=\"softmax\")(x)\n",
"transformer = keras.Model([source, target], target_predictions)"
]
},
{
"cell_type": "code",
"execution_count": 0,
"metadata": {
"colab_type": "code"
},
"outputs": [],
"source": [
"transformer.summary(line_length=80)"
]
},
{
"cell_type": "code",
"execution_count": 0,
"metadata": {
"colab_type": "code"
},
"outputs": [],
"source": [
"transformer.compile(\n",
" optimizer=\"adam\",\n",
" loss=\"sparse_categorical_crossentropy\",\n",
" weighted_metrics=[\"accuracy\"],\n",
")\n",
"transformer.fit(train_ds, epochs=15, validation_data=val_ds)"
]
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text"
},
"source": [
"#### Embedding positional information"
]
},
{
"cell_type": "code",
"execution_count": 0,
"metadata": {
"colab_type": "code"
},
"outputs": [],
"source": [
"from keras import ops\n",
"\n",
"class PositionalEmbedding(keras.Layer):\n",
" def __init__(self, sequence_length, input_dim, output_dim):\n",
" super().__init__()\n",
" self.token_embeddings = layers.Embedding(input_dim, output_dim)\n",
" self.position_embeddings = layers.Embedding(sequence_length, output_dim)\n",
"\n",
" def call(self, inputs):\n",
" positions = ops.cumsum(ops.ones_like(inputs), axis=-1) - 1\n",
" embedded_tokens = self.token_embeddings(inputs)\n",
" embedded_positions = self.position_embeddings(positions)\n",
" return embedded_tokens + embedded_positions"
]
},
{
"cell_type": "code",
"execution_count": 0,
"metadata": {
"colab_type": "code"
},
"outputs": [],
"source": [
"hidden_dim = 256\n",
"intermediate_dim = 2056\n",
"num_heads = 8\n",
"\n",
"source = keras.Input(shape=(None,), dtype=\"int32\", name=\"english\")\n",
"x = PositionalEmbedding(sequence_length, vocab_size, hidden_dim)(source)\n",
"encoder_output = TransformerEncoder(hidden_dim, intermediate_dim, num_heads)(\n",
" source=x,\n",
" source_mask=source != 0,\n",
")\n",
"\n",
"target = keras.Input(shape=(None,), dtype=\"int32\", name=\"spanish\")\n",
"x = PositionalEmbedding(sequence_length, vocab_size, hidden_dim)(target)\n",
"x = TransformerDecoder(hidden_dim, intermediate_dim, num_heads)(\n",
" target=x,\n",
" source=encoder_output,\n",
" source_mask=source != 0,\n",
")\n",
"x = layers.Dropout(0.5)(x)\n",
"target_predictions = layers.Dense(vocab_size, activation=\"softmax\")(x)\n",
"transformer = keras.Model([source, target], target_predictions)"
]
},
{
"cell_type": "code",
"execution_count": 0,
"metadata": {
"colab_type": "code"
},
"outputs": [],
"source": [
"transformer.compile(\n",
" optimizer=\"adam\",\n",
" loss=\"sparse_categorical_crossentropy\",\n",
" weighted_metrics=[\"accuracy\"],\n",
")\n",
"transformer.fit(train_ds, epochs=30, validation_data=val_ds)"
]
},
{
"cell_type": "code",
"execution_count": 0,
"metadata": {
"colab_type": "code"
},
"outputs": [],
"source": [
"import numpy as np\n",
"\n",
"spa_vocab = spanish_tokenizer.get_vocabulary()\n",
"spa_index_lookup = dict(zip(range(len(spa_vocab)), spa_vocab))\n",
"\n",
"def generate_translation(input_sentence):\n",
" tokenized_input_sentence = english_tokenizer([input_sentence])\n",
" decoded_sentence = \"[start]\"\n",
" for i in range(sequence_length):\n",
" tokenized_target_sentence = spanish_tokenizer([decoded_sentence])\n",
" tokenized_target_sentence = tokenized_target_sentence[:, :-1]\n",
" inputs = [tokenized_input_sentence, tokenized_target_sentence]\n",
" next_token_predictions = transformer.predict(inputs, verbose=0)\n",
" sampled_token_index = np.argmax(next_token_predictions[0, i, :])\n",
" sampled_token = spa_index_lookup[sampled_token_index]\n",
" decoded_sentence += \" \" + sampled_token\n",
" if sampled_token == \"[end]\":\n",
" break\n",
" return decoded_sentence\n",
"\n",
"test_eng_texts = [pair[0] for pair in test_pairs]\n",
"for _ in range(5):\n",
" input_sentence = random.choice(test_eng_texts)\n",
" print(\"-\")\n",
" print(input_sentence)\n",
" print(generate_translation(input_sentence))"
]
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text"
},
"source": [
"### Classification with a pretrained Transformer"
]
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text"
},
"source": [
"#### Pretraining a Transformer encoder"
]
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text"
},
"source": [
"#### Loading a pretrained Transformer"
]
},
{
"cell_type": "code",
"execution_count": 0,
"metadata": {
"colab_type": "code"
},
"outputs": [],
"source": [
"import keras_hub\n",
"\n",
"tokenizer = keras_hub.models.Tokenizer.from_preset(\"roberta_base_en\")\n",
"backbone = keras_hub.models.Backbone.from_preset(\"roberta_base_en\")"
]
},
{
"cell_type": "code",
"execution_count": 0,
"metadata": {
"colab_type": "code"
},
"outputs": [],
"source": [
"tokenizer(\"The quick brown fox\")"
]
},
{
"cell_type": "code",
"execution_count": 0,
"metadata": {
"colab_type": "code"
},
"outputs": [],
"source": [
"backbone.summary(line_length=80)"
]
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text"
},
"source": [
"#### Preprocessing IMDb movie reviews"
]
},
{
"cell_type": "code",
"execution_count": 0,
"metadata": {
"colab_type": "code"
},
"outputs": [],
"source": [
"import os, pathlib, shutil, random\n",
"\n",
"zip_path = keras.utils.get_file(\n",
" origin=\"https://ai.stanford.edu/~amaas/data/sentiment/aclImdb_v1.tar.gz\",\n",
" fname=\"imdb\",\n",
" extract=True,\n",
")\n",
"\n",
"imdb_extract_dir = pathlib.Path(zip_path) / \"aclImdb\"\n",
"train_dir = pathlib.Path(\"imdb_train\")\n",
"test_dir = pathlib.Path(\"imdb_test\")\n",
"val_dir = pathlib.Path(\"imdb_val\")\n",
"\n",
"shutil.copytree(imdb_extract_dir / \"test\", test_dir, dirs_exist_ok=True)\n",
"\n",
"val_percentage = 0.2\n",
"for category in (\"neg\", \"pos\"):\n",
" src_dir = imdb_extract_dir / \"train\" / category\n",
" src_files = os.listdir(src_dir)\n",
" random.Random(1337).shuffle(src_files)\n",
" num_val_samples = int(len(src_files) * val_percentage)\n",
"\n",
" os.makedirs(train_dir / category, exist_ok=True)\n",
" os.makedirs(val_dir / category, exist_ok=True)\n",
" for index, file in enumerate(src_files):\n",
" if index < num_val_samples:\n",
" shutil.copy(src_dir / file, val_dir / category / file)\n",
" else:\n",
" shutil.copy(src_dir / file, train_dir / category / file)"
]
},
{
"cell_type": "code",
"execution_count": 0,
"metadata": {
"colab_type": "code"
},
"outputs": [],
"source": [
"from keras.utils import text_dataset_from_directory\n",
"\n",
"batch_size = 16\n",
"train_ds = text_dataset_from_directory(train_dir, batch_size=batch_size)\n",
"val_ds = text_dataset_from_directory(val_dir, batch_size=batch_size)\n",
"test_ds = text_dataset_from_directory(test_dir, batch_size=batch_size)"
]
},
{
"cell_type": "code",
"execution_count": 0,
"metadata": {
"colab_type": "code"
},
"outputs": [],
"source": [
"def preprocess(text, label):\n",
" packer = keras_hub.layers.StartEndPacker(\n",
" sequence_length=512,\n",
" start_value=tokenizer.start_token_id,\n",
" end_value=tokenizer.end_token_id,\n",
" pad_value=tokenizer.pad_token_id,\n",
" return_padding_mask=True,\n",
" )\n",
" token_ids, padding_mask = packer(tokenizer(text))\n",
" return {\"token_ids\": token_ids, \"padding_mask\": padding_mask}, label\n",
"\n",
"preprocessed_train_ds = train_ds.map(preprocess)\n",
"preprocessed_val_ds = val_ds.map(preprocess)\n",
"preprocessed_test_ds = test_ds.map(preprocess)"
]
},
{
"cell_type": "code",
"execution_count": 0,
"metadata": {
"colab_type": "code"
},
"outputs": [],
"source": [
"next(iter(preprocessed_train_ds))"
]
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text"
},
"source": [
"#### Fine-tuning a pretrained Transformer"
]
},
{
"cell_type": "code",
"execution_count": 0,
"metadata": {
"colab_type": "code"
},
"outputs": [],
"source": [
"inputs = backbone.input\n",
"x = backbone(inputs)\n",
"x = x[:, 0, :]\n",
"x = layers.Dropout(0.1)(x)\n",
"x = layers.Dense(768, activation=\"relu\")(x)\n",
"x = layers.Dropout(0.1)(x)\n",
"outputs = layers.Dense(1, activation=\"sigmoid\")(x)\n",
"classifier = keras.Model(inputs, outputs)"
]
},
{
"cell_type": "code",
"execution_count": 0,
"metadata": {
"colab_type": "code"
},
"outputs": [],
"source": [
"classifier.compile(\n",
" optimizer=keras.optimizers.Adam(5e-5),\n",
" loss=\"binary_crossentropy\",\n",
" metrics=[\"accuracy\"],\n",
")\n",
"classifier.fit(\n",
" preprocessed_train_ds,\n",
" validation_data=preprocessed_val_ds,\n",
")"
]
},
{
"cell_type": "code",
"execution_count": 0,
"metadata": {
"colab_type": "code"
},
"outputs": [],
"source": [
"classifier.evaluate(preprocessed_test_ds)"
]
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text"
},
"source": [
"### What makes the Transformer effective?"
]
}
],
"metadata": {
"accelerator": "GPU",
"colab": {
"collapsed_sections": [],
"name": "chapter15_language-models-and-the-transformer",
"private_outputs": false,
"provenance": [],
"toc_visible": true
},
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.10.0"
}
},
"nbformat": 4,
"nbformat_minor": 0
}