<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://naveenprasanth.github.io/feed.xml" rel="self" type="application/atom+xml" /><link href="https://naveenprasanth.github.io/" rel="alternate" type="text/html" /><updated>2026-02-18T13:01:09+00:00</updated><id>https://naveenprasanth.github.io/feed.xml</id><title type="html">Naveen Prasanth</title><subtitle>Principal AI Platform Engineer — LLM systems, RAG pipelines, and multi-tenant SaaS across healthcare and fintech.</subtitle><author><name>Naveen Prasanth</name></author><entry><title type="html">ML Building Blocks: What Kaggle Taught Me on Day One</title><link href="https://naveenprasanth.github.io/machine-learning/ml-building-blocks/" rel="alternate" type="text/html" title="ML Building Blocks: What Kaggle Taught Me on Day One" /><published>2024-10-15T00:00:00+00:00</published><updated>2024-10-15T00:00:00+00:00</updated><id>https://naveenprasanth.github.io/machine-learning/ml-building-blocks</id><content type="html" xml:base="https://naveenprasanth.github.io/machine-learning/ml-building-blocks/"><![CDATA[<h3 id="how-this-started">How this started</h3>

<p>I often find myself going down rabbit holes. Today’s rabbit hole was Kaggle. I started clicking through starter notebooks for NLP competitions. The kind where people predict which chatbot response a human would prefer, or classify text into categories.</p>

<p>After the third or fourth notebook, something clicked. They all follow the same playbook. Nobody starts from scratch. They grab a pretrained model, wire up a standard architecture, get a baseline running as fast as possible, and <em>then</em> the real work begins. Swapping loss functions, tweaking how vectors are pooled, trying different training tricks. The architecture is a template. The wins come from knowing which knobs to turn.</p>

<p>That got me thinking. The building blocks underneath these notebooks are worth writing down. Not as theory, but as a reference you can actually use when you’re reading competition code and wondering “wait, what is a bi-encoder again?” So I’m starting a series. This is the first entry.</p>

<h3 id="encoders-how-text-becomes-numbers">Encoders: How text becomes numbers</h3>

<p>You can’t feed raw text into a neural network. You need to turn it into numbers first. An <strong>encoder</strong> does exactly this. It reads text and produces a vector (just a list of numbers) that captures the meaning.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>"I love this app" → Encoder → [0.2, -0.8, 0.5, ..., 0.1]  (768 numbers)
</code></pre></div></div>

<p>This vector is called an <strong>embedding</strong>. Similar texts produce similar vectors. Think of it like coordinates on a map. “I love this app” and “This app is great” would end up close together, while “stock market crash” would be far away.</p>

<h3 id="bi-encoder-two-encoders-working-independently">Bi-Encoder: two encoders, working independently</h3>

<p>“Bi” just means two. That’s it. Two encoders running in parallel on two different inputs.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Text A → Encoder → Vector A
Text B → Encoder → Vector B
</code></pre></div></div>

<p>Usually it’s the same model used twice, but the inputs are processed independently. Text A doesn’t know about Text B during encoding. Why does this matter? Because you can pre-compute vectors for a million documents ahead of time, store them, and at search time only compute the query vector once. This is how fast search works.</p>

<h3 id="cross-encoder-slower-but-smarter">Cross-Encoder: slower but smarter</h3>

<p>Instead of encoding separately, you feed both texts together as one input.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>"[CLS] Text A [SEP] Text B" → Single Encoder → score
</code></pre></div></div>

<p>The model sees both texts at the same time, so it can compare them word-by-word internally. More accurate, but much slower. You can’t pre-compute anything because every pair needs a fresh pass through the model.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Bi-Encoder:    fast,  less accurate  → good for searching millions of docs
Cross-Encoder: slow,  more accurate  → good for re-ranking top 10 results
</code></pre></div></div>

<p>In practice, many systems use both. Bi-encoder to quickly find the top 100 candidates, cross-encoder to re-rank and pick the best 10.</p>

<h3 id="classification-heads-making-decisions">Classification heads: making decisions</h3>

