Pretraining GPT Model on Large Dataset & Finetuning on Shakespeare Dataset¶


  1. Find a dataset that is very large, so large that you can't see a gap between train and val loss. Pretrain the transformer on this data, then initialize with that model and finetune it on tiny shakespeare with a smaller number of steps and lower learning rate. Can you obtain a lower validation loss by the use of pretraining?

    Comment: The validation loss on the shakespeare dataset without pretraining is $\textbf{1.5597}$.
  • Number of steps: $5000$
  • Learning rate: $6e^{-4}$
In [1]:
# This Python 3 environment comes with many helpful analytics libraries installed
# It is defined by the kaggle/python Docker image: https://github.com/kaggle/docker-python
# For example, here's several helpful packages to load

import numpy as np # linear algebra
import pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)

# Input data files are available in the read-only "../input/" directory
# For example, running this (by clicking run or pressing Shift+Enter) will list all files under the input directory

import os
for dirname, _, filenames in os.walk('/kaggle/input'):
    for filename in filenames:
        print(os.path.join(dirname, filename))

# You can write up to 20GB to the current directory (/kaggle/working/) that gets preserved as output when you create a version using "Save & Run All" 
# You can also write temporary files to /kaggle/temp/, but they won't be saved outside of the current session

Let's use the huggingface datasets library to gain access to a large collection of nlp datasets. For this exercise, I've chosen to use the cnn_dailymail dataset, which is contains over $\textbf{300,000}$ online news articles.

This dataset is also already split into a training and validation set, which is nice.

In [30]:
from datasets import load_dataset
from copy import deepcopy
import string
import time, timeit
import math, re
import random
from tqdm import tqdm
import csv, os

import torch
import torch.nn as nn
import torch.nn.functional as F
from torch.utils.data import Dataset, DataLoader

import matplotlib.pyplot as plt
In [3]:
data = load_dataset("ccdv/cnn_dailymail", "1.0.0", trust_remote_code=True)
train_data = data['train']
val_data = data['validation']
In [4]:
len(train_data), len(val_data)
Out[4]:
(287113, 13368)
In [5]:
train_data[1]
Out[5]:
{'article': '(CNN) -- Usain Bolt rounded off the world championships Sunday by claiming his third gold in Moscow as he anchored Jamaica to victory in the men\'s 4x100m relay. The fastest man in the world charged clear of United States rival Justin Gatlin as the Jamaican quartet of Nesta Carter, Kemar Bailey-Cole, Nickel Ashmeade and Bolt won in 37.36 seconds. The U.S finished second in 37.56 seconds with Canada taking the bronze after Britain were disqualified for a faulty handover. The 26-year-old Bolt has now collected eight gold medals at world championships, equaling the record held by American trio Carl Lewis, Michael Johnson and Allyson Felix, not to mention the small matter of six Olympic titles. The relay triumph followed individual successes in the 100 and 200 meters in the Russian capital. "I\'m proud of myself and I\'ll continue to work to dominate for as long as possible," Bolt said, having previously expressed his intention to carry on until the 2016 Rio Olympics. Victory was never seriously in doubt once he got the baton safely in hand from Ashmeade, while Gatlin and the United States third leg runner Rakieem Salaam had problems. Gatlin strayed out of his lane as he struggled to get full control of their baton and was never able to get on terms with Bolt. Earlier, Jamaica\'s women underlined their dominance in the sprint events by winning the 4x100m relay gold, anchored by Shelly-Ann Fraser-Pryce, who like Bolt was completing a triple. Their quartet recorded a championship record of 41.29 seconds, well clear of France, who crossed the line in second place in 42.73 seconds. Defending champions, the United States, were initially back in the bronze medal position after losing time on the second handover between Alexandria Anderson and English Gardner, but promoted to silver when France were subsequently disqualified for an illegal handover. The British quartet, who were initially fourth, were promoted to the bronze which eluded their men\'s team. Fraser-Pryce, like Bolt aged 26, became the first woman to achieve three golds in the 100-200 and the relay. In other final action on the last day of the championships, France\'s Teddy Tamgho became the third man to leap over 18m in the triple jump, exceeding the mark by four centimeters to take gold. Germany\'s Christina Obergfoll finally took gold at global level in the women\'s javelin after five previous silvers, while Kenya\'s Asbel Kiprop easily won a tactical men\'s 1500m final. Kiprop\'s compatriot Eunice Jepkoech Sum was a surprise winner of the women\'s 800m. Bolt\'s final dash for golden glory brought the eight-day championship to a rousing finale, but while the hosts topped the medal table from the United States there was criticism of the poor attendances in the Luzhniki Stadium. There was further concern when their pole vault gold medalist Yelena Isinbayeva made controversial remarks in support of Russia\'s new laws, which make "the propagandizing of non-traditional sexual relations among minors" a criminal offense. She later attempted to clarify her comments, but there were renewed calls by gay rights groups for a boycott of the 2014 Winter Games in Sochi, the next major sports event in Russia.',
 'highlights': "Usain Bolt wins third gold of world championship . Anchors Jamaica to 4x100m relay victory . Eighth gold at the championships for Bolt . Jamaica double up in women's 4x100m relay .",
 'id': '0002095e55fcbd3a2f366d9bf92a95433dc305ef'}
