To demonstrate how neural networks can learn effective encodings of input data.
I used PyTorch to build an autoencoder that learned compact representations of Fashion-MNIST images. The model was trained to compress and reconstruct grayscale images of clothing.
Architecture Overview :
Encoder: Two fully connected layers reducing the dimensionality from 784 to 32.
Decoder: Two fully connected layers reconstructing the images back to 784 dimensions.
Loss Function: Mean Squared Error (MSE) for reconstruction accuracy.
Optimizer: Adam with a learning rate of 0.0001.
Dataset :Fashion-MNIST dataset (28×28 grayscale images of 8 clothing categories)
Training Details :
Number of epochs (10)
Batch size (64)
Optimizer (Adam)
Loss function (MSE)
CODE:
%cd /content/drive/MyDrive/Autoencoders
import os
import torch
import torchvision
import torch.nn as nn
import torchvision.transforms as transforms
import torch.optim as optim
import matplotlib.pyplot as plt
import torch.nn.functional as F
from torchvision import datasets
from torch.utils.data import DataLoader
from torchvision.utils import save_image
from tqdm.notebook import tqdm
from google.colab import drive
drive.mount('/content/drive')
#learning parameters
#learning parameters
epochs = 10
batch_size = 64
lr = 0.0001
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
#image transformations
transform = transforms.Compose([
transforms.ToTensor(),
])
#learning parameters
epochs = 10
batch_size = 64
lr = 0.0001
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
#image transformations
transform = transforms.Compose([
transforms.ToTensor(),
])
train_data = datasets.FashionMNIST(
root='./data',
train=True,
download=True,
transform=transform
)
val_data = datasets.FashionMNIST(
root='./data',
train=False,
download=True,
transform=transform
)
train_loader = DataLoader(
train_data,
batch_size=batch_size,
)
val_loader = DataLoader(
val_data,
batch_size=batch_size
)
100%|██████████| 26.4M/26.4M [00:01<00:00, 13.3MB/s]
100%|██████████| 29.5k/29.5k [00:00<00:00, 212kB/s]
100%|██████████| 4.42M/4.42M [00:01<00:00, 3.88MB/s]
100%|██████████| 5.15k/5.15k [00:00<00:00, 12.2MB/s]
class Autoencoder(nn.Module):
def __init__(self): # Fixed: __init__ instead of _init_
super(Autoencoder, self).__init__() # Fixed: __init__ instead of _init_
# encoder
self.enc1 = nn.Linear(in_features=784, out_features=512) # Fixed: = instead of nn and out_features=512
self.enc2 = nn.Linear(in_features=512, out_features=32) # Fixed: = instead of nn and out_features=32
# decoder
self.dec1 = nn.Linear(in_features=32, out_features=512)
self.dec2 = nn.Linear(in_features=512, out_features=784)
def forward(self, x):
# encoding
x = F.relu(self.enc1(x))
x = F.relu(self.enc2(x)) # Fixed: = instead of nn
# decoding
x = F.relu(self.dec1(x)) # Fixed: = instead of nn
x = torch.sigmoid(self.dec2(x))
return x
model = Autoencoder().to(device)
print(model)
Autoencoder(
(enc1): Linear(in_features=784, out_features=512, bias=True)
(enc2): Linear(in_features=512, out_features=32, bias=True)
(dec1): Linear(in_features=32, out_features=512, bias=True)
(dec2): Linear(in_features=512, out_features=784, bias=True)
)
criterion = nn.MSELoss()
optimizer = optim.Adam(model.parameters(), lr=lr)
def fit(model, dataloader):
model.train()
running_loss = 0.0
for i, data in tqdm(enumerate(dataloader), total=int(len(train_data)/dataloader.batch_size)):
data, _ = data # Fixed: Unpacking the data and target
data = data.to(device) # Fixed: Assigning the result to data
data = data.view(data.size(0), -1) # Fixed: Assigning the result to data
optimizer.zero_grad()
reconstruction = model(data) # Fixed: Assigning the result to reconstruction
loss = criterion(reconstruction, data) # Fixed: Calling the criterion function
loss.backward()
running_loss += loss.item()
optimizer.step()
train_loss = running_loss/len(dataloader.dataset) # Fixed: Assigning the result to train_loss
return train_loss
def validate(model, dataloader):
model.eval()
running_loss = 0.0
with torch.no_grad():
for i, data in tqdm(enumerate(dataloader), total=int(len(val_data)/dataloader.batch_size)):
data, _ = data # Fixed: Unpacking the data and target
data = data.to(device) # Fixed: Assigning the result to data
data = data.view(data.size(0), -1) # Fixed: Assigning the result to data
reconstruction = model(data)
loss = criterion(reconstruction, data)
running_loss += loss.item()
#save the last batch input and output of every epoch
if i == int(len(val_data)/dataloader.batch_size) - 1:
num_rows = 8
both = torch.cat((data.view(batch_size, 1, 28, 28)[:8],
reconstruction.view(batch_size, 1, 28, 28)[:8]))
save_image(both.cpu(), f"output{epoch}.png", nrow=num_rows) # Fixed: f-string and nrow
output = plt.imread(f"output{epoch}.png") # Fixed: f-string
plt.imshow(output)
plt.show()
val_loss = running_loss/len(dataloader.dataset) # Fixed: Assigning the result to val_loss
return val_loss
train_loss = []
val_loss = []
for epoch in range(epochs):
print(f"Epoch {(epoch+1)} of {epochs}") # Fixed: Added indentation and f-string formatting
train_epoch_loss = fit(model, train_loader)
val_epoch_loss = validate(model, val_loader) # Fixed: Added indentation and assignment operator
train_loss.append(train_epoch_loss)
val_loss.append(val_epoch_loss)
print(f"Train Loss: {train_epoch_loss:.6f}") # Fixed: Added indentation and f-string formatting
print(f"Val Loss: {val_epoch_loss:.6f}") # Fixed: Added indentation and f-string formatting
Result :
Epoch 1 of 10
938/? [00:10<00:00, 124.66it/s]
157/? [00:01<00:00, 153.09it/s]
Train Loss: 0.000713
Val Loss: 0.000399
Epoch 2 of 10
938/? [00:08<00:00, 129.43it/s]
157/? [00:01<00:00, 159.32it/s]
Train Loss: 0.000355
Val Loss: 0.000325
Epoch 3 of 10
938/? [00:07<00:00, 97.80it/s]
157/? [00:01<00:00, 90.92it/s]
Train Loss: 0.000303
Val Loss: 0.000287
Epoch 4 of 10
938/? [00:07<00:00, 130.18it/s]
157/? [00:01<00:00, 157.18it/s]
Train Loss: 0.000271
Val Loss: 0.000261
Epoch 5 of 10
938/? [00:08<00:00, 121.42it/s]
157/? [00:01<00:00, 123.47it/s]
Train Loss: 0.000249
Val Loss: 0.000242
Epoch 6 of 10
938/? [00:08<00:00, 85.55it/s]
157/? [00:01<00:00, 141.80it/s]
Train Loss: 0.000233
Val Loss: 0.000229
Epoch 7 of 10
938/? [00:07<00:00, 123.00it/s]
157/? [00:01<00:00, 147.64it/s]
Train Loss: 0.000221
Val Loss: 0.000219
Epoch 8 of 10
938/? [00:08<00:00, 122.96it/s]
157/? [00:01<00:00, 149.03it/s]
Train Loss: 0.000212
Val Loss: 0.000211
Epoch 9 of 10
938/? [00:09<00:00, 123.92it/s]
157/? [00:01<00:00, 148.72it/s]
Train Loss: 0.000206
Val Loss: 0.000205
Epoch 10 of 10
938/? [00:08<00:00, 91.59it/s]
157/? [00:01<00:00, 111.21it/s]
Train Loss: 0.000200
Val Loss: 0.000200
The top row displays original Fashion-MNIST images.
The bottom row shows their reconstructed versions generated by the autoencoder after training.
The reconstructions capture the general shape and features, indicating successful dimensionality reduction and learning of key visual patterns.
Collab Notebook link: