# Softmax 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.

This exercise is analogous to the SVM exercise. You will:

• implement a fully-vectorized loss function for the Softmax classifier
• implement the fully-vectorized expression for its analytic gradient
• use a validation set to tune the learning rate and regularization strength
• optimize the loss function with SGD
• visualize the final learned weights
``````

In :

import random
import numpy as np
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'

``````
``````

In :

def get_CIFAR10_data(num_training=49000, num_validation=1000, num_test=1000, num_dev=500):
"""
Load the CIFAR-10 dataset from disk and perform preprocessing to prepare
it for the linear classifier. These are the same steps as we used for the
SVM, but condensed to a single function.
"""
# 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))

# Preprocessing: reshape the image data into rows
X_train = np.reshape(X_train, (X_train.shape, -1))
X_val = np.reshape(X_val, (X_val.shape, -1))
X_test = np.reshape(X_test, (X_test.shape, -1))
X_dev = np.reshape(X_dev, (X_dev.shape, -1))

# Normalize the data: subtract the mean image
mean_image = np.mean(X_train, axis = 0)
X_train -= mean_image
X_val -= mean_image
X_test -= mean_image
X_dev -= mean_image

# add bias dimension and transform into columns
X_train = np.hstack([X_train, np.ones((X_train.shape, 1))])
X_val = np.hstack([X_val, np.ones((X_val.shape, 1))])
X_test = np.hstack([X_test, np.ones((X_test.shape, 1))])
X_dev = np.hstack([X_dev, np.ones((X_dev.shape, 1))])

return X_train, y_train, X_val, y_val, X_test, y_test, X_dev, y_dev

# Invoke the above function to get our data.
X_train, y_train, X_val, y_val, X_test, y_test, X_dev, y_dev = get_CIFAR10_data()
print('Train data shape: ', X_train.shape)
print('Train labels shape: ', y_train.shape)
print('Validation data shape: ', X_val.shape)
print('Validation labels shape: ', y_val.shape)
print('Test data shape: ', X_test.shape)
print('Test labels shape: ', y_test.shape)
print('dev data shape: ', X_dev.shape)
print('dev labels shape: ', y_dev.shape)

``````
``````

Train data shape:  (49000, 3073)
Train labels shape:  (49000,)
Validation data shape:  (1000, 3073)
Validation labels shape:  (1000,)
Test data shape:  (1000, 3073)
Test labels shape:  (1000,)
dev data shape:  (500, 3073)
dev labels shape:  (500,)

``````

## Softmax Classifier

Your code for this section will all be written inside cs231n/classifiers/softmax.py.

``````

In :

# First implement the naive softmax loss function with nested loops.
# Open the file cs231n/classifiers/softmax.py and implement the
# softmax_loss_naive function.

from cs231n.classifiers.softmax import softmax_loss_naive
import time

# Generate a random softmax weight matrix and use it to compute the loss.
W = np.random.randn(3073, 10) * 0.0001
loss, grad = softmax_loss_naive(W, X_dev, y_dev, 0.0)

# As a rough sanity check, our loss should be something close to -log(0.1).
print('loss: %f' % loss)
print('sanity check: %f' % (-np.log(0.1)))

``````
``````

loss: 2.313761
sanity check: 2.302585

``````

## Inline Question 1:

Why do we expect our loss to be close to -log(0.1)? Explain briefly.**

Your answer: Because all weight initialize as small normal distributed numbers. So all class scores will be small numbers that almost the same. So probalilities for each class will will distibutet uniform ~log(1/C), where C - number of classes.

``````

In :

# Complete the implementation of softmax_loss_naive and implement a (naive)
# version of the gradient that uses nested loops.
loss, grad = softmax_loss_naive(W, X_dev, y_dev, 0.0)

# As we did for the SVM, use numeric gradient checking as a debugging tool.
f = lambda w: softmax_loss_naive(w, X_dev, y_dev, 0.0)

# similar to SVM case, do another gradient check with regularization
loss, grad = softmax_loss_naive(W, X_dev, y_dev, 5e1)
f = lambda w: softmax_loss_naive(w, X_dev, y_dev, 5e1)

``````
``````

numerical: -1.790949 analytic: -1.790949, relative error: 1.752995e-08
numerical: 0.349373 analytic: 0.349373, relative error: 3.179683e-08
numerical: 0.142426 analytic: 0.142426, relative error: 1.111935e-07
numerical: 3.464696 analytic: 3.464696, relative error: 3.322329e-09
numerical: -0.395100 analytic: -0.395101, relative error: 1.189373e-07
numerical: -0.895227 analytic: -0.895227, relative error: 7.356911e-09
numerical: 0.576761 analytic: 0.576761, relative error: 3.457294e-08
numerical: 1.553783 analytic: 1.553783, relative error: 2.037480e-08
numerical: 1.721440 analytic: 1.721440, relative error: 2.794162e-08
numerical: -2.676360 analytic: -2.676360, relative error: 9.616563e-09
numerical: 0.601433 analytic: 0.601434, relative error: 7.928276e-08
numerical: 0.657779 analytic: 0.657779, relative error: 8.982156e-08
numerical: -0.090357 analytic: -0.090357, relative error: 2.673495e-07
numerical: 2.007323 analytic: 2.007323, relative error: 7.212294e-09
numerical: -0.396097 analytic: -0.396097, relative error: 1.630474e-08
numerical: 0.497768 analytic: 0.497767, relative error: 1.137572e-07
numerical: -0.309478 analytic: -0.309478, relative error: 4.983903e-08
numerical: -0.280136 analytic: -0.280136, relative error: 7.839923e-08
numerical: 0.923927 analytic: 0.923927, relative error: 5.453143e-08
numerical: -0.000968 analytic: -0.000968, relative error: 5.335570e-05