In [6]:
# torch.randint??
In [7]:
indices = torch.randint(len(train_data), (4,))
indices
for i in indices:
    print(i)
#     train_data[i.item()])
tensor(256824)
tensor(4194)
tensor(191757)
tensor(221651)
In [8]:
len(set(train_data[1]['article']))
Out[8]:
66
In [9]:
unique_characters = set()
for i in range(200):
#     print(set(train_data[i]['article']), len(set(train_data[i]['article'])))
    unique_characters.update(set(train_data[i]['article']))
print(unique_characters, len(unique_characters))
{'+', 'c', '|', 'L', 'R', 'w', 'U', '"', 'd', '§', "'", 'T', '8', '½', '_', 'á', '9', 'ñ', 'ø', '»', '1', '«', 'Â', 'z', 'H', '₩', '6', 'G', 'Q', 's', 'N', '7', 'ê', '&', 'Z', '%', 'B', ';', '\u200b', '-', ']', 'M', '!', 'Y', 'X', '2', 'F', '.', 'E', 'p', '/', 'v', 'K', '€', '@', 'g', '4', 'x', 'l', '(', '?', 'k', 'e', 'Ã', '\xa0', '3', 'é', 'i', 'b', 'h', '#', 'o', '[', 'ë', 'ć', 'C', '©', 'S', 'D', 'j', 'n', '$', 'f', 'm', 'r', 'P', ')', 'I', '—', 'V', ':', 'W', 'ž', 'J', 'y', 'ö', 'q', '0', '*', 'u', '¢', 't', '°', 'A', 'a', '5', 'O', 'ç', ',', ' ', 'â'} 111
In [10]:
def count_key_occurrences(data, key):
    count = sum(key in entry for entry in data if isinstance(entry, dict))
    return count

# Example usage
key_count = count_key_occurrences(train_data, 'article')
print(f"Number of dictionaries with 'article' key: {key_count}")
Number of dictionaries with 'article' key: 287113
In [11]:
type(train_data)
Out[11]:
datasets.arrow_dataset.Dataset
In [12]:
num_articles = len(train_data['article'])
num_highlights = len(train_data['highlights'])
num_ids = len(train_data['id'])
print(f"Number of articles: {num_articles}")
print(f"Number of highlights: {num_highlights}")
print(f"Number of ids: {num_ids}")
Number of articles: 287113
Number of highlights: 287113
Number of ids: 287113
In [13]:
# Check type and contents
print(f"Type for 'article': {type(train_data['article'])}")
print(f"Type for 'highlight': {type(train_data['highlights'])}")
print(f"Type for 'id': {type(train_data['id'])}")
Type for 'article': <class 'list'>
Type for 'highlight': <class 'list'>
Type for 'id': <class 'list'>
In [14]:
class LayerNorm(nn.Module):
    """ LayerNorm but with an optional bias. PyTorch doesn't support simply bias=False """

    def __init__(self, ndim, bias):
        super().__init__()
        self.weight = nn.Parameter(torch.ones(ndim))
        self.bias = nn.Parameter(torch.zeros(ndim)) if bias else None

    def forward(self, input):
        return F.layer_norm(input, self.weight.shape, self.weight, self.bias, 1e-5)
    
    
