Saturday, 18 January 2025

Hour 10 - Advanced Fine-Tuning Techniques

Lecture Notes: 


1. Concepts

What is Fine-Tuning?

Fine-tuning refers to the process of taking a pre-trained model and adjusting its weights based on a smaller, task-specific dataset. This allows the model to adapt and perform better on specialized tasks (e.g., summarizing PDFs, extracting data from websites) without requiring the massive computational resources needed for training a model from scratch.


Advanced Fine-Tuning Techniques

Fine-tuning is an iterative process that can be enhanced with advanced strategies to optimize the model's performance. These strategies are designed to improve the model's efficiency and its ability to generalize on new, unseen data.

1. Learning Rate Schedulers
  • A learning rate scheduler adjusts the learning rate during training to prevent overshooting the optimal solution and to accelerate convergence.
  • Types:
    • Constant Learning Rate: Keeps the learning rate constant.
    • Step Decay: Reduces the learning rate after a set number of epochs.
    • Exponential Decay: Gradually decreases the learning rate.
    • Cosine Annealing: Gradually reduces the learning rate in a cosine curve to explore a wide range of potential solutions before narrowing down.
2. Early Stopping
  • Stops training when the model’s performance on a validation set no longer improves. This helps prevent overfitting and saves time by avoiding unnecessary training steps.
3. Data Augmentation
  • Expands the size and variety of your training dataset by applying transformations to the input data (e.g., rotating images, paraphrasing text). This allows the model to generalize better to new data.
4. Gradient Accumulation
  • A technique to simulate a larger batch size when limited by GPU memory. The gradients are accumulated over multiple smaller mini-batches before performing a parameter update.
5. Model Regularization
  • Helps prevent the model from overfitting by adding a penalty to the loss function based on the complexity of the model.
  • Types:
    • L1/L2 Regularization: Adds a penalty to the weights of the model to prevent them from becoming too large.
    • Dropout: Randomly drops units (neurons) in the neural network during training to prevent overfitting.
6. Knowledge Distillation
  • Involves training a smaller model (student) to mimic the behavior of a larger, more powerful model (teacher). The smaller model can achieve similar performance with fewer parameters and resources.

2. Key Aspects of Advanced Fine-Tuning

  1. Optimizing Hyperparameters

    • Fine-tuning involves selecting the right hyperparameters, including learning rate, batch size, optimizer type, and number of epochs. Using techniques like grid search and random search can help find optimal settings.
  2. Transfer Learning

    • Fine-tuning a pre-trained model on a specific task takes advantage of the knowledge the model has already learned from a vast corpus of general data, reducing the amount of training required for task-specific adaptation.
  3. Model Evaluation During Fine-Tuning

    • It's crucial to evaluate the model at various stages of fine-tuning to ensure that improvements are being made and that the model is not overfitting.
  4. Computational Resources

    • Advanced fine-tuning techniques often require more computational resources. Optimizing the training process (e.g., through gradient accumulation or data parallelism) can help manage these resources effectively.

3. Implementation of Advanced Fine-Tuning Techniques

Prerequisites:

  • Pre-trained model (e.g., Llama).
  • A dataset for the specific task (e.g., PDF summarization, web scraping).
  • Python packages: transformers, torch, datasets, sklearn.

Learning Rate Scheduler

A learning rate scheduler can be used to adjust the learning rate dynamically during training.

from transformers import AdamW, get_linear_schedule_with_warmup
import torch

# Initialize model and tokenizer
model = LlamaForCausalLM.from_pretrained("llama-7b")
optimizer = AdamW(model.parameters(), lr=5e-5)

# Define scheduler
epochs = 3
train_dataloader = DataLoader(training_data, batch_size=8, shuffle=True)
num_training_steps = len(train_dataloader) * epochs
lr_scheduler = get_linear_schedule_with_warmup(optimizer, num_warmup_steps=0, num_training_steps=num_training_steps)

# Training loop with learning rate scheduler
for epoch in range(epochs):
    for batch in train_dataloader:
        optimizer.zero_grad()
        inputs = batch["input_ids"].to(device)
        labels = batch["labels"].to(device)
        
        outputs = model(inputs, labels=labels)
        loss = outputs.loss
        loss.backward()

        optimizer.step()
        lr_scheduler.step()  # Adjust learning rate

    print(f"Epoch {epoch + 1} completed with loss: {loss.item()}")

Early Stopping

Early stopping ensures that the training process halts once the model's performance on the validation set stops improving.

from transformers import Trainer, TrainingArguments

training_args = TrainingArguments(
    output_dir="./results",
    evaluation_strategy="epoch",  # Evaluate at the end of each epoch
    save_strategy="epoch",        # Save the model checkpoint at the end of each epoch
    per_device_train_batch_size=8,
    per_device_eval_batch_size=8,
    num_train_epochs=5,
    weight_decay=0.01,
    load_best_model_at_end=True,   # Load the best model after training
    metric_for_best_model="accuracy",  # Best model based on accuracy
)

trainer = Trainer(
    model=model,
    args=training_args,
    train_dataset=train_data,
    eval_dataset=eval_data,
    tokenizer=tokenizer,
)

trainer.train()

Data Augmentation for Text

In NLP tasks like summarization or question answering, data augmentation can involve techniques such as paraphrasing or using back-translation to create new examples from existing ones.

from nltk.corpus import wordnet

