<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Rickesh T N</title>
    <description>The latest articles on DEV Community by Rickesh T N (@rickeshtn).</description>
    <link>https://dev.to/rickeshtn</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4066262%2Fafe79880-1a39-479b-b6f1-0a5d39b9802a.jpeg</url>
      <title>DEV Community: Rickesh T N</title>
      <link>https://dev.to/rickeshtn</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/rickeshtn"/>
    <language>en</language>
    <item>
      <title>Three Ways Your Training Data Lies to You (And None of Them Throw an Error)</title>
      <dc:creator>Rickesh T N</dc:creator>
      <pubDate>Fri, 07 Aug 2026 00:21:27 +0000</pubDate>
      <link>https://dev.to/rickeshtn/three-ways-your-training-data-lies-to-you-and-none-of-them-throw-an-error-4044</link>
      <guid>https://dev.to/rickeshtn/three-ways-your-training-data-lies-to-you-and-none-of-them-throw-an-error-4044</guid>
      <description>&lt;p&gt;Every failure I am about to describe produced a clean run. No exception, no stack trace, no red build. Each one produced a plausible number that I believed for longer than I should have.&lt;/p&gt;

&lt;p&gt;That is the category of bug I have come to fear most. A crash tells you it crashed. A silently broken dataset tells you nothing at all, and your metrics will politely agree with it.&lt;/p&gt;

&lt;p&gt;Here are three from the last year, all from my own work, all found late.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. The dataset that was 92% one category
&lt;/h2&gt;

&lt;p&gt;I had a training set of 688 records for a multi-category vision-language task. Thirteen categories. Reasonable size for a fine-tune, already used in a completed training run whose results I had written up.&lt;/p&gt;

&lt;p&gt;While preparing a stratified split, I joined the records back against the source annotations and actually counted the categories.&lt;/p&gt;

&lt;p&gt;630 of 688 were a single category: scene captions. Zero examples of traffic signals. Zero of planning. Zero of uncertainty. Several categories the evaluation explicitly measured had no representation in training at all.&lt;/p&gt;

&lt;p&gt;The previous fine-tune had shown gains on some of those very categories. I had interpreted this as the model learning the task. The real explanation was duller and more useful: the model had learned the &lt;em&gt;answer format&lt;/em&gt; from caption supervision, and format alignment alone was enough to move a multiple-choice score. Nothing category-specific had been learned, because nothing category-specific had been shown.&lt;/p&gt;

&lt;p&gt;The root cause was upstream and boring. The conversion script I inherited only rewrote file paths and dropped records with missing frames. It faithfully preserved a caption-only selection made further up the chain. It had no opinion about balance because nobody had asked it to have one.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What I changed:&lt;/strong&gt; the composition of a training set is now an artifact I generate and inspect before any run, not a property I assume. A category histogram takes seconds. I had not looked, for months.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. The 18-hour run that converged perfectly to nothing
&lt;/h2&gt;

&lt;p&gt;Large model, QLoRA, multi-GPU, 8 camera views per sample. Roughly a full day of compute.&lt;/p&gt;

&lt;p&gt;The loss curve was beautiful. It fell from 19.4 to 15.7 over the first 26 steps, then to 0.078 by step 51, then flattened near 0.02 and stayed there. Token accuracy reached 0.99. Gradient norms decayed smoothly. Nothing in the training telemetry looked wrong.&lt;/p&gt;

&lt;p&gt;The task metric was 0.10.&lt;/p&gt;

&lt;p&gt;The cause: I was supervising on free-text answers, median 11 words, phrased like "One should keep to the right side of the road and drive slowly." The evaluation was four-option multiple choice, scored on the index of the chosen option.&lt;/p&gt;

&lt;p&gt;The model learned, quickly and correctly, to reproduce the &lt;em&gt;style&lt;/em&gt; of the training answers. That is genuinely what the loss was asking for, and it maxed it out in about 50 steps. It was never asked to select an option, so it never learned to.&lt;/p&gt;

