{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.10.14","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"none","dataSources":[{"sourceId":3004,"databundleVersionId":861823,"sourceType":"competition"}],"dockerImageVersionId":30762,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import numpy as np\nimport matplotlib.pyplot as plt\nimport seaborn as sns","metadata":{"execution":{"iopub.status.busy":"2024-10-05T14:52:23.002515Z","iopub.execute_input":"2024-10-05T14:52:23.002916Z","iopub.status.idle":"2024-10-05T14:52:25.185322Z","shell.execute_reply.started":"2024-10-05T14:52:23.002876Z","shell.execute_reply":"2024-10-05T14:52:25.184188Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import warnings\nwarnings.filterwarnings(\"ignore\", category=FutureWarning)\nwarnings.filterwarnings(\"ignore\", category=UserWarning)\n\nsns.set_palette(\"Spectral\")\n\n# Set a base style\nsns.set_style(\"whitegrid\")\n\n# Customize specific style parameters\ncustom_params = {\n    \"axes.spines.right\": False,\n    \"axes.spines.top\": False,\n    \"axes.grid\": True,\n    \"grid.linestyle\": \"--\",\n    \"grid.color\": \"#cccccc\",\n    \"axes.facecolor\": \"#f0f0f0\",\n    \"axes.labelsize\": 14,\n    \"axes.titlesize\": 16,\n    \"xtick.labelsize\": 12,\n    \"ytick.labelsize\": 12\n}\nsns.set_context(\"notebook\", rc=custom_params)","metadata":{"execution":{"iopub.status.busy":"2024-10-05T14:54:34.607250Z","iopub.execute_input":"2024-10-05T14:54:34.607959Z","iopub.status.idle":"2024-10-05T14:54:34.623473Z","shell.execute_reply.started":"2024-10-05T14:54:34.607914Z","shell.execute_reply":"2024-10-05T14:54:34.621943Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Importing necessary modules from sklearn for dataset loading, model selection, preprocessing, and metrics\nfrom sklearn.datasets import load_digits  # For loading the digits dataset\nfrom sklearn.model_selection import train_test_split, KFold  # For splitting data into training and testing sets, and for cross-validation\nfrom sklearn.preprocessing import StandardScaler  # For scaling/normalizing the data\nfrom sklearn.metrics import accuracy_score, classification_report  # For evaluating model performance","metadata":{"execution":{"iopub.status.busy":"2024-10-05T14:54:40.115214Z","iopub.execute_input":"2024-10-05T14:54:40.115635Z","iopub.status.idle":"2024-10-05T14:54:40.419545Z","shell.execute_reply.started":"2024-10-05T14:54:40.115598Z","shell.execute_reply":"2024-10-05T14:54:40.418355Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Load the digits dataset from sklearn\ndigits = load_digits()\n# Extract the data and target from the loaded dataset\nX, y = digits.data, digits.target","metadata":{"execution":{"iopub.status.busy":"2024-10-05T14:54:52.760650Z","iopub.execute_input":"2024-10-05T14:54:52.761207Z","iopub.status.idle":"2024-10-05T14:54:52.797448Z","shell.execute_reply.started":"2024-10-05T14:54:52.761152Z","shell.execute_reply":"2024-10-05T14:54:52.796206Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Display the shape of the input data matrix X\nprint(\"X shape:\", X.shape)\n# Display the shape of the target variable y\nprint(\"y shape:\", y.shape)","metadata":{"execution":{"iopub.status.busy":"2024-10-05T14:55:01.650989Z","iopub.execute_input":"2024-10-05T14:55:01.651442Z","iopub.status.idle":"2024-10-05T14:55:01.658548Z","shell.execute_reply.started":"2024-10-05T14:55:01.651401Z","shell.execute_reply":"2024-10-05T14:55:01.657394Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Split the data into training and testing sets\nX_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)","metadata":{"execution":{"iopub.status.busy":"2024-10-05T14:55:10.006836Z","iopub.execute_input":"2024-10-05T14:55:10.007569Z","iopub.status.idle":"2024-10-05T14:55:10.019019Z","shell.execute_reply.started":"2024-10-05T14:55:10.007514Z","shell.execute_reply":"2024-10-05T14:55:10.017683Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print(\"X_train shape:\", X_train.shape)\nprint(\"X_test shape:\", X_test.shape)\nprint(\"y_train shape:\", y_train.shape)\nprint(\"y_test shape:\", y_test.shape)","metadata":{"execution":{"iopub.status.busy":"2024-10-05T14:55:20.698237Z","iopub.execute_input":"2024-10-05T14:55:20.698666Z","iopub.status.idle":"2024-10-05T14:55:20.705223Z","shell.execute_reply.started":"2024-10-05T14:55:20.698627Z","shell.execute_reply":"2024-10-05T14:55:20.703949Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Normalize the data\nscaler = StandardScaler()  # Initialize the StandardScaler\nX_train = scaler.fit_transform(X_train)  # Fit and transform the training data\nX_test = scaler.transform(X_test)  # Transform the testing data","metadata":{"execution":{"iopub.status.busy":"2024-10-05T14:55:29.419352Z","iopub.execute_input":"2024-10-05T14:55:29.419759Z","iopub.status.idle":"2024-10-05T14:55:29.429286Z","shell.execute_reply.started":"2024-10-05T14:55:29.419723Z","shell.execute_reply":"2024-10-05T14:55:29.428135Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":" **X**: Input data matrix of shape $(n_x, m)$, where:\n  - $( n_x$): Number of input features.\n  - $( m )$: Number of training examples.","metadata":{}},{"cell_type":"code","source":"# Transpose the training data to change its shape from (m, nx) to (nx, m) as required by the neural network\nX_train = X_train.T\n# Transpose the testing data to change its shape from (m, nx) to (nx, m) as required by the neural network\nX_test = X_test.T\n\n# Display the new shape of the transposed training data\nprint(\"X_train shape:\", X_train.shape)\n# Display the new shape of the transposed testing data\nprint(\"X_test shape:\", X_test.shape)","metadata":{"execution":{"iopub.status.busy":"2024-10-05T14:55:48.482428Z","iopub.execute_input":"2024-10-05T14:55:48.482952Z","iopub.status.idle":"2024-10-05T14:55:48.490628Z","shell.execute_reply.started":"2024-10-05T14:55:48.482905Z","shell.execute_reply":"2024-10-05T14:55:48.489206Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# One-hot encode the labels\ndef one_hot_encode(y, num_classes):\n    # np.eye creates an identity matrix of shape (num_classes, num_classes)\n    # This is used to create a one-hot encoding for each class\n    # The [y] indexing selects the rows corresponding to the labels in y\n    # The .T transposes the result to have shape (num_classes, m) where m is the number of examples\n    return np.eye(num_classes)[y].T","metadata":{"execution":{"iopub.status.busy":"2024-10-05T14:55:59.417390Z","iopub.execute_input":"2024-10-05T14:55:59.418402Z","iopub.status.idle":"2024-10-05T14:55:59.423993Z","shell.execute_reply.started":"2024-10-05T14:55:59.418338Z","shell.execute_reply":"2024-10-05T14:55:59.422764Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"y_train_one_hot = one_hot_encode(y_train, 10)\ny_test_one_hot = one_hot_encode(y_test, 10)","metadata":{"execution":{"iopub.status.busy":"2024-10-05T14:56:07.821573Z","iopub.execute_input":"2024-10-05T14:56:07.822901Z","iopub.status.idle":"2024-10-05T14:56:07.828719Z","shell.execute_reply.started":"2024-10-05T14:56:07.822845Z","shell.execute_reply":"2024-10-05T14:56:07.827664Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**Y**: True label matrix of shape $(n_y, m)$, where:\n  - $( n_y )$: Number of output classes (e.g., 1 for binary classification, C for multiclass).\n  - $( m)$: Number of training examples.","metadata":{}},{"cell_type":"code","source":"print(\"y_train_one_hot shape:\", y_train_one_hot.shape)\nprint(\"y_test_one_hot shape:\", y_test_one_hot.shape)","metadata":{"execution":{"iopub.status.busy":"2024-10-05T14:56:27.684200Z","iopub.execute_input":"2024-10-05T14:56:27.685095Z","iopub.status.idle":"2024-10-05T14:56:27.691326Z","shell.execute_reply.started":"2024-10-05T14:56:27.685047Z","shell.execute_reply":"2024-10-05T14:56:27.690039Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"- **Input layer**: $X_{\\text{train}}$  has shape \\($64, 1437$) \\, meaning our input have 64 features and 1437 training examples.\n\n- **Output layer**: \\( $y_{\\text{train\\_one\\_hot}}$ \\) has shape  ($10, 1437$) , so our network's output should have 10 units, corresponding to the 10 classes in one-hot encoding.\n\nWe'll design the network with three hidden layers, with the number of units you prefer for each layer:\n\n### Network Design:\n\n1. **Input Layer**: 64 units $from ( X_{\\text{train}})$.\n\n2. **Hidden Layer 1**: 128 units, ReLU activation.\n\n3. **Hidden Layer 2**: 64 units, ReLU activation.\n\n4. **Hidden Layer 3**: 32 units, ReLU activation.\n\n5. **Output Layer**: 10 units, softmax activation (to match the 10 classes).\n\n### Forward Propagation:\n\n1. $Z^{[1]} = W^{[1]} X + b^{[1]}$ \n\n2. $A^{[1]} = g_{\\text{ReLU}}(Z^{[1]})$ \n\n3.  $Z^{[2]} = W^{[2]} A^{[1]} + b^{[2]}$\n\n4. $A^{[2]} = g_{\\text{ReLU}}(Z^{[2]})$\n\n5. $Z^{[3]} = W^{[3]} A^{[2]} + b^{[3]}$\n\n6. $A^{[3]} = g_{\\text{ReLU}}(Z^{[3]})$\n\n7. $Z^{[4]} = W^{[4]} A^{[3]} + b^{[4]}$\n\n8. $A^{[4]} = g_{\\text{softmax}}(Z^{[4]})$\n\n### Backward Propagation:\n\n1.  $dZ^{[4]} = A^{[4]} - Y$ \n\n2.  $dW^{[4]} = \\frac{1}{m} dZ^{[4]} A^{[3]T}$\n\n3.  $db^{[4]} = \\frac{1}{m} \\sum dZ^{[4]}$\n\n###  Entering Hidden Layers\n\n4. $dZ^{[3]} = W^{[4]T} dZ^{[4]} \\cdot g'_{\\text{ReLU}}(Z^{[3]})$ \n\n5. $dW^{[3]} = \\frac{1}{m} dZ^{[3]} A^{[2]T}$\n\n6. $db^{[3]} = \\frac{1}{m} \\sum dZ^{[3]}$\n\n7. $dZ^{[2]} = W^{[3]T} dZ^{[3]} \\cdot g'_{\\text{ReLU}}(Z^{[2]})$\n\n8. $dW^{[2]} = \\frac{1}{m} dZ^{[2]} A^{[1]T}$\n\n9. $db^{[2]} = \\frac{1}{m} \\sum dZ^{[2]}$\n\n10. $dZ^{[1]} = W^{[2]T} dZ^{[2]} \\cdot g'_{\\text{ReLU}}(Z^{[1]})$\n\n11. $dW^{[1]} = \\frac{1}{m} dZ^{[1]} X^T$\n\n12.  $db^{[1]} = \\frac{1}{m} \\sum dZ^{[1]}$\n\n  \n\n### Parameter Updates:\n\n1. $W^{[4]} := W^{[4]} - \\alpha dW^{[4]}$\n\n2. $b^{[4]} := b^{[4]} - \\alpha db^{[4]}$ \n\n3. $W^{[3]} := W^{[3]} - \\alpha dW^{[3]}$\n\n4. $b^{[3]} := b^{[3]} - \\alpha db^{[3]}$ \n\n5. $W^{[2]} := W^{[2]} - \\alpha dW^{[2]}$\n\n6.  $b^{[2]} := b^{[2]} - \\alpha db^{[2]}$\n\n7. $W^{[1]} := W^{[1]} - \\alpha dW^{[1]}$\n\n8. $b^{[1]} := b^{[1]} - \\alpha db^{[1]}$\n\n### Variable Shapes:\n\n- $A^{[0]}$ = X : \\( $64 \\times m$ \\)\n\n-  $Z^{[1]} \\sim A^{[1]}$ : \\( $128 \\times m$ \\)\n\n-  $W^{[1]}$ : \\( $128 \\times 64$ \\)\n\n- $b^{[1]}$: \\( $128 \\times 1$ \\)\n\n- $Z^{[2]} \\sim A^{[2]}$ : \\( $64 \\times m$ \\)\n\n- $\\ W^{[2]}$ \\: \\( $64 \\times 128$ \\)\n\n- $b^{[2]}$ \\: \\( $64 \\times 1$ \\)\n\n- $Z^{[3]} \\sim A^{[3]}$ : \\( $32 \\times m$ \\)\n\n- $W^{[3]}$: \\( $32 \\times 64$ \\)\n\n-  $b^{[3]}$: \\( $32 \\times 1$ \\)\n\n- $Z^{[4]} \\sim A^{[4]}$: \\( $10 \\times m$ \\)\n\n- $W^{[4]}$: \\( $10 \\times 32$ \\)\n\n- $b^{[4]}$ : \\( $10 \\times 1$ \\)","metadata":{}},{"cell_type":"code","source":"def initialize_parameters():\n    np.random.seed(1)  # For reproducibility, ensuring the same random numbers are generated each time\n\n    parameters = {}  # Initialize an empty dictionary to store the parameters\n\n    # Layer 1 (Input to Hidden Layer 1)\n    parameters['W1'] = np.random.randn(128, 64) * np.sqrt(2 / 64)  # Weight matrix for layer 1, using Xavier initialization for weights\n    parameters['b1'] = np.zeros((128, 1))  # Bias vector for layer 1, initialized as zeros\n\n    # Layer 2 (Hidden Layer 1 to Hidden Layer 2)\n    parameters['W2'] = np.random.randn(64, 128) * np.sqrt(2 / 128)  # Weight matrix for layer 2, using Xavier initialization for weights\n    parameters['b2'] = np.zeros((64, 1))  # Bias vector for layer 2, initialized as zeros\n\n    # Layer 3 (Hidden Layer 2 to Hidden Layer 3)\n    parameters['W3'] = np.random.randn(32, 64) * np.sqrt(2 / 64)  # Weight matrix for layer 3, using Xavier initialization for weights\n    parameters['b3'] = np.zeros((32, 1))  # Bias vector for layer 3, initialized as zeros\n\n    # Output Layer (Hidden Layer 3 to Output)\n    parameters['W4'] = np.random.randn(10, 32) * np.sqrt(2 / 32)  # Weight matrix for output layer, using Xavier initialization for weights\n    parameters['b4'] = np.zeros((10, 1))  # Bias vector for output layer, initialized as zeros\n\n    return parameters","metadata":{"execution":{"iopub.status.busy":"2024-10-05T14:57:10.235766Z","iopub.execute_input":"2024-10-05T14:57:10.236834Z","iopub.status.idle":"2024-10-05T14:57:10.247135Z","shell.execute_reply.started":"2024-10-05T14:57:10.236743Z","shell.execute_reply":"2024-10-05T14:57:10.245804Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def RELu(Z):\n    # np.maximum returns an element-wise maximum of array elements.\n    # Here, it ensures that all negative values in Z are set to 0, while non-negative values remain unchanged.\n    return np.maximum(0, Z)","metadata":{"execution":{"iopub.status.busy":"2024-10-05T14:57:18.649915Z","iopub.execute_input":"2024-10-05T14:57:18.650360Z","iopub.status.idle":"2024-10-05T14:57:18.655692Z","shell.execute_reply.started":"2024-10-05T14:57:18.650319Z","shell.execute_reply":"2024-10-05T14:57:18.654539Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def softmax(Z):\n    # Subtract the maximum value from Z to prevent overflow and ensure numerical stability\n    exp_Z = np.exp(Z - np.max(Z, axis=0, keepdims=True))\n    # Normalize the exponentiated values by dividing by their sum to ensure probabilities sum to 1\n    return exp_Z / np.sum(exp_Z, axis=0, keepdims=True)","metadata":{"execution":{"iopub.status.busy":"2024-10-05T14:57:26.528888Z","iopub.execute_input":"2024-10-05T14:57:26.529668Z","iopub.status.idle":"2024-10-05T14:57:26.535585Z","shell.execute_reply.started":"2024-10-05T14:57:26.529617Z","shell.execute_reply":"2024-10-05T14:57:26.534323Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def forward_propagation(X, parameters):\n    # Retrieve parameters for each layer\n    W1, b1 = parameters['W1'], parameters['b1']  # Layer 1 parameters\n    W2, b2 = parameters['W2'], parameters['b2']  # Layer 2 parameters\n    W3, b3 = parameters['W3'], parameters['b3']  # Layer 3 parameters\n    W4, b4 = parameters['W4'], parameters['b4']  # Layer 4 parameters\n\n    # Forward propagation: Layer 1 (Input -> Hidden Layer 1)\n    Z1 = np.dot(W1, X) + b1  # Compute the linear component of Layer 1\n    A1 = RELu(Z1)  # Apply ReLU activation function to Z1\n\n    # Forward propagation: Layer 2 (Hidden Layer 1 -> Hidden Layer 2)\n    Z2 = np.dot(W2, A1) + b2  # Compute the linear component of Layer 2\n    A2 = RELu(Z2)  # Apply ReLU activation function to Z2\n\n    # Forward propagation: Layer 3 (Hidden Layer 2 -> Hidden Layer 3)\n    Z3 = np.dot(W3, A2) + b3  # Compute the linear component of Layer 3\n    A3 = RELu(Z3)  # Apply ReLU activation function to Z3\n\n    # Forward propagation: Layer 4 (Hidden Layer 3 -> Output Layer)\n    Z4 = np.dot(W4, A3) + b4  # Compute the linear component of Layer 4\n    A4 = softmax(Z4)  # Apply softmax activation function to Z4 for output layer\n\n    # Store all activations and Z values for backpropagation\n    cache = {\n        \"Z1\": Z1, \"A1\": A1,  # Store Z and A for Layer 1\n        \"Z2\": Z2, \"A2\": A2,  # Store Z and A for Layer 2\n        \"Z3\": Z3, \"A3\": A3,  # Store Z and A for Layer 3\n        \"Z4\": Z4, \"A4\": A4   # Store Z and A for Layer 4\n    }\n    \n    return A4, cache","metadata":{"execution":{"iopub.status.busy":"2024-10-05T14:57:35.099859Z","iopub.execute_input":"2024-10-05T14:57:35.100428Z","iopub.status.idle":"2024-10-05T14:57:35.111819Z","shell.execute_reply.started":"2024-10-05T14:57:35.100379Z","shell.execute_reply":"2024-10-05T14:57:35.110311Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def relu_derivative(Z):\n    # Returns 1 where Z > 0, otherwise 0\n    return Z > 0\n\ndef backpropagation(X, Y, cache, parameters):\n    m = X.shape[1]  # Number of examples\n\n    # Retrieve cached values\n    A1, A2, A3, A4 = cache['A1'], cache['A2'], cache['A3'], cache['A4']\n    Z1, Z2, Z3, Z4 = cache['Z1'], cache['Z2'], cache['Z3'], cache['Z4']\n\n    # Retrieve parameters\n    W2, W3, W4 = parameters['W2'], parameters['W3'], parameters['W4']\n\n    # Backpropagation: Output Layer\n    dZ4 = A4 - Y  # dZ^{[4]} = A^{[4]} - Y\n    dW4 = (1 / m) * np.dot(dZ4, A3.T)  # dW^{[4]} = (1/m) * dZ^{[4]} * A^{[3]T}\n    db4 = (1 / m) * np.sum(dZ4, axis=1, keepdims=True)  # db^{[4]} = (1/m) * sum(dZ^{[4]}, axis=1)\n\n    # Backpropagation: Layer 3 (Hidden Layer 3 -> Hidden Layer 2)\n    dZ3 = np.dot(W4.T, dZ4) * relu_derivative(Z3)  # dZ^{[3]} = W^{[4]T} * dZ^{[4]} * g'(Z^{[3]})\n    dW3 = (1 / m) * np.dot(dZ3, A2.T)  # dW^{[3]} = (1/m) * dZ^{[3]} * A^{[2]T}\n    db3 = (1 / m) * np.sum(dZ3, axis=1, keepdims=True)  # db^{[3]} = (1/m) * sum(dZ^{[3]}, axis=1)\n\n    # Backpropagation: Layer 2 (Hidden Layer 2 -> Hidden Layer 1)\n    dZ2 = np.dot(W3.T, dZ3) * relu_derivative(Z2)  # dZ^{[2]} = W^{[3]T} * dZ^{[3]} * g'(Z^{[2]})\n    dW2 = (1 / m) * np.dot(dZ2, A1.T)  # dW^{[2]} = (1/m) * dZ^{[2]} * A^{[1]T}\n    db2 = (1 / m) * np.sum(dZ2, axis=1, keepdims=True)  # db^{[2]} = (1/m) * sum(dZ^{[2]}, axis=1)\n\n    # Backpropagation: Layer 1 (Hidden Layer 1 -> Input Layer)\n    dZ1 = np.dot(W2.T, dZ2) * relu_derivative(Z1)  # dZ^{[1]} = W^{[2]T} * dZ^{[2]} * g'(Z^{[1]})\n    dW1 = (1 / m) * np.dot(dZ1, X.T)  # dW^{[1]} = (1/m) * dZ^{[1]} * X^T\n    db1 = (1 / m) * np.sum(dZ1, axis=1, keepdims=True)  # db^{[1]} = (1/m) * sum(dZ^{[1]}, axis=1)\n\n    # Store gradients in a dictionary\n    gradients = {\n        'dW1': dW1, 'db1': db1,\n        'dW2': dW2, 'db2': db2,\n        'dW3': dW3, 'db3': db3,\n        'dW4': dW4, 'db4': db4\n    }\n\n    return gradients","metadata":{"execution":{"iopub.status.busy":"2024-10-05T14:57:49.595829Z","iopub.execute_input":"2024-10-05T14:57:49.596361Z","iopub.status.idle":"2024-10-05T14:57:49.612399Z","shell.execute_reply.started":"2024-10-05T14:57:49.596317Z","shell.execute_reply":"2024-10-05T14:57:49.611067Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def update_parameters(parameters, gradients, learning_rate):\n    # Update parameters for all layers\n    # Layer 1\n    parameters['W1'] = parameters['W1'] - learning_rate * gradients['dW1']  # Update weights for layer 1\n    parameters['b1'] = parameters['b1'] - learning_rate * gradients['db1']  # Update bias for layer 1\n\n    # Layer 2\n    parameters['W2'] = parameters['W2'] - learning_rate * gradients['dW2']  # Update weights for layer 2\n    parameters['b2'] = parameters['b2'] - learning_rate * gradients['db2']  # Update bias for layer 2\n\n    # Layer 3\n    parameters['W3'] = parameters['W3'] - learning_rate * gradients['dW3']  # Update weights for layer 3\n    parameters['b3'] = parameters['b3'] - learning_rate * gradients['db3']  # Update bias for layer 3\n\n    # Layer 4\n    parameters['W4'] = parameters['W4'] - learning_rate * gradients['dW4']  # Update weights for layer 4\n    parameters['b4'] = parameters['b4'] - learning_rate * gradients['db4']  # Update bias for layer 4\n\n    return parameters","metadata":{"execution":{"iopub.status.busy":"2024-10-05T14:57:59.144369Z","iopub.execute_input":"2024-10-05T14:57:59.144822Z","iopub.status.idle":"2024-10-05T14:57:59.153209Z","shell.execute_reply.started":"2024-10-05T14:57:59.144780Z","shell.execute_reply":"2024-10-05T14:57:59.151970Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def compute_cost(A4, Y):\n    # Calculate the number of training examples\n    m = Y.shape[1]\n    # Define a small value to avoid division by zero\n    epsilon = 1e-15\n    # Clip the values of A4 to ensure they are not too close to 0 or 1\n    A4 = np.clip(A4, epsilon, 1 - epsilon)\n    # Calculate the cost using the formula for binary cross-entropy loss\n    cost = -np.sum(Y * np.log(A4)) / m\n    return cost","metadata":{"execution":{"iopub.status.busy":"2024-10-05T14:58:08.461113Z","iopub.execute_input":"2024-10-05T14:58:08.461574Z","iopub.status.idle":"2024-10-05T14:58:08.468326Z","shell.execute_reply.started":"2024-10-05T14:58:08.461532Z","shell.execute_reply":"2024-10-05T14:58:08.467008Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def train_neural_network(X_train, Y_train, parameters, learning_rate=0.01, num_iterations=1000, print_cost=False):\n    costs = []  # Initialize a list to keep track of the cost at each epoch\n\n    for i in range(num_iterations):\n        # 1. Forward Propagation: Compute the output of the neural network\n        A4, cache = forward_propagation(X_train, parameters)\n        \n        # 2. Compute the cost: Calculate the difference between the predicted output and the actual output\n        cost = compute_cost(A4, Y_train)\n        \n        # 3. Backward Propagation: Compute the gradients of the cost with respect to the parameters\n        gradients = backpropagation(X_train, Y_train, cache, parameters)\n        \n        # 4. Update Parameters: Adjust the parameters to minimize the cost\n        parameters = update_parameters(parameters, gradients, learning_rate)\n        \n        # 5. Store and print the cost every 100 iterations\n        if i % 100 == 0:\n            costs.append(cost)  # Store the cost at this iteration\n            if print_cost:\n                print(f\"Cost after iteration {i}: {cost}\")  # Print the cost if print_cost is True\n\n    return costs","metadata":{"execution":{"iopub.status.busy":"2024-10-05T14:58:18.048813Z","iopub.execute_input":"2024-10-05T14:58:18.049273Z","iopub.status.idle":"2024-10-05T14:58:18.057631Z","shell.execute_reply.started":"2024-10-05T14:58:18.049231Z","shell.execute_reply":"2024-10-05T14:58:18.056438Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def predict(X, parameters):\n    # Forward propagate the input X through the network to get the output\n    AL, _ = forward_propagation(X, parameters)\n    # Return the index of the maximum value along the 0th axis, which corresponds to the predicted class\n    return np.argmax(AL, axis=0)","metadata":{"execution":{"iopub.status.busy":"2024-10-05T14:58:29.348785Z","iopub.execute_input":"2024-10-05T14:58:29.349233Z","iopub.status.idle":"2024-10-05T14:58:29.355170Z","shell.execute_reply.started":"2024-10-05T14:58:29.349192Z","shell.execute_reply":"2024-10-05T14:58:29.354011Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Initialize the parameters for the neural network\nparameters = initialize_parameters()\n\n# Train the neural network with the specified parameters and hyperparameters\ncost = train_neural_network(X_train, y_train_one_hot, parameters, learning_rate=0.1, num_iterations=2500, print_cost=True)\n\n# Use the trained parameters to make predictions on the test set\ntest_predictions = predict(X_test, parameters)\n\n# Calculate the accuracy of the predictions\ntest_accuracy = accuracy_score(np.argmax(y_test_one_hot, axis=0), test_predictions)\nprint(f\"Test Accuracy: {test_accuracy:.4f}\")  # Print the test accuracy\n\n# Generate and print the classification report for the predictions\nprint(\"\\nClassification Report:\")\nprint(classification_report(np.argmax(y_test_one_hot, axis=0), test_predictions))\n\n# Plot the cost over iterations to visualize the training process\nplt.figure(figsize=(8, 6))  # Set the figure size\nsns.lineplot(x=range(0, 2500, 100), y=cost, marker='o', color='#e74645', linewidth=2)  # Plot the cost\nplt.title('Cost vs. Iterations')  # Set the title\nplt.xlabel('Iterations')  # Set the x-axis label\nplt.ylabel('Cost')  # Set the y-axis label\nplt.show()  # Show the plot","metadata":{"execution":{"iopub.status.busy":"2024-10-05T14:58:40.682328Z","iopub.execute_input":"2024-10-05T14:58:40.682777Z","iopub.status.idle":"2024-10-05T14:59:06.838766Z","shell.execute_reply.started":"2024-10-05T14:58:40.682734Z","shell.execute_reply":"2024-10-05T14:59:06.837683Z"},"trusted":true},"execution_count":null,"outputs":[]}]}