diff --git a/Codes/Code-3b.md b/Codes/Code-3b.md new file mode 100644 index 0000000..34e62d9 --- /dev/null +++ b/Codes/Code-3b.md @@ -0,0 +1,233 @@ +# Practical-3b (Convolutional Neural Network - MNIST Fashion Dataset) + +Problem Statement: Convolutional Neural Network (CNN): Use MNIST Fashion Dataset and create a classifier to classify fashion clothing into categories. + +> [!NOTE] +> Download dataset directly from [source](https://www.kaggle.com/datasets/zalando-research/fashionmnist). +> Dataset available in [Datasets](../Datasets/fashionmnist.zip) directory. +> In the code, dataset is downloaded directly from Keras/TensorFlow in 2nd step (Load Dataset) + +--- + +## Pre-requisities + +1. Install packages using `pip`: `pip install tensorflow keras numpy matplotlib seaborn scikit-learn` (`tensorflow` requires Python 3.9 - 3.12) + +## Steps + +--- + +## Code + +### 1. Import Libraries: + +```python3 +import numpy as np +import matplotlib.pyplot as plt +import seaborn as sns +import tensorflow as tf +from tensorflow.keras.models import Sequential +from tensorflow.keras.layers import Input, Conv2D, AvgPool2D, GlobalAveragePooling2D, Dense +from tensorflow.keras.utils import to_categorical +from sklearn.metrics import confusion_matrix, classification_report +``` + +### 2. Load Dataset: + +```python3 +# Fashion MNIST is built into Keras, downloads automatically on first run +(X_train, y_train), (X_test, y_test) = tf.keras.datasets.fashion_mnist.load_data() + +''' +import numpy as np +import gzip +import os + +def load_fashion_mnist(path): + """Load Fashion MNIST from local .gz files (Kaggle Zalando format).""" + files = { + 'X_train': 'train-images-idx3-ubyte.gz', + 'y_train': 'train-labels-idx1-ubyte.gz', + 'X_test': 't10k-images-idx3-ubyte.gz', + 'y_test': 't10k-labels-idx1-ubyte.gz', + } + + with gzip.open(os.path.join(path, files['X_train'])) as f: + X_train = np.frombuffer(f.read(), np.uint8, offset=16).reshape(-1, 28, 28) + with gzip.open(os.path.join(path, files['y_train'])) as f: + y_train = np.frombuffer(f.read(), np.uint8, offset=8) + with gzip.open(os.path.join(path, files['X_test'])) as f: + X_test = np.frombuffer(f.read(), np.uint8, offset=16).reshape(-1, 28, 28) + with gzip.open(os.path.join(path, files['y_test'])) as f: + y_test = np.frombuffer(f.read(), np.uint8, offset=8) + + return (X_train, y_train), (X_test, y_test) + +# Replace the Keras load line with: +(X_train, y_train), (X_test, y_test) = load_fashion_mnist('./fashion-mnist/') +''' + +print("Training set shape:", X_train.shape) # (60000, 28, 28) +print("Test set shape: ", X_test.shape) # (10000, 28, 28) +print("Classes:", np.unique(y_train)) +``` + +### 3. Exploratory Data Analysis (EDA): + +```python3 +class_names = ['T-shirt/top', 'Trouser', 'Pullover', 'Dress', 'Coat', + 'Sandal', 'Shirt', 'Sneaker', 'Bag', 'Ankle boot'] + +# Class distribution +unique, counts = np.unique(y_train, return_counts=True) +plt.figure(figsize=(10, 4)) +plt.bar([class_names[i] for i in unique], counts) +plt.xticks(rotation=45, ha='right') +plt.title("Training Set Class Distribution") +plt.ylabel("Count") +plt.tight_layout() +plt.show() + +# Sample images (one per class) +plt.figure(figsize=(15, 3)) +for i, cls in enumerate(class_names): + idx = np.where(y_train == i)[0][0] # index of first image for this class + plt.subplot(1, 10, i + 1) + plt.imshow(X_train[idx], cmap='gray') + plt.title(cls, fontsize=7) + plt.axis('off') +plt.suptitle("Sample Image per Class") +plt.tight_layout() +plt.show() +``` + +### 4. Preprocess Data: + +```python3 +# Reshape to add channel dimension: (samples, 28, 28) -> (samples, 28, 28, 1) +X_train = X_train.reshape(-1, 28, 28, 1).astype('float32') / 255.0 # normalize to [0,1] +X_test = X_test.reshape(-1, 28, 28, 1).astype('float32') / 255.0 + +# One-hot encode labels: e.g. class 3 of 10 -> [0,0,0,1,0,0,0,0,0,0] +y_train_cat = to_categorical(y_train, num_classes=10) +y_test_cat = to_categorical(y_test, num_classes=10) + +print("X_train shape:", X_train.shape) +print("y_train_cat shape:", y_train_cat.shape) +``` + +### 5. Build the CNN Model: + +```python3 +model = Sequential() + +model.add(Input(shape=(28, 28, 1))) # input: 28x28 grayscale image +model.add(Conv2D(64, kernel_size=(3, 3), activation='relu', padding='same')) # 64 filters, extract features +model.add(AvgPool2D(pool_size=(2, 2))) # downsample to 14x14 + +model.add(Conv2D(32, kernel_size=(3, 3), activation='relu', padding='same')) # 32 filters, refine features +model.add(AvgPool2D(pool_size=(2, 2))) # downsample to 7x7 + +model.add(GlobalAveragePooling2D()) # average each feature map to single value +model.add(Dense(10, activation='softmax')) # output: probability for each of 10 classes + +model.summary() +``` + +### 6. Compile the Model: + +```python3 +# categorical_crossentropy: standard loss for multi-class one-hot classification +model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy']) +``` + +### 7. Train the Model: + +```python3 +# validation_data uses test set to monitor performance after each epoch +history = model.fit(X_train, y_train_cat, epochs=10, validation_data=(X_test, y_test_cat)) +``` + +### 8. Evaluate the Model on Test Data: + +```python3 +loss, accuracy = model.evaluate(X_test, y_test_cat) +print(f"Test Loss: {loss:.4f}") +print(f"Test Accuracy: {accuracy*100:.2f}%") +``` + +### 9. Plot Training vs Validation Accuracy: + +```python3 +plt.plot(history.history['accuracy'], label='Training Accuracy') +plt.plot(history.history['val_accuracy'], label='Validation Accuracy') +plt.title('Model Accuracy Over Epochs') +plt.xlabel('Epoch') +plt.ylabel('Accuracy') +plt.legend() +plt.grid(True) +plt.show() +``` + +### 10. Plot Training vs Validation Loss: + +```python3 +plt.plot(history.history['loss'], label='Training Loss') +plt.plot(history.history['val_loss'], label='Validation Loss') +plt.title('Model Loss Over Epochs') +plt.xlabel('Epoch') +plt.ylabel('Loss') +plt.legend() +plt.grid(True) +plt.show() +``` + +### 11. Confusion Matrix and Classification Report: + +```python3 +y_pred = np.argmax(model.predict(X_test), axis=1) # predicted class index + +cm = confusion_matrix(y_test, y_pred) +plt.figure(figsize=(10, 8)) +sns.heatmap(cm, annot=True, fmt='d', cmap='Blues', + xticklabels=class_names, yticklabels=class_names) +plt.title('Confusion Matrix') +plt.ylabel('Actual') +plt.xlabel('Predicted') +plt.xticks(rotation=45, ha='right') +plt.tight_layout() +plt.show() + +print("\nClassification Report:\n") +print(classification_report(y_test, y_pred, target_names=class_names)) +``` + +### 12. Visualize Sample Predictions: + +```python3 +# batch predict all test images, then pick 10 random ones to display +all_preds = np.argmax(model.predict(X_test), axis=1) +random_indices = np.random.choice(len(X_test), 10, replace=False) + +plt.figure(figsize=(20, 4)) +for i, idx in enumerate(random_indices): + plt.subplot(2, 5, i + 1) + plt.imshow(X_test[idx].reshape(28, 28), cmap='gray') + predicted = class_names[all_preds[idx]] + actual = class_names[y_test[idx]] + color = 'green' if predicted == actual else 'red' # green = correct, red = wrong + plt.title(f"P: {predicted}\nA: {actual}", fontsize=8, color=color) + plt.axis('off') +plt.suptitle("Sample Predictions (Green=Correct, Red=Wrong)") +plt.tight_layout() +plt.show() +``` + +--- + +## Miscellaneous + +- [Dataset source](https://www.kaggle.com/datasets/zalando-research/fashionmnist) + +--- +