[Python] How can I convert this code into binary classification?

Discussão em 'Python' iniciado por Stack, Setembro 24, 2024.

  1. Stack

    Stack Membro Participativo

    The Actual Code link: https://github.com/mrdbourke/pytorc...rch_model_deployment_exercise_solutions.ipynb

    I am trying to adjust the actual code above to work with my custom dataset for binary classification. I appreciate your help!

    from google.colab import drive
    drive.mount('/content/drive')

    %%writefile get_data.py
    import os
    import zipfile

    from pathlib import Path

    import requests

    # Setup path to data folder
    data_path = Path("/content/drive/MyDrive/Dataset/")
    image_path = data_path / "/content/drive/MyDrive/Dataset/Datas"

    # If the image folder doesn't exist, download it and prepare it...
    if image_path.is_dir():
    print(f"{image_path} directory exists.")
    else:
    print(f"Did not find {image_path} directory, creating one...")
    image_path.mkdir(parents=True, exist_ok=True)

    %%writefile data_setup.py
    """
    Contains functionality for creating PyTorch DataLoaders for
    image classification data.
    """
    import os

    from torchvision import datasets, transforms
    from torch.utils.data import DataLoader

    NUM_WORKERS = os.cpu_count()

    def create_dataloaders(
    train_dir: str,
    test_dir: str,
    transform: transforms.Compose,
    batch_size: int,
    num_workers: int=NUM_WORKERS
    ):

    # Use ImageFolder to create dataset(s)
    train_data = datasets.ImageFolder(train_dir, transform=transform)
    test_data = datasets.ImageFolder(test_dir, transform=transform)

    # Get class names
    class_names = train_data.classes

    # Turn images into data loaders
    train_dataloader = DataLoader(
    train_data,
    batch_size=batch_size,
    shuffle=True,
    num_workers=num_workers,
    pin_memory=True,
    )
    test_dataloader = DataLoader(
    test_data,
    batch_size=batch_size,
    shuffle=False,
    num_workers=num_workers,
    pin_memory=True,
    )

    return train_dataloader, test_dataloader, class_names

    %%writefile engine.py
    """
    Contains functions for training and testing a PyTorch model.
    """
    import torch
    from tqdm.auto import tqdm
    from typing import Dict, List, Tuple

    def train_step(model: torch.nn.Module,
    dataloader: torch.utils.data.DataLoader,
    loss_fn: torch.nn.Module,
    optimizer: torch.optim.Optimizer,
    device: torch.device) -> Tuple[float, float]:

    # Put model in train mode
    model.train()

    # Setup train loss and train accuracy values
    train_loss, train_acc = 0, 0

    # Loop through data loader data batches
    for batch, (X, y) in enumerate(dataloader):
    # Send data to target device
    X, y = X.to(device), y.to(device)
    y = y.unsqueeze(1).float() # Ensure y has the shape [batch_size, 1]

    # Print the shape of y for debugging
    print(f"Train Step - y shape: {y.shape}")

    # 1. Forward pass
    y_pred = model(X)

    # 2. Calculate and accumulate loss
    loss = loss_fn(y_pred, y)
    train_loss += loss.item()

    # 3. Optimizer zero grad
    optimizer.zero_grad()

    # 4. Loss backward
    loss.backward()

    # 5. Optimizer step
    optimizer.step()

    # Calculate and accumulate accuracy metric across all batches
    y_pred_class = torch.round(torch.sigmoid(y_pred))
    train_acc += (y_pred_class == y).sum().item() / len(y_pred)

    # Adjust metrics to get average loss and accuracy per batch
    train_loss = train_loss / len(dataloader)
    train_acc = train_acc / len(dataloader)
    return train_loss, train_acc

    def test_step(model: torch.nn.Module,
    dataloader: torch.utils.data.DataLoader,
    loss_fn: torch.nn.Module,
    device: torch.device) -> Tuple[float, float]:

    # Put model in eval mode
    model.eval()

    # Setup test loss and test accuracy values
    test_loss, test_acc = 0, 0

    # Turn on inference context manager
    with torch.inference_mode():
    # Loop through DataLoader batches
    for batch, (X, y) in enumerate(dataloader):
    # Send data to target device
    X, y = X.to(device), y.to(device)
    y = y.unsqueeze(1).float() # Ensure y has the shape [batch_size, 1]

    # Print the shape of y for debugging
    print(f"Test Step - y shape: {y.shape}")

    # 1. Forward pass
    test_pred_logits = model(X)

    # 2. Calculate and accumulate loss
    loss = loss_fn(test_pred_logits, y)
    test_loss += loss.item()

    # Calculate and accumulate accuracy
    test_pred_labels = torch.round(torch.sigmoid(test_pred_logits))
    test_acc += ((test_pred_labels == y).sum().item() / len(test_pred_labels))

    # Adjust metrics to get average loss and accuracy per batch
    test_loss = test_loss / len(dataloader)
    test_acc = test_acc / len(dataloader)
    return test_loss, test_acc

    def train(model: torch.nn.Module,
    train_dataloader: torch.utils.data.DataLoader,
    test_dataloader: torch.utils.data.DataLoader,
    optimizer: torch.optim.Optimizer,
    loss_fn: torch.nn.Module,
    epochs: int,
    device: torch.device) -> Dict[str, List]:

    # Create empty results dictionary
    results = {"train_loss": [],
    "train_acc": [],
    "test_loss": [],
    "test_acc": []
    }

    # Loop through training and testing steps for a number of epochs
    for epoch in tqdm(range(epochs)):
    train_loss, train_acc = train_step(model=model,
    dataloader=train_dataloader,
    loss_fn=loss_fn,
    optimizer=optimizer,
    device=device)
    test_loss, test_acc = test_step(model=model,
    dataloader=test_dataloader,
    loss_fn=loss_fn,
    device=device)

    # Print out what's happening
    print(
    f"Epoch: {epoch+1} | "
    f"train_loss: {train_loss:.4f} | "
    f"train_acc: {train_acc:.4f} | "
    f"test_loss: {test_loss:.4f} | "
    f"test_acc: {test_acc:.4f}"
    )

    # Update results dictionary
    results["train_loss"].append(train_loss)
    results["train_acc"].append(train_acc)
    results["test_loss"].append(test_loss)
    results["test_acc"].append(test_acc)

    # Return the filled results at the end of the epochs
    return results

    %%writefile model_builder.py
    """
    Contains PyTorch model code to instantiate a TinyVGG model.
    """
    import torch
    from torch import nn

    class TinyVGG(nn.Module):

    def __init__(self, input_shape: int, hidden_units: int, output_shape: int) -> None:
    super().__init__()
    self.conv_block_1 = nn.Sequential(
    nn.Conv2d(in_channels=input_shape,
    out_channels=hidden_units,
    kernel_size=3,
    stride=1,
    padding=0),
    nn.ReLU(),
    nn.Conv2d(in_channels=hidden_units,
    out_channels=hidden_units,
    kernel_size=3,
    stride=1,
    padding=0),
    nn.ReLU(),
    nn.MaxPool2d(kernel_size=2,
    stride=2)
    )
    self.conv_block_2 = nn.Sequential(
    nn.Conv2d(hidden_units, hidden_units, kernel_size=3, padding=0),
    nn.ReLU(),
    nn.Conv2d(hidden_units, hidden_units, kernel_size=3, padding=0),
    nn.ReLU(),
    nn.MaxPool2d(2)
    )
    self.classifier = nn.Sequential(
    nn.Flatten(),
    # Where did this in_features shape come from?
    # It's because each layer of our network compresses and changes the shape of our inputs data.
    nn.Linear(in_features=hidden_units*13*13,
    out_features=1)
    )

    def forward(self, x: torch.Tensor):
    x = self.conv_block_1(x)
    x = self.conv_block_2(x)
    x = self.classifier(x)
    return x
    # return self.classifier(self.block_2(self.block_1(x))) # <- leverage the benefits of operator fusion

    %%writefile utils.py
    """
    Contains various utility functions for PyTorch model training and saving.
    """
    import torch
    from pathlib import Path

    def save_model(model: torch.nn.Module,
    target_dir: str,
    model_name: str):

    # Create target directory
    target_dir_path = Path(target_dir)
    target_dir_path.mkdir(parents=True,
    exist_ok=True)

    # Create model save path
    assert model_name.endswith(".pth") or model_name.endswith(".pt"), "model_name should end with '.pt' or '.pth'"
    model_save_path = target_dir_path / model_name

    # Save the model state_dict()
    print(f"[INFO] Saving model to: {model_save_path}")
    torch.save(obj=model.state_dict(),
    f=model_save_path)
    %%writefile train.py
    """
    Trains a PyTorch image classification model using device-agnostic code.
    """

    import os
    import argparse

    import torch

    from torchvision import transforms

    import data_setup, engine, model_builder, utils

    # Create a parser
    parser = argparse.ArgumentParser(description="Get some hyperparameters.")

    # Get an arg for num_epochs
    parser.add_argument("--num_epochs",
    default=10,
    type=int,
    help="the number of epochs to train for")

    # Get an arg for batch_size
    parser.add_argument("--batch_size",
    default=32,
    type=int,
    help="number of samples per batch")

    # Get an arg for hidden_units
    parser.add_argument("--hidden_units",
    default=10,
    type=int,
    help="number of hidden units in hidden layers")

    # Get an arg for learning_rate
    parser.add_argument("--learning_rate",
    default=0.001,
    type=float,
    help="learning rate to use for model")

    # Create an arg for training directory
    parser.add_argument("--train_dir",
    default="/content/drive/MyDrive/Dataset/Datas/Train",
    type=str,
    help="directory file path to training data in standard image classification format")

    # Create an arg for test directory
    parser.add_argument("--test_dir",
    default="/content/drive/MyDrive/Dataset/Datas/Test",
    type=str,
    help="directory file path to testing data in standard image classification format")

    # Get our arguments from the parser
    args = parser.parse_args()

    # Setup hyperparameters
    NUM_EPOCHS = args.num_epochs
    BATCH_SIZE = args.batch_size
    HIDDEN_UNITS = args.hidden_units
    LEARNING_RATE = args.learning_rate
    print(f"[INFO] Training a model for {NUM_EPOCHS} epochs with batch size {BATCH_SIZE} using {HIDDEN_UNITS} hidden units and a learning rate of {LEARNING_RATE}")

    # Setup directories
    train_dir = args.train_dir
    test_dir = args.test_dir
    print(f"[INFO] Training data file: {train_dir}")
    print(f"[INFO] Testing data file: {test_dir}")

    # Setup target device
    device = "cuda" if torch.cuda.is_available() else "cpu"

    # Create transforms
    data_transform = transforms.Compose([
    transforms.Resize((64, 64)),
    transforms.ToTensor()
    ])

    # Create DataLoaders with help from data_setup.py
    train_dataloader, test_dataloader, class_names = data_setup.create_dataloaders(
    train_dir=train_dir,
    test_dir=test_dir,
    transform=data_transform,
    batch_size=BATCH_SIZE
    )

    # Create model with help from model_builder.py
    model = model_builder.TinyVGG(
    input_shape=3,
    hidden_units=HIDDEN_UNITS,
    output_shape=1
    ).to(device)

    # Set loss and optimizer
    loss_fn = torch.nn.BCEWithLogitsLoss()
    optimizer = torch.optim.Adam(model.parameters(),
    lr=LEARNING_RATE)

    # Start training with help from engine.py
    engine.train(model=model,
    train_dataloader=train_dataloader,
    test_dataloader=test_dataloader,
    loss_fn=loss_fn,
    optimizer=optimizer,
    epochs=NUM_EPOCHS,
    device=device)

    # Save the model with help from utils.py
    utils.save_model(model=model,
    target_dir="models",
    model_name="05_going_modular_script_mode_tinyvgg_model.pth")

    !python train.py --num_epochs 10 --batch_size 32 --hidden_units 128 --learning_rate 0.001



    The Actual Code link: https://github.com/mrdbourke/pytorc...rch_model_deployment_exercise_solutions.ipynb

    I am trying to adjust the actual code above to work with my custom dataset for binary classification. I appreciate your help!

    I have 512x512 RGB images, but the above code is resizing them to 64x64. I am using BCEWithLogitsLoss, and I am trying to use unsqueeze, but I can't transition from multi-class classification to binary classification. I have two classes: diseased and healthy, and I have a custom dataset. I keep getting the errors: "Target size (torch.Size([32])) must be the same as input size (torch.Size([32, 1]))" or "RuntimeError: result type Float can't be cast to the desired output type Long."

    What part of the code is faulty, and how should I proceed? I would really appreciate your help!

    Continue reading...

Compartilhe esta Página