Latest Post

Reinforcement Learning for Credit Scoring: Applications in Fintech

Here’s something that’ll blow your mind: the way fintech companies decide whether to lend you money is getting a serious upgrade. And I’m not talking about minor tweaks to old formulas — I’m talking about reinforcement learning algorithms that literally learn from every lending decision they make.

Image Classification with Transfer Learning: Use Pre-Trained Models Effectively

Training a neural network from scratch used to mean weeks of compute time and thousands of dollars in GPU costs. I learned this the hard way when I spent three days training a model on my laptop, only to get 60% accuracy. Then I tried transfer learning and got 95% accuracy in two hours.

That’s when it clicked: transfer learning is basically cheating, and you absolutely should do it.

Why waste time reinventing the wheel when Google, Facebook, and Microsoft have already trained models on millions of images? You can grab their work, tweak it for your specific problem, and get production-ready results in hours instead of weeks. Let me show you how.

What Transfer Learning Actually Means

Transfer learning is simple: take a model trained on one task and adapt it for another. It’s like how learning Spanish helps you learn Italian — the fundamental concepts transfer over.

In image classification, this means taking models trained on ImageNet (1.4 million images across 1000 categories) and fine-tuning them for your specific task. Maybe you’re classifying medical images, identifying plant species, or sorting products.

The pre-trained model already learned to recognize edges, textures, shapes, and complex patterns. That knowledge transfers to almost any image classification task. You just need to teach it the specifics of YOUR problem.

Here’s the beautiful part: you only retrain the last few layers, not the entire network. This means less data required, faster training, and better results. It’s the closest thing to a free lunch in machine learning.


Why Transfer Learning Beats Training from Scratch

Let me count the ways this approach saves your sanity:

You need way less data. Training from scratch might need 10,000+ images per class. With transfer learning? Sometimes you can get good results with just 100–200 images per class. The pre-trained model already understands images — you’re just teaching it new categories.

Training is ridiculously faster. Instead of days or weeks, you’re looking at hours or even minutes. The frozen layers don’t need training, so you’re only updating a fraction of the network’s parameters.

Better accuracy with less effort. Pre-trained models learned from millions of images. Your dataset of 500 images can’t compete with that experience. Transfer learning lets you leverage that massive training data for free.

Smaller GPU requirements. You can fine-tune models on a regular laptop. No need for expensive cloud GPUs (though they help). I’ve trained solid classifiers on a 5-year-old MacBook.

The only time you shouldn’t use transfer learning? When your images are radically different from natural photos — think medical scans, satellite imagery, or microscope images. Even then, it often helps.

Choosing the Right Pre-Trained Model

Not all pre-trained models are created equal. Here’s your quick decision guide:

ResNet (Residual Networks)

ResNet50 is my go-to starting point. It’s the reliable Honda Civic of pre-trained models — nothing flashy, just solid performance.

Comes in different depths: ResNet18, ResNet34, ResNet50, ResNet101, ResNet152. Deeper = more accurate but slower. Start with ResNet50 and adjust from there.

When to use it: General purpose classification, when you want something battle-tested and reliable.

EfficientNet

EfficientNet is newer and more sophisticated. It scales depth, width, and resolution together for optimal performance at each size.

EfficientNet-B0 through B7 offer different accuracy/speed tradeoffs. B0 is fastest, B7 is most accurate.

When to use it: When you want state-of-the-art accuracy and are willing to experiment a bit.

VGG16/VGG19

VGG is old school (2014) but still works. Simple architecture, easy to understand, but larger file sizes.

When to use it: Learning purposes, when you want something straightforward to modify.

MobileNet

MobileNet is optimized for mobile devices and edge computing. Smaller, faster, slight accuracy tradeoff.

When to use it: Deploying to phones, Raspberry Pi, or anywhere resources are limited.

Vision Transformers (ViT)

ViT is the cutting-edge approach using transformer architecture instead of convolutions. Amazing accuracy, but needs more data for fine-tuning.

When to use it: When accuracy is paramount and you have decent-sized datasets.