<p>After the encoder gives you a vector, you need to actually <em>decide</em> something with it. The layer that makes the decision is called the <strong>head</strong>.</p>

<p><strong>Softmax</strong> turns raw scores into probabilities that sum to 1. Use this when the categories are mutually exclusive. It’s either spam <em>or</em> not spam, not both.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Raw scores:    [2.0, 1.0, 0.5]
After softmax: [0.59, 0.24, 0.17]   ← sums to 1.0
</code></pre></div></div>

<p><strong>Sigmoid</strong> turns each score independently into a 0 to 1 probability. They don’t need to sum to 1. Use this when multiple labels can be true at once. A photo can be “outdoor” AND “sunny” AND “beach” all at once.</p>

<p>An easy way to remember:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Softmax:  "pick one"  → this email is Inbox OR Spam OR Promotions
Sigmoid:  "pick any"  → this email gets tags: Important + Work + Urgent
</code></pre></div></div>

<h3 id="loss-functions-how-the-model-learns">Loss functions: how the model learns</h3>

<p>A loss function measures how wrong the model is. Training is just minimizing this number. There are different flavors depending on what you’re trying to teach the model.</p>

<p><strong>Cross-Entropy Loss</strong> is the standard for classification. Measures how far your predicted probabilities are from the true label. If the model says “90% class A” and it really is class A, loss is low. If the model says “10% class A” and it really is class A, loss is high. Simple.</p>

<p><strong>Contrastive Loss</strong> teaches the model that similar things should have similar vectors and different things should have different vectors. You give it pairs and it pushes vectors closer or apart. This is how embedding models learn.</p>

<p><strong>Triplet Loss</strong> is the same idea but with three items: an anchor, a positive, and a negative. The anchor should be closer to the positive than to the negative.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>BEFORE training:
    Anchor -------- Positive
    Anchor ---- Negative        ← negative is closer, bad

AFTER training:
    Anchor -- Positive
    Anchor --------------- Negative   ← fixed
</code></pre></div></div>

<p>This is how face recognition works. “Is this the same person?” becomes a distance calculation.</p>

<p><strong>Ranking Loss</strong> says given two items, the model should score the better one higher. This is what powers search ranking and the reward models in RLHF.</p>

<p>These form a nice progression:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Cross-Entropy:  "classify this into a bucket"
Contrastive:    "are these two similar or different?"
Triplet:        "which of these two is closer to this reference?"
Ranking:        "which of these should score higher?"
</code></pre></div></div>

<h3 id="distance-metrics-measuring-similarity">Distance metrics: measuring similarity</h3>

<p>When you have two vectors, how do you measure “how similar” they are?</p>

<p><strong>Euclidean distance</strong> is the straight-line distance. Like measuring with a ruler. Smaller means more similar.</p>

<p><strong>Cosine similarity</strong> measures the angle between vectors and ignores magnitude. This is what embedding search systems use, and for good reason. Two sentences can mean the same thing but have different lengths. Cosine doesn’t care about vector length, only direction.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> 1.0 = identical direction (same meaning)
 0.0 = perpendicular (unrelated)
-1.0 = opposite direction (opposite meaning)
</code></pre></div></div>

<h3 id="training-tricks">Training tricks</h3>

<p><strong>Fine-tuning</strong> is when you take a model that already learned language from billions of words and continue training it on your small dataset. Like hiring an experienced chef and teaching them your restaurant’s menu. Much faster than training someone from scratch.</p>

<p><strong>Label smoothing</strong> is when instead of telling the model “I’m 100% sure this is class A”, you say “I’m 98% sure.” Soft labels like <code class="language-plaintext highlighter-rouge">[0.98, 0.01, 0.01]</code> instead of hard labels like <code class="language-plaintext highlighter-rouge">[1.0, 0.0, 0.0]</code>. This prevents the model from becoming overconfident and keeps its predictions calibrated.</p>

<h3 id="architectural-patterns">Architectural patterns</h3>

<p><strong>Siamese Network</strong> is when the same model processes two or more inputs with shared weights. Named after Siamese twins. It’s not a specific model. It’s a pattern you can apply to any model.</p>

