{
  "nbformat": 4,
  "nbformat_minor": 0,
  "metadata": {
    "colab": {
      "provenance": []
    },
    "kernelspec": {
      "name": "python3",
      "display_name": "Python 3"
    },
    "language_info": {
      "name": "python"
    }
  },
  "cells": [
    {
      "cell_type": "code",
      "execution_count": 4,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "mk43RirOWyAi",
        "outputId": "2fdd1192-1c11-47de-b584-efaa2cbc670f"
      },
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "Epoch 0 Error: 0.4993\n",
            "Epoch 1000 Error: 0.4992\n",
            "Epoch 2000 Error: 0.4901\n",
            "Epoch 3000 Error: 0.4318\n",
            "Epoch 4000 Error: 0.3821\n",
            "Epoch 5000 Error: 0.3542\n",
            "Epoch 6000 Error: 0.3367\n",
            "Epoch 7000 Error: 0.3236\n",
            "Epoch 8000 Error: 0.3134\n",
            "Epoch 9000 Error: 0.3052\n",
            "\n",
            "Final Output After Training:\n",
            "[[0.]\n",
            " [1.]\n",
            " [1.]\n",
            " [1.]]\n"
          ]
        }
      ],
      "source": [
        "import numpy as np\n",
        "\n",
        "# Sigmoid function and derivative\n",
        "def sigmoid(x):\n",
        "    return 1 / (1 + np.exp(-x))\n",
        "\n",
        "def sigmoid_derivative(x):\n",
        "    return x * (1 - x)\n",
        "\n",
        "# XOR input and output\n",
        "X = np.array([[0,0],\n",
        "              [0,1],\n",
        "              [1,0],\n",
        "              [1,1]])\n",
        "\n",
        "y = np.array([[0],\n",
        "              [1],\n",
        "              [1],\n",
        "              [0]])\n",
        "\n",
        "# Seed for reproducibility\n",
        "np.random.seed(1)\n",
        "\n",
        "# Initialize weights randomly with mean 0\n",
        "input_layer_neurons = 2\n",
        "hidden_layer_neurons = 2\n",
        "output_neurons = 1\n",
        "\n",
        "# Weights\n",
        "weights_input_hidden = 2 * np.random.rand(input_layer_neurons, hidden_layer_neurons) - 1\n",
        "weights_hidden_output = 2 * np.random.rand(hidden_layer_neurons, output_neurons) - 1\n",
        "\n",
        "# Training\n",
        "epochs = 10000\n",
        "learning_rate = 0.1\n",
        "\n",
        "for epoch in range(epochs):\n",
        "    # ---- FORWARD PROPAGATION ----\n",
        "    hidden_input = np.dot(X, weights_input_hidden)\n",
        "    hidden_output = sigmoid(hidden_input)\n",
        "\n",
        "    final_input = np.dot(hidden_output, weights_hidden_output)\n",
        "    predicted_output = sigmoid(final_input)\n",
        "\n",
        "    # ---- BACKPROPAGATION ----\n",
        "    error = y - predicted_output\n",
        "    d_predicted_output = error * sigmoid_derivative(predicted_output)\n",
        "\n",
        "    error_hidden_layer = d_predicted_output.dot(weights_hidden_output.T)\n",
        "    d_hidden_layer = error_hidden_layer * sigmoid_derivative(hidden_output)\n",
        "\n",
        "    # ---- UPDATE WEIGHTS ----\n",
        "    weights_hidden_output += hidden_output.T.dot(d_predicted_output) * learning_rate\n",
        "    weights_input_hidden += X.T.dot(d_hidden_layer) * learning_rate\n",
        "\n",
        "    # Print error occasionally\n",
        "    if epoch % 1000 == 0:\n",
        "        print(f\"Epoch {epoch} Error: {np.mean(np.abs(error)):.4f}\")\n",
        "\n",
        "# Final result\n",
        "print(\"\\nFinal Output After Training:\")\n",
        "print(predicted_output.round())\n"
      ]
    }
  ]
}