IMO, start with ResNet50 or EfficientNet-B0. They cover 90% of use cases and you can always switch later if needed.

Your First Transfer Learning Project (PyTorch Version)

Let’s build an actual image classifier using transfer learning. We’ll classify images into custom categories — use whatever interests you.

Setting Up

Install the essentials:

bash

pip install torch torchvision matplotlib

Loading a Pre-Trained Model

Here’s how simple it is to load ResNet50 with ImageNet weights:

python

import torch
import torchvision.models as models
from torchvision import transforms
from torch import nn, optim
# Load pre-trained ResNet50
model = models.resnet50(pretrained=True)
# Freeze all layers
for param in model.parameters():
param.requires_grad = False
# Replace the final layer for your number of classes
num_classes = 5 # Change this to your number of categories
model.fc = nn.Linear(model.fc.in_features, num_classes)
print(model)

That’s it. You’ve got a model that already understands images, and we’ve modified the final layer to classify into YOUR categories.

Freezing layers (param.requires_grad = False) means those weights won't update during training. We're only training the new final layer. This is the "transfer" part—we're transferring learned features.

Preparing Your Data

Your images should be organized like this:

dataset/
train/
class1/
image1.jpg
image2.jpg
class2/
image1.jpg
image2.jpg
val/
class1/
image1.jpg
class2/
image1.jpg

PyTorch’s ImageFolder loader handles this automatically:

python

from torchvision.datasets import ImageFolder
from torch.utils.data import DataLoader
# Data transformations
transform = transforms.Compose([
transforms.Resize(256),
transforms.CenterCrop(224), # ResNet expects 224x224
transforms.ToTensor(),
transforms.Normalize(mean=[0.485, 0.456, 0.406],
std=[0.229, 0.224, 0.225])
])
# Load datasets
train_dataset = ImageFolder('dataset/train', transform=transform)
val_dataset = ImageFolder('dataset/val', transform=transform)
# Create data loaders
train_loader = DataLoader(train_dataset, batch_size=32, shuffle=True)
val_loader = DataLoader(val_dataset, batch_size=32, shuffle=False)
print(f"Training samples: {len(train_dataset)}")
print(f"Classes: {train_dataset.classes}")

Those normalization values are ImageNet statistics. Pre-trained models expect inputs normalized this way — don’t change them.

Training Your Classifier

Now the actual training loop:

python

# Move model to GPU if available
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model = model.to(device)
# Loss function and optimizer
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.fc.parameters(), lr=0.001)
# Training
num_epochs = 10
for epoch in range(num_epochs):
model.train()
running_loss = 0.0
correct = 0
total = 0

for images, labels in train_loader:
images, labels = images.to(device), labels.to(device)

# Forward pass
outputs = model(images)
loss = criterion(outputs, labels)

# Backward pass
optimizer.zero_grad()
loss.backward()
optimizer.step()

# Statistics
running_loss += loss.item()
_, predicted = outputs.max(1)
total += labels.size(0)
correct += predicted.eq(labels).sum().item()

# Validation
model.eval()
val_correct = 0
val_total = 0

with torch.no_grad():
for images, labels in val_loader:
images, labels = images.to(device), labels.to(device)
outputs = model(images)
_, predicted = outputs.max(1)
val_total += labels.size(0)
val_correct += predicted.eq(labels).sum().item()

print(f'Epoch {epoch+1}/{num_epochs}')
print(f'Train Loss: {running_loss/len(train_loader):.4f}, Accuracy: {100.*correct/total:.2f}%')
print(f'Val Accuracy: {100.*val_correct/val_total:.2f}%')
print()
# Save the model
torch.save(model.state_dict(), 'classifier.pth')

Run this and watch your accuracy climb. On a decent dataset, you should see 80–90%+ validation accuracy pretty quickly.

Making Predictions with Your Trained Model

Once trained, using your model is straightforward:

python

