Build your own GPT

Dataset Preparation for Training Language Model

Preparing a dataset for language model training involves three steps: preprocessing, which cleans and standardizes text; tokenization, which transforms text into numerical indices; and batching, which groups sequences to enhance training efficiency.

import torchimport re
# Step 1: Preprocessing - Clean and standardize texttext = "Hello, World! Welcome to Language Modeling."text = re.sub(r'[^a-zA-Z\s,.:;?_&$!"()\-\*\[\]]', '', text.lower()).strip()
# Step 2: Tokenization - Convert text into numerical indiceschars = sorted(list(set(text)))stoi = {ch: i for i, ch in enumerate(chars)}encoded_text = [stoi[c] for c in text]
# Step 3: Batching - Group sequences for efficient trainingbatch_size, block_size = 4, 8data = torch.tensor(encoded_text, dtype=torch.long)batched_data = [data[i:i+block_size] for i in range(0, len(data), block_size)]

Encoding and Decoding

Encoding functions convert text into lists of indices for efficient tokenization, while decoding functions revert indices back to text. In a bigram model, this is done using stoi (string to index) and itos (index to string) dictionaries.

# Create mappings for encoding and decodingchars = sorted(list(set("sample text for encoding")))stoi = {ch: i for i, ch in enumerate(chars)}itos = {i: ch for i, ch in enumerate(chars)}
# Encoding function: Convert text to indicesencode = lambda s: [stoi[c] for c in s]
# Decoding function: Convert indices back to textdecode = lambda l: ''.join([itos[i] for i in l])
# Example usageencoded = encode("sample")decoded = decode(encoded)
print("Encoded:", encoded)print("Decoded:", decoded)

Attention Mechanism

The attention mechanism helps transformers identify important words in a sentence. It uses matrix multiplication and the softmax function to assign weights to words, combining their embeddings based on importance and context. This allows the model to understand relationships between words, even those far apart in the text.

import torchimport torch.nn.functional as F
B, T, C = 2, 5, 4  # Batch size, Sequence length, Embedding dimensionx = torch.randn(B, T, C)  # Random input embeddings
# Define query, key, and value transformationsquery = x  # Using the input embeddings as querieskey = x    # Using the input embeddings as keysvalue = x  # Using the input embeddings as values
# Step 1: Calculate attention scores using matrix multiplicationattention_scores = torch.matmul(query, key.transpose(-2, -1))  # Shape: (B, T, T)
# Step 2: Apply softmax to convert scores into attention weightsattention_weights = F.softmax(attention_scores, dim=-1)  # Shape: (B, T, T)
# Step 3: Use the attention weights to combine the value embeddingsoutput = torch.matmul(attention_weights, value)  # Shape: (B, T, C)
# Output the resultsprint("Attention Weights:\n", attention_weights)print("Output Embeddings:\n", output)

Self-Attention Mechanism in Transformers

Self-attention in transformers uses key, query, and value projections along with positional embeddings. This combination creates context-aware representations that capture both the relationships between tokens and the overall structure of the sequence.

import torchimport torch.nn.functional as F
B, T, C = 2, 5, 4  # Batch size, Sequence length, Embedding dimensionx = torch.randn(B, T, C)  # Random input embeddings
# Positional embeddings: Simulating positional information added to xpositional_embeddings = torch.randn(B, T, C)x = x + positional_embeddings  # Adding positional information to embeddings
# Using x as the query, key, and value for simplicityquery = xkey = xvalue = x
# Step 1: Calculate attention scores manuallyscores = (query * key).sum(dim=-1) / torch.sqrt(torch.tensor(C, dtype=torch.float32))
# Step 2: Apply softmax to get attention weightsattention_weights = F.softmax(scores, dim=-1)
# Step 3: Compute the output by combining the value embeddings with attention weightsoutput = (attention_weights.unsqueeze(-1) * value).sum(dim=1)
# Output the resultsprint("Attention Weights:\n", attention_weights)print("Self-Attention Output:\n", output)

Positional Encoding

