<?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: Utsav D</title>
    <description>The latest articles on DEV Community by Utsav D (@utsav_d_ae8a63f5daa5b5c23).</description>
    <link>https://dev.to/utsav_d_ae8a63f5daa5b5c23</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%2F4150845%2Fff188ed8-dcce-4863-80d5-8edff2511ec1.jpg</url>
      <title>DEV Community: Utsav D</title>
      <link>https://dev.to/utsav_d_ae8a63f5daa5b5c23</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/utsav_d_ae8a63f5daa5b5c23"/>
    <language>en</language>
    <item>
      <title>I Built a Tiny Neural Network From Scratch in Python — No PyTorch</title>
      <dc:creator>Utsav D</dc:creator>
      <pubDate>Tue, 29 Sep 2026 19:20:00 +0000</pubDate>
      <link>https://dev.to/utsav_d_ae8a63f5daa5b5c23/i-built-a-tiny-neural-network-from-scratch-in-python-no-pytorch-46gl</link>
      <guid>https://dev.to/utsav_d_ae8a63f5daa5b5c23/i-built-a-tiny-neural-network-from-scratch-in-python-no-pytorch-46gl</guid>
      <description>&lt;h1&gt;
  
  
  I Built a Tiny Neural Network From Scratch in Python — No PyTorch
&lt;/h1&gt;

&lt;p&gt;I didn't want to just call &lt;code&gt;model.fit()&lt;/code&gt; and say I understood neural networks.&lt;/p&gt;

&lt;p&gt;So I decided to build one myself.&lt;/p&gt;

&lt;p&gt;No PyTorch.&lt;br&gt;
No TensorFlow.&lt;br&gt;
No Keras.&lt;/p&gt;

&lt;p&gt;Just &lt;strong&gt;Python + NumPy + the mathematics behind a neural network&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The goal wasn't to build a production-ready deep learning framework. The goal was to understand what actually happens inside a neural network during training.&lt;/p&gt;

&lt;p&gt;By the end, we will have a small neural network that can learn a binary classification problem using:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Forward propagation&lt;/li&gt;
&lt;li&gt;ReLU activation&lt;/li&gt;
&lt;li&gt;Sigmoid activation&lt;/li&gt;
&lt;li&gt;Binary cross-entropy loss&lt;/li&gt;
&lt;li&gt;Backpropagation&lt;/li&gt;
&lt;li&gt;Gradient descent&lt;/li&gt;
&lt;li&gt;NumPy matrix operations&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  1. What Are We Actually Building?
&lt;/h2&gt;

&lt;p&gt;Our network will have three parts:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Input Layer
    ↓
Hidden Layer
    ↓
Output Layer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;3 input features
      ↓
4 hidden neurons
      ↓
1 output neuron
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The output represents the probability of belonging to class 1.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;0.02 → Class 0
0.91 → Class 1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  2. The Mathematics Behind a Neuron
&lt;/h2&gt;

&lt;p&gt;A neuron first calculates a weighted sum:&lt;/p&gt;

&lt;p&gt;$$&lt;br&gt;
z = XW + b&lt;br&gt;
$$&lt;/p&gt;

&lt;p&gt;where:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;X&lt;/code&gt; = input&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;W&lt;/code&gt; = weights&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;b&lt;/code&gt; = bias&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;z&lt;/code&gt; = pre-activation value&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then an activation function is applied.&lt;/p&gt;

&lt;p&gt;For the hidden layer we'll use ReLU:&lt;/p&gt;

&lt;p&gt;$$&lt;br&gt;
ReLU(x) = \max(0,x)&lt;br&gt;
$$&lt;/p&gt;

&lt;p&gt;For the output layer we'll use sigmoid:&lt;/p&gt;

&lt;p&gt;$$&lt;br&gt;
\sigma(x) = \frac{1}{1+e^{-x}}&lt;br&gt;
$$&lt;/p&gt;

&lt;p&gt;The sigmoid converts the output into a value between 0 and 1.&lt;/p&gt;


&lt;h2&gt;
  
  
  3. Creating Some Data
&lt;/h2&gt;

&lt;p&gt;We'll create a small synthetic binary classification dataset.&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;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;seed&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="n"&gt;n_samples&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;200&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="n"&gt;random&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;randn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n_samples&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;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&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="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&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="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&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="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;])&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;int&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;reshape&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&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="nf"&gt;print&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="n"&gt;shape&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="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;shape&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The shapes are:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;X → (200, 3)
y → (200, 1)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So every sample contains three features.&lt;/p&gt;




&lt;h1&gt;
  
  
  4. Activation Functions
&lt;/h1&gt;

&lt;p&gt;Let's implement ReLU and sigmoid ourselves.&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;def&lt;/span&gt; &lt;span class="nf"&gt;relu&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="k"&gt;return&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;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;relu_derivative&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="nf"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&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="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;sigmoid&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="k"&gt;return&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;x&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The derivative of ReLU is important during backpropagation.&lt;/p&gt;

&lt;p&gt;It is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1 when x &amp;gt; 0
0 when x &amp;lt;= 0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h1&gt;
  
  
  5. Building the Neural Network
&lt;/h1&gt;