def synonym_augmentation(text):
    words = text.split()
    augmented_words = []
    
    for word in words:
        synonyms = wordnet.synsets(word)
        if synonyms:
            synonym = synonyms[0].lemmas()[0].name()  # Choose first synonym
            augmented_words.append(synonym)
        else:
            augmented_words.append(word)
    
    return " ".join(augmented_words)

augmented_text = synonym_augmentation("The research paper discusses novel methods in machine learning.")
print(augmented_text)

Gradient Accumulation

To simulate larger batch sizes without requiring large memory, you can accumulate gradients over several mini-batches before performing a gradient update.

from torch.utils.data import DataLoader

gradient_accumulation_steps = 4  # Accumulate gradients over 4 mini-batches

optimizer.zero_grad()
for step, batch in enumerate(train_dataloader):
    inputs = batch["input_ids"].to(device)
    labels = batch["labels"].to(device)
    
    outputs = model(inputs, labels=labels)
    loss = outputs.loss
    loss.backward()

    # Perform optimization step every `gradient_accumulation_steps` steps
    if (step + 1) % gradient_accumulation_steps == 0:
        optimizer.step()
        optimizer.zero_grad()

Model Regularization (Dropout)

Incorporating dropout in your model can help regularize the neural network and avoid overfitting.

from transformers import LlamaForCausalLM, LlamaConfig

# Define model configuration with dropout
config = LlamaConfig.from_pretrained("llama-7b")
config.attention_probs_dropout_prob = 0.1  # Dropout in attention layers
config.hidden_dropout_prob = 0.1  # Dropout in hidden layers

# Load model with custom configuration
model = LlamaForCausalLM(config)

# Training the model
optimizer = AdamW(model.parameters(), lr=5e-5)
for epoch in range(epochs):
    model.train()
    for batch in train_dataloader:
        optimizer.zero_grad()
        inputs = batch["input_ids"].to(device)
        labels = batch["labels"].to(device)
        
        outputs = model(inputs, labels=labels)
        loss = outputs.loss
        loss.backward()
        optimizer.step()

4. Real-Life Example: Fine-Tuning a Summarization Model

In this example, we will fine-tune a pre-trained Llama model for summarizing research papers. We will use early stopping, learning rate scheduling, and data augmentation techniques to ensure optimal training.

  1. Objective: Fine-tune a pre-trained Llama model on a summarization dataset.
  2. Dataset: A collection of research papers and their corresponding summaries.
  3. Techniques Applied:
    • Learning Rate Scheduler: Gradual adjustment of the learning rate.
    • Early Stopping: Halt training when the validation loss plateaus.
    • Data Augmentation: Increase dataset diversity using paraphrasing.
    • Model Regularization: Use dropout to prevent overfitting.

5. Summary

  • Advanced Fine-Tuning Techniques are essential to improving the performance of your model, particularly when you're working with specialized tasks like summarizing PDFs or extracting data.
  • Key techniques like learning rate scheduling, early stopping, data augmentation, and gradient accumulation allow for more efficient training and better model generalization.
  • Model Regularization (e.g., dropout) and knowledge distillation can further help in making the model robust and efficient.

6. Homework/Practice

  1. Fine-tune a pre-trained model for a custom task (e.g., summarization, Q&A, etc.).
  2. Implement a learning rate scheduler and evaluate its impact on training.
  3. Apply data augmentation and observe how it affects model generalization on unseen data.
  4. Experiment with gradient accumulation for large batch sizes on a resource-limited machine.

This concludes the lecture on Advanced Fine-Tuning Techniques.

Hour 9 - Metrics & Evaluation for Fine-Tuned Models

Lecture Notes: 


1. Concepts

What are Model Metrics?

  • Metrics are quantitative measures used to evaluate the performance of a model. They help assess how well a model is performing, both during training and after fine-tuning.
  • Metrics are essential in understanding the accuracy, precision, recall, F1-score, and other aspects of model performance.

Why are Metrics Important?

  • Metrics guide model improvements, provide insight into whether fine-tuning has been successful, and identify areas where the model can be further enhanced.
  • The evaluation process helps determine if the model can generalize well to new, unseen data or if it’s overfitting to the training data.

Key Types of Metrics for NLP Models:

  1. Accuracy: The percentage of correct predictions over the total predictions.
  2. Precision: The proportion of positive predictions that are actually correct.
  3. Recall: The proportion of actual positives that were correctly predicted.
  4. F1-Score: The harmonic mean of precision and recall, providing a balance between the two.
  5. BLEU (Bilingual Evaluation Understudy): Used primarily for evaluating machine translation models (or tasks like summarization).
  6. ROUGE (Recall-Oriented Understudy for Gisting Evaluation): Used for evaluating the quality of summaries by comparing the overlap of n-grams between the model output and a reference summary.
  7. Loss Function: Measures how far the model’s predictions are from the actual output. During fine-tuning, the goal is to minimize the loss.

2. Key Aspects of Metrics & Evaluation

  1. Choosing the Right Metric:

    • The right metric depends on the task. For tasks like summarization, ROUGE and BLEU are often used. For classification tasks, accuracy, precision, and recall are more relevant.
  2. Overfitting vs. Generalization:

    • Overfitting happens when a model performs well on training data but poorly on new data. Evaluating the model on both training and validation data helps detect overfitting.
    • Generalization refers to how well the model performs on unseen data.
  3. Evaluation Datasets:

    • Use validation and test datasets to evaluate the model.
    • Validation Set: Used during training to tune hyperparameters and prevent overfitting.
    • Test Set: Used only after training to evaluate the final performance of the model.
  4. Model Evaluation Pipeline:

    • Step 1: Prepare the evaluation dataset.
    • Step 2: Generate predictions using the fine-tuned model.
    • Step 3: Compare the model’s predictions to the true outputs using metrics.

