Sorry for such a long post. I tried to provide all the relevant information. This is my first attempt at neural networks so I’ve done a few things the long way to allow for some debugging. It mostly works so I haven’t gone through and done any cleanup.
I’m using four 7 channel images in an attempt to classify wetlands from upland and water. Training, validation, and testing is showing very promising results with accuracy around 90% in all classes. When I save the model, load it, and classify one of the training images I’m getting around 60% accuracy.
I break up the original files into 128x128 images and do some data augmentation which includes flipping, rotating, and transposing. The paths are all saved in a csv file. There are around 35k tiny images.
I’m using:
python 3.6.6
pytorch 0.4.1
UNet model
CrossEntropyLoss
Adam optimizer
Setting up the environment to get reproducible results
np.random.seed(0)
torch.manual_seed(0)
torch.cuda.manual_seed_all(0)
torch.backends.cudnn.deterministic = True
torch.backends.cudnn.benchmark = False
model = unet.UNet(num_classes, in_channels=in_channels+extra_indices, depth=6, start_filts=64)
if torch.cuda.is_available:
model.cuda()
I get population mean and stdev as well as class weights based off of a shuffled sample of my training data. I have some areas of nodata which I ignore.
dataset_train = data_utils.SatIn(data_path, csvfile, 'train', transform=transforms.Compose([aug.RemoveZeros(), aug.RemoveNoData(), aug.WetlandOnly(), aug.SpecificBands(usebands), aug.NDVI(bands=[1,2])]))
train_dataloader = DataLoader(dataset_train, batch_size=2000, num_workers=1, shuffle=True)
if normalize or weighting:
for i, data in enumerate(train_dataloader):
if i == 0:
if normalize:
numpy_image = data['sat_img'].numpy()
popmean = torch.from_numpy(np.mean(np.multiply(numpy_image, (numpy_image < -3.5028235e+4) | (numpy_image > -3.3028235e+4)), axis=(0,2,3))).float()
popstd = torch.from_numpy(np.std(np.multiply(numpy_image, (numpy_image < -3.5028235e+4) | (numpy_image > -3.3028235e+4)), axis=(0,2,3))).float()
if weighting:
unique, counts = np.unique(data['map_img'].numpy(), return_counts=True)
mapunique = unique
mapcount = counts
print('Unique:', unique)
print('Counts:', counts)
if weighting:
weights = 1 - (counts/sum(counts))
break
Setup loss and optimizer
#criterion
if weighting:
criterion = nn.CrossEntropyLoss(weight = weight)
else:
criterion = nn.CrossEntropyLoss()
# optimizer
optimizer = optim.Adam(model.parameters(), lr=learning_rate)
# decay LR
lr_scheduler = optim.lr_scheduler.StepLR(optimizer, step_size=5, gamma=0.1)
Load data
dataset_train = data_utils.SatIn(data_path, csvfile, 'train', transform=transforms.Compose([aug.RemoveZeros(), aug.RemoveNoData(), aug.WetlandOnly(), aug.SpecificBands(usebands), aug.NDVI(bands=[1,2]), aug.ToTensorTarget(), aug.NormalizeTarget(mean=popmean, std=popstd)]))
dataset_val = data_utils.SatIn(data_path, csvfile, 'valid', transform=transforms.Compose([aug.RemoveZeros(), aug.RemoveNoData(), aug.WetlandOnly(), aug.SpecificBands(usebands), aug.NDVI(bands=[1,2]), aug.ToTensorTarget(), aug.NormalizeTarget(mean=popmean, std=popstd)]))
dataset_test = data_utils.SatIn(data_path, csvfile, 'test', transform=transforms.Compose([aug.RemoveZeros(), aug.RemoveNoData(), aug.WetlandOnly(), aug.SpecificBands(usebands), aug.NDVI(bands=[1,2]), aug.ToTensorTarget(), aug.NormalizeTarget(mean=popmean, std=popstd)]))
train_dataloader = DataLoader(dataset_train, batch_size=batch_size, num_workers=4, shuffle=True)
val_dataloader = DataLoader(dataset_val, batch_size=3, num_workers=4, shuffle=False)
test_dataloader = DataLoader(dataset_test, batch_size=3, num_workers=4, shuffle=False)
Training, validation, and testing have very similar methods. I call model.eval() and don’t do any of the optimizer of backwards propogation in validation and testing.
def train(train_loader, model, criterion, optimizer, scheduler, epoch_num):
model.train()
correct = 0
totalcount = 0
scheduler.step()
# iterate over data
for idx, data in enumerate(tqdm(train_loader, desc="training")):
# get the inputs and wrap in Variable
if torch.cuda.is_available():
inputs = Variable(data['sat_img'].cuda())
labels = Variable(data['map_img'].cuda())
else:
inputs = Variable(data['sat_img'])
labels = Variable(data['map_img'])
optimizer.zero_grad()
outputs = model(inputs)
loss = criterion(outputs, labels.long())
loss.backward()
optimizer.step()
_, predicted = torch.max(outputs.data, 1)
test = predicted == labels.long()
correct += test.sum().item()
totalcount += test.size()[0] * test.size()[1] * test.size()[2]
print('Training Loss: {:.4f}, Accuracy: {:.4f}'.format(loss.data[0], correct/totalcount))
return {'train_loss': loss.data[0], 'train_acc' : correct/totalcount}
def validation(valid_loader, model, criterion, epoch_num):
correct = 0
totalcount = 0
model.eval()
# Iterate over data.
for idx, data in enumerate(tqdm(valid_loader, desc='validation')):
# get the inputs and wrap in Variable
if torch.cuda.is_available():
inputs = Variable(data['sat_img'].cuda(), volatile=True)
labels = Variable(data['map_img'].cuda(), volatile=True)
else:
inputs = Variable(data['sat_img'], volatile=True)
labels = Variable(data['map_img'], volatile=True)
# forward
outputs = model(inputs)
loss = criterion(outputs, labels.long())
_, predicted = torch.max(outputs.data, 1)
test = predicted == labels.long()
correct += test.sum().item()
totalcount += test.size()[0] * test.size()[1] * test.size()[2]
print('Validation Loss: {:.4f} Acc: {:.4f}'.format(loss.data[0], correct/totalcount))
return {'valid_loss': loss.data[0], 'valid_acc': correct/totalcount}
Epoch loop
for epoch in range(start_epoch, num_epochs):
lr_scheduler.step()
training= train(train_dataloader, model, criterion, optimizer, lr_scheduler, epoch)
vallidating = validation(val_dataloader, model, criterion, epoch)
Save model
torch.save(model.state_dict(),os.path.join(rootdir,'3Image_8bands.pth'))
At this point everything looks great. Now to load the model
#Set seeds
np.random.seed(0)
torch.manual_seed(0)
torch.cuda.manual_seed_all(0)
torch.backends.cudnn.deterministic = True
torch.backends.cudnn.benchmark = False
torch.backends.cudnn.enabled = False
# get model
model = unet.UNet(num_classes, in_channels=in_channels+extra_indices, depth=6, start_filts=64)
if torch.cuda.is_available():
model.cuda()
model.load_state_dict(torch.load(os.path.join(rootdir, modelName)))
model.eval()
I breakup the image to be classified in the same way I did for training. The original image is broken down into 128x128 chunks and each of those chunks run through the classifier.
collectpredictions = []
dataset_test = classify.ClassOut(data_path, csvfilename, 'test', transform=transforms.Compose([aug.RemoveZeros(), aug.RemoveNoData(), aug.WetlandOnly(), aug.SpecificBands(usebands), aug.NDVI(bands=[1,2]), aug.ToTensorTarget(), aug.NormalizeTarget(mean=popmean, std=popstd)]))
test_dataloader = DataLoader(dataset_test, batch_size=1, num_workers=3, shuffle=False)
for idx, data in enumerate(test_dataloader):
# get the inputs and wrap in Variable
if torch.cuda.is_available():
inputs = Variable(data['sat_img'].cuda(), volatile=True)
else:
inputs = Variable(data['sat_img'], volatile=True)
outputs = model(inputs)
_, predicted = torch.max(outputs.data, 1)
collectpredictions.append(predicted[0].cpu().numpy())
Where might I be going wrong?
Thanks!