&lt;p&gt;Training and evaluation were measuring different tasks. Both were internally consistent. Neither could detect the other's disagreement.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What I changed:&lt;/strong&gt; before a long run starts, I now write down what the eval measures and what the loss optimizes, in one line each, and check they describe the same thing. When loss collapses to near zero within a few dozen steps, I treat that as an alarm rather than a success. Genuine learning on a hard task does not look like that.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. The dataset that was 40% smaller than its file size claimed
&lt;/h2&gt;

&lt;p&gt;Assessing a large public robotics corpus for adoption. The headline: 3.91 TB compressed, 4.65 TB extracted, hundreds of shards, dozens of buildings, thousands of hours of teleoperated manipulation.&lt;/p&gt;

&lt;p&gt;The number that mattered was buried in the paper's method section. Only about 60% of the raw data converted into reliable 3D flow annotations. The rest failed depth estimation, camera pose optimization, or point tracking. Then a further filter kept only trajectories with actual robot-object contact and real object motion.&lt;/p&gt;

&lt;p&gt;Nominal size: thousands of hours. Training-ready size after both filters: roughly 500 hours.&lt;/p&gt;

&lt;p&gt;Every plan built on the first number was wrong by a factor of several. Storage estimates, download time, compute budget, and most importantly the question of whether the corpus was even large enough for what we wanted.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What I changed:&lt;/strong&gt; I now treat &lt;em&gt;yield&lt;/em&gt; as the primary dataset metric, not size. What fraction of raw capture survives every stage into training-ready data? For collected data this is a hard operational KPI, and it is the one that determines cost per usable hour. Bytes on disk is a storage figure. It tells you almost nothing about what you can train on.&lt;/p&gt;

&lt;h2&gt;
  
  
  The pattern
&lt;/h2&gt;

&lt;p&gt;All three share a shape.&lt;/p&gt;

&lt;p&gt;A pipeline stage did exactly what it was written to do. A metric moved in the direction that metric moves when things go well. And the thing I actually cared about was not being measured by anything at all.&lt;/p&gt;

&lt;p&gt;The defenses that work for me:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Check that work happened, not just that output exists.&lt;/strong&gt; A gate that verifies "400 rows were produced" while never checking "any row succeeded" will pass a completely dead run. I once had an evaluation return zero correct on every row for 45 minutes because a kernel was unavailable on that GPU. Zero is a valid ratio. Row counts agreed. The gate passed and the sweep moved on.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Count infrastructure failures separately, and require zero.&lt;/strong&gt; A weak model produces well-formed output and scores badly. A broken pipeline produces nothing and scores identically. Collapsing those two into one number destroys the only signal that distinguishes them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Emit progress with errors first.&lt;/strong&gt; Anything running longer than a minute should print failure count before position before metric. Silence is not neutral. A job working perfectly and a job failing on every single item look exactly the same from outside when neither prints anything.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Validate splits as their own gated job.&lt;/strong&gt; I run split validation as a separate step that must pass before training is allowed to start. It checks class balance, domain distribution across sites, and leakage between splits. It has caught things that would otherwise have cost days of GPU time and, worse, produced a believable number.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Write down what the metric would look like if the thing were broken.&lt;/strong&gt; If you cannot answer that, you cannot tell success from failure, and you will default to reading any completed run as a successful one.&lt;/p&gt;

&lt;h2&gt;
  
  
  The uncomfortable part
&lt;/h2&gt;

&lt;p&gt;In every one of these cases the data was the limiting factor, and in every case I spent the first stretch of debugging looking at the model.&lt;/p&gt;

&lt;p&gt;That instinct is hard to unlearn, because the model is where the interesting work feels like it lives. But architecture, optimizer, and learning rate are all things I can inspect in a config file in about a minute. The composition of the training set is something I have to deliberately go and measure, and that extra step is exactly why it goes unchecked.&lt;/p&gt;