<p><strong>Pooling</strong> is needed because transformers output one vector per token. But for classification you need one vector for the whole text. How do you collapse it?</p>

<ul>
  <li><strong>[CLS] token</strong>: BERT adds a special token at the start whose vector becomes the summary</li>
  <li><strong>Average Pooling</strong>: average all token vectors together (most stable, very common in competition code)</li>
  <li><strong>Max Pooling</strong>: take the max value across all tokens for each dimension (captures strongest signals)</li>
</ul>

<h3 id="putting-it-all-together">Putting it all together</h3>

<p>Now here’s the fun part. Remember those Kaggle notebooks I was reading? Here’s what they actually look like once you know the vocabulary:</p>

<blockquote>
  <p>Bi-encoder with shared weights (Siamese) processes each input separately → average pooling collapses token embeddings into one vector → concatenate the vectors → dense layer + softmax head for classification → trained with cross-entropy loss and label smoothing.</p>
</blockquote>

<p>Every piece is a building block from this post. The competition is about choosing the right combination and fine-tuning it well.</p>

<p>I’ll keep adding to this series as I work through more notebooks. Next up, I want to dig into what makes DeBERTa the go-to model for so many NLP competitions.</p>

<h2 id="tldr">TLDR:</h2>

<p>Every Kaggle NLP notebook follows the same pattern. Pretrained encoder, pooling layer, classification head, loss function. The building blocks are:</p>

<ol>
  <li><strong>Encoders</strong> turn text into vectors. Bi-encoders are fast (pre-compute vectors), cross-encoders are accurate (see both texts at once).</li>
  <li><strong>Softmax</strong> picks one class, <strong>sigmoid</strong> picks many.</li>
  <li><strong>Loss functions</strong> go from classification (cross-entropy) to comparison (contrastive, triplet, ranking).</li>
  <li><strong>Cosine similarity</strong> is how you measure whether two vectors mean the same thing.</li>
  <li><strong>Fine-tuning</strong> a pretrained model beats training from scratch. <strong>Label smoothing</strong> keeps it honest.</li>
</ol>

<p>The architecture is a template. The wins come from knowing which knobs to turn.</p>]]></content><author><name>Naveen Prasanth</name></author><category term="machine-learning" /><category term="kaggle" /><category term="deep learning" /><category term="NLP" /><summary type="html"><![CDATA[I started browsing Kaggle competitions today and noticed something. Every notebook follows the same playbook.]]></summary></entry><entry><title type="html">Irrational Numbers</title><link href="https://naveenprasanth.github.io/mathematics/irrational_numbers/" rel="alternate" type="text/html" title="Irrational Numbers" /><published>2023-05-20T00:00:00+00:00</published><updated>2023-05-20T00:00:00+00:00</updated><id>https://naveenprasanth.github.io/mathematics/irrational_numbers</id><content type="html" xml:base="https://naveenprasanth.github.io/mathematics/irrational_numbers/"><![CDATA[<p>For people who don’t have energy or time today, there is TLDR at the bottom. I would appreciate it if you come back and read the full blog.</p>

<h3 id="numbers-between-0-and-1">Numbers between 0 and 1</h3>
<p>I often think about what kind of problems people would have faced before inventing things and how the inventions would have solved them. In pursuit of this knowledge, I pondered how math would have been before the invention of real numbers. What kind of gaps were they having in their models? That’s when I came to an understanding that irrational numbers are natural and part of our existence.</p>

<p>There are infinite numbers between 0 and 1. But there are two sets of infinities within this range,</p>
<ol>
  <li>Numbers that you get from dividing any one integer by another integer. Eg. Splitting a pizza between 5 friends, everyone gets 20% or 0.2 pizza. We all studied how to get the fraction of any real in school like 0.42069.</li>
  <li>Numbers that contradict <em>type 1</em> . Eg. <em>1.41421356…</em>, <em>3.14159265358979323846…</em>, <em>2.71828182845904523536…</em> 
