<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Dev Vaibhav</title>
    <description>The latest articles on DEV Community by Dev Vaibhav (@dev_vaibhav_37).</description>
    <link>https://dev.to/dev_vaibhav_37</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4133036%2F48caa67e-28cc-473a-bd33-7fd29b35558f.jpg</url>
      <title>DEV Community: Dev Vaibhav</title>
      <link>https://dev.to/dev_vaibhav_37</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/dev_vaibhav_37"/>
    <language>en</language>
    <item>
      <title>Building a Neural Network From Scratch: The Mathematics Behind Learning XOR</title>
      <dc:creator>Dev Vaibhav</dc:creator>
      <pubDate>Mon, 19 Oct 2026 08:03:33 +0000</pubDate>
      <link>https://dev.to/dev_vaibhav_37/building-a-neural-network-from-scratch-the-mathematics-behind-learning-xor-1o4b</link>
      <guid>https://dev.to/dev_vaibhav_37/building-a-neural-network-from-scratch-the-mathematics-behind-learning-xor-1o4b</guid>
      <description>&lt;p&gt;&lt;em&gt;Understanding forward propagation, backpropagation, and gradient descent through a neural network built using Python, NumPy, and mathematics.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Introduction
&lt;/h3&gt;

&lt;p&gt;Making a neural network produce predictions is relatively straightforward when using modern deep-learning frameworks. Understanding how the network actually learns is a different challenge.&lt;/p&gt;

&lt;p&gt;How does it calculate its output? How does it measure an incorrect prediction? And how do derivatives determine which weights and biases need to change?&lt;/p&gt;

&lt;p&gt;I wanted to understand these operations without hiding them behind automatic differentiation.&lt;/p&gt;

&lt;p&gt;So I built a fully trainable neural network from scratch using Python and NumPy, implementing the mathematical operations myself without relying on PyTorch or TensorFlow.&lt;/p&gt;

&lt;p&gt;The network follows a &lt;strong&gt;2 → 3 → 1 architecture&lt;/strong&gt; and learns the XOR function.&lt;/p&gt;

&lt;p&gt;Despite its small size, the implementation brings together several fundamental concepts: matrix multiplication, activation functions, loss functions, the chain rule, backpropagation, and gradient descent.&lt;/p&gt;

&lt;p&gt;In this article, I’ll break down these concepts, explain the mathematics behind them, and connect the equations to their actual Python implementation.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;AI-assisted writing and visuals.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The goal isn’t just to build a working neural network. &lt;strong&gt;It’s to understand how it learns.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;1. Why XOR?&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;XOR (exclusive OR) is a logical operation that returns 1 when its two binary inputs are different and 0 when they are the same.&lt;/p&gt;

&lt;p&gt;Despite having only four possible input combinations, XOR presents an interesting challenge.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F40zjz49y1jgcm5ejkj4z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F40zjz49y1jgcm5ejkj4z.png" width="800" height="325"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Figure 1 — XOR truth table and input-space visualization. AI-generated illustration.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;A single linear classifier cannot correctly separate the two classes using one straight decision boundary.&lt;/p&gt;

&lt;p&gt;Consider a neuron computing:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;z = w₁x₁ + w₂x₂ + b&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Here, x₁ and x₂ represent the input features, w₁ and w₂ are their corresponding weights, and b is the bias.&lt;/p&gt;

&lt;p&gt;Applying a threshold to this weighted sum produces a linear decision boundary. However, the positive XOR examples occupy opposite corners of the input space, as do the negative examples.&lt;/p&gt;

&lt;p&gt;No single straight line can correctly separate both classes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;XOR is not linearly separable.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;To solve this problem, we need a network capable of learning nonlinear relationships. A hidden layer combined with nonlinear activation functions makes this possible.&lt;/p&gt;
&lt;h3&gt;
  
  
  2. Network Architecture
&lt;/h3&gt;

&lt;p&gt;To solve XOR, I built a small fully connected feedforward neural network with a &lt;strong&gt;2 → 3 → 1 architecture&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The network consists of three layers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Input layer:&lt;/strong&gt; Two input features (x₁ and x₂), representing the binary XOR inputs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hidden layer:&lt;/strong&gt; Three neurons using the Rectified Linear Unit (ReLU) activation function.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Output layer:&lt;/strong&gt; One neuron using sigmoid activation to produce a value between 0 and 1.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Every input connects to each hidden neuron, and all three hidden neurons connect to the output neuron.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fdlyrqbk7j7o536kk9brr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fdlyrqbk7j7o536kk9brr.png" width="800" height="600"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Figure 2 — The 2 → 3 → 1 neural network architecture, showing two inputs, three hidden neurons, and one output. AI-generated illustration.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Each connection has a trainable weight, while every hidden neuron and the output neuron have their own bias.&lt;/p&gt;

&lt;p&gt;In total, the network contains &lt;strong&gt;13 trainable parameters&lt;/strong&gt; :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;6 hidden-layer weights&lt;/li&gt;
&lt;li&gt;3 hidden-layer biases&lt;/li&gt;
&lt;li&gt;3 output-layer weights&lt;/li&gt;
&lt;li&gt;1 output bias&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These parameters determine how the network transforms inputs into predictions. During training, their values are gradually adjusted to minimize prediction error.&lt;/p&gt;

&lt;p&gt;The hidden layer introduces nonlinearity through ReLU, allowing the network to learn patterns that a single linear classifier cannot represent.&lt;/p&gt;

&lt;p&gt;The output layer uses sigmoid because we’re solving a binary classification problem. Its output is interpreted as a prediction score, with 0.5 used as the classification threshold.&lt;/p&gt;

&lt;p&gt;Despite its small architecture, this network is sufficient to learn the four XOR combinations.&lt;/p&gt;
&lt;h3&gt;
  
  
  3. Preparing the Dataset and Initializing Parameters
&lt;/h3&gt;

&lt;p&gt;Before implementing forward propagation, we need to prepare our training data and initialize the network’s weights and biases.&lt;/p&gt;
&lt;h4&gt;
  
  
  Preparing the XOR Dataset
&lt;/h4&gt;

&lt;p&gt;Our dataset contains all four possible XOR input combinations.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;numpy&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;

&lt;span class="n"&gt;X&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;array&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;array&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;X&lt;/strong&gt; represents the input features, with shape (4, 2).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;y&lt;/strong&gt; represents the expected outputs, with shape (4, 1).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The four rows represent training examples, while the two columns of X represent input features.&lt;/p&gt;

&lt;p&gt;Instead of processing each example separately, we’ll use &lt;strong&gt;NumPy vectorization&lt;/strong&gt; to perform calculations across the entire dataset.&lt;/p&gt;

&lt;h3&gt;
  
  
  Initializing Weights and Biases
&lt;/h3&gt;