class MultiHeadSelfAttention(nn.Module):
    def __init__(self, n_embd, n_head):
        super().__init__()
        assert n_embd % n_head == 0
        self.n_head = n_head                                                     # nh        
        self.n_embd = n_embd
        self.head_size = n_embd // n_head                                        # hs
        self.scale = self.head_size ** -0.5

        # key, query, value projections for all heads, but in a batch & output projection
        self.qkv_proj = nn.Linear(n_embd, 3 * n_embd, bias=bias)
        self.out_proj = nn.Linear(n_embd, n_embd, bias=bias)
        
        # regularization: dropout
        self.attn_dropout = nn.Dropout(dropout)
        self.resid_dropout = nn.Dropout(dropout)
#         self.dropout = nn.Dropout(dropout)

        # mask for self-attention
        self.register_buffer("mask", torch.tril(torch.ones(block_size, block_size)).view(1, 1, block_size, block_size))
        #self.register_buffer('tril', torch.tril(torch.ones(block_size, block_size)))

    def forward(self, x):
        B, T, C = x.size()  # batch size, sequence length, embedding dimensionality (n_embd)
        
        # calculate query, key, values for all heads in batch and move head forward to be the batch dim
        qkv = self.qkv_proj(x)
        qkv = qkv.view(B, T, self.n_head, 3 * self.head_size)
        
        # splits the qkv tensor into three tensors along the last dimension to get Q, K, V tensors
        q, k, v = qkv.chunk(3, dim=-1)                                           # (B, T, nh, hs)
        q = q.transpose(1, 2)                                                    # (B, nh, T, hs)
        k = k.transpose(1, 2)                                                    # (B, nh, T, hs)
        v = v.transpose(1, 2)                                                    # (B, nh, T, hs)
        
        # compute attention weights ('affinities')
        # self.scale = 1.0 / math.sqrt(k.size(-1))
        att_w = (q @ k.transpose(-2, -1)) * self.scale                           # (B, nh, T, hs) @ (B, nh, hs, T) -> (B, nh, T, T)
        att_w = att_w.masked_fill(self.mask[:, :, :T, :T] == 0, float('-inf'))   # (B, nh, T, T)
        att_w = F.softmax(att_w, dim=-1)                                         # (B, nh, T, T)
        att_w = self.attn_dropout(att_w)                                              # (B, nh, T, T)

        attn_output = att_w @ v                                                   # (B, nh, T, T) @ (B, nh, T, hs) -> (B, nh, T, hs)
        
        # re-assemble all head outputs side by side & final output projection
        attn_output = attn_output.transpose(1, 2).contiguous().view(B, T, C)      # (B, T, n_embd)
        out_proj = self.resid_dropout(self.out_proj(attn_output))                 # (B, T, n_embd)
        return out_proj

    
class FeedFoward(nn.Module):
    """ a feedforward MLP"""

    def __init__(self, n_embd, activation_fn='relu'):
        super().__init__()
        self.in_proj = nn.Linear(n_embd, 4 * n_embd, bias=bias)   # fully connected
        self.activation_fn = activation_fn
        self.gelu = nn.GELU() 
        self.relu = nn.ReLU()
        self.out_proj = nn.Linear(4 * n_embd, n_embd, bias=bias)  # projection
        self.dropout = nn.Dropout(dropout)

    def forward(self, x):
        x = self.in_proj(x)
        if self.activation_fn == 'relu':
            x = self.relu(x) 
        elif self.activation_fn == 'gelu':
            x = self.gelu(x)
        x = self.out_proj(x)
        x = self.dropout(x)
        return x

class Block(nn.Module):
    def __init__(self, n_embd, n_head, activation_fn='relu'):
        super().__init__()
        self.ln1 = LayerNorm(n_embd, bias=bias)              # nn.LayerNorm(n_embd)
        self.attn = MultiHeadSelfAttention(n_embd, n_head)
        self.ln2 = LayerNorm(n_embd, bias=bias)              # nn.LayerNorm(n_embd)
        self.ffwd = FeedFoward(n_embd, activation_fn=activation_fn)

    def forward(self, x):
        x = x + self.attn(self.ln1(x))
        x = x + self.ffwd(self.ln2(x))
        return x
