{"metadata":{"kernelspec":{"name":"python3","display_name":"Python 3","language":"python"},"language_info":{"name":"python","version":"3.11.11","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"none","dataSources":[],"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"id":"cd09490a-66a8-4c45-bb4a-d6da31266bbf","cell_type":"markdown","source":"---\n\n## ⭐ Hope you enjoyed this Notebook!  \n### 🙏 Your <span style=\"color:#e63946; font-weight:bold; text-transform: uppercase;\">upvotes</span> & <span style=\"color:#1d3557; font-weight:bold;\">comments</span> mean a lot! 💬❤️\n\n---\n","metadata":{}},{"id":"128703bc","cell_type":"code","source":"import tensorflow as tf\nfrom tensorflow import keras\nimport numpy as np\nimport matplotlib.pyplot as plt\nimport pandas as pd\nfrom tensorflow.keras.layers import Dense, Flatten ,Dropout\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-12T10:52:23.356533Z","iopub.execute_input":"2025-05-12T10:52:23.356929Z","iopub.status.idle":"2025-05-12T10:52:23.362447Z","shell.execute_reply.started":"2025-05-12T10:52:23.356903Z","shell.execute_reply":"2025-05-12T10:52:23.361276Z"}},"outputs":[],"execution_count":null},{"id":"202228e7","cell_type":"code","source":"minst = tf.keras.datasets.mnist\n(X_train, y_train), (X_test, y_test) = minst.load_data()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-12T10:52:23.364275Z","iopub.execute_input":"2025-05-12T10:52:23.364627Z","iopub.status.idle":"2025-05-12T10:52:23.711631Z","shell.execute_reply.started":"2025-05-12T10:52:23.364603Z","shell.execute_reply":"2025-05-12T10:52:23.710539Z"}},"outputs":[],"execution_count":null},{"id":"a96d442b","cell_type":"code","source":"# Display multiple images in a subplot\nplt.figure(figsize=(10, 5))\n\nfor i in range(6):  # Display 6 images\n    plt.subplot(2, 3, i + 1)  # 2 rows, 3 columns\n    plt.imshow(X_train[i], cmap=plt.get_cmap('Greys'))\n    plt.title(f\"Label: {y_train[i]}\")\n    # plt.axis('off')  # Hide axes\n\nplt.tight_layout()\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-12T10:52:23.712935Z","iopub.execute_input":"2025-05-12T10:52:23.713636Z","iopub.status.idle":"2025-05-12T10:52:24.568244Z","shell.execute_reply.started":"2025-05-12T10:52:23.713603Z","shell.execute_reply":"2025-05-12T10:52:24.567340Z"}},"outputs":[],"execution_count":null},{"id":"30969f46","cell_type":"code","source":"print(\"Shape of X_train is: \",X_train.shape )\nprint(\"Shape of X_test is: \",X_test.shape )","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-12T10:52:24.569150Z","iopub.execute_input":"2025-05-12T10:52:24.569593Z","iopub.status.idle":"2025-05-12T10:52:24.574872Z","shell.execute_reply.started":"2025-05-12T10:52:24.569558Z","shell.execute_reply":"2025-05-12T10:52:24.573903Z"}},"outputs":[],"execution_count":null},{"id":"8f361277","cell_type":"code","source":"print(\"Shape of y_train is: \",y_train.shape )\nprint(\"Shape of y_test is: \",y_test.shape )","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-12T10:52:24.577038Z","iopub.execute_input":"2025-05-12T10:52:24.577287Z","iopub.status.idle":"2025-05-12T10:52:24.594532Z","shell.execute_reply.started":"2025-05-12T10:52:24.577268Z","shell.execute_reply":"2025-05-12T10:52:24.593520Z"}},"outputs":[],"execution_count":null},{"id":"0a7756ee","cell_type":"code","source":"# # cnn  acc uracy 98.5%  \n# X_train = X_train.reshape(X_train.shape[0], 28, 28, 1)\n# X_test = X_test.reshape(X_test.shape[0], 28, 28, 1)\n\n# acc 96.5%\n# Reshape the data to be 2D\n# Flatten the images\n# X_train = X_train.reshape(X_train.shape[0], 28 * 28)\nX_train = X_train.reshape(-1, 28 * 28)\nX_test = X_test.reshape(-1, 28 * 28)\n\n# Normalize the data\nX_train ,X_test  = X_train / 255.0, X_test / 255.0","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-12T10:52:24.595514Z","iopub.execute_input":"2025-05-12T10:52:24.595922Z","iopub.status.idle":"2025-05-12T10:52:24.805682Z","shell.execute_reply.started":"2025-05-12T10:52:24.595889Z","shell.execute_reply":"2025-05-12T10:52:24.804675Z"}},"outputs":[],"execution_count":null},{"id":"fbbbcc5f","cell_type":"code","source":"# Convert the labels to one-hot encoding\ny_train , y_test  = tf.one_hot(y_train,10), tf.one_hot(y_test,10)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-12T10:52:24.806575Z","iopub.execute_input":"2025-05-12T10:52:24.806880Z","iopub.status.idle":"2025-05-12T10:52:24.812974Z","shell.execute_reply.started":"2025-05-12T10:52:24.806851Z","shell.execute_reply":"2025-05-12T10:52:24.812142Z"}},"outputs":[],"execution_count":null},{"id":"a9b85f3a-7a35-41d2-8810-3f2392d4fa31","cell_type":"markdown","source":"# Explanation of the Sequential Neural Network Model\n\n## 1. Model Overview\n\nThis model is a simple feedforward neural network designed for image classification tasks, such as recognizing handwritten digits from 28x28 grayscale images. It outputs probabilities across 10 classes using a softmax activation in the final layer.\n\n## 2. Layer-by-Layer Breakdown\n\n- **Flatten Layer:** Converts the 2D input image into a 1D vector so it can be processed by fully connected layers.\n- **Dense Layers:** Two dense (fully connected) layers with 128 and 64 neurons respectively, each followed by a ReLU activation function. These layers learn hierarchical features from the input data.\n- **Dropout Layers:** After each dense layer, dropout is applied with a rate of 20%. Dropout randomly deactivates neurons during training to reduce overfitting and improve generalization.\n- **Output Layer:** A dense layer with 10 neurons and softmax activation, which produces the probability distribution over the 10 classes.\n\n## 3. How the Model Works\n\nThe model first flattens the input image to a vector, then passes it through the dense layers to extract meaningful features. Dropout layers help prevent the model from memorizing the training data, enhancing its ability to generalize to new inputs. Finally, the output layer predicts the class probabilities.\n\n---","metadata":{}},{"id":"313c2b4a","cell_type":"code","source":"model = keras.Sequential([\n    Flatten(input_shape=X_train.shape[1:]), # 28 * 28 = 784\n    # Dense(512, activation='relu'),  # 512\n    # Dropout(0.2),\n    Dense(128, activation='relu'),  # 128\n    Dropout(0.2),\n    Dense(64, activation='relu'),  # 64\n    Dropout(0.2),\n    # Dense(32, activation='relu'),  # 32\n    # Dropout(0.2),\n    # Dense(16, activation='relu'),  # 16\n    # Dropout(0.2),\n    Dense(10, activation='softmax')\n])\nmodel.summary()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-12T10:52:24.813923Z","iopub.execute_input":"2025-05-12T10:52:24.814216Z","iopub.status.idle":"2025-05-12T10:52:24.881502Z","shell.execute_reply.started":"2025-05-12T10:52:24.814191Z","shell.execute_reply":"2025-05-12T10:52:24.880776Z"}},"outputs":[],"execution_count":null},{"id":"6420e6b8-0aa2-4242-b870-c30239f99187","cell_type":"code","source":"model.compile(optimizer='adam',\n              loss='categorical_crossentropy',\n              metrics=['accuracy'])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-12T10:52:24.882391Z","iopub.execute_input":"2025-05-12T10:52:24.882837Z","iopub.status.idle":"2025-05-12T10:52:24.892773Z","shell.execute_reply.started":"2025-05-12T10:52:24.882782Z","shell.execute_reply":"2025-05-12T10:52:24.892032Z"}},"outputs":[],"execution_count":null},{"id":"c4289891-3d01-442e-8c1c-57378079b487","cell_type":"code","source":"history = model.fit(X_train, y_train, epochs=10, batch_size=32, validation_split = .2, verbose=1)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-12T10:52:24.894030Z","iopub.execute_input":"2025-05-12T10:52:24.894363Z","iopub.status.idle":"2025-05-12T10:53:13.798617Z","shell.execute_reply.started":"2025-05-12T10:52:24.894333Z","shell.execute_reply":"2025-05-12T10:53:13.797479Z"}},"outputs":[],"execution_count":null},{"id":"0a200606-2cd6-48e2-8b36-9d00dd9435ea","cell_type":"code","source":"loss , acc = model.evaluate(X_test, y_test, verbose=1)\nloss , acc ","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-12T10:53:13.802697Z","iopub.execute_input":"2025-05-12T10:53:13.803140Z","iopub.status.idle":"2025-05-12T10:53:14.577919Z","shell.execute_reply.started":"2025-05-12T10:53:13.803113Z","shell.execute_reply":"2025-05-12T10:53:14.576974Z"}},"outputs":[],"execution_count":null},{"id":"8d07115a-cae2-42c9-9851-b123715ceebc","cell_type":"code","source":"# Predict the first 10 images in the test set\npredictions = model.predict(X_test[:20])\n# Get the predicted class labels\npredicted_labels = np.argmax(predictions, axis=1)\npredicted_labels\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-12T10:53:14.578870Z","iopub.execute_input":"2025-05-12T10:53:14.579137Z","iopub.status.idle":"2025-05-12T10:53:14.712123Z","shell.execute_reply.started":"2025-05-12T10:53:14.579117Z","shell.execute_reply":"2025-05-12T10:53:14.711268Z"}},"outputs":[],"execution_count":null},{"id":"a93a7284-c592-46d7-9e58-84b6eaacec67","cell_type":"code","source":"# Display the first 10 images with their predicted labels\nplt.figure(figsize=(10, 5))\nfor i in range(20):  # Display 10 images\n    plt.subplot(5, 4, i + 1)  # 2 rows, 5 columns\n    plt.imshow(X_test[i].reshape(28, 28), cmap=plt.get_cmap('Greys'))\n    plt.title(f\"Predicted: {predicted_labels[i]}\\n True Label: {np.argmax(y_test[i])}\")\n    \n    plt.axis('off')  # Hide axes\nplt.tight_layout()\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-12T10:53:14.713103Z","iopub.execute_input":"2025-05-12T10:53:14.713359Z","iopub.status.idle":"2025-05-12T10:53:15.706989Z","shell.execute_reply.started":"2025-05-12T10:53:14.713339Z","shell.execute_reply":"2025-05-12T10:53:15.706178Z"}},"outputs":[],"execution_count":null},{"id":"c29b3593-700a-4edb-abee-70f49f420a53","cell_type":"code","source":"df = pd.DataFrame(history.history)\ndf.plot(figsize=(8, 5))\nplt.grid(True)\nplt.gca().set_ylim(0, 1)\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-12T10:53:15.707943Z","iopub.execute_input":"2025-05-12T10:53:15.708286Z","iopub.status.idle":"2025-05-12T10:53:15.894464Z","shell.execute_reply.started":"2025-05-12T10:53:15.708264Z","shell.execute_reply":"2025-05-12T10:53:15.893608Z"}},"outputs":[],"execution_count":null},{"id":"72c64c35-8ce7-42b5-8c33-cd133406b609","cell_type":"markdown","source":"## Summary\n\nThis architecture is straightforward and effective for simple image classification problems. It balances model complexity and regularization by using dropout. The model can be easily extended by adding more layers or adjusting the number of neurons to fit different tasks or datasets.\n","metadata":{}},{"id":"445931bf","cell_type":"markdown","source":"## Funcation API","metadata":{}},{"id":"28ae5702-99a0-4a0e-829c-9c3782442f1a","cell_type":"markdown","source":"# Explanation of Using a Function to Build Layers in a Keras Model\n\n---\n\n## 1. Why use a function like `wide_block`?\n\n- To **group multiple layers** together into a reusable block.\n- Makes the code cleaner and easier to maintain.\n- Avoids repeating the same layers multiple times.\n\n---\n\n## 2. What does the `wide_block` function do?\n\n- Takes an input tensor `x`.\n- Flattens the input (converts 2D image to 1D vector).\n- Applies a Dense layer with ReLU activation.\n- Applies Dropout for regularization.\n- Applies another Dense layer.\n- Returns the processed tensor.\n\n---\n\n## 3. How to use `wide_block` in the model?\n\n- Define the input layer (e.g., shape `(28, 28)` for MNIST images).\n- Pass the input through the `wide_block` function to get one feature branch.\n- Create another branch of layers directly on the input.\n- Concatenate the outputs of both branches.\n- Add a final Dense layer with softmax activation for classification.\n- Define the model with the input and final output.\n\n---\n","metadata":{}},{"id":"42c676b6","cell_type":"code","source":"def wide_block(x, hidden_layer=512, drop_out=0.2):\n    x = tf.keras.layers.Flatten()(x)\n    x = tf.keras.layers.Dense(hidden_layer, activation='relu')(x)\n    x = tf.keras.layers.Dropout(drop_out)(x)\n    x = tf.keras.layers.Dense(64, activation='relu')(x)\n    concat = tf.keras.layers.Concatenate()([x, x])\n    \n    return concat","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-12T10:53:15.895308Z","iopub.execute_input":"2025-05-12T10:53:15.895539Z","iopub.status.idle":"2025-05-12T10:53:15.901244Z","shell.execute_reply.started":"2025-05-12T10:53:15.895522Z","shell.execute_reply":"2025-05-12T10:53:15.900284Z"}},"outputs":[],"execution_count":null},{"id":"8d12b8f0","cell_type":"code","source":"input_layer  = tf.keras.layers.Input(shape=X_train.shape[1:]) # 28 * 28 = 784\n\nblock = wide_block (input_layer, 256,  0.2)\n\nx = tf.keras.layers.Flatten()(input_layer)\nx = tf.keras.layers.Dense(128, activation='relu')(x)\nx = tf.keras.layers.Dense(64, activation='relu')(x)\n\nconcat = tf.keras.layers.Concatenate()([block,x])\n\noutput_layer = tf.keras.layers.Dense(10, activation='softmax')(concat)\n\nmodel = tf.keras.Model(inputs=input_layer, outputs=output_layer)\n\nmodel.summary()\n\nmodel.compile(optimizer='adam',\n              loss='categorical_crossentropy',\n              metrics=['accuracy'])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-12T10:53:15.902212Z","iopub.execute_input":"2025-05-12T10:53:15.902496Z","iopub.status.idle":"2025-05-12T10:53:15.996634Z","shell.execute_reply.started":"2025-05-12T10:53:15.902465Z","shell.execute_reply":"2025-05-12T10:53:15.995851Z"}},"outputs":[],"execution_count":null},{"id":"3db156d8","cell_type":"code","source":"# tf.keras.utils.plot_model(model, show_shapes=True, show_layer_names=True)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-12T10:53:15.997651Z","iopub.execute_input":"2025-05-12T10:53:15.997990Z","iopub.status.idle":"2025-05-12T10:53:16.002527Z","shell.execute_reply.started":"2025-05-12T10:53:15.997963Z","shell.execute_reply":"2025-05-12T10:53:16.001671Z"}},"outputs":[],"execution_count":null},{"id":"bbd092bc","cell_type":"code","source":"history = model.fit(X_train, y_train, epochs=10, batch_size=32, validation_split = .2, verbose=1)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-12T10:53:16.003311Z","iopub.execute_input":"2025-05-12T10:53:16.003552Z","iopub.status.idle":"2025-05-12T10:54:24.792704Z","shell.execute_reply.started":"2025-05-12T10:53:16.003533Z","shell.execute_reply":"2025-05-12T10:54:24.791875Z"}},"outputs":[],"execution_count":null},{"id":"55d60a97","cell_type":"code","source":"loss , acc = model.evaluate(X_test, y_test, verbose=1)\nloss , acc ","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-12T10:54:24.794000Z","iopub.execute_input":"2025-05-12T10:54:24.794280Z","iopub.status.idle":"2025-05-12T10:54:25.643574Z","shell.execute_reply.started":"2025-05-12T10:54:24.794252Z","shell.execute_reply":"2025-05-12T10:54:25.642868Z"}},"outputs":[],"execution_count":null},{"id":"7a481f17","cell_type":"code","source":"# Predict the first 10 images in the test set\npredictions = model.predict(X_test[:20])\n\n# Get the predicted class labels\npredicted_labels = np.argmax(predictions, axis=1)\npredicted_labels\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-12T10:54:25.645270Z","iopub.execute_input":"2025-05-12T10:54:25.645506Z","iopub.status.idle":"2025-05-12T10:54:25.803000Z","shell.execute_reply.started":"2025-05-12T10:54:25.645489Z","shell.execute_reply":"2025-05-12T10:54:25.802172Z"}},"outputs":[],"execution_count":null},{"id":"cc677ecd","cell_type":"code","source":"# Display the first 10 images with their predicted labels\nplt.figure(figsize=(10, 5))\nfor i in range(20): \n    plt.subplot(5, 4, i + 1)\n    plt.imshow(X_test[i].reshape(28, 28), cmap=plt.get_cmap('Greys'))\n    plt.title(f\"Predicted: {predicted_labels[i]} \\n True : {np.argmax(y_test[i])}\")\n    plt.axis('off')  # Hide axes\nplt.tight_layout()\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-12T10:54:25.803895Z","iopub.execute_input":"2025-05-12T10:54:25.804178Z","iopub.status.idle":"2025-05-12T10:54:27.243499Z","shell.execute_reply.started":"2025-05-12T10:54:25.804159Z","shell.execute_reply":"2025-05-12T10:54:27.242546Z"}},"outputs":[],"execution_count":null},{"id":"42363e30","cell_type":"code","source":"df = pd.DataFrame(history.history)\ndf.plot(figsize=(8, 5))\nplt.grid(True)\nplt.gca().set_ylim(0, 1)\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-12T10:54:27.244778Z","iopub.execute_input":"2025-05-12T10:54:27.245175Z","iopub.status.idle":"2025-05-12T10:54:27.431524Z","shell.execute_reply.started":"2025-05-12T10:54:27.245147Z","shell.execute_reply":"2025-05-12T10:54:27.430486Z"}},"outputs":[],"execution_count":null},{"id":"b64022dc-f871-4782-a8fc-534d74ffe917","cell_type":"markdown","source":"\n---\n\n## Summary\n\n- The `wide_block` function helps modularize your model.\n- You can combine multiple feature extraction paths.\n- Concatenating outputs allows the model to learn richer representations.\n- This approach keeps your code organized and flexible.\n\n---","metadata":{}},{"id":"c63eca29","cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"id":"18639ef3","cell_type":"markdown","source":"# Subclassing API","metadata":{}},{"id":"a8b00f21-7f0d-42a9-a6bb-dad509be51c1","cell_type":"markdown","source":"# Explanation of the `WideDeepModel` Custom Keras Model\n\n---\n\n## 1. What is `WideDeepModel`?\n\n- A **custom Keras model** subclassing `tf.keras.Model`.\n- Combines a \"wide\" input (the flattened original input) with a \"deep\" network (several Dense layers).\n- Outputs classification probabilities using a softmax layer.\n\n---\n\n## 2. Key components in `__init__`:\n\n- `Flatten` layer: flattens the 2D input image into a 1D vector.\n- Two Dense layers (`dense_layer_1` and `dense_layer_2`) with ReLU activation (or any specified activation).\n- Dropout layers after each Dense layer to reduce overfitting.\n- Final Dense layer with softmax activation for classification into `num_classes` categories.\n\n---\n\n## 3. What happens in the `call` method?\n\n- Input `inputs` is flattened.\n- Passed through the first Dense layer + dropout → `x1`.\n- Passed through the second Dense layer + dropout → `x2`.\n- Concatenate the output of the deep path (`x2`) with the original flattened input (`x`).\n- Pass the concatenated vector to the output layer to get class probabilities.\n\n---\n\n## 4. The `build_graph` method:\n\n- Creates a Keras functional model with an input shape `(28, 28)`.\n- Calls the model on this input to build the computation graph.\n- Useful for visualizing the model summary or plot.\n\n---","metadata":{}},{"id":"b7569697","cell_type":"code","source":"class WideDeepModel(tf.keras.Model):\n    def __init__(self, input_shape, num_classes, act=\"relu\", drop_out=0.2, **kwargs):\n        super(WideDeepModel, self).__init__(**kwargs)\n        \n        self.flatten = tf.keras.layers.Flatten()\n        self.dense_layer_1 = tf.keras.layers.Dense(128, activation=act)\n        self.drop_out_1 = tf.keras.layers.Dropout(drop_out)\n        self.dense_layer_2 = tf.keras.layers.Dense(64, activation=act)\n        self.drop_out_2 = tf.keras.layers.Dropout(drop_out)\n        self.output_layer = tf.keras.layers.Dense(num_classes, activation='softmax')\n\n    def call(self, inputs):\n        x = self.flatten(inputs)\n        x1 = self.dense_layer_1(x)\n        x1 = self.drop_out_1(x1)\n        x2 = self.dense_layer_2(x1)\n        x2 = self.drop_out_2(x2)\n        \n        concat = tf.keras.layers.Concatenate()([x2, x])\n        \n        output = self.output_layer(concat)\n        return output\n\n    def build_graph(self):\n        x = tf.keras.Input(shape=(28, 28))  \n        return tf.keras.Model(inputs=[x], outputs=self.call(x))\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-12T10:54:27.432726Z","iopub.execute_input":"2025-05-12T10:54:27.433107Z","iopub.status.idle":"2025-05-12T10:54:27.441703Z","shell.execute_reply.started":"2025-05-12T10:54:27.433080Z","shell.execute_reply":"2025-05-12T10:54:27.440649Z"}},"outputs":[],"execution_count":null},{"id":"5cf292dc","cell_type":"code","source":"model = WideDeepModel(input_shape=(28, 28), num_classes=10)\nmodel.build_graph() \nmodel.summary()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-12T10:54:27.442588Z","iopub.execute_input":"2025-05-12T10:54:27.442924Z","iopub.status.idle":"2025-05-12T10:54:27.511474Z","shell.execute_reply.started":"2025-05-12T10:54:27.442902Z","shell.execute_reply":"2025-05-12T10:54:27.510659Z"}},"outputs":[],"execution_count":null},{"id":"c715fb61","cell_type":"code","source":"# tf.keras.utils.plot_model(model.build_graph(), show_shapes=False, show_layer_names=True, to_file='model.png')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-12T10:54:27.512284Z","iopub.execute_input":"2025-05-12T10:54:27.512551Z","iopub.status.idle":"2025-05-12T10:54:27.516684Z","shell.execute_reply.started":"2025-05-12T10:54:27.512522Z","shell.execute_reply":"2025-05-12T10:54:27.515979Z"}},"outputs":[],"execution_count":null},{"id":"c1240190","cell_type":"code","source":"model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy'])\nhistory = model.fit(X_train, y_train, epochs=12, batch_size=32, validation_split=0.2, verbose=1)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-12T10:54:27.517730Z","iopub.execute_input":"2025-05-12T10:54:27.517991Z","iopub.status.idle":"2025-05-12T10:55:24.085836Z","shell.execute_reply.started":"2025-05-12T10:54:27.517972Z","shell.execute_reply":"2025-05-12T10:55:24.085079Z"}},"outputs":[],"execution_count":null},{"id":"72951f09","cell_type":"code","source":"loss, acc = model.evaluate(X_test, y_test, verbose=1)\nprint('Test loss:', loss)\nprint('Test accuracy:', acc)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-12T10:55:24.087300Z","iopub.execute_input":"2025-05-12T10:55:24.088267Z","iopub.status.idle":"2025-05-12T10:55:24.871715Z","shell.execute_reply.started":"2025-05-12T10:55:24.088240Z","shell.execute_reply":"2025-05-12T10:55:24.870750Z"}},"outputs":[],"execution_count":null},{"id":"ba892162-5f7c-4002-b7cf-d0a36c82f67d","cell_type":"code","source":"predictions = model.predict(X_test[:20])\npredicted_labels = np.argmax(predictions, axis=1)\npredicted_labels","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-12T10:57:51.657703Z","iopub.execute_input":"2025-05-12T10:57:51.658131Z","iopub.status.idle":"2025-05-12T10:57:51.799047Z","shell.execute_reply.started":"2025-05-12T10:57:51.658105Z","shell.execute_reply":"2025-05-12T10:57:51.798202Z"}},"outputs":[],"execution_count":null},{"id":"089e7ce9","cell_type":"code","source":"# Display the first 10 images with their predicted labels\nplt.figure(figsize=(10, 5))\nfor i in range(20): \n    plt.subplot(5, 4, i + 1)\n    plt.imshow(X_test[i].reshape(28, 28), cmap=plt.get_cmap('Greys'))\n    plt.title(f\"Predicted: {predicted_labels[i]} \\n True : {np.argmax(y_test[i])}\")\n    plt.axis('off')  # Hide axes\nplt.tight_layout()\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-12T10:57:52.921478Z","iopub.execute_input":"2025-05-12T10:57:52.921840Z","iopub.status.idle":"2025-05-12T10:57:53.903458Z","shell.execute_reply.started":"2025-05-12T10:57:52.921774Z","shell.execute_reply":"2025-05-12T10:57:53.902531Z"}},"outputs":[],"execution_count":null},{"id":"e085b430-8b64-4999-98a8-e539f9693015","cell_type":"code","source":"np.argmax(y_test[8])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-12T10:57:53.986603Z","iopub.execute_input":"2025-05-12T10:57:53.986981Z","iopub.status.idle":"2025-05-12T10:57:53.994297Z","shell.execute_reply.started":"2025-05-12T10:57:53.986956Z","shell.execute_reply":"2025-05-12T10:57:53.993292Z"}},"outputs":[],"execution_count":null},{"id":"b72688e6","cell_type":"code","source":"df = pd.DataFrame(history.history)\ndf.plot(figsize=(8, 5))\nplt.grid(True)\nplt.gca().set_ylim(0, 1)\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-12T10:57:54.215546Z","iopub.execute_input":"2025-05-12T10:57:54.215924Z","iopub.status.idle":"2025-05-12T10:57:54.409009Z","shell.execute_reply.started":"2025-05-12T10:57:54.215896Z","shell.execute_reply":"2025-05-12T10:57:54.408008Z"}},"outputs":[],"execution_count":null},{"id":"2d757dc8-f7e8-45a2-9799-a040464f7f8c","cell_type":"markdown","source":"## Summary:\n\n- This model combines **original input features** (wide part) with **learned deep features**.\n- Using subclassing gives more flexibility for custom behavior.\n- The concatenation step helps the model learn from both raw and processed features.\n- `build_graph` helps visualize the model architecture.\n\n---\n\n","metadata":{}},{"id":"b4d4d1e0-631b-458c-a08b-42655cce5da3","cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}