3. Implementation of Evaluation and Metrics

Prerequisites:

  • Fine-tuned model (e.g., a PDF summarization model).
  • Evaluation dataset (e.g., PDFs with summaries or web-scraped content).

Example: Evaluating a Fine-Tuned Model

Step 1: Set Up Metrics (Accuracy, Precision, Recall, F1, BLEU, ROUGE)

You’ll use sklearn for traditional metrics (Accuracy, Precision, Recall, F1) and rouge-score for ROUGE and BLEU.

pip install scikit-learn rouge-score
Step 2: Generate Predictions

Assume you have a fine-tuned model that generates summaries for research papers. Here’s how to evaluate it:

from transformers import LlamaForCausalLM, LlamaTokenizer
from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score
from rouge_score import rouge_scorer

# Load model and tokenizer
model = LlamaForCausalLM.from_pretrained("./fine_tuned_model")
tokenizer = LlamaTokenizer.from_pretrained("./fine_tuned_model")

# Define evaluation data (text of research papers and their corresponding summaries)
eval_data = [
    {"input": "Research paper content 1", "output": "Summary of paper 1"},
    {"input": "Research paper content 2", "output": "Summary of paper 2"},
    # Add more samples for evaluation
]

# Generate predictions using the fine-tuned model
def generate_summary(input_text):
    inputs = tokenizer(input_text, return_tensors="pt", truncation=True, padding=True)
    summary_ids = model.generate(inputs["input_ids"], max_length=100, num_beams=2, early_stopping=True)
    summary = tokenizer.decode(summary_ids[0], skip_special_tokens=True)
    return summary

predictions = [generate_summary(d['input']) for d in eval_data]
actuals = [d['output'] for d in eval_data]
Step 3: Calculate Evaluation Metrics

Now, let’s calculate some key metrics.

  1. Accuracy:
    • Compare if the generated summary exactly matches the target summary.
# Simple exact match accuracy
accuracy = accuracy_score(actuals, predictions)
print(f"Accuracy: {accuracy:.4f}")
  1. Precision, Recall, F1-Score:
    • If your summaries are in binary or multi-class format, use precision, recall, and F1.
precision = precision_score(actuals, predictions, average="macro")
recall = recall_score(actuals, predictions, average="macro")
f1 = f1_score(actuals, predictions, average="macro")

print(f"Precision: {precision:.4f}")
print(f"Recall: {recall:.4f}")
print(f"F1-Score: {f1:.4f}")
  1. ROUGE Score:
    • ROUGE scores compare the overlap between the model’s generated summary and the reference summary.
# Using the rouge_score library
scorer = rouge_scorer.RougeScorer(["rouge1", "rouge2", "rougeL"], use_stemmer=True)
rouge_scores = [scorer.score(actual, pred) for actual, pred in zip(actuals, predictions)]

# Print ROUGE scores
for i, score in enumerate(rouge_scores):
    print(f"Example {i+1}: ROUGE-1: {score['rouge1'].fmeasure:.4f}, ROUGE-2: {score['rouge2'].fmeasure:.4f}, ROUGE-L: {score['rougeL'].fmeasure:.4f}")
  1. BLEU Score:
    • BLEU is commonly used for evaluating machine translation or text generation tasks.
from nltk.translate.bleu_score import sentence_bleu

# Compute BLEU score
bleu_scores = [sentence_bleu([actual.split()], pred.split()) for actual, pred in zip(actuals, predictions)]
print(f"BLEU Score: {sum(bleu_scores) / len(bleu_scores):.4f}")
Step 4: Visualize the Results (Optional)

Visualizing the performance of your model can give you a clearer understanding of its strengths and weaknesses.

import matplotlib.pyplot as plt

# Example: Plot ROUGE Scores for different examples
rouge_1_scores = [score['rouge1'].fmeasure for score in rouge_scores]
rouge_2_scores = [score['rouge2'].fmeasure for score in rouge_scores]
rouge_L_scores = [score['rougeL'].fmeasure for score in rouge_scores]

plt.plot(rouge_1_scores, label='ROUGE-1')
plt.plot(rouge_2_scores, label='ROUGE-2')
plt.plot(rouge_L_scores, label='ROUGE-L')
plt.legend()
plt.title("ROUGE Scores for Each Example")
plt.xlabel("Example Index")
plt.ylabel("ROUGE Score")
plt.show()

4. Real-Life Example: Evaluating PDF Summarization

Consider a scenario where you have a fine-tuned model that summarizes research papers (PDFs).

  1. Objective: Evaluate how well the model generates summaries by comparing them to human-provided summaries.
  2. Metrics: Use accuracy, ROUGE, and BLEU to evaluate the performance. ROUGE is ideal for summarization because it captures recall of important words, and BLEU ensures the fluency of the summary.
Step 1: Scrape and Label PDF Data

Use PyPDF2 to scrape content from PDFs and manually label a few examples with reference summaries.

import PyPDF2

def extract_text_from_pdf(pdf_path):
    with open(pdf_path, "rb") as file:
        reader = PyPDF2.PdfReader(file)
        text = ""
        for page in reader.pages:
            text += page.extract_text()
        return text

pdf_text = extract_text_from_pdf("sample_paper.pdf")
print(pdf_text[:500])  # Print first 500 characters of extracted text
Step 2: Fine-Tune and Evaluate Model

Fine-tune the model with PDF data and evaluate the performance using the metrics described above.


