Image features exercise

Complete and hand in this completed worksheet (including its outputs and any supporting code outside of the worksheet) with your assignment submission. For more details see the assignments page on the course website.

We have seen that we can achieve reasonable performance on an image classification task by training a linear classifier on the pixels of the input image. In this exercise we will show that we can improve our classification performance by training linear classifiers not on raw pixels but on features that are computed from the raw pixels.

All of your work for this exercise will be done in this notebook.


In [1]:
import random
import numpy as np
from cs231n.data_utils import load_CIFAR10
import matplotlib.pyplot as plt

from __future__ import print_function

%matplotlib inline
plt.rcParams['figure.figsize'] = (10.0, 8.0) # set default size of plots
plt.rcParams['image.interpolation'] = 'nearest'
plt.rcParams['image.cmap'] = 'gray'

# for auto-reloading extenrnal modules
# see http://stackoverflow.com/questions/1907993/autoreload-of-modules-in-ipython
%load_ext autoreload
%autoreload 2

Load data

Similar to previous exercises, we will load CIFAR-10 data from disk.


In [2]:
from cs231n.features import color_histogram_hsv, hog_feature

def get_CIFAR10_data(num_training=49000, num_validation=1000, num_test=1000):
    # Load the raw CIFAR-10 data
    cifar10_dir = 'cs231n/datasets/cifar-10-batches-py'
    X_train, y_train, X_test, y_test = load_CIFAR10(cifar10_dir)
    
    # Subsample the data
    mask = list(range(num_training, num_training + num_validation))
    X_val = X_train[mask]
    y_val = y_train[mask]
    mask = list(range(num_training))
    X_train = X_train[mask]
    y_train = y_train[mask]
    mask = list(range(num_test))
    X_test = X_test[mask]
    y_test = y_test[mask]
    
    return X_train, y_train, X_val, y_val, X_test, y_test

X_train, y_train, X_val, y_val, X_test, y_test = get_CIFAR10_data()

Extract Features

For each image we will compute a Histogram of Oriented Gradients (HOG) as well as a color histogram using the hue channel in HSV color space. We form our final feature vector for each image by concatenating the HOG and color histogram feature vectors.

Roughly speaking, HOG should capture the texture of the image while ignoring color information, and the color histogram represents the color of the input image while ignoring texture. As a result, we expect that using both together ought to work better than using either alone. Verifying this assumption would be a good thing to try for the bonus section.

The hog_feature and color_histogram_hsv functions both operate on a single image and return a feature vector for that image. The extract_features function takes a set of images and a list of feature functions and evaluates each feature function on each image, storing the results in a matrix where each column is the concatenation of all feature vectors for a single image.


In [3]:
from cs231n.features import *

num_color_bins = 10 # Number of bins in the color histogram
feature_fns = [hog_feature, lambda img: color_histogram_hsv(img, nbin=num_color_bins)]
X_train_feats = extract_features(X_train, feature_fns, verbose=True)
X_val_feats = extract_features(X_val, feature_fns)
X_test_feats = extract_features(X_test, feature_fns)

# Preprocessing: Subtract the mean feature
mean_feat = np.mean(X_train_feats, axis=0, keepdims=True)
X_train_feats -= mean_feat
X_val_feats -= mean_feat
X_test_feats -= mean_feat

# Preprocessing: Divide by standard deviation. This ensures that each feature
# has roughly the same scale.
std_feat = np.std(X_train_feats, axis=0, keepdims=True)
X_train_feats /= std_feat
X_val_feats /= std_feat
X_test_feats /= std_feat

# Preprocessing: Add a bias dimension
X_train_feats = np.hstack([X_train_feats, np.ones((X_train_feats.shape[0], 1))])
X_val_feats = np.hstack([X_val_feats, np.ones((X_val_feats.shape[0], 1))])
X_test_feats = np.hstack([X_test_feats, np.ones((X_test_feats.shape[0], 1))])


