Learn practical skills, build real-world projects, and advance your career
Created 4 years ago
Fruits-360: A dataset of images containing fruits and vegetables
- Total number of images: 90483.
- Training set size: 67692 images (one fruit or vegetable per image).
- Test set size: 22688 images (one fruit or vegetable per image).
- Multi-fruits set size: 103 images (more than one fruit (or fruit class) per image)
- Number of classes: 131 (fruits and vegetables).
- Image size: 100x100 pixels.
import os
import torch
import matplotlib.pyplot as plt
from torch import nn
from torch import optim
from os.path import join
from torchvision import models
from tqdm.notebook import tqdm
from torchvision import transforms
from torch.nn import functional as F
from torchvision.datasets import ImageFolder
from torch.utils.data import Subset, DataLoader, random_split
plt.rcParams["figure.figsize"] = (12, 12)
# Clone dataset from GitHub
!git clone https://github.com/Horea94/Fruit-Images-Dataset
Cloning into 'Fruit-Images-Dataset'...
remote: Enumerating objects: 8684, done.
remote: Counting objects: 100% (8684/8684), done.
remote: Compressing objects: 100% (8665/8665), done.
remote: Total 385849 (delta 31), reused 8664 (delta 19), pack-reused 377165
Receiving objects: 100% (385849/385849), 2.10 GiB | 32.12 MiB/s, done.
Resolving deltas: 100% (1191/1191), done.
Checking out files: 100% (90502/90502), done.
# Helper functions
def load_dataset(path):
data = load_files(path)
files = np.array(data['filenames'])
targets = np.array(data['target'])
target_labels = np.array(data['target_names'])
return files,targets,target_labels
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)
def predict_and_plot(images, labels, plot_size=[1, 6]):
prediction_limit = plot_size[0] * plot_size[1]
output = []
for idx, (image, label) in enumerate(zip(images, labels)):
if idx >= prediction_limit:
break
xb = image.unsqueeze(0)
xb = to_device(xb, device)
yb = model(xb)
_, preds = torch.max(yb, dim=1)
prediction = preds[0].item()
output.append(prediction)
result = torch.IntTensor(output)
plot_img(images, labels, result)
def plot_img(images, labels, predictions=None, plot_size=[1, 6]):
fig, axes = plt.subplots(*plot_size)
fig.tight_layout()
for idx, ax in enumerate(axes):
if predictions != None:
predictions_text = f'\nPrediction:\n{predictions[idx].item()}: {train_dataset.classes[predictions[idx]]}'
ax.set_xlabel(predictions_text)
ax.xaxis.label.set_color('green' if predictions[idx].item() == labels[idx].item() else 'red')
ax.title.set_text(f'{labels[idx].item()}: {train_dataset.classes[labels[idx]]}')
ax.imshow(images[idx].cpu().permute(1, 2, 0))
@torch.no_grad()
def predict_dl(dl, model):
torch.cuda.empty_cache()
batch_probs = []
for xb, targets in tqdm(dl):
probs = model(xb)
_, preds = torch.max(probs, dim=1)
correct = torch.sum(preds == targets).item()
total = targets.size(0)
batch_probs.append((correct, total))
correct = sum(list(zip(*batch_probs))[0])
total = sum(list(zip(*batch_probs))[1])
return correct / total
def plot_chart(title, data_labels, data, x_label, y_label):
fig = plt.figure(figsize=(20,10))
plt.title(title)
for data_label, data_list in zip(data_labels, data):
plt.plot(data_list, label=data_label)
plt.xlabel(x_label, fontsize=12)
plt.ylabel(y_label, fontsize=12)
plt.legend(loc='best')
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)
# Model class
class FruitModel(nn.Module):
def __init__(self):
super().__init__()
# Use a pretrained model
self.network = models.resnet18(pretrained=True)
# Replace last layer
num_ftrs = self.network.fc.in_features
self.network.fc = nn.Linear(num_ftrs, len(train_dataset.classes))
def forward(self, xb):
return torch.sigmoid(self.network(xb))
def training_step(self, batch):
images, targets = batch
out = self(images)
_, preds = torch.max(out, dim=1)
correct = torch.sum(preds == targets).item()
total = targets.size(0)
loss = F.cross_entropy(out, targets)
return loss, correct, total
def validation_step(self, batch):
images, targets = batch
out = self(images) # Generate predictions
_, preds = torch.max(out, dim=1)
correct = torch.sum(preds == targets).item()
total = targets.size(0)
loss = F.cross_entropy(out, targets) # Calculate loss
return {'val_loss': loss.detach(), 'val_correct': correct, 'val_total': total}
def validation_epoch_end(self, outputs):
batch_losses = [x['val_loss'] for x in outputs]
epoch_loss = torch.stack(batch_losses).mean() # Combine losses
correct = sum([x['val_correct'] for x in outputs])
total = sum([x['val_total'] for x in outputs])
return {'val_loss': epoch_loss.item(), 'val_correct': correct, 'val_total': total}
def epoch_end(self, epoch, result):
print("Epoch [{}], train_loss: {:.4f}, val_loss: {:.4f}, train_acc: {:.2%}, val_acc: {:.2%}".format(
epoch, result['train_loss'], result['val_loss'], result['train_correct'] / result['train_total'], result['val_correct'] / result['val_total']))