&lt;p&gt;Now we can create the actual network.&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;class&lt;/span&gt; &lt;span class="nc"&gt;NeuralNetwork&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;input_size&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;hidden_size&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;output_size&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;W1&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;randn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input_size&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;hidden_size&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;0.1&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;b1&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;span class="n"&gt;hidden_size&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;W2&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;randn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hidden_size&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;output_size&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;0.1&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;b2&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;span class="n"&gt;output_size&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The architecture is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;3 → 4 → 1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;W1 = 3 × 4
b1 = 1 × 4

W2 = 4 × 1
b2 = 1 × 1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h1&gt;
  
  
  6. Forward Propagation
&lt;/h1&gt;

&lt;p&gt;Now we need to pass the input through the network.&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;def&lt;/span&gt; &lt;span class="nf"&gt;forward&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&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="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;z1&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;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;W1&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;b1&lt;/span&gt;
    &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;a1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;relu&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;z1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;z2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;a1&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;W2&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;b2&lt;/span&gt;
    &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;a2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;sigmoid&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;z2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;a2&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Mathematically:&lt;/p&gt;

&lt;p&gt;$$&lt;br&gt;
Z_1 = XW_1+b_1&lt;br&gt;
$$&lt;/p&gt;

&lt;p&gt;$$&lt;br&gt;
A_1 = ReLU(Z_1)&lt;br&gt;
$$&lt;/p&gt;

&lt;p&gt;$$&lt;br&gt;
Z_2 = A_1W_2+b_2&lt;br&gt;
$$&lt;/p&gt;

&lt;p&gt;$$&lt;br&gt;
A_2 = Sigmoid(Z_2)&lt;br&gt;
$$&lt;/p&gt;

&lt;p&gt;And &lt;code&gt;A2&lt;/code&gt; becomes our prediction.&lt;/p&gt;


&lt;h1&gt;
  
  
  7. Measuring the Error
&lt;/h1&gt;

&lt;p&gt;A model needs to know how wrong its predictions are.&lt;/p&gt;

&lt;p&gt;For binary classification, we'll use binary cross-entropy:&lt;/p&gt;

&lt;p&gt;$$&lt;br&gt;
L = -\frac{1}{m}\sum&lt;br&gt;
[y\log(\hat y)+(1-y)\log(1-\hat y)]&lt;br&gt;
$$&lt;/p&gt;

&lt;p&gt;In Python:&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;def&lt;/span&gt; &lt;span class="nf"&gt;binary_cross_entropy&lt;/span&gt;&lt;span class="p"&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;y_pred&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

    &lt;span class="n"&gt;epsilon&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;1e-8&lt;/span&gt;

    &lt;span class="n"&gt;y_pred&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;clip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;y_pred&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;epsilon&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;epsilon&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&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;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;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;y_pred&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="mi"&gt;1&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="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&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;y_pred&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;The &lt;code&gt;clip()&lt;/code&gt; prevents numerical problems caused by taking the logarithm of zero.&lt;/p&gt;




&lt;h1&gt;
  
  
  8. The Part That Usually Gets Hidden: Backpropagation
&lt;/h1&gt;

&lt;p&gt;This is where things get interesting.&lt;/p&gt;

&lt;p&gt;During backpropagation, we calculate how much each parameter contributed to the error.&lt;/p&gt;

&lt;p&gt;For the output layer:&lt;/p&gt;

&lt;p&gt;$$&lt;br&gt;
dZ_2 = A_2-y&lt;br&gt;
$$&lt;/p&gt;

&lt;p&gt;Then:&lt;/p&gt;

&lt;p&gt;$$&lt;br&gt;
dW_2 = \frac{A_1^T dZ_2}{m}&lt;br&gt;
$$&lt;/p&gt;

&lt;p&gt;$$&lt;br&gt;
db_2 = \frac{\sum dZ_2}{m}&lt;br&gt;
$$&lt;/p&gt;

&lt;p&gt;Then we propagate the gradient back into the hidden layer:&lt;/p&gt;

&lt;p&gt;$$&lt;br&gt;
dA_1 = dZ_2W_2^T&lt;br&gt;
$$&lt;/p&gt;

&lt;p&gt;$$&lt;br&gt;
dZ_1 = dA_1 \odot ReLU'(Z_1)&lt;br&gt;
$$&lt;/p&gt;

&lt;p&gt;Finally:&lt;/p&gt;

&lt;p&gt;$$&lt;br&gt;
dW_1 = \frac{X^TdZ_1}{m}&lt;br&gt;
$$&lt;/p&gt;

&lt;p&gt;and:&lt;/p&gt;

&lt;p&gt;$$&lt;br&gt;
db_1 = \frac{\sum dZ_1}{m}&lt;br&gt;
$$&lt;/p&gt;

&lt;p&gt;The implementation:&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;def&lt;/span&gt; &lt;span class="nf"&gt;backward&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&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="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;learning_rate&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.01&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

    &lt;span class="n"&gt;m&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;shape&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="c1"&gt;# Output layer gradient
&lt;/span&gt;    &lt;span class="n"&gt;dz2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;a2&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;dw2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;a1&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;dz2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;
    &lt;span class="n"&gt;db2&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;dz2&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="n"&gt;keepdims&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;

    &lt;span class="c1"&gt;# Hidden layer gradient
&lt;/span&gt;    &lt;span class="n"&gt;da1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dz2&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;W2&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;dz1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;da1&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nf"&gt;relu_derivative&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;z1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;dw1&lt;/span&gt; &lt;span class="o"&gt;=&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="n"&gt;T&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt; &lt;span class="n"&gt;dz1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;
    &lt;span class="n"&gt;db1&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;dz1&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="n"&gt;keepdims&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;

    &lt;span class="c1"&gt;# Update parameters
