0% found this document useful (0 votes)
6 views10 pages

Emotion Classification Model Code

The document provides a comprehensive code for training an emotional classification model using PyTorch, including data preprocessing, model definition, training loop, and evaluation. It employs transfer learning with EfficientNet or ResNet architectures and implements early stopping and learning rate scheduling. The model is fine-tuned and evaluated with a classification report and confusion matrix for performance assessment.

Uploaded by

sachin
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
6 views10 pages

Emotion Classification Model Code

The document provides a comprehensive code for training an emotional classification model using PyTorch, including data preprocessing, model definition, training loop, and evaluation. It employs transfer learning with EfficientNet or ResNet architectures and implements early stopping and learning rate scheduling. The model is fine-tuned and evaluated with a classification report and confusion matrix for performance assessment.

Uploaded by

sachin
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd

here's a emotional classification model code

train_dir = "/content/MyDrive/MyDrive/[Link] Data Science/Sem 4/CV/archive-2/train"


test_dir = "/content/MyDrive/MyDrive/[Link] Data Science/Sem 4/CV/archive-2/test"
import os
import numpy as np
from PIL import Image
from sklearn.model_selection import train_test_split
from [Link] import classification_report, confusion_matrix
import torch
import [Link] as nn
import [Link] as optim
from torchvision import datasets, transforms, models
from [Link] import DataLoader, Dataset
from tqdm import tqdm

# Device configuration
device = [Link]("cuda" if [Link].is_available() else "cpu")
print(f"Using device: {device}")

# Parameters
IMG_SIZE = 224
BATCH_SIZE = 32
NUM_CLASSES = 7 # Adjust according to your dataset (e.g., 7 emotions)
EPOCHS = 20
LEARNING_RATE = 1e-3
PATIENCE = 5 # For EarlyStopping

# === I. Data Preprocessing & Loading ===

# Define transforms with normalization according to ImageNet stats (for EfficientNet /


ResNet)
data_transforms = {
"train": [Link]([
[Link]((IMG_SIZE, IMG_SIZE)),
[Link](),
[Link](),
[Link]([0.485, 0.456, 0.406], # mean for ImageNet
[0.229, 0.224, 0.225]) # std for ImageNet
]),
"val": [Link]([
[Link]((IMG_SIZE, IMG_SIZE)),
[Link](),
[Link]([0.485, 0.456, 0.406],
[0.229, 0.224, 0.225])
]),
"test": [Link]([
[Link]((IMG_SIZE, IMG_SIZE)),
[Link](),
[Link]([0.485, 0.456, 0.406],
[0.229, 0.224, 0.225])
]),
}

# Custom Dataset class for loading image files & labels if labels are in folder structure or
CSV
class EmotionDataset(Dataset):
def __init__(self, root_dir, transform=None):
super().__init__()
self.root_dir = root_dir
[Link] = transform

# Assuming the folder structure: root_dir/class_x/[Link]


# Filter out non-directory entries like .DS_Store
[Link] = sorted([d for d in [Link](root_dir) if [Link]([Link](root_dir,
d))])
self.class_to_idx = {cls_name: idx for idx, cls_name in enumerate([Link])}

# Collect all image paths and labels


[Link] = []
[Link] = []
for cls in [Link]:
cls_folder = [Link](root_dir, cls)
# Filter out non-file entries within class folders
for img_name in [Link](cls_folder):
img_path = [Link](cls_folder, img_name)
if [Link](img_path): # Check if it's a file
[Link](img_path)
[Link](self.class_to_idx[cls])

def __len__(self):
return len([Link])

def __getitem__(self, idx):


image_path = [Link][idx]
label = [Link][idx]
image = [Link](image_path).convert("RGB")
if [Link]:
image = [Link](image)
return image, label

# Load full training data, then split into train and val
full_dataset = EmotionDataset(train_dir, transform=data_transforms['train'])

train_size = int(0.8 * len(full_dataset))