&lt;p&gt;Our network contains two sets of weights and biases: one for the hidden layer and another for the output layer.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;rng&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;default_rng&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Hidden layer
&lt;/span&gt;&lt;span class="n"&gt;W_h&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rng&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;normal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="n"&gt;b_h&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;zeros&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Output layer
&lt;/span&gt;&lt;span class="n"&gt;W_o&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rng&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;normal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="n"&gt;b_o&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;zeros&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We initialize the weights using a normal distribution with a mean of 0 and a standard deviation of 0.5.&lt;/p&gt;

&lt;p&gt;The random seed &lt;strong&gt;42&lt;/strong&gt; makes our initialization reproducible, allowing us to obtain the same starting weights across runs.&lt;/p&gt;

&lt;p&gt;The biases are initially set to zero.&lt;/p&gt;

&lt;p&gt;Why random weights?&lt;/p&gt;

&lt;p&gt;If all hidden neurons started with identical weights and biases, they would receive identical gradients and could continue learning the same features.&lt;/p&gt;

&lt;p&gt;Random initialization helps break this symmetry, allowing different neurons to learn different representations.&lt;/p&gt;

&lt;p&gt;With our dataset and parameters ready, we can begin the first forward pass and observe how the network produces its initial predictions.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Forward Propagation: How the Network Makes Predictions
&lt;/h3&gt;

&lt;p&gt;Forward propagation is the process of passing input data through a neural network to calculate its predictions.&lt;/p&gt;

&lt;p&gt;At each trainable layer, two operations occur:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Calculate a weighted sum and add a bias.&lt;/li&gt;
&lt;li&gt;Apply an activation function.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Let’s break down how these operations work in our 2 → 3 → 1 network.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fy1cwnxsdr9vyqpi7uyr5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fy1cwnxsdr9vyqpi7uyr5.png" width="800" height="600"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Figure 3 — Forward propagation through the hidden and output layers. AI-generated illustration.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The diagram uses single-example column-vector notation, while the NumPy implementation processes row-wise batches.&lt;/p&gt;
&lt;h3&gt;
  
  
  Step 1: Hidden Layer - Weighted Sum
&lt;/h3&gt;

&lt;p&gt;The first operation is:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Zₕ = XWₕ + bₕ&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Where:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;X:&lt;/strong&gt; Input matrix with shape (4, 2).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Wₕ:&lt;/strong&gt; Hidden-layer weights with shape (2, 3).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;bₕ:&lt;/strong&gt; Hidden-layer biases with shape (3,).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Zₕ:&lt;/strong&gt; Resulting weighted sums with shape (4, 3).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In NumPy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;z_h&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;X&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt; &lt;span class="n"&gt;W_h&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;b_h&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The @ operator performs matrix multiplication.&lt;/p&gt;

&lt;p&gt;The dimensions are:&lt;/p&gt;

&lt;p&gt;(4, 2) @ (2, 3) → (4, 3)&lt;/p&gt;

&lt;p&gt;NumPy then broadcasts the three biases across all four training examples.&lt;/p&gt;

&lt;p&gt;Each row now contains three weighted sums, one for each hidden neuron.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: Applying ReLU
&lt;/h3&gt;

&lt;p&gt;Next, we introduce nonlinearity using the Rectified Linear Unit (ReLU).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ReLU(z) = max(0, z)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;ReLU replaces negative values with zero while keeping positive values unchanged.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;h&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;maximum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;z_h&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For example:&lt;/p&gt;

&lt;p&gt;ReLU([-2, 3, -1]) → [0, 3, 0]&lt;/p&gt;

&lt;p&gt;Why is this important?&lt;/p&gt;

&lt;p&gt;Without nonlinear activation functions, stacking multiple affine layers would still produce an affine transformation. The network would remain unable to learn XOR’s nonlinear decision boundary.&lt;/p&gt;

&lt;p&gt;The resulting hidden activation matrix has shape (4, 3).&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: Output Layer - Weighted Sum
&lt;/h3&gt;

&lt;p&gt;The hidden activations now become inputs to the output neuron.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Zₒ = HWₒ + bₒ&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Where:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;H:&lt;/strong&gt; Hidden activations with shape (4, 3).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Wₒ:&lt;/strong&gt; Output weights with shape (3, 1).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;bₒ:&lt;/strong&gt; Output bias with shape (1,).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Zₒ:&lt;/strong&gt; Output weighted sums with shape (4, 1).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In NumPy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;z_o&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;h&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt; &lt;span class="n"&gt;W_o&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;b_o&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Matrix multiplication:&lt;/p&gt;

&lt;p&gt;(4, 3) @ (3, 1) → (4, 1)&lt;/p&gt;

&lt;p&gt;We now have one output value for each training example, before activation.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4: Applying Sigmoid
&lt;/h3&gt;

&lt;p&gt;Finally, we apply the sigmoid activation function.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;σ(z) = 1 / (1 + e⁻ᶻ)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Sigmoid maps values into the interval between 0 and 1, making it useful for producing scores in binary classification.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;a_o&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;z_o&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We use 0.5 as our classification threshold:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Output ≥ 0.5 → Class 1&lt;/li&gt;
&lt;li&gt;Output below 0.5 → Class 0&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At this point, our network has successfully completed a forward pass.&lt;/p&gt;

&lt;p&gt;However, &lt;strong&gt;making predictions is not the same as learning&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The initial outputs depend entirely on the randomly initialized parameters. To improve those predictions, the network must first measure how far they are from the expected values.&lt;/p&gt;

&lt;p&gt;Before discussing the loss, let’s follow one actual input through our initialized network.&lt;/p&gt;

&lt;h4&gt;
  
  
  4.1 A Forward Pass With Actual Numbers
&lt;/h4&gt;

&lt;p&gt;Let’s examine what happens when our randomly initialized network receives the input:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;X = [0, 1]&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Using our initial weights (seed 42), the hidden-layer weighted sums are approximately:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Zₕ = [0.470282, −0.975518, −0.651090]&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Applying ReLU:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;H = [0.470282, 0, 0]&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Only the first hidden neuron remains active. The other two produce zero because their weighted sums are negative.&lt;/p&gt;

&lt;p&gt;Next, we calculate the output weighted sum:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;zₒ = 0.470282 × 0.063920 ≈ 0.030061&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Applying sigmoid:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ŷ = 1 / (1 + e⁻⁰·⁰³⁰⁰⁶¹) ≈ 0.507515&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The correct XOR target for (0, 1) is 1.&lt;/p&gt;

&lt;p&gt;Our untrained network produces approximately 0.5075. Although this is already above the classification threshold of 0.5, the prediction still has considerable error.&lt;/p&gt;

&lt;p&gt;This is why classification accuracy alone doesn’t tell us how well the network has learned.&lt;/p&gt;

&lt;p&gt;We need a loss function.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Measuring Error With Mean Squared Error
&lt;/h3&gt;

&lt;p&gt;To measure the difference between predictions and their actual targets, we’ll use &lt;strong&gt;Mean Squared Error (MSE)&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The formula is:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;MSE = (1/N) Σ(ŷᵢ − yᵢ)²&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Where:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;N&lt;/strong&gt; represents the number of training examples.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ŷᵢ&lt;/strong&gt; represents the predicted output.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;yᵢ&lt;/strong&gt; represents the actual target.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Σ&lt;/strong&gt; represents summation across all examples.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For our network, N = 4 because we have four XOR combinations.&lt;/p&gt;