&lt;/span&gt;    &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;W2&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="n"&gt;learning_rate&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;dw2&lt;/span&gt;
    &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;b2&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="n"&gt;learning_rate&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;db2&lt;/span&gt;

    &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;W1&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="n"&gt;learning_rate&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;dw1&lt;/span&gt;
    &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;b1&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="n"&gt;learning_rate&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;db1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's essentially the core of training.&lt;/p&gt;




&lt;h1&gt;
  
  
  9. Training the Network
&lt;/h1&gt;

&lt;p&gt;Now let's put everything together.&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;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;NeuralNetwork&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;input_size&lt;/span&gt;&lt;span class="o"&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;hidden_size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;output_size&lt;/span&gt;&lt;span class="o"&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;epochs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2000&lt;/span&gt;
&lt;span class="n"&gt;learning_rate&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;loss_history&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&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 pass
&lt;/span&gt;    &lt;span class="n"&gt;predictions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forward&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="c1"&gt;# Calculate loss
&lt;/span&gt;    &lt;span class="n"&gt;loss&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;binary_cross_entropy&lt;/span&gt;&lt;span class="p"&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;predictions&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Backpropagation
&lt;/span&gt;    &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;backward&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="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;learning_rate&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;loss_history&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;loss&lt;/span&gt;&lt;span class="p"&gt;)&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;200&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;4&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;At the beginning, the network has essentially no idea what it is doing.&lt;/p&gt;

&lt;p&gt;Its weights are randomly initialized.&lt;/p&gt;

&lt;p&gt;But every iteration does this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Input
  ↓
Prediction
  ↓
Calculate Error
  ↓
Calculate Gradients
  ↓
Update Weights
  ↓
Repeat
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Over time, the loss should decrease.&lt;/p&gt;




&lt;h1&gt;
  
  
  10. Making Predictions
&lt;/h1&gt;

&lt;p&gt;After training, we can use the network to predict new samples.&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;predictions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forward&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="n"&gt;predicted_classes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;predictions&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;Let's calculate accuracy:&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;accuracy&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;predicted_classes&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="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;Accuracy: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;accuracy&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&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;The important part isn't the exact number.&lt;/p&gt;

&lt;p&gt;The important part is that &lt;strong&gt;we built the learning process ourselves&lt;/strong&gt;.&lt;/p&gt;




&lt;h1&gt;
  
  
  11. Looking at the Training Process
&lt;/h1&gt;

&lt;p&gt;We can visualize the loss:&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;matplotlib.pyplot&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;plt&lt;/span&gt;

&lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;plot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;loss_history&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;xlabel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Epoch&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ylabel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Loss&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;title&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Training Loss&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;show&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see the loss generally decrease as training progresses.&lt;/p&gt;

&lt;p&gt;That curve is one of the simplest ways to see learning happening.&lt;/p&gt;




&lt;h1&gt;
  
  
  12. What Is Actually Happening During One Training Step?
&lt;/h1&gt;

&lt;p&gt;It can be reduced to seven steps:&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1 — Input
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;X
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 2 — Forward propagation
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;X → Hidden Layer → Output
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 3 — Prediction
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ŷ
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 4 — Loss
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ŷ compared with y
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 5 — Backpropagation
&lt;/h3&gt;

&lt;p&gt;Calculate gradients.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 6 — Gradient descent
&lt;/h3&gt;

&lt;p&gt;Update the weights:&lt;/p&gt;

&lt;p&gt;$$&lt;br&gt;
W := W-\eta\frac{\partial L}{\partial W}&lt;br&gt;
$$&lt;/p&gt;

&lt;p&gt;where &lt;code&gt;η&lt;/code&gt; is the learning rate.&lt;/p&gt;
&lt;h3&gt;
  
  
  Step 7 — Repeat
&lt;/h3&gt;

&lt;p&gt;Thousands of times.&lt;/p&gt;

&lt;p&gt;That's training.&lt;/p&gt;


&lt;h1&gt;
  
  
  13. So What Does PyTorch Actually Do?
&lt;/h1&gt;

&lt;p&gt;After building this manually, frameworks like PyTorch become much easier to understand.&lt;/p&gt;

&lt;p&gt;When you write:&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;loss&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;backward&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;PyTorch is performing the gradient calculations for you.&lt;/p&gt;

&lt;p&gt;When you write:&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;optimizer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;step&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;the parameters are updated.&lt;/p&gt;

&lt;p&gt;And when you define:&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;nn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Linear&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;4&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;you're creating something conceptually similar to:&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;W&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;randn&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;4&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;b&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;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Of course, real frameworks handle much more:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Automatic differentiation&lt;/li&gt;
&lt;li&gt;GPU acceleration&lt;/li&gt;
&lt;li&gt;Optimizers&lt;/li&gt;
&lt;li&gt;Memory management&lt;/li&gt;
&lt;li&gt;Tensor operations&lt;/li&gt;
&lt;li&gt;Neural-network modules&lt;/li&gt;
&lt;li&gt;Mixed precision&lt;/li&gt;
&lt;li&gt;Distributed training&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But the fundamental ideas are still the same.&lt;/p&gt;




&lt;h1&gt;
  
  
  14. Why Build One From Scratch?
&lt;/h1&gt;