5. Code Summary

from transformers import LlamaForCausalLM, LlamaTokenizer
from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score
from rouge_score import rouge_scorer
from nltk.translate.bleu_score import sentence_bleu
import matplotlib.pyplot as plt

# Load model and tokenizer
model = LlamaForCausalLM.from_pretrained("./fine_tuned_model")
tokenizer = LlamaTokenizer.from_pretrained("./fine_tuned_model")

# Example: Evaluation Data
eval_data = [{"input": "Research paper content 1", "output": "Summary of paper 1"}]

# Generate predictions
predictions = [generate_summary(d['input']) for d in eval_data]
actuals = [d['output'] for d in eval_data]

# Evaluate with Accuracy, Precision, Recall, F1-Score
accuracy = accuracy_score(actuals, predictions)
precision = precision_score(actuals, predictions, average="macro")
recall = recall_score(actuals, predictions, average="macro")
f1 = f1_score(actuals, predictions, average="macro")

# ROUGE Scores
scorer = rouge_scorer.RougeScorer(["rouge1", "rouge2", "rougeL"], use_stemmer=True)
rouge_scores = [scorer.score(actual, pred) for actual, pred in zip(actuals, predictions)]

#

BLEU Score bleu_scores = [sentence_bleu([actual.split()], pred.split()) for actual, pred in zip(actuals, predictions)]

Visualization of ROUGE Scores

plt.plot([score['rouge1'].fmeasure for score in rouge_scores], label='ROUGE-1') plt.legend() plt.show()


---

### **6. Summary**

- **Concepts Covered**: Metrics for evaluation, including accuracy, precision, recall, F1-score, ROUGE, BLEU, and loss functions.
- **Key Aspects**: Evaluation ensures that models generalize well to new data and do not overfit. Different metrics are suited for different types of tasks (summarization, classification).
- **Real-Life Example**: Evaluating a PDF summarization model using ROUGE, BLEU, and traditional metrics.
- **Implementation**: Code for calculating various metrics using Python and common libraries like `sklearn`, `rouge-score`, and `nltk`.

---

### **7. Homework/Practice**

1. Evaluate your fine-tuned model using the above metrics on a new test set of PDFs or web-scraped data.
2. Experiment with different evaluation strategies such as using multiple BLEU references or adjusting the length of summaries.

Hour 8 - Introduction to Fine-Tuning Custom PDF and Web Scraping Models

Lecture Notes: 


1. Concepts

What is Fine-Tuning?

  • Fine-tuning is the process of adjusting a pre-trained model to improve its performance for a specific task or dataset.
  • Fine-tuning allows a model to better understand and generate responses based on domain-specific data, improving its accuracy and usefulness in real-world applications.

Why Fine-Tune PDF and Web Scraping Models?

  • Models that are trained on general data may not understand the nuances or specific needs of tasks like summarizing academic papers or extracting specific data from web pages.
  • Fine-tuning allows the model to specialize in these tasks by exposing it to relevant, labeled data.

Key Idea

  • Fine-tuning involves updating the weights of a model after it has been pre-trained. This is achieved by training it on new data that aligns with the target task.

2. Key Aspects of Fine-Tuning

  1. Base Model Selection:
    • Choose a model that already has useful general knowledge. Models like Llama are good starting points for fine-tuning.
  2. Dataset Preparation:
    • Labeled Data: For fine-tuning, you need a labeled dataset. For example, if you want to fine-tune a model for summarizing research papers, you need a dataset of papers paired with their summaries.
    • For PDFs: Label the data with clear instructions for the model to understand key points, summaries, or other types of content.
    • For Web Scraping: You can label data for specific types of information such as titles, articles, or key facts extracted from scraped web pages.
  3. Training Process:
    • The training process involves using small batches of data to modify the model’s weights.
    • Learning Rate: A key parameter for fine-tuning that controls how much the weights change during training.
  4. Evaluation:
    • After fine-tuning, evaluate the model to check if it performs well on new, unseen data.
  5. Transfer Learning:
    • Fine-tuning is a form of transfer learning, where you apply knowledge from one domain (general model) to another (specific task).

3. Implementation

Prerequisites:

  • Python Libraries: torch, transformers, ollama
  • Data Preparation: A dataset with labeled examples of the target task (summaries, extracted content).

Example: Fine-Tuning for PDF Summarization

Step 1: Create a Dataset for Fine-Tuning

First, prepare a small dataset of PDF summaries (input-output pairs).

# Example dataset for fine-tuning (PDF summaries)
data = [
    {"input": "Text of research paper 1", "output": "Summary of paper 1"},
    {"input": "Text of research paper 2", "output": "Summary of paper 2"},
    # Add more labeled examples
]
Step 2: Define the Model and Tokenizer

For fine-tuning, you’ll need to choose a pre-trained model. Let's assume we are working with Llama.

from transformers import LlamaForCausalLM, LlamaTokenizer

# Load the pre-trained model and tokenizer
model = LlamaForCausalLM.from_pretrained("llama")
tokenizer = LlamaTokenizer.from_pretrained("llama")
Step 3: Tokenize the Dataset

Convert the text data into tokens that can be fed into the model.

inputs = tokenizer([d['input'] for d in data], padding=True, truncation=True, return_tensors="pt")
labels = tokenizer([d['output'] for d in data], padding=True, truncation=True, return_tensors="pt")