&lt;p&gt;Consider our previous example:&lt;/p&gt;

&lt;p&gt;Target = 1&lt;/p&gt;

&lt;p&gt;Prediction ≈ 0.507515&lt;/p&gt;

&lt;p&gt;The squared error becomes:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;(0.507515 − 1)² ≈ 0.242542&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We repeat this calculation for all four examples and compute their average.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fr7cdzgg55i5osy8mj5mc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fr7cdzgg55i5osy8mj5mc.png" width="799" height="391"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Figure 4 — Initial XOR predictions, squared errors, and MSE before training. Values reproduced from the actual NumPy implementation.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;In NumPy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;error&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;a_o&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;
&lt;span class="n"&gt;squared_error&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
&lt;span class="n"&gt;loss&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mean&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;squared_error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At initialization, the network’s actual loss is approximately:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;MSE = 0.25023636&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Our objective is to reduce this value by adjusting the weights and biases.&lt;/p&gt;

&lt;p&gt;However, calculating the loss only tells us how wrong the predictions are. It doesn’t tell us which parameters to change or by how much.&lt;/p&gt;

&lt;p&gt;To answer that question, we need derivatives and the chain rule.&lt;/p&gt;

&lt;p&gt;That’s where &lt;strong&gt;backpropagation&lt;/strong&gt; begins.&lt;/p&gt;

&lt;h3&gt;
  
  
  6. Backpropagation: Understanding the Chain Rule
&lt;/h3&gt;

&lt;p&gt;So far, our network can produce predictions and calculate their errors using Mean Squared Error.&lt;/p&gt;

&lt;p&gt;But one important question remains:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How does the network know which weights and biases to change to reduce the loss?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is where backpropagation comes in.&lt;/p&gt;

&lt;p&gt;Backpropagation calculates the gradients of the loss with respect to each trainable parameter using the &lt;strong&gt;chain rule of differentiation&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;A gradient tells us how sensitive the loss is to a small change in a particular parameter.&lt;/p&gt;

&lt;p&gt;Before calculating these gradients, we need to understand how the operations depend on one another.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F4au1oijxdyvzf5i536ug.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F4au1oijxdyvzf5i536ug.png" width="800" height="600"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Figure 5 — Gradient flow from the loss through the output and hidden layers. AI-generated illustration.&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  6.1 Understanding the Chain Rule
&lt;/h3&gt;

&lt;p&gt;Recall the equations from forward propagation:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;z = wh + b&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;a = σ(z)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;L = (a − y)²&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;h&lt;/strong&gt; represents an activation entering the neuron.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;w&lt;/strong&gt; represents its weight.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;b&lt;/strong&gt; represents its bias.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;z&lt;/strong&gt; represents the weighted sum.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;a&lt;/strong&gt; represents the activated output.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;y&lt;/strong&gt; represents the expected target.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;L&lt;/strong&gt; represents the squared error for one example.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Notice how these operations depend on one another.&lt;/p&gt;

&lt;p&gt;The weight influences the weighted sum, the weighted sum influences the activation, and the activation influences the loss.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;w → z → a → L&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;To calculate how changing the weight affects the loss, we apply the chain rule:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;∂L/∂w = (∂L/∂a) × (∂a/∂z) × (∂z/∂w)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This expression contains three derivatives:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;∂L/∂a:&lt;/strong&gt; How the loss changes when the activation changes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;∂a/∂z:&lt;/strong&gt; How the activation changes when the weighted sum changes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;∂z/∂w:&lt;/strong&gt; How the weighted sum changes when the weight changes.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Multiplying them gives us the gradient of the loss with respect to the weight.&lt;/p&gt;

&lt;p&gt;Our actual network uses four training examples, so we’ll apply this same principle to the batch MSE.&lt;/p&gt;
&lt;h3&gt;
  
  
  6.2 Differentiating Mean Squared Error
&lt;/h3&gt;

&lt;p&gt;Recall our loss function:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;L = (1/N) Σ(aᵢ − yᵢ)²&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We want to calculate its derivative with respect to each prediction, aᵢ.&lt;/p&gt;

&lt;p&gt;For one example, the squared error is:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;(aᵢ − yᵢ)²&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Using the power rule and chain rule:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;d/daᵢ [(aᵢ − yᵢ)²] = 2(aᵢ − yᵢ)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Since MSE averages the squared errors across N examples, we include the factor 1/N:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;∂L/∂aᵢ = (2/N)(aᵢ − yᵢ)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For our complete batch, the NumPy implementation is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;dl_da_o&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;X&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a_o&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, len(X) equals 4, and the resulting gradient array has shape (4, 1).&lt;/p&gt;

&lt;p&gt;We have now calculated the first derivative needed for backpropagation.&lt;/p&gt;

&lt;p&gt;Next, we’ll differentiate the sigmoid activation and combine both derivatives using the chain rule.&lt;/p&gt;

&lt;h3&gt;
  
  
  6.3 Differentiating the Sigmoid Activation
&lt;/h3&gt;

&lt;p&gt;From forward propagation, recall the sigmoid function:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;a = σ(z) = 1 / (1 + e⁻ᶻ)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Here, z represents the output neuron’s weighted sum, and a represents its activated output.&lt;/p&gt;

&lt;p&gt;To understand how changes in z affect a, we differentiate sigmoid with respect to z.&lt;/p&gt;

&lt;p&gt;The derivative is:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;∂a/∂z = e⁻ᶻ / (1 + e⁻ᶻ)²&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This expression can be simplified using the original sigmoid function:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;∂a/∂z = σ(z)(1 − σ(z))&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Since we already calculated the sigmoid output during forward propagation, we can reuse it directly:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;∂a/∂z = a(1 − a)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In NumPy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;da_od_zo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;a_o&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;a_o&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This calculates the sigmoid derivative for all four training examples simultaneously.&lt;/p&gt;

&lt;p&gt;The resulting array has shape (4, 1).&lt;/p&gt;

&lt;h3&gt;
  
  
  6.4 Applying the Chain Rule
&lt;/h3&gt;

&lt;p&gt;We now have two derivatives.&lt;/p&gt;

&lt;p&gt;From Section 6.2, the MSE derivative:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;∂L/∂a = (2/N)(a − y)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;And from Section 6.3, the sigmoid derivative:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;∂a/∂z = a(1 − a)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We want to calculate how the loss changes with respect to the output weighted sum, z.&lt;/p&gt;

&lt;p&gt;Using the chain rule:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;∂L/∂z = (∂L/∂a) × (∂a/∂z)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Substituting both expressions:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;∂L/∂z = (2/N)(a − y) × a(1 − a)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For the entire batch, multiplication is performed elementwise.&lt;/p&gt;

&lt;p&gt;In NumPy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;dl_dz_o&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dl_da_o&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;da_od_zo&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The resulting gradient has shape (4, 1).&lt;/p&gt;

