code: """
  Nick Kaparinos
  Dogs vs. Cats
  Kaggle Competition
  Grid Search using pytorch
  """
  
  import pandas as pd
  from random import seed
  from utilities import *
  from torch.utils.data import DataLoader
  from torchvision.models import vgg16
  from torch.utils.tensorboard import SummaryWriter
  import torch
  import time
  
  # Options
  pd.set_option('display.max_columns', None)
  pd.set_option('display.max_rows', None)
  do_preprocess = False
  use_vgg = False
  IMG_SIZE = 75
  start = time.perf_counter()
  seed(0)
  np.random.seed(0)
  tf.random.set_seed(0)
  torch.manual_seed(0)
  device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
  print(f"Using: {device}")
  
  # Tensorboard
  LOG_DIR = 'logs/pytorch'
  writer = SummaryWriter(log_dir=LOG_DIR)
  
  # Preprocess Data
  if do_preprocess:
      preprocess_data(IMG_SIZE, convert_to_grayscale=False)
  
  # Read Data
  X_train = np.load("X_train.npy")
  y_train = np.load("y_train.npy")
  X_test = np.load("X_test.npy")
  input_channels = X_train.shape[3]
  IMG_SIZE = X_train.shape[1]
  
  # Convert to tensors
  X_train = torch.Tensor(X_train)
  X_test = torch.Tensor(X_test)
  y_train = torch.Tensor(y_train)
  
  # Scale
  X_train = X_train / 255.0
  X_test = X_test / 255.0
  
  # Model
  if use_vgg:
      model = vgg16(pretrained=True)
  
      # Freeze parameters
      for param in model.parameters():
          param.requires_grad = False
  
      # Add final layer
      num_features = model.classifier._modules['6'].in_features
      model.classifier._modules['6'] = nn.Linear(num_features, 1)
      model.classifier._modules['7'] = nn.Sigmoid()
      model = model.to(device)
  else:
      model = pytorch_model(input_channels=input_channels).to(device)
  print(model)
  
  learning_rate = 5e-5
  epochs = 5
  loss_fn = torch.nn.BCELoss()
  optimizer = torch.optim.Adam(model.parameters(), lr=learning_rate)
  
  # Training
  X_train = X_train.to(device)
  y_train = y_train.to(device)
  
  # Validation split
  VAL_PCT = 0.1
  val_size = int(len(X_train) * VAL_PCT)
  X_val = X_train[-val_size:]
  y_val = y_train[-val_size:]
  X_train = X_train[:-val_size]
  y_train = y_train[:-val_size]
  
  # Data loaders
  batch_size = 128
  train_dataset = [(X_train[i], y_train[i]) for i in range(X_train.size()[0])]
  validation_dataset = [(X_val[i], y_val[i]) for i in range(X_val.size()[0])]
  train_dataloader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True)
  test_dataloader = DataLoader(validation_dataset, batch_size=batch_size, shuffle=True)
  
  # Training
  for epoch in range(epochs):
      print(f"-----------------Epoch {epoch + 1}-----------------")
      pytorch_train_loop(train_dataloader, model, loss_fn, optimizer, writer, epoch)
      pytorch_test_loop(test_dataloader, model, loss_fn, writer, epoch)
  print("Training done!")
  
  # Execution Time
  writer.close()
  end = time.perf_counter()
  print(f"\nExecution time = {end - start:.2f} second(s)")
  debug = True
  
apis: torch.utils.data.DataLoader torch.manual_seed pandas.set_option torch.cuda.is_available torch.utils.tensorboard.SummaryWriter torch.nn.BCELoss torch.Tensor