&lt;p&gt;You might be wondering:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Why spend time implementing something that PyTorch already does?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Because using a framework and understanding the underlying process are different things.&lt;/p&gt;

&lt;p&gt;When you only use:&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;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fit&lt;/span&gt;&lt;span class="p"&gt;(...)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;it's easy to think of training as a black box.&lt;/p&gt;

&lt;p&gt;Building the network manually forces you to understand:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Why weights exist&lt;/li&gt;
&lt;li&gt;Why biases exist&lt;/li&gt;
&lt;li&gt;What activation functions do&lt;/li&gt;
&lt;li&gt;What a gradient represents&lt;/li&gt;
&lt;li&gt;Why the loss changes&lt;/li&gt;
&lt;li&gt;How errors move backward&lt;/li&gt;
&lt;li&gt;Why learning rate matters&lt;/li&gt;
&lt;li&gt;Why matrix dimensions matter&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once those concepts click, high-level deep-learning code becomes much less mysterious.&lt;/p&gt;




&lt;h1&gt;
  
  
  15. What I Would Add Next
&lt;/h1&gt;

&lt;p&gt;This implementation is intentionally tiny.&lt;/p&gt;

&lt;p&gt;A more serious version could add:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Mini-batch gradient descent&lt;/li&gt;
&lt;li&gt;Multiple hidden layers&lt;/li&gt;
&lt;li&gt;Softmax for multiclass classification&lt;/li&gt;
&lt;li&gt;Adam optimizer&lt;/li&gt;
&lt;li&gt;Dropout&lt;/li&gt;
&lt;li&gt;Batch normalization&lt;/li&gt;
&lt;li&gt;L2 regularization&lt;/li&gt;
&lt;li&gt;Model saving/loading&lt;/li&gt;
&lt;li&gt;Train/validation/test splits&lt;/li&gt;
&lt;li&gt;Hyperparameter tuning&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The next interesting experiment would be to implement the same architecture twice:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;NumPy implementation
        vs
PyTorch implementation
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then compare their training behavior and code complexity.&lt;/p&gt;




&lt;h1&gt;
  
  
  16. Project Structure
&lt;/h1&gt;

&lt;p&gt;A simple project could look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;tiny-neural-network/
│
├── neural_network.py
├── train.py
├── data.py
├── visualize.py
├── requirements.txt
└── README.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;requirements.txt&lt;/code&gt; could be as simple as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;numpy
matplotlib
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it.&lt;/p&gt;

&lt;p&gt;No deep-learning framework is required.&lt;/p&gt;




&lt;h1&gt;
  
  
  17. The Biggest Thing I Learned
&lt;/h1&gt;

&lt;p&gt;The most useful part of this experiment wasn't the final classifier.&lt;/p&gt;

&lt;p&gt;It was realizing how much abstraction modern frameworks provide.&lt;/p&gt;

&lt;p&gt;A few lines of PyTorch can represent operations that involve:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Matrix multiplication
        ↓
Activation
        ↓
Loss
        ↓
Derivatives
        ↓
Gradient calculation
        ↓
Parameter updates
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When you understand those operations individually, frameworks stop feeling like magic.&lt;/p&gt;

&lt;p&gt;They become tools.&lt;/p&gt;




&lt;h1&gt;
  
  
  Final Takeaway
&lt;/h1&gt;

&lt;p&gt;You don't need to build every machine-learning model from scratch.&lt;/p&gt;

&lt;p&gt;In fact, for real projects, you usually shouldn't.&lt;/p&gt;

&lt;p&gt;PyTorch and TensorFlow exist for very good reasons.&lt;/p&gt;

&lt;p&gt;But building a tiny neural network once is an excellent way to understand what happens underneath the APIs.&lt;/p&gt;

&lt;p&gt;The next time you see:&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;loss&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;backward&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;optimizer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;step&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;you'll have a much better idea of what those two lines actually represent.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The framework is the abstraction.&lt;br&gt;
The mathematics is what makes it work.&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>deeplearning</category>
      <category>machinelearning</category>
      <category>python</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>I Made an LLM Read My PDFs Without Fine-Tuning It</title>
      <dc:creator>Utsav D</dc:creator>
      <pubDate>Tue, 29 Sep 2026 19:16:26 +0000</pubDate>
      <link>https://dev.to/utsav_d_ae8a63f5daa5b5c23/i-made-an-llm-read-my-pdfs-without-fine-tuning-it-58e2</link>
      <guid>https://dev.to/utsav_d_ae8a63f5daa5b5c23/i-made-an-llm-read-my-pdfs-without-fine-tuning-it-58e2</guid>
      <description>&lt;h1&gt;
  
  
  I Built a PDF Chatbot Without Fine-Tuning an LLM — Here's How It Works
&lt;/h1&gt;

&lt;p&gt;I had a simple problem.&lt;/p&gt;

&lt;p&gt;I had a PDF containing a lot of information, and I wanted to ask questions about it.&lt;/p&gt;

&lt;p&gt;Something like:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"What are the main findings?"&lt;/p&gt;

&lt;p&gt;"What dataset was used?"&lt;/p&gt;

&lt;p&gt;"Explain the methodology in simple terms."&lt;/p&gt;

&lt;p&gt;"Where does the paper discuss its limitations?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;My first thought was:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do I need to train an AI model on the PDF?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;No.&lt;/p&gt;

&lt;p&gt;I built a system that lets an LLM answer questions about a document without fine-tuning the LLM on that document.&lt;/p&gt;