&lt;p&gt;We have now propagated the loss gradient backward through the sigmoid activation.&lt;/p&gt;

&lt;p&gt;The next step is to determine how the output weights and bias contribute to this gradient.&lt;/p&gt;

&lt;h3&gt;
  
  
  6.5 Calculating Output Weight and Bias Gradients
&lt;/h3&gt;

&lt;p&gt;Recall our output-layer equation:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Zₒ = HWₒ + bₒ&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;H:&lt;/strong&gt; Hidden-layer activations.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Wₒ:&lt;/strong&gt; Output-layer weights.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;bₒ:&lt;/strong&gt; Output-layer bias.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Zₒ:&lt;/strong&gt; Output pre-activation values.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For a single training example, the output weighted sum is:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;zₒ = h₁w₁ + h₂w₂ + h₃w₃ + bₒ&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let’s differentiate it with respect to the first output weight, w₁.&lt;/p&gt;

&lt;p&gt;Since the other terms do not depend on w₁:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;∂zₒ/∂w₁ = h₁&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Similarly:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;∂zₒ/∂w₂ = h₂&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;∂zₒ/∂w₃ = h₃&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Now apply the chain rule:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;∂L/∂w₁ = (∂L/∂zₒ) × h₁&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The same principle applies to the remaining output weights.&lt;/p&gt;

&lt;p&gt;Because we’re processing four examples, we sum their contributions using matrix multiplication.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;∂L/∂Wₒ = Hᵀ @ (∂L/∂Zₒ)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In NumPy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;dW_o&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt; &lt;span class="n"&gt;dl_dz_o&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let’s verify the matrix dimensions:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;(3, 4) @ (4, 1) → (3, 1)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The result contains three gradients, one for each output weight.&lt;/p&gt;

&lt;h4&gt;
  
  
  Calculating the Output Bias Gradient
&lt;/h4&gt;

&lt;p&gt;The output weighted sum also depends on its bias:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;zₒ = h₁w₁ + h₂w₂ + h₃w₃ + bₒ&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Differentiating with respect to bₒ:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;∂zₒ/∂bₒ = 1&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Applying the chain rule:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;∂L/∂bₒ = (∂L/∂zₒ) × 1&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For the complete batch, we sum the contributions from all four examples:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;db_o&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dl_dz_o&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;axis&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The result has shape (1,), since our output layer contains only one bias.&lt;/p&gt;

&lt;p&gt;Notice that we don’t divide by N again. The averaging factor was already included when differentiating MSE.&lt;/p&gt;

&lt;p&gt;At this point, we’ve calculated the gradients for all three output weights and the output bias.&lt;/p&gt;

&lt;p&gt;However, our network also contains six hidden-layer weights and three hidden biases.&lt;/p&gt;

&lt;p&gt;To calculate their gradients, we need to propagate the error backward through the hidden layer and its ReLU activation.&lt;/p&gt;

&lt;h3&gt;
  
  
  6.6 Backpropagating Through the Hidden Layer
&lt;/h3&gt;

&lt;p&gt;So far, we’ve calculated the gradients for the output layer: three weights and one bias.&lt;/p&gt;

&lt;p&gt;But our network also contains six hidden-layer weights and three hidden biases.&lt;/p&gt;

&lt;p&gt;To calculate their gradients, we need to propagate the loss backward through the hidden layer.&lt;/p&gt;

&lt;p&gt;Recall the equations from forward propagation:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Zₕ = XWₕ + bₕ&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;H = ReLU(Zₕ)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Zₒ = HWₒ + bₒ&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The hidden activations influence the output weighted sum, which influences the prediction and ultimately the loss.&lt;/p&gt;

&lt;p&gt;We already calculated &lt;strong&gt;∂L/∂Zₒ&lt;/strong&gt; in Section 6.4.&lt;/p&gt;

&lt;p&gt;Now we want to determine how the loss changes with respect to the hidden activations, H.&lt;/p&gt;

&lt;p&gt;For a single example, the output weighted sum is:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;zₒ = h₁w₁ + h₂w₂ + h₃w₃ + bₒ&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Differentiating with respect to each hidden activation:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;∂zₒ/∂h₁ = w₁&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;∂zₒ/∂h₂ = w₂&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;∂zₒ/∂h₃ = w₃&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Applying the chain rule:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;∂L/∂hⱼ = (∂L/∂zₒ) × wⱼ&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For the entire batch, we can calculate these gradients using matrix multiplication:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;∂L/∂H = (∂L/∂Zₒ) @ Wₒᵀ&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In NumPy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;dl_dh&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dl_dz_o&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt; &lt;span class="n"&gt;W_o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let’s verify the dimensions:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;(4, 1) @ (1, 3) → (4, 3)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The resulting matrix contains the loss gradients for all three hidden activations across all four training examples.&lt;/p&gt;

&lt;p&gt;Notice that we use &lt;strong&gt;Wₒᵀ&lt;/strong&gt; , the transpose of the output weight matrix, not the output weight gradients.&lt;/p&gt;

&lt;h3&gt;
  
  
  6.7 Differentiating ReLU
&lt;/h3&gt;

&lt;p&gt;Our hidden layer uses ReLU:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;h = ReLU(z) = max(0, z)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Its derivative is:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ReLU’(z) = 1 when z &amp;gt; 0, otherwise 0&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;At exactly zero, ReLU is not differentiable. Our implementation uses a derivative value of 0.&lt;/p&gt;

&lt;p&gt;This means gradients pass through positive hidden pre-activations, while inactive ReLU units receive zero gradient through that activation.&lt;/p&gt;

&lt;p&gt;We can calculate the derivative using a Boolean mask:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;dh_dhz&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;z_h&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;astype&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The expression (z_h &amp;gt; 0) returns True for positive values and False otherwise.&lt;/p&gt;

&lt;p&gt;Using .astype(float) converts these Boolean values into 1.0 and 0.0.&lt;/p&gt;

&lt;p&gt;Now we apply the chain rule:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;∂L/∂Zₕ = (∂L/∂H) × ReLU’(Zₕ)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In NumPy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;dl_dzh&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dl_dh&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;dh_dhz&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This operation uses elementwise multiplication.&lt;/p&gt;

&lt;p&gt;Both arrays have shape (4, 3), so the result also has shape (4, 3).&lt;/p&gt;

&lt;p&gt;We have now propagated the loss gradient backward through the hidden activation function.&lt;/p&gt;

&lt;h3&gt;
  
  
  6.8 Calculating Hidden Weight Gradients
&lt;/h3&gt;

&lt;p&gt;Recall the hidden-layer weighted sum:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Zₕ = XWₕ + bₕ&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For a single hidden neuron, its weighted sum is:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;zₕ = x₁w₁ + x₂w₂ + bₕ&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Differentiating with respect to the first weight:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;∂zₕ/∂w₁ = x₁&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Similarly:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;∂zₕ/∂w₂ = x₂&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Applying the chain rule:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;∂L/∂w₁ = (∂L/∂zₕ) × x₁&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;∂L/∂w₂ = (∂L/∂zₕ) × x₂&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We apply this principle to all three hidden neurons and sum contributions across the four training examples.&lt;/p&gt;