In [15]:
class GPT(nn.Module):

    def __init__(self, activation_fn='relu'):
        super().__init__()
        assert vocab_size is not None
        assert block_size is not None
            
        # transformer
        self.token_embedding_table = nn.Embedding(vocab_size, n_embd)
        self.position_embedding_table = nn.Embedding(block_size, n_embd)
        self.dropout = nn.Dropout(dropout)
        self.blocks = nn.Sequential(*[Block(n_embd, n_head, activation_fn=activation_fn) for _ in range(n_layer)])
        self.ln_f = LayerNorm(n_embd, bias=bias)   # nn.LayerNorm(n_embd) # final layer norm
        
        self.lm_head = nn.Linear(n_embd, vocab_size, bias=False)
        self.num_parameters = self.get_num_params() 
        print("number of parameters: %.2fM" % (self.num_parameters/1e6,))

        self.apply(self._init_weights)   # init all the weights
        # apply special scaled init to the residual projections, per GPT-2 paper
        for pn, p in self.named_parameters():
            if pn.endswith('out_proj.weight'):
                torch.nn.init.normal_(p, mean=0.0, std=0.02/math.sqrt(2 * n_layer))

    def _init_weights(self, module):
        if isinstance(module, nn.Linear) or isinstance(module, nn.Embedding):
            nn.init.normal_(module.weight, mean=0.0, std=0.02)
            if isinstance(module, nn.Linear) and module.bias is not None:
                nn.init.zeros_(module.bias)
        elif isinstance(module, nn.LayerNorm):
            torch.nn.init.zeros_(module.bias)
            torch.nn.init.ones_(module.weight)        
                
    def get_num_params(self):
        return sum(p.numel() for p in self.parameters() if p.requires_grad)

    def forward(self, idx, targets=None):
        device = idx.device
        B, T = idx.shape
        assert T <= block_size, f"Cannot forward sequence of length {T}, block size is only {block_size}"
        pos = torch.arange(T, dtype=torch.long, device=device).unsqueeze(0)       # (1,T)
        
        # idx and targets are both (B,T) tensor of integers [C = n_embd]
        tok_emb = self.token_embedding_table(idx)                                 # (B,T,C)
        pos_emb = self.position_embedding_table(pos)                              # (T,C)
        x = tok_emb + pos_emb                                                     # (B,T,C)
        x = self.dropout(x)
        x = self.blocks(x)                                                        # (B,T,C)
        x = self.ln_f(x)                                                          # (B,T,C)
        logits = self.lm_head(x)                                                  # (B,T,vocab_size)

        if targets is None:
            loss = None
        else:
            B, T, C = logits.shape
            logits_flat = logits.view(B*T, C)                                     # (B*T, vocab_size)
            targets_flat = targets.view(B*T)                                      # (B*T,)
            loss = F.cross_entropy(logits_flat, targets_flat)
#         else:
#             loss = F.cross_entropy(logits.view(-1, logits.size(-1)), targets.view(-1), ignore_index=-1)

        return logits, loss

    
    @torch.no_grad()
    def generate(self, idx, max_new_tokens, temperature=1.0, do_sample=True, top_k=None):
        """
        Take a conditioning sequence of indices idx (LongTensor of shape (b,t)) and complete
        the sequence max_new_tokens times, feeding the predictions back into the model each time.
        Most likely you'll want to make sure to be in model.eval() mode of operation for this.
        """
        for _ in range(max_new_tokens):
            # if the sequence context (idx) is growing too long we must crop it at block_size
            idx_len = idx.size(1)                                  # get length of idx, T, from idx shape (B, T)
            idx_cut = idx[:, -block_size:]                         # crop idx to the last block_size tokens
            idx_cond = idx if idx_len <= block_size else idx_cut   # crop idx to the last block_size tokens if it exceeds block_size
            
            logits, loss = self(idx_cond)                          # get the predictions
            logits = logits[:, -1, :] / temperature                # (B, C) pluck the logits of the last time step and scale by temperature. logits[all batches, last timestamp, all channels]
            
            # if top_k is specified, the logits are filtered to keep only the top k values.
            if top_k is not None:
                top_k = min(top_k, logits.size(-1))                # ensures top_k does not exceed the number of logits.
                top_v, _ = torch.topk(logits, top_k)               # selects the top k logits
                logits[logits < top_v[:, [-1]]] = -float('Inf')    # sets all logits that are not in the top k to -inf, effectively removing them from consideration when sampling.
            
            probs = F.softmax(logits, dim=-1)                      # (B, C) - apply softmax to turn logits to probabilities
            
            # either sample from the distribution or take the most likely element
            if do_sample:
                idx_next = torch.multinomial(probs, num_samples=1) # (B, 1) - sample next token from the prob. distribution
            else:
                _, idx_next = torch.topk(probs, k=1, dim=-1)       # (B, 1) - take the most likely token if not sampling
            idx = torch.cat((idx, idx_next), dim=1)                # (B, T+1) - append sampled index to the running sequence
        return idx