# Create dataset for PyTorch
import torch
class PDFSummaryDataset(torch.utils.data.Dataset):
    def __init__(self, inputs, labels):
        self.inputs = inputs
        self.labels = labels
        
    def __getitem__(self, idx):
        return {"input_ids": self.inputs["input_ids"][idx], "labels": self.labels["input_ids"][idx]}

    def __len__(self):
        return len(self.inputs["input_ids"])

# Create DataLoader for batching
train_dataset = PDFSummaryDataset(inputs, labels)
train_loader = torch.utils.data.DataLoader(train_dataset, batch_size=2, shuffle=True)
Step 4: Fine-Tune the Model

Now, you can start the fine-tuning process using the dataset.

from transformers import Trainer, TrainingArguments

# Define training arguments
training_args = TrainingArguments(
    output_dir="./model_output",      # output directory
    evaluation_strategy="steps",      # evaluation strategy to adopt during training
    learning_rate=5e-5,               # learning rate
    per_device_train_batch_size=2,    # batch size
    num_train_epochs=3,               # number of epochs
    weight_decay=0.01                 # weight decay to avoid overfitting
)

# Define the Trainer
trainer = Trainer(
    model=model,                      # the pre-trained model
    args=training_args,               # training arguments
    train_dataset=train_dataset,      # training dataset
    eval_dataset=train_dataset        # evaluation dataset (optional)
)

# Fine-tune the model
trainer.train()
Step 5: Save the Fine-Tuned Model

After training, save your fine-tuned model.

model.save_pretrained("./fine_tuned_model")
tokenizer.save_pretrained("./fine_tuned_model")

4. Real-Life Example

Scenario: Fine-Tuning for Extracting Key Information from Web Scraped Articles

  • Objective: Fine-tune a model to extract specific information (e.g., author name, publication date, and article summary) from web pages scraped using BeautifulSoup.
Step 1: Scrape Data from the Web

Use the requests and BeautifulSoup libraries to scrape articles from a webpage.

from bs4 import BeautifulSoup
import requests

def scrape_web_article(url):
    response = requests.get(url)
    soup = BeautifulSoup(response.text, "html.parser")
    title = soup.find("h1").get_text()
    author = soup.find("span", class_="author").get_text()  # Example class
    return {"title": title, "author": author}

# Example: scrape an article
article = scrape_web_article("https://example.com/article")
print(article)
Step 2: Label the Data

Label the scraped content with the correct output (summary, author name, etc.).

web_data = [
    {"input": "Text from scraped article 1", "output": "Summary and key points"},
    # Add more data
]
Step 3: Fine-Tune the Model

Follow the same fine-tuning steps as in the PDF case, using the web-scraped content.


5. Code Summary

from transformers import LlamaForCausalLM, LlamaTokenizer, Trainer, TrainingArguments
import torch

# Load and prepare the model and tokenizer
model = LlamaForCausalLM.from_pretrained("llama")
tokenizer = LlamaTokenizer.from_pretrained("llama")

# Prepare dataset (input-output pairs)
data = [
    {"input": "Text of research paper 1", "output": "Summary of paper 1"},
    # Add more labeled examples
]

inputs = tokenizer([d['input'] for d in data], padding=True, truncation=True, return_tensors="pt")
labels = tokenizer([d['output'] for d in data], padding=True, truncation=True, return_tensors="pt")

# Fine-tune the model
training_args = TrainingArguments(
    output_dir="./model_output", num_train_epochs=3, per_device_train_batch_size=2, learning_rate=5e-5
)
trainer = Trainer(model=model, args=training_args, train_dataset=train_dataset)
trainer.train()

# Save the fine-tuned model
model.save_pretrained("./fine_tuned_model")
tokenizer.save_pretrained("./fine_tuned_model")

6. Summary

  • Concepts Covered: Fine-tuning, transfer learning, dataset preparation, training, and evaluation.
  • Key Aspects: Fine-tuning requires a labeled dataset, careful model selection, and tuning of hyperparameters.
  • Real-Life Example: Fine-tuning a model for summarizing research papers (PDFs) and extracting key details from web-scraped content.
  • Implementation: Steps involved creating datasets, tokenizing them, fine-tuning the model, and evaluating it.

7. Homework/Practice

  1. Fine-tune the model you created in the previous lesson to summarize a new set of PDFs.
  2. Use web-scraped content and fine-tune the model for extracting key details (e.g., title, author, summary) from articles.
  3. Experiment with different learning rates and batch sizes to see how they affect model performance.

These lecture notes provide a step-by-step introduction to fine-tuning models for custom tasks like PDF summarization and web scraping, offering practical examples with Python and Ollama CLI code.

Hour 7 - Creating Custom Models for PDF and Web Scraping

Lecture Notes: 


1. Concepts

Custom Models in Ollama

  • Custom Models: Tailored versions of base models created to handle specific tasks like answering questions from PDFs or summarizing web pages.
  • Ollama allows users to create models by defining custom system prompts and incorporating specific templates.

PDF and Web Scraping with AI

  • PDF Parsing: Extracting meaningful information (e.g., text, metadata) from PDF documents.
  • Web Scraping: Collecting data from websites for insights or analysis.
  • Both tasks require processing structured and unstructured text data, making them ideal for custom AI models.

2. Key Aspects

  1. Key Components of a Custom Model for PDF and Web Scraping:

    • Input Source: The source data (PDFs or web pages).
    • Preprocessing: Cleaning and structuring the data for AI consumption.
    • Model Behavior: Tailored system prompts to guide output generation.
  2. Why Custom Models for PDF and Web Scraping?

    • Automate repetitive tasks like extracting summaries or key points.
    • Handle domain-specific data with fine-tuned responses.
    • Increase efficiency in research, data collection, and reporting.
  3. Challenges:

    • Handling large or complex PDFs.
    • Avoiding CAPTCHA and legal concerns during web scraping.
    • Processing noisy or unstructured data effectively.