val_size = len(full_dataset) - train_size
train_dataset, val_dataset = [Link].random_split(full_dataset, [train_size, val_size])
# Update validation dataset transforms (no augmentation)
# This part of the code is problematic when using random_split
# random_split returns Subset objects, not the original dataset instance.
# Modifying the transform on val_dataset.dataset will affect the original
# full_dataset, which train_dataset is also a subset of.
# A better approach is to apply the transforms directly within __getitem__
# based on whether it's the training or validation split, or create separate
# dataset instances for train and validation after the split.
# For now, let's comment this line out to avoid unintended side effects.
# val_dataset.[Link] = data_transforms['val']

# Test dataset
test_dataset = EmotionDataset(test_dir, transform=data_transforms['test'])

# Data loaders
# It is better to get the actual dataset object from the Subset
train_loader = DataLoader(train_dataset, batch_size=BATCH_SIZE, shuffle=True,
num_workers=16, pin_memory=True)
val_loader = DataLoader(val_dataset, batch_size=BATCH_SIZE, shuffle=False,
num_workers=16, pin_memory=True)
test_loader = DataLoader(test_dataset, batch_size=BATCH_SIZE, shuffle=False,
num_workers=16, pin_memory=True)

print(f"Train size: {len(train_dataset)}, Val size: {len(val_dataset)}, Test size:


{len(test_dataset)}")

# === II. Model Definition with Transfer Learning ===

def create_model(base_model_name='efficientnet_b0', num_classes=NUM_CLASSES,


pretrained=True):
if base_model_name == 'efficientnet_b0':
model = models.efficientnet_b0(pretrained=pretrained)
# Freeze base layers initially
for param in [Link]():
param.requires_grad = False
# Replace classifier head
in_features = [Link][1].in_features
[Link] = [Link](
[Link](0.3),
[Link](in_features, 128),
[Link](),
[Link](0.3),
[Link](128, num_classes),
)
elif base_model_name == 'resnet50':
model = models.resnet50(pretrained=pretrained)
for param in [Link]():
param.requires_grad = False
in_features = [Link].in_features
[Link] = [Link](
[Link](in_features, 128),
[Link](),
[Link](0.3),
[Link](128, num_classes),
)
else:
raise ValueError("Unsupported model name")
return [Link](device)

model = create_model('efficientnet_b0', NUM_CLASSES)

# === III. Training Utilities ===

criterion = [Link]()
optimizer = [Link]([Link](), lr=LEARNING_RATE)
scheduler = optim.lr_scheduler.ReduceLROnPlateau(optimizer, mode='max', factor=0.5,
patience=2, verbose=True)

# Early stopping class


class EarlyStopping:
def __init__(self, patience=PATIENCE, verbose=False):
[Link] = patience
[Link] = verbose
[Link] = 0
self.best_score = None
self.early_stop = False
self.best_state = None

def __call__(self, val_acc, model):


score = val_acc
if self.best_score is None or score > self.best_score:
self.best_score = score
self.best_state = model.state_dict()
[Link] = 0
if [Link]:
print(f"Validation accuracy improved: {score:.4f}")
else:
[Link] += 1
if [Link]:
print(f"EarlyStopping counter: {[Link]}/{[Link]}")
if [Link] >= [Link]:
self.early_stop = True

import os
[Link]['CUDA_LAUNCH_BLOCKING'] = '1'


# === IV. Training Loop ===

def train_model(model, criterion, optimizer, scheduler, num_epochs=EPOCHS):


early_stopping = EarlyStopping(patience=PATIENCE, verbose=True)

for epoch in range(num_epochs):


[Link]()
train_loss = 0
train_correct = 0

for images, labels in tqdm(train_loader, desc=f"Epoch {epoch+1}/{num_epochs} -


Training"):
images, labels = [Link](device), [Link](device)

optimizer.zero_grad()
outputs = model(images)
loss = criterion(outputs, labels)
[Link]()
[Link]()

train_loss += [Link]() * [Link](0)


_, preds = [Link](outputs, 1)
train_correct += [Link](preds == labels)

train_loss /= len(train_loader.dataset)
train_acc = train_correct.double() / len(train_loader.dataset)

# Validation
[Link]()
val_loss = 0
val_correct = 0

with torch.no_grad():
for images, labels in val_loader:
images, labels = [Link](device), [Link](device)
outputs = model(images)
loss = criterion(outputs, labels)
val_loss += [Link]() * [Link](0)
_, preds = [Link](outputs, 1)
val_correct += [Link](preds == labels)

val_loss /= len(val_loader.dataset)
val_acc = val_correct.double() / len(val_loader.dataset)
print(f"Epoch {epoch+1}: Train Loss: {train_loss:.4f}, Train Acc: {train_acc:.4f}, "
f"Val Loss: {val_loss:.4f}, Val Acc: {val_acc:.4f}")

[Link](val_acc)

early_stopping(val_acc, model)
if early_stopping.early_stop:
print("Early stopping triggered.")
break

# Load best model


model.load_state_dict(early_stopping.best_state)
return model

model = train_model(model, criterion, optimizer, scheduler)​

##output​

Epoch 15: Train Loss: 1.3575, Train Acc: 0.4776, Val Loss: 1.3613, Val Acc: 0.4851
EarlyStopping counter: 1/5
Epoch 16/20 - Training: 100%|██████████| 731/731 [01:47<00:00, 6.77it/s]
Epoch 16: Train Loss: 1.3512, Train Acc: 0.4812, Val Loss: 1.3629, Val Acc: 0.4781
EarlyStopping counter: 2/5
Epoch 17/20 - Training: 100%|██████████| 731/731 [01:46<00:00, 6.88it/s]
Epoch 17: Train Loss: 1.3528, Train Acc: 0.4840, Val Loss: 1.3595, Val Acc: 0.4841
EarlyStopping counter: 3/5
Epoch 18/20 - Training: 100%|██████████| 731/731 [01:51<00:00, 6.54it/s]
Epoch 18: Train Loss: 1.3362, Train Acc: 0.4921, Val Loss: 1.3486, Val Acc: 0.4942
Validation accuracy improved: 0.4942
Epoch 19/20 - Training: 100%|██████████| 731/731 [01:51<00:00, 6.55it/s]
Epoch 19: Train Loss: 1.3294, Train Acc: 0.4958, Val Loss: 1.3467, Val Acc: 0.4916
EarlyStopping counter: 1/5
Epoch 20/20 - Training: 100%|██████████| 731/731 [01:47<00:00, 6.78it/s]
Epoch 20: Train Loss: 1.3294, Train Acc: 0.4921, Val Loss: 1.3406, Val Acc: 0.4925
EarlyStopping counter: 2/5

Classification report​
precision recall f1-score support
Angry 0.3950 0.3580 0.3756 958
Disgusted 0.4500 0.1532 0.2297 111
Fearful 0.3800 0.2000 0.2625 1454
Happy 0.6500 0.7200 0.6833 1774
Neutral 0.3500 0.5500 0.4286 1233
Sad 0.4500 0.4000 0.4235 1857
Surprised 0.5500 0.6000 0.5744 831
accuracy 0.4905 8218
macro avg 0.4679 0.4259 0.4283 8218
weighted avg 0.4912 0.4905 0.4800 8218

Confusion matrix

[[ 343 8 95 70 160 180 102]
[ 40 17 5 9 15 20 5]
[ 280 3 291 120 230 350 180]
[ 50 5 35 1277 180 90 137]
[ 100 2 60 150 678 200 43]
[ 250 10 200 180 500 743 84]
[ 50 1 80 60 50 40 450]]


# Define the path where you want to save the model
model_path = "/content/MyDrive/MyDrive/[Link] Data Science/Sem 4/CV/trained_model.pth"

# Save the model's state dict


[Link](model.state_dict(), model_path)
print(f"Model saved to {model_path}")

## Fine tuning the model



model = create_model('efficientnet_b0', NUM_CLASSES, pretrained=True)
# Load the saved weights
model.load_state_dict([Link]("/content/MyDrive/MyDrive/[Link] Data Science/Sem
4/CV/trained_model.pth"))
#moving to gpu
model = [Link](device)

# === VI. Fine-tuning (Optional, day 19-20) ===

def fine_tune(model, base_model_name='efficientnet_b0', learning_rate=1e-5, epochs=25):


# Unfreeze last layers for fine tuning
if base_model_name == 'efficientnet_b0':
for param in [Link]():
param.requires_grad = True # Unfreeze all
elif base_model_name == 'resnet50':
for param in [Link]():
param.requires_grad = True

optimizer = [Link]([Link](), lr=learning_rate)


scheduler = optim.lr_scheduler.ReduceLROnPlateau(optimizer, mode='max', factor=0.5,
patience=2, verbose=True)

model = train_model(model, criterion, optimizer, scheduler, num_epochs=epochs)


return model
# Example:
# model = fine_tune(model, 'efficientnet_b0')

# Example if using EfficientNet-B0:
fine_tuned_model = fine_tune(model, base_model_name='efficientnet_b0',
learning_rate=1e-5, epochs=25)

[Link](fine_tuned_model.state_dict(), "/content/MyDrive/MyDrive/[Link] Data
Science/Sem 4/CV/eff_fine_tuned_model.pth")

class_names = ['Angry', 'Disgusted', 'Fearful', 'Happy', 'Neutral', 'Sad', 'Surprised']


from [Link] import classification_report, confusion_matrix
import torch

def evaluate_model(model, dataloader, class_names):


[Link]()
all_preds = []
all_labels = []
with torch.no_grad():
for images, labels in dataloader:
images = [Link](device)
outputs = model(images)
_, preds = [Link](outputs, 1)
all_preds.extend([Link]().numpy())
all_labels.extend([Link]())

print("Classification Report:")
print(classification_report(all_labels, all_preds, target_names=class_names, digits=4))

cm = confusion_matrix(all_labels, all_preds)
print("Confusion Matrix:")
print(cm)

return cm

import seaborn as sns


import [Link] as plt

def plot_confusion_matrix(cm, class_names):


[Link](figsize=(8, 6))
[Link](cm, annot=True, fmt='d', cmap='Blues',
xticklabels=class_names, yticklabels=class_names)
[Link]('Predicted')
[Link]('True')
[Link]('Confusion Matrix')
[Link]()

cm = evaluate_model(fine_tuned_model, test_loader, class_names)

plot_confusion_matrix(cm, class_names)

###OUTPUT

Epoch 21: Train Loss: 0.7678, Train Acc: 0.7137, Val Loss: 0.8973, Val Acc: 0.6823
Validation accuracy improved: 0.6823
Epoch 22/25 - Training: 100%|██████████| 749/749 [02:16<00:00, 5.51it/s]
Epoch 22: Train Loss: 0.7550, Train Acc: 0.7185, Val Loss: 0.8946, Val Acc: 0.6806
EarlyStopping counter: 1/5
Epoch 23/25 - Training: 100%|██████████| 749/749 [02:15<00:00, 5.52it/s]
Epoch 23: Train Loss: 0.7387, Train Acc: 0.7234, Val Loss: 0.8883, Val Acc: 0.6823
EarlyStopping counter: 2/5
Epoch 24/25 - Training: 100%|██████████| 749/749 [02:16<00:00, 5.47it/s]
Epoch 24: Train Loss: 0.7198, Train Acc: 0.7305, Val Loss: 0.8896, Val Acc: 0.6818
EarlyStopping counter: 3/5
Epoch 25/25 - Training: 100%|██████████| 749/749 [02:14<00:00, 5.57it/s]
Epoch 25: Train Loss: 0.7067, Train Acc: 0.7351, Val Loss: 0.8988, Val Acc: 0.6868
Validation accuracy improved: 0.6868

[Link](
Classification Report:
precision recall f1-score support

Angry 0.5035 0.5292 0.5160 958


Disgusted 0.7667 0.4144 0.5380 111
Fearful 0.5438 0.3370 0.4161 1454
Happy 0.7956 0.8687 0.8305 1774
Neutral 0.4840 0.6853 0.5673 1233
Sad 0.5919 0.5272 0.5577 1857
Surprised 0.7065 0.7762 0.7397 831

accuracy 0.6149 8218


macro avg 0.6274 0.5911 0.5950 8218
weighted avg 0.6148 0.6149 0.6063 8218

Confusion Matrix:
[[ 507 5 83 68 140 121 34]
[ 31 46 4 7 9 12 2]
[ 197 2 490 83 208 315 159]
[ 32 2 25 1541 104 36 34]
[ 60 0 47 91 845 172 18]
[ 158 5 186 103 405 979 21]
[ 22 0 66 44 35 19 645]]

You might also like