Done extracting features for 1000 / 49000 images
Done extracting features for 2000 / 49000 images
Done extracting features for 3000 / 49000 images
Done extracting features for 4000 / 49000 images
Done extracting features for 5000 / 49000 images
Done extracting features for 6000 / 49000 images
Done extracting features for 7000 / 49000 images
Done extracting features for 8000 / 49000 images
Done extracting features for 9000 / 49000 images
Done extracting features for 10000 / 49000 images
Done extracting features for 11000 / 49000 images
Done extracting features for 12000 / 49000 images
Done extracting features for 13000 / 49000 images
Done extracting features for 14000 / 49000 images
Done extracting features for 15000 / 49000 images
Done extracting features for 16000 / 49000 images
Done extracting features for 17000 / 49000 images
Done extracting features for 18000 / 49000 images
Done extracting features for 19000 / 49000 images
Done extracting features for 20000 / 49000 images
Done extracting features for 21000 / 49000 images
Done extracting features for 22000 / 49000 images
Done extracting features for 23000 / 49000 images
Done extracting features for 24000 / 49000 images
Done extracting features for 25000 / 49000 images
Done extracting features for 26000 / 49000 images
Done extracting features for 27000 / 49000 images
Done extracting features for 28000 / 49000 images
Done extracting features for 29000 / 49000 images
Done extracting features for 30000 / 49000 images
Done extracting features for 31000 / 49000 images
Done extracting features for 32000 / 49000 images
Done extracting features for 33000 / 49000 images
Done extracting features for 34000 / 49000 images
Done extracting features for 35000 / 49000 images
Done extracting features for 36000 / 49000 images
Done extracting features for 37000 / 49000 images
Done extracting features for 38000 / 49000 images
Done extracting features for 39000 / 49000 images
Done extracting features for 40000 / 49000 images
Done extracting features for 41000 / 49000 images
Done extracting features for 42000 / 49000 images
Done extracting features for 43000 / 49000 images
Done extracting features for 44000 / 49000 images
Done extracting features for 45000 / 49000 images
Done extracting features for 46000 / 49000 images
Done extracting features for 47000 / 49000 images
Done extracting features for 48000 / 49000 images

Train SVM on features

Using the multiclass SVM code developed earlier in the assignment, train SVMs on top of the features extracted above; this should achieve better results than training SVMs directly on top of raw pixels.


In [8]:
# Use the validation set to tune the learning rate and regularization strength

from cs231n.classifiers.linear_classifier import LinearSVM

learning_rates = [1e-9, 1e-8, 1e-7]
regularization_strengths = [5e4, 5e5, 5e6]
#learning_rates = list(map(lambda x: x*1e-9, np.arange(0.9, 2, 0.1)))
#regularization_strengths = list(map(lambda x: x*1e4, np.arange(1, 10)))

results = {}
best_val = -1
best_svm = None
iters = 2000

################################################################################
# TODO:                                                                        #
# Use the validation set to set the learning rate and regularization strength. #
# This should be identical to the validation that you did for the SVM; save    #
# the best trained classifer in best_svm. You might also want to play          #
# with different numbers of bins in the color histogram. If you are careful    #
# you should be able to get accuracy of near 0.44 on the validation set.       #
################################################################################
for lr in learning_rates:
    for reg in regularization_strengths:
        print('Training with lr={0}, reg={1}'.format(lr, reg))
        svm = LinearSVM()
        loss_hist = svm.train(X_train_feats, y_train, learning_rate=lr, reg=reg, num_iters=iters)
        y_train_pred = svm.predict(X_train_feats)
        y_val_pred = svm.predict(X_val_feats)
        train_accuracy = np.mean(y_train == y_train_pred)
        validation_accuracy = np.mean(y_val == y_val_pred)
        if validation_accuracy > best_val:
            best_val = validation_accuracy
            best_svm = svm
        results[(lr, reg)] = (validation_accuracy, train_accuracy)
################################################################################
#                              END OF YOUR CODE                                #
################################################################################