3. Implementation

CLI Commands for Custom Models:

Command Description Example
ollama run Run a custom model to process extracted text. ollama run pdf_reader --prompt "Summarize"
ollama create Create a new model with a system prompt and template. ollama create pdf_reader -f ./modelfile
ollama pull Pull a base model as a starting point. ollama pull llama
ollama show Display the details of the custom model. ollama show pdf_reader

4. Real-Life Example

Scenario: Extracting Key Points from Research PDFs

  • Objective: Build a model to summarize PDFs containing scientific research papers.
  • Use Case: A researcher needs concise summaries to save time.

5. Code Examples

Step 1: Preprocess PDFs

Use Python to extract text from PDFs. Libraries like PyPDF2 or pdfplumber are commonly used.

import pdfplumber

def extract_text_from_pdf(pdf_path):
    with pdfplumber.open(pdf_path) as pdf:
        text = ""
        for page in pdf.pages:
            text += page.extract_text()
    return text

# Example usage
pdf_text = extract_text_from_pdf("example_research.pdf")
print(pdf_text[:500])  # Print the first 500 characters

Step 2: Create a Custom Model

Define a modelfile with behavior tailored for summarizing research.

Modelfile (modelfile.txt):

FROM llama
SYSTEM """
You are a research assistant. Summarize the content of research papers in a concise and clear manner. Include key points and findings.
"""

Create the custom model with Ollama CLI:

# Create the custom model
ollama create pdf_reader -f ./modelfile.txt

Step 3: Run the Custom Model

Pass the extracted text from the PDF to the model.

# Run the custom model
ollama run pdf_reader --prompt "Summarize the following: [Insert extracted text here]"

Step 4: Web Scraping for Data

Use Python with libraries like BeautifulSoup to scrape data from web pages.

from bs4 import BeautifulSoup
import requests

def scrape_web_page(url):
    response = requests.get(url)
    soup = BeautifulSoup(response.text, "html.parser")
    return soup.get_text()

# Example usage
web_text = scrape_web_page("https://example.com/research-article")
print(web_text[:500])  # Print the first 500 characters

Step 5: Integrate Web Data into the Model

Run the scraped content through the custom model.

# Run the custom model with web-scraped content
ollama run pdf_reader --prompt "Summarize the following: [Insert scraped text here]"

6. Example Outputs

PDF Summary:

"This research explores the impact of climate change on agriculture. Key findings include a 20% decrease in crop yield due to rising temperatures and droughts. Adaptive measures, such as genetic modification, show potential to mitigate these effects."

Web Scraping Summary:

"The article discusses the latest advancements in AI, focusing on generative models and their applications in healthcare and education."


7. Summary

  • Concepts Covered: Custom models, PDF parsing, and web scraping.
  • Key Aspects: Preprocessing, model creation, and data integration.
  • Implementation: Preprocessing PDFs and web data, creating a model, and running it for summaries.
  • Real-Life Example: Summarizing research papers and web content.

8. Homework/Practice

  1. Extract text from a PDF of your choice and pass it through a custom Ollama model.
  2. Scrape a webpage and summarize its content using the model.
  3. Experiment with different system prompts to customize model behavior.
  4. Compare the summaries generated by a base model and your custom model.

These lecture notes provide a comprehensive understanding of creating custom models for PDF and web scraping tasks, with practical examples and code samples to enhance learning.

Hour 6 - Overview of Models

Lecture Notes: 


1. Concepts

What is a Model in Machine Learning?

  • A model is a computational representation of a process used to make predictions or generate insights based on input data.
  • In the context of language models, such as those used in Ollama, a model generates text or embeddings based on input prompts.

Key Components of a Model:

  1. Architecture: Defines the structure of the model (e.g., transformers like GPT, BERT).
  2. Parameters: Determines the model's capacity to learn from data (e.g., number of neurons, layers).
  3. Weights: Encoded knowledge the model learns during training.
  4. System Prompt: A predefined instruction guiding the model’s behavior.
  5. Fine-Tuning: Adjusting the model for a specific task by retraining on a domain-specific dataset.

2. Key Aspects

Types of Models in Ollama:

  1. Pre-trained Models: Models trained on large datasets for general-purpose tasks.
    • Example: GPT, Llama, Mistral.
  2. Fine-Tuned Models: Pre-trained models further trained on specific datasets for specialized tasks.
    • Example: A chatbot fine-tuned for customer service.
  3. Custom Models: Created by users with specific system prompts, templates, or datasets.

Why Use Models in Ollama?

  • Enable text generation, summarization, translation, embedding generation, and more.
  • Provide flexibility through CLI for creating, modifying, and deploying models.

3. Implementation

CLI Commands for Working with Models

Command Description Example
ollama run Run a model to generate text based on a prompt. ollama run llama --prompt "Hello!"
ollama create Create a custom model from a base model. ollama create mymodel -f ./modelfile
ollama pull Download a model from Ollama’s repository. ollama pull llama
ollama show Display details of a model. ollama show llama
ollama ls List all available models locally. ollama ls
ollama rm Remove a model. ollama rm llama
ollama cp Copy a model (e.g., for renaming). ollama cp llama llama_custom

4. Real-Life Example

Scenario: Customizing a Model for Technical FAQs