&lt;p&gt;The matrix equation becomes:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;∂L/∂Wₕ = Xᵀ @ (∂L/∂Zₕ)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In NumPy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;dW_h&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;X&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt; &lt;span class="n"&gt;dl_dzh&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Checking the dimensions:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;(2, 4) @ (4, 3) → (2, 3)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The resulting gradient matrix contains six values, corresponding to our six hidden-layer weights.&lt;/p&gt;

&lt;h3&gt;
  
  
  6.9 Calculating Hidden Bias Gradients
&lt;/h3&gt;

&lt;p&gt;Finally, we calculate the hidden bias gradients.&lt;/p&gt;

&lt;p&gt;For a hidden neuron:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;zₕ = x₁w₁ + x₂w₂ + bₕ&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Differentiating with respect to its bias:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;∂zₕ/∂bₕ = 1&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Applying the chain rule:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;∂L/∂bₕ = (∂L/∂zₕ) × 1&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For all four training examples, we sum the gradients along the batch dimension:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;db_h&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dl_dzh&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;axis&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The result has shape (3,), giving us one gradient for each hidden neuron.&lt;/p&gt;

&lt;h3&gt;
  
  
  6.10 Completing Backpropagation
&lt;/h3&gt;

&lt;p&gt;We’ve now calculated all the gradients required to train our network.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Gradient Shape Parameters
----------------------------------
dW_h (2, 3) 6
db_h (3,) 3
dW_o (3, 1) 3
db_o (1,) 1
----------------------------------
Total 13
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The complete hidden-layer backpropagation implementation is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Gradient through output weights
&lt;/span&gt;&lt;span class="n"&gt;dl_dh&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dl_dz_o&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt; &lt;span class="n"&gt;W_o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;

&lt;span class="c1"&gt;# Gradient through ReLU
&lt;/span&gt;&lt;span class="n"&gt;dh_dhz&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;z_h&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;astype&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;dl_dzh&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dl_dh&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;dh_dhz&lt;/span&gt;

&lt;span class="c1"&gt;# Hidden weight gradients
&lt;/span&gt;&lt;span class="n"&gt;dW_h&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;X&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt; &lt;span class="n"&gt;dl_dzh&lt;/span&gt;

&lt;span class="c1"&gt;# Hidden bias gradients
&lt;/span&gt;&lt;span class="n"&gt;db_h&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dl_dzh&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;axis&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All these gradients are calculated using the current weights and the values saved during forward propagation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;An important distinction: backpropagation calculates gradients; it does not update the network’s parameters.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We now know how each of our 13 parameters influences the loss.&lt;/p&gt;

&lt;p&gt;The next step is to use these gradients to adjust the weights and biases through &lt;strong&gt;gradient descent&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  7. Gradient Descent: Updating the Parameters
&lt;/h3&gt;

&lt;p&gt;We’ve now calculated the gradients for all 13 trainable parameters through backpropagation.&lt;/p&gt;

&lt;p&gt;But calculating gradients alone doesn’t improve our network. We need to use them to update the weights and biases.&lt;/p&gt;

&lt;p&gt;This is where &lt;strong&gt;gradient descent&lt;/strong&gt; comes in.&lt;/p&gt;

&lt;p&gt;Gradient descent is an optimization algorithm that adjusts parameters in the direction opposite to their gradients, aiming to minimize the loss function.&lt;/p&gt;

&lt;h3&gt;
  
  
  7.1 Understanding the Update Rule
&lt;/h3&gt;

&lt;p&gt;The general gradient descent equation is:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;θ_new = θ_old − α(∂L/∂θ)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Where:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;θ&lt;/strong&gt; represents a trainable parameter, such as a weight or bias.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;α&lt;/strong&gt; represents the learning rate.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;∂L/∂θ&lt;/strong&gt; represents the gradient of the loss with respect to that parameter.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The learning rate controls the size of each update.&lt;/p&gt;

&lt;p&gt;A very large learning rate may cause unstable training, while a very small learning rate can make convergence slow.&lt;/p&gt;

&lt;p&gt;For our XOR network, we use:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;α = 0.05&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7cm7zrb2fgd0ccj0nj2y.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7cm7zrb2fgd0ccj0nj2y.png" width="800" height="600"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Figure 6 — Gradient descent updates weights and biases using the calculated gradients. AI-generated illustration.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;In this diagram, η represents the learning rate, denoted by α in our equations. Both refer to the same parameter.&lt;/p&gt;
&lt;h3&gt;
  
  
  7.2 Updating Weights and Biases
&lt;/h3&gt;

&lt;p&gt;We apply the same update rule to all four parameter groups.&lt;/p&gt;

&lt;p&gt;In NumPy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Hidden layer updates
&lt;/span&gt;&lt;span class="n"&gt;W_h&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;W_h&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;A&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;dW_h&lt;/span&gt;
&lt;span class="n"&gt;b_h&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;b_h&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;A&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;db_h&lt;/span&gt;

&lt;span class="c1"&gt;# Output layer updates
&lt;/span&gt;&lt;span class="n"&gt;W_o&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;W_o&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;A&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;dW_o&lt;/span&gt;
&lt;span class="n"&gt;b_o&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;b_o&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;A&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;db_o&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, A stores our learning rate of 0.05.&lt;/p&gt;

&lt;p&gt;Each parameter is adjusted using its corresponding gradient.&lt;/p&gt;

&lt;p&gt;An important detail is that &lt;strong&gt;all gradients must be calculated before updating any weights or biases&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This ensures that every gradient corresponds to the same forward pass and parameter state.&lt;/p&gt;

&lt;h3&gt;
  
  
  7.3 A Real Weight Update
&lt;/h3&gt;

&lt;p&gt;Let’s examine one actual update from our network’s initial training step.&lt;/p&gt;

&lt;p&gt;The first output-layer weight begins at approximately:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;w = 0.06392020&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;After backpropagation, its gradient is:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;∂L/∂w ≈ 0.00123833&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Using the gradient descent equation:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;w_new = 0.06392020 − (0.05 × 0.00123833)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Therefore:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;w_new ≈ 0.06385829&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The weight changes only slightly, but this is how learning progresses: through repeated updates across all trainable parameters.&lt;/p&gt;

&lt;p&gt;After updating all 13 parameters in our first training step, the MSE decreases from approximately &lt;strong&gt;0.25023636 to 0.25020869&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This is a small improvement, but it demonstrates that the calculated gradients are being used to adjust the network.&lt;/p&gt;

&lt;h3&gt;
  
  
  7.4 Repeating the Learning Process
&lt;/h3&gt;

&lt;p&gt;One update is not enough to learn the complete XOR mapping.&lt;/p&gt;

&lt;p&gt;We repeat the training cycle:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Forward Propagation → Loss Calculation → Backpropagation → Gradient Descent&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;After each update, the new weights and biases are used to calculate the next predictions.&lt;/p&gt;