&lt;p&gt;The basic idea is called &lt;strong&gt;Retrieval-Augmented Generation, or RAG&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;And once I understood how it worked, the architecture was surprisingly straightforward.&lt;/p&gt;




&lt;h1&gt;
  
  
  What I Wanted to Build
&lt;/h1&gt;

&lt;p&gt;The goal was simple:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Upload PDF
     ↓
Ask a question
     ↓
Find the relevant parts of the PDF
     ↓
Give those parts to the LLM
     ↓
Generate an answer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For example, imagine I upload a 100-page research paper.&lt;/p&gt;

&lt;p&gt;I ask:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"What dataset did the authors use?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I don't want the LLM to process the entire document from scratch every time.&lt;/p&gt;

&lt;p&gt;Instead, my system should find the section containing the dataset information and send only that relevant context to the LLM.&lt;/p&gt;

&lt;p&gt;That is the basic idea behind RAG.&lt;/p&gt;




&lt;h1&gt;
  
  
  The Architecture
&lt;/h1&gt;

&lt;p&gt;The complete pipeline looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                  PDF
                   │
                   ▼
            Text Extraction
                   │
                   ▼
             Text Chunking
                   │
                   ▼
          Embedding Generation
                   │
                   ▼
            Vector Database
                   │
                   │
        ┌──────────┘
        │
        ▼
   User Question
        │
        ▼
 Question Embedding
        │
        ▼
 Similarity Search
        │
        ▼
 Relevant Chunks
        │
        ▼
      LLM
        │
        ▼
     Answer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There are two important phases here.&lt;/p&gt;

&lt;h3&gt;
  
  
  Phase 1: Indexing
&lt;/h3&gt;

&lt;p&gt;The PDF is processed and stored in a searchable format.&lt;/p&gt;

&lt;h3&gt;
  
  
  Phase 2: Retrieval + Generation
&lt;/h3&gt;

&lt;p&gt;When the user asks a question, the system retrieves the relevant information and gives it to the LLM.&lt;/p&gt;

&lt;p&gt;Let's break that down.&lt;/p&gt;




&lt;h1&gt;
  
  
  Step 1: Extract Text From the PDF
&lt;/h1&gt;

&lt;p&gt;The first thing we need is the actual text.&lt;/p&gt;

&lt;p&gt;For a normal text-based PDF, a library such as PyMuPDF can extract it.&lt;/p&gt;

&lt;p&gt;A simplified example:&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;fitz&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;extract_text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pdf_path&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;document&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fitz&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pdf_path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;pages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;document&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;pages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_text&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pages&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we have something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Introduction...

Related Work...

Methodology...

Dataset...

Experiments...

Results...

Conclusion...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But we aren't ready to send this entire text to the LLM.&lt;/p&gt;

&lt;p&gt;There could be thousands or hundreds of thousands of words.&lt;/p&gt;

&lt;p&gt;So the next step is important.&lt;/p&gt;




&lt;h1&gt;
  
  
  Step 2: Split the Document Into Chunks
&lt;/h1&gt;

&lt;p&gt;Instead of treating the entire PDF as one giant piece of text, we divide it into smaller chunks.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PDF
│
├── Chunk 1
├── Chunk 2
├── Chunk 3
├── Chunk 4
├── Chunk 5
└── ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;Because when someone asks a question, we don't necessarily need the entire document.&lt;/p&gt;

&lt;p&gt;Suppose the PDF contains this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Page 1
Introduction...

Page 2
Related Work...

Page 3
Dataset...

Page 4
Methodology...

Page 5
Training...

Page 6
Results...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the user asks:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"What dataset was used?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We only need the relevant portion.&lt;/p&gt;

&lt;p&gt;A simple chunking strategy could look like:&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;def&lt;/span&gt; &lt;span class="nf"&gt;create_chunks&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;chunk_size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;overlap&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;chunks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

    &lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;

    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&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;text&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;end&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;chunk_size&lt;/span&gt;
        &lt;span class="n"&gt;chunks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;end&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
        &lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;chunk_size&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;overlap&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;chunks&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The overlap is useful because an important sentence might otherwise fall exactly between two chunks.&lt;/p&gt;

&lt;p&gt;For a real project, chunk size should be tested rather than blindly chosen.&lt;/p&gt;




&lt;h1&gt;
  
  
  Step 3: Convert Text Into Embeddings
&lt;/h1&gt;

&lt;p&gt;Now comes one of the most important parts.&lt;/p&gt;

&lt;p&gt;A computer cannot directly perform semantic similarity search on ordinary sentences.&lt;/p&gt;

&lt;p&gt;We convert each chunk into a numerical representation called an &lt;strong&gt;embedding&lt;/strong&gt;.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"What dataset was used?"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;might become something conceptually like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[0.21, -0.18, 0.74, 0.03, ...]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The actual embedding contains many dimensions.&lt;/p&gt;

&lt;p&gt;The important idea is that semantically similar text should have similar vector representations.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Question:
"What dataset did the researchers use?"

Document chunk:
"The experiments were conducted using the WESAD dataset..."
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These two pieces of text are semantically related even though they don't use exactly the same words.&lt;/p&gt;

&lt;p&gt;That's why embeddings are useful.&lt;/p&gt;




&lt;h1&gt;
  
  
  Step 4: Store the Embeddings
&lt;/h1&gt;

&lt;p&gt;Now we need somewhere to store the vectors.&lt;/p&gt;

