In [1]:
# Step 1: Import all the tools we need
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Input, Conv2D, MaxPooling2D, Flatten, Dense, Dropout
from tensorflow.keras.utils import to_categorical
/Users/saroshbaig/Library/Python/3.9/lib/python/site-packages/urllib3/__init__.py:35: NotOpenSSLWarning: urllib3 v2 only supports OpenSSL 1.1.1+, currently the 'ssl' module is compiled with 'LibreSSL 2.8.3'. See: https://github.com/urllib3/urllib3/issues/3020 warnings.warn(
In [2]:
# Step 2: Load the training and test data
train_data = pd.read_csv("sign_mnist_train.csv")
test_data = pd.read_csv("sign_mnist_test.csv")
# Step 3: Split the data into images (X) and labels (y)
X_train = train_data.drop("label", axis=1).values
y_train = train_data["label"].values
X_test = test_data.drop("label", axis=1).values
y_test = test_data["label"].values
In [3]:
# Step 4: Reshape the images
# Turn flat images into 28x28 pixels, with 1 color channel (grayscale)
X_train = X_train.reshape(-1, 28, 28, 1)
X_test = X_test.reshape(-1, 28, 28, 1)
# Step 5: Convert pixel values to smaller decimal numbers (0 to 1)
X_train = X_train.astype("float32") / 255
X_test = X_test.astype("float32") / 255
# Display the first 10 reshaped images
import matplotlib.pyplot as plt
plt.figure(figsize=(10, 5))
for i in range(10):
image = X_train[i].reshape(28, 28) # Remove channel dim for display
plt.subplot(2, 5, i + 1)
plt.imshow(image, cmap="gray")
plt.title(f"Label: {y_train[i]}")
plt.axis("off")
plt.tight_layout()
plt.show()
In [4]:
# Step 6: Turn the labels into one-hot encoded format
y_train_cat = to_categorical(y_train, num_classes=25) # 25 classes (A-Y, no J or Z)
y_test_cat = to_categorical(y_test, num_classes=25)
In [5]:
# Step 7: Build the CNN model
model = Sequential([
Input(shape=(28, 28, 1)), # Input layer (28x28 image, 1 channel)
Conv2D(32, (3, 3), activation='relu'), # First convolution layer
MaxPooling2D(pool_size=(2, 2)), # First max pooling layer
Conv2D(64, (3, 3), activation='relu'), # Second convolution layer
MaxPooling2D(pool_size=(2, 2)), # Second max pooling layer
Flatten(), # Flatten image to 1D list
Dense(128, activation='relu'), # Fully connected layer
Dropout(0.3), # Dropout to prevent overfitting
Dense(25, activation='softmax') # Output layer (25 classes)
])
In [6]:
# Step 8: Compile the model
model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy'])
# Show the model structure
model.summary()
Model: "sequential"
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┓ ┃ Layer (type) ┃ Output Shape ┃ Param # ┃ ┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━┩ │ conv2d (Conv2D) │ (None, 26, 26, 32) │ 320 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ max_pooling2d (MaxPooling2D) │ (None, 13, 13, 32) │ 0 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ conv2d_1 (Conv2D) │ (None, 11, 11, 64) │ 18,496 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ max_pooling2d_1 (MaxPooling2D) │ (None, 5, 5, 64) │ 0 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ flatten (Flatten) │ (None, 1600) │ 0 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ dense (Dense) │ (None, 128) │ 204,928 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ dropout (Dropout) │ (None, 128) │ 0 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ dense_1 (Dense) │ (None, 25) │ 3,225 │ └─────────────────────────────────┴────────────────────────┴───────────────┘
Total params: 226,969 (886.60 KB)
Trainable params: 226,969 (886.60 KB)
Non-trainable params: 0 (0.00 B)
In [7]:
# Step 9: Train the model
history = model.fit(
X_train, y_train_cat,
epochs=10,
batch_size=64,
validation_data=(X_test, y_test_cat),
verbose=2
)
Epoch 1/10 429/429 - 5s - 13ms/step - accuracy: 0.5601 - loss: 1.4526 - val_accuracy: 0.8270 - val_loss: 0.5311 Epoch 2/10 429/429 - 5s - 11ms/step - accuracy: 0.9026 - loss: 0.3060 - val_accuracy: 0.8889 - val_loss: 0.3336 Epoch 3/10 429/429 - 5s - 11ms/step - accuracy: 0.9639 - loss: 0.1220 - val_accuracy: 0.9244 - val_loss: 0.2528 Epoch 4/10 429/429 - 5s - 11ms/step - accuracy: 0.9784 - loss: 0.0723 - val_accuracy: 0.9235 - val_loss: 0.2704 Epoch 5/10 429/429 - 5s - 11ms/step - accuracy: 0.9858 - loss: 0.0490 - val_accuracy: 0.9257 - val_loss: 0.2412 Epoch 6/10 429/429 - 5s - 11ms/step - accuracy: 0.9879 - loss: 0.0405 - val_accuracy: 0.9310 - val_loss: 0.2784 Epoch 7/10 429/429 - 5s - 11ms/step - accuracy: 0.9906 - loss: 0.0298 - val_accuracy: 0.9381 - val_loss: 0.2266 Epoch 8/10 429/429 - 5s - 11ms/step - accuracy: 0.9936 - loss: 0.0227 - val_accuracy: 0.9363 - val_loss: 0.2680 Epoch 9/10 429/429 - 5s - 12ms/step - accuracy: 0.9942 - loss: 0.0202 - val_accuracy: 0.9387 - val_loss: 0.2922 Epoch 10/10 429/429 - 5s - 13ms/step - accuracy: 0.9920 - loss: 0.0244 - val_accuracy: 0.9522 - val_loss: 0.2701
In [8]:
# Step 11: See the predictions on the first 10 images
predictions = model.predict(X_test)
predicted_labels = np.argmax(predictions, axis=1)
# Show the first 10 test images with predicted and actual labels
plt.figure(figsize=(12, 6))
for i in range(10):
plt.subplot(2, 5, i + 1)
plt.imshow(X_test[i].reshape(28, 28), cmap='gray')
plt.title(f"Pred: {predicted_labels[i]}\nTrue: {y_test[i]}")
plt.axis('off')
plt.tight_layout()
plt.show()
225/225 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step