&lt;p&gt;Over many iterations, the network gradually adjusts its parameters to reduce prediction errors.&lt;/p&gt;

&lt;p&gt;In our implementation, we repeat this process for &lt;strong&gt;5,000 epochs&lt;/strong&gt; , using all four XOR examples for each update.&lt;/p&gt;

&lt;p&gt;This method is called &lt;strong&gt;full-batch gradient descent&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Now that we’ve implemented forward propagation, loss calculation, backpropagation, and gradient descent, we can combine everything into one complete training loop.&lt;/p&gt;

&lt;h3&gt;
  
  
  8. Bringing Everything Together: The Training Loop
&lt;/h3&gt;

&lt;p&gt;We’ve implemented all four fundamental operations required to train our neural network:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Forward propagation&lt;/strong&gt;  — Calculate predictions using the current weights and biases.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Loss calculation&lt;/strong&gt;  — Measure prediction errors using Mean Squared Error.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Backpropagation&lt;/strong&gt;  — Calculate gradients using the chain rule.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Gradient descent&lt;/strong&gt;  — Update weights and biases to reduce the loss.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Now we can combine these operations into a single training loop.&lt;/p&gt;

&lt;h3&gt;
  
  
  8.1 Implementing the Complete Training Loop
&lt;/h3&gt;

&lt;p&gt;We use a learning rate of 0.05 and train the network for 5,000 epochs.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;A&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.05&lt;/span&gt;
&lt;span class="n"&gt;epochs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5000&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;epoch&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;epochs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

    &lt;span class="c1"&gt;# Forward propagation
&lt;/span&gt;    &lt;span class="n"&gt;z_h&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;X&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt; &lt;span class="n"&gt;W_h&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;b_h&lt;/span&gt;
    &lt;span class="n"&gt;h&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;maximum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;z_h&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;z_o&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;h&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt; &lt;span class="n"&gt;W_o&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;b_o&lt;/span&gt;
    &lt;span class="n"&gt;a_o&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;z_o&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

    &lt;span class="c1"&gt;# Mean Squared Error
&lt;/span&gt;    &lt;span class="n"&gt;error&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;a_o&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;
    &lt;span class="n"&gt;squared_error&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
    &lt;span class="n"&gt;loss&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mean&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;squared_error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Output layer backpropagation
&lt;/span&gt;    &lt;span class="n"&gt;dl_da_o&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;X&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a_o&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;da_od_zo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;a_o&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;a_o&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;dl_dz_o&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dl_da_o&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;da_od_zo&lt;/span&gt;

    &lt;span class="n"&gt;dW_o&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt; &lt;span class="n"&gt;dl_dz_o&lt;/span&gt;
    &lt;span class="n"&gt;db_o&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dl_dz_o&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;axis&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Hidden layer backpropagation
&lt;/span&gt;    &lt;span class="n"&gt;dl_dh&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dl_dz_o&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt; &lt;span class="n"&gt;W_o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;

    &lt;span class="n"&gt;dh_dhz&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;z_h&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;astype&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;dl_dzh&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dl_dh&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;dh_dhz&lt;/span&gt;

    &lt;span class="n"&gt;dW_h&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;X&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt; &lt;span class="n"&gt;dl_dzh&lt;/span&gt;
    &lt;span class="n"&gt;db_h&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dl_dzh&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;axis&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Gradient descent
&lt;/span&gt;    &lt;span class="n"&gt;W_h&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;W_h&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;A&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;dW_h&lt;/span&gt;
    &lt;span class="n"&gt;b_h&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;b_h&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;A&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;db_h&lt;/span&gt;

    &lt;span class="n"&gt;W_o&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;W_o&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;A&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;dW_o&lt;/span&gt;
    &lt;span class="n"&gt;b_o&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;b_o&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;A&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;db_o&lt;/span&gt;

    &lt;span class="c1"&gt;# Display training progress
&lt;/span&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;epoch&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Epoch &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;epoch&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; | Loss: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;loss&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the complete training loop from our NumPy implementation.&lt;/p&gt;

&lt;p&gt;Notice that every operation we previously derived appears here. There is no automatic differentiation or built-in neural network optimizer.&lt;/p&gt;

&lt;h3&gt;
  
  
  8.2 Understanding an Epoch
&lt;/h3&gt;

&lt;p&gt;An epoch represents one complete pass through the training dataset.&lt;/p&gt;

&lt;p&gt;Since our dataset contains only four XOR combinations, each epoch processes all four examples together.&lt;/p&gt;

&lt;p&gt;During each epoch, the network:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Calculates the predictions for all four examples.&lt;/li&gt;
&lt;li&gt;Computes the average squared error.&lt;/li&gt;
&lt;li&gt;Calculates gradients for all 13 parameters.&lt;/li&gt;
&lt;li&gt;Updates the weights and biases once.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Because the complete dataset contributes to every gradient calculation, this approach is called &lt;strong&gt;full-batch gradient descent&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  8.3 Tracking the Training Loss
&lt;/h3&gt;

&lt;p&gt;We use the following condition to print the training progress:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;epoch&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Epoch &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;epoch&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; | Loss: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;loss&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This prints the loss every 500 epochs, allowing us to observe how the network changes during training.&lt;/p&gt;

&lt;p&gt;The training loop still performs an update during every epoch. The condition only controls when the loss is displayed.&lt;/p&gt;

&lt;p&gt;An important detail is that the displayed loss is calculated &lt;strong&gt;before the parameter update for that epoch&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;After training finishes, we need to perform another forward pass using the final updated weights and biases to obtain the actual final predictions and loss.&lt;/p&gt;

&lt;h3&gt;
  
  
  8.4 Why the Order Matters
&lt;/h3&gt;

&lt;p&gt;The sequence of operations is important.&lt;/p&gt;

&lt;p&gt;All gradients must be calculated before updating the parameters because backpropagation depends on the weights and activations from the same forward pass.&lt;/p&gt;

&lt;p&gt;Updating the output weights before calculating the hidden-layer gradients would introduce inconsistencies.&lt;/p&gt;

&lt;p&gt;By following the correct sequence, every gradient corresponds to the same parameter state.&lt;/p&gt;

&lt;p&gt;After 5,000 epochs, our network has repeatedly adjusted its parameters to minimize prediction errors.&lt;/p&gt;

&lt;p&gt;The next step is to evaluate the trained network and examine whether it has successfully learned the XOR mapping.&lt;/p&gt;

&lt;h3&gt;
  
  
  9. Evaluating the Trained Neural Network
&lt;/h3&gt;

&lt;p&gt;After 5,000 epochs of forward propagation, backpropagation, and gradient descent, it’s time to evaluate whether our network has successfully learned the XOR function.&lt;/p&gt;

&lt;p&gt;We’ll examine how the loss changed during training, inspect the final predictions, and calculate classification accuracy.&lt;/p&gt;

&lt;h3&gt;
  
  
  9.1 Evaluating the Final Parameters
&lt;/h3&gt;