&lt;p&gt;A vector database or vector index can be used for this.&lt;/p&gt;

&lt;p&gt;For a small local project, FAISS is one option.&lt;/p&gt;

&lt;p&gt;Conceptually:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Chunk 1 → Embedding 1
Chunk 2 → Embedding 2
Chunk 3 → Embedding 3
Chunk 4 → Embedding 4
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We store the relationship between the vector and its original text.&lt;/p&gt;

&lt;p&gt;So later, when we find a relevant vector, we can retrieve the original chunk.&lt;/p&gt;

&lt;p&gt;The system is essentially building a searchable representation of the PDF.&lt;/p&gt;




&lt;h1&gt;
  
  
  Step 5: The User Asks a Question
&lt;/h1&gt;

&lt;p&gt;Now the interesting part begins.&lt;/p&gt;

&lt;p&gt;Suppose I ask:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"What dataset was used in the experiments?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The question itself is converted into an embedding.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User Question
      ↓
Embedding Model
      ↓
Question Vector
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We then compare that vector against the vectors stored from the PDF.&lt;/p&gt;

&lt;p&gt;The goal is to find the chunks that are semantically closest to the question.&lt;/p&gt;




&lt;h1&gt;
  
  
  Step 6: Retrieve the Relevant Chunks
&lt;/h1&gt;

&lt;p&gt;Suppose the search returns:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Chunk 18
Chunk 42
Chunk 43
Chunk 67
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The system can select the top few results.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Question
   ↓
Vector Search
   ↓
Top 5 Relevant Chunks
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We now have the context needed to answer the question.&lt;/p&gt;

&lt;p&gt;This is the &lt;strong&gt;retrieval&lt;/strong&gt; part of Retrieval-Augmented Generation.&lt;/p&gt;




&lt;h1&gt;
  
  
  Step 7: Give the Context to the LLM
&lt;/h1&gt;

&lt;p&gt;Now we finally use the language model.&lt;/p&gt;

&lt;p&gt;Instead of asking:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"What dataset was used?"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;we provide the retrieved information as context.&lt;/p&gt;

&lt;p&gt;Conceptually:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Use the following context to answer the question.

Context:
[Relevant chunk 1]

[Relevant chunk 2]

[Relevant chunk 3]

Question:
What dataset was used?

Answer:
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The LLM can now generate an answer based on the retrieved document content.&lt;/p&gt;

&lt;p&gt;This is where the "generation" part of RAG comes in.&lt;/p&gt;




&lt;h1&gt;
  
  
  The Complete Flow
&lt;/h1&gt;

&lt;p&gt;Putting everything together:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                  ┌──────────────┐
                  │     PDF      │
                  └──────┬───────┘
                         │
                         ▼
                ┌─────────────────┐
                │ Text Extraction │
                └────────┬────────┘
                         │
                         ▼
                ┌─────────────────┐
                │    Chunking     │
                └────────┬────────┘
                         │
                         ▼
                ┌─────────────────┐
                │   Embeddings    │
                └────────┬────────┘
                         │
                         ▼
                ┌─────────────────┐
                │  Vector Index   │
                └─────────────────┘


User Question
      │
      ▼
┌─────────────────┐
│ Question        │
│ Embedding       │
└────────┬────────┘
         │
         ▼
┌─────────────────┐
│ Similarity      │
│ Search          │
└────────┬────────┘
         │
         ▼
┌─────────────────┐
│ Relevant        │
│ Document Chunks │
└────────┬────────┘
         │
         ▼
┌─────────────────┐
│      LLM        │
└────────┬────────┘
         │
         ▼
      Answer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's the entire idea.&lt;/p&gt;

&lt;p&gt;It sounds complicated when people say "build a RAG pipeline."&lt;/p&gt;

&lt;p&gt;The individual steps are actually quite understandable.&lt;/p&gt;




&lt;h1&gt;
  
  
  Why Not Just Put the Entire PDF Into the LLM?
&lt;/h1&gt;

&lt;p&gt;This is a reasonable question.&lt;/p&gt;

&lt;p&gt;Modern LLMs can process large amounts of text.&lt;/p&gt;

&lt;p&gt;So why bother with retrieval?&lt;/p&gt;

&lt;p&gt;Because document-based applications have several practical problems.&lt;/p&gt;

&lt;p&gt;A document may be:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Very large&lt;/li&gt;
&lt;li&gt;Frequently updated&lt;/li&gt;
&lt;li&gt;Made up of many unrelated sections&lt;/li&gt;
&lt;li&gt;Too expensive to repeatedly process in full&lt;/li&gt;
&lt;li&gt;Full of information irrelevant to the current question&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Retrieval lets us narrow the information down before generation.&lt;/p&gt;

&lt;p&gt;Instead of:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;100-page document
      ↓
      LLM
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;we can do:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;100-page document
      ↓
Find relevant information
      ↓
5 useful chunks
      ↓
LLM
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The second approach gives the model a much more focused context.&lt;/p&gt;




&lt;h1&gt;
  
  
  The Most Important Problem: Hallucinations
&lt;/h1&gt;

&lt;p&gt;At this point, the chatbot looks impressive.&lt;/p&gt;

&lt;p&gt;But there is a problem.&lt;/p&gt;

&lt;p&gt;What happens if the answer isn't actually in the PDF?&lt;/p&gt;