# Print out results.
for lr, reg in sorted(results):
    train_accuracy, val_accuracy = results[(lr, reg)]
    print('lr %e reg %e train accuracy: %f val accuracy: %f' % (
                lr, reg, train_accuracy, val_accuracy))

print('best validation accuracy achieved during cross-validation: %f' % best_val)


Training with lr=1e-09, reg=50000.0
Training with lr=1e-09, reg=500000.0
Training with lr=1e-09, reg=5000000.0
Training with lr=1e-08, reg=50000.0
Training with lr=1e-08, reg=500000.0
Training with lr=1e-08, reg=5000000.0
Training with lr=1e-07, reg=50000.0
Training with lr=1e-07, reg=500000.0
Training with lr=1e-07, reg=5000000.0
lr 1.000000e-09 reg 5.000000e+04 train accuracy: 0.115000 val accuracy: 0.119898
lr 1.000000e-09 reg 5.000000e+05 train accuracy: 0.090000 val accuracy: 0.091816
lr 1.000000e-09 reg 5.000000e+06 train accuracy: 0.368000 val accuracy: 0.357367
lr 1.000000e-08 reg 5.000000e+04 train accuracy: 0.120000 val accuracy: 0.113429
lr 1.000000e-08 reg 5.000000e+05 train accuracy: 0.418000 val accuracy: 0.413735
lr 1.000000e-08 reg 5.000000e+06 train accuracy: 0.412000 val accuracy: 0.409531
lr 1.000000e-07 reg 5.000000e+04 train accuracy: 0.416000 val accuracy: 0.411633
lr 1.000000e-07 reg 5.000000e+05 train accuracy: 0.405000 val accuracy: 0.408592
lr 1.000000e-07 reg 5.000000e+06 train accuracy: 0.373000 val accuracy: 0.380122
best validation accuracy achieved during cross-validation: 0.418000

In [9]:
# Evaluate your trained SVM on the test set
y_test_pred = best_svm.predict(X_test_feats)
test_accuracy = np.mean(y_test == y_test_pred)
print(test_accuracy)


0.425

In [10]:
# An important way to gain intuition about how an algorithm works is to
# visualize the mistakes that it makes. In this visualization, we show examples
# of images that are misclassified by our current system. The first column
# shows images that our system labeled as "plane" but whose true label is
# something other than "plane".

examples_per_class = 8
classes = ['plane', 'car', 'bird', 'cat', 'deer', 'dog', 'frog', 'horse', 'ship', 'truck']
for cls, cls_name in enumerate(classes):
    idxs = np.where((y_test != cls) & (y_test_pred == cls))[0]
    idxs = np.random.choice(idxs, examples_per_class, replace=False)
    for i, idx in enumerate(idxs):
        plt.subplot(examples_per_class, len(classes), i * len(classes) + cls + 1)
        plt.imshow(X_test[idx].astype('uint8'))
        plt.axis('off')
        if i == 0:
            plt.title(cls_name)
plt.show()


Inline question 1:

Describe the misclassification results that you see. Do they make sense?

It makes sense given that we are using color histogram features, so for some results the background seems to affect. For example, blue background/flat background for a plane, trucks as cars (street + background) and the other way, etc.

Neural Network on image features

Earlier in this assigment we saw that training a two-layer neural network on raw pixels achieved better classification performance than linear classifiers on raw pixels. In this notebook we have seen that linear classifiers on image features outperform linear classifiers on raw pixels.

For completeness, we should also try training a neural network on image features. This approach should outperform all previous approaches: you should easily be able to achieve over 55% classification accuracy on the test set; our best model achieves about 60% classification accuracy.


In [11]:
print(X_train_feats.shape)


(49000, 155)

In [32]:
from cs231n.classifiers.neural_net import TwoLayerNet

input_dim = X_train_feats.shape[1]
hidden_dim = 500
num_classes = 10

best_net = None