In [16]:
def get_batch(split):
    data = train_data if split == 'train' else val_data
    ix1 = torch.randint(len(data), (batch_size,))
    
    articles = [data[i.item()]['article'] for i in ix1]
    encoded_articles = [encode(a) for a in articles]
    tokenized_articles = torch.tensor([encoding for article in encoded_articles for encoding in article], dtype=torch.long, device=device)
    ix2 = torch.randint(len(tokenized_articles) - block_size, (batch_size,))
    x = torch.stack([tokenized_articles[i:i+block_size] for i in ix2])
    y = torch.stack([tokenized_articles[i+1:i+block_size+1] for i in ix2])
    x, y = x.to(device), y.to(device)
    return x, y

@torch.no_grad()
def estimate_loss(model):
    out = {}
    model.eval()
    for split in ['train', 'val']:
        losses = torch.zeros(eval_iters)
        for k in range(eval_iters):
            X, Y = get_batch(split)
            logits, loss = model(X, Y)
            losses[k] = loss.item()
        out[split] = losses.mean()
    model.train()
    return out

def train_model(model, learning_rate, max_iters, eval_interval):
    train_losses = []
    val_losses = []
    epochs = []

    # create a PyTorch optimizer
    optimizer = torch.optim.AdamW(model.parameters(), lr=learning_rate)

    for iter in range(max_iters):
        optimizer.zero_grad(set_to_none=True)

        # every once in a while evaluate the loss on train and val sets
        if iter % eval_interval == 0 or iter == max_iters - 1:
            losses = estimate_loss(model)
            train_losses.append(losses['train'])#.item())
            val_losses.append(losses['val'])#.item())
            epochs.append(iter)
            print(f"step {iter}: train loss {losses['train']:.4f}, val loss {losses['val']:.4f}")

        # sample a batch of data
        xb, yb = get_batch('train')

        # evaluate the loss
        logits, loss = model(xb, yb)
        loss.backward()
        optimizer.step()
    return epochs, train_losses, val_losses

def plot_loss(epochs, train_losses, val_losses, rot=45):
    # Create a figure and axes
    fig, ax = plt.subplots(figsize=(10, 6))

    # Plot the training and validation losses
    ax.plot(epochs, train_losses, marker='o', label='Training Loss')
    ax.plot(epochs, val_losses, marker='o', label='Validation Loss')

    ax.set_xlabel('Iterations', fontsize=14)                       # Set the x-axis label
    ax.set_ylabel('Loss', fontsize=14)                             # Set the y-axis label
    ax.set_xticks(epochs)                                          # Set the x-axis ticks
    ax.set_xticklabels(epochs, rotation=rot, fontsize=12)         # Set the x-axis ticks' labels
    ax.tick_params(axis='y', labelsize=12)                         # Set the y-axis ticks' labels
    ax.yaxis.set_major_formatter(plt.FormatStrFormatter('%.2f'))   # Set the y-axis format
    ax.set_title('Training and Validation Losses', fontsize=16)    # Set the title
    ax.legend(fontsize=12)                                         # Add a legend
    ax.grid(linestyle='--', alpha=0.5)                             # Add grid lines
    plt.tight_layout()                                             # Adjust the spacing between subplots
    plt.show()                                                     # Display the plot

def generate_text(model, context, max_new_tokens=1000, temp=1.0, do_sample=True, top_k=None):
    # generate from the model
    model.eval()
    m = model.to(device)
#     context = torch.zeros((1, 1), dtype=torch.long, device=device)
    generated = m.generate(context, max_new_tokens=max_new_tokens,temperature=temp, do_sample=do_sample, top_k=top_k)
    print(decode(m.generate(context, max_new_tokens=max_new_tokens,temperature=temp)[0].tolist()))
    #open('more.txt', 'w').write(decode(m.generate(context, max_new_tokens=max_new_tokens)[0].tolist()))
    model.train()  