Imagine creating a custom model to answer FAQs for a software company.

  • Base Model: Llama 2.
  • Customization: Add a system prompt that aligns the model with the company’s tone and expertise.

5. Code Examples

Step 1: Pull the Base Model

Download a base model to use as the foundation for your customization.

# Pull a model from Ollama's repository
ollama pull llama

Step 2: Create a Custom Model

Define the behavior of your custom model in a modelfile.

Modelfile (modelfile.txt):

FROM llama
SYSTEM """
You are a technical support assistant for Acme Software.
Provide concise and accurate answers to customer queries.
"""

Use the ollama create command to generate the model.

# Create a custom model
ollama create acme_support -f ./modelfile.txt

Step 3: Run the Custom Model

Test your custom model by running it with a prompt.

# Run the custom model
ollama run acme_support --prompt "What are the system requirements for Acme Pro 3.0?"

Step 4: Show Model Details

Inspect the properties of the newly created model.

# Display model details
ollama show acme_support

6. Example Output

Input Prompt:

"What are the system requirements for Acme Pro 3.0?"

Model Response:

"Acme Pro 3.0 requires Windows 10 or later, 8GB RAM, and 20GB of free disk space. For macOS, it supports version 11.0 or newer."


7. Summary

  • Concepts Covered: Overview of models, their types, and customization in Ollama.
  • Key Aspects: Pre-trained, fine-tuned, and custom models, along with the CLI commands.
  • Implementation: Demonstrated creating, running, and inspecting models.
  • Real-Life Example: Built a custom technical support model.

8. Homework/Practice

  1. Pull a base model and test its default behavior.
  2. Create a custom model with a system prompt of your choice.
  3. Run your custom model to answer specific questions.
  4. Experiment with fine-tuning a model by modifying the modelfile or adding new training data.

This lecture provides a solid foundation in understanding and working with models in Ollama, emphasizing practical usage with CLI commands and real-life applications.

Hour 5 - Working with Vector Databases

Lecture Notes: 


1. Concepts

What is a Vector Database?

  • A vector database is designed to store, manage, and query high-dimensional vector embeddings efficiently.
  • It enables similarity search and nearest neighbor queries, critical for working with embeddings generated by models like Ollama.

Key Features of Vector Databases:

  1. High-Dimensional Indexing: Stores embeddings (vectors) and enables fast searches.
  2. Similarity Search: Finds vectors closest to a given query vector using distance metrics like cosine similarity or Euclidean distance.
  3. Scalability: Handles large-scale data efficiently.
  4. Integration: Can work with other data structures (e.g., JSON metadata) for richer querying.

2. Key Aspects

Why Use a Vector Database?

  • Efficiency: Optimized for querying large-scale vector data.
  • Accuracy: Provides precise similarity results using advanced indexing algorithms like HNSW (Hierarchical Navigable Small World).
  • Real-World Use Cases: Image retrieval, semantic search, recommendation systems, chatbots.

Common Vector Databases:

  • Pinecone
  • Weaviate
  • Qdrant
  • Milvus

Querying Techniques:

  1. K-Nearest Neighbors (KNN): Finds top K vectors closest to the query vector.
  2. Hybrid Search: Combines vector similarity with traditional keyword-based searches.

3. Implementation

Setting Up a Vector Database

  1. Install and configure the database. Most vector databases provide cloud-hosted and local setups.
  2. Store embeddings: Use the embeddings generated by ollama embed.
  3. Query embeddings: Perform similarity searches to find relevant results.

4. CLI Commands for Working with Vector Databases

Command Description Example
ollama embed Generate embeddings for text or documents. ollama embed "example text" --format json
ollama run Use embeddings as part of a model query. ollama run mymodel --format json

5. Real-Life Example

Scenario: Build a Semantic Search Engine with a Vector Database

Suppose we want to search through a collection of customer reviews to find those most relevant to a user query. The embeddings of the reviews will be stored in a vector database and queried for similarity.


6. Code Examples

Step 1: Install Qdrant Vector Database

Qdrant is an easy-to-use vector database with local and cloud options.

# Install Qdrant locally via Docker
docker pull qdrant/qdrant
docker run -p 6333:6333 qdrant/qdrant

Step 2: Store Embeddings in Qdrant

import qdrant_client
from qdrant_client.models import PointStruct
import json

# Initialize Qdrant client
client = qdrant_client.QdrantClient(url="http://localhost:6333")

# Create a collection for embeddings
client.recreate_collection(
    collection_name="customer_reviews",
    vector_size=512,  # Dimension of embeddings
    distance="Cosine"
)

# Load embedding generated by Ollama
with open("review1_embedding.json", "r") as file:
    review1 = json.load(file)

with open("review2_embedding.json", "r") as file:
    review2 = json.load(file)

# Insert embeddings into the database
points = [
    PointStruct(id=1, vector=review1["embedding"], payload={"text": review1["text"]}),
    PointStruct(id=2, vector=review2["embedding"], payload={"text": review2["text"]}),
]

client.upsert(collection_name="customer_reviews", points=points)

Step 3: Query the Vector Database

# Simulate a user query
query = "What do customers say about product quality?"

# Generate query embedding (replace with actual embedding generated by Ollama)
query_embedding = [0.12, 0.34, ...]  # Placeholder example

# Perform similarity search
results = client.search(
    collection_name="customer_reviews",
    query_vector=query_embedding,
    limit=2  # Retrieve top 2 matches
)

# Display results
for result in results:
    print(f"Score: {result.score}")
    print(f"Review: {result.payload['text']}")