&lt;p&gt;Once training finishes, we perform another forward pass using the updated weights and biases.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;z_h_new&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;X&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt; &lt;span class="n"&gt;W_h&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;b_h&lt;/span&gt;
&lt;span class="n"&gt;h_new&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;maximum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;z_h_new&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;z_o_new&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;h_new&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt; &lt;span class="n"&gt;W_o&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;b_o&lt;/span&gt;
&lt;span class="n"&gt;a_o_new&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;z_o_new&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="c1"&gt;# Final Mean Squared Error
&lt;/span&gt;&lt;span class="n"&gt;new_loss&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mean&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;a_o_new&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Convert probabilities into binary classes
&lt;/span&gt;&lt;span class="n"&gt;predictions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a_o_new&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;astype&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Why perform another forward pass?&lt;/p&gt;

&lt;p&gt;The predictions calculated during the last training epoch were produced &lt;strong&gt;before the final parameter update&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;To evaluate the fully trained network, we need predictions calculated using its final weights and biases.&lt;/p&gt;

&lt;h3&gt;
  
  
  9.2 Visualizing the Training Loss
&lt;/h3&gt;

&lt;p&gt;At the beginning of training, our network’s Mean Squared Error was approximately:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Initial MSE = 0.25023636&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;As training progressed, the loss gradually decreased.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fs93v1fk6tkxbnk9qrvzj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fs93v1fk6tkxbnk9qrvzj.png" width="800" height="424"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Actual loss across 5,000 training updates, reproduced from our NumPy experiment.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Here are some recorded loss values:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Epoch Training MSE
-------------------------
0 0.25023636
500 0.22134667
1000 0.18205232
1500 0.16114958
2000 0.08436386
2500 0.02993619
3000 0.01605965
3500 0.01045661
4000 0.00756720
4500 0.00584314
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These values represent the loss before the parameter update at each listed epoch.&lt;/p&gt;

&lt;p&gt;The loss decreases gradually at first, followed by a more noticeable improvement around epochs 1,500–2,500.&lt;/p&gt;

&lt;p&gt;This indicates that the network is progressively finding parameters that fit the XOR targets more closely.&lt;/p&gt;

&lt;h3&gt;
  
  
  9.3 Final XOR Predictions
&lt;/h3&gt;

&lt;p&gt;After completing all 5,000 updates, the network produces the following results:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Input Target Output Class
-------------------------------------
(0,0) 0 0.08389788 0
(0,1) 1 0.95051648 1
(1,0) 1 0.95173893 1
(1,1) 0 0.08393200 0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqbslidnpl34vc7uic5k7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqbslidnpl34vc7uic5k7.png" width="800" height="424"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Final sigmoid outputs compared with the expected XOR targets.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The model outputs values close to zero for the negative XOR examples and values close to one for the positive examples.&lt;/p&gt;

&lt;p&gt;Using a classification threshold of 0.5, all four predictions match their expected targets.&lt;/p&gt;

&lt;h3&gt;
  
  
  9.4 Final Loss and Accuracy
&lt;/h3&gt;

&lt;p&gt;Our final Mean Squared Error is:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;MSE = 0.00471529585312808&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;And our classification accuracy is:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Accuracy = (4 / 4) × 100 = 100%&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The network has successfully learned to classify all four XOR combinations in its training dataset.&lt;/p&gt;

&lt;p&gt;However, an important distinction needs to be made.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;100% training accuracy does not necessarily imply strong generalization.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Our evaluation uses the same four combinations that were used during training. This experiment demonstrates that our small network can fit the XOR mapping, but it does not evaluate performance on an independent dataset.&lt;/p&gt;

&lt;p&gt;Additionally, training behaviour can vary with random initialization, learning rate, and network architecture.&lt;/p&gt;

&lt;p&gt;For this particular configuration, the results demonstrate that our manually implemented forward propagation, backpropagation, and gradient descent work together to learn a nonlinear mapping without relying on a deep-learning framework.&lt;/p&gt;

&lt;h3&gt;
  
  
  10. What I Learned From Building This Network
&lt;/h3&gt;

&lt;p&gt;Building this neural network from scratch helped me understand how the mathematical concepts behind neural networks connect to one another.&lt;/p&gt;

&lt;p&gt;Instead of relying on a framework to handle the calculations, I had to work through every step myself.&lt;/p&gt;

&lt;p&gt;A few things became much clearer during the process:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Forward propagation&lt;/strong&gt; determines how inputs are transformed into predictions through weights, biases, and activation functions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Loss functions&lt;/strong&gt; measure how far predictions are from their expected targets.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Backpropagation&lt;/strong&gt; applies the chain rule to calculate how each trainable parameter influences the loss.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Gradient descent&lt;/strong&gt; uses those gradients to update parameters and improve predictions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Matrix dimensions and vectorization&lt;/strong&gt; are essential for implementing these operations efficiently using NumPy.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One particularly important realization was that &lt;strong&gt;backpropagation and gradient descent are not the same operation&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Backpropagation calculates gradients. Gradient descent uses them to update the parameters.&lt;/p&gt;

&lt;p&gt;Understanding this distinction made the training process much easier to reason about.&lt;/p&gt;

&lt;h3&gt;
  
  
  10.1 Challenges and Limitations
&lt;/h3&gt;

&lt;p&gt;The most challenging part was understanding how gradients propagate backward through multiple layers.&lt;/p&gt;

&lt;p&gt;Calculating derivatives for a single neuron was manageable, but extending those calculations across an entire batch required careful attention to the chain rule, matrix multiplication, transposes, and array shapes.&lt;/p&gt;

&lt;p&gt;The project also has a few limitations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Small dataset:&lt;/strong&gt; The network was trained and evaluated on the same four XOR combinations.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Initialization sensitivity:&lt;/strong&gt; Different random weights can lead to different training outcomes, particularly with a small ReLU network.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Limited evaluation:&lt;/strong&gt; Achieving 100% training accuracy does not demonstrate generalization to unseen data.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Gradient verification:&lt;/strong&gt; Although the training results were reproduced, a complete numerical gradient check of all 13 parameters remains future work.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These limitations don’t invalidate the experiment. They define what the results demonstrate and what still needs further investigation.&lt;/p&gt;

&lt;h3&gt;
  
  
  11. Conclusion
&lt;/h3&gt;

&lt;p&gt;This project started with a simple question:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How does a neural network actually learn?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Building a small XOR network helped me understand the answer through mathematics and implementation rather than relying entirely on abstractions.&lt;/p&gt;

&lt;p&gt;Using Python and NumPy, I implemented a 2 → 3 → 1 neural network with 13 trainable parameters, ReLU and sigmoid activations, Mean Squared Error, manually derived backpropagation, and full-batch gradient descent.&lt;/p&gt;

&lt;p&gt;After 5,000 epochs, the network successfully classified all four XOR combinations, achieving a final training MSE of approximately &lt;strong&gt;0.004715&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The architecture is small, but the fundamental principles involved extend to much larger neural networks.&lt;/p&gt;

