Deep Learning Laboratory Manual – With Code Skeletons
This manual contains objectives, steps, and Python/PyTorch starter code for each lab.
Experiment 1: Word Embeddings (Neural Word2Vec)
Objective: Design and implement a neural network to generate word embeddings for a document corpus.
Steps:
1. Collect & clean corpus; tokenize and build vocabulary.
2. Create training pairs for Skip-gram (center→context) or CBOW (context→center).
3. Define embedding and output layers; train with negative sampling.
4. Evaluate using nearest neighbors; visualize with PCA/t-SNE.
Starter Code (skeleton):
# Word2Vec (Skip-gram with Negative Sampling) – PyTorch Skeleton
import math, random, collections
import torch, torch.nn as nn
from torch.utils.data import Dataset, DataLoader
# 1) Toy corpus and preprocessing (replace with your corpus)
corpus = "we love deep learning and we love neural networks".split()
vocab = sorted(set(corpus))
stoi = {w:i for i,w in enumerate(vocab)}
itos = {i:w for w,i in stoi.items()}
# 2) Build training pairs for Skip-gram
window = 2
pairs = []
for i, w in enumerate(corpus):
center = stoi[w]
for j in range(max(0, i-window), min(len(corpus), i+window+1)):
if i == j:
continue
context = stoi[corpus[j]]
pairs.append((center, context))
# Negative sampling table (uniform for skeleton)
unigram = [1]*len(vocab)
class SkipGramNegDataset(Dataset):
def __init__(self, pairs, num_neg=5):
self.pairs = pairs; self.num_neg = num_neg
def __len__(self): return len(self.pairs)
def __getitem__(self, idx):
c, ctx = self.pairs[idx]
negs = []
while len(negs) < self.num_neg:
n = random.randrange(len(vocab))
if n != ctx:
negs.append(n)
return torch.tensor(c), torch.tensor(ctx), torch.tensor(negs)
ds = SkipGramNegDataset(pairs, num_neg=5)
dl = DataLoader(ds, batch_size=32, shuffle=True)
# 3) Model
embed_dim = 50
class SGNS(nn.Module):
def __init__(self, vocab_size, d):
super().__init__()
self.in_embed = nn.Embedding(vocab_size, d)
self.out_embed = nn.Embedding(vocab_size, d)
def forward(self, center, pos, neg):
v = self.in_embed(center) # [B, d]
u_pos = self.out_embed(pos) # [B, d]
u_neg = self.out_embed(neg) # [B, K, d]
pos_score = torch.sum(v*u_pos, dim=1) # [B]
neg_score = torch.bmm(u_neg, v.unsqueeze(2)).squeeze() # [B, K]
loss = -torch.log(torch.sigmoid(pos_score) + 1e-9).mean() \
-torch.log(torch.sigmoid(-neg_score) + 1e-9).mean()
return loss
def get_embeddings(self):
return self.in_embed.weight.data
device = 'cuda' if torch.cuda.is_available() else 'cpu'
model = SGNS(len(vocab), embed_dim).to(device)
opt = torch.optim.Adam(model.parameters(), lr=1e-3)
# 4) Train (toy loop)
for epoch in range(5):
for center, pos, neg in dl:
center, pos, neg = center.to(device), pos.to(device), neg.to(device)
loss = model(center, pos, neg)
opt.zero_grad(); loss.backward(); opt.step()
print(f"Epoch {epoch+1}, loss={loss.item():.4f}")
emb = model.get_embeddings()
print("Embedding for 'we':", emb[stoi['we']][:5])
Experiment 2: Deep Neural Network for Classification (Tabular)
Objective: Build a feedforward DNN classifier for a tabular dataset.
Steps:
1. Load dataset (e.g., UCI, Kaggle). Split into train/val/test.
2. Standardize numeric features; encode categorical features.
3. Define DNN with ReLU and dropout; train with cross-entropy.
4. Report accuracy, precision/recall/F1; show confusion matrix.
Starter Code (skeleton):
# DNN Classifier – PyTorch Skeleton (synthetic data for structure)
import torch, torch.nn as nn
from sklearn.datasets import make_classification
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import classification_report, confusion_matrix
import numpy as np
X, y = make_classification(n_samples=2000, n_features=20, n_informative=10, n_classes=3, random_
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
scaler = StandardScaler().fit(X_train)
X_train = scaler.transform(X_train).astype(np.float32)
X_test = scaler.transform(X_test).astype(np.float32)
X_train, y_train = torch.tensor(X_train), torch.tensor(y_train, dtype=torch.long)
X_test, y_test = torch.tensor(X_test), torch.tensor(y_test, dtype=torch.long)
class MLP(nn.Module):
def __init__(self, in_dim, hidden=[128,64], out_dim=3):
super().__init__()
layers = []
dims = [in_dim]+hidden
for a,b in zip(dims[:-1], dims[1:]):
layers += [nn.Linear(a,b), nn.ReLU(), nn.Dropout(0.2)]
layers += [nn.Linear(hidden[-1], out_dim)]
self.net = nn.Sequential(*layers)
def forward(self, x): return self.net(x)
device = 'cuda' if torch.cuda.is_available() else 'cpu'
model = MLP(20).to(device)
opt = torch.optim.Adam(model.parameters(), lr=1e-3)
crit = nn.CrossEntropyLoss()
for epoch in range(20):
model.train()
opt.zero_grad()
loss = crit(model(X_train.to(device)), y_train.to(device))
loss.backward(); opt.step()
if (epoch+1)%5==0:
print("epoch", epoch+1, "loss", float(loss))
model.eval()
with torch.no_grad():
logits = model(X_test.to(device)).cpu().numpy()
y_pred = logits.argmax(1)
print(classification_report(y_test, y_pred))
print("Confusion:\n", confusion_matrix(y_test, y_pred))
Experiment 3: CNN for Image Classification
Objective: Design and implement a CNN for an image dataset (e.g., CIFAR-10).
Steps:
1. Load dataset with torchvision; apply normalization and augmentation.
2. Build CNN (Conv-BN-ReLU-Pool × N; FC → Softmax).
3. Train with Adam; use early stopping; track accuracy.
4. Evaluate; visualize sample predictions.
Starter Code (skeleton):
# CNN for CIFAR-10 – PyTorch Skeleton
import torch, torch.nn as nn, torch.nn.functional as F
from torch.utils.data import DataLoader
import torchvision
import torchvision.transforms as T
device = 'cuda' if torch.cuda.is_available() else 'cpu'
transform = T.Compose([
T.RandomHorizontalFlip(),
T.RandomCrop(32, padding=4),
T.ToTensor(),
T.Normalize((0.4914, 0.4822, 0.4465), (0.2023, 0.1994, 0.2010))
])
train_set = torchvision.datasets.CIFAR10(root='./data', train=True, download=True, transform=tra
test_set = torchvision.datasets.CIFAR10(root='./data', train=False, download=True,
transform=T.Compose([T.ToTensor(),
T.Normalize((0.4914,0.4822,0.4465),(0.2023,0.1994,0.20
train_loader = DataLoader(train_set, batch_size=128, shuffle=True, num_workers=2)
test_loader = DataLoader(test_set, batch_size=256, shuffle=False, num_workers=2)
class SimpleCNN(nn.Module):
def __init__(self, num_classes=10):
super().__init__()
self.features = nn.Sequential(
nn.Conv2d(3, 32, 3, padding=1), nn.BatchNorm2d(32), nn.ReLU(),
nn.Conv2d(32, 32, 3, padding=1), nn.BatchNorm2d(32), nn.ReLU(),
nn.MaxPool2d(2),
nn.Conv2d(32, 64, 3, padding=1), nn.BatchNorm2d(64), nn.ReLU(),
nn.Conv2d(64, 64, 3, padding=1), nn.BatchNorm2d(64), nn.ReLU(),
nn.MaxPool2d(2),
)
self.classifier = nn.Sequential(
nn.Flatten(),
nn.Linear(64*8*8, 256), nn.ReLU(), nn.Dropout(0.5),
nn.Linear(256, num_classes)
)
def forward(self, x): return self.classifier(self.features(x))
model = SimpleCNN().to(device)
opt = torch.optim.Adam(model.parameters(), lr=1e-3)
crit = nn.CrossEntropyLoss()
for epoch in range(5):
model.train()
for x,y in train_loader:
x,y = x.to(device), y.to(device)
opt.zero_grad(); loss = crit(model(x), y); loss.backward(); opt.step()
print("epoch", epoch+1, "loss", float(loss))
# Eval
model.eval(); correct=total=0
with torch.no_grad():
for x,y in test_loader:
x,y = x.to(device), y.to(device)
pred = model(x).argmax(1)
correct += (pred==y).sum().item(); total += y.numel()
print("Test Acc:", correct/total)
Experiment 4: Autoencoder for Image Compression
Objective: Build an autoencoder and demonstrate compression on an image dataset (MNIST).
Steps:
1. Load grayscale images (MNIST).
2. Define encoder → bottleneck → decoder (Conv or Dense).
3. Train with MSE or BCE loss; visualize reconstructions.
4. Explore latent dim effect on quality.
Starter Code (skeleton):
# Convolutional Autoencoder – PyTorch Skeleton (MNIST)
import torch, torch.nn as nn
import torchvision
import torchvision.transforms as T
from torch.utils.data import DataLoader
device = 'cuda' if torch.cuda.is_available() else 'cpu'
train_set = torchvision.datasets.MNIST(root='./data', train=True, download=True, transform=T.ToT
test_set = torchvision.datasets.MNIST(root='./data', train=False, download=True, transform=T.To
train_loader = DataLoader(train_set, batch_size=128, shuffle=True)
test_loader = DataLoader(test_set, batch_size=128)
class ConvAE(nn.Module):
def __init__(self, latent=16):
super().__init__()
self.enc = nn.Sequential(
nn.Conv2d(1, 16, 3, stride=2, padding=1), nn.ReLU(),
nn.Conv2d(16, 32, 3, stride=2, padding=1), nn.ReLU(),
)
self.dec = nn.Sequential(
nn.ConvTranspose2d(32, 16, 4, stride=2, padding=1), nn.ReLU(),
nn.ConvTranspose2d(16, 1, 4, stride=2, padding=1), nn.Sigmoid()
)
def forward(self, x): return self.dec(self.enc(x))
model = ConvAE().to(device)
opt = torch.optim.Adam(model.parameters(), lr=1e-3)
crit = nn.MSELoss()
for epoch in range(5):
model.train()
for x,_ in train_loader:
x = x.to(device)
opt.zero_grad(); out = model(x)
loss = crit(out, x); loss.backward(); opt.step()
print("epoch", epoch+1, "loss", float(loss))
# After training, visualize a few reconstructions (in notebook use matplotlib).
Experiment 5: Text Document Classification (LSTM)
Objective: Design and implement an LSTM classifier for textual documents.
Steps:
1. Load dataset (e.g., 20 Newsgroups/AG News).
2. Tokenize and pad sequences; build embeddings (pretrained optional).
3. Define LSTM/GRU classifier; train with cross-entropy.
4. Report accuracy/F1; show sample predictions.
Starter Code (skeleton):
# LSTM Text Classifier – PyTorch Skeleton (toy tokenization)
import torch, torch.nn as nn
from torch.utils.data import Dataset, DataLoader
texts = ["good movie", "bad plot", "awesome film", "terrible acting"]
labels = [1,0,1,0]
vocab = {"<pad>":0}
for t in texts:
for w in t.split():
if w not in vocab: vocab[w] = len(vocab)
def encode(text, maxlen=4):
ids = [vocab.get(w,0) for w in text.split()]
ids = ids[:maxlen] + [0]*(maxlen-len(ids))
return ids
X = torch.tensor([encode(t) for t in texts])
y = torch.tensor(labels, dtype=torch.long)
class TextDS(Dataset):
def __len__(self): return len(X)
def __getitem__(self, i): return X[i], y[i]
dl = DataLoader(TextDS(), batch_size=2, shuffle=True)
class LSTMClassifier(nn.Module):
def __init__(self, vocab_size, emb=32, hid=64, num_classes=2):
super().__init__()
self.emb = nn.Embedding(vocab_size, emb, padding_idx=0)
self.lstm = nn.LSTM(emb, hid, batch_first=True)
self.fc = nn.Linear(hid, num_classes)
def forward(self, x):
e = self.emb(x)
o,(h,c) = self.lstm(e)
return self.fc(h[-1])
model = LSTMClassifier(len(vocab)).train()
opt = torch.optim.Adam(model.parameters(), lr=1e-3)
crit = nn.CrossEntropyLoss()
for epoch in range(20):
for xb,yb in dl:
opt.zero_grad(); out = model(xb)
loss = crit(out, yb); loss.backward(); opt.step()
print("Trained. Predict:", model(X).argmax(1))
Experiment 6: Time Series Forecasting (LSTM)
Objective: Design and implement an LSTM for forecasting univariate time series.
Steps:
1. Prepare sliding windows (lookback → target).
2. Define LSTM regressor; train with MSE.
3. Evaluate with RMSE/MAE; plot forecast vs actual.
4. Experiment with window size and hidden units.
Starter Code (skeleton):
# LSTM for Time Series Forecasting – PyTorch Skeleton
import torch, torch.nn as nn
from torch.utils.data import Dataset, DataLoader
import math
# Synthetic sine data
import numpy as np
t = np.arange(0, 400, 0.1)
series = np.sin(0.05*t) + 0.1*np.random.randn(len(t))
def make_windows(data, lookback=50, horizon=1):
X, y = [], []
for i in range(len(data)-lookback-horizon):
X.append(data[i:i+lookback])
y.append(data[i+lookback:i+lookback+horizon])
X = torch.tensor(np.array(X), dtype=torch.float32).unsqueeze(-1)
y = torch.tensor(np.array(y), dtype=torch.float32)
return X, y
X, y = make_windows(series, lookback=60, horizon=1)
train_sz = int(0.8*len(X))
Xtr, Xt, ytr, yt = X[:train_sz], X[train_sz:], y[:train_sz], y[train_sz:]
dl = DataLoader(list(zip(Xtr,ytr)), batch_size=64, shuffle=True)
class LSTMReg(nn.Module):
def __init__(self, hid=64):
super().__init__()
self.lstm = nn.LSTM(1, hid, batch_first=True)
self.fc = nn.Linear(hid, 1)
def forward(self, x):
o,(h,c) = self.lstm(x)
return self.fc(o[:,-1,:])
model = LSTMReg()
opt = torch.optim.Adam(model.parameters(), lr=1e-3)
crit = nn.MSELoss()
for epoch in range(10):
for xb,yb in dl:
opt.zero_grad(); out = model(xb)
loss = crit(out, yb.squeeze(1)); loss.backward(); opt.step()
print("epoch", epoch+1, "loss", float(loss))
with torch.no_grad():
pred = model(Xt).squeeze().numpy()
print("Test RMSE:", float(np.sqrt(((pred-yt.squeeze().numpy())**2).mean())))
Experiment 7: Transfer Learning for Images (Pre-trained CNN)
Objective: Enable pre-trained models (e.g., ResNet18) to classify a custom dataset.
Steps:
1. Organize dataset in ImageFolder structure: data/train/class_i, data/val/class_i.
2. Load torchvision.models.resnet18(pretrained=True); freeze backbone or fine-tune.
3. Replace final FC to match num_classes; train with small LR.
4. Evaluate and save best model.
Starter Code (skeleton):
# Transfer Learning with ResNet18 – PyTorch Skeleton
import torch, torch.nn as nn
from torch.utils.data import DataLoader
import torchvision
import torchvision.transforms as T
from torchvision.models import resnet18
device = 'cuda' if torch.cuda.is_available() else 'cpu'
train_tfms = T.Compose([T.Resize(256), T.RandomResizedCrop(224), T.RandomHorizontalFlip(),
T.ToTensor(), T.Normalize([0.485,0.456,0.406],[0.229,0.224,0.225])])
val_tfms = T.Compose([T.Resize(256), T.CenterCrop(224),
T.ToTensor(), T.Normalize([0.485,0.456,0.406],[0.229,0.224,0.225])])
train_ds = torchvision.datasets.ImageFolder("data/train", transform=train_tfms)
val_ds = torchvision.datasets.ImageFolder("data/val", transform=val_tfms)
train_dl = DataLoader(train_ds, batch_size=32, shuffle=True, num_workers=2)
val_dl = DataLoader(val_ds, batch_size=64, shuffle=False, num_workers=2)
num_classes = len(train_ds.classes)
model = resnet18(weights="IMAGENET1K_V1").to(device) # torchvision>=0.13
# Freeze backbone (optional)
for p in model.parameters(): p.requires_grad = False
model.fc = nn.Linear(model.fc.in_features, num_classes).to(device)
opt = torch.optim.Adam(model.fc.parameters(), lr=1e-3)
crit = nn.CrossEntropyLoss()
for epoch in range(5):
model.train()
for x,y in train_dl:
x,y = x.to(device), y.to(device)
opt.zero_grad(); loss = crit(model(x), y); loss.backward(); opt.step()
print("epoch", epoch+1, "loss", float(loss))
# Validation
model.eval(); correct=total=0
with torch.no_grad():
for x,y in val_dl:
x,y = x.to(device), y.to(device)
pred = model(x).argmax(1)
correct += (pred==y).sum().item(); total += y.numel()
print("Val Acc:", correct/total)
Experiment 8: Sentiment Analysis on Reviews (LSTM or BERT)
Objective: Read a dataset of text reviews and classify as positive or negative.
Steps:
1. Option A (classic): Tokenize → Embedding → LSTM/GRU → Linear.
2. Option B (modern): Use a pretrained transformer (e.g., distilbert-base-uncased).
3. Train/evaluate with accuracy and F1; show confusion matrix.
4. Demonstrate single-sentence inference.
Starter Code (skeleton):
# Sentiment Analysis – Hugging Face Transformers Skeleton (DistilBERT)
!pip -q install transformers datasets --upgrade
from datasets import load_dataset
from transformers import AutoTokenizer, DataCollatorWithPadding, AutoModelForSequenceClassificat
import numpy as np
from sklearn.metrics import accuracy_score, f1_score, precision_score, recall_score
dataset = load_dataset("imdb")
tokenizer = AutoTokenizer.from_pretrained("distilbert-base-uncased")
def tokenize_fn(batch):
return tokenizer(batch["text"], truncation=True)
tokenized = dataset.map(tokenize_fn, batched=True)
data_collator = DataCollatorWithPadding(tokenizer=tokenizer)
model = AutoModelForSequenceClassification.from_pretrained("distilbert-base-uncased", num_labels
def compute_metrics(eval_pred):
logits, labels = eval_pred
preds = np.argmax(logits, axis=-1)
return {
"accuracy": accuracy_score(labels, preds),
"f1": f1_score(labels, preds),
"precision": precision_score(labels, preds),
"recall": recall_score(labels, preds),
}
args = TrainingArguments(
output_dir="out",
evaluation_strategy="epoch",
per_device_train_batch_size=8,
per_device_eval_batch_size=8,
num_train_epochs=1,
fp16=True if torch.cuda.is_available() else False,
logging_steps=50
)
trainer = Trainer(
model=model,
args=args,
train_dataset=tokenized["train"].shuffle(seed=42).select(range(4000)), # subset for speed
eval_dataset=tokenized["test"].select(range(1000)),
tokenizer=tokenizer,
data_collator=data_collator,
compute_metrics=compute_metrics
)
trainer.train()
metrics = trainer.evaluate()
print(metrics)
# Inference
pred = trainer.predict(tokenized["test"].select(range(1)))
print("Sample prediction logits:", pred.predictions)