#!/usr/bin/env python
# coding: utf-8
#
Building A Handwritten Digits Classifier
#
# ![Classifier.jpg](attachment:Classifier.jpg)
# # Image Classification
#
# **Why is image classification a hard task?**
#
# Within the field of machine learning and pattern recognition, image classification (especially for handwritten text) is towards the difficult end of the spectrum. There are a few reasons for this.
#
# First, each image in a training set is high dimensional. Each pixel in an image is a feature and a separate column. This means that a 128 x 128 image has 16384 features.
#
# Second, images are often downsampled to lower resolutions and transformed to grayscale (no color). This is a limitation of compute power unfortunately. The resolution of a 8 megapixel photo has 3264 by 2448 pixels, for a total of 7,990,272 features (or about 8 million). Images of this resolution are usually scaled down to between 128 and 512 pixels in either direction for significantly faster processing. This often results in a loss of detail that's available for training and pattern matching.
#
# Third, the features in an image don't have an obvious linear or nonlinear relationship that can be learned with a model like linear or logistic regression. In grayscale, each pixel is just represented as a brightness value ranging from 0 to 256.
# ## Project Goal
#
# In this Guided Project, I'll explore the effectiveness of deep, feedforward neural networks at classifying images as follows:
#
# - explore why image classification is a hard task
# - observe the limitations of traditional machine learning models for image classification
# - train, test, and improve a few different deep neural networks for image classification
# In[1]:
# import key python libraries to perform desired excutions.
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
get_ipython().run_line_magic('matplotlib', 'inline')
from matplotlib.ticker import FormatStrFormatter
from sklearn.datasets import load_digits
from sklearn.neighbors import KNeighborsClassifier
from sklearn.model_selection import KFold
from sklearn.neural_network import MLPClassifier
from sklearn.metrics import accuracy_score
import warnings
warnings.filterwarnings('ignore')
digits_data = load_digits()
# ## Download Dataset
#
# Rather than using the pd.read_csv command, I will use the load_digits() function shown below to return a copy of the hand-written digits dataset from UCI (University of California, Irvine). The dataset is available to observe [here.](https://archive.ics.uci.edu/ml/datasets/optical+recognition+of+handwritten+digits 'here')
# In[2]:
digits_data.keys()
labels = pd.Series(digits_data['target'])
data = pd.DataFrame(digits_data['data'])
data.head(1)
# In[3]:
first_image = data.iloc[0]
np_image = first_image.values
np_image = np_image.reshape(8,8)
plt.imshow(np_image, cmap='gray_r')
# In[4]:
f, axarr = plt.subplots(2, 4)
axarr[0, 0].imshow(data.iloc[0].values.reshape(8,8), cmap='gray_r')
axarr[0, 1].imshow(data.iloc[99].values.reshape(8,8), cmap='gray_r')
axarr[0, 2].imshow(data.iloc[199].values.reshape(8,8), cmap='gray_r')
axarr[0, 3].imshow(data.iloc[299].values.reshape(8,8), cmap='gray_r')
axarr[1, 0].imshow(data.iloc[999].values.reshape(8,8), cmap='gray_r')
axarr[1, 1].imshow(data.iloc[1099].values.reshape(8,8), cmap='gray_r')
axarr[1, 2].imshow(data.iloc[1199].values.reshape(8,8), cmap='gray_r')
axarr[1, 3].imshow(data.iloc[1299].values.reshape(8,8), cmap='gray_r')
# ## K-Nearest Neighbors Model
# In[5]:
# 50% Train / test validation
def train_knn(nneighbors, train_features, train_labels):
knn = KNeighborsClassifier(n_neighbors = nneighbors)
knn.fit(train_features, train_labels)
return knn
def test(model, test_features, test_labels):
predictions = model.predict(test_features)
train_test_df = pd.DataFrame()
train_test_df['correct_label'] = test_labels
train_test_df['predicted_label'] = predictions
overall_accuracy = sum(train_test_df["predicted_label"] == train_test_df["correct_label"])/len(train_test_df)
return overall_accuracy
def cross_validate(k):
fold_accuracies = []
kf = KFold(n_splits = 4, random_state=2, shuffle=True)
for train_index, test_index in kf.split(data):
train_features, test_features = data.loc[train_index], data.loc[test_index]
train_labels, test_labels = labels.loc[train_index], labels.loc[test_index]
model = train_knn(k, train_features, train_labels)
overall_accuracy = test(model, test_features, test_labels)
fold_accuracies.append(overall_accuracy)
return fold_accuracies
for n in range(1,5):
knn_one_accuracies = cross_validate(n)
acc_mean = np.mean(knn_one_accuracies)
print('{:.5f}'.format(acc_mean))
# In[6]:
k_values = list(range(1,10))
k_overall_accuracies = []
for k in k_values:
k_accuracies = cross_validate(k)
k_mean_accuracy = np.mean(k_accuracies)
k_overall_accuracies.append(k_mean_accuracy)
fig,ax = plt.subplots(figsize=(11,7))
plt.title('Mean Accuracy vs. k-value', fontsize=22, pad=15)
plt.plot(k_values, k_overall_accuracies, linewidth=2, c='blue')
plt.xlabel('k-value', fontsize=20, labelpad=15)
plt.ylabel('Mean Accuracy', fontsize=20, labelpad=15)
plt.xticks(fontsize=16)
plt.yticks(fontsize=16)
ax.yaxis.set_major_formatter(FormatStrFormatter('%.4f'))
plt.show()
# ## Observations
#
# The graph above shows a general downward trend for mean accuracy as k-value increases: not by a large amount, but still a slight reduction in mean accuracy.
# ## Neural Network With One Hidden Layer
# In[7]:
kf = KFold(n_splits = 2, random_state=2, shuffle=True)
for train_index, test_index in kf.split(data):
train_features, test_features = data.loc[train_index], data.loc[test_index]
train_labels, test_labels = labels.loc[train_index], labels.loc[test_index]
accuracies = []
neurons = [8, 16, 32, 64, 128, 256]
for n in neurons:
mlp = MLPClassifier(hidden_layer_sizes=(n,), activation='logistic')
mlp.fit(train_features, train_labels)
nn_predictions = mlp.predict(test_features)
accuracy = accuracy_score(test_labels, nn_predictions)
accuracies.append(accuracy)
fig,ax = plt.subplots(figsize=(11,7))
ax.yaxis.grid(linestyle='dashed', color='grey', alpha=0.4) # horizontal lines
x = neurons
y = accuracies
plt.title('Model Accuracy vs. Neuron Quantity', fontsize=22, pad=20)
plt.plot(x,y, linewidth=2, marker='o')
plt.xlabel('Neuron Quantity', fontsize=18, labelpad=15)
plt.ylabel('Model Accuracy', fontsize=18, labelpad=15)
plt.xticks(fontsize=16)
plt.yticks(fontsize=16)
ax.yaxis.set_major_formatter(FormatStrFormatter('%.4f'))
plt.show()
# ## Observations: Neural Network w/ One Hidden Layer
#
# The graph above shows me that the slope of model accuracy is very high when increasing from low quanities of neurons and then tapers off and levels out.
#
# The magnitude of model accuracy reaches at least 98%.
# ## Neural Network With Two Hidden Layers
# In[8]:
# 50% train / test validation
def train_nn(neuron_arch, train_features, train_labels):
mlp = MLPClassifier(hidden_layer_sizes=neuron_arch)
mlp.fit(train_features, train_labels)
return mlp
# cross-validation
def cross_validate(neuron_arch):
fold_accuracies = []
kf = KFold(n_splits = 4, random_state=2, shuffle=True)
for train_index, test_index in kf.split(data):
train_features, test_features = data.loc[train_index], data.loc[test_index]
train_labels, test_labels = labels.loc[train_index], labels.loc[test_index]
model = train_nn(neuron_arch, train_features, train_labels)
overall_accuracy = test(model, test_features, test_labels)
fold_accuracies.append(overall_accuracy)
return fold_accuracies
nn_two_neurons = [
(64,64),
(128, 128),
(256, 256)]
nn_two_accuracies = []
for n in nn_two_neurons:
nn_accuracies = cross_validate(n)
nn_mean_accuracy = np.mean(nn_accuracies)
nn_two_accuracies.append(nn_mean_accuracy)
fig,ax = plt.subplots(figsize=(11,7))
ax.yaxis.grid(linestyle='dashed', color='grey', alpha=0.4) # horizontal lines
plt.title('Mean Accuracy vs. Neurons In Two Hidden Layers', fontsize=22, pad=20)
plt.xlabel('Neuron Quantity', fontsize=18, labelpad=15)
plt.ylabel('Model Accuracy', fontsize=18, labelpad=15)
plt.xticks(fontsize=16)
plt.yticks(fontsize=16)
ax.yaxis.set_major_formatter(FormatStrFormatter('%.4f'))
x = [i[0] for i in nn_two_neurons]
plt.plot(x, nn_two_accuracies, linewidth=2, marker='o')
plt.show()
Q = nn_two_accuracies
fmtL = "nn_two_accuracies = " + ', '.join(["{:.5f}"]*len(Q))
print(fmtL.format(*Q))
# ## Observations: Neural Network w/ Two Hidden Layers
#
# The graph above is much the same in pattern as the one further above for one hidden layer.
#
# The magnitude of model accuracy reaches at least 98%.
# ## Neural Network With Three Hidden Layers
# In[9]:
nn_three_neurons = [
(10,10,10),
(64, 64, 64),
(128, 128, 128)]
nn_three_accuracies = []
for n in nn_three_neurons:
nn_accuracies = cross_validate(n)
nn_mean_accuracy = np.mean(nn_accuracies)
nn_three_accuracies.append(nn_mean_accuracy)
fig,ax = plt.subplots(figsize=(11,7))
ax.yaxis.grid(linestyle='dashed', color='grey', alpha=0.4) # horizontal lines
plt.title('Mean Accuracy vs. Neurons In Three Hidden Layers', fontsize=22, pad=20)
plt.xlabel('Neuron Quantity', fontsize=18, labelpad=15)
plt.ylabel('Model Accuracy', fontsize=18, labelpad=15)
plt.xticks(fontsize=16)
plt.yticks(fontsize=16)
ax.yaxis.set_major_formatter(FormatStrFormatter('%.4f'))
x = [i[0] for i in nn_three_neurons]
plt.plot(x, nn_three_accuracies, linewidth=2, marker='o')
plt.show()
Q2 = nn_three_accuracies
fmtL = "nn_three_accuracies = " + ', '.join(["{:.5f}"]*len(Q2))
print(fmtL.format(*Q2))
# ## Observations: Neural Network w/ Three Hidden Layers
#
# The graph above is much the same in pattern as the one further above for one and two hidden layers. Higher quantity of neuronas increasaes model accuracy.
#
# The magnitude of model accuracy reaches close to 98%.
# ## Conclusions
#
# In using K-Fold Cross Validation and Neural Network along with various parameter settings, number of K-Folds, number of neurons and number of hidden layers, it was very interesting to see how they impacted model accuracy.
#
# In general, the model accuracy with the right combination of parameter settings was very high, reaching 98% which is very impressive.