In [ ]:
%pylab inline
import torch
import sys
sys.path.append('..')
sys.path.append('../..')
from data import load
device = torch.device('cuda') if torch.cuda.is_available() else torch.device('cpu')
print('device = ', device)
In [ ]:
class ConvNet(torch.nn.Module):
    class Block(torch.nn.Module):
        def __init__(self, n_input, n_output, stride=1):
            super().__init__()
            self.net = torch.nn.Sequential(
              torch.nn.Conv2d(n_input, n_output, kernel_size=3, padding=1, stride=stride, bias=False),
              torch.nn.BatchNorm2d(n_output),
              torch.nn.ReLU(),
              torch.nn.Conv2d(n_output, n_output, kernel_size=3, padding=1, bias=False),
              torch.nn.BatchNorm2d(n_output),
              torch.nn.ReLU()
            )
            self.downsample = None
            if stride != 1 or n_input != n_output:
                self.downsample = torch.nn.Sequential(torch.nn.Conv2d(n_input, n_output, 1, stride=stride),
                                                      torch.nn.BatchNorm2d(n_output))
        
        def forward(self, x):
            identity = x
            if self.downsample is not None:
                identity = self.downsample(x)
            return self.net(x) + identity
        
    def __init__(self, layers=[32,64,128], n_input_channels=3):
        super().__init__()
        L = [torch.nn.Conv2d(n_input_channels, 32, kernel_size=7, padding=3, stride=2, bias=False),
             torch.nn.BatchNorm2d(32),
             torch.nn.ReLU(),
             torch.nn.MaxPool2d(kernel_size=3, stride=2, padding=1)
            ]
        c = 32
        for l in layers:
            L.append(self.Block(c, l, stride=2))
            c = l
        self.network = torch.nn.Sequential(*L)
        self.classifier = torch.nn.Linear(c, 1)
    
    def forward(self, x):
        # Compute the features
        z = self.network(x)
        # Global average pooling
        z = z.mean(dim=[2,3])
        # Classify
        return self.classifier(z)[:,0]
    
model = ConvNet()
model.train()
print( model.training )
model.eval()
print( model.training )
In [ ]:
n_epochs = 100

train_data = load.get_dogs_and_cats(resize=(128,128), batch_size=32)
optimizer = torch.optim.Adam(model.parameters())
scheduler = torch.optim.lr_scheduler.StepLR(optimizer, step_size=30, gamma=0.1)
scheduler = torch.optim.lr_scheduler.MultiStepLR(optimizer, [50,75], gamma=0.1)
scheduler = torch.optim.lr_scheduler.ReduceLROnPlateau(optimizer, 'max')

# Transfer the data to a GPU (optional)
model = model.to(device)

# Construct the loss and accuracy functions
loss = torch.nn.BCEWithLogitsLoss()
accuracy = lambda o, l: ((o > 0).long() == l.long()).float()

# Train the network
for epoch in range(n_epochs):
    model.train()
    accuracies = []
    for it, (data, label) in enumerate(train_data):
        # Transfer the data to a GPU (optional)
        data, label = data.to(device), label.to(device)

        # Produce the output
        o = model(data)

        # Compute the loss and accuracy
        loss_val = loss(o, label.float())
        accuracies.extend(accuracy(o, label).detach().cpu().numpy())

        # Take a gradient step
        optimizer.zero_grad()
        loss_val.backward()
        optimizer.step()
        break
#     scheduler.step()
    scheduler.step(np.mean(accuracies))
    print( 'epoch = ', epoch, 'optimizer_lr', optimizer.param_groups[0]['lr'], 'accuracy', np.mean(accuracies))
In [ ]: