Phi-4 Mini — Entities & Topics Extraction (Indian Financial News)

A QLoRA adapter fine-tuned on top of unsloth/Phi-4-mini-instruct-bnb-4bit for one narrow task: extracting named entities and topic tags from Indian financial news articles, as strict JSON.

This is an adapter only — load it together with the base model, do not expect a standalone full model here.

What it does

Given a news article (title + body), the model returns JSON in this shape:

{
  "entities": [
    {
      "name": "Reliance Industries",
      "type": "COMPANY_LISTED",
      "canonical_candidate": "RELIANCE",
      "confidence": 0.95
    }
  ],
  "topics": ["EARNINGS", "CORPORATE_ACTION"]
}

entities[].type is one of: COMPANY_LISTED, COMPANY_UNLISTED, PERSON, REGULATOR, GOVT_BODY, SECTOR, PRODUCT, LOCATION, FUND, INDEX, COMMODITY, COUNTRY.

topics is 1-3 codes from a fixed 24-code taxonomy covering things like EARNINGS, REGULATORY, MONETARY_POLICY, OIL_AND_GAS, MARKET_INTRADAY, etc.

This is intentionally a narrow slice of a larger schema (the same pipeline elsewhere also extracts events, relationships, sentiment, and summaries) — entities+topics was chosen as a cheap, falsifiable first test of whether fine-tuning a small local model was worth pursuing before investing in the full schema.

Why this exists

The base pipeline runs full-schema extraction via GPT-4o/GPT-4o-mini per article. This adapter is an experiment in replacing that with a small, locally-run, fine-tuned model (Phi-4 Mini, 3.8B params) for the entity/topic subset of the task — to reduce per-article cost and external API dependency, if the quality holds up.

Training data

  • 706 Indian financial news articles, each labeled by GPT-4o (not mini) using a dedicated narrow prompt asking only for entities + topics.
  • Labeling cost: $5.88 total (1,391,186 input tokens + 240,285 output tokens at GPT-4o pricing).
  • Source articles pulled from MarketAux (free tier, India-focused financial news), full body scraped via trafilatura where available.

Training procedure

  • Method: QLoRA (4-bit base + LoRA adapters), via Unsloth.
  • Base model: unsloth/Phi-4-mini-instruct-bnb-4bit
  • LoRA config: rank 16, alpha 16, dropout 0.0, targeting q_proj, k_proj, v_proj, o_proj, gate_proj, up_proj, down_proj
  • Trainable params: 8,912,896 / 3,844,934,656 (0.23%)
  • Batch size: 1, gradient accumulation 8 (effective batch size 8)
  • Epochs: 3, learning rate 2e-4, linear schedule, adamw_8bit optimizer
  • Hardware: single NVIDIA RTX 3070, 8GB VRAM, ~91 minutes total training time
  • Final train loss: ~1.01 (down from ~2.28 at start)

Trained with trl's SFTTrainer on chat-formatted examples (system prompt + article as user message + target JSON as assistant message), using the base model's native chat template.

How to use

from unsloth import FastLanguageModel

model, tokenizer = FastLanguageModel.from_pretrained(
    model_name="ritam-m/phi4-mini-entities-topics-v1",
    max_seq_length=3072,
    load_in_4bit=True,
)
FastLanguageModel.for_inference(model)

messages = [
    {"role": "system", "content": SYSTEM_PROMPT},  # see below
    {"role": "user", "content": f"TITLE: {title}\n\nBODY:\n{body}\n\n---\n{taxonomy_block}"},
]
inputs = tokenizer.apply_chat_template(
    messages, tokenize=True, add_generation_prompt=True, return_tensors="pt"
).to(model.device)
outputs = model.generate(input_ids=inputs, max_new_tokens=1500, temperature=0.0, do_sample=False)
print(tokenizer.decode(outputs[0][inputs.shape[1]:], skip_special_tokens=True))

The system prompt and topic/entity taxonomy used during training must be reproduced at inference time — this adapter was trained against one exact prompt format and will degrade if you change it materially. See the compare_one.py / build_comparison_sheet.py scripts in the source repo for the exact prompt text.

To get zero-shot (base model) behavior for comparison, wrap generation in with model.disable_adapter(): ... rather than loading a separate model.

Known limitations

  • JSON truncation on long entity lists: articles with many entities (>~20) can exceed the max_new_tokens budget before the JSON closes, producing unparseable output. Increase max_new_tokens for entity-dense articles.
  • Occasional repetition collapse: on a small number of articles, generation degenerates into repeating a single token/phrase indefinitely under greedy decoding (temperature=0.0) rather than completing the JSON. Not yet root-caused; a non-zero temperature retry is a plausible mitigation but untested.
  • Narrow scope by design: this adapter does not extract events, relationships, sentiment, or summaries — only entities and topics. It was deliberately not trained on the full schema (see "Why this exists" above).
  • Geography-specific: the taxonomy and prompt are built specifically for Indian financial markets news; entity types like REGULATOR/GOVT_BODY and the topic taxonomy assume that context.
  • Evaluated only via a small (15-article) blind qualitative comparison against the zero-shot base model, reviewed by a human analyst — not a quantitative benchmark (e.g. no F1/precision-recall scoring against ground truth at scale).
Downloads last month
20
Inference Providers NEW
This model isn't deployed by any Inference Provider. 🙋 Ask for provider support

Model tree for ritam-m/phi4-mini-entities-topics-v1

Adapter
(11)
this model