A Step-by-Step Guide to Video Classification with Deep Learning in Python
Video classification is one of the most exciting and impactful areas of deep learning and computer vision. From analyzing sports highlights to detecting anomalous events in surveillance footage to understanding human behavior in retail stores, the ability to automatically recognize and categorize the content of videos has immense value across industries.
However, video classification also poses unique challenges compared to the more well-studied problem of image classification. Videos are dynamic sequences of frames with rich temporal dependencies and large data volume, requiring specialized deep learning architectures and significant computational resources.
In this in-depth tutorial, we‘ll walk through the key steps and considerations for building an effective video classifier using deep learning in Python, including:
1. Obtaining and preparing a video dataset
The first step is to source a suitable video dataset for your task. You‘ll need a labeled dataset with many video samples for each category you want to recognize. Standard public datasets for benchmarking video classification models include:
- UCF101 dataset: 13,320 videos from 101 action categories
- Kinetics dataset: 650,000 video clips covering 700 human action classes
- Moments in Time dataset: 1 million labeled 3-second videos
If you need to prepare your own dataset, you‘ll have to collect relevant videos, annotate them with labels, and set up a data pipeline for efficiently loading and iterating through the data. Consider factors like file format, resolution, duration, and dataset size.
2. Extracting frames from videos
Deep learning models take tensors as input, so we need to convert our videos into sequences of images. This involves iterating through each video, extracting frames at a specified rate (e.g. 1 frame per second), and saving them to disk or memory.
Here‘s a simple example in Python using OpenCV:
import cv2
import os
def extract_frames(video_path, frames_dir, overwrite=False, start=-1, end=-1, every=1):
"""Extract frames from a video using OpenCVs VideoCapture method"""
video_id = os.path.basename(video_path).split(‘.‘)[0]
if not os.path.exists(frames_dir):
os.mkdir(frames_dir)
cap = cv2.VideoCapture(video_path)
if not cap.isOpened():
print(f"Error opening video {video_path}")
return
frame_count = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
frame_width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
frame_height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
if end < 0:
end = frame_count
every = abs(every)
for frame_no in range(0, frame_count, every):
if frame_no < start:
continue
elif frame_no > end:
break
cap.set(cv2.CAP_PROP_POS_FRAMES, frame_no)
ret, frame = cap.read()
if ret:
frame_path = os.path.join(frames_dir, f"{video_id}_{frame_no:06d}.jpg")
if not os.path.exists(frame_path) or overwrite:
cv2.imwrite(frame_path, frame)
else:
print(f"Error reading frame {frame_no}")
break
cap.release()
Extracting too many frames will blow up your storage requirements, while too few may not capture enough temporal information. I‘ve found extracting 1-2 frames per second works well in most cases as a good balance.
3. Preprocessing and augmenting frames
Before feeding the frames into a model, you‘ll usually want to apply some preprocessing:
- Resizing frames to a consistent size, e.g. 224×224 pixels. This is often required by pretrained CNN architectures.
- Normalizing pixel values to [0,1] range
- Converting to RGB colorspace if needed
- Data augmentation: random cropping, flipping, color jittering, etc. This helps the model learn invariances and improves generalization.
Here‘s an example using tf.keras preprocessing utilities:
from tensorflow.keras.preprocessing.image import ImageDataGenerator
data_generator = ImageDataGenerator(
featurewise_center=True,
featurewise_std_normalization=True,
rotation_range=20,
width_shift_range=0.2,
height_shift_range=0.2,
zoom_range=0.3,
horizontal_flip=True)
data_generator.fit(x_train)
train_generator = data_generator.flow(x_train, y_train, batch_size)
test_generator = data_generator.flow(x_test, y_test, batch_size)
4. Extracting features using deep learning
Now we‘re ready to extract meaningful features from our frames using deep neural networks. There are a few main approaches:
-
CNN on individual frames: The simplest method is to run each frame independently through a 2D CNN and average the predictions. This neglects temporal information but can still work decently and is a good baseline. Common architectures include ResNet, Inception, DenseNet pretrained on ImageNet.
-
3D CNN on frame sequences: 3D CNNs like C3D and I3D extend standard 2D convolutions to the temporal dimension, extracting features from frame subsequences. This captures short-term motion patterns and can improve performance.
-
CNN + RNN: After extracting CNN features from each frame, we can feed the sequence to an RNN like an LSTM to model long-term dependencies. The final hidden state or output sequence is used for classification.
-
Two-stream networks: Use two parallel pathways – one CNN stream on RGB frames, and another on precomputed optical flow frames. The two streams capture complementary appearance and motion information.
Here‘s a sketch of a CNN+LSTM architecture in Keras:
import tensorflow as tf
input_shape = (num_frames, 224, 224, 3)
inputs = tf.keras.Input(shape=input_shape)
x = tf.keras.layers.TimeDistributed(
tf.keras.applications.ResNet50(include_top=False, weights=‘imagenet‘, pooling=‘avg‘))(inputs)
x = tf.keras.layers.LSTM(512, return_sequences=True)(x)
x = tf.keras.layers.LSTM(512)(x)
outputs = tf.keras.layers.Dense(num_classes, activation=‘softmax‘)(x)
model = tf.keras.Model(inputs, outputs)
In practice, I recommend starting with pretrained CNN features and finetuning the whole model end-to-end. Initializing the CNN weights from ImageNet or Kinetics pretraining will significantly speed up convergence.
5. Training the video classifier
Once you‘ve decided on a model architecture, it‘s time to set up the training pipeline. Key steps include:
- Defining the train/validation/test splits
- Specifying loss function (e.g. categorical cross-entropy) and optimizer (e.g. Adam)
- Figuring out how to effectively load and batch video data, using generators if necessary to avoid memory issues
- Setting hyperparameters like batch size, learning rate, number of epochs
- Adding callbacks for learning rate scheduling, early stopping, model checkpointing, etc.
Here‘s an example training loop in Keras:
model.compile(optimizer=‘adam‘, loss=‘categorical_crossentropy‘, metrics=[‘accuracy‘, ‘top_k_categorical_accuracy‘])
epochs = 50
steps_per_epoch = train_generator.n // batch_size
validation_steps = test_generator.n // batch_size
history = model.fit(
train_generator,
steps_per_epoch=steps_per_epoch,
epochs=epochs,
validation_data=test_generator,
validation_steps=validation_steps,
callbacks=[...]
Some tips:
- The right learning rate schedule is critical. I like to use a warm-up phase followed by cosine annealing.
- Label smoothing regularization and mixup data augmentation can help combat overfitting.
- Be patient – video models are much slower to train than image models. Multi-GPU training can help but has diminishing returns.
6. Evaluating performance
Finally, we can benchmark our trained model on the held-out test set. The most common evaluation metrics for video classification are:
- Top-1 accuracy: % of test samples where the highest confidence prediction matches the true label
- Top-5 accuracy: % where true label is in the top 5 predicted labels
- Confusion matrix: shows which classes are most often confused
- Per-class accuracy: to assess if the model struggles with certain categories
Analyze the model‘s errors to gain insights for future improvements. Are there any systematic failure modes? Does the model get confused by similar classes or backgrounds?
Conclusion & Next Steps
In this guide, we walked through the key steps to building an effective video classifier using deep learning:
- Obtaining and preparing video data
- Extracting frames from videos
- Preprocessing and augmenting frames
- Extracting features using CNN, 3D CNN, or CNN-RNN architectures
- Training the model
- Evaluating on test data
For further reading, I recommend the following resources:
- Karpathy et al‘s DeepVideo chapter
- Carreira and Zisserman‘s review of video classification with 3D CNNs
- Christoph Feichtenhofer‘s video understanding tutorials
I‘m excited to see how you apply these techniques to your own video datasets and use cases. Feel free to experiment with different architectures, features, and training strategies – the possibilities are endless!
With the rapid progress in self-supervised learning from video, I believe the future is bright for learning rich representations that transfer to many downstream video tasks. Fine-grained understanding of actions, interactions, and dynamics will unlock a new wave of intelligent video applications.