&lt;p&gt;The failure mode is not carelessness. It is that broken data produces output that looks entirely normal.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;I work on robotics data pipelines, model evaluation, and the operational side of making training data trustworthy. &lt;a href="https://linkedin.com/in/rickeshnatarajan" rel="noopener noreferrer"&gt;linkedin.com/in/rickeshnatarajan&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>machinelearning</category>
      <category>datascience</category>
      <category>mlops</category>
      <category>ai</category>
    </item>
    <item>
      <title>Your reasoning model isn't dumb. Your parser is throwing away its best answers.</title>
      <dc:creator>Rickesh T N</dc:creator>
      <pubDate>Fri, 07 Aug 2026 00:19:57 +0000</pubDate>
      <link>https://dev.to/rickeshtn/your-reasoning-model-isnt-dumb-your-parser-is-throwing-away-its-best-answers-4kdg</link>
      <guid>https://dev.to/rickeshtn/your-reasoning-model-isnt-dumb-your-parser-is-throwing-away-its-best-answers-4kdg</guid>
      <description>&lt;p&gt;I benchmarked a vision-language model and scored it at 0.31.&lt;/p&gt;

&lt;p&gt;The real number was 0.70. Same model, same weights, same hardware, same 100 questions. The only thing that changed was how I read its output.&lt;/p&gt;

&lt;p&gt;I had already written up the 0.31 as a capability finding and concluded the model was unsuitable. That conclusion was wrong, and the failure was entirely in my harness. Here is the mistake, because I doubt I am the only one making it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The setup
&lt;/h2&gt;

&lt;p&gt;I was evaluating a batch of open-weight and frontier models on a multiple-choice benchmark: multi-view driving scenes, four options per question, one correct answer. Standard stuff. The prompt asked for reasoning followed by a final line, &lt;code&gt;Answer: X&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;My scoring code did the obvious thing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Answer:\s*([A-D])&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;pred&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;group&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;   &lt;span class="c1"&gt;# None scores as wrong
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That last comment is the bug.&lt;/p&gt;

&lt;h2&gt;
  
  
  What actually happened
&lt;/h2&gt;

&lt;p&gt;The model I was testing is a "thinking" model. It emits a long internal reasoning trace before it commits to an answer. I had a generation budget of 1024 tokens.&lt;/p&gt;

&lt;p&gt;On easy questions it reasoned briefly, emitted &lt;code&gt;Answer: B&lt;/code&gt;, and scored fine. On hard questions it reasoned at length, hit the token cap mid-thought, and never emitted the answer line at all.&lt;/p&gt;

&lt;p&gt;So the harness scored every one of those as wrong.&lt;/p&gt;

&lt;p&gt;64 of 100 questions returned no parseable answer. Zero of those were image-loading errors or crashes. They were all truncation. And the truncation was not random:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Uncertainty                 0/8   answered
Counterfactual              0/3   answered
Safety-critical Planning    1/11  answered
Safety-critical Prediction  3/12  answered
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Look at that distribution. The questions the model failed to answer were precisely the questions that required the most reasoning. My harness was systematically discarding the model's performance on exactly the hard subset I was trying to measure, and reporting the result as a capability ceiling.&lt;/p&gt;

&lt;p&gt;Of the 36 it did answer, it got 86% right. The model was fine. My measurement was garbage.&lt;/p&gt;

&lt;h2&gt;
  
  
  The fix
&lt;/h2&gt;

&lt;p&gt;Stop parsing free text. Constrain the decoding to a schema.&lt;/p&gt;

&lt;p&gt;With Ollama:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;resp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ollama&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;chat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;my-vlm:9b&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;role&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;images&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;imgs&lt;/span&gt;&lt;span class="p"&gt;}],&lt;/span&gt;
    &lt;span class="nb"&gt;format&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;                       &lt;span class="c1"&gt;# enforced at decode time
&lt;/span&gt;        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;object&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;properties&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;answer&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;string&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;enum&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;A&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;B&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;C&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;D&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]}},&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;required&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;answer&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="n"&gt;think&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The equivalent exists nearly everywhere now: &lt;code&gt;response_format&lt;/code&gt; with a JSON schema on OpenAI-compatible endpoints, structured outputs in vLLM via &lt;code&gt;guided_json&lt;/code&gt;, Outlines, or plain grammar-constrained sampling in llama.cpp.&lt;/p&gt;