Positional encodings provide information about the order of tokens in the input sequence, which is crucial because transformers do not process data sequentially. By adding positional encodings to token embeddings, the model can capture the structure and relationships within a sentence, aiding in tasks like predicting the next token in a bigram model.

import numpy as np
def generate_positional_embeddings(T, C):    # Initialize positional embeddings with zeros    positional_embeddings = np.zeros((T, C))    # Calculate the embeddings using sine and cosine functions    for t in range(T):        for c in range(C):            if c % 2 == 0:                positional_embeddings[t, c] = np.sin(t / (10000 ** (c / C)))            else:                positional_embeddings[t, c] = np.cos(t / (10000 ** (c / C)))    return positional_embeddings
# Call the function with T=10 and C=4positional_embeddings = generate_positional_embeddings(10, 4)print("Positional Embeddings:\n", positional_embeddings)

Feedforward Layers

Feedforward layers in transformers are similar to those in standard neural networks. They apply linear transformations followed by non-linear activation functions, like ReLU, to token representations. This step enhances the model’s ability to learn complex patterns before passing data to the attention layers.

import torchimport torch.nn.functional as F
B, C = 3, 8  # Batch size, Embedding dimensionx = torch.randn(B, C)  # Random input token representations
# Define a feedforward layer with a linear transformation and ReLU activationlinear_layer = torch.nn.Linear(C, C)output = F.relu(linear_layer(x))
# Output the resultsprint("Input Token Representations:\n", x)print("\nOutput After Feedforward Layer:\n", output)

Layer Normalization

Layer normalization stabilizes the training of transformer models by normalizing token embeddings. This process helps improve convergence and overall model performance by ensuring consistent distribution of input values to each layer.

import torchimport torch.nn as nn
B, T, C = 3, 5, 4  # Batch size, Sequence length, Embedding dimensionx = torch.randn(B, T, C)  # Random input token embeddings
# Apply layer normalizationlayer_norm = nn.LayerNorm(C)normalized_x = layer_norm(x)
# Output the resultsprint("Original Token Embeddings:\n", x)print("\nNormalized Token Embeddings:\n", normalized_x)

Single-Head vs. Multi-Head Attention

Single-head attention computes one set of attention scores, capturing only one type of relationship at a time. Multi-head attention uses multiple attention heads to capture various relationships within the input sequence, allowing the model to understand complex dependencies more effectively.

What is Hugging face Transformers?

Hugging Face’s Transformers Library

  • The goal of the Hugging Face Transformers library is to provide a single Python API through which any transformer model can be loaded, trained, fine-tuned and saved.
  • The Hugging Face Transformers library provides thousands of pretrained models to perform tasks on different modalities such as text, vision, and audio. It’s backed by the three most popular deep learning libraries – JAX, PyTorch and TensorFlow.

The pipeline() function

  • The most basic object in the Transformers library is the pipeline() function. It connects a model with its necessary preprocessing and postprocessing steps, allowing us to directly input any text and get an intelligible answer.
  • Some of the text-related tasks available in the pipeline() function are feature extraction, named entity recognition, sentiment analysis, summarization and text generation.
from transformers import pipeline
classifier = pipeline("sentiment-analysis")classifier(    [        sample_text_sequence_1,        sample_text_sequence_2,    ])

Model Hub

  • A model is a general term that can mean either architecture or checkpoint. Architecture refers to the specific neural network configuration uses and checkpoints are the weights for a given architecture. For example, BERT is an architecture, while bert-base-uncased is a checkpoint.
  • The Hugging Face Hub is a platform with over 350k models, 75k datasets, and 150k demo apps, all open source and publicly available, in an online platform from which models can be downloaded from or uploaded to.

Model Cards

  • Model cards are markdown files that accompany the transformer models, provide handy information and are essential for discoverability, reproducibility, and sharing in AI research.
  • Model cards contain additional metadata about the model including and not limited to the model weights, training parameters, training datasets, model performance evaluation results and its intended uses and potential limitations.

