<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en-AU"><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://bikrambhattarai.com/feed.xml" rel="self" type="application/atom+xml" /><link href="https://bikrambhattarai.com/" rel="alternate" type="text/html" hreflang="en-AU" /><updated>2026-11-13T20:10:37+11:00</updated><id>https://bikrambhattarai.com/feed.xml</id><title type="html">Bikram Bhattarai</title><subtitle>Bikram Bhattarai is a data engineer in Melbourne, with over two years of SQL and reporting work as a customer insights analyst, now finishing a Master of Data Science at Swinburne. SQL, Python, data pipelines and figures that reconcile.</subtitle><author><name>Bikram Bhattarai</name></author><entry><title type="html">The 98.9% score that was a bug</title><link href="https://bikrambhattarai.com/articles/2026/the-98-9-percent-score-that-was-a-bug/" rel="alternate" type="text/html" title="The 98.9% score that was a bug" /><published>2026-11-12T00:00:00+11:00</published><updated>2026-11-12T00:00:00+11:00</updated><id>https://bikrambhattarai.com/articles/2026/the-98-9-percent-score-that-was-a-bug</id><content type="html" xml:base="https://bikrambhattarai.com/articles/2026/the-98-9-percent-score-that-was-a-bug/"><![CDATA[<p>In first semester this year I was the data engineer on a six person sentiment analysis project at Swinburne. The question was how well a model trained on everyday writing carries over to financial writing, and what kind of extra training helps.</p>

<p>We used two public datasets. GoEmotions is Reddit comments, 43,404 of them once its emotions were mapped onto our five labels. The Financial PhraseBank is 4,846 sentences from financial news. My job was to get both into one shape and hand the team data they could trust.</p>

<h2 id="one-script-and-a-rule">One script, and a rule</h2>

<p>All cleaning and splitting lived in a single script, and the README carried one rule in bold: do not write data cleaning or splitting code inside your modelling notebooks. Everyone loaded the same files.</p>

<p>The finance sentences were split 70/15/15 into training, validation and test sets, with a fixed seed, keeping the label mix the same in each part. That gave a test set of 727 sentences that no model was meant to see during training.</p>

<h2 id="the-score-that-was-too-good">The score that was too good</h2>

<p>Most of our models scored between 82% and 85% accuracy on that test set. Then the model trained on both datasets together came back at 98.9%.</p>

<p>A jump like that is a symptom, not a win. The cause was in the data. The combined training file had been built from the full finance dataset, all 4,846 rows, which included the 727 test sentences. The model was being marked on sentences it had already seen.</p>

<p>The fix was to rebuild the combined file from the Reddit comments plus the finance training set only, 46,796 rows, check that no test sentence was left in it, and retrain. The score fell to 84.7%, in line with everything else.</p>

<h2 id="what-the-clean-numbers-said">What the clean numbers said</h2>

<p>With the leak gone, the results were less exciting and more useful.</p>

<ul>
  <li>A TF-IDF model with a linear SVM, trained only on Reddit comments, got 56% on the finance sentences. The same model trained on finance sentences got 76%. General writing does not transfer for free.</li>
  <li>For BERT, adding 43,404 Reddit comments to 3,392 finance sentences moved accuracy by a point or so in either direction: 83.9% with finance data alone, 84.7% with the two mixed, 82.4% when trained on one and then the other.</li>
  <li>Every one of those BERT models scored zero on the “fear” label. The test set had only 7 fear sentences, and there were 32 to learn from. Accuracy hid that completely, which is why we reported macro F1 beside it: 0.57 to 0.60, a long way under 85%.</li>
</ul>

<p>The stratified split matters for the same reason. Fear is under 1% of the finance data. A plain random split could easily put 4 fear sentences in the test set, or 10, and the comparison between models would change with the draw.</p>

<h2 id="what-i-took-from-it">What I took from it</h2>

<p>I did not build the models. My teammates did, and the modelling choices are theirs. What I owned was whether the numbers could be believed.</p>

<p>The habit I kept is that a split is something you verify, not something you assume. And the result you are happiest about is the first one to check.</p>

