<?xml version="1.0" encoding="UTF-8"?>
  <rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
      <title>Rajdeep Mondal - Deep Learning for Computer Vision</title>
      <link>https://rajdeepmondal.com/series/cs231n</link>
      <description>Deep Learning for Computer Vision: every entry in the series.</description>
      <language>en-us</language>
      <managingEditor>rajdeep@rajdeepmondal.com (Rajdeep Mondal)</managingEditor>
      <webMaster>rajdeep@rajdeepmondal.com (Rajdeep Mondal)</webMaster>
      <lastBuildDate>Tue, 07 Jul 2026 12:00:00 GMT</lastBuildDate>
      <atom:link href="https://rajdeepmondal.com/series/cs231n/feed.xml" rel="self" type="application/rss+xml"/>
      
  <item>
    <guid isPermaLink="true">https://rajdeepmondal.com/writing/computer-vision-the-generative-modeling-view</guid>
    <title>Name the Job Before You Name the Family</title>
    <link>https://rajdeepmondal.com/writing/computer-vision-the-generative-modeling-view</link>
    <description>Generative modeling is a set of contracts, and the contract you name chooses the model family. Name the metric, then name the failure it hides.</description>
    <content:encoded><![CDATA[<div class="essay-prose"><p>Generative modeling is a set of contracts, and the contract you name chooses the model family. Name the family first and you spend the project defending a choice you cannot test.</p><p>The job is to learn enough structure from the training distribution. With that structure you can assign probability, sample, reconstruct, interpolate, edit, and reason about missing data. The phrase make new images covers all of that and distinguishes none of it.</p><h2 id="each-job-in-generative-modeling-asks-for-a-different-contract">Each job in generative modeling asks for a different contract</h2><p>Likelihood estimation, sampling, reconstruction, representation learning, controllable editing, compression, and missing-data reasoning are separate jobs. Each one names a different output and hands you a different kind of control over the image distribution. Treat them as one job and every comparison afterward is muddy.</p><p>A job that needs likelihood takes an explicit probabilistic model, one that scores data cleanly. Sharp visual samples sit closer to adversarial and diffusion-style objectives. Latent-variable methods are the ones to look at when the job is a compact representation.</p><h2 id="each-family-answers-a-different-question">Each family answers a different question</h2><p>The generative-models note walked the mechanics, PixelRNN and PixelCNN, autoencoders, VAEs, and GAN training. Underneath those mechanics sit three different objectives.</p><p>An autoregressive model predicts the next visible piece from the pieces before it. A VAE compresses an image into a latent variable, decodes it back, and keeps that latent space sampleable. The GAN objective trains a generator until a discriminator cannot separate its samples from real data. Each objective carries its own strengths and its own failure modes.</p><h2 id="a-latent-space-is-a-bet-that-hidden-factors-explain-the-pixels">A latent space is a bet that hidden factors explain the pixels</h2><p>Latent-variable models bet that lower-dimensional hidden factors explain high-dimensional images. An organized latent space makes interpolation and sampling behave, and a tangled one reconstructs but does not generalize gracefully.</p><p>Reconstruction error looks the same in both cases. Walk a path between two latents and watch whether the decoded images stay plausible. When representation is the job, that walk is the evaluation.</p><h2 id="sampling-makes-the-model-show-its-distribution">Sampling makes the model show its distribution</h2><p>A classifier hides behind one accuracy number. A generative model must put its distribution on the page. The samples show blur, mode collapse, missing global structure, texture shortcuts, and memorization. You can see the failure before you compute anything.</p><h2 id="name-the-metric-then-name-what-the-metric-hides">Name the metric, then name what the metric hides</h2><p>Likelihood, sample sharpness, diversity, speed, controllability, reconstruction, and downstream features each judge a different promise. Sharpness does not report diversity, which is why the list carries both. Every metric on that list hides a failure, and a second measurement has to catch it.</p><p>The classification note warned about the clean accuracy number, and a clean likelihood number buys no more trust. A shallow pass compares samples by taste. A serious pass compares promises by evidence.</p><figure class="rm-article-image"><div class="relative overflow-hidden"><div class="bg-muted absolute inset-0 z-10 animate-pulse rounded-[inherit]"><div class="animate-shimmer via-paper-bright/70 absolute inset-0 bg-linear-to-r from-transparent to-transparent"></div></div></div><figcaption>Read a row left to right and the family arrives last, after the failure its metric hides.</figcaption></figure><h2 id="the-builder-test">The Builder Test</h2><p>Before you name a family, write three lines. The output the system needs goes on the first. The metric that judges it goes on the second, and the failure that metric hides goes on the third. If the third line stays empty, the comparison is not ready to run.</p><p>A family chosen because its samples looked good in a talk fails the first line.</p><h2 id="what-carries">What Carries</h2><p>The overview note opened on one question, which is what representation survives an image nobody arranged for the model. Samples are that representation made visible, and generation answers the question with pictures rather than a score. Name what your representation must survive: pose, lighting, scale, background, occlusion. Then choose the machine that survives it.</p><div class="rm-essay-close" aria-hidden="true"><svg width="76" height="32" viewBox="0 0 76 32" aria-hidden="true" class="rm-fmark-essay"><path d="M 2 14 Q 18 2 36 14 T 74 12" fill="none" stroke="currentColor" stroke-width="1.1" stroke-linecap="round" opacity="0.5"></path><line x1="2" y1="24" x2="60" y2="24" stroke="currentColor" stroke-width="1.1"></line><circle cx="68" cy="24" r="2.8" fill="var(--vermilion)"></circle></svg></div></div>]]></content:encoded>
    <pubDate>Tue, 07 Jul 2026 12:00:00 GMT</pubDate>
    <author>rajdeep@rajdeepmondal.com (Rajdeep Mondal)</author>
    <category>machine-learning</category><category>computer-vision</category><category>generative-modeling</category><category>vae</category><category>gan</category><category>sampling</category>
  </item>

  <item>
    <guid isPermaLink="true">https://rajdeepmondal.com/writing/computer-vision-pixelrnn-predicts-an-image-one-piece-at-a-time</guid>
    <title>Exact Likelihood Has a Price in Order and Speed</title>
    <link>https://rajdeepmondal.com/writing/computer-vision-pixelrnn-predicts-an-image-one-piece-at-a-time</link>
    <description>An autoregressive image model buys an exact likelihood and pays in the order it commits to and the sampling speed it gives up. The mask is what keeps the contract honest.</description>
    <content:encoded><![CDATA[<div class="essay-prose"><p>An autoregressive image model buys an exact likelihood. It pays twice: once in the order it commits to, once in the sampling speed it gives up. The mask is what keeps that contract honest.</p><p>PixelRNN models an image as a sequence. Predict the next pixel from the pixels before it, then repeat. A model that assigns high probability to real images learned something about the image distribution.</p><p>The generative models note named this family and stopped at the price. Two things set that price: a product of conditionals, and a mask that enforces the order.</p><h2 id="factor-the-image-into-conditionals-and-the-objective-stops-being-vague">Factor the image into conditionals and the objective stops being vague</h2><p>An autoregressive model decomposes the probability of an image into a product of conditional probabilities. The model predicts each pixel from the pixels that came before it. Train it by maximizing likelihood, or by minimizing negative log likelihood. Those are the same objective written two ways.</p><p>The objective is explicit, which is cleaner than many generative methods manage. You know what you optimize. The number the model reports is the number the training loop moved.</p><h2 id="the-order-is-part-of-the-model">The order is part of the model</h2><p>Choose the order and you choose the prediction problem. Each pixel inherits one context, the pixels ranked before it, and nothing else. Change the ranking and every conditional answers a different question.</p><p>Every prediction carries a context and a probability. The visualizing ConvNets note left confidence as a claim about the model itself. Here the likelihood states the context it assumed and the quantity it computed. A general sense of image quality decides nothing.</p><h2 id="masked-convolutions-stop-the-model-from-seeing-the-pixel-it-must-predict">Masked convolutions stop the model from seeing the pixel it must predict</h2><p>PixelCNN uses masked convolutions so each prediction sees only the pixels the order allows. A convolution kernel covers a neighborhood in every direction. The mask zeroes the taps at the target position and after it. Causality then holds inside a convolutional architecture.</p><figure class="rm-article-image"><div class="relative overflow-hidden"><div class="bg-muted absolute inset-0 z-10 animate-pulse rounded-[inherit]"><div class="animate-shimmer via-paper-bright/70 absolute inset-0 bg-linear-to-r from-transparent to-transparent"></div></div></div><figcaption>The mask decides what one prediction sees, and a parallel training pass still samples pixel by pixel.</figcaption></figure><p>Drop the mask and the model reads the answer it must predict. Training loss falls for the wrong reason. The note on setup failures asked whether the train-time and test-time paths agree. A leaking mask breaks that agreement inside one forward pass, because at sampling time those pixels do not exist.</p><p>The mask also makes training parallel. A recurrent scan walks positions one after another. Masked convolutions score every position of a training image in one pass, and the autoregressive factorization survives.</p><h2 id="sampling-pays-the-bill-for-exactness">Sampling pays the bill for exactness</h2><p>The bill arrives at generation time. Every pixel depends on the pixels before it, so sampling runs one position at a time. An image takes as many forward passes as it has pixels.</p><p>Generate all pixels at once and the cost disappears along with the model. That breaks the contract. You trained each conditional with the earlier pixels in hand, and independent sampling removes them.</p><h2 id="global-structure-must-travel-through-many-conditional-decisions">Global structure must travel through many conditional decisions</h2><p>Local consistency comes free. Each prediction conditions on nearby pixels that are already fixed, so edges line up and texture stays continuous.</p><p>Global structure has to travel. A commitment made in one corner of the image reaches the far corner through a long chain of conditionals. Every link in that chain can drop it.</p><p>That is one reason later systems add stronger global representations: latent variables, attention, or diffusion dynamics. Each of those gives long-range structure a route that skips the pixel-by-pixel chain.</p><h2 id="the-builder-test">The Builder Test</h2><p>Take one output position. Draw the receptive field around it and mark every input pixel the mask allows through. Then look at the target position and everything after it in your chosen order.</p><p>If any of those taps survive the mask, the model trains on the answer. Fix the mask before you read another loss curve.</p><h2 id="what-carries">What Carries</h2><p>Diffusion and transformer-style token models run most image generation now. The lesson outlives the architecture. A generative model is accountable when its factorization, its objective, and its sampling procedure are all stated and all agree.</p><p>An explicit contract like this one is easy to judge. The harder job comes first: name the contract the system needs, then choose the family that can sign it.</p><div class="rm-essay-close" aria-hidden="true"><svg width="76" height="32" viewBox="0 0 76 32" aria-hidden="true" class="rm-fmark-essay"><path d="M 2 14 Q 18 2 36 14 T 74 12" fill="none" stroke="currentColor" stroke-width="1.1" stroke-linecap="round" opacity="0.5"></path><line x1="2" y1="24" x2="60" y2="24" stroke="currentColor" stroke-width="1.1"></line><circle cx="68" cy="24" r="2.8" fill="var(--vermilion)"></circle></svg></div></div>]]></content:encoded>
    <pubDate>Sun, 05 Jul 2026 12:00:00 GMT</pubDate>
    <author>rajdeep@rajdeepmondal.com (Rajdeep Mondal)</author>
    <category>machine-learning</category><category>computer-vision</category><category>pixelrnn</category><category>pixelcnn</category><category>autoregressive-models</category><category>generative-models</category>
  </item>

  <item>
    <guid isPermaLink="true">https://rajdeepmondal.com/writing/computer-vision-practical-convnet-tips-that-still-matter</guid>
    <title>The Old Habits Survive Because the Failure Modes Survive</title>
    <link>https://rajdeepmondal.com/writing/computer-vision-practical-convnet-tips-that-still-matter</link>
    <description>Old ConvNet advice still works because the failures it prevents did not change: bad data, wrong normalization, unstable learning rates, leakage, weak baselines, and uninspected errors.</description>
    <content:encoded><![CDATA[<div class="essay-prose"><p>The field moved from AlexNet to ResNets to ConvNeXt to ViTs, and the training habits did not become optional. Old ConvNet advice still works because the failures it prevents did not change. Bad data, wrong normalization, unstable learning rates, leakage, weak baselines, and uninspected errors still break strong models.</p><p>Most practical ConvNet work is preparation. You clean the old machinery well enough that the model gets a fair chance.</p><h2 id="augmentation-states-which-changes-must-not-change-the-label">Augmentation states which changes must not change the label</h2><p>An augmentation is an invariance claim written in code. Random crops, flips, color jitter, and scale changes each assert that some transformation leaves the label alone. The classification note named what a vision system must stay invariant to: pose, lighting, small translation, occlusion, and clutter.</p><p>The constraint is taste. Augmentation must not change the task. Flip a street-sign dataset blindly and you can change the answer. Turn medical scans casually and you teach the model a lie.</p><figure class="rm-article-image"><div class="relative overflow-hidden"><div class="bg-muted absolute inset-0 z-10 animate-pulse rounded-[inherit]"><div class="animate-shimmer via-paper-bright/70 absolute inset-0 bg-linear-to-r from-transparent to-transparent"></div></div></div><figcaption>The same flip preserves the label in one row and destroys it in another.</figcaption></figure><h2 id="normalization-must-be-identical-between-train-and-test-except-for-stochastic-augmentation">Normalization must be identical between train and test except for stochastic augmentation</h2><p>Subtract means. Scale channels. Run the same pipeline at test time, with the random transforms switched off. A model trained under one input distribution and served under another must improvise. Nothing crashes when the two pipelines disagree, so the bug shows up only as a worse number.</p><h2 id="stacks-of-small-filters-beat-one-large-filter">Stacks of small filters beat one large filter</h2><p>Stacks of small filters build large receptive fields with fewer parameters and more nonlinearities than one giant filter. That is why repeated 3 by 3 convolutions became a durable pattern. The motif matters when you pick a backbone, because it sets parameter count at a fixed receptive field.</p><h2 id="the-learning-rate-is-a-schedule-that-changes-during-training">The learning rate is a schedule that changes during training</h2><p>Warmups, step decay, cosine schedules, and careful restarts express one instinct. The correct step size at the start is the wrong step size later. An unstable rate looks like a bad architecture from the outside. Watch the curve before you blame the model.</p><h2 id="a-dull-recipe-beats-a-clever-mess">A dull recipe beats a clever mess</h2><p>Start from a known backbone and match preprocessing to it. Use pretrained weights when a good one exists. Train a simple head and keep the shapes honest. Get a validation baseline that behaves sanely before you tune anything.</p><p>Stack tricks after that. Augmentation, schedules, weight decay, label smoothing, mixup, and stronger pretraining can all help. Each one is easier to reason about once the baseline holds still.</p><p>The one-change rule is the same discipline the training note defined, one level further along the pipeline. Change augmentation, optimizer, batch size, and architecture at once, and you can win the run while learning nothing. That trade is fine for a desperate leaderboard run. It is bad for understanding.</p><p>Log every run. Save the config beside its checkpoint, track data versions, inspect calibration, and test on corruptions or distribution slices. Those checks guard the same split the classification note sealed. A dataset that changes under a run makes every comparison meaningless.</p><h2 id="if-you-cannot-name-the-dominant-error-mode-changing-the-architecture-is-theater">If you cannot name the dominant error mode, changing the architecture is theater</h2><p>Accuracy reports the scoreboard. Error analysis decides the next intervention. Look at false positives, false negatives, confusion pairs, rare classes, lighting failures, background shortcuts, and corrupted inputs.</p><p>One error mode usually dominates. A newer model does not fix it until the boring checks pass.</p><h2 id="the-builder-test">The Builder Test</h2><p>Name the dominant error mode from your last run in one sentence, with the slice it lives in. Without that sentence, run error analysis before you touch the architecture. The sentence is the permission slip for the change.</p><h2 id="what-carries">What Carries</h2><p>Dull work buys the right to test an interesting architecture. Discipline decides whether a result is real. The last two notes ask what result you were trying to produce.</p><div class="rm-essay-close" aria-hidden="true"><svg width="76" height="32" viewBox="0 0 76 32" aria-hidden="true" class="rm-fmark-essay"><path d="M 2 14 Q 18 2 36 14 T 74 12" fill="none" stroke="currentColor" stroke-width="1.1" stroke-linecap="round" opacity="0.5"></path><line x1="2" y1="24" x2="60" y2="24" stroke="currentColor" stroke-width="1.1"></line><circle cx="68" cy="24" r="2.8" fill="var(--vermilion)"></circle></svg></div></div>]]></content:encoded>
    <pubDate>Fri, 03 Jul 2026 12:00:00 GMT</pubDate>
    <author>rajdeep@rajdeepmondal.com (Rajdeep Mondal)</author>
    <category>machine-learning</category><category>computer-vision</category><category>convnet-tips</category><category>training</category><category>practical-ml</category>
  </item>

  <item>
    <guid isPermaLink="true">https://rajdeepmondal.com/writing/computer-vision-adversarial-examples-expose-the-gap-between-scores-and-sight</guid>
    <title>Adversarial Examples Expose the Gap Between Scores and Sight</title>
    <link>https://rajdeepmondal.com/writing/computer-vision-adversarial-examples-expose-the-gap-between-scores-and-sight</link>
    <description>The gradients that trained the model are the gradients that break it. FGSM takes one step, PGD takes many, and a defense means nothing until you name the attacker.</description>
    <content:encoded><![CDATA[<div class="essay-prose"><p>The gradients that trained the model are the gradients that break it. A defense means nothing until you name the attacker.</p><p>A tiny perturbation, invisible or irrelevant to a human, moves a network into a confident wrong prediction. The overview opened this unit with a pressure list: pose, lighting, scale, background, occlusion, class imbalance. An adversary is that list with intent. It searches for the one shift you never tested.</p><h2 id="differentiability-makes-the-attack-cheap">Differentiability makes the attack cheap</h2><p>A neural network is differentiable from the loss back to the pixels. Training walks that path to the weights. The attack walks the same path to the input. It nudges each pixel toward a higher target score, or a lower score on the correct class.</p><p>The visualization note computed that same input gradient and drew it as a saliency map. Saliency asks which pixels the model leans on. The attack asks which pixels to move. One backward pass answers both.</p><div class="uth-essay uth-figure-embed"><div><figure class="fig reveal" id="cs231n-adversarial-gap">
  
  <div class="fig-head">
    <span class="fig-title">tiny pixel changes can move the score</span>
    <span class="ctrl">
      
      
    </span>
  </div>
  <div class="stage" id="cs231n-adversarial-gap-stage" role="group" aria-live="polite" aria-describedby="cs231n-adversarial-gap-caption">
    <div class="course-visual adv-wrap" aria-hidden="true">
      <div class="adv-card"><span class="adv-label">clean input</span><div class="adv-grid adv-clean"><span class="px"></span><span class="px"></span><span class="px"></span><span class="px"></span><span class="px"></span><span class="px"></span><span class="px"></span><span class="px"></span><span class="px"></span><span class="px"></span><span class="px"></span><span class="px"></span><span class="px"></span><span class="px"></span><span class="px"></span><span class="px"></span><span class="px"></span><span class="px"></span><span class="px"></span><span class="px"></span><span class="px"></span><span class="px"></span><span class="px"></span><span class="px"></span><span class="px"></span><span class="px"></span><span class="px"></span><span class="px"></span><span class="px"></span><span class="px"></span><span class="px"></span><span class="px"></span><span class="px"></span><span class="px"></span><span class="px"></span><span class="px"></span></div></div>
      <div class="adv-op">+</div>
      <div class="adv-card"><span class="adv-label">gradient nudge</span><div class="adv-grid adv-noise"><span class="px hot"></span><span class="px"></span><span class="px"></span><span class="px"></span><span class="px"></span><span class="px"></span><span class="px"></span><span class="px hot"></span><span class="px"></span><span class="px"></span><span class="px"></span><span class="px hot"></span><span class="px"></span><span class="px"></span><span class="px hot"></span><span class="px"></span><span class="px"></span><span class="px"></span><span class="px"></span><span class="px"></span><span class="px"></span><span class="px hot"></span><span class="px hot"></span><span class="px"></span><span class="px"></span><span class="px"></span><span class="px"></span><span class="px"></span><span class="px hot"></span><span class="px"></span><span class="px"></span><span class="px"></span><span class="px"></span><span class="px hot"></span><span class="px"></span><span class="px hot"></span></div></div>
      <div class="adv-op">=</div>
      <div class="adv-card"><span class="adv-label">same to us</span><div class="adv-grid adv-perturbed"><span class="px hot"></span><span class="px"></span><span class="px"></span><span class="px"></span><span class="px"></span><span class="px"></span><span class="px"></span><span class="px hot"></span><span class="px"></span><span class="px"></span><span class="px"></span><span class="px hot"></span><span class="px"></span><span class="px"></span><span class="px hot"></span><span class="px"></span><span class="px"></span><span class="px"></span><span class="px"></span><span class="px"></span><span class="px"></span><span class="px hot"></span><span class="px hot"></span><span class="px"></span><span class="px"></span><span class="px"></span><span class="px"></span><span class="px"></span><span class="px hot"></span><span class="px"></span><span class="px"></span><span class="px"></span><span class="px"></span><span class="px hot"></span><span class="px"></span><span class="px hot"></span></div></div>
      <div class="adv-score">
        <span class="adv-label">model score</span>
        <div class="adv-row adv-cat"><span>cat</span><span class="adv-track"><i class="adv-bar"></i></span><span>82</span></div>
        <div class="adv-row adv-truck"><span>truck</span><span class="adv-track"><i class="adv-bar"></i></span><span>88</span></div>
      </div>
    </div>
    <div class="course-rail" data-rail aria-hidden="true"><span class="course-node" data-node="0">
        <span class="course-dot" aria-hidden="true">1</span>
        <span class="course-node-label">image</span>
      </span><span class="course-node" data-node="1">
        <span class="course-dot" aria-hidden="true">2</span>
        <span class="course-node-label">gradient</span>
      </span><span class="course-node" data-node="2">
        <span class="course-dot" aria-hidden="true">3</span>
        <span class="course-node-label">nudge</span>
      </span><span class="course-node" data-node="3">
        <span class="course-dot" aria-hidden="true">4</span>
        <span class="course-node-label">flip</span>
      </span></div>
    <div class="course-grid"><div class="course-step" data-step="0">
        <span class="course-k">image</span>
        <span class="course-v">start with an input a human still recognizes</span>
      </div><div class="course-step" data-step="1">
        <span class="course-k">gradient</span>
        <span class="course-v">find the pixel direction that most changes the model score</span>
      </div><div class="course-step" data-step="2">
        <span class="course-k">nudge</span>
        <span class="course-v">add a small perturbation that stays hard to see</span>
      </div><div class="course-step" data-step="3">
        <span class="course-k">flip</span>
        <span class="course-v">the model changes its answer even when the image still looks the same</span>
      </div></div>
    <div class="course-status" data-status aria-live="polite"><b>image.</b> start with an input a human still recognizes</div>
  </div>
  <figcaption id="cs231n-adversarial-gap-caption">Adversarial examples expose the gap between human similarity and model geometry.</figcaption>
</figure></div><span hidden=""></span></div><h2 id="a-high-softmax-score-is-confidence-under-the-learned-representation">A high softmax score is confidence under the learned representation</h2><p>A high softmax score reports confidence under the learned representation. It carries no claim about whether the image is unambiguous, safe, or aligned with human sight. A perturbation can push the model across a decision boundary while a human sees the same object.</p><p>That gap decides how much weight the score deserves. It matters most in systems that touch money, safety, identity, health, or physical control.</p><p>Clean held-out accuracy is the number the classification note warned about, and the adversary attaches an optimizer to it. Your held-out set arrives by sampling. The adversarial set arrives by search.</p><h2 id="fgsm-takes-one-step-pgd-takes-many">FGSM takes one step, PGD takes many</h2><p>The Fast Gradient Sign Method takes one step in the direction that increases loss with respect to the input. PGD takes many smaller steps and projects each one back inside the perturbation budget. It is usually the stronger first-order attack.</p><p>One step assumes the loss surface stays straight across the whole budget. Many small steps drop that assumption and follow the curve, which is the whole difference between the two. A defense tested only against FGSM reports a number PGD does not support.</p><figure class="rm-article-image"><div class="relative overflow-hidden"><div class="bg-muted absolute inset-0 z-10 animate-pulse rounded-[inherit]"><div class="animate-shimmer via-paper-bright/70 absolute inset-0 bg-linear-to-r from-transparent to-transparent"></div></div></div><figcaption>The FGSM arrow crosses the budget box in one step, and PGD projects every smaller step back inside it.</figcaption></figure><h2 id="a-defense-without-a-named-attacker-is-a-wish">A defense without a named attacker is a wish</h2><p>A robustness claim only means something inside a threat model. Name what the attacker can change: pixels within an L-infinity bound, a physical sticker, lighting, camera angle, or compression. Say what the attacker can examine, including your weights, your preprocessing, and the defense itself. The success condition decides the rest, because any wrong label and one specific wrong label are different attacks.</p><p>Change any one of those three and the defense changes with it. A method that holds under an L-infinity budget can fail completely against a sticker on the object. Write the threat model before you write the defense.</p><h2 id="a-defense-that-hides-gradients-looks-strong-until-someone-adapts">A defense that hides gradients looks strong until someone adapts</h2><p>Gradient masking, image compression, and naive preprocessing all lower attack success on the first try. They damage the gradient the attacker needs. The attack then fails for a reason that has nothing to do with robustness. A weak evaluation reads that failure as a defense.</p><p>An adaptive attack that knows the defense reads it correctly. It usually shows the model was only hard to attack with the first method you tried. The honest standard is harsh: evaluate against attacks that know your defense.</p><p>The options that survive that standard are adversarial training, certified robustness in limited settings, better evaluation protocols, and system-level safeguards. Each one costs training time or narrows the setting where the claim holds.</p><h2 id="the-builder-test">The Builder Test</h2><p>Before you ship a vision model, run four evaluations and report them in one place.</p><ul><li>Clean held-out accuracy.</li><li>Accuracy under corruptions.</li><li>Accuracy under distribution shift.</li><li>Accuracy under an adaptive attack that knows your defense.</li></ul><p>A defense that moves only the first number has moved nothing. After launch, keep watching real failures. The threat model you did not write is the one production finds.</p><h2 id="what-carries">What Carries</h2><p>Accuracy says how often the model is right on the dataset you chose. Robustness asks how badly it fails when the world stops cooperating. The reliability work that catches most of your real failures is older and duller than any attack. Do that work first, then earn the right to worry about the attacker.</p><div class="rm-essay-close" aria-hidden="true"><svg width="76" height="32" viewBox="0 0 76 32" aria-hidden="true" class="rm-fmark-essay"><path d="M 2 14 Q 18 2 36 14 T 74 12" fill="none" stroke="currentColor" stroke-width="1.1" stroke-linecap="round" opacity="0.5"></path><line x1="2" y1="24" x2="60" y2="24" stroke="currentColor" stroke-width="1.1"></line><circle cx="68" cy="24" r="2.8" fill="var(--vermilion)"></circle></svg></div></div>]]></content:encoded>
    <pubDate>Wed, 01 Jul 2026 12:00:00 GMT</pubDate>
    <author>rajdeep@rajdeepmondal.com (Rajdeep Mondal)</author>
    <category>machine-learning</category><category>computer-vision</category><category>adversarial-examples</category><category>robustness</category><category>security</category>
  </item>

  <item>
    <guid isPermaLink="true">https://rajdeepmondal.com/writing/computer-vision-nerf-learns-a-scene-as-a-function</guid>
    <title>NeRF Learns a Scene as a Function</title>
    <link>https://rajdeepmondal.com/writing/computer-vision-nerf-learns-a-scene-as-a-function</link>
    <description>NeRF stores a scene as a function you query, and posed photographs are enough supervision because rendering is differentiable.</description>
    <content:encoded><![CDATA[<div class="essay-prose"><p>NeRF stores a scene as a function you query. Posed photographs are enough supervision, because rendering is differentiable and the gradient reaches the field through the image.</p><p>Give the network a 3D location and a viewing direction. It returns density and color. Render enough rays through that function and you have a new view of the scene. The pipeline stores no mesh and no voxel grid.</p><h2 id="the-scene-becomes-a-field-you-query-instead-of-a-table-you-store">The scene becomes a field you query instead of a table you store</h2><p>The field maps coordinates to radiance and density. Density says whether something is there. Color says what light leaves that point in a given direction.</p><p>The representation stays continuous, so you can query between sampled points. A grid stores answers at fixed places, and a coarse grid caps the detail you can get back. A function computes an answer wherever you ask.</p><h2 id="rendering-supplies-the-training-signal">Rendering supplies the training signal</h2><p>A ray leaves the camera and passes through the scene. The renderer samples points along the ray and asks the field for density and color at each one. It accumulates those samples into one predicted pixel. Training compares that pixel to the captured pixel at the same place.</p><p>The backward chain here is the one from the backprop note, extended through image formation. Every accumulation step is a differentiable operation, so the gradient walks back through the samples and into the field weights. Backpropagation updates the field until rendered views match observed views.</p><p>The loss rule from the linear classifier note holds without change. Posed photographs are the labels. Wrong means a rendered pixel that disagrees with the captured one. No 3D ground-truth mesh enters the loop.</p><figure class="rm-article-image"><div class="relative overflow-hidden"><div class="bg-muted absolute inset-0 z-10 animate-pulse rounded-[inherit]"><div class="animate-shimmer via-paper-bright/70 absolute inset-0 bg-linear-to-r from-transparent to-transparent"></div></div></div><figcaption>The forward pass accumulates samples into one pixel, and the gradient returns along the same path into the field.</figcaption></figure><h2 id="view-dependence-is-a-feature-of-the-representation">View dependence is a feature of the representation</h2><p>Color depends on viewing direction, so one point can look different to two cameras. That is how the field carries highlights and other view-dependent effects. The scene lives in the parameters of a function, and you reach it by querying along a ray.</p><p>The overview note listed the pressures a representation must survive, and pose sits first on that list. One field serves every camera, so view consistency becomes a property of the representation. The test is a camera pose the model never saw. Render it and look for geometry that drifts.</p><h2 id="positional-encoding-lets-an-mlp-hold-high-frequency-detail">Positional encoding lets an MLP hold high-frequency detail</h2><p>A plain MLP struggles to hold high-frequency detail when you feed it raw coordinates. Positional encoding maps each coordinate into a set of sinusoidal features. Those features give the network a handle on fine structure. The change happens before the first layer.</p><p>The architecture note called a design a bet about what structure the data has. That encoding places the bet at the input. The bet is that the scene holds fine structure the raw coordinates make hard to reach.</p><h2 id="classic-nerf-pays-for-exactness-on-every-ray">Classic NeRF pays for exactness on every ray</h2><p>Each pixel costs many samples along its ray, and each image costs many rays. Per-scene optimization repeats that bill for every new scene. That makes classic NeRF slow to train and slow to render.</p><p>It also needs good camera poses and enough coverage. A pose that is off puts the ray in the wrong place. The field then fits the scene to that wrong ray.</p><p>Later work improved speed, generalization, and editing. The original design still earns study, because geometry and appearance become one differentiable training problem.</p><h2 id="the-builder-test">The Builder Test</h2><p>Trace one pixel from the camera to the loss. Pick one pixel in one training image and name the ray it comes from. Write down the samples along that ray and the accumulation that turns them into a color. The captured pixel at the other end of that comparison is the label.</p><p>Now move the camera pose by a small amount and walk the same chain. The ray starts somewhere else, every sample moves with it, and the loss charges the field for the difference. You pass this test when you can point at the exact step where pose error becomes geometry error.</p><h2 id="what-carries">What Carries</h2><p>A scene can be learned as a function when rendering gives the model a way to be wrong. Before you trust any new representation, find the operation that makes its error measurable. A representation shift changes what a model can express, and it changes nothing about how easily gradients turn against it.</p><div class="rm-essay-close" aria-hidden="true"><svg width="76" height="32" viewBox="0 0 76 32" aria-hidden="true" class="rm-fmark-essay"><path d="M 2 14 Q 18 2 36 14 T 74 12" fill="none" stroke="currentColor" stroke-width="1.1" stroke-linecap="round" opacity="0.5"></path><line x1="2" y1="24" x2="60" y2="24" stroke="currentColor" stroke-width="1.1"></line><circle cx="68" cy="24" r="2.8" fill="var(--vermilion)"></circle></svg></div></div>]]></content:encoded>
    <pubDate>Mon, 29 Jun 2026 12:00:00 GMT</pubDate>
    <author>rajdeep@rajdeepmondal.com (Rajdeep Mondal)</author>
    <category>machine-learning</category><category>computer-vision</category><category>nerf</category><category>3d-vision</category><category>rendering</category>
  </item>

  <item>
    <guid isPermaLink="true">https://rajdeepmondal.com/writing/computer-vision-generative-models-learn-the-shape-of-the-data</guid>
    <title>Generative Models Learn the Shape of the Data</title>
    <link>https://rajdeepmondal.com/writing/computer-vision-generative-models-learn-the-shape-of-the-data</link>
    <description>Autoregressive models buy likelihood and pay in sampling speed. VAEs organize the latent space and blur. GANs sharpen and collapse. Name the metric first.</description>
    <content:encoded><![CDATA[<div class="essay-prose"><p>Each generative family buys one property and pays in a fixed direction. Name the metric first, and the family follows.</p><p>The linear-classifier note treated the loss as the real decision, because the loss decides what a model attends to. Generation applies the same discipline to the metric. Until you name one, sample quality stays a matter of taste.</p><h2 id="generation-asks-which-images-can-come-from-this-data-at-all">Generation asks which images can come from this data at all</h2><p>Classification asks which label fits this image, and a classifier answers with one accuracy number. Generation has no such number to stand behind.</p><p>A generative model has to put samples on the page. The batch shows what the model treats as typical. It also shows which modes it misses, which details it blurs, and which artifacts it repeats.</p><h2 id="autoregressive-models-buy-clean-likelihood-and-pay-in-sampling-speed">Autoregressive models buy clean likelihood and pay in sampling speed</h2><p>PixelRNN and PixelCNN model an image as a sequence of conditional predictions. The model predicts each pixel from the pixels before it, in one fixed order. That order gives a likelihood you can compute directly.</p><p>Sampling has to follow that order, one pixel at a time. The cost lands on generation, and it lands there again on every sample you draw.</p><h2 id="autoencoders-compress-and-vaes-make-the-latent-space-sampleable">Autoencoders compress, and VAEs make the latent space sampleable</h2><p>An autoencoder maps an input to a latent code and rebuilds the input from that code. The code keeps the information reconstruction needs and drops the rest. Sampling from that space is a separate job, and a plain autoencoder does not organize it for you.</p><p>A variational autoencoder puts a distribution over the latents. The encoder predicts that distribution, and the decoder rebuilds from samples of it.</p><p>The loss is a negotiated settlement between two terms. The reconstruction term wants faithful outputs. The KL term wants the latent distribution to stay close to a simple prior.</p><p>Press the prior too hard and reconstructions suffer. Press reconstruction too hard and the space stops being worth sampling. Most VAEs settle where the samples blur.</p><p>The check is cheap. Decode two nearby latent points and look at both images. If the pair stays sensible, the space is organized enough to sample from.</p><h2 id="gans-sharpen-samples-and-destabilize-training">GANs sharpen samples and destabilize training</h2><p>A GAN runs a generator against a discriminator. The generator makes images, the discriminator separates generated images from real ones, and the generator improves by fooling it.</p><p>Sharpness comes out of that game. The discriminator rejects unrealistic samples, so the generator learns from a signal closer to perception.</p><p>The same game breaks the training. The generator can exploit the discriminator instead of covering the data, or collapse onto a few modes. The signal keeps moving while the model learns, which is why GAN evaluation stays hard.</p><h2 id="likelihood-and-sample-quality-are-different-scores">Likelihood and sample quality are different scores</h2><p>One model assigns good likelihood and still produces blurry samples. Another produces sharp samples and makes likelihood hard to compute. Rank the same models by one score and then the other, and the orders disagree.</p><p>Comparing autoregressive models, VAEs, GANs, and diffusion-style methods needs a named metric. The candidates are likelihood, perceptual quality, diversity, controllability, and speed.</p><figure class="rm-article-image"><div class="relative overflow-hidden"><div class="bg-muted absolute inset-0 z-10 animate-pulse rounded-[inherit]"><div class="animate-shimmer via-paper-bright/70 absolute inset-0 bg-linear-to-r from-transparent to-transparent"></div></div></div><figcaption>The training signal decides the sampling procedure, and the sampling procedure decides which failure shows up first.</figcaption></figure><p>Each metric stays quiet about its own failure. Likelihood says nothing about blur, and perceptual quality says nothing about missing modes or memorization.</p><p>Generation turns into copying when the data, the objective, or the evaluation lets memorization pass as sampling. The classification note named leakage and near-duplicate checks for the same reason. Both ask what the model kept.</p><p>A sharp sample is the model being confident about the data distribution. The visualization note put that warning on a class score, and it carries over here. Confidence is confidence under the representation the model learned.</p><h2 id="the-builder-test">The Builder Test</h2><p>Take a model you trained and generate a batch. Judge the batch on the one metric you named before you looked at it. Then write down the failure that metric cannot see.</p><p>The likely failures are already known. Autoregressive sampling runs slow and VAE reconstructions blur. GAN training drops modes, and diffusion needs a run of denoising steps for every image it makes.</p><p>The batch then needs a second check, chosen against the failure you wrote down. Count distinct modes when the metric rewards sharpness. Compare each sample against its nearest training image when the metric rewards fidelity.</p><p>If the batch fails that second check, the score it earned does not mean anything yet.</p><h2 id="what-carries">What Carries</h2><p>Samples are the honest surface of a generative model. One good sample is the cheapest thing that surface can give, and a collapsed model still produces one. Evidence starts at a batch, judged on the metric you named before generating.</p><p>Every family here learns a distribution over images. The next representation learns one scene, with rendering supplying the label.</p><div class="rm-essay-close" aria-hidden="true"><svg width="76" height="32" viewBox="0 0 76 32" aria-hidden="true" class="rm-fmark-essay"><path d="M 2 14 Q 18 2 36 14 T 74 12" fill="none" stroke="currentColor" stroke-width="1.1" stroke-linecap="round" opacity="0.5"></path><line x1="2" y1="24" x2="60" y2="24" stroke="currentColor" stroke-width="1.1"></line><circle cx="68" cy="24" r="2.8" fill="var(--vermilion)"></circle></svg></div></div>]]></content:encoded>
    <pubDate>Sat, 27 Jun 2026 12:00:00 GMT</pubDate>
    <author>rajdeep@rajdeepmondal.com (Rajdeep Mondal)</author>
    <category>machine-learning</category><category>computer-vision</category><category>generative-models</category><category>vae</category><category>gan</category><category>pixelcnn</category>
  </item>

  <item>
    <guid isPermaLink="true">https://rajdeepmondal.com/writing/computer-vision-transformers-turn-attention-into-an-architecture</guid>
    <title>Transformers Turn Attention Into an Architecture</title>
    <link>https://rajdeepmondal.com/writing/computer-vision-transformers-turn-attention-into-an-architecture</link>
    <description>A vision transformer trades convolution&#39;s locality and weight sharing for learned routing between patches, and the training recipe pays the difference.</description>
    <content:encoded><![CDATA[<div class="essay-prose"><p>A vision transformer trades convolution&#x27;s built-in locality for learned routing, and the training recipe pays the difference.</p><p>The attention note left routing as one operation inside a recurrent model. A transformer promotes routing to the backbone. Recurrence goes away, and the model reads every token in parallel. In language the tokens are words or subwords. In vision they are image patches.</p><h2 id="self-attention-makes-the-path-between-two-positions-short">Self-attention makes the path between two positions short</h2><p>Inside self-attention every token reads from other tokens. A patch gathers evidence from a distant patch in one step. A word gathers evidence from earlier or later words, depending on the setup.</p><p>A recurrent model carries one compressed summary forward, and that compression is the ceiling. Attention removes the carry.</p><p>Stack the blocks and the effect compounds. Each layer builds a table of relationships between the visible positions. Then it updates every patch with the evidence attention routes to it. Local patch evidence becomes global object evidence.</p><h2 id="patch-size-is-a-modeling-decision-you-make-before-training">Patch size is a modeling decision you make before training</h2><p>Patchification is the entry point. The model cuts the image into patches, embeds each patch, adds positional information, and feeds the sequence into transformer blocks. After that step the architecture looks language-like.</p><p>The patch is a rough unit. Small patches keep fine structure and lengthen the sequence, and large patches cut compute and lose detail. Tokenization is part of the model, so set the patch size against the smallest structure your label depends on.</p><p>The patch grid also changes how the pressure list from the overview note reaches the model. Translation now moves evidence between patches instead of sliding it under one shared filter. Scale decides how many patches an object covers.</p><h2 id="attention-needs-position-because-it-does-not-have-one">Attention needs position because it does not have one</h2><p>Attention is mostly permutation-friendly. Shuffle the tokens and the operation barely notices. The model needs positional information to know where a token came from.</p><p>In vision that information carries weight. A patch at the top of the image and the same patch at the bottom are not the same evidence. Position is the only thing that separates them.</p><h2 id="multi-head-attention-gives-the-model-several-routing-channels">Multi-head attention gives the model several routing channels</h2><p>Different heads learn different routing patterns. One head stays local while another connects object parts, and a third reaches for global context. The gain is capacity for several relation types at once.</p><p>Do not over-romanticize a single head. A tidy caption for one head is something a person writes after training.</p><h2 id="less-built-in-bias-means-the-data-must-teach-what-the-architecture-no-longer-assumes">Less built-in bias means the data must teach what the architecture no longer assumes</h2><p>The convolution note named the two assumptions a ConvNet ships with: locality and weight sharing. Nearby pixels belong together, and the model reuses one filter across every position. Patch tokens keep much less of both.</p><figure class="rm-article-image"><div class="relative overflow-hidden"><div class="bg-muted absolute inset-0 z-10 animate-pulse rounded-[inherit]"><div class="animate-shimmer via-paper-bright/70 absolute inset-0 bg-linear-to-r from-transparent to-transparent"></div></div></div><figcaption>Patchification drops locality and weight sharing, and the training recipe is what puts that structure back.</figcaption></figure><p>A vanilla transformer must learn that structure from data and pretraining. Vision transformers became convincing alongside scale, stronger augmentation, large datasets, and self-supervised pretraining. Architecture details hand back some of the prior too, and the training recipe carries a lot of the win.</p><p>The same recipe teaches whatever else the data contains. The visualization note&#x27;s shortcut hunt transfers to attention maps without a change of tools. A route can land on the background, a repeated texture, or a dataset artifact and still produce the right label. A clean attention map is confidence under the learned representation, and nothing more.</p><h2 id="small-data-tight-latency-and-dominant-local-texture-still-favor-convnets">Small data, tight latency, and dominant local texture still favor ConvNets</h2><p>I still reach for a convolutional backbone when the data is small and the latency budget is tight. Local texture and spatial hierarchy can dominate a task, and convolution already assumes both.</p><p>A transformer is a trade. Take it when distant parts of the image decide the label and you can pay the data bill.</p><h2 id="the-builder-test">The Builder Test</h2><p>Name the smallest structure your label depends on. Measure it against your patch size. If that structure fits inside one patch, the grid is fine. If it straddles several patches, attention must reassemble it, and that reassembly is what you test first.</p><p>Then name what supplies the locality prior you removed. If you cannot name one, you dropped a prior and put nothing in its place.</p><p>Inspect what a patch attends to. If the route does not match the evidence a person uses, the model solves a different task.</p><h2 id="what-carries">What Carries</h2><p>Carry the routing view. A transformer learns which pieces of the image must talk to each other. The architecture says almost nothing about which pieces those are.</p><p>Price the prior before you remove it. When nothing in the recipe teaches locality, the patch grid buys flexibility you cannot use.</p><p>Routing and recurrence both end at a label. The next family must produce the image itself.</p><div class="rm-essay-close" aria-hidden="true"><svg width="76" height="32" viewBox="0 0 76 32" aria-hidden="true" class="rm-fmark-essay"><path d="M 2 14 Q 18 2 36 14 T 74 12" fill="none" stroke="currentColor" stroke-width="1.1" stroke-linecap="round" opacity="0.5"></path><line x1="2" y1="24" x2="60" y2="24" stroke="currentColor" stroke-width="1.1"></line><circle cx="68" cy="24" r="2.8" fill="var(--vermilion)"></circle></svg></div></div>]]></content:encoded>
    <pubDate>Thu, 25 Jun 2026 12:00:00 GMT</pubDate>
    <author>rajdeep@rajdeepmondal.com (Rajdeep Mondal)</author>
    <category>machine-learning</category><category>computer-vision</category><category>transformers</category><category>self-attention</category><category>vision-transformers</category>
  </item>

  <item>
    <guid isPermaLink="true">https://rajdeepmondal.com/writing/computer-vision-attention-routes-context-instead-of-compressing-it</guid>
    <title>Attention Routes Context Instead of Compressing It</title>
    <link>https://rajdeepmondal.com/writing/computer-vision-attention-routes-context-instead-of-compressing-it</link>
    <description>Queries ask, keys advertise, values answer. A captioning decoder builds a different context vector for every word instead of reading one compressed summary.</description>
    <content:encoded><![CDATA[<div class="essay-prose"><p>Attention replaces one compressed state with a different context vector for every step. The mechanism is retrieval.</p><h2 id="a-fixed-image-vector-asks-one-representation-to-serve-every-word">A fixed image vector asks one representation to serve every word</h2><p>A captioning decoder that reads one fixed image vector asks that vector to serve every word it generates. The word dog and the word ball need different regions of the same image. One summary cannot hold both at full strength.</p><p>The recurrent networks note priced that compression. One hidden state gets rewritten at every step, and everything the last word needs has to survive the trip. Attention lets the decoder build its context after it knows what this step is asking for.</p><p>The change has a price. The decoder now keeps every region available and scores all of them at every step.</p><div class="uth-essay uth-figure-embed"><div><figure class="fig reveal" id="cs231n-attention-routing">
  
  <div class="fig-head">
    <span class="fig-title">attention routes context on demand</span>
    <span class="ctrl">
      
      
    </span>
  </div>
  <div class="stage" id="cs231n-attention-routing-stage" role="group" aria-live="polite" aria-describedby="cs231n-attention-routing-caption">
    <div class="course-visual vit-wrap" aria-hidden="true">
      <div class="vit-patches">
        <div class="vit-grid"><span class="vit-patch"></span><span class="vit-patch"></span><span class="vit-patch"></span><span class="vit-patch"></span><span class="vit-patch"></span><span class="vit-patch"></span><span class="vit-patch"></span><span class="vit-patch"></span><span class="vit-patch"></span><span class="vit-patch"></span><span class="vit-patch"></span><span class="vit-patch"></span><span class="vit-patch"></span><span class="vit-patch"></span><span class="vit-patch"></span><span class="vit-patch"></span></div>
        <div class="vit-heads"><span class="vit-head">head A: local edge</span><span class="vit-head">head B: object part</span></div>
      </div>
      <div class="vit-panel">
        <div class="vit-row" data-vit="tokens"><b>tokens</b><span>image patches become a sequence</span></div>
        <div class="vit-row" data-vit="attention"><b>attention</b><span class="vit-matrix"><i class="vit-cell"></i><i class="vit-cell"></i><i class="vit-cell"></i><i class="vit-cell"></i><i class="vit-cell"></i><i class="vit-cell"></i><i class="vit-cell"></i><i class="vit-cell"></i><i class="vit-cell"></i><i class="vit-cell"></i><i class="vit-cell"></i><i class="vit-cell"></i><i class="vit-cell"></i><i class="vit-cell"></i><i class="vit-cell"></i><i class="vit-cell"></i></span></div>
        <div class="vit-row" data-vit="mix"><b>mix</b><span>heads route different evidence</span></div>
        <div class="vit-row" data-vit="stack"><b>stack</b><span>later layers carry more global context</span></div>
      </div>
    </div>
    <div class="course-rail" data-rail aria-hidden="true"><span class="course-node" data-node="0">
        <span class="course-dot" aria-hidden="true">1</span>
        <span class="course-node-label">query</span>
      </span><span class="course-node" data-node="1">
        <span class="course-dot" aria-hidden="true">2</span>
        <span class="course-node-label">keys</span>
      </span><span class="course-node" data-node="2">
        <span class="course-dot" aria-hidden="true">3</span>
        <span class="course-node-label">weights</span>
      </span><span class="course-node" data-node="3">
        <span class="course-dot" aria-hidden="true">4</span>
        <span class="course-node-label">mix</span>
      </span></div>
    <div class="course-grid"><div class="course-step" data-step="0">
        <span class="course-k">query</span>
        <span class="course-v">the current position asks what information it needs</span>
      </div><div class="course-step" data-step="1">
        <span class="course-k">keys</span>
        <span class="course-v">other positions advertise what they contain</span>
      </div><div class="course-step" data-step="2">
        <span class="course-k">weights</span>
        <span class="course-v">similarity decides how much each position matters</span>
      </div><div class="course-step" data-step="3">
        <span class="course-k">mix</span>
        <span class="course-v">values are combined into a context-aware representation</span>
      </div></div>
    <div class="course-status" data-status aria-live="polite"><b>query.</b> the current position asks what information it needs</div>
  </div>
  <figcaption id="cs231n-attention-routing-caption">Attention is a routing mechanism. It lets each token or patch pull the context it needs instead of compressing everything into one state.</figcaption>
</figure></div><span hidden=""></span></div><h2 id="queries-ask-keys-advertise-values-answer">Queries ask, keys advertise, values answer</h2><p>A query states what this step needs. Keys describe what each stored item holds, and values carry the information that comes back.</p><p>Scores compare the query to the keys. The score between a query and a key decides how much of that value enters the context. Each step weights the patches it needs and gives the rest little weight.</p><p>That makes attention a differentiable retrieval and routing mechanism. The operation is learned and soft, and it usually runs in parallel.</p><figure class="rm-article-image"><div class="relative overflow-hidden"><div class="bg-muted absolute inset-0 z-10 animate-pulse rounded-[inherit]"><div class="animate-shimmer via-paper-bright/70 absolute inset-0 bg-linear-to-r from-transparent to-transparent"></div></div></div><figcaption>The previous step scored the same regions differently, which is why the context changes with the query.</figcaption></figure><h2 id="vision-language-tasks-buy-grounding-from-it">Vision-language tasks buy grounding from it</h2><p>In image captioning, the decoder looks at the regions that matter as it produces each word. When it generates red car, it must look at the region holding the car, not the sky.</p><p>That is grounding, and visual question answering needs it too. The language side gets a mechanism that connects each text decision to visual evidence. The relations it routes over can be spatial, semantic, or cross-modal.</p><p>The model is also more flexible than one fixed image vector, and it is easier to inspect. You can read which region fed which word.</p><h2 id="attention-weights-are-evidence-about-routing-and-nothing-more">Attention weights are evidence about routing and nothing more</h2><p>Attention weights are useful evidence about routing. A map shows which region the model scored highly for a word. It does not show that the region caused the word.</p><p>The visualization note set the rule for probes. A probe earns its place when it produces the next test. An attention map lives under that rule, so read one map, then write down the question it raises.</p><div class="v-decision"><span class="kicker">What routing does not fix</span><div class="body">Routing decides which region enters the context. The quality of that region is a separate question. If a background cue predicts the word, attention can select that cue. The caption still comes out right, and the model stays brittle.</div></div><h2 id="attention-moved-the-bottleneck-from-memory-to-routing">Attention moved the bottleneck from memory to routing</h2><p>Self-attention lets tokens attend to other tokens in the same sequence. Transformers stack that operation with feed-forward layers, normalization, residual connections, and positional information.</p><p>The old bottleneck was memory compression. The new bottleneck is routing, compute, context length, and data. Those are the limits self-attention inherits when it becomes the backbone.</p><p>Architecture is a bet about what structure the data has. Attention bets that the structure worth having is which piece matters at this step.</p><h2 id="the-builder-test">The Builder Test</h2><p>Take one generated word from your captioning model. Print the top attention weights and the regions they select.</p><p>Then trace the whole path: query, keys, values, weights, and the evidence that came back. Ask whether that evidence supports the word the model produced or a different word nearby. If a nearby word fits the selected region better, you have found the next test to run.</p><h2 id="what-carries">What Carries</h2><p>Routing decides what the model looks at. Looking is still not understanding.</p><p>Routing is the operation that pays here. The next move is to make routing the whole backbone.</p><div class="rm-essay-close" aria-hidden="true"><svg width="76" height="32" viewBox="0 0 76 32" aria-hidden="true" class="rm-fmark-essay"><path d="M 2 14 Q 18 2 36 14 T 74 12" fill="none" stroke="currentColor" stroke-width="1.1" stroke-linecap="round" opacity="0.5"></path><line x1="2" y1="24" x2="60" y2="24" stroke="currentColor" stroke-width="1.1"></line><circle cx="68" cy="24" r="2.8" fill="var(--vermilion)"></circle></svg></div></div>]]></content:encoded>
    <pubDate>Tue, 23 Jun 2026 12:00:00 GMT</pubDate>
    <author>rajdeep@rajdeepmondal.com (Rajdeep Mondal)</author>
    <category>machine-learning</category><category>computer-vision</category><category>attention</category><category>sequence-modeling</category><category>transformers</category>
  </item>

  <item>
    <guid isPermaLink="true">https://rajdeepmondal.com/writing/computer-vision-rnns-remember-by-carrying-state</guid>
    <title>RNNs Remember by Carrying State</title>
    <link>https://rajdeepmondal.com/writing/computer-vision-rnns-remember-by-carrying-state</link>
    <description>An RNN carries one compressed summary forward, and that compression is both the mechanism and the ceiling. Gates buy the state a straighter path through time.</description>
    <content:encoded><![CDATA[<div class="essay-prose"><p>An RNN carries one compressed summary forward, and that compression is both the mechanism and the ceiling. The hidden state is the only thing that reaches the last step. Whatever the model knows about the past must fit inside it.</p><p>Images are spatial. Captions are sequential. A vision system that describes or acts over time needs memory. Recurrence is the old answer: read one token at a time and carry a state forward.</p><h2 id="the-hidden-state-is-a-notebook-rewritten-at-every-step">The hidden state is a notebook rewritten at every step</h2><p>At each step the model reads the current input and the previous hidden state, then writes a new one. That is the whole recurrence. The notebook is rewritten at every step and never extended.</p><p>A character-level model shows the shape at its smallest. The input is one character. The output is a distribution over the next character. Every earlier character reaches that distribution only through the state.</p><p>That summary is not a transcript. A long-range dependency that fails is information the representation did not keep.</p><h2 id="the-same-transition-weights-recur-at-every-step">The same transition weights recur at every step</h2><p>One transition function runs at every time step. The same weights read the first element of the sequence and the last.</p><p>Convolution reuses one filter across space. Recurrence reuses one transition across time. Both are bets about structure, and this bet is that the past compresses into a single state.</p><p>Sharing pays twice. It handles sequences of variable length, and it learns temporal patterns that repeat at any position. It also builds one long chain. A gradient that reaches the first step travels back through every step in between.</p><h2 id="backpropagation-through-time-makes-the-chain-explicit">Backpropagation through time makes the chain explicit</h2><p>To train the transition, unroll the network across time and run backprop on the unrolled graph. The same parameters appear at every step, so their gradients accumulate across the whole sequence.</p><figure class="rm-article-image"><div class="relative overflow-hidden"><div class="bg-muted absolute inset-0 z-10 animate-pulse rounded-[inherit]"><div class="animate-shimmer via-paper-bright/70 absolute inset-0 bg-linear-to-r from-transparent to-transparent"></div></div></div><figcaption>One state runs forward through shared weights, and the same path backward is where the gradient shrinks or grows.</figcaption></figure><p>That backward path is the chain rule the backprop note made concrete, now multiplied along time. Gradients vanish or explode as they pass through many repeated transitions.</p><p>The failure has one signature. The model keeps recent tokens and forgets older ones that still matter.</p><h2 id="gates-buy-a-stable-path-for-information-across-time">Gates buy a stable path for information across time</h2><p>An LSTM splits the state in two. A memory cell holds what the model keeps. The rest of the network sees only the exposed hidden state.</p><p>Gates control the write, forget, and read operations, and the model learns each gate. It decides what enters the cell and what decays inside it.</p><p>That is controlled memory, and it buys a more stable path for information and gradients across time. The model keeps important information longer and lets irrelevant noise decay.</p><p>Transformers displaced RNNs for many tasks, and the state lesson outlasted them.</p><h2 id="image-captioning-is-the-bridge-from-one-label-to-a-sequence">Image captioning is the bridge from one label to a sequence</h2><p>Image captioning starts with visual features from a CNN. A sequence model then produces words one step at a time.</p><p>The two parts have clean jobs. The encoder extracts visual evidence. The decoder turns that evidence and its own previous words into the next word.</p><p>The image features initialize or condition the decoder. The visual evidence enters once and must last to the final word.</p><p>That split is the frozen backbone with a new head from the transfer note, moved from space to time. Both reuse one visual representation for a new output.</p><h2 id="the-builder-test">The Builder Test</h2><p>Write down what the hidden state must still hold at the last step. Name a specific item.</p><p>Then count the steps between where that information enters and where the model uses it. That count is the length of the trip the state must survive.</p><p>Build the smallest version of that trip. Make a sequence whose answer depends on one early step, then read the output at the last step.</p><p>A model that holds on short sequences and fails on long ones has reported the answer. The state dropped the early item.</p><p>Check the objective too. A loss scored one step at a time can stay low while the whole caption is wrong.</p><p>A weak review says the model has memory. The strong review names which memory is too compressed to survive.</p><h2 id="what-carries">What Carries</h2><p>Whenever a model reads a sequence, name what it carries forward and what it drops before the decision. The dropped part is where the failure starts.</p><p>Gates lengthen the trip a state can survive. The bottleneck is still one state, and the next note removes it instead of enlarging it.</p><div class="rm-essay-close" aria-hidden="true"><svg width="76" height="32" viewBox="0 0 76 32" aria-hidden="true" class="rm-fmark-essay"><path d="M 2 14 Q 18 2 36 14 T 74 12" fill="none" stroke="currentColor" stroke-width="1.1" stroke-linecap="round" opacity="0.5"></path><line x1="2" y1="24" x2="60" y2="24" stroke="currentColor" stroke-width="1.1"></line><circle cx="68" cy="24" r="2.8" fill="var(--vermilion)"></circle></svg></div></div>]]></content:encoded>
    <pubDate>Sun, 21 Jun 2026 12:00:00 GMT</pubDate>
    <author>rajdeep@rajdeepmondal.com (Rajdeep Mondal)</author>
    <category>machine-learning</category><category>computer-vision</category><category>rnn</category><category>sequence-modeling</category><category>image-captioning</category><category>lstm</category>
  </item>

  <item>
    <guid isPermaLink="true">https://rajdeepmondal.com/writing/computer-vision-transfer-learning-is-buying-a-head-start</guid>
    <title>Transfer Learning Is Buying a Head Start</title>
    <link>https://rajdeepmondal.com/writing/computer-vision-transfer-learning-is-buying-a-head-start</link>
    <description>Two numbers set the plan: how much labeled data you have, and how far it sits from the pretraining set. Where you freeze and what you fine-tune follow.</description>
    <content:encoded><![CDATA[<div class="essay-prose"><p>Two numbers decide the transfer plan: the size of your labeled set, and its distance from the pretraining data. Everything else follows from those two, including where you freeze. The common failure is reaching for a pretrained backbone by reflex, without asking whether the borrowed features match the job.</p><h2 id="training-a-large-vision-model-from-scratch-is-usually-the-wrong-default">Training a large vision model from scratch is usually the wrong default</h2><p>Random weights hold no visual features, so scratch training pays for edges and textures you can borrow. Start from a model that already learned those features on a large dataset. You then have two moves: use it as a feature extractor, or fine-tune part of it for your task.</p><p>Both moves rest on the same bet, which is that the source task learned features your task can reuse. Early visual filters, textures, shapes, and object parts transfer even when the final labels change. You replace the classifier head because it was trained to name a different set of classes.</p><h2 id="data-size-and-domain-similarity-give-you-four-cases">Data size and domain similarity give you four cases</h2><p>Similarity here means how close your images are to what the pretrained model already saw. Small and similar is the easy corner. A frozen backbone and a new head are enough. A large similar dataset earns more room, so fine-tune deeper and trust the result more.</p><p>The dangerous corner is small and different. The borrowed features can be wrong for your images, and you do not have enough data to learn better ones. Large and different makes full fine-tuning plausible, and pretraining on your own domain becomes a real option.</p><figure class="rm-article-image"><div class="relative overflow-hidden"><div class="bg-muted absolute inset-0 z-10 animate-pulse rounded-[inherit]"><div class="animate-shimmer via-paper-bright/70 absolute inset-0 bg-linear-to-r from-transparent to-transparent"></div></div></div><figcaption>One backbone in four situations, where the freeze line and the learning rates move with data size and domain distance.</figcaption></figure><h2 id="feature-extraction-is-fast-and-has-a-ceiling">Feature extraction is fast and has a ceiling</h2><p>The pretrained network turns your images into embeddings, and a small classifier trains on top. This is fast and stable, and it overfits a small dataset less easily than a full fine-tune. The ceiling arrives when the frozen features miss something specific to your domain. The head cannot invent what the backbone never encoded.</p><p>A frozen backbone bets on coverage. The pressures the source images spanned, pose, lighting, scale, background, occlusion, must also cover yours. When they do not, the head reads a representation that already discarded the signal you need.</p><h2 id="fine-tuning-must-adapt-the-features-it-inherited">Fine-tuning must adapt the features it inherited</h2><p>Fine-tuning updates part or all of the pretrained network. The features you want are already in there, so every update is a disturbance you must keep small. Use a smaller learning rate for the pretrained layers and a larger one for the new head. A reckless learning rate erases the head start you paid for.</p><p>Unfreeze from the top down. Early layers hold generic edges and textures. Later layers hold dataset-specific semantics, which is where your task differs most. That order is a default, and error analysis overrides it.</p><h2 id="fast-improvement-is-exactly-when-leakage-hides">Fast improvement is exactly when leakage hides</h2><p>Transfer makes performance rise fast, and a fast rise covers a dirty split. Check that near-duplicate images do not cross train and validation before you trust the validation curve. The split is still the only instrument you have, and a borrowed backbone does not repair a broken one.</p><p>Check your preprocessing against what the pretrained model expects. That match is the same train-test path agreement the note on setup failures demanded. You now inherit that contract from a model you did not train.</p><p>One more leak sits upstream. The pretraining set can already contain your evaluation images, and no split of your own data will show it.</p><h2 id="the-builder-test">The Builder Test</h2><p>Freeze the backbone and train the head. Record the errors on the hard slices. Pull the confident mistakes first, which is the same inspection the visualization note used to catch a shortcut. Unfreeze deeper blocks only when that analysis shows the representation itself is the limit.</p><h2 id="what-carries">What Carries</h2><p>Transfer is a bet that the source features are reusable here, and the error slices are how you settle it. Transfer moves a representation between tasks. The next problem needs a representation that moves through time.</p><div class="rm-essay-close" aria-hidden="true"><svg width="76" height="32" viewBox="0 0 76 32" aria-hidden="true" class="rm-fmark-essay"><path d="M 2 14 Q 18 2 36 14 T 74 12" fill="none" stroke="currentColor" stroke-width="1.1" stroke-linecap="round" opacity="0.5"></path><line x1="2" y1="24" x2="60" y2="24" stroke="currentColor" stroke-width="1.1"></line><circle cx="68" cy="24" r="2.8" fill="var(--vermilion)"></circle></svg></div></div>]]></content:encoded>
    <pubDate>Fri, 19 Jun 2026 12:00:00 GMT</pubDate>
    <author>rajdeep@rajdeepmondal.com (Rajdeep Mondal)</author>
    <category>machine-learning</category><category>computer-vision</category><category>transfer-learning</category><category>fine-tuning</category><category>pretraining</category>
  </item>

  <item>
    <guid isPermaLink="true">https://rajdeepmondal.com/writing/computer-vision-visualizing-convnets-is-debugging-the-representation</guid>
    <title>Visualizing ConvNets Is Debugging the Representation</title>
    <link>https://rajdeepmondal.com/writing/computer-vision-visualizing-convnets-is-debugging-the-representation</link>
    <description>A visualization earns its place only when it changes a decision. Its job is to catch the model using the wrong evidence, and the test you run afterward is the only proof it produced anything.</description>
    <content:encoded><![CDATA[<div class="essay-prose"><p>A visualization earns its place only when it changes a decision. Collect different data, fix a shortcut, reject a deployment, add a test. A picture that moves none of them is decoration.</p><p>The useful job is narrow. A probe catches the model using the wrong evidence while you can still act on it.</p><h2 id="feature-space-neighbors-show-what-the-model-thinks-is-similar">Feature-space neighbors show what the model thinks is similar</h2><p>Pull the nearest neighbors of an image in raw pixel space and you retrieve similar color and similar background. Pull them in the feature space of a trained ConvNet and the neighbors share meaning instead. That difference is the finding. The network built a space where visual meaning is easier to compare.</p><p>Run the same retrieval across the training set and it pays a second time. Tight clusters that mix two labels mark unclear class boundaries. Exact repeats mark duplicates. An image sitting deep inside the wrong cluster is a candidate for a bad label. The case worth an hour is the neighbor that looks wrong to you and correct to the model.</p><h2 id="a-t-sne-map-generates-questions-and-proves-nothing">A t-SNE map generates questions and proves nothing</h2><p>t-SNE compresses high-dimensional features into a two-dimensional map, and similar images often cluster there. Use it for intuition and for error inspection. The clustering is not evidence of structure, because compressing to two dimensions discards what it cannot fit. A cluster that surprises you buys one thing, which is a question you can settle with examples.</p><h2 id="the-valuable-visualization-is-the-one-that-catches-the-shortcut">The valuable visualization is the one that catches the shortcut</h2><p>A bird classifier can learn sky. A medical model can learn scanner artifacts, and a product classifier can learn background color. All three stay accurate on the split you measured. They keep that accuracy until the deployment distribution changes, and users meet the failure before you do.</p><p>The classification note sorted misclassified images by confidence before trusting any number. The shortcut hunt uses the same sort. Pull the confident mistakes first, because a confident mistake is where a shortcut hides.</p><figure class="rm-article-image"><div class="relative overflow-hidden"><div class="bg-muted absolute inset-0 z-10 animate-pulse rounded-[inherit]"><div class="animate-shimmer via-paper-bright/70 absolute inset-0 bg-linear-to-r from-transparent to-transparent"></div></div></div><figcaption>Three probes over one confident mistake converge on a single shortcut hypothesis, which the next test can falsify.</figcaption></figure><h2 id="saliency-is-a-probe-and-every-probe-distorts">Saliency is a probe, and every probe distorts</h2><p>Saliency asks which pixels most affect the class score. Deconvolution-style methods and gradient visualizations try to show what a neuron or a layer responds to. Run either one over a ConvNet and you read the local shared filters the convolution note built.</p><p>The builder question stays concrete. The model uses the object, or it uses a shortcut: background, watermark, color cast, or border artifact.</p><p>Deconvnets, gradients, feature inversions, and t-SNE each distort the truth in their own way. None of them replaces a quantitative test or error analysis. The requirement is the same for all four. A probe earns its run only when it hands you a testable next question. That question points at a shortcut, a class confusion, or a feature dependence you can verify.</p><h2 id="confidence-is-confidence-under-the-learned-representation">Confidence is confidence under the learned representation</h2><p>A ConvNet can be confidently wrong on an image that looks meaningless to a human. The same model can flip on a perturbation a human barely notices. The representation it learned is not human perception.</p><p>A ConvNet stays useful under that limit. A confidence score is a statement about the model, and it holds only inside the representation the model learned. State the limit before anyone acts on the number.</p><h2 id="the-builder-test">The Builder Test</h2><p>Take the class your model confuses most often. Pull feature-space neighbors for its confident mistakes and look at what those neighbors share. Name that shortcut in one sentence. Then write the quantitative test that falsifies it, and run the test before you touch the model.</p><h2 id="what-carries">What Carries</h2><p>A probe leaves behind two things, a picture and a test. Keep the test and let the screenshot go. Once you can see what a representation learned, the practical question is whether you can borrow it.</p><div class="rm-essay-close" aria-hidden="true"><svg width="76" height="32" viewBox="0 0 76 32" aria-hidden="true" class="rm-fmark-essay"><path d="M 2 14 Q 18 2 36 14 T 74 12" fill="none" stroke="currentColor" stroke-width="1.1" stroke-linecap="round" opacity="0.5"></path><line x1="2" y1="24" x2="60" y2="24" stroke="currentColor" stroke-width="1.1"></line><circle cx="68" cy="24" r="2.8" fill="var(--vermilion)"></circle></svg></div></div>]]></content:encoded>
    <pubDate>Wed, 17 Jun 2026 12:00:00 GMT</pubDate>
    <author>rajdeep@rajdeepmondal.com (Rajdeep Mondal)</author>
    <category>machine-learning</category><category>computer-vision</category><category>visualization</category><category>interpretability</category><category>saliency</category><category>cnn</category>
  </item>

  <item>
    <guid isPermaLink="true">https://rajdeepmondal.com/writing/computer-vision-convolutional-networks-exploit-the-shape-of-images</guid>
    <title>Convolutional Networks Exploit the Shape of Images</title>
    <link>https://rajdeepmondal.com/writing/computer-vision-convolutional-networks-exploit-the-shape-of-images</link>
    <description>Convolution writes one claim about images into the architecture: useful patterns are local, and they repeat across space. The prior is the advantage, and a wrong prior is a cost already paid.</description>
    <content:encoded><![CDATA[<div class="essay-prose"><p>Convolution writes one claim about images into the architecture: useful patterns are local, and they repeat across space. That claim is the prior. The note on hidden layers called architecture a bet about the structure of the data. Convolution is that bet written into the wiring.</p><h2 id="a-convnet-refuses-to-flatten-the-image-for-as-long-as-it-can">A ConvNet refuses to flatten the image for as long as it can</h2><p>A fully connected network treats an image like a long spreadsheet row. Width, height, and channels collapse at the door. Flattening keeps every pixel value and loses the arrangement.</p><p>A ConvNet holds on to the grid until the classifier. Nearby pixels are related. The same edge detector is useful in many locations. Convolution puts both facts into the architecture rather than making the weights find them again.</p><p>Classic stacks repeat conv, nonlinearity, and pooling, then hand the result to a classifier. Later stacks changed the filters, the depth, and the connections. The constraint stayed the same. A network must keep spatial structure while it turns pixels into class evidence.</p><h2 id="a-local-receptive-field-is-the-whole-first-assumption">A local receptive field is the whole first assumption</h2><p>A convolutional neuron looks at one small region of the input. That region is its receptive field. A 3 by 3 filter asks a small question. A 5 by 5 filter asks a larger one and pays more parameters for the answer.</p><p>Early visual evidence is local. Edges, corners, colors, and textures do not need every pixel at once. Full connectivity in the first layer buys connections the image cannot justify.</p><p>Stack the layers and the region one unit sees grows with depth. The receptive field at the last conv layer is something you compute.</p><div class="uth-essay uth-figure-embed"><div><figure class="fig reveal" id="cs231n-convolution-bias">
  
  <div class="fig-head">
    <span class="fig-title">convolution shares one question across space</span>
    <span class="ctrl">
      
      
    </span>
  </div>
  <div class="stage" id="cs231n-convolution-bias-stage" role="group" aria-live="polite" aria-describedby="cs231n-convolution-bias-caption">
    <div class="course-visual layers-wrap">
      <div class="layers-stack">
        <div class="layers-layer" data-layer="input"><span class="layers-k">input</span><span class="layers-v">raw signal enters</span></div>
        <div class="layers-layer" data-layer="hidden"><span class="layers-k">hidden</span><span class="layers-v">nonlinear layers bend the space</span></div>
        <div class="layers-layer" data-layer="reuse"><span class="layers-k">reuse</span><span class="layers-v">features combine into parts</span></div>
        <div class="layers-layer" data-layer="score"><span class="layers-k">score</span><span class="layers-v">features become class scores</span></div>
      </div>
      <div class="layers-space"><svg viewBox="0 0 180 130" role="img" aria-label="layers bending a feature space"><circle class="layers-shape" cx="52" cy="44" r="22"/><rect class="layers-shape" x="98" y="25" width="42" height="42" rx="8"/><path class="layers-shape" d="M42 96 C74 72 104 116 146 84" fill="none" stroke-width="4"/></svg></div>
    </div>
    <div class="course-rail" data-rail aria-hidden="true"><span class="course-node" data-node="0">
        <span class="course-dot" aria-hidden="true">1</span>
        <span class="course-node-label">local</span>
      </span><span class="course-node" data-node="1">
        <span class="course-dot" aria-hidden="true">2</span>
        <span class="course-node-label">share</span>
      </span><span class="course-node" data-node="2">
        <span class="course-dot" aria-hidden="true">3</span>
        <span class="course-node-label">pool</span>
      </span><span class="course-node" data-node="3">
        <span class="course-dot" aria-hidden="true">4</span>
        <span class="course-node-label">stack</span>
      </span></div>
    <div class="course-grid"><div class="course-step" data-step="0">
        <span class="course-k">local</span>
        <span class="course-v">look at small neighborhoods instead of every pixel at once</span>
      </div><div class="course-step" data-step="1">
        <span class="course-k">share</span>
        <span class="course-v">use the same filter everywhere so patterns can move</span>
      </div><div class="course-step" data-step="2">
        <span class="course-k">pool</span>
        <span class="course-v">summarize nearby evidence to gain tolerance</span>
      </div><div class="course-step" data-step="3">
        <span class="course-k">stack</span>
        <span class="course-v">combine edges, textures, parts, and objects through depth</span>
      </div></div>
    <div class="course-status" data-status aria-live="polite"><b>local.</b> look at small neighborhoods instead of every pixel at once</div>
  </div>
  <figcaption id="cs231n-convolution-bias-caption">A ConvNet works because images have structure. Locality and weight sharing are the useful bias.</figcaption>
</figure></div><span hidden=""></span></div><h2 id="weight-sharing-is-where-the-efficiency-comes-from">Weight sharing is where the efficiency comes from</h2><p>The same filter slides across the image, so one learned detector runs at every position. A horizontal edge that matters in the top-left probably matters in the bottom-right, and one set of weights covers both.</p><p>The note on linear classifiers priced the other arrangement. One row of the weight matrix is one class template. That template has to average every pose, background, and lighting the class shows up in. Weight sharing is the opposite trade. One detector gets reused across many positions, so position stops eating capacity.</p><p>Without sharing, a first conv layer explodes into millions of parameters. With sharing, you learn one filter and apply it everywhere.</p><h2 id="the-output-size-formula-decides-whether-the-layers-fit">The output size formula decides whether the layers fit</h2><p>Stride sets how far the filter moves each step, and a larger stride shrinks the output. Padding adds border values so filters fit at the edges and the spatial size survives the layer.</p><p>With input width W, filter size F, padding P, and stride S, one line gives the output width.</p><div class="codeblock"><pre><code class="language-text">output_size = (W - F + 2 * P) / S + 1</code></pre></div><p>A non-integer result means the layer does not fit cleanly. The same arithmetic decides memory and how fast spatial information shrinks. Track it per layer or architecture design becomes guesswork.</p><p>The backpropagation note called the boring bugs shapes, caches, and missing averages. This is the same discipline moved up from one tensor to a whole stack. A layer that does not fit is a shape bug you can catch before any data moves.</p><figure class="rm-article-image"><div class="relative overflow-hidden"><div class="bg-muted absolute inset-0 z-10 animate-pulse rounded-[inherit]"><div class="animate-shimmer via-paper-bright/70 absolute inset-0 bg-linear-to-r from-transparent to-transparent"></div></div></div><figcaption>The same weights at every stop, an output size fixed by W, F, P. And S, and pooling drawn as the step that throws positions away.</figcaption></figure><h2 id="pooling-discards-detail-on-purpose-so-name-the-trade">Pooling discards detail on purpose, so name the trade</h2><p>Max pooling keeps the strongest activation in a region and drops the rest. It cuts compute and buys tolerance to small shifts.</p><p>The image classification note listed what a vision system must survive: pose, lighting, small translation, occlusion, and clutter. Convolution and pooling buy the small translation. Everything else on that list, plus class imbalance, still lands on the data and the loss. Locality says nothing about a cat photographed from above.</p><div class="v-decision"><span class="kicker">The pooling trade</span><div class="body">Every downsampling step throws information away. Take the step when shift tolerance and cheaper layers are worth more than the position you lose. A pooling layer copied from an older architecture is an inherited cost. Price it against the task you have.</div></div><h2 id="im2col-is-the-systems-compromise-that-made-convolution-fast">im2col is the systems compromise that made convolution fast</h2><p>A convolution is many dot products between filters and local image patches. im2col unfolds those patches into columns, reshapes the filters into rows, and turns the layer into one matrix multiply.</p><p>The cost is duplicated input values, because the same pixel lands in several columns. The return is that the implementation runs on optimized matrix multiply kernels. Budget the duplicate copies next to the activations, since both fall out of the same layer arithmetic. This compromise, more memory for a faster kernel, appears everywhere in deep learning.</p><h2 id="the-builder-test">The Builder Test</h2><p>Take the stack you plan to train and do the arithmetic by hand before you run it.</p><ul><li>Compute output width and height at every layer, and stop at the first non-integer result.</li><li>Say what one unit in the last conv layer sees in the original image.</li><li>Name what pooling discarded on the way there, and name the task that needs it back.</li></ul><p>A blank answer on the third line means the pooling layer arrived by habit. Change it or defend it before you spend a training run on it.</p><h2 id="what-carries">What Carries</h2><p>The prior is the advantage. A ConvNet starts with locality and reuse already assumed, and it spends its capacity on which filters are worth keeping.</p><p>A prior that does not match the data is a cost you already paid. Locality and reuse hold on natural images. On data whose useful pattern is global, or tied to one fixed position, the constraint returns nothing.</p><p>A matching prior still lets a network learn the wrong evidence. The next job is to look at what it learned, one image at a time.</p><div class="rm-essay-close" aria-hidden="true"><svg width="76" height="32" viewBox="0 0 76 32" aria-hidden="true" class="rm-fmark-essay"><path d="M 2 14 Q 18 2 36 14 T 74 12" fill="none" stroke="currentColor" stroke-width="1.1" stroke-linecap="round" opacity="0.5"></path><line x1="2" y1="24" x2="60" y2="24" stroke="currentColor" stroke-width="1.1"></line><circle cx="68" cy="24" r="2.8" fill="var(--vermilion)"></circle></svg></div></div>]]></content:encoded>
    <pubDate>Mon, 15 Jun 2026 12:00:00 GMT</pubDate>
    <author>rajdeep@rajdeepmondal.com (Rajdeep Mondal)</author>
    <category>machine-learning</category><category>computer-vision</category><category>convolutional-networks</category><category>cnn</category><category>pooling</category><category>architecture</category>
  </item>

  <item>
    <guid isPermaLink="true">https://rajdeepmondal.com/writing/computer-vision-a-minimal-neural-network-case-study</guid>
    <title>At Two Dimensions Nothing Hides</title>
    <link>https://rajdeepmondal.com/writing/computer-vision-a-minimal-neural-network-case-study</link>
    <description>A spiral in two dimensions is the cheapest place to watch a linear model fail and a hidden layer fix it, and at that size no part of the loop can hide.</description>
    <content:encoded><![CDATA[<div class="essay-prose"><p>A spiral in two dimensions is the cheapest place to watch a linear model fail and a hidden layer fix it. The data has two coordinates, so the decision boundary is a line you can draw and look at. The failure arrives as a shape instead of a cause you inferred.</p><h2 id="the-spiral-defeats-a-straight-boundary-no-matter-how-you-tune-it">The spiral defeats a straight boundary no matter how you tune it</h2><p>The linear classifier maps each point to class scores and trains with softmax loss plus regularization. It draws straight boundaries and nothing else. The spiral classes wrap around one another, so no straight boundary separates them.</p><p>That failure is the reason to run the linear model first. A straight boundary losing to a curve names its own cause, so you stop looking at the learning rate and the data loader. The linear-classifier note priced this limit as one averaged template per class. In two dimensions the same limit is a straight line, and the spiral is built to beat it.</p><h2 id="one-hidden-layer-carves-the-space-into-regions">One hidden layer carves the space into regions</h2><p>A hidden layer transforms the input before classification. ReLU units carve the space into regions, and the final linear classifier works on that learned feature space rather than the raw two coordinates. The visible result is a curved, piecewise boundary.</p><p>The boundary bends because the network learned a feature transformation. The model finds an intermediate space where the final decision is easier. At this size you watch that happen rather than accept it on report.</p><div class="uth-essay uth-figure-embed"><div><figure class="fig reveal" id="cs231n-case-boundary">
  
  <div class="fig-head">
    <span class="fig-title">one hidden layer bends the boundary</span>
    <span class="ctrl">
      
      
    </span>
  </div>
  <div class="stage" id="cs231n-case-boundary-stage" role="group" aria-live="polite" aria-describedby="cs231n-case-boundary-caption">
    <div class="course-visual db-wrap">
      <div class="db-plot">
        <span class="db-region"></span>
        <span class="db-dot a d1"></span><span class="db-dot b d2"></span><span class="db-dot a d3"></span><span class="db-dot b d4"></span><span class="db-dot a d5"></span><span class="db-dot b d6"></span>
        <span class="db-line"></span>
        <svg class="db-bend" viewBox="0 0 320 210" role="img" aria-label="curved decision boundary separating two classes" preserveAspectRatio="none"><path d="M18 156 C74 84 110 180 158 106 S246 42 302 78" fill="none" stroke="var(--accent)" stroke-width="3" stroke-linecap="round"/></svg>
      </div>
      <div class="db-panel">
        <div class="db-row" data-db="plot"><b>plot</b>small data makes the mistake visible</div>
        <div class="db-row" data-db="linear"><b>linear</b>a straight separator misses curved structure</div>
        <div class="db-row" data-db="hidden"><b>hidden</b>ReLUs carve the space into bendable pieces</div>
        <div class="db-row" data-db="train"><b>train</b>loss and gradients move the pieces into place</div>
      </div>
    </div>
    <div class="course-rail" data-rail aria-hidden="true"><span class="course-node" data-node="0">
        <span class="course-dot" aria-hidden="true">1</span>
        <span class="course-node-label">plot</span>
      </span><span class="course-node" data-node="1">
        <span class="course-dot" aria-hidden="true">2</span>
        <span class="course-node-label">linear</span>
      </span><span class="course-node" data-node="2">
        <span class="course-dot" aria-hidden="true">3</span>
        <span class="course-node-label">hidden</span>
      </span><span class="course-node" data-node="3">
        <span class="course-dot" aria-hidden="true">4</span>
        <span class="course-node-label">train</span>
      </span></div>
    <div class="course-grid"><div class="course-step" data-step="0">
        <span class="course-k">plot</span>
        <span class="course-v">use a small 2D dataset so the failure is visible</span>
      </div><div class="course-step" data-step="1">
        <span class="course-k">linear</span>
        <span class="course-v">draw straight boundaries and watch where they break</span>
      </div><div class="course-step" data-step="2">
        <span class="course-k">hidden</span>
        <span class="course-v">add ReLU units to carve piecewise regions</span>
      </div><div class="course-step" data-step="3">
        <span class="course-k">train</span>
        <span class="course-v">run the full loop: loss, gradient, update, validation</span>
      </div></div>
    <div class="course-status" data-status aria-live="polite"><b>plot.</b> use a small 2D dataset so the failure is visible</div>
  </div>
  <figcaption id="cs231n-case-boundary-caption">The tiny case study is a microscope. It lets you see representation learning instead of only reading the final accuracy.</figcaption>
</figure></div><span hidden=""></span></div><h2 id="the-toy-contains-the-entire-loop">The toy contains the entire loop</h2><p>Four ideas from earlier notes run at once in this toy. The score function turns points into class scores and the softmax loss says how strongly the model is wrong. Backpropagation delivers the blame to every parameter, and gradient descent spends it on an update.</p><ul><li>Initialize the weights with small random values so units learn different features.</li><li>Run a forward pass to compute class scores.</li><li>Compute the softmax loss and add regularization.</li><li>Backpropagate the gradients through the score function.</li><li>Update the weights with gradient descent.</li><li>Monitor training and validation behavior, and trust no single number.</li></ul><p>Once that loop is real in two dimensions, the larger image models stop being mysterious. They run bigger, more structured versions of the same loop.</p><h2 id="regularization-moves-the-boundary-where-you-can-see-it-move">Regularization moves the boundary where you can see it move</h2><p>Turn the penalty down and the hidden-layer model overfits the toy data. A larger hidden size makes the boundary more flexible, and too little regularization makes it jagged around individual points. Capacity and penalty stop being quantities you infer and become a shape on the screen.</p><p>The training note makes the tiny-batch overfit test the first gate before scale. This is the same gate with the answer drawn. A hidden layer that cannot overfit a spiral with the penalty turned down has a broken wire. You find it before a real dataset is involved.</p><figure class="rm-article-image"><div class="relative overflow-hidden"><div class="bg-muted absolute inset-0 z-10 animate-pulse rounded-[inherit]"><div class="animate-shimmer via-paper-bright/70 absolute inset-0 bg-linear-to-r from-transparent to-transparent"></div></div></div><figcaption>The boundary shape is the readout for capacity, which turns a sweep over hidden size and regularization into something you look at.</figcaption></figure><h2 id="a-small-model-you-cannot-explain-is-borrowed-confidence">A small model you cannot explain is borrowed confidence</h2><p>Every part is visible at this size: data, scores, loss, gradients, updates, and boundary. The spiral is unrealistic on purpose, because removing scale is what lets you inspect the mechanism.</p><p>Large models give confusion room to hide behind parameters. A confusion that survives the spiral only becomes more expensive on the next run. Explain the small case before you buy the large one.</p><h2 id="the-builder-test">The Builder Test</h2><p>Run the linear model on the spiral first and confirm the limit yourself. Add one hidden layer and watch the boundary bend. Then sweep hidden size and regularization, one knob per run, and predict the boundary shape before each run finishes. Check the gradients against numerical tests on a tiny input before you trust the sweep.</p><h2 id="what-carries">What Carries</h2><p>Use toy problems to see the mechanism and real data to see the failure modes. Two dimensions hand you the whole loop and remove one thing. A point on the spiral has two coordinates and no neighbors, so the model has no spatial structure to exploit. Pixels have neighbors, and the next architecture takes that structure for free rather than making the model learn it.</p><div class="rm-essay-close" aria-hidden="true"><svg width="76" height="32" viewBox="0 0 76 32" aria-hidden="true" class="rm-fmark-essay"><path d="M 2 14 Q 18 2 36 14 T 74 12" fill="none" stroke="currentColor" stroke-width="1.1" stroke-linecap="round" opacity="0.5"></path><line x1="2" y1="24" x2="60" y2="24" stroke="currentColor" stroke-width="1.1"></line><circle cx="68" cy="24" r="2.8" fill="var(--vermilion)"></circle></svg></div></div>]]></content:encoded>
    <pubDate>Sat, 13 Jun 2026 12:00:00 GMT</pubDate>
    <author>rajdeep@rajdeepmondal.com (Rajdeep Mondal)</author>
    <category>machine-learning</category><category>computer-vision</category><category>case-study</category><category>toy-model</category><category>softmax</category>
  </item>

  <item>
    <guid isPermaLink="true">https://rajdeepmondal.com/writing/computer-vision-training-neural-nets-without-lying-to-yourself</guid>
    <title>Training Neural Nets Without Lying to Yourself</title>
    <link>https://rajdeepmondal.com/writing/computer-vision-training-neural-nets-without-lying-to-yourself</link>
    <description>Training is an inspection loop. A network that cannot memorize ten examples has not earned a larger dataset, and no optimizer repairs bad labels or leakage.</description>
    <content:encoded><![CDATA[<div class="essay-prose"><p>Training is an inspection loop. A network that cannot memorize ten examples has not earned a larger dataset.</p><p>The loop is cheap and the run is not. You examine gradients, overfit a tiny batch, and compare train behavior with validation behavior. Each reading decides whether the evidence supports the next move.</p><h2 id="overfit-ten-examples-before-you-touch-a-real-dataset">Overfit ten examples before you touch a real dataset</h2><p>Take ten labeled examples and train until the model reproduces every one of them. A working pipeline memorizes ten examples without effort. If it cannot, the pipeline is broken.</p><p>The usual causes are wrong labels, gradients that never arrive, and regularization set too strong. A loss wired to the wrong tensor does the same, and so does broken data loading. Each survives a full run, which is the expensive way to find it.</p><p>The tiny-batch test is the gradient check from the backprop note, moved one level up the stack. That check tested one gate at a time. This one tests the whole pipeline against a task that is too easy to fail. Two more checks cost minutes: examine the augmentation, and make sure the metric means what you think it means.</p><figure class="rm-article-image"><div class="relative overflow-hidden"><div class="bg-muted absolute inset-0 z-10 animate-pulse rounded-[inherit]"><div class="animate-shimmer via-paper-bright/70 absolute inset-0 bg-linear-to-r from-transparent to-transparent"></div></div></div><figcaption>A gate that fails returns you to a fix, and only a run that clears all four earns more data.</figcaption></figure><h2 id="momentum-and-adaptive-methods-change-how-the-update-moves">Momentum and adaptive methods change how the update moves</h2><p>Plain SGD zigzags through ravines. Momentum keeps a velocity vector that smooths the path and carries the update through consistent downhill directions. Nesterov momentum peeks ahead before it computes the gradient. RMSProp and Adam scale updates by recent gradient statistics.</p><p>Every one of these changes the shape of the curve the optimization note taught you to read. The data underneath stays exactly as wrong as it was, so validation still decides.</p><h2 id="an-optimizer-cannot-fix-bad-labels-broken-preprocessing-or-leakage">An optimizer cannot fix bad labels, broken preprocessing, or leakage</h2><p>Adam, RMSProp, momentum, and learning-rate schedules make training smoother. They leave bad labels, broken preprocessing, impossible tasks, and leakage where they were. The setup note made preprocessing part of the model contract, and no optimizer renegotiates that contract.</p><p>When one optimizer is the only thing that makes a configuration work, inspect the configuration instead of tuning it. Treat the rescue as a symptom and go find what it covered.</p><h2 id="hyperparameter-search-needs-a-hypothesis-a-budget-and-a-record">Hyperparameter search needs a hypothesis, a budget, and a record</h2><p>Search learning rate and regularization on log scales, because useful values differ by orders of magnitude. Run coarse sweeps first and keep them short. Refine only inside a region that already looks good.</p><p>The validation set that ranks those sweeps is the same split the classification note sealed. Every sweep spends a little of it, so keep the count. Record the settings you tried, what stayed fixed, and which images failed.</p><p>A sweep that runs until a number improves has produced a number and nothing else. Write the hypothesis before the run starts, and the result answers a question you can name.</p><h2 id="ensembles-buy-accuracy-with-complexity-you-must-pay-to-serve">Ensembles buy accuracy with complexity you must pay to serve</h2><p>Different models make different mistakes, and that disagreement is where the accuracy comes from. You pay for it in training time, serving cost, and how hard the system is to explain. Take the trade when the gain is worth the cost, and name the cost first.</p><h2 id="change-one-knob-at-a-time-or-the-run-teaches-nothing">Change one knob at a time or the run teaches nothing</h2><p>Two changes in one run produce one number and no way to attribute it. The run after that inherits the confusion and costs more to interpret.</p><p>A run with a broken validation split does not deserve a leaderboard. Scaling a confused experiment makes the confusion expensive, because longer runs hide wiring mistakes behind noise you paid for.</p><p>A new architecture does not rescue a broken training loop. Run the boring checks first, and the architecture becomes testable.</p><h2 id="the-builder-test">The Builder Test</h2><p>Babysit the first run through four readings, in order.</p><ul><li>The first loss value, which rules out a loss wired to the wrong tensor.</li><li>The tiny-batch overfit, which rules out the rest of the wiring.</li><li>The gradient scale, which rules out dead gradients and an unstable initialization.</li><li>The train-validation gap, which separates a model that learned structure from one that memorized.</li></ul><p>A failed reading ends the run there. Fix that one thing, restart the four, and carry exactly one knob change into the next full run.</p><h2 id="what-carries">What Carries</h2><p>The small run is the cheapest teacher you have. A big run teaches the same lesson slowly, and you pay for the delay in compute.</p><p>So shrink the next experiment until every part of it is visible. The smallest honest experiment has two dimensions, where nothing hides behind size.</p><div class="rm-essay-close" aria-hidden="true"><svg width="76" height="32" viewBox="0 0 76 32" aria-hidden="true" class="rm-fmark-essay"><path d="M 2 14 Q 18 2 36 14 T 74 12" fill="none" stroke="currentColor" stroke-width="1.1" stroke-linecap="round" opacity="0.5"></path><line x1="2" y1="24" x2="60" y2="24" stroke="currentColor" stroke-width="1.1"></line><circle cx="68" cy="24" r="2.8" fill="var(--vermilion)"></circle></svg></div></div>]]></content:encoded>
    <pubDate>Thu, 11 Jun 2026 12:00:00 GMT</pubDate>
    <author>rajdeep@rajdeepmondal.com (Rajdeep Mondal)</author>
    <category>machine-learning</category><category>computer-vision</category><category>training</category><category>hyperparameters</category><category>optimizers</category><category>evaluation</category>
  </item>

  <item>
    <guid isPermaLink="true">https://rajdeepmondal.com/writing/computer-vision-data-initialization-normalization-and-regularization</guid>
    <title>Most Training Failures Are Setup Failures</title>
    <link>https://rajdeepmondal.com/writing/computer-vision-data-initialization-normalization-and-regularization</link>
    <description>Most failures that look like model failures are setup failures. Data scale, initialization, normalization, regularization, and the loss must agree with each other.</description>
    <content:encoded><![CDATA[<div class="essay-prose"><p>Most training failures are setup failures. Bad input scale, bad initialization, missing normalization, careless regularization, and a mismatched loss make a good architecture look broken. A mismatch among those five wastes days that look like architecture problems.</p><h2 id="preprocessing-removes-friction-that-has-nothing-to-do-with-the-model">Preprocessing removes friction that has nothing to do with the model</h2><p>Mean subtraction centers the data. Scaling keeps feature ranges comparable. For images that usually means subtracting the training-set mean image, or the per-channel means.</p><p>Optimization works better when features sit on comparable scales and activations stop getting pushed into bad regions. Preprocessing removes variation the label never depended on, and it belongs before every other decision in the setup.</p><h2 id="initialization-gives-learning-a-fair-start-and-nothing-more">Initialization gives learning a fair start and nothing more</h2><p>Weights too small and the signal vanishes. Weights too large and the activations or the gradients explode. Symmetric initialization makes every neuron in a layer learn the same thing. Random initialization breaks that symmetry, and it works only when the scale matches the layer.</p><p>Read the activation scale after the first forward pass. A collapse toward zero or a blowup names the initialization scale, before you touch the architecture.</p><h2 id="batch-normalization-and-dropout-change-the-function-you-evaluate">Batch normalization and dropout change the function you evaluate</h2><figure class="rm-article-image"><div class="relative overflow-hidden"><div class="bg-muted absolute inset-0 z-10 animate-pulse rounded-[inherit]"><div class="animate-shimmer via-paper-bright/70 absolute inset-0 bg-linear-to-r from-transparent to-transparent"></div></div></div><figcaption>Only the dropout masks and the batch statistics are allowed to differ between the two paths.</figcaption></figure><p>Batch normalization normalizes intermediate activations and learns a scale and a shift. It makes the network less sensitive to initialization and it can allow higher learning rates. It also changes the training system.</p><p>The layer uses batch statistics during training and stored running statistics during evaluation. You must track those running statistics and know what the layer does in each mode. Do not paste it everywhere.</p><p>Dropout disables units at random during training. The network cannot lean on one exact path, so it learns distributed representations that tend to generalize better. At test time dropout uses the full network with the correct scaling convention.</p><p>Forget the train() and eval() switch and you change the function you measure. The curve still moves and the number still prints, and both describe a model you are not shipping. I check the mode switch before I read any validation number.</p><h2 id="train-time-and-test-time-paths-must-agree-except-where-you-meant-them-to-differ">Train-time and test-time paths must agree except where you meant them to differ</h2><p>Preprocessing is part of the model contract. Subtract the training mean during training and you must subtract the same mean at test time. When the two input pipelines diverge, you evaluate the model in a different world than the one it learned.</p><p>The classification note put the trust boundary at the split. Leakage sends training information across that line. A mismatched pipeline sends the model across it. Both break the one instrument that reports the truth.</p><p>The only deliberate mismatch is training-only randomness such as augmentation or dropout.</p><h2 id="regularization-is-a-budget-on-how-much-you-trust-the-data">Regularization is a budget on how much you trust the data</h2><p>Every training setup decides how much to trust the data, the model, and the objective. Regularization is the explicit budget on that trust. It says how much freedom the model gets to fit the training data.</p><ul><li>L2 penalizes large weights, so the decision stops resting on a few fragile ones.</li><li>Dropout removes units during training, so no single narrow path through the network carries the answer.</li><li>Augmentation broadens the examples, so accidental correlations stop holding across the training set.</li><li>Early stopping cuts the run before memorization dominates.</li></ul><p>Each one answers the same question. How do we stop the model before it turns accidents into rules?</p><p>Training loss going down is a fact about the training set. The train-validation gap is the number to read. When training keeps improving and validation gets worse, the model is memorizing details that do not travel. When validation improves as training becomes a little harder, regularization is doing its job.</p><h2 id="the-loss-is-the-game-the-model-plays">The loss is the game the model plays</h2><p>The loss must match the task. Classification usually uses softmax cross-entropy or an SVM-style loss. Multilabel tasks need different treatment, and regression needs different objectives again.</p><p>Softmax cross-entropy and the SVM loss are the two the linear-classifier note weighed. Pick one and keep one name for it through the unit. A model tuned against a loss that states the wrong task moves numbers without moving the result.</p><p>The loss defines what counts as wrong, and every other control is tuned against that definition. Together they decide whether training learns structure or exploits convenience.</p><h2 id="the-builder-test">The Builder Test</h2><p>Take a run you already have and plot the train and validation curves on the same axes. Then change exactly one thing: the preprocessing, the initialization scale, the regularizer, or the loss. Run it again and compare the gap.</p><p>An intervention that shrinks the gap while validation keeps improving is the one to keep. If the gap shrinks because both curves flattened, the change destroyed useful signal. Change one thing per run. Two changes and you cannot say which one moved the gap.</p><h2 id="what-carries">What Carries</h2><p>Carry the gap. Generalization lives in the space between the examples you fit and the pattern you learn.</p><p>With the setup honest, the remaining question is how to run the training loop without lying to yourself. Keep the two curves on the screen while you answer it.</p><div class="rm-essay-close" aria-hidden="true"><svg width="76" height="32" viewBox="0 0 76 32" aria-hidden="true" class="rm-fmark-essay"><path d="M 2 14 Q 18 2 36 14 T 74 12" fill="none" stroke="currentColor" stroke-width="1.1" stroke-linecap="round" opacity="0.5"></path><line x1="2" y1="24" x2="60" y2="24" stroke="currentColor" stroke-width="1.1"></line><circle cx="68" cy="24" r="2.8" fill="var(--vermilion)"></circle></svg></div></div>]]></content:encoded>
    <pubDate>Tue, 09 Jun 2026 12:00:00 GMT</pubDate>
    <author>rajdeep@rajdeepmondal.com (Rajdeep Mondal)</author>
    <category>machine-learning</category><category>computer-vision</category><category>preprocessing</category><category>normalization</category><category>dropout</category><category>batch-normalization</category>
  </item>

  <item>
    <guid isPermaLink="true">https://rajdeepmondal.com/writing/computer-vision-neural-networks-are-learned-feature-machines</guid>
    <title>Neural Networks Are Learned Feature Machines</title>
    <link>https://rajdeepmondal.com/writing/computer-vision-neural-networks-are-learned-feature-machines</link>
    <description>A hidden layer is a learned change of coordinates, and its only defense is that it makes the final comparison easier. Remove the nonlinearity and the stack collapses into one linear map.</description>
    <content:encoded><![CDATA[<div class="essay-prose"><p>A hidden layer is a learned change of coordinates. Its only defense is that it makes the final comparison easier.</p><p>The linear classifier note ended on one averaged template per class. That template holds every pose and every background of a class in one picture, so it averages them. A hidden layer removes that specific limit.</p><p>It rewrites the input into coordinates where a straight boundary can do the work. The overview asked which representation survives an image nobody arranged for the model. This is the first stage where the model chooses that representation instead of receiving it.</p><h2 id="remove-the-nonlinearity-and-the-stack-collapses-into-one-linear-map">Remove the nonlinearity and the stack collapses into one linear map</h2><p>A neuron takes its inputs, computes a weighted sum, adds a bias, and sends the result through a nonlinearity. Take the nonlinearity out and two stacked layers multiply into one matrix. The depth stays on the page, and the model it defines is the template you already had.</p><p>That nonlinearity is the activation, and choosing it is a structural decision. ReLU, sigmoid, tanh, and their variants set how information and gradients move through the stack.</p><figure class="rm-article-image"><div class="relative overflow-hidden"><div class="bg-muted absolute inset-0 z-10 animate-pulse rounded-[inherit]"><div class="animate-shimmer via-paper-bright/70 absolute inset-0 bg-linear-to-r from-transparent to-transparent"></div></div></div><figcaption>The nonlinearity is the whole reason the second layer has anything new to work with.</figcaption></figure><h2 id="depth-is-worth-paying-for-when-later-layers-reuse-earlier-features">Depth is worth paying for when later layers reuse earlier features</h2><p>Early layers pick up simple patterns. Later layers combine them into edges, textures, parts, and structure that belongs to a class. Each stage buys its inputs from the stage below, and only the first one reads pixels.</p><p>One averaged template cannot do this. It smears every mode of a class into one average. A stack keeps the modes apart, because a part detector can fire on two images that do not look alike.</p><p>Depth pays out only when the optimizer can train it and the data can support it. Otherwise the extra layers become capacity for memorization, instability, or shortcuts.</p><h2 id="activation-choice-is-a-decision-about-gradient-flow">Activation choice is a decision about gradient flow</h2><p>Sigmoid and tanh saturate. Once a unit sits in the flat region, the gradient it passes down is small. The layers under it learn slowly. ReLU is simple and often trains better, and it fails in its own way.</p><p>A unit that parks on the negative side passes no gradient, and it stays parked. That unit is finished learning for the rest of the run.</p><h2 id="universal-approximation-does-not-promise-that-sgd-finds-the-function">Universal approximation does not promise that SGD finds the function</h2><p>Depth, width, and activation each set capacity, optimization difficulty, memory, speed, and overfitting risk. Universal approximation is a statement about capacity. It says a network can represent a wide family of functions.</p><p>It says nothing about whether SGD finds that function, whether the result generalizes, or what the training costs. Representable and trainable are separate properties, and generalizable is a third.</p><div class="v-decision"><span class="kicker">Capacity</span><div class="body">Too little capacity and the model underfits. Too much capacity with weak data or weak regularization and the model memorizes shortcuts. Read the gap between training loss and validation loss, the curve the optimization note used to name a broken run. Capacity, optimization, and regularization move together, so decide them together.</div></div><p>The useful question is whether this is the right amount of power for this evidence.</p><h2 id="the-builder-test">The Builder Test</h2><p>Ask what changed between input space and hidden space. Take the examples the model gets wrong and find where they sit in the hidden representation. If they are still mixed in with the examples it gets right, the layer has not started doing work.</p><p>Then check which failures still look linear. Those are the cases the new coordinates left where they were.</p><p>The common failure is counting layers without asking what each one makes easier. When the data, the loss, or the optimization is wrong, more layers make the bug harder to see.</p><h2 id="what-carries">What Carries</h2><p>Architecture is a bet about what structure the data has. A hidden layer is the smallest version of that bet. You claim that useful intermediate features exist in this data, and the training run settles it.</p><p>The bet raises the price of every setup choice. When the representation itself is learned, a bad setup looks exactly like a bad model. Audit the setup before you add another layer.</p><div class="rm-essay-close" aria-hidden="true"><svg width="76" height="32" viewBox="0 0 76 32" aria-hidden="true" class="rm-fmark-essay"><path d="M 2 14 Q 18 2 36 14 T 74 12" fill="none" stroke="currentColor" stroke-width="1.1" stroke-linecap="round" opacity="0.5"></path><line x1="2" y1="24" x2="60" y2="24" stroke="currentColor" stroke-width="1.1"></line><circle cx="68" cy="24" r="2.8" fill="var(--vermilion)"></circle></svg></div></div>]]></content:encoded>
    <pubDate>Sun, 07 Jun 2026 12:00:00 GMT</pubDate>
    <author>rajdeep@rajdeepmondal.com (Rajdeep Mondal)</author>
    <category>machine-learning</category><category>computer-vision</category><category>neural-networks</category><category>activations</category><category>architecture</category>
  </item>

  <item>
    <guid isPermaLink="true">https://rajdeepmondal.com/writing/computer-vision-backpropagation-is-blame-accounting</guid>
    <title>Backpropagation Is Blame Accounting</title>
    <link>https://rajdeepmondal.com/writing/computer-vision-backpropagation-is-blame-accounting</link>
    <description>Backpropagation is the chain rule kept as bookkeeping. Almost every bug in it is a wrong shape, a stale cache, or a missing batch average.</description>
    <content:encoded><![CDATA[<div class="essay-prose"><p>Backpropagation is the chain rule kept as bookkeeping. The forward pass computes values. The backward pass computes blame. Almost every bug in it is a wrong shape, a stale cache, or a missing batch average.</p><p>The calculus is one line. The work is keeping shapes, cached values, and gradient routes correct across many gates. Most implementation bugs are bookkeeping errors that still cost you a training run.</p><h2 id="every-gate-keeps-a-two-line-contract">Every gate keeps a two-line contract</h2><p>A network is a graph of small operations: add, multiply, max, matrix multiply, and a nonlinear activation. Each one stores the value it produced and owes its inputs a derivative on the way back. Those two facts are the whole contract.</p><p>The upstream gradient says how much the final loss moves when the output of this gate moves. The local gradient says how much that output moves when one input moves. Multiply them and the input has its share.</p><div class="codeblock"><pre><code class="language-python">upstream = dL_dout
local = dout_dx
dL_dx = upstream * local</code></pre></div><p>An operation sees its own local derivative and the signal arriving from the operation after it. Nothing else about the network reaches it. Chain those backward and every parameter gets a gradient.</p><h2 id="the-forward-pass-buys-memory-so-the-backward-pass-can-exist">The forward pass buys memory so the backward pass can exist</h2><p>The backward pass runs on values the forward pass already computed. A multiply gate needs its original inputs. A ReLU needs the side of zero it landed on, and a softmax loss needs the probabilities it produced.</p><p>This is why a neural net library stores activations during the forward pass. The memory is deliberate. It buys the ability to compute gradients later.</p><h2 id="gradient-flow-has-four-patterns">Gradient flow has four patterns</h2><p>Four patterns cover most of the gates you derive by hand. Each pattern comes with the value the gate must cache to run it.</p><figure class="rm-article-image"><div class="relative overflow-hidden"><div class="bg-muted absolute inset-0 z-10 animate-pulse rounded-[inherit]"><div class="animate-shimmer via-paper-bright/70 absolute inset-0 bg-linear-to-r from-transparent to-transparent"></div></div></div><figcaption>The same upstream gradient leaves each gate by a different route, and the cached forward value decides which.</figcaption></figure><ul><li>Add distributes the upstream gradient to both inputs, and caches nothing.</li><li>Multiply sends each input the upstream gradient times the other input, so it caches both.</li><li>Max routes the whole gradient through the winner, so it caches which input won.</li><li>ReLU passes gradient for positive activations and blocks it for negatives, so it caches the sign.</li></ul><p>Once you can name the pattern at each gate, the backward pass is local rules chained in reverse. The forward pass decides the routing, and the cache is where it wrote that decision down.</p><h2 id="local-derivatives-are-what-make-autodiff-modular">Local derivatives are what make autodiff modular</h2><p>Give every operation one interface. The forward returns an output and stores what it needs. The backward receives the upstream gradient and returns gradients for its inputs.</p><p>Matrix multiply, ReLU, a normalization layer, and a loss all participate on that interface. Each one has to know only its local derivative. A new operation joins an autodiff system without that system knowing anything else about your model.</p><p>The library handles gradients by running this interface for you. You still have to know what it saves, routes, multiplies, and sums.</p><h2 id="the-bugs-are-boring-which-is-why-they-survive">The bugs are boring, which is why they survive</h2><p>Backprop bugs arrive as a wrong transpose, an omitted batch average, or a forgotten cache. They also arrive as a gradient with the wrong shape, or a regularization term applied to the bias. A wrong broadcast still produces numbers. The numbers land on the wrong weight.</p><p>Shape discipline keeps blame arriving at the right parameter. Assert the shape of every gradient against the tensor it corrects. The assertion fails where the bug is.</p><p>A gradient that is absent, too large, or exactly zero has a local cause. Look at the operation, the shape, the activation, or the loss connection.</p><p>Run a numerical gradient check on a tiny case before you trust a large training run. The optimization note ran that check on the whole loss. Here it points at one gate.</p><h2 id="the-builder-test">The Builder Test</h2><p>Trace one scalar by hand. Write the forward values and the loss, then walk backward one operation at a time. Shrink the graph until you can inspect every local derivative by hand.</p><p>The trace itself is simple. The hard part is doing it the same way through many gates and keeping the thread. If the manual trace disagrees with the code, trust the trace until you find the bug.</p><h2 id="what-carries">What Carries</h2><p>Learning is prediction, mistake, blame, correction. Backprop is the blame made precise.</p><p>The loss decides the goal, and you chose the loss back in the linear classifier note. Backprop carries the consequence of that goal backward through the network. With blame delivered to every parameter, the open question is what the middle parameters are supposed to represent.</p><div class="rm-essay-close" aria-hidden="true"><svg width="76" height="32" viewBox="0 0 76 32" aria-hidden="true" class="rm-fmark-essay"><path d="M 2 14 Q 18 2 36 14 T 74 12" fill="none" stroke="currentColor" stroke-width="1.1" stroke-linecap="round" opacity="0.5"></path><line x1="2" y1="24" x2="60" y2="24" stroke="currentColor" stroke-width="1.1"></line><circle cx="68" cy="24" r="2.8" fill="var(--vermilion)"></circle></svg></div></div>]]></content:encoded>
    <pubDate>Fri, 05 Jun 2026 12:00:00 GMT</pubDate>
    <author>rajdeep@rajdeepmondal.com (Rajdeep Mondal)</author>
    <category>machine-learning</category><category>computer-vision</category><category>backpropagation</category><category>gradients</category><category>chain-rule</category>
  </item>

  <item>
    <guid isPermaLink="true">https://rajdeepmondal.com/writing/computer-vision-optimization-is-how-models-pay-for-being-wrong</guid>
    <title>Optimization Is How Models Pay for Being Wrong</title>
    <link>https://rajdeepmondal.com/writing/computer-vision-optimization-is-how-models-pay-for-being-wrong</link>
    <description>Optimization is repeated local correction. The loss curve is the instrument that names which correction is broken, and it speaks before final accuracy does.</description>
    <content:encoded><![CDATA[<div class="essay-prose"><p>Optimization is repeated local correction. The loss curve is the instrument that names which correction is broken. The height you descend is the loss chosen in the note on linear classifiers. The coordinates are the parameters. Once a score function and a loss exist, training becomes a movement problem.</p><p>The job is to move downhill without wasting steps and without walking into nonsense.</p><h2 id="the-gradient-is-local-advice-about-a-surface-you-cannot-see">The gradient is local advice about a surface you cannot see</h2><p>A gradient says how the loss changes when you nudge each parameter. It is local advice about the point you stand on. It names the direction that rises fastest right here, and descent steps the other way.</p><p>In a real network the parameter space has millions or billions of dimensions. You do not visualize that surface. You probe it, one local measurement of slope at a time.</p><p>The gradient never points at the best answer. It points at the steepest local change. It earns its keep when you repeat small corrections in the direction it gives.</p><div class="uth-essay uth-figure-embed"><div><figure class="fig reveal" id="cs231n-gradient-step">
  
  <div class="fig-head">
    <span class="fig-title">gradient descent is controlled correction</span>
    <span class="ctrl">
      
      
    </span>
  </div>
  <div class="stage" id="cs231n-gradient-step-stage" role="group" aria-live="polite" aria-describedby="cs231n-gradient-step-caption">
    <div class="course-visual opt-wrap" aria-hidden="true">
      <div class="opt-surface">
        <span class="opt-contour c1"></span><span class="opt-contour c2"></span><span class="opt-contour c3"></span>
        <span class="opt-arrow"></span><span class="opt-dot"></span>
      </div>
      <div class="opt-panel">
        <div class="opt-row" data-opt="loss"><b>loss</b><span>height tells how wrong the model is</span></div>
        <div class="opt-row" data-opt="slope"><b>slope</b><span>the gradient points uphill, so we step the other way</span></div>
        <div class="opt-row" data-opt="step"><b>rate</b><span class="opt-rate"><i></i></span></div>
        <div class="opt-row" data-opt="repeat"><b>repeat</b><span>many local moves become learning</span></div>
      </div>
    </div>
    <div class="course-rail" data-rail aria-hidden="true"><span class="course-node" data-node="0">
        <span class="course-dot" aria-hidden="true">1</span>
        <span class="course-node-label">measure</span>
      </span><span class="course-node" data-node="1">
        <span class="course-dot" aria-hidden="true">2</span>
        <span class="course-node-label">slope</span>
      </span><span class="course-node" data-node="2">
        <span class="course-dot" aria-hidden="true">3</span>
        <span class="course-node-label">step</span>
      </span><span class="course-node" data-node="3">
        <span class="course-dot" aria-hidden="true">4</span>
        <span class="course-node-label">repeat</span>
      </span></div>
    <div class="course-grid"><div class="course-step" data-step="0">
        <span class="course-k">measure</span>
        <span class="course-v">compute the loss so the model knows how wrong it is</span>
      </div><div class="course-step" data-step="1">
        <span class="course-k">slope</span>
        <span class="course-v">use the gradient to find which parameter changes reduce the loss</span>
      </div><div class="course-step" data-step="2">
        <span class="course-k">step</span>
        <span class="course-v">move a little, not all the way, because the slope is local</span>
      </div><div class="course-step" data-step="3">
        <span class="course-k">repeat</span>
        <span class="course-v">keep correcting until progress slows or validation says stop</span>
      </div></div>
    <div class="course-status" data-status aria-live="polite"><b>measure.</b> compute the loss so the model knows how wrong it is</div>
  </div>
  <figcaption id="cs231n-gradient-step-caption">Optimization is not magic search. It is repeated blame and correction under a learning-rate budget.</figcaption>
</figure></div><span hidden=""></span></div><h2 id="random-search-is-the-embarrassment-baseline">Random search is the embarrassment baseline</h2><p>One crude method samples random weights and keeps the best. It ignores the shape of the loss surface and treats learning as lottery tickets. It must make you uncomfortable.</p><p>Run it once anyway, as the floor. It gives the first loss value to beat, which makes the next run comparable. An optimizer that cannot beat random poking is reporting a broken setup. Look at the data path and the loss.</p><h2 id="sgd-trades-an-exact-gradient-for-many-more-updates">SGD trades an exact gradient for many more updates</h2><p>Computing the gradient over the full dataset at every step costs too much. Stochastic gradient descent uses minibatches instead, and the estimate it returns is noisy. A full-dataset step is exact and rare. A minibatch step is noisy and frequent, and frequency wins the wall clock.</p><p>The noise buys something. It helps the optimizer escape sharp local quirks. It also makes the step size matter more, because a full stride along a rough direction overshoots.</p><h2 id="the-learning-rate-decides-whether-descent-is-careful-slow-or-chaotic">The learning rate decides whether descent is careful, slow, or chaotic</h2><p>The learning rate is the size of the step you take blindfolded. Too small and training crawls. Too large and the loss jumps or explodes. A stable run moves downhill at a pace the surface supports.</p><p>Sweep the learning rate before you change anything else. A run that crawls and a run that explodes both name that knob first.</p><h2 id="read-the-curve-before-you-touch-the-architecture">Read the curve before you touch the architecture</h2><p>The mistake is to treat gradient descent like a button. Optimization stays empirical even when the math is clean. Your instruments are the loss curve, the gradient norms, the validation behavior, and the update magnitudes.</p><figure class="rm-article-image"><div class="relative overflow-hidden"><div class="bg-muted absolute inset-0 z-10 animate-pulse rounded-[inherit]"><div class="animate-shimmer via-paper-bright/70 absolute inset-0 bg-linear-to-r from-transparent to-transparent"></div></div></div><figcaption>Four first runs on the same axes, and the setting each shape sends you to before the architecture.</figcaption></figure><ul><li>The loss is flat from the first step. Examine the learning rate, the gradients, and whether the data reaches the model at all.</li><li>The loss explodes. Examine the step size, the initialization, and the loss for numerical safety.</li><li>Training improves and validation stalls. The model is learning the training set more than the task, so examine overfit and leakage.</li><li>Both improve slowly. The setup can work, and the step size or the capacity is underpowered.</li></ul><p>A gradient norm at zero and a loss that will not move are one finding on two instruments. Learning rate, labels, batch size, initialization, and implementation bugs announce themselves here before final accuracy does.</p><p>Minibatch noise complicates the reading. The per-step loss is jagged by construction, so a single step is not a reading. Compare a window of steps against the window before it. A jagged curve trending down is doing its job. A jagged curve that stays flat sends you back to the list above.</p><h2 id="numerical-gradients-are-slow-truth">Numerical gradients are slow truth</h2><p>A numerical gradient changes one parameter by a tiny amount and watches the loss move. An analytic gradient computes the derivative directly, and training runs on it because it is fast. Numerical gradients are too slow for training. They are the reference that says whether the analytic ones are right.</p><p>When you write a new loss or a new layer, check it on a tiny input first. Numerical gradients are affordable there. The same habit has a larger version. Before a long run, make sure the model can overfit a tiny batch. Both checks are cheap, and both stop a broken setup before it costs you hours.</p><h2 id="the-builder-test">The Builder Test</h2><p>Predict the shape of the loss curve before you launch the run. Write the prediction down, launch, and compare the first steps against it. Name the mismatch as one of four things: scale, learning rate, data, or gradients.</p><p>Sweeping the knob until one curve looks good teaches you nothing about what was broken. The prediction turns a run into evidence. A curve that matches confirms the setup. A curve that misses names the bug.</p><h2 id="what-carries">What Carries</h2><p>A single gradient can be noisy, local, or badly scaled. The curve over many steps is the evidence.</p><p>A gradient helps only when it reaches every parameter. The next note is the bookkeeping that delivers it.</p><div class="rm-essay-close" aria-hidden="true"><svg width="76" height="32" viewBox="0 0 76 32" aria-hidden="true" class="rm-fmark-essay"><path d="M 2 14 Q 18 2 36 14 T 74 12" fill="none" stroke="currentColor" stroke-width="1.1" stroke-linecap="round" opacity="0.5"></path><line x1="2" y1="24" x2="60" y2="24" stroke="currentColor" stroke-width="1.1"></line><circle cx="68" cy="24" r="2.8" fill="var(--vermilion)"></circle></svg></div></div>]]></content:encoded>
    <pubDate>Wed, 03 Jun 2026 12:00:00 GMT</pubDate>
    <author>rajdeep@rajdeepmondal.com (Rajdeep Mondal)</author>
    <category>machine-learning</category><category>computer-vision</category><category>optimization</category><category>gradient-descent</category><category>sgd</category>
  </item>

  <item>
    <guid isPermaLink="true">https://rajdeepmondal.com/writing/computer-vision-linear-classifiers-margins-and-softmax</guid>
    <title>One Template Per Class Is the Whole Limit</title>
    <link>https://rajdeepmondal.com/writing/computer-vision-linear-classifiers-margins-and-softmax</link>
    <description>A linear classifier throws away the training set and keeps a weight matrix. The price of that compression is one averaged template per class.</description>
    <content:encoded><![CDATA[<div class="essay-prose"><p>A linear classifier throws away the training set and keeps a weight matrix instead. Nearest neighbor kept every image and changed only the comparison rule. This model reverses that trade, and it is the first real model in the course. The price of the compression is one averaged template per class.</p><h2 id="the-whole-model-is-one-matrix-multiply">The whole model is one matrix multiply</h2><p>The function is blunt. Flatten the image into a column, multiply by a weight matrix, add a bias, read one score per class. For CIFAR-10 that is 3,072 pixel inputs and 10 class scores.</p><div class="codeblock"><pre><code class="language-python">scores = W @ x + b
predicted_class = argmax(scores)</code></pre></div><p>The model is simple enough that shape mistakes are embarrassing and common. Write the shapes down once and check the code against them.</p><div class="codeblock"><pre><code class="language-text">x:      3072 x 1
W:      10 x 3072
b:      10 x 1
scores: 10 x 1</code></pre></div><p>If you can write those four lines without looking, you understand the model.</p><p>The bias moves the decision boundaries. Without it, every boundary must pass through the origin. A common trick appends a constant 1 to every input vector. Then W absorbs it as one extra column.</p><p>That is bookkeeping. Clean bookkeeping matters, because backpropagation will depend on boring and correct shapes.</p><h2 id="a-row-of-w-is-a-class-template-you-can-reshape-into-an-image">A row of W is a class template you can reshape into an image</h2><p>Each row of W is one class template. It likes some pixels, dislikes others, and returns a single number. That number is a dot product between the row and the image.</p><p>The row has the same length as the flattened image. Reshape it back into image shape and you can look at what the model learned about that class. In early vision that window matters, because the model stays small enough to inspect all the way down.</p><p>Geometry comes before probability here. The classifier cuts pixel space with planes, and each score reports which side of a boundary holds the image. The size of the score reports the strength of that answer.</p><h2 id="one-template-must-average-every-visual-mode-of-the-class">One template must average every visual mode of the class</h2><p>One template per class is also the limit. Cars appear red and blue, front-facing and side-facing, and half-occluded. One row of W must average all of those modes, and the average is a blurry compromise.</p><p>Those modes are the pressure list from the overview note. Pose, scale, color, and occlusion are what a single averaged row cannot hold at once.</p><p>The weights also use whatever separates the training set, including background and scale. A feature that splits the training images without generalizing still lowers the loss. The failure stays legible, which is the useful part. When one boundary cannot separate the visual modes, you know why deeper representations become necessary.</p><h2 id="svm-and-softmax-punish-different-stories">SVM and softmax punish different stories</h2><p>Multiclass SVM loss asks more than whether the correct class scores highest. It asks whether the correct class beats every rival by a margin.</p><p>The margin is the point, because a barely correct score is fragile. A score that wins by enough leaves room for a rival to move.</p><p>A rival sitting inside the margin costs loss. Once every rival clears it, that example contributes zero data loss and SVM stops pushing on it.</p><p>Softmax starts somewhere else. It turns the same scores into a probability distribution. Cross-entropy then charges the model when the true class gets low probability.</p><p>Softmax also hands you a number that looks like confidence. High probability and calibrated probability are separate claims, and the held-out split settles which one you have.</p><p>SVM keeps rivals away from the correct class. Softmax piles probability mass onto it. Both make bad scores expensive, and they press on the same ten numbers in different places.</p><figure class="rm-article-image"><div class="relative overflow-hidden"><div class="bg-muted absolute inset-0 z-10 animate-pulse rounded-[inherit]"><div class="animate-shimmer via-paper-bright/70 absolute inset-0 bg-linear-to-r from-transparent to-transparent"></div></div></div><figcaption>The same score vector, read once as template matches and once as the quantity each loss prices.</figcaption></figure><p>The difference shows up in the training curves. SVM can ignore examples that are already safe. Softmax keeps shaping confidence long after the ranking is correct.</p><p>Accuracy counts whether the top label matched. Loss reports how strongly the model was wrong or right, and the optimization note reads that curve.</p><h2 id="regularization-states-which-fitting-solution-you-meant">Regularization states which fitting solution you meant</h2><p>Without regularization, many weight settings fit the training data, some sharp and brittle, others smaller and smoother. Regularization names which one you prefer.</p><p>L2 penalizes large weights, which stops one accidental pixel from carrying the decision. The data loss says fit the labels. The regularizer says fit them in this kind of way. That preference is the model you meant to build.</p><h2 id="the-builder-test">The Builder Test</h2><p>Run three checks before you reach for a bigger model.</p><ul><li>Reshape each row of W into image shape and look at it.</li><li>Inspect the class confusions and ask which two templates overlap.</li><li>Compare train loss with validation loss on the same run.</li></ul><p>Read the gap between those two numbers. A wide gap means the model fit noise instead of structure.</p><h2 id="what-carries">What Carries</h2><p>Scores become behavior only through a loss. The loss defines what the model pays attention to, so choose it with care. It says how strongly the model is wrong. The next question is how the parameters move because of it.</p><div class="rm-essay-close" aria-hidden="true"><svg width="76" height="32" viewBox="0 0 76 32" aria-hidden="true" class="rm-fmark-essay"><path d="M 2 14 Q 18 2 36 14 T 74 12" fill="none" stroke="currentColor" stroke-width="1.1" stroke-linecap="round" opacity="0.5"></path><line x1="2" y1="24" x2="60" y2="24" stroke="currentColor" stroke-width="1.1"></line><circle cx="68" cy="24" r="2.8" fill="var(--vermilion)"></circle></svg></div></div>]]></content:encoded>
    <pubDate>Mon, 01 Jun 2026 12:00:00 GMT</pubDate>
    <author>rajdeep@rajdeepmondal.com (Rajdeep Mondal)</author>
    <category>machine-learning</category><category>computer-vision</category><category>linear-classifier</category><category>svm</category><category>softmax</category><category>regularization</category>
  </item>

  <item>
    <guid isPermaLink="true">https://rajdeepmondal.com/writing/computer-vision-image-classification-is-a-promise-about-generalization</guid>
    <title>Image Classification Is a Promise About Generalization</title>
    <link>https://rajdeepmondal.com/writing/computer-vision-image-classification-is-a-promise-about-generalization</link>
    <description>Image classification is a promise that the rule holds on the next image. The split is the only instrument that tests the promise, and it leaks in two ways.</description>
    <content:encoded><![CDATA[<div class="essay-prose"><p>Image classification is a promise that the rule holds on the next image. The split is the only instrument that tests the promise. The overview note listed evaluation as a stage of the pipeline. The split is that stage doing its work.</p><h2 id="a-32-by-32-rgb-image-arrives-as-3072-numbers">A 32 by 32 RGB image arrives as 3,072 numbers</h2><p>A larger image arrives as a bigger slab of the same values. The first job is to turn those numbers into one label from a fixed set. Understanding is not in the slab.</p><p>You cannot hand-write the rules for cat or car in pixels. The same object appears across poses, backgrounds, lighting conditions, scales, textures, and partial views. Every condition adds cases, and the rulebook explodes before it covers one class.</p><h2 id="you-collect-examples-because-you-cannot-write-the-rulebook">You collect examples because you cannot write the rulebook</h2><p>So you collect examples. Give the model labeled images, pick a model family, and learn from the examples. Then measure on images the model never saw. A model that succeeds only on the images it memorized is a lookup table with good manners.</p><p>Every classifier in this course keeps the same contract.</p><div class="codeblock"><pre><code class="language-python">model.train(X_train, y_train)
y_pred = model.predict(X_test)
accuracy = mean(y_pred == y_test)</code></pre></div><p>Train on labeled images, predict on unseen images, score the match. The implementation behind train and predict changes with every model that follows. The contract stays fixed, which is why the crude baseline is still worth writing.</p><h2 id="nearest-neighbor-is-the-honest-baseline-because-it-makes-you-define-distance">Nearest neighbor is the honest baseline because it makes you define distance</h2><p>To train, nearest neighbor remembers every image. To predict, it compares the new image against every stored image and copies the closest label. It forces you to define distance before it can answer anything.</p><p>L1 adds the absolute pixel differences. L2 squares them, which punishes large disagreements harder. Neither distance understands an object, and both compare numbers.</p><p>The first neighbor can be an outlier. k-nearest neighbor asks the top k neighbors to vote, which smooths small islands of bad decisions. Do not trust the first kid who points at an answer. Ask five nearby and take the vote.</p><p>Nearest neighbor pays at prediction time. It stores the whole training set and scans it for every query. That cost grows with the dataset. Whatever this baseline already solves is not evidence for the model that replaces it.</p><h2 id="pixel-closeness-is-not-semantic-closeness">Pixel closeness is not semantic closeness</h2><p>Move a cat a few pixels to the right and the raw vector changes a lot. The picture still means the same thing to you. Change the background behind a car and the model finds a closer match in the wrong class. The colors line up and the label does not.</p><p>You stay invariant to pose, lighting, small translation, partial occlusion, and background clutter. Pixel distance treats each of those as a large change. The overview note named that list as the standing pressure on every vision model. Nearest neighbor fails all of it at once.</p><p>A vision system must learn those invariances. It cannot assume them. The repair has to come from the representation, because L1 and L2 read only the numbers they are handed.</p><h2 id="tune-on-the-test-set-and-it-becomes-training-data">Tune on the test set and it becomes training data</h2><p>Train on the training set. Tune k, the distance metric, the learning rate, and regularization on the validation set. Touch the test set once, at the end. Validation is the wall between exploration and final judgment.</p><p>A clean split tells you whether the model learned the object, the dataset, or a shortcut. A dirty one tells you nothing and reads the same.</p><p>The split leaks in two ways. Near-duplicate images cross from training into validation, and the validation number then reports memory. Repeated peeking at the test set carries the same information across one decision at a time.</p><figure class="rm-article-image"><div class="relative overflow-hidden"><div class="bg-muted absolute inset-0 z-10 animate-pulse rounded-[inherit]"><div class="animate-shimmer via-paper-bright/70 absolute inset-0 bg-linear-to-r from-transparent to-transparent"></div></div></div><figcaption>Close both arrows and the test number means what it says.</figcaption></figure><p>Each leak ends in the same place. The test number stays clean and lies. Peek often enough and you optimize the scoreboard instead of the task. The split is the first honest boundary between memory and vision.</p><h2 id="the-builder-test">The Builder Test</h2><p>Sort the misclassified images by confidence and start at the top. Inspect those confident mistakes by category, by background, and by distance to their nearest neighbors. Read the confusion matrix and find the class pairs carrying the real ambiguity.</p><p>Some of those mistakes are label noise, and a wrong label is not a model failure. Audit the split before you believe any number. Search for near-duplicates crossing training and validation. Check class frequency and photographer bias in the classes you confuse most. If a shortcut explains the confident mistakes, write the test that falsifies it.</p><h2 id="what-carries">What Carries</h2><p>The visible label is cat, car, truck, or bird. The hidden label is whether the rule survives outside the training set. Nearest neighbor keeps the data and only changes the comparison rule. The next model throws the data away. It keeps parameters instead.</p><div class="rm-essay-close" aria-hidden="true"><svg width="76" height="32" viewBox="0 0 76 32" aria-hidden="true" class="rm-fmark-essay"><path d="M 2 14 Q 18 2 36 14 T 74 12" fill="none" stroke="currentColor" stroke-width="1.1" stroke-linecap="round" opacity="0.5"></path><line x1="2" y1="24" x2="60" y2="24" stroke="currentColor" stroke-width="1.1"></line><circle cx="68" cy="24" r="2.8" fill="var(--vermilion)"></circle></svg></div></div>]]></content:encoded>
    <pubDate>Sat, 30 May 2026 12:00:00 GMT</pubDate>
    <author>rajdeep@rajdeepmondal.com (Rajdeep Mondal)</author>
    <category>machine-learning</category><category>computer-vision</category><category>image-classification</category><category>knn</category><category>validation</category>
  </item>

  <item>
    <guid isPermaLink="true">https://rajdeepmondal.com/writing/computer-vision-computer-vision-system-map</guid>
    <title>Vision Is Representation Under Pressure</title>
    <link>https://rajdeepmondal.com/writing/computer-vision-computer-vision-system-map</link>
    <description>Every method here answers one question: what representation survives an image nobody arranged for the model. Pose, lighting, scale, and occlusion decide the answer.</description>
    <content:encoded><![CDATA[<div class="essay-prose"><p>Every method in this sequence answers one question: what representation survives an image that nobody arranged for the model. The notes run from pixels to systems that classify, localize, generate, attend, transfer, and fail in ways you can name.</p><p>An image is a block of numbers until you build the machine that can use it. From those numbers the model must recover objects, shapes, texture, motion, and meaning. The work is turning raw arrays into decisions you can act on.</p><h2 id="the-pipeline-is-what-you-are-learning-and-it-has-seven-stages">The pipeline is what you are learning, and it has seven stages</h2><p>The pipeline is the lesson. The stages run in this order: data, representation, loss, optimization, architecture, evaluation, and failure analysis. Each stage decides what the next stage can see.</p><p>Data sets the ceiling on what the model can learn about. Representation decides what survives the first transform. The loss states what counts as wrong, and the model optimizes that statement rather than your intent. Optimization moves the parameters, and backpropagation is the bookkeeping for blame that tells each one how it contributed. Architecture makes some structure cheap to express and leaves the rest expensive.</p><p>Evaluation is the stage where the split does the work. Train fits the parameters. Validation picks the choices you make by hand. Test reports what happens on data you never touched. Failure analysis then decides what you fix next, and the fix often lands back at data or representation.</p><p>The pressures are the same at every stage: pose, lighting, scale, background, occlusion, viewpoint, and class imbalance. Every later note tests against that one list.</p><figure class="rm-article-image"><div class="relative overflow-hidden"><div class="bg-muted absolute inset-0 z-10 animate-pulse rounded-[inherit]"><div class="animate-shimmer via-paper-bright/70 absolute inset-0 bg-linear-to-r from-transparent to-transparent"></div></div></div><figcaption>Every stage takes the same pressure, so naming the one that gave way is the first move in failure analysis.</figcaption></figure><h2 id="every-method-asks-the-same-question-in-a-different-costume">Every method asks the same question in a different costume</h2><p>The sequence defines the task first, then makes the simplest models fail in public. After that come gradients, backpropagation, neural networks, convolution, and the training discipline that survives contact with real data. Later notes add attention, transformers, generative models, NeRF, and adversarial examples.</p><p>Each method answers the same two things: what must stay fixed, and what the model must learn from data.</p><p>Nearest neighbor fixes the data and changes the comparison rule. Linear classifiers learn a weight matrix, and from there the parameters carry the model. Neural networks learn layers of representation. ConvNets learn local filters and share them across space.</p><p>Transformers learn routing. Generative models learn the data distribution. Adversarial examples show that the learned representation is not the same thing as human vision.</p><p>Each answer buys an invariance and pays for it with information it throws away. Convolution buys locality and weight sharing because images repeat local patterns, and it gives up per-pixel parameters.</p><p>Historical order teaches the names. Shortcut order teaches the work: what shortcut each architecture blocks, and what new shortcut it opens. Track what each method makes easier to represent and which failure it leaves exposed.</p><h2 id="the-contract-holds-even-when-the-architecture-changes">The contract holds even when the architecture changes</h2><p>Every note in the sequence works under one contract. You start with raw visual data, define a task, choose a representation and an objective, and optimize it. Then you show the result survives new examples.</p><p>That is why notes written before transformers still decide how I read the new ones. Six standing checks carry across all of them.</p><ul><li>Name the input and its exact shape.</li><li>Say where the labels came from and who made them.</li><li>Write the loss down and read it as the goal the model pursues.</li><li>Pick the baseline that makes any gain believable.</li><li>State what changed after training.</li><li>Find what breaks under distribution shift.</li></ul><h2 id="read-every-note-with-one-builder-question">Read every note with one builder question</h2><p>One filter decides what I keep from a note. If I must implement this or debug it tomorrow, what do I need to remember?</p><p>That filter cuts a lot of academic fog. It keeps the shape math, the loss intuition, and the failure modes. It keeps the small implementation details that decide whether a notebook trains or only looks like it trains.</p><h2 id="a-clean-accuracy-number-is-the-easiest-thing-to-trust-too-early">A clean accuracy number is the easiest thing to trust too early</h2><p>A model can win the average case and break under a lighting change, an occlusion, or a rare class. It can also break because it found a background cue and used it.</p><p>Vision models see tensors and correlations, and a background cue is a correlation like any other. That is why baselines, augmentation, saliency, ablations, and error analysis carry the weight here. Trust an accuracy number after it holds on a slice you chose to be hard.</p><h2 id="the-builder-test">The Builder Test</h2><p>Pick one misclassified image and explain the mistake. Name the feature the model used and the evidence that proves it. Then name the one change that makes the model less brittle.</p><p>You pass when another person looks at the same evidence and sees the same cue.</p><h2 id="what-carries">What Carries</h2><p>Generalization is the whole point. Training accuracy is a rehearsal, and the image the model never saw is the performance.</p><p>The first stage fixes the task: pixels in, one label out. Start there. The label must hold under the pressure list, and the split is the instrument that reports whether it does.</p><div class="rm-essay-close" aria-hidden="true"><svg width="76" height="32" viewBox="0 0 76 32" aria-hidden="true" class="rm-fmark-essay"><path d="M 2 14 Q 18 2 36 14 T 74 12" fill="none" stroke="currentColor" stroke-width="1.1" stroke-linecap="round" opacity="0.5"></path><line x1="2" y1="24" x2="60" y2="24" stroke="currentColor" stroke-width="1.1"></line><circle cx="68" cy="24" r="2.8" fill="var(--vermilion)"></circle></svg></div></div>]]></content:encoded>
    <pubDate>Thu, 28 May 2026 12:00:00 GMT</pubDate>
    <author>rajdeep@rajdeepmondal.com (Rajdeep Mondal)</author>
    <category>machine-learning</category><category>computer-vision</category><category>technical-notes</category><category>deep-learning</category>
  </item>

    </channel>
  </rss>