``````
``````

In :

# Now that we have a naive implementation of the softmax loss function and its gradient,
# implement a vectorized version in softmax_loss_vectorized.
# The two versions should compute the same results, but the vectorized version should be
# much faster.
tic = time.time()
loss_naive, grad_naive = softmax_loss_naive(W, X_dev, y_dev, 0.000005)
toc = time.time()
print('naive loss: %e computed in %fs' % (loss_naive, toc - tic))

from cs231n.classifiers.softmax import softmax_loss_vectorized
tic = time.time()
loss_vectorized, grad_vectorized = softmax_loss_vectorized(W, X_dev, y_dev, 0.000005)
toc = time.time()
print('vectorized loss: %e computed in %fs' % (loss_vectorized, toc - tic))

# As we did for the SVM, we use the Frobenius norm to compare the two versions
print('Loss difference: %f' % np.abs(loss_naive - loss_vectorized))

``````
``````

naive loss: 2.313761e+00 computed in 0.145764s
vectorized loss: 2.313761e+00 computed in 0.006911s
Loss difference: 0.000000

``````
``````

In :

# Use the validation set to tune hyperparameters (regularization strength and
# learning rate). You should experiment with different ranges for the learning
# rates and regularization strengths; if you are careful you should be able to
# get a classification accuracy of over 0.35 on the validation set.
from cs231n.classifiers import Softmax
results = {}
best_val = -1
best_softmax = None
learning_rates = [1e-7, 5e-7, 1e-6]
regularization_strengths = [2.5e4, 5e4, 1e4]

################################################################################
# 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 softmax classifer in best_softmax.                          #
################################################################################

for lr in learning_rates:
for rg in regularization_strengths:
model = Softmax()
model.train(X_train, y_train, learning_rate=lr, reg=rg,
num_iters=1000, verbose=False)

y_train_pred = model.predict(X_train)
train_acc = np.mean(y_train == y_train_pred)

y_val_pred = model.predict(X_val)
val_acc = np.mean(y_val == y_val_pred)

if val_acc > best_val:
best_val = val_acc
best_softmax = model
results[(lr, rg)] = (train_acc, val_acc)

################################################################################
#                              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)

``````
``````

lr 1.000000e-07 reg 1.000000e+04 train accuracy: 0.334531 val accuracy: 0.350000
lr 1.000000e-07 reg 2.500000e+04 train accuracy: 0.328918 val accuracy: 0.341000
lr 1.000000e-07 reg 5.000000e+04 train accuracy: 0.304755 val accuracy: 0.323000
lr 5.000000e-07 reg 1.000000e+04 train accuracy: 0.354449 val accuracy: 0.362000
lr 5.000000e-07 reg 2.500000e+04 train accuracy: 0.321347 val accuracy: 0.341000
lr 5.000000e-07 reg 5.000000e+04 train accuracy: 0.301551 val accuracy: 0.317000
lr 1.000000e-06 reg 1.000000e+04 train accuracy: 0.344163 val accuracy: 0.352000
lr 1.000000e-06 reg 2.500000e+04 train accuracy: 0.330102 val accuracy: 0.351000
lr 1.000000e-06 reg 5.000000e+04 train accuracy: 0.286898 val accuracy: 0.286000
best validation accuracy achieved during cross-validation: 0.362000

``````
``````

In :

# evaluate on test set
# Evaluate the best softmax on test set
y_test_pred = best_softmax.predict(X_test)
test_accuracy = np.mean(y_test == y_test_pred)
print('softmax on raw pixels final test set accuracy: %f' % (test_accuracy, ))

``````
``````

softmax on raw pixels final test set accuracy: 0.368000

``````
``````

In :

# Visualize the learned weights for each class
w = best_softmax.W[:-1,:] # strip out the bias
w = w.reshape(32, 32, 3, 10)

w_min, w_max = np.min(w), np.max(w)

classes = ['plane', 'car', 'bird', 'cat', 'deer', 'dog', 'frog', 'horse', 'ship', 'truck']
for i in range(10):
plt.subplot(2, 5, i + 1)

# Rescale the weights to be between 0 and 255
wimg = 255.0 * (w[:, :, :, i].squeeze() - w_min) / (w_max - w_min)
plt.imshow(wimg.astype('uint8'))
plt.axis('off')
plt.title(classes[i])

``````
``````

``````
``````

In [ ]:

``````