In [17]:
chars = sorted(list(set(string.printable)))
vocab_size = len(chars) + 1
# create a mapping from characters to integers
stoi = { ch:i for i,ch in enumerate(chars) }
itos = { i:ch for i,ch in enumerate(chars) }
stoi['ukn'] = len(stoi)
itos[len(itos)] = 'ukn'
encode = lambda s: [stoi.get(c, stoi['ukn']) for c in s] # encoder: take a string, output a list of integers
decode = lambda l: ''.join([itos[i] for i in l]) # decoder: take a list of integers, output a string
In [18]:
print(chars)
print(vocab_size)
['\t', '\n', '\x0b', '\x0c', '\r', ' ', '!', '"', '#', '$', '%', '&', "'", '(', ')', '*', '+', ',', '-', '.', '/', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?', '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '[', '\\', ']', '^', '_', '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '{', '|', '}', '~']
101
In [19]:
# hyperparameters
batch_size = 64 #16       #32 how many independent sequences will we process in parallel?
block_size = 256 #32       #8 what is the maximum context length for predictions?
max_iters = 5000      #3000
eval_interval = 500   #300
learning_rate = 6e-4 #1e-3  #1e-2
device = 'cuda' if torch.cuda.is_available() else 'cpu'
eval_iters = 200
n_embd = 384 #64           #32  (n_head * batch_size)
n_head = 6 #4                   (n_embd/batch_size)
n_layer = 6 #4
dropout = 0.2 #0.0
bias = False  # True: bias in Linears and LayerNorms, like GPT-2. False: a bit better and faster
# ------------

#--------------------------------------------------------------------------------------
start_time = time.time()
#--------------------------------------------------------------------------------------

torch.manual_seed(1337)
model = GPT(activation_fn='gelu').to(device)
epochs, train_losses, val_losses = train_model(model, learning_rate, max_iters, eval_interval)
plot_loss(epochs, train_losses, val_losses)
context = torch.zeros((1, 1), dtype=torch.long, device=device)
generate_text(model, context, max_new_tokens=1000, temp=0.8, do_sample=True, top_k=None)

#--------------------------------------------------------------------------------------
end_time = time.time()
# Print elapsed time
elapsed_time = end_time - start_time
print(f"Elapsed time: {elapsed_time:.4f} seconds")
#--------------------------------------------------------------------------------------
number of parameters: 10.80M
step 0: train loss 4.6709, val loss 4.6716
step 500: train loss 2.3473, val loss 2.3471
step 1000: train loss 1.7039, val loss 1.7130
step 1500: train loss 1.5072, val loss 1.5150
step 2000: train loss 1.4277, val loss 1.4371
step 2500: train loss 1.3773, val loss 1.3880
step 3000: train loss 1.3475, val loss 1.3603
step 3500: train loss 1.3321, val loss 1.3372
step 4000: train loss 1.3098, val loss 1.3174
step 4500: train loss 1.2987, val loss 1.3051
step 4999: train loss 1.2878, val loss 1.2939
No description has been provided for this image
	 the second state of the police and stopped him at him with a standing the page of a crime gun of an estate award and the former skin of the collapse by a business courld. Miss McDonald said the six waters had assaulted those arrested in the past of passing holds, the first companies, said at when it had not a more than a cleaner than the slage of the bank inside. She said: 'It would forgot the laugh. 'We didn't have been discovered by anybody and a beach and then we don't be in the day of people who have a father of the complaint.' The dark of the failure is able to until a part of police in the same winner were created by the star wife, Prosecution said Mitchell was fully designed by the lawsuit having bought a billion-battle family, which are responsible for the 1980s in the first player to qet a major car up, the president of sex. And then we see the subject a problem. 'It was it was the foul beauty. The trainer should be a hot in everybody to access over the interpretation and is 
Elapsed time: 1872.7520 seconds
In [20]:
1819/60
Out[20]:
30.316666666666666
In [25]:
starting_text = 'the democratic party wants to'
# start = torch.tensor(encode(starting_text), dtype=torch.long, device=device).reshape(-1, 1)
# generate = model.generate(start, max_new_tokens=10000,temperature=0.8)
# print(decode(generate[0].tolist()))
In [26]:
start = torch.tensor(encode(starting_text), dtype=torch.long, device=device).reshape(-1, 1)
generate_text(model, start, max_new_tokens=1000, temp=0.8, do_sample=True, top_k=None)
tic because they would be released. Sorry was a little more consideration for the country's police of opening installed the worst to the newssary of the Star must have taken to the best family and we were the site of me after the day. 'No shoulder work with the first time in October 2011,' he said. He received his outbreaking behavior forces and really every senior body who cannot be really black with the group of WARK Hospital because it is taken to sue the base on the condolet in a first match. It did believe the party of the world that we know this is that do adopt the climb and that their pattern can talk for the scene and the defendance between London and he straighted the worst. They were not one of the borld they were so long, they were at back on illness block. One of the whole grades in Bournow and the man had also driven Travel at British . one of the incident locals with outraged to the TV affair in 1987, said the 15-year-old has died. It . is so much and possible to say "can
In [27]:
!wget https://raw.githubusercontent.com/karpathy/char-rnn/master/data/tinyshakespeare/input.txt
with open('input.txt', 'r', encoding='utf-8') as f:
    text = f.read()
--2024-08-06 21:35:18--  https://raw.githubusercontent.com/karpathy/char-rnn/master/data/tinyshakespeare/input.txt
Resolving raw.githubusercontent.com (raw.githubusercontent.com)... 185.199.108.133, 185.199.109.133, 185.199.110.133, ...
Connecting to raw.githubusercontent.com (raw.githubusercontent.com)|185.199.108.133|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 1115394 (1.1M) [text/plain]
Saving to: 'input.txt'

input.txt           100%[===================>]   1.06M  --.-KB/s    in 0.06s   

2024-08-06 21:35:18 (18.4 MB/s) - 'input.txt' saved [1115394/1115394]

In [28]:
print("length of dataset in characters: ", len(text), '\n')

# here are all the unique characters that occur in this text
chars = sorted(list(set(text)))
vocab_size = len(chars)
print("Unique characters in dataset",''.join(chars))
print("\nTotal number of unique characters in dataset:", vocab_size, '\n')

# let's look at the first 1000 characters
print('----------------------------------------------------------------')
print(text[:100])
length of dataset in characters:  1115394 

Unique characters in dataset 
 !$&',-.3:;?ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz

Total number of unique characters in dataset: 65 

----------------------------------------------------------------
First Citizen:
Before we proceed any further, hear me speak.

All:
Speak, speak.

First Citizen:
You
In [36]:
data = torch.tensor(encode(text), dtype=torch.long)
n = int(0.9*len(data)) # first 90% will be train, rest val
train_data = data[:n]
val_data = data[n:]

# data loading
def get_batch(split):
    # generate a small batch of data of inputs x and targets y
    data = train_data if split == 'train' else val_data
    ix = torch.randint(len(data) - block_size, (batch_size,))
    x = torch.stack([data[i:i+block_size] for i in ix])
    y = torch.stack([data[i+1:i+block_size+1] for i in ix])
    x, y = x.to(device), y.to(device)
    return x, y
In [37]:
pretrained_model = deepcopy(model)
for block in pretrained_model.blocks[3:]:
    for p in block.parameters():
        p.requires_grad = False
In [38]:
max_iters = 1000
eval_interval = 100
learning_rate = 3e-5 
#--------------------------------------------------------------------------------------
start_time = time.time()
#--------------------------------------------------------------------------------------

torch.manual_seed(1337)
model = pretrained_model.to(device)
epochs, train_losses, val_losses = train_model(model, learning_rate, max_iters, eval_interval)
plot_loss(epochs, train_losses, val_losses)
context = torch.zeros((1, 1), dtype=torch.long, device=device)
generate_text(model, context, max_new_tokens=5000, temp=0.8, do_sample=True, top_k=None)

#--------------------------------------------------------------------------------------
end_time = time.time()
# Print elapsed time
elapsed_time = end_time - start_time
print(f"Elapsed time: {elapsed_time:.4f} seconds")
#--------------------------------------------------------------------------------------
step 0: train loss 1.3397, val loss 1.5109
step 100: train loss 1.3386, val loss 1.5042
step 200: train loss 1.3326, val loss 1.5054
step 300: train loss 1.3290, val loss 1.4998
step 400: train loss 1.3260, val loss 1.4970
step 500: train loss 1.3231, val loss 1.4942
step 600: train loss 1.3211, val loss 1.4935
step 700: train loss 1.3139, val loss 1.4883
step 800: train loss 1.3135, val loss 1.4895
step 900: train loss 1.3138, val loss 1.4892
step 999: train loss 1.3096, val loss 1.4855
No description has been provided for this image
	Ay, and time and go the performance?

BUCKINGHAM:
For your highness.

ESCALUS:
What brothers, this the disease this deed is in shame
To be in the number of my lords.

CAPULET:
I think you I have an outfitted I am true?

SICINIUS:
He would lament live in amendment of him,
And and my male heavy father.

KING EDWARD IV:
Therefore dreams thou be before the will leave I quit,
Where I fell their great things is where's well,
And of harm'd it with almost poor sensure's lives,
Though I cannot find our cheeks,
It is not as the tongue is dry every lord.

HESTBROKEN:
Farewell, is then I may have killed and be rail
And thy straining common journey, and soul:
But a party of suits of an increase rome,
I'll not consider with two stands and friends?
You know thy grace but be'en in honest grave
And more than the break is a commission.

SICINIUS:
I thought how she not than the mount of lord.
Thou marry the shepherd that were a glittery,
And said the vitamine of a kind of good appeals
To business my lives from some health of her love.

BUCKINGHAM:
Bring thee, and your lords shalt the house,
And love the world bags, the enclose of the demonst
One dear wound so a hand well that I then well.

Second Servingman:
No, when this child be a flower known: strike your tents
That this morrow that hath your life is sense.

ANGELO:
By a sign of Bolingbroke,
That help while you have an encounter man.

ROMEO:
There can your art shall attend that I sland to the gods
Renown can stand, there is a cheer leutened by the state.

LUCIO:
Romeo!

KING RICHARD III:
But, the legislation of the Grace of Gloucester
To be looking in these such a very tent for Henry,
For a man will look on violence, and a man.

GLOUCESTER:
For my lord.

SICINIUS:
You are the lady and
the house of this can be freely previous comfort,
What you are not my friend? where it is quit way!
Thus is your still and eyes, although we are no fear,
Because I am still well as a daughter.

WORS:
He knew you with the corner for the lordship,
That in the blood of valiants.

BRUTUS:
I will the pieces a world, I am sure alone.

SUCh best more than your company there is they have lost.

CAMILLO:
May good art thee you for me, my soul bloody
Than your best blasted in prince in the assair:
Then that had been to do the time.

LUCIO:
If no mine see my gracious garface,
The comes ropes from a layer thy devised and one womb of our heart.

PAULINA:
And let it is like to stand to perfect the since,
If I know that not wait out thee.

BENVOLIO:
What is he doth in my success.

DUKE VINCENTIO:
But, not a private such cheeks, and there is no more as the time.

HENRY VI:
Therefore me had the company all my lord,
As we do this defence of thy man, the compound is of his perbless
To make you be honour with it, we promised, and
yet not go but he's completely fellow'd of good,
Which he is him with not his soul. The organisation shall sadies
But in his back, dain't by the most substantial accused
With my scenario:
Therefore seems to be made by the ear of his life;
If it was between for the royal sceizes,
He said that had been a beauty carries and have
his sons in this soul of the ground: then it was not in:
Then thou then got to the present thy father honour.

PRINCE EDWARD IV:
He has delivered: thou art die thou better to break.

SOMENENIUS:
Why.

First:
I burn thee like a dear and army whose words well.

BENVOLIO:
Adam, and I help me not him and that be stopp his vain on,
I do watching sovereign women that she was shall still received
Here, the subhericans are a free since in all these
things.
If we revenged to the time of this shepherd?

YORK:
If we slaughter bloods down your mother's name.

KING HENRY VI:
Ay, but be not swarred in your native action,
That I had  good neck the morning haste dishapper
In thy gracious lives, if I did have been drop'd.
I don't hear the party,
I would not in the poor poor of a most season.

YORK:
Therefore wouldst the queen, my lord leave your country.

Provost:
Have you shall spoke the abuse of the decease,
With my wife, if you be once account.

First Murderer:
What! shall find you welcome the traitor?

PAULINA:
What is the tears, is your well.

HENRY VI

KING EDWARD IV:
Are still in intention sworn list, good more south for
Brows my garden which worth relate to care the debt.

LADY ANNE:
I do not really with our harms man
To remember him, but by his strucre down.

ANNE:
I must not be said, my brother and you was thee
Better as no mine hours against the face, in the same flee hours were
In beating; and a week with some slow, fair from a bed more.

LEONTES:
The gay
Send up the same throne. This is made with the bed
Your hostages of such soul stay-for it alongsces
more than his life, protest fools, lives, they are all as holy fare
With a death to their oils, or faults and with it.

KING RICHARD II:
These country, with my protest country, and I must have
To see there and sigh of men and his full of Romeo,
To blood my lords.

First Citizen:
Why, what, I have so out of thee nurse:
Pea
Elapsed time: 558.3099 seconds

Comment: This looks like proper Shakespearean dialogue. Yay !!! The validation loss of $\textbf{1.4855}$ is lower, with fewer training steps and lower learning rate, for the finetuned model compared to the original baseline model.

In [ ]: