Loading…
Loading…
A SetFit text classifier that categorizes Indian bank/credit-card transaction narrations (UPI, NEFT, IMPS, ACH, POS) into everyday personal-finance spending categories.
It was built for a personal local-first finance tracker, after an explicit search turned up no public dataset that pairs real Indian narration formats with category labels — datasets either have real-looking Indian bank text with no labels (built for OCR/document-AI), or have category labels attached to generic Western merchant names with an "India" tag that doesn't reflect the actual UPI/NEFT/IMPS text shape. This model closes that specific gap using entirely synthetic, privacy-safe training data — see the companion training dataset, which also includes the generator script.
Three categories were added, driven by real gaps found running this model in production:
Salary — split out of Income & Deposits so recurring paycheck income can be tracked
separately from one-off credits (refunds, interest, cashback).Internal Transfer — money moved between two accounts the same person/household owns
(e.g. "SELF FUND TRANSFER", "MOB/SELFFT/..."). Previously these either inflated spend/investment
totals or scattered across unrelated categories.Loan Payment — loan EMI repayments. The training data specifically teaches the model to
tell a loan EMI (numeric-reference-only narrations like ECS EMI 100826 or
<ref>_EMI_<date>) apart from a retail purchase paid via no-cost EMI (e.g. EMI Uniqlo),
which stays categorized as the thing actually bought.⚠️ Breaking change: model.predict(...) can now return 3 new label strings it never returned
before. If your code maps predictions through a fixed switch/dict keyed on the old 18 categories,
update it to handle (or explicitly ignore) Salary, Internal Transfer, and Loan Payment before
upgrading.
Classifies a transaction description into one of 21 categories:
Groceries · Eating Out · Kids Activities · Shopping & Clothing · Medicine & Pharmacy ·
Hospital & Medical · Utilities · Rent & Mortgage · Loan Payment · Transportation & Gas ·
Entertainment & Subscriptions · Travel · Insurance · Education · Personal Care ·
Investments & Savings Transfer · ATM & Cash · Fees & Interest · Salary ·
Income & Deposits · Internal Transfer
It handles two input styles, both seen during training:
UPI/Swiggy Instamart/swiggy123@ybl/Payment/HDFC, NEFT Dr-HDFC0000953-SURESH VERMA-Blinkit"paid electricity bill BESCOM online", "SIP auto debit HDFC Flexicap fund"970 rows, entirely synthetic, generated from:
Nothing in this model was trained on real user transaction data. The generator script is included in the
[Budgeter repo] (synthetic_indian_generator.py) if you want to inspect or extend it.
| Test set | Accuracy | High-confidence wrong (≥0.6, would slip past a review gate) |
|---|---|---|
| Held-out synthetic (unseen seed, 370 rows) | 94.6% | 0 |
| Hand-crafted natural-language set, not from the generator's templates (30 rows) | 90.0% | 1 |
The held-out misses cluster almost entirely around one genuinely ambiguous boundary — "Interest Credit" /
"Dividend Credit" / "Cashback Credit" vs. Investments & Savings Transfer — and land at low confidence, so
in the source app these get flagged for human review rather than silently misfiled. Zero held-out misses are
confidently wrong, which matters more in practice than the raw accuracy number.
Honest limitation: this is trained entirely on synthetic data. It has not been evaluated against real bank statements (for privacy reasons, it never will be by the original author) — treat the numbers above as a reasonable proxy, not a guarantee, and validate against your own data before relying on it.
from setfit import SetFitModel
model = SetFitModel.from_pretrained("Sumeetgpt/indian-transaction-categorizer")
model.predict(["UPI/Swiggy Instamart/swiggy@ybl/Payment/HDFC"])
# ['Groceries']
probs = model.predict_proba(["UPI/Swiggy Instamart/swiggy@ybl/Payment/HDFC"])[0]
# use max(probs) as a confidence score; gate low-confidence predictions for human review
sentence-transformers/all-MiniLM-L6-v2