from PIL import Image
# Load the model
model = models.resnet50(pretrained=False)
model.fc = nn.Linear(model.fc.in_features, num_classes)
model.load_state_dict(torch.load('classifier.pth'))
model.eval()
# Prepare an image
img = Image.open('test_image.jpg')
img_tensor = transform(img).unsqueeze(0) # Add batch dimension
# Predict
with torch.no_grad():
output = model(img_tensor)
probabilities = torch.nn.functional.softmax(output, dim=1)
confidence, predicted_class = probabilities.max(1)
print(f"Predicted: {train_dataset.classes[predicted_class.item()]}")
print(f"Confidence: {confidence.item()*100:.2f}%")

You’ve got a working classifier. Ship it, show it off, whatever — you’re done with the basics.

Fine-Tuning: The Next Level

Freezing all layers works, but you can get better results by fine-tuning — unfreezing some layers and training them with a lower learning rate.

Here’s the strategy:

python

# Load pre-trained model
model = models.resnet50(pretrained=True)
# Replace final layer
model.fc = nn.Linear(model.fc.in_features, num_classes)
# First, train only the new layer for a few epochs (we did this above)
# Then, unfreeze the last few layers
# Unfreeze layer4 (last convolutional block)
for param in model.layer4.parameters():
param.requires_grad = True
# Use different learning rates for different layers
optimizer = optim.Adam([
{'params': model.layer4.parameters(), 'lr': 1e-4}, # Low LR for pre-trained
{'params': model.fc.parameters(), 'lr': 1e-3} # Higher LR for new layer
])
# Continue training for more epochs

This lets the model adapt pre-trained features to your specific domain. The low learning rate prevents destroying the useful features learned on ImageNet.

When to fine-tune: When you have enough data (1000+ images) and want to squeeze out extra accuracy.

Data Augmentation: Your Secret Weapon

More data = better models, but collecting data is expensive. Data augmentation creates variations of existing images, effectively multiplying your dataset size.

python

train_transform = transforms.Compose([
transforms.Resize(256),
transforms.RandomCrop(224), # Random crops
transforms.RandomHorizontalFlip(), # Random flips
transforms.RandomRotation(15), # Slight rotations
transforms.ColorJitter(brightness=0.2, # Color variations
contrast=0.2,
saturation=0.2),
transforms.ToTensor(),
transforms.Normalize(mean=[0.485, 0.456, 0.406],
std=[0.229, 0.224, 0.225])
])
# Use augmented transform for training data only
train_dataset = ImageFolder('dataset/train', transform=train_transform)

Every epoch sees slightly different versions of each image. It’s like having 10x more data without collecting 10x more images.

Don’t augment validation data — you want to test on clean, unmodified images.

TensorFlow/Keras Version (For the TensorFlow Fans)

PyTorch not your thing? Here’s the TensorFlow equivalent:

python

import tensorflow as tf
from tensorflow.keras import layers, models
from tensorflow.keras.applications import ResNet50
# Load pre-trained ResNet50
base_model = ResNet50(weights='imagenet', include_top=False, input_shape=(224, 224, 3))
# Freeze the base model
base_model.trainable = False
# Build your classifier on top
model = models.Sequential([
base_model,
layers.GlobalAveragePooling2D(),
layers.Dense(256, activation='relu'),
layers.Dropout(0.5),
layers.Dense(num_classes, activation='softmax')
])
# Compile
model.compile(
optimizer='adam',
loss='categorical_crossentropy',
metrics=['accuracy']
)
# Train
model.fit(
train_dataset,
validation_data=val_dataset,
epochs=10
)
# Save
model.save('classifier.h5')

Same concepts, different syntax. Pick whichever framework you prefer.

Common Mistakes (That I’ve Definitely Made)

Forgetting to freeze layers: If you don’t freeze, you’ll retrain everything and likely overfit on small datasets. Always freeze first, fine-tune later.

Wrong image normalization: Pre-trained models expect specific normalization. Use the values they were trained with (ImageNet stats for most models).

Overfitting on small datasets: With 50 images per class, your model will memorize training data. Use data augmentation, dropout, and early stopping.

Not using a validation set: You need separate validation data to know if your model actually works or just memorized training data.