&lt;p&gt;Suppose I ask:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"What did the authors say about a technology that isn't mentioned anywhere in the paper?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A language model might still try to answer.&lt;/p&gt;

&lt;p&gt;That's dangerous.&lt;/p&gt;

&lt;p&gt;A document chatbot should not confidently invent information simply because the user asked a question.&lt;/p&gt;

&lt;p&gt;So one of the most important rules I would add is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;If the retrieved context does not contain enough information to answer the question, say so.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;I couldn't find enough information in the provided
document to answer this question.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's much better than generating a convincing but unsupported answer.&lt;/p&gt;




&lt;h1&gt;
  
  
  I Would Test the Chatbot With Questions Like These
&lt;/h1&gt;

&lt;p&gt;Instead of testing only easy questions, I would deliberately try to break it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Test 1 — Direct question
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;"What dataset was used?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Expected:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;A direct answer based on the document.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Test 2 — Multiple sections
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;"How does the proposed method differ from the baseline?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This may require retrieving information from more than one section.&lt;/p&gt;

&lt;h3&gt;
  
  
  Test 3 — Missing information
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;"What programming language was used to build the company's mobile application?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If the PDF never discusses this, the system should say that it cannot find the answer.&lt;/p&gt;

&lt;h3&gt;
  
  
  Test 4 — Ambiguous question
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;"What was the result?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The document may contain multiple results.&lt;/p&gt;

&lt;p&gt;The chatbot should ideally ask for clarification or use the surrounding context.&lt;/p&gt;

&lt;h3&gt;
  
  
  Test 5 — Misleading question
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;"Why did the researchers use Dataset X?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If Dataset X doesn't exist in the document, the system shouldn't accept the assumption as fact.&lt;/p&gt;

&lt;p&gt;This type of testing is much more interesting than simply asking:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"What is the title of the paper?"&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h1&gt;
  
  
  RAG vs Fine-Tuning
&lt;/h1&gt;

&lt;p&gt;This was one of the biggest things I wanted to understand.&lt;/p&gt;

&lt;p&gt;These two approaches solve different problems.&lt;/p&gt;

&lt;h3&gt;
  
  
  Fine-tuning
&lt;/h3&gt;

&lt;p&gt;Fine-tuning changes the model's behavior by training it further on a dataset.&lt;/p&gt;

&lt;p&gt;Conceptually:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Base Model
    ↓
Training Data
    ↓
Fine-Tuning
    ↓
Modified Model
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  RAG
&lt;/h3&gt;

&lt;p&gt;RAG doesn't require the model to learn the document's contents during training.&lt;/p&gt;

&lt;p&gt;Instead:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Document
    ↓
Index

Question
    ↓
Retrieve relevant information
    ↓
LLM
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That makes RAG particularly useful for document collections that change frequently.&lt;/p&gt;

&lt;p&gt;If I add another PDF, I don't necessarily need to retrain the language model.&lt;/p&gt;

&lt;p&gt;I can process the new document and add its chunks to the retrieval system.&lt;/p&gt;




&lt;h1&gt;
  
  
  A Simple Project Structure
&lt;/h1&gt;

&lt;p&gt;A project like this can be organized fairly cleanly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pdf-chatbot/
│
├── app.py
├── ingest.py
├── retriever.py
├── generator.py
├── embeddings.py
│
├── data/
│   └── documents/
│
├── index/
│   └── vector_store/
│
├── requirements.txt
└── README.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ingest.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;handles:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PDF
→ extraction
→ chunking
→ embeddings
→ indexing
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;While:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;retriever.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;handles:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;question
→ embedding
→ similarity search
→ relevant chunks
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;generator.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;handles:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;context + question
→ LLM
→ answer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Keeping those responsibilities separate makes the project easier to debug.&lt;/p&gt;




&lt;h1&gt;
  
  
  What I Would Put in the UI
&lt;/h1&gt;

&lt;p&gt;The interface doesn't need to be complicated.&lt;/p&gt;

&lt;p&gt;Something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌─────────────────────────────────────────┐
│           PDF Question Answering        │
├─────────────────────────────────────────┤
│                                         │
│       [ Upload PDF ]                    │
│                                         │
│  ─────────────────────────────────────  │
│                                         │
│  Ask a question:                        │
│  ┌───────────────────────────────────┐  │
│  │ What dataset was used?            │  │
│  └───────────────────────────────────┘  │
│                                         │
│              [ Ask ]                    │
│                                         │
├─────────────────────────────────────────┤
│ Answer                                  │
│                                         │
│ The experiments used ...                │
│                                         │
├─────────────────────────────────────────┤
│ Sources                                 │
│                                         │
│ Page 4                                  │
│ Page 7                                  │
└─────────────────────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;strong&gt;source section&lt;/strong&gt; is particularly useful.&lt;/p&gt;

&lt;p&gt;Instead of simply saying:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Here is the answer."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;the application can show:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Here are the document sections used to generate this answer."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That makes the system easier to inspect.&lt;/p&gt;




&lt;h1&gt;
  
  
  What I Learned
&lt;/h1&gt;

&lt;p&gt;The interesting thing about this project wasn't actually the chatbot.&lt;/p&gt;

&lt;p&gt;It was understanding the separation between &lt;strong&gt;knowledge retrieval&lt;/strong&gt; and &lt;strong&gt;language generation&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The LLM doesn't necessarily need to memorize every document.&lt;/p&gt;

&lt;p&gt;It can be given the relevant information when the question arrives.&lt;/p&gt;