Tokenizers

  • Tokenization is an essential step in preprocessing text in Natural Language Processing (NLP) tasks. Tokenizers break down a stream of textual data into words, subwords, or symbols (like punctuation) that can then be converted into numbers or vectors to be processed by algorithms. 
  • A tokenizer in a transformer model is responsible for splitting the input text into tokens, mapping each token to an integer and adding additional inputs that may be useful to the model.

The from_pretrained() method

The from_pretrained() method can be used to load and save a pretrained transformer model. The AutoTokenizerAutoProcessor and AutoModel classes allow one to load tokenizers, processors and models respectively for any model architecture.

from transformers import AutoModel, AutoTokenizer
checkpoint = 'pretrained-model-you-want'tokenizer = AutoTokenizer.from_pretrained(checkpoint)model = AutoModel.from_pretrained(checkpoint)

Token selection strategies

Decoder models employ various strategies in next token generation, which can be adjusted by the user. These include n-gram penalties, which prevent token sequences of n length from repeating; sampling, which chooses the next token at random from among a collection of likely next tokens; and temperature, which adjusts the predictability of the randomly selected next token, with higher temperatures producing less predictable output.

When decoder models simply select the most probable next token for their output, it’s called “greedy search.” When the model projects several tokens further into the potential output and chooses the most probable multi-token sequence from among several candidates, it’s known as “beam search.”

What is ML and its Types

What is ML?

Arthur Samuel said it as: “the field of study that gives computers the ability to learn without being explicitly programmed.”

Tom Mitchell says with more detailed definition: “A computer program is said to learn from experience E with respect to some class of tasks T and performance measure P, if its performance at tasks in T, as measured by P, improves with experience E.”

In general, any ML problem can be assigned to one of two broad classifications:

supervised learning, OR

unsupervised learning.

Supervised Learning

In supervised learning, we are given a data set and already know what our correct output should look like, Knowing relationship between the input and the output.

Supervised learning problems are categorized into “regression” and “classification” problems.

 In a regression problem, we are trying to predict results within a continuous output,

In a classification problem, we are instead trying to predict A or B

Unsupervised Learning

Unsupervised learning, on the other hand, allows us to approach problems with little or no idea what our results should look like.

With unsupervised learning there is no feedback based on the prediction results, i.e., there is no teacher to correct you.

Eg:

Clustering: Take a collection of 1000 essays written on the US Economy, and find a way to automatically group these essays into a small number that are somehow similar or related by different variables, such as word frequency, sentence length, page count, and so on.

Non-clustering: The “Cocktail Party Algorithm”, which can find structure in messy data (such as the identification of individual voices and music from a mesh of sounds at a cocktail party .

How much or How many – Regression

Is this A or B – Classification

Is this weird – Anomaly detection

How is it organized? – Clustering

What should I do next? – Reinforcement Learning

What is Cost Function?

Cost Function

It is a function that measures the performance of a Machine Learning model for given data.

It quantifies the error between predicted values and expected values and presents it in the form of a single real number. Depending on the problem Cost Function can be formed in many different ways. The purpose of Cost Function is to be either:

  • Minimized – then returned value is usually called costloss or error. The goal is to find the values of model parameters for which Cost Function return as small number as possible.

  • Maximized – then the value it yields is named a reward. The goal is to find values of model parameters for which returned number is as large as possible.

What is Logistic Regression

Linear and Logistic Regression are basically from Statistics but also used in ML. Linear Regression is used for understanding the relationship between input and output numerical variables.

Logistic Regression with Python. Logistic regression was once the most… |  by ODSC - Open Data Science | Medium

In ML, Its termed as  simple model which is a linear predictive model.

Linear Regression as Math equation:

Y = aX + c

Y = Output variable;  X = Input Variable ; c = Constant ; a= slope of line.

in ML, we say c= constant and a as weight and then we can predict the value of y for any new x.

So, we draw a random line on the graph for some random value of c and a. Lets say we keep c and a both 1 (c=1, a=1) and draw the line on the graph for each (well at least 2 ) x. Based upon values of x this line might end up in one of the Positions like – little up, down, left, right.

It can then be converted to Probabilistic model called sigmoid function to calculate the Probability.

The difference between Linear regression and Logistic Regression