7. Summary

  • Concepts Covered: What vector databases are, why they're useful, and how they enable efficient similarity searches.
  • Key Aspects: High-dimensional indexing, similarity measures, and practical use cases.
  • CLI Commands: Use ollama embed to generate embeddings for storing in the database.
  • Real-Life Example: Semantic search through customer reviews using Qdrant.
  • Code Examples: Storing and querying embeddings in Qdrant.

8. Homework/Practice

  1. Install a vector database of your choice (e.g., Qdrant, Milvus).
  2. Generate embeddings for five text samples using ollama embed.
  3. Store these embeddings in the database.
  4. Implement a Python script to perform similarity searches and rank the results.
  5. Experiment with different distance metrics (e.g., Euclidean vs. Cosine).

This lecture introduces students to working with vector databases and includes a practical example with Qdrant, a widely-used vector database.

Hour 4 - Introduction to Embeddings

Lecture Notes: 

 Here’s an lecture notes with a code sample that includes generating and using embeddings with Ollama:

1. Concepts

What are Embeddings?

  • Definition: Embeddings are numerical representations of text, words, or concepts in a vector space. These vectors capture semantic meaning, allowing models to understand relationships between words or phrases.
  • Key Idea: Words or sentences with similar meanings are mapped to vectors that are close together in the vector space.

How Embeddings Work:

  • Transform textual data into fixed-size dense vectors.
  • Represent semantic similarity (e.g., "king" and "queen" will have similar embeddings).
  • Provide a foundation for tasks like search, clustering, and recommendation systems.

2. Key Aspects

Properties of Embeddings:

  1. Dimensionality: Number of values in the vector (e.g., 512, 768).
  2. Contextual vs. Static:
    • Static Embeddings: Fixed embeddings for words (e.g., Word2Vec, GloVe).
    • Contextual Embeddings: Represent words based on their context (e.g., BERT, GPT).
  3. Similarity Measures: Cosine similarity is commonly used to compare embeddings.

Applications of Embeddings:

  • Search Engines: Find documents or information using semantic similarity.
  • Recommendation Systems: Recommend items based on user preferences.
  • Clustering and Classification: Group similar data points together.

3. Implementation

Step-by-Step: Using Embeddings in Ollama

  1. Generate Embeddings:

    • Use the Ollama CLI to create embeddings for text or documents.
  2. Store Embeddings:

    • Save the embeddings in a JSON file or a vector database.
  3. Perform Similarity Search:

    • Compare embeddings to find semantically similar items.

4. CLI Commands for Embeddings

Command Description Example
ollama embed Generates embeddings for a given text or document. ollama embed "The quick brown fox"
ollama embed --format Outputs embeddings in JSON format for easier integration with databases. ollama embed "AI is amazing" --format json

5. Real-Life Example

Scenario: Building a Semantic Search Engine

Suppose you want to search a set of documents based on meaning rather than exact keyword matches. Use embeddings to find documents most relevant to a user's query.


6. Code Examples

Generating and Storing Embeddings with Ollama CLI

# Generate embeddings for a document
ollama embed "Artificial Intelligence is fascinating." --format json > ai_embedding.json

# Generate embeddings for another text
ollama embed "Machine learning is a subset of AI." --format json > ml_embedding.json

# Inspect the JSON output
cat ai_embedding.json

Sample output in ai_embedding.json:

{
  "text": "Artificial Intelligence is fascinating.",
  "embedding": [0.123, -0.456, 0.789, ...]
}

Implementing Similarity Search with Ollama and Python

import json
import numpy as np
from sklearn.metrics.pairwise import cosine_similarity

# Load embeddings generated by Ollama
with open("ai_embedding.json", "r") as file:
    ai_data = json.load(file)

with open("ml_embedding.json", "r") as file:
    ml_data = json.load(file)

# Extract embeddings
ai_embedding = np.array(ai_data["embedding"])
ml_embedding = np.array(ml_data["embedding"])

# Simulate a user query and generate its embedding (use Ollama CLI in practice)
query = "Tell me about AI and its applications."
query_embedding = np.random.rand(len(ai_embedding))  # Replace with actual embedding

# Compute cosine similarity
similarities = cosine_similarity([query_embedding], [ai_embedding, ml_embedding])
ranked_indices = similarities.argsort()[0][::-1]

# Map indices to documents
documents = [
    ai_data["text"],
    ml_data["text"]
]

# Print results
print("Query:", query)
print("Top matches:")
for idx in ranked_indices:
    print(f"- {documents[idx]} (Score: {similarities[0][idx]:.4f})")

7. Summary

  • Concepts Covered: Definition and significance of embeddings, their properties, and applications.
  • Key Aspects: Dimensionality, contextual vs. static embeddings, and similarity measures.
  • CLI Commands: Generating and using embeddings with ollama embed.
  • Real-Life Example: Semantic search for finding relevant documents.
  • Code Examples: Generating embeddings using Ollama CLI and performing similarity search.

8. Homework/Practice

  1. Use ollama embed to generate embeddings for five text samples.
  2. Save the embeddings in JSON files.
  3. Write a Python script to load these embeddings and implement a semantic search engine.
  4. Experiment with additional similarity measures (e.g., Euclidean distance).

This extended lecture note now includes a practical demonstration of generating embeddings using the Ollama CLI and processing them programmatically for real-world applications.

OpenWebUI - Beginner's Tutorial

  OpenWebUI Tutorial: Setting Up and Using Local Llama 3.2 with Ollama Introduction This tutorial provides a step-by-step guide to setting...