Learn data science and machine learning by building real-world projects on Jovian
In [1]:
project_name = 'generative-modeling-cars-dataset-zerotogansproject'
In [2]:
# Uncomment and install libraries if imports fail
!conda install numpy pandas pytorch torchvision -c pytorch -y
!pip install matplotlib --upgrade --quiet
Collecting package metadata (current_repodata.json): ...working... done Solving environment: ...working... done # All requested packages already installed.
In [3]:
DATA_DIR = '../input/stanford-cars-dataset/cars_train/cars_train/'
In [4]:
import os

print(os.listdir(DATA_DIR))
--------------------------------------------------------------------------- FileNotFoundError Traceback (most recent call last) <ipython-input-4-f41284434047> in <module> 1 import os 2 ----> 3 print(os.listdir(DATA_DIR)) FileNotFoundError: [WinError 3] 系統找不到指定的路徑。: '../input/stanford-cars-dataset/cars_train/cars_train/'
In [5]:
print(os.listdir(DATA_DIR)[:10])
--------------------------------------------------------------------------- FileNotFoundError Traceback (most recent call last) <ipython-input-5-3a2105de0d93> in <module> ----> 1 print(os.listdir(DATA_DIR)[:10]) FileNotFoundError: [WinError 3] 系統找不到指定的路徑。: '../input/stanford-cars-dataset/cars_train/cars_train/'
In [6]:
from torch.utils.data import DataLoader
from torchvision.datasets import ImageFolder
import torchvision.transforms as T
In [7]:
image_size = 32
batch_size = 64
stats = (0.5, 0.5, 0.5), (0.5, 0.5, 0.5)
In [8]:
DATA_DIR = '../input/stanford-cars-dataset/'
In [9]:
train_ds = ImageFolder(DATA_DIR, transform=T.Compose([
    T.Resize(image_size),
    T.CenterCrop(image_size),
    T.ToTensor(),
    T.Normalize(*stats)]))

train_dl = DataLoader(train_ds, batch_size, shuffle=True, num_workers=3, pin_memory=True)