################################################################################
# TODO: Train a two-layer neural network on image features. You may want to    #
# cross-validate various parameters as in previous sections. Store your best   #
# model in the best_net variable.                                              #
################################################################################
learning_rates = np.arange(0.1, 1.6, 0.1)
regularization_params = [1e-5, 1e-4, 1e-3, 1e-2, 1e-1, 1]
results = {}
best_val_accuracy = 0

for lr in learning_rates:
    for reg in regularization_params:
        net = TwoLayerNet(input_dim, hidden_dim, num_classes)
        stats = net.train(X_train_feats, y_train, X_val_feats, y_val, num_iters=2000, batch_size=200,
                          learning_rate=lr, learning_rate_decay=0.95, reg=reg)
        val_accuracy = (net.predict(X_val_feats) == y_val).mean()
        if val_accuracy > best_val_accuracy:
            best_val_accuracy = val_accuracy
            best_net = net         
        print('LR: {0} REG: {1} ACC: {2}'.format(lr, reg,  val_accuracy))
    
print('best validation accuracy achieved during cross-validation: {0}'.format(best_val_accuracy))
net = best_net
################################################################################
#                              END OF YOUR CODE                                #
################################################################################


LR: 0.1 REG: 1e-05 ACC: 0.539
LR: 0.1 REG: 0.0001 ACC: 0.539
LR: 0.1 REG: 0.001 ACC: 0.537
LR: 0.1 REG: 0.01 ACC: 0.516
LR: 0.1 REG: 0.1 ACC: 0.449
LR: 0.1 REG: 1 ACC: 0.087
LR: 0.2 REG: 1e-05 ACC: 0.579
LR: 0.2 REG: 0.0001 ACC: 0.593
LR: 0.2 REG: 0.001 ACC: 0.584
LR: 0.2 REG: 0.01 ACC: 0.543
LR: 0.2 REG: 0.1 ACC: 0.432
LR: 0.2 REG: 1 ACC: 0.079
LR: 0.30000000000000004 REG: 1e-05 ACC: 0.594
LR: 0.30000000000000004 REG: 0.0001 ACC: 0.586
LR: 0.30000000000000004 REG: 0.001 ACC: 0.581
LR: 0.30000000000000004 REG: 0.01 ACC: 0.528
LR: 0.30000000000000004 REG: 0.1 ACC: 0.413
LR: 0.30000000000000004 REG: 1 ACC: 0.078
LR: 0.4 REG: 1e-05 ACC: 0.596
LR: 0.4 REG: 0.0001 ACC: 0.589
LR: 0.4 REG: 0.001 ACC: 0.598
LR: 0.4 REG: 0.01 ACC: 0.55
LR: 0.4 REG: 0.1 ACC: 0.433
LR: 0.4 REG: 1 ACC: 0.079
LR: 0.5 REG: 1e-05 ACC: 0.59
LR: 0.5 REG: 0.0001 ACC: 0.591
LR: 0.5 REG: 0.001 ACC: 0.588
LR: 0.5 REG: 0.01 ACC: 0.52
LR: 0.5 REG: 0.1 ACC: 0.402
LR: 0.5 REG: 1 ACC: 0.105
LR: 0.6 REG: 1e-05 ACC: 0.579
LR: 0.6 REG: 0.0001 ACC: 0.58
LR: 0.6 REG: 0.001 ACC: 0.596
LR: 0.6 REG: 0.01 ACC: 0.537
LR: 0.6 REG: 0.1 ACC: 0.407
LR: 0.6 REG: 1 ACC: 0.079
LR: 0.7000000000000001 REG: 1e-05 ACC: 0.588
LR: 0.7000000000000001 REG: 0.0001 ACC: 0.583
LR: 0.7000000000000001 REG: 0.001 ACC: 0.582
LR: 0.7000000000000001 REG: 0.01 ACC: 0.541
LR: 0.7000000000000001 REG: 0.1 ACC: 0.409
LR: 0.7000000000000001 REG: 1 ACC: 0.078
LR: 0.8 REG: 1e-05 ACC: 0.554
LR: 0.8 REG: 0.0001 ACC: 0.581
LR: 0.8 REG: 0.001 ACC: 0.593
LR: 0.8 REG: 0.01 ACC: 0.53
LR: 0.8 REG: 0.1 ACC: 0.376
LR: 0.8 REG: 1 ACC: 0.098
LR: 0.9 REG: 1e-05 ACC: 0.571
LR: 0.9 REG: 0.0001 ACC: 0.576
LR: 0.9 REG: 0.001 ACC: 0.583
LR: 0.9 REG: 0.01 ACC: 0.538
LR: 0.9 REG: 0.1 ACC: 0.427
LR: 0.9 REG: 1 ACC: 0.098
LR: 1.0 REG: 1e-05 ACC: 0.574
LR: 1.0 REG: 0.0001 ACC: 0.56
LR: 1.0 REG: 0.001 ACC: 0.589
LR: 1.0 REG: 0.01 ACC: 0.534
LR: 1.0 REG: 0.1 ACC: 0.411
LR: 1.0 REG: 1 ACC: 0.107
LR: 1.1 REG: 1e-05 ACC: 0.568
LR: 1.1 REG: 0.0001 ACC: 0.587
LR: 1.1 REG: 0.001 ACC: 0.578
LR: 1.1 REG: 0.01 ACC: 0.525
LR: 1.1 REG: 0.1 ACC: 0.369
LR: 1.1 REG: 1 ACC: 0.113
LR: 1.2000000000000002 REG: 1e-05 ACC: 0.562
LR: 1.2000000000000002 REG: 0.0001 ACC: 0.564
LR: 1.2000000000000002 REG: 0.001 ACC: 0.574
LR: 1.2000000000000002 REG: 0.01 ACC: 0.514
LR: 1.2000000000000002 REG: 0.1 ACC: 0.374
LR: 1.2000000000000002 REG: 1 ACC: 0.107
LR: 1.3000000000000003 REG: 1e-05 ACC: 0.566
LR: 1.3000000000000003 REG: 0.0001 ACC: 0.572
LR: 1.3000000000000003 REG: 0.001 ACC: 0.576
LR: 1.3000000000000003 REG: 0.01 ACC: 0.532
LR: 1.3000000000000003 REG: 0.1 ACC: 0.419
LR: 1.3000000000000003 REG: 1 ACC: 0.078
LR: 1.4000000000000001 REG: 1e-05 ACC: 0.558
LR: 1.4000000000000001 REG: 0.0001 ACC: 0.555
LR: 1.4000000000000001 REG: 0.001 ACC: 0.571
LR: 1.4000000000000001 REG: 0.01 ACC: 0.484
LR: 1.4000000000000001 REG: 0.1 ACC: 0.359
LR: 1.4000000000000001 REG: 1 ACC: 0.087
LR: 1.5000000000000002 REG: 1e-05 ACC: 0.567
LR: 1.5000000000000002 REG: 0.0001 ACC: 0.566
LR: 1.5000000000000002 REG: 0.001 ACC: 0.571
LR: 1.5000000000000002 REG: 0.01 ACC: 0.49
LR: 1.5000000000000002 REG: 0.1 ACC: 0.371
LR: 1.5000000000000002 REG: 1 ACC: 0.098
best validation accuracy achieved during cross-validation: 0.598

In [33]:
# Run your neural net classifier on the test set. You should be able to
# get more than 55% accuracy.

test_acc = (best_net.predict(X_test_feats) == y_test).mean()
print(test_acc)


0.594

Bonus: Design your own features!

You have seen that simple image features can improve classification performance. So far we have tried HOG and color histograms, but other types of features may be able to achieve even better classification performance.

For bonus points, design and implement a new type of feature and use it for image classification on CIFAR-10. Explain how your feature works and why you expect it to be useful for image classification. Implement it in this notebook, cross-validate any hyperparameters, and compare its performance to the HOG + Color histogram baseline.

Bonus: Do something extra!

Use the material and code we have presented in this assignment to do something interesting. Was there another question we should have asked? Did any cool ideas pop into your head as you were working on the assignment? This is your chance to show off!