&lt;p&gt;The point is that the model can no longer produce an unparseable output. The constraint is applied during sampling, not checked afterwards.&lt;/p&gt;

&lt;p&gt;Re-ran the same 100 questions: &lt;strong&gt;0.70, with a 100% answer rate.&lt;/strong&gt; The categories that had been at zero came back at 0.82 and 0.75.&lt;/p&gt;

&lt;h2&gt;
  
  
  The second-order trap
&lt;/h2&gt;

&lt;p&gt;I hit a related version of this on a larger model in the same family, and the fix was less obvious.&lt;/p&gt;

&lt;p&gt;That model also truncated, on 36 of 100 questions. I had a fallback that recovered an answer letter from the tail of a truncated trace, so those rows produced &lt;em&gt;something&lt;/em&gt;. The score came out 0.67.&lt;/p&gt;

&lt;p&gt;But the recovered answers were junk in a specific, dangerous way. Predicted answers skewed heavily toward option A (48 predictions of A against 23 in the ground truth). When the model gets cut off mid-reasoning, the letter you scrape from the tail is not a decision, it is whatever token happened to be nearby. That is position bias, and it looks exactly like a real answer to your scorer.&lt;/p&gt;

&lt;p&gt;Rows where the reasoning actually completed scored 0.83. I re-ran just the truncated rows with a budget of 8192 instead of 3072, and the merged score came to 0.73.&lt;/p&gt;

&lt;p&gt;So: tail-recovery is worse than a non-answer. A non-answer is visibly missing. A biased recovered answer silently contaminates your accuracy in a direction you did not choose.&lt;/p&gt;

&lt;p&gt;Note also that 16 of those 36 questions &lt;em&gt;still&lt;/em&gt; truncated at 8192 tokens. 0.73 remains a floor, not a measurement.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I do now
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Match the evaluator's generation budget to the training or intended-use budget.&lt;/strong&gt; Mismatched budgets scored 31% of my rows incorrectly in one run. If you fine-tuned at 512 tokens, do not evaluate at 128.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Track truncation as a first-class metric.&lt;/strong&gt; Log &lt;code&gt;clipped_ratio&lt;/code&gt; and &lt;code&gt;parseable_rate&lt;/code&gt; next to accuracy. If either moves, your accuracy number is not comparable to the previous run. I now fail a run outright if the parse-failure count is anything other than what I expect.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Distinguish infrastructure failure from poor performance.&lt;/strong&gt; An unparseable output is a harness event, not a model event. They must be counted separately, always. A weak model still produces well-formed answers and simply scores badly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Never let a non-answer score as wrong by default.&lt;/strong&gt; Make it raise, or count it in its own bucket. Silently mapping "I could not read this" to "the model was incorrect" is how you get a confident, published, wrong conclusion.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Verify the harness against a known-good signal before trusting any number it emits.&lt;/strong&gt; An evaluation harness that has never been checked against a baseline you can sanity-check by hand is untested code that produces numbers, not a measurement instrument.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this matters more than it used to
&lt;/h2&gt;

&lt;p&gt;Reasoning models broke an assumption that free-text answer parsing quietly relied on for years: that a model's answer appears in its output. With extended chain-of-thought, the answer arrives last, and last is exactly what a token budget truncates.&lt;/p&gt;

&lt;p&gt;Every leaderboard comparing a thinking model against a non-thinking one under a shared token budget is, at minimum, measuring something other than what it claims. The thinking model pays for its reasoning out of the same budget that has to carry its answer.&lt;/p&gt;

&lt;p&gt;I published 0.31 as a capability limit and had to retract it. The model was never the problem.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;I write about ML evaluation, robotics data pipelines, and the ways measurement quietly fails. &lt;a href="https://linkedin.com/in/rickeshnatarajan" rel="noopener noreferrer"&gt;linkedin.com/in/rickeshnatarajan&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>machinelearning</category>
      <category>llm</category>
      <category>evaluation</category>
      <category>ai</category>
    </item>
  </channel>
</rss>