--------------------------------------------------------------------------- FileNotFoundError Traceback (most recent call last) <ipython-input-9-fbf97b6015bf> in <module> ----> 1 train_ds = ImageFolder(DATA_DIR, transform=T.Compose([ 2 T.Resize(image_size), 3 T.CenterCrop(image_size), 4 T.ToTensor(), 5 T.Normalize(*stats)])) C:\ProgramData\Anaconda3\lib\site-packages\torchvision\datasets\folder.py in __init__(self, root, transform, target_transform, loader, is_valid_file) 251 is_valid_file: Optional[Callable[[str], bool]] = None, 252 ): --> 253 super(ImageFolder, self).__init__(root, loader, IMG_EXTENSIONS if is_valid_file is None else None, 254 transform=transform, 255 target_transform=target_transform, C:\ProgramData\Anaconda3\lib\site-packages\torchvision\datasets\folder.py in __init__(self, root, loader, extensions, transform, target_transform, is_valid_file) 124 super(DatasetFolder, self).__init__(root, transform=transform, 125 target_transform=target_transform) --> 126 classes, class_to_idx = self._find_classes(self.root) 127 samples = self.make_dataset(self.root, class_to_idx, extensions, is_valid_file) 128 if len(samples) == 0: C:\ProgramData\Anaconda3\lib\site-packages\torchvision\datasets\folder.py in _find_classes(self, dir) 162 No class is a subdirectory of another. 163 """ --> 164 classes = [d.name for d in os.scandir(dir) if d.is_dir()] 165 classes.sort() 166 class_to_idx = {cls_name: i for i, cls_name in enumerate(classes)} FileNotFoundError: [WinError 3] 系統找不到指定的路徑。: '../input/stanford-cars-dataset/'
In [10]:
import torch
from torchvision.utils import make_grid
import matplotlib.pyplot as plt
%matplotlib inline
In [11]:
def denorm(img_tensors):
    return img_tensors * stats[1][0] + stats[0][0]
In [12]:
def show_images(images, nmax=64):
    fig, ax = plt.subplots(figsize=(8, 8))
    ax.set_xticks([]); ax.set_yticks([])
    ax.imshow(make_grid(denorm(images.detach()[:nmax]), nrow=8).permute(1, 2, 0))

def show_batch(dl, nmax=64):
    for images, _ in dl:
        show_images(images, nmax)
        break
In [13]:
show_batch(train_dl)
--------------------------------------------------------------------------- NameError Traceback (most recent call last) <ipython-input-13-a3a3d7f4dd06> in <module> ----> 1 show_batch(train_dl) NameError: name 'train_dl' is not defined
In [14]:
!pip install jovian --upgrade --quiet
In [15]:
import jovian
In [ ]:
jovian.commit(project=project_name, environment=None)
[jovian] Attempting to save notebook..
In [ ]:
def get_default_device():
    """Pick GPU if available, else CPU"""
    if torch.cuda.is_available():
        return torch.device('cuda')
    else:
        return torch.device('cpu')
    
def to_device(data, device):
    """Move tensor(s) to chosen device"""
    if isinstance(data, (list,tuple)):
        return [to_device(x, device) for x in data]
    return data.to(device, non_blocking=True)

class DeviceDataLoader():
    """Wrap a dataloader to move data to a device"""
    def __init__(self, dl, device):
        self.dl = dl
        self.device = device
        
    def __iter__(self):
        """Yield a batch of data after moving it to device"""
        for b in self.dl: 
            yield to_device(b, self.device)

    def __len__(self):
        """Number of batches"""
        return len(self.dl)
In [ ]:
device = get_default_device()
device
In [ ]:
train_dl = DeviceDataLoader(train_dl, device)
In [ ]:
import torch.nn as nn
In [ ]:
discriminator = nn.Sequential(
    # in: 3 x 32 x 32
    
    nn.Conv2d(3, 32, kernel_size=4, stride=2, padding=1, bias=False),
    nn.BatchNorm2d(32),
    nn.LeakyReLU(0.2, inplace=True),
    # out: 32 x 16 x 16
    
    nn.Conv2d(32, 64, kernel_size=4, stride=2, padding=1, bias=False),
    nn.BatchNorm2d(64),
    nn.LeakyReLU(0.2, inplace=True),
    # out: 64 x 8 x 8
    
    nn.Conv2d(64, 128, kernel_size=4, stride=2, padding=1, bias=False),
    nn.BatchNorm2d(128),
    nn.LeakyReLU(0.2, inplace=True),
    # out: 128 x 4 x 4
    
    nn.Conv2d(128, 256, kernel_size=4, stride=2, padding=1, bias=False),
    nn.BatchNorm2d(256),
    nn.LeakyReLU(0.2, inplace=True),
    # out: 256 x 4 x 4
    
    
    nn.Conv2d(256, 1, kernel_size=4, stride=1, padding=0, bias=False),
    # out: 1 x 1 x 1
    
    nn.Flatten(),
    nn.Sigmoid())
In [ ]:
discriminator = to_device(discriminator, device)
In [ ]:
latent_size = 64
In [ ]:
generator = nn.Sequential(
    # in: latent_size x 1 x 1
    
    nn.ConvTranspose2d(latent_size, 256, kernel_size=4, stride=1, padding=0, bias=False),
    nn.BatchNorm2d(256),
    nn.ReLU(True),
    # out: 256 x 4 x 4
    
    
    nn.ConvTranspose2d(256, 128, kernel_size=4, stride=2, padding=1, bias=False),
    nn.BatchNorm2d(128),
    nn.ReLU(True),
    # out: 128 x 8 x 8
    
    nn.ConvTranspose2d(128, 64, kernel_size=4, stride=2, padding=1, bias=False),
    nn.BatchNorm2d(64),
    nn.ReLU(True),
    # out: 64 x 16 x 16

    
    nn.ConvTranspose2d(64, 3, kernel_size=4, stride=2, padding=1, bias=False),
    nn.Tanh()
    # out: 3 x 32 x 32
) 
In [ ]:
xb = torch.randn(batch_size, latent_size, 1, 1) # random latent tensors
fake_images = generator(xb)
print(fake_images.shape)
show_images(fake_images)
In [ ]:
generator = to_device(generator, device)
In [ ]:
def train_discriminator(real_images, optimizer_d):
    # Clear discriminator gradients
    optimizer_d.zero_grad()
    
    # Pass real images through discriminator
    real_predictions = discriminator(real_images)
    real_targets = torch.ones(real_images.size(0), 1, device=device)
    real_loss = F.binary_cross_entropy(real_predictions, real_targets)
    real_score = torch.mean(real_predictions).item()
    
    # Generate fake images
    latent = torch.randn(batch_size, latent_size, 1, 1, device=device)
    fake_images = generator(latent)
    
    # Pass fake images through discriminator
    fake_targets = torch.zeros(fake_images.size(0), 1, device=device)
    fake_predictions = discriminator(fake_images)
    fake_loss = F.binary_cross_entropy(fake_predictions, fake_targets)
    fake_score = torch.mean(fake_predictions).item()
    
    # Update discriminator weights
    loss = real_loss + fake_loss
    loss.backward()
    optimizer_d.step()
    
    return loss.item(), real_score, fake_score
In [ ]:
def train_generator(optimizer_g):
    # Clear generator gradients
    optimizer_g.zero_grad()
    
    # Generate fake images
    latent = torch.randn(batch_size, latent_size, 1, 1, device=device)
    fake_images = generator(latent)
    
    # Try to fool the discriminator
    predictions = discriminator(fake_images)
    targets = torch.ones(batch_size, 1, device=device)
    loss = F.binary_cross_entropy(predictions, targets)
    
    # Update generator weights
    loss.backward()
    optimizer_g.step()
    
    return loss.item()
In [ ]:
from torchvision.utils import save_image
In [ ]:
images_sampled_dir = 'generated_images'
os.makedirs(images_sampled_dir, exist_ok=True)
In [ ]:
def save_sampled_images(index, latent_tensors, show=True):
    fake_images = generator(latent_tensors)
    fake_fname = 'generated-images-{0:0=4d}.png'.format(index)
    save_image(denorm(fake_images), os.path.join(images_sampled_dir, fake_fname), nrow=8)
    print('Saving', fake_fname)
    if show:
        fig, ax = plt.subplots(figsize=(8, 8))
        ax.set_xticks([]); ax.set_yticks([])
        ax.imshow(make_grid(fake_images.cpu().detach(), nrow=8).permute(1, 2, 0))
In [ ]:
fixed_latent = torch.randn(64, latent_size, 1, 1, device=device)
In [ ]:
 save_sampled_images(0, fixed_latent)
In [ ]:
from tqdm.notebook import tqdm
import torch.nn.functional as F 
In [ ]:
def fit(epochs, lr, start_idx=1):
    torch.cuda.empty_cache()
    
    # Losses & scores
    losses_g = []
    losses_d = []
    real_scores = []
    fake_scores = [] 
    
    optimizer_d = torch.optim.Adam(discriminator.parameters(), lr=lr, betas=(0.5, 0.999))
    optimizer_g = torch.optim.Adam(generator.parameters(), lr=lr, betas=(0.5, 0.999))
    
    #The loop for the training of discriminator and generator
    for epoch in range(epochs):
        for real_images, _ in tqdm(train_dl):
            # Train discriminator
            loss_d, real_score, fake_score = train_discriminator(real_images, optimizer_d)
            # Train generator
            loss_g = train_generator(optimizer_g)
            
    # Record losses & scores
    losses_g.append(loss_g)
    losses_d.append(loss_d)
    real_scores.append(real_score)
    fake_scores.append(fake_score)
    
    
    # Log losses & scores (last batch)
    print("Epoch [{}/{}], loss_g: {:.4f}, loss_d: {:.4f}, real_score: {:.4f}, fake_score: {:.4f}".format(
            epoch+1, epochs, loss_g, loss_d, real_score, fake_score))
        
          
    # Save generated images
    save_sampled_images(epoch+start_idx, fixed_latent, show=False)
    
    return losses_g, losses_d, real_scores, fake_scores 
           
            

  
In [5]:
lr = 0.0005
epochs = 15
In [6]:
!pip install jovian --upgrade --quiet
In [ ]:
import jovian 
jovian.reset()
jovian.log_hyperparams(lr=lr, epochs=epochs)
[jovian] Please enter your API key ( from https://jovian.ai/ ): API KEY:
In [ ]:
history = fit(epochs, lr)
In [ ]:
losses_g, losses_d, real_scores, fake_scores = history
In [ ]:
jovian.log_metrics(loss_g=losses_g[-1], 
                   loss_d=losses_d[-1], 
                   real_score=real_scores[-1], 
                   fake_score=fake_scores[-1])
In [ ]:
# Save the model checkpoints 
torch.save(generator.state_dict(), 'G.ckpt')
torch.save(discriminator.state_dict(), 'D.ckpt')
In [ ]:
from IPython.display import Image
In [ ]:
Image('./generated/generated-images-0001.png')
In [ ]:
import cv2
import os

vid_fname = 'gans_training.avi'
In [ ]:
files = [os.path.join(images_sampled_dir, f) for f in os.listdir(images_sampled_dir) if 'generated' in f]
files.sort()

out = cv2.VideoWriter(vid_fname,cv2.VideoWriter_fourcc(*'MP4V'), 1, (530,530))
[out.write(cv2.imread(fname)) for fname in files]
out.release()
In [ ]:
plt.plot(losses_d, '-')
plt.plot(losses_g, '-')
plt.xlabel('epoch')
plt.ylabel('loss')
plt.legend(['Discriminator', 'Generator'])
plt.title('Losses');
In [ ]:
plt.plot(real_scores, '-')
plt.plot(fake_scores, '-')
plt.xlabel('epoch')
plt.ylabel('score')
plt.legend(['Real', 'Fake'])
plt.title('Scores');
In [ ]:
import jovian
In [ ]:
jovian.commit(project=project_name, 
              outputs=['G.ckpt', 'D.ckpt', 'gans_training.avi'], 
              environment=None) 
In [ ]:
jovian.commit(project=project_name, environment=None)