Digit Recognizer
Download public Digit Recognizer train/test CSVs, visualize flattened digit images, train a classifier, validate accuracy, and create submission.csv.
Final deliverable: A complete Digit Recognizer notebook with validation metrics and ImageId/Label submission.csv.
Dataset: Digit Recognizer public CSV mirror
The notebook downloads public train.csv and test.csv mirrors for the Kaggle Digit Recognizer task. Rows contain labels plus 784 pixel columns.
- pixel0 through pixel783: grayscale pixel intensity values
- label: digit label from 0 to 9 in train.csv
- pixel0 through pixel783: grayscale pixel intensity values
Download Digit Recognizer Data
Download public train.csv and test.csv files without Kaggle credentials.
Run the public download cell and confirm train.csv and test.csv exist.
# Run this cell in Colab, Kaggle, or a local notebook.
# It downloads public dataset files directly, so you do not need Kaggle credentials.
from pathlib import Path
from urllib.request import urlretrieve
import zipfile
DATA_DIR = Path("data/digit-recognizer")
DATA_DIR.mkdir(parents=True, exist_ok=True)
def find_file(name):
possible_roots = [DATA_DIR, Path("/kaggle/input")]
for root in possible_roots:
if root.exists():
matches = list(root.rglob(name))
if matches:
return matches[0]
raise FileNotFoundError(f"Could not find {name}. Run the dataset download cell first.")
files = {
"train.csv": "https://raw.githubusercontent.com/wehrley/Kaggle-Digit-Recognizer/master/train.csv",
"test.csv": "https://raw.githubusercontent.com/wehrley/Kaggle-Digit-Recognizer/master/test.csv"
}
for filename, url in files.items():
target = DATA_DIR / filename
if target.exists() and target.stat().st_size > 0:
print(f"already exists: {target}")
continue
print(f"downloading {filename} ...")
urlretrieve(url, target)
print(f"saved: {target}")
for zip_path in DATA_DIR.glob("*.zip"):
extract_dir = DATA_DIR / zip_path.stem
extract_dir.mkdir(exist_ok=True)
if any(extract_dir.iterdir()):
print(f"already extracted: {extract_dir}")
continue
with zipfile.ZipFile(zip_path, "r") as zf:
zf.extractall(extract_dir)
print(f"extracted: {zip_path} -> {extract_dir}")
print("Files:")
all_files = [path for path in sorted(DATA_DIR.rglob("*")) if path.is_file()]
for path in all_files[:40]:
print(path)
if len(all_files) > 40:
print(f"... {len(all_files) - 40} more files not shown")Show reference solution
# Run this cell in Colab, Kaggle, or a local notebook.
# It downloads public dataset files directly, so you do not need Kaggle credentials.
from pathlib import Path
from urllib.request import urlretrieve
import zipfile
DATA_DIR = Path("data/digit-recognizer")
DATA_DIR.mkdir(parents=True, exist_ok=True)
def find_file(name):
possible_roots = [DATA_DIR, Path("/kaggle/input")]
for root in possible_roots:
if root.exists():
matches = list(root.rglob(name))
if matches:
return matches[0]
raise FileNotFoundError(f"Could not find {name}. Run the dataset download cell first.")
files = {
"train.csv": "https://raw.githubusercontent.com/wehrley/Kaggle-Digit-Recognizer/master/train.csv",
"test.csv": "https://raw.githubusercontent.com/wehrley/Kaggle-Digit-Recognizer/master/test.csv"
}
for filename, url in files.items():
target = DATA_DIR / filename
if target.exists() and target.stat().st_size > 0:
print(f"already exists: {target}")
continue
print(f"downloading {filename} ...")
urlretrieve(url, target)
print(f"saved: {target}")
for zip_path in DATA_DIR.glob("*.zip"):
extract_dir = DATA_DIR / zip_path.stem
extract_dir.mkdir(exist_ok=True)
if any(extract_dir.iterdir()):
print(f"already extracted: {extract_dir}")
continue
with zipfile.ZipFile(zip_path, "r") as zf:
zf.extractall(extract_dir)
print(f"extracted: {zip_path} -> {extract_dir}")
print("Files:")
all_files = [path for path in sorted(DATA_DIR.rglob("*")) if path.is_file()]
for path in all_files[:40]:
print(path)
if len(all_files) > 40:
print(f"... {len(all_files) - 40} more files not shown")This keeps the notebook credential-free while matching the Kaggle submission shape.
Load and Inspect Pixels
Read train.csv and separate labels from pixel features.
Load train.csv, print shape, and inspect label distribution.
from pathlib import Path
import pandas as pd
possible_roots = [
Path("data/digit-recognizer"),
Path("/kaggle/input"),
]
def find_file(name):
for root in possible_roots:
if root.exists():
matches = list(root.rglob(name))
if matches:
return matches[0]
raise FileNotFoundError(f"Could not find {name}. Run the dataset download/load cell first.")
train_path = find_file("train.csv")
df = pd.read_csv(train_path)
print(train_path)
print(df.shape)
df.head()
# Separate y and X from the training DataFrame
# Check label countsShow reference solution
from pathlib import Path
import pandas as pd
possible_roots = [
Path("data/digit-recognizer"),
Path("/kaggle/input"),
]
def find_file(name):
for root in possible_roots:
if root.exists():
matches = list(root.rglob(name))
if matches:
return matches[0]
raise FileNotFoundError(f"Could not find {name}. Run the dataset download/load cell first.")
train_path = find_file("train.csv")
df = pd.read_csv(train_path)
print(train_path)
print(df.shape)
df.head()
import numpy as np
import matplotlib.pyplot as plt
y = df["label"].astype(int)
X = df.drop(columns=["label"])
print(X.shape, y.shape)
display(y.value_counts().sort_index())Each row is a flattened 28x28 image. Separating X and y makes the supervised learning setup explicit.
Visualize Handwritten Digits
Reshape flat pixel rows into 28x28 images and plot examples.
Plot a small grid of digit images with their labels.
# Pick a few rows # Reshape each row to 28x28 # Plot images
Show reference solution
fig, axes = plt.subplots(2, 5, figsize=(8, 4))
for ax, idx in zip(axes.ravel(), range(10)):
image = X.iloc[idx].values.reshape(28, 28)
ax.imshow(image, cmap="gray")
ax.set_title(f"label={y.iloc[idx]}")
ax.axis("off")
plt.tight_layout()
plt.show()Visualizing images catches data-loading mistakes immediately. If the digits look scrambled, reshaping or column order is wrong.
Train a Baseline Classifier
Train a practical baseline model on normalized pixel values.
Create a validation split, scale pixels to 0-1, train a RandomForestClassifier, and measure accuracy.
from sklearn.model_selection import train_test_split from sklearn.ensemble import RandomForestClassifier from sklearn.metrics import accuracy_score, classification_report # Split data # Normalize pixel values # Train model and evaluate
Show reference solution
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import accuracy_score, classification_report
X_train, X_val, y_train, y_val = train_test_split(
X, y, test_size=0.2, random_state=42, stratify=y
)
X_train_scaled = X_train / 255.0
X_val_scaled = X_val / 255.0
model = RandomForestClassifier(n_estimators=150, random_state=42, n_jobs=-1)
model.fit(X_train_scaled, y_train)
val_preds = model.predict(X_val_scaled)
print("accuracy:", accuracy_score(y_val, val_preds))
print(classification_report(y_val, val_preds))Accuracy is intuitive for balanced digit classification, but the class report helps catch digits the model confuses more often.
Inspect Mistakes
Use a confusion matrix and visual examples to see which digits are confused.
Plot a confusion matrix and show a few incorrect predictions.
from sklearn.metrics import ConfusionMatrixDisplay # Plot confusion matrix # Display a few mistakes
Show reference solution
from sklearn.metrics import ConfusionMatrixDisplay
ConfusionMatrixDisplay.from_predictions(y_val, val_preds, cmap="Blues")
plt.title("Digit Confusion Matrix")
plt.show()
mistakes = X_val[val_preds != y_val].head(10)
mistake_true = y_val[val_preds != y_val].head(10)
mistake_pred = val_preds[val_preds != y_val][:10]
fig, axes = plt.subplots(2, 5, figsize=(8, 4))
for ax, (_, row), true_label, pred_label in zip(axes.ravel(), mistakes.iterrows(), mistake_true, mistake_pred):
ax.imshow(row.values.reshape(28, 28), cmap="gray")
ax.set_title(f"T:{true_label} P:{pred_label}")
ax.axis("off")
plt.tight_layout()
plt.show()Mistake inspection makes model evaluation concrete. It shows whether errors are random or tied to visually similar digits.
Create Digit Recognizer Submission
Train on all labeled rows, predict test images, and save ImageId/Label submission.csv.
Load test.csv, scale pixels the same way, predict labels, and save submission.csv.
# Fit model on all labeled images # Load and scale test.csv # Predict labels and save submission.csv
Show reference solution
model.fit(X / 255.0, y)
test_df = pd.read_csv(find_file("test.csv"))
test_preds = model.predict(test_df / 255.0).astype(int)
submission = pd.DataFrame({
"ImageId": np.arange(1, len(test_preds) + 1),
"Label": test_preds,
})
submission.to_csv("submission.csv", index=False)
display(submission.head())
print("saved submission.csv", submission.shape)Digit Recognizer submissions use one-based ImageId values in the same order as test.csv.