&lt;p&gt;That changes the way you think about building AI applications.&lt;/p&gt;

&lt;p&gt;Instead of asking:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"How do I train an AI to know everything in this document?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;you can ask:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"How do I efficiently retrieve the right information and give it to the model?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That is a much more practical engineering problem.&lt;/p&gt;




&lt;h1&gt;
  
  
  Where This Can Be Used
&lt;/h1&gt;

&lt;p&gt;The same architecture can be adapted to many applications.&lt;/p&gt;

&lt;h3&gt;
  
  
  Research papers
&lt;/h3&gt;

&lt;p&gt;Upload papers and ask questions about:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;methodology&lt;/li&gt;
&lt;li&gt;datasets&lt;/li&gt;
&lt;li&gt;experiments&lt;/li&gt;
&lt;li&gt;limitations&lt;/li&gt;
&lt;li&gt;results&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  College notes
&lt;/h3&gt;

&lt;p&gt;Upload lecture notes and ask:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Explain Unit 3 in simple terms."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Company documentation
&lt;/h3&gt;

&lt;p&gt;Upload internal documentation and search it conversationally.&lt;/p&gt;

&lt;h3&gt;
  
  
  Legal documents
&lt;/h3&gt;

&lt;p&gt;Retrieve relevant sections from large documents.&lt;/p&gt;

&lt;h3&gt;
  
  
  Product manuals
&lt;/h3&gt;

&lt;p&gt;Ask:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"How do I reset this device?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Technical documentation
&lt;/h3&gt;

&lt;p&gt;Ask questions without manually searching through hundreds of pages.&lt;/p&gt;

&lt;p&gt;The basic architecture remains similar.&lt;/p&gt;




&lt;h1&gt;
  
  
  What I Would Improve Next
&lt;/h1&gt;

&lt;p&gt;The first version of a RAG system is relatively simple.&lt;/p&gt;

&lt;p&gt;A production-quality version is much harder.&lt;/p&gt;

&lt;p&gt;There are several things I would improve.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Better chunking
&lt;/h3&gt;

&lt;p&gt;Fixed character lengths aren't always ideal.&lt;/p&gt;

&lt;p&gt;A chunk should ideally preserve meaningful context.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Better retrieval
&lt;/h3&gt;

&lt;p&gt;Basic similarity search isn't always enough.&lt;/p&gt;

&lt;p&gt;Hybrid search can combine semantic similarity with keyword-based retrieval.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Re-ranking
&lt;/h3&gt;

&lt;p&gt;Instead of immediately passing the top results to the LLM, a re-ranking model can help determine which retrieved chunks are actually the most relevant.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Source citations
&lt;/h3&gt;

&lt;p&gt;The chatbot should tell the user exactly where its answer came from.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Better handling of tables
&lt;/h3&gt;

&lt;p&gt;PDFs aren't just text.&lt;/p&gt;

&lt;p&gt;Tables, figures, columns, and scanned pages can make extraction much harder.&lt;/p&gt;

&lt;h3&gt;
  
  
  6. Evaluation
&lt;/h3&gt;

&lt;p&gt;A serious RAG application needs evaluation.&lt;/p&gt;

&lt;p&gt;I'd want to measure things such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Retrieval accuracy&lt;/li&gt;
&lt;li&gt;Answer correctness&lt;/li&gt;
&lt;li&gt;Context relevance&lt;/li&gt;
&lt;li&gt;Hallucination rate&lt;/li&gt;
&lt;li&gt;Response latency&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Without evaluation, "it seems to work" isn't enough.&lt;/p&gt;




&lt;h1&gt;
  
  
  The Bigger Picture
&lt;/h1&gt;

&lt;p&gt;The interesting part about RAG isn't that it lets you "chat with PDFs."&lt;/p&gt;

&lt;p&gt;That's just one application.&lt;/p&gt;

&lt;p&gt;The larger idea is that an LLM can be connected to an external knowledge source without having that knowledge permanently embedded into its parameters.&lt;/p&gt;

&lt;p&gt;The architecture becomes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;External Knowledge
       ↓
    Retrieval
       ↓
    Context
       ↓
      LLM
       ↓
    Response
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And that pattern can be used far beyond PDFs.&lt;/p&gt;

&lt;p&gt;That's what made this project interesting to me.&lt;/p&gt;

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

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"Can I make an AI answer questions about my PDF?"&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The answer was yes.&lt;/p&gt;

&lt;p&gt;But the more interesting realization was:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;I didn't need to train the AI to know the document.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I just needed to build a good way for the AI to find the right information.&lt;/p&gt;

&lt;p&gt;And that is the basic idea behind Retrieval-Augmented Generation.&lt;/p&gt;




&lt;h1&gt;
  
  
  Final Takeaway
&lt;/h1&gt;

&lt;p&gt;If you're building your first document-based AI application, don't immediately think about fine-tuning.&lt;/p&gt;

&lt;p&gt;Start by asking:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Can I retrieve the right information
and give it to the model at the right time?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the answer is yes, you may already have most of what you need.&lt;/p&gt;

&lt;p&gt;The model generates the language.&lt;/p&gt;

&lt;p&gt;The retrieval system finds the knowledge.&lt;/p&gt;

&lt;p&gt;And the application connects the two.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;That's RAG.&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>llm</category>
      <category>programming</category>
      <category>python</category>
    </item>
  </channel>
</rss>