Training for too many epochs: Watch validation accuracy. When it stops improving (or starts decreasing), stop training. Continuing wastes time and causes overfitting.

Dealing with Domain Shift

Transfer learning works great when your images look similar to ImageNet (natural photos). But what if you’re classifying X-rays, satellite images, or microscope slides?

Try it anyway: Even with domain shift, transfer learning often helps. The low-level features (edges, textures) still transfer.

Use domain-specific pre-trained models: Medical imaging has models pre-trained on medical data. Check for your specific domain.

Unfreeze more layers: Might need to fine-tune deeper layers to adapt features to your domain.

Consider training from scratch: If transfer learning isn’t helping after experimentation, you might need to train from scratch. Rare, but it happens.

Evaluating Your Model Properly

Accuracy is great, but it doesn’t tell the whole story. Use these metrics:

python

from sklearn.metrics import classification_report, confusion_matrix
import numpy as np
# Get predictions on validation set
all_preds = []
all_labels = []
model.eval()
with torch.no_grad():
for images, labels in val_loader:
images = images.to(device)
outputs = model(images)
_, preds = outputs.max(1)
all_preds.extend(preds.cpu().numpy())
all_labels.extend(labels.numpy())
# Classification report
print(classification_report(all_labels, all_preds,
target_names=train_dataset.classes))
# Confusion matrix
cm = confusion_matrix(all_labels, all_preds)
print(cm)

This shows precision, recall, and F1 scores per class. You’ll spot if your model is biased toward certain categories or struggling with specific classes.

Deploying Your Classifier

Got a working model? Time to make it useful:

Export for Production

python

# PyTorch to ONNX
dummy_input = torch.randn(1, 3, 224, 224)
torch.onnx.export(model, dummy_input, "classifier.onnx")

ONNX runs on pretty much any platform and is often faster than raw PyTorch/TensorFlow.

Build a Simple API

Quick Flask API for your classifier:

python

from flask import Flask, request, jsonify
from PIL import Image
import io
app = Flask(__name__)
# Load model once at startup
model = load_your_model()
model.eval()
@app.route('/predict', methods=['POST'])
def predict():
img_bytes = request.files['image'].read()
img = Image.open(io.BytesIO(img_bytes))

# Preprocess and predict
img_tensor = transform(img).unsqueeze(0)
with torch.no_grad():
output = model(img_tensor)
probs = torch.nn.functional.softmax(output, dim=1)
confidence, predicted = probs.max(1)

return jsonify({
'class': class_names[predicted.item()],
'confidence': confidence.item()
})
if __name__ == '__main__':
app.run()

Now you can send images via HTTP and get predictions back. Deploy to Heroku, AWS, wherever.

Real-World Performance Tips

Batch prediction: Process multiple images at once for better GPU utilization:

python

batch = torch.stack([transform(img1), transform(img2), transform(img3)])
predictions = model(batch)

Image size matters: Larger images = better accuracy but slower inference. Find the minimum size that gives acceptable accuracy.

Model quantization: Reduce model size and increase speed with minimal accuracy loss:

python

quantized_model = torch.quantization.quantize_dynamic(
model, {torch.nn.Linear}, dtype=torch.qint8
)

This can reduce model size by 4x and double inference speed.

Your Roadmap to Mastery

You’ve learned the fundamentals of transfer learning. Here’s how to level up:

Week 1: Build 3 simple classifiers on different datasets. Get comfortable with the workflow.

Week 2: Experiment with different pre-trained models. See how ResNet, EfficientNet, and ViT compare on your data.

Week 3: Try fine-tuning. Unfreeze layers progressively and optimize learning rates.

Week 4: Deploy a model as an API or web app. Nothing teaches like putting something in production.

Don’t just read tutorials — build real projects. Classify your own photos, solve actual problems, make something you care about.

Transfer learning is your shortcut to professional-quality image classifiers. Stop training from scratch like it’s 2012. Grab a pre-trained model, fine-tune it on your data, and ship something amazing this weekend.

What are you going to classify?

Comments