The ‘…’ simply means the number goes on without repeating itself in a pattern and we cannot get a fraction out of this.
But why would we care about a number that goes on infinitely? more importantly, Can I split a pizza between 5 friends using these numbers?
It cannot! But it can help you find something better. It can tell you how big of a cheese crust you will get in your pizza. How you ask!</li>
</ol>

<p><img src="/assets/img/pizza.png" alt="The pizza" /></p>

<h3 id="our-options-for-measuring-the-length-of-a-circle">Our options for measuring the length of a circle?</h3>
<ol>
  <li>Take a thread, and carefully hold it throughout the out edge of the pizza. Mark the point where we surround the pizza.</li>
  <li>Take a ruler, wash it first, then measure the diameter of the pizza. Find the circumference (ie. Cheese crust length) by using 2<em>$\pi$</em>r.</li>
</ol>

<p>You guessed it right, the <em>3.14159265358979323846…</em> that we have seen in the example is $\pi$, an irrational number. This number is the ratio between the length that you measured with the ruler and the length that you measured with the thread. So, if you know the diameter of any pizza, you can just compute how many inches of cheese crust you will get in that pizza.</p>

<h3 id="oh-wait-pi-is-never-ending-and-non-repeating-how-to-decide-when-to-stop">Oh wait, $\pi$ is never ending and non-repeating, how to decide when to stop</h3>
<p>It all depends on the problem we are solving and the accuracy we need. In the last millennia, people have come up with
a lot of cool ways to accurately generate this ratio. In our use cases, we can stop whenever we feel the accuracy is good enough for us. In schools, we would have only used the <em>3.14</em> approximation of $\pi$.</p>

<p>So the ratio of its diameter to the circumference of a circle that exists in nature is an Irrational number.
Likewise, we can find irrational numbers in many real-world models.</p>

<h3 id="more-examples">More examples</h3>

<ol>
  <li>
    <p>Take a slide that we enjoy in parks, for argument’s sake, let’s assume the stairs and floor as two adjacent sides of a square. Then the slide will be the hypotenuse of the square. We want to know how much smooth steel we have to buy for the slide. We can use help from Pythagoras to find that $slide^2$ = $4^2$ + $4^2$, 
ie slide = 4*$\sqrt{2}$
<img src="/assets/img/slide.png" alt="The slide" /></p>

    <p>For any square with side ‘x’, the diagonal will always be ‘x’*$\sqrt{2}$.</p>
  </li>
  <li>
    <p>In continuous exponential growth or exponential decay, the number <em>2.71828…</em> also known as Euler’s number plays a vital role. It is being used in modeling population growth, compound interest, and radioactive decay.</p>
  </li>
  <li>
    <p>Golden ratio $\phi$ = <em>1.161803…</em> . The ratio which we see in the branching of trees, spiral pattern in sea shells, arrangement of leaves, and the Human body is also an irrational number.</p>
  </li>
</ol>

<p><img src="/assets/img/golden.png" alt="The Golden ratio" /></p>

<h2 id="tldr">TLDR:</h2>
<p>Irrational numbers are real numbers that cannot be expressed as a fraction or ratio of two integers. They are non-repeating, non-terminating decimal numbers. We might wonder what are the use cases or real-world scenarios where we might need/use them. Irrational numbers exist in nature.</p>

<ol>
  <li>Pi (π): Pi is approximately <em>3.14159…</em> and is the ratio of a circle’s circumference to its diameter.</li>
  <li>The Square Root of 2 (√2): The square root of 2 is approximately <em>1.41421…</em> is used for calculating the diagonal length of a square.</li>
  <li>Euler’s Number (e) = <em>2.71828…</em> used in modeling population growth, compound interest, and radioactive decay.
and so much more.</li>
</ol>

<p>This is the importance of irrational numbers. They fill in the gaps and let us model the world in a consistent, smooth and simple way.</p>]]></content><author><name>Naveen Prasanth</name></author><category term="mathematics" /><category term="irrational numbers" /><category term="math" /><summary type="html"><![CDATA[Irrational numbers are natural and part of our existence — from pizza crusts to playground slides.]]></summary></entry></feed>