&lt;p&gt;There’s still plenty to explore, from numerical gradient checking and alternative activation functions to deeper architectures and more challenging datasets.&lt;/p&gt;

&lt;p&gt;But this experiment established an important foundation for understanding those systems.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Frameworks make neural networks easier to build. Understanding the mathematics makes them easier to reason about.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Source Code and Further Reading
&lt;/h3&gt;

&lt;p&gt;The complete implementation, mathematical derivations, training results, and diagrams are available in my GitHub repository:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/Vaibh37/Neural-Network" rel="noopener noreferrer"&gt;&lt;strong&gt;Neural-Network — GitHub&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you find the project helpful, consider starring the repository. Feedback and suggestions are always welcome.&lt;/p&gt;

</description>
      <category>python</category>
      <category>deeplearning</category>
      <category>artificialintelligen</category>
      <category>machinelearning</category>
    </item>
    <item>
      <title>solid project. worth checking out.</title>
      <dc:creator>Dev Vaibhav</dc:creator>
      <pubDate>Sat, 19 Sep 2026 14:45:29 +0000</pubDate>
      <link>https://dev.to/dev_vaibhav_37/solid-project-worth-checking-out-4co</link>
      <guid>https://dev.to/dev_vaibhav_37/solid-project-worth-checking-out-4co</guid>
      <description>&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/divyasinghdev/i-built-echocadence-a-vocabulary-app-built-to-make-words-stick-5f82" class="crayons-story__hidden-navigation-link"&gt;I Built EchoCadence: A Vocabulary App Built to Make Words Stick 🔁&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
      &lt;a href="https://dev.to/divyasinghdev/i-built-echocadence-a-vocabulary-app-built-to-make-words-stick-5f82" class="crayons-article__context-note crayons-article__context-note__feed"&gt;&lt;p&gt;Built during an MLH hackathon using Tiger Cloud&lt;/p&gt;

&lt;/a&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/divyasinghdev" class="crayons-avatar  crayons-avatar--l  "&gt;
            &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F889475%2F1165be61-6903-4b59-af67-c262acfb1c94.webp" alt="divyasinghdev profile" class="crayons-avatar__image"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/divyasinghdev" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Divya
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Divya
                &lt;a href="/++"&gt;&lt;img alt="Subscriber" class="subscription-icon" src="https://assets.dev.to/assets/subscription-icon-805dfa7ac7dd660f07ed8d654877270825b07a92a03841aa99a1093bd00431b2.png"&gt;&lt;/a&gt;
                
              
              &lt;div id="story-author-preview-content-4694383" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/divyasinghdev" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&gt;
                        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F889475%2F1165be61-6903-4b59-af67-c262acfb1c94.webp" class="crayons-avatar__image" alt=""&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Divya&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/divyasinghdev/i-built-echocadence-a-vocabulary-app-built-to-make-words-stick-5f82" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Sep 19&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/divyasinghdev/i-built-echocadence-a-vocabulary-app-built-to-make-words-stick-5f82" id="article-link-4694383"&gt;
          I Built EchoCadence: A Vocabulary App Built to Make Words Stick 🔁
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/mlhacks"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;mlhacks&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/devchallenge"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;devchallenge&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/hackathon"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;hackathon&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/tigerdata"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;tigerdata&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/divyasinghdev/i-built-echocadence-a-vocabulary-app-built-to-make-words-stick-5f82" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/raised-hands-74b2099fd66a39f2d7eed9305ee0f4553df0eb7b4f11b01b6b1b499973048fe5.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/multi-unicorn-b44d6f8c23cdd00964192bedc38af3e82463978aa611b4365bd33a0f1f4f3e97.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;42&lt;span class="hidden s:inline"&gt;&amp;nbsp;reactions&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/divyasinghdev/i-built-echocadence-a-vocabulary-app-built-to-make-words-stick-5f82#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              

              24&lt;span class="hidden s:inline"&gt;&amp;nbsp;comments&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            9 min read
          &lt;/small&gt;
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


</description>
    </item>
    <item>
      <title>[Boost]</title>
      <dc:creator>Dev Vaibhav</dc:creator>
      <pubDate>Sat, 19 Sep 2026 14:44:27 +0000</pubDate>
      <link>https://dev.to/dev_vaibhav_37/-424a</link>
      <guid>https://dev.to/dev_vaibhav_37/-424a</guid>
      <description>&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/divyasinghdev/i-built-echocadence-a-vocabulary-app-built-to-make-words-stick-5f82" class="crayons-story__hidden-navigation-link"&gt;I Built EchoCadence: A Vocabulary App Built to Make Words Stick 🔁&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
      &lt;a href="https://dev.to/divyasinghdev/i-built-echocadence-a-vocabulary-app-built-to-make-words-stick-5f82" class="crayons-article__context-note crayons-article__context-note__feed"&gt;&lt;p&gt;Built during an MLH hackathon using Tiger Cloud&lt;/p&gt;

&lt;/a&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/divyasinghdev" class="crayons-avatar  crayons-avatar--l  "&gt;
            &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F889475%2F1165be61-6903-4b59-af67-c262acfb1c94.webp" alt="divyasinghdev profile" class="crayons-avatar__image"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/divyasinghdev" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Divya
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Divya
                &lt;a href="/++"&gt;&lt;img alt="Subscriber" class="subscription-icon" src="https://assets.dev.to/assets/subscription-icon-805dfa7ac7dd660f07ed8d654877270825b07a92a03841aa99a1093bd00431b2.png"&gt;&lt;/a&gt;
                
              
              &lt;div id="story-author-preview-content-4694383" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/divyasinghdev" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&gt;
                        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F889475%2F1165be61-6903-4b59-af67-c262acfb1c94.webp" class="crayons-avatar__image" alt=""&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Divya&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/divyasinghdev/i-built-echocadence-a-vocabulary-app-built-to-make-words-stick-5f82" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Sep 19&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/divyasinghdev/i-built-echocadence-a-vocabulary-app-built-to-make-words-stick-5f82" id="article-link-4694383"&gt;
          I Built EchoCadence: A Vocabulary App Built to Make Words Stick 🔁
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/mlhacks"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;mlhacks&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/devchallenge"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;devchallenge&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/hackathon"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;hackathon&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/tigerdata"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;tigerdata&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/divyasinghdev/i-built-echocadence-a-vocabulary-app-built-to-make-words-stick-5f82" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/raised-hands-74b2099fd66a39f2d7eed9305ee0f4553df0eb7b4f11b01b6b1b499973048fe5.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/multi-unicorn-b44d6f8c23cdd00964192bedc38af3e82463978aa611b4365bd33a0f1f4f3e97.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;42&lt;span class="hidden s:inline"&gt;&amp;nbsp;reactions&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/divyasinghdev/i-built-echocadence-a-vocabulary-app-built-to-make-words-stick-5f82#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              

              24&lt;span class="hidden s:inline"&gt;&amp;nbsp;comments&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            9 min read
          &lt;/small&gt;
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


</description>
    </item>
  </channel>
</rss>