<p>The code and the dashboard are <a href="https://github.com/TDP-2026-Repository/TECHNOLOGY-DESIGN-PROJECT">on GitHub</a>, and there is a short summary on the <a href="/projects/#sentiment">work page</a>.</p>]]></content><author><name>Bikram Bhattarai</name></author><category term="Data engineering" /><category term="NLP" /><category term="evaluation" /><summary type="html"><![CDATA[One of our sentiment models scored 98.9% when every other model sat near 85%. Notes from the data side of a team project, where the best result we had was the one we threw away.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://bikrambhattarai.com/assets/og/leak-post.png" /><media:content medium="image" url="https://bikrambhattarai.com/assets/og/leak-post.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">The file in my own project I am not allowed to read</title><link href="https://bikrambhattarai.com/articles/2026/the-file-i-am-not-allowed-to-read/" rel="alternate" type="text/html" title="The file in my own project I am not allowed to read" /><published>2026-11-12T00:00:00+11:00</published><updated>2026-11-12T00:00:00+11:00</updated><id>https://bikrambhattarai.com/articles/2026/the-file-i-am-not-allowed-to-read</id><content type="html" xml:base="https://bikrambhattarai.com/articles/2026/the-file-i-am-not-allowed-to-read/"><![CDATA[<p>Overseas Student Health Cover is the insurance most international students in Australia have to hold. It is also easy to misunderstand until the first bill arrives. This year three of us at Swinburne built a learning prototype for it, with Medibank as the industry partner.</p>

<p>The app has short lessons, a guide to finding care, a bill explainer that works on made-up invoices, and a chat that answers general questions from the insurer’s member guide. My part was the bill explainer’s extraction, the benefit arithmetic, a question classifier and the evaluation code.</p>

<h2 id="money-code-gets-its-tests-first">Money code gets its tests first</h2>

<p>The benefit arithmetic is the one place where a wrong answer could cost a student real money, so its tests were written before the code. The rules are short:</p>

<ul>
  <li>Money is never a float. Every amount is a decimal, rounded half up to the cent.</li>
  <li>A benefit can never be more than what was charged.</li>
  <li>The out of pocket gap can never be negative.</li>
  <li>If a rule is missing, or the Medicare schedule fee it depends on is not known, the function returns nothing, and the screen says that Medibank confirms the exact benefit.</li>
</ul>

<p>That last rule matters most. One test takes a 95.00 dollar charge for a service with a schedule fee of 41.20 and checks that the benefit comes from the fee, not the charge, which leaves a gap of 53.80. Another takes the schedule fee away and checks that the answer is no answer. A calculator that guesses is worse than one that says it does not know.</p>

<h2 id="the-file-i-am-not-allowed-to-read">The file I am not allowed to read</h2>

<p>Our invoices are synthetic. One teammate wrote the script that generates them, and I wrote the code that reads them. The README names a reviewer for every file. Beside the generator it says a different teammate reviews it, and never me.</p>

<p>The reason is simple. Two of the invoice layouts were set aside to score my extractor. If I read the generator, I see what those layouts look like, and the score stops meaning anything. So in a three person repository there is one file I do not open.</p>

<p>The plan gives me the mirror image of that job on the safety side. I write the hidden test questions for the chat’s safety gate, which a teammate builds, and they stay out of the repository until evaluation day. Building against the set you are scored on is not a measurement.</p>

<h2 id="an-answer-or-a-fixed-refusal">An answer, or a fixed refusal</h2>

<p>The chat follows the same idea. Every question goes through three separate steps: a review of the question, a draft written only from the member guide, and an audit of the draft. Quotes are checked against the source PDF. If any step fails, the student gets a fixed message, not a best guess.</p>

<p>Emergencies, symptoms and “will my policy pay for this” are stopped before anything is looked up, and pointed to the right service instead. The wording is always “this document shows”, never “you are covered”, because only the insurer can say that.</p>

<p>And no real document goes in, ever. No real invoice, policy number or student. That is a privacy rule, and it is also why the build was small enough to finish.</p>

<h2 id="what-i-would-not-claim">What I would not claim</h2>

<p>It is a prototype. Tests that pass with a stand-in for the model do not prove the live model is accurate, and our README says so. Parts of the planned study, including the labels coded by two people, are not finished and are not presented as finished.</p>

<p>What I took from the project is an order of work: decide what the system must refuse to do, and what each person must not see, before deciding what it should do.</p>

<p>The code is <a href="https://github.com/Bikram2051/oshc-when-it-matters">on GitHub</a>, and there is a short summary on the <a href="/projects/#oshc">work page</a>.</p>]]></content><author><name>Bikram Bhattarai</name></author><category term="Evaluation" /><category term="testing" /><category term="Python" /><summary type="html"><![CDATA[On a team prototype about health cover for international students, the most useful rules were about what each of us must not see and what the software must refuse to say.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://bikrambhattarai.com/assets/og/oshc-post.png" /><media:content medium="image" url="https://bikrambhattarai.com/assets/og/oshc-post.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">What 57,217 crashes say about car weight</title><link href="https://bikrambhattarai.com/articles/2026/what-57217-crashes-say-about-car-weight/" rel="alternate" type="text/html" title="What 57,217 crashes say about car weight" /><published>2026-11-12T00:00:00+11:00</published><updated>2026-11-12T00:00:00+11:00</updated><id>https://bikrambhattarai.com/articles/2026/what-57217-crashes-say-about-car-weight</id><content type="html" xml:base="https://bikrambhattarai.com/articles/2026/what-57217-crashes-say-about-car-weight/"><![CDATA[<p>This semester a classmate and I took one question to Victoria’s road crash data: when two cars collide, how much does weight decide which driver is badly hurt?</p>

<p>The short answer is a lot. In crashes where exactly one driver was killed or seriously injured, it was the driver of the lighter car two times out of three. After adjusting for driver age and sex, vehicle age, seatbelt use and point of impact, each 10% of extra weight went with about 20% lower odds for that car’s driver.</p>

<p>The <a href="/projects/crash-weight/">full case study</a> has the charts and the numbers. This note is about how we got there.</p>

<h2 id="compare-the-two-drivers-not-all-drivers">Compare the two drivers, not all drivers</h2>

<p>People who drive small cars are not the same people who drive large utes, and they do not drive on the same roads at the same times. A model that compares all drivers has to adjust for every one of those differences.</p>

<p>We avoided most of that by comparing the two drivers inside the same crash. They share the road, the weather, the speed limit and the moment, so none of those can explain why one was hurt and the other was not. The method is conditional logistic regression.</p>

<p>It has a cost. A crash only tells you something when the two outcomes differ. We started with 57,217 crashes, and 12,078 of them did the work.</p>

<h2 id="three-things-the-data-corrected">Three things the data corrected</h2>

<p><strong>The shape.</strong> Our plan said the risk would change by a fixed amount per 100 kg. I tested that straight line against a flexible curve before trusting it, and it did not hold. What matters is the ratio of the two weights. An extra 340 kg roughly doubles the odds between two small cars and adds about half between two large ones.</p>

<p><strong>The confounder.</strong> We expected vehicle age to explain part of the weight effect, because older cars are lighter and less safe. It explained almost nothing. Driver sex explained two thirds of the shift. Women in the data drove lighter cars and had higher odds of serious injury, so weight had been taking the credit for some of that.</p>

<p><strong>The codes.</strong> A speed zone of 999 is not a very fast road. It means unknown. We read every column as text and checked each one against the data dictionary before turning anything into a number.</p>

<h2 id="what-it-does-not-show">What it does not show</h2>

<p>This is an association in police-reported data. Heavy vehicles are also taller and stiffer, and without make and model we cannot separate weight from design.</p>

<p>It is also only the driver’s side of the story. Other research has found that heavier vehicles are deadlier for the pedestrians they hit. A heavier fleet protects the people inside it and costs the people outside it, and any honest summary needs both halves.</p>]]></content><author><name>Bikram Bhattarai</name></author><category term="Road safety" /><category term="statistics" /><category term="R" /><summary type="html"><![CDATA[When two cars collide in Victoria, the driver of the lighter one is hurt twice as often. Notes from a semester with the state's crash data, including three things the data corrected me on.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://bikrambhattarai.com/assets/og/crash-post.png" /><media:content medium="image" url="https://bikrambhattarai.com/assets/og/crash-post.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry></feed>