A hybrid news article classification system that combines TF-IDF shortlisting with fine-tuned Mistral-7B-LoRA inference. Articles are classified into 17 categories using a two-stage pipeline that balances speed and accuracy, served via a Django REST API with an interactive web UI.
- TF-IDF Shortlist — Word and character n-gram vectorizers rank the top-N most likely categories using cosine similarity against pre-trained label centroids.
- LLM Inference — A 4-bit quantized Mistral-7B-Instruct model (LoRA-adapted) performs restricted decoding, scoring only the shortlisted candidate tokens. This prevents hallucinations and keeps inference fast.
- Chunk Fusion — Long articles are split into overlapping windows. Each window is scored independently, then logits are fused across chunks using a configurable pooling strategy.
| Letter | Category | Letter | Category |
|---|---|---|---|
| A | arts | J | lifestyle |
| B | crime | K | politics |
| C | disaster | L | religion |
| D | economy | M | science |
| E | education | N | social |
| F | environmental | O | sport |
| G | health | P | unrest |
| H | humanInterest | Q | weather |
| I | labour |
- Backend: Django 6, Django REST Framework
- ML: PyTorch, Hugging Face Transformers, PEFT (LoRA), scikit-learn, SciPy
- Quantization: BitsAndBytes (4-bit NF4, double quant)
- Base Model:
mistralai/Mistral-7B-Instruct-v0.3 - Frontend: Vanilla HTML/CSS/JavaScript
llm_classifier_interface/
├── llm_classifier_interface/
│ ├── interface.py # Core ML pipeline (TF-IDF + LLM)
│ ├── views.py # Django endpoints
│ ├── urls.py # URL routing
│ ├── settings.py # Django config
│ ├── tfidf_assets/ # Vectorizers, centroids, meta.json
│ └── mistral_lora_06012026/# LoRA adapter weights + tokenizer
├── templates/
│ └── index.html # Web UI
└── manage.py
pip install django djangorestframework torch transformers peft bitsandbytes scikit-learn scipy joblibNote: BitsAndBytes 4-bit quantization requires a CUDA-capable GPU with at least ~6GB VRAM.
Place the following in the expected directories (or override via environment variables):
llm_classifier_interface/mistral_lora_06012026/— LoRA adapter weights and tokenizer configllm_classifier_interface/tfidf_assets/—tfidf_word.joblib,tfidf_char.joblib,centroids/,meta.json
Override paths with environment variables:
export LORA_DIR=/path/to/lora_adapter
export TFIDF_DIR=/path/to/tfidf_assetspython manage.py migrate
python manage.py runserverThe model loads on the first request and is reused for all subsequent requests.
Open http://localhost:8000/ in a browser. Paste an article, adjust parameters, and click Classify.
POST /api/predict/
Content-Type: application/x-www-form-urlencoded
| Parameter | Type | Default | Description |
|---|---|---|---|
text |
string | required | Article text to classify |
topN |
int | 5 |
Number of TF-IDF candidate labels to shortlist |
k_chunks |
int | 4 |
Number of top-scoring chunks to run through LLM |
mode |
string | hybrid |
Logit fusion mode: max, sum, or hybrid |
alpha |
float | 0.7 |
Weight of sum in hybrid mode (sum·α + max·(1-α)) |
Example response:
{
"label": "politics",
"confidence": 0.8731,
"candidates": [
{"label": "politics", "score": 0.412},
{"label": "economy", "score": 0.289},
{"label": "social", "score": 0.201}
]
}- Restricted decoding: The model only scores the 17 valid category tokens — output is always a valid label.
- Chunk ranking: Windows are ranked by TF-IDF relevance before LLM inference; only the top
k_chunksare processed, keeping latency bounded regardless of article length. - Singleton model:
NewsClassifierServiceis instantiated once per process and shared across all requests. - Hybrid fusion:
mode=hybridblendssumpooling (rewards consistent agreement across chunks) withmaxpooling (rewards a single strong signal), controlled byalpha.