Update README.md
Browse files
README.md
CHANGED
|
@@ -1,4 +1,159 @@
|
|
| 1 |
---
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2 |
datasets:
|
| 3 |
-
- Nasim435/
|
| 4 |
-
---
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
---
|
| 2 |
+
language:
|
| 3 |
+
- en
|
| 4 |
+
license: mit
|
| 5 |
+
library_name: catboost
|
| 6 |
+
pipeline_tag: text-classification
|
| 7 |
+
tags:
|
| 8 |
+
- prompt-routing
|
| 9 |
+
- llm-routing
|
| 10 |
+
- multi-label-classification
|
| 11 |
+
- prompt-complexity
|
| 12 |
+
- catboost
|
| 13 |
+
- scikit-learn
|
| 14 |
+
- model-router
|
| 15 |
+
- token-budgeting
|
| 16 |
+
pretty_name: Prompt Router (CatBoost Multi-Label Classifier)
|
| 17 |
datasets:
|
| 18 |
+
- Nasim435/Multi-label-Prompt-Dataset
|
| 19 |
+
---
|
| 20 |
+
|
| 21 |
+
# Multi-Label Prompt Classifier
|
| 22 |
+
|
| 23 |
+
A fast, lightweight multi-label machine learning model designed for prompt complexity estimation, task intent classification, output token length forecasting, and dynamic LLM routing. The model executes inference in **< 10ms on CPU** with **zero GPU dependencies**.
|
| 24 |
+
|
| 25 |
+
## Model Summary
|
| 26 |
+
|
| 27 |
+
- **Architecture**: Scikit-Learn `OneVsRestClassifier` ensemble of 23 binary `CatBoostClassifier` estimators
|
| 28 |
+
- **Feature Pipeline**: 5,000 TF-IDF features (unigram + bigram) combined with 19 handcrafted structural/semantic text features
|
| 29 |
+
- **Number of Target Classes**: 23 multi-label categories across 4 semantic dimensions
|
| 30 |
+
- **Inference Latency**: < 10ms per prompt on standard CPU
|
| 31 |
+
- **Memory Footprint**: ~13 MB model weights
|
| 32 |
+
- **Primary Use Case**: Classifying raw user prompts to route them to the most cost-effective LLM tier and enforce pre-inference token budgets without calling an auxiliary LLM.
|
| 33 |
+
|
| 34 |
+
## Model Files & Artifacts
|
| 35 |
+
|
| 36 |
+
The repository contains four serialized artifacts:
|
| 37 |
+
|
| 38 |
+
| File | Size | Description |
|
| 39 |
+
|:---|:---:|:---|
|
| 40 |
+
| **`feature_extractor.pkl`** | 211 KB | Scikit-Learn transformer pipeline combining 5,000 TF-IDF n-gram features with 19 structural heuristics (sentence count, code blocks, math symbols, domain keywords). |
|
| 41 |
+
| **`prompt_router.pkl`** | 13.0 MB | Trained `OneVsRestClassifier` wrapping 23 individual `CatBoostClassifier` models (iterations=300, depth=6, learning_rate=0.1). |
|
| 42 |
+
| **`label_binarizer.pkl`** | 826 B | Fitted Scikit-Learn `MultiLabelBinarizer` mapping categorical label names to binary arrays. |
|
| 43 |
+
| **`thresholds.npy`** | 312 B | Optimal decision threshold matrix ($t_{\text{opt}}$) tuned per class to maximize individual F1 scores. |
|
| 44 |
+
|
| 45 |
+
## Target Classes (23 Multi-Label Tags)
|
| 46 |
+
|
| 47 |
+
The model predicts across 23 categorical dimensions simultaneously:
|
| 48 |
+
|
| 49 |
+
1. **Complexity Tier**: `easy`, `moderate`, `hard`
|
| 50 |
+
2. **Reasoning Depth**: `reasoning-light`, `reasoning-moderate`, `reasoning-intensive`
|
| 51 |
+
3. **Expected Output Token Length**: `short-output` ($\le 200$), `medium-output` ($\approx 500$), `long-output` ($\ge 1,200$)
|
| 52 |
+
4. **Execution Priority & Compute Tier**: `cheap`, `balanced`, `premium`, `realtime`, `interactive`, `background`
|
| 53 |
+
5. **Task & Domain Intent**: `coding`, `debugging`, `infrastructure`, `architecture`, `architecture-heavy`, `mlops`, `analysis`, `research`
|
| 54 |
+
|
| 55 |
+
## Evaluation & Benchmark Performance
|
| 56 |
+
|
| 57 |
+
Evaluated on an independent 20% holdout test set (372 samples):
|
| 58 |
+
|
| 59 |
+
| Metric | Baseline ($t=0.50$) | Tuned Thresholds ($t=t_{\text{opt}}$) | Relative Change |
|
| 60 |
+
|:---|:---:|:---:|:---:|
|
| 61 |
+
| **Macro F1 Score** | **0.8094** | **0.8320** | **+2.79%** |
|
| 62 |
+
| **Micro F1 Score** | **0.8282** | **0.8419** | **+1.65%** |
|
| 63 |
+
| **Weighted F1 Score** | **0.8300** | **0.8447** | **+1.77%** |
|
| 64 |
+
| **Hamming Loss** | **0.0907** | **0.0840** | **-7.39% (Lower is better)** |
|
| 65 |
+
| **Inference Latency** | **< 10ms** | **< 10ms** | **CPU Real-Time** |
|
| 66 |
+
|
| 67 |
+
### Per-Class Evaluation Breakdown
|
| 68 |
+
|
| 69 |
+
| Label | Precision | Recall | F1-Score | Optimal Threshold ($t_{\text{opt}}$) | Test Support |
|
| 70 |
+
|:---|:---:|:---:|:---:|:---:|:---:|
|
| 71 |
+
| `architecture-heavy` | 1.00 | 0.90 | **0.95** | 0.40 | 29 |
|
| 72 |
+
| `interactive` | 0.91 | 0.98 | **0.94** | 0.35 | 230 |
|
| 73 |
+
| `mlops` | 1.00 | 0.85 | **0.92** | 0.45 | 27 |
|
| 74 |
+
| `hard` | 0.92 | 0.90 | **0.91** | 0.50 | 136 |
|
| 75 |
+
| `reasoning-intensive` | 0.92 | 0.90 | **0.91** | 0.50 | 136 |
|
| 76 |
+
| `realtime` | 0.90 | 0.92 | **0.91** | 0.40 | 48 |
|
| 77 |
+
| `background` | 0.93 | 0.85 | **0.89** | 0.55 | 91 |
|
| 78 |
+
| `long-output` | 0.94 | 0.86 | **0.89** | 0.55 | 104 |
|
| 79 |
+
| `premium` | 0.84 | 0.93 | **0.88** | 0.40 | 114 |
|
| 80 |
+
| `medium-output` | 0.85 | 0.92 | **0.88** | 0.40 | 177 |
|
| 81 |
+
| `debugging` | 0.90 | 0.80 | **0.85** | 0.50 | 46 |
|
| 82 |
+
| `short-output` | 0.86 | 0.81 | **0.84** | 0.50 | 91 |
|
| 83 |
+
| `coding` | 0.77 | 0.90 | **0.83** | 0.40 | 105 |
|
| 84 |
+
| `easy` | 0.88 | 0.77 | **0.82** | 0.55 | 96 |
|
| 85 |
+
| `reasoning-light` | 0.88 | 0.76 | **0.82** | 0.55 | 96 |
|
| 86 |
+
| `cheap` | 0.82 | 0.79 | **0.80** | 0.50 | 90 |
|
| 87 |
+
| `balanced` | 0.75 | 0.83 | **0.79** | 0.45 | 122 |
|
| 88 |
+
| `moderate` | 0.67 | 0.89 | **0.77** | 0.35 | 140 |
|
| 89 |
+
| `reasoning-moderate` | 0.65 | 0.92 | **0.76** | 0.35 | 140 |
|
| 90 |
+
| `research` | 0.71 | 0.77 | **0.74** | 0.45 | 22 |
|
| 91 |
+
| `infrastructure` | 0.62 | 0.83 | **0.71** | 0.35 | 77 |
|
| 92 |
+
| `analysis` | 0.56 | 0.85 | **0.68** | 0.35 | 41 |
|
| 93 |
+
| `architecture` | 0.80 | 0.56 | **0.66** | 0.55 | 43 |
|
| 94 |
+
|
| 95 |
+
## Quick Start & Inference
|
| 96 |
+
|
| 97 |
+
### Installation
|
| 98 |
+
|
| 99 |
+
```bash
|
| 100 |
+
pip install catboost scikit-learn numpy pandas joblib scipy
|
| 101 |
+
```
|
| 102 |
+
|
| 103 |
+
### Loading and Predicting
|
| 104 |
+
|
| 105 |
+
```python
|
| 106 |
+
import joblib
|
| 107 |
+
import numpy as np
|
| 108 |
+
import pandas as pd
|
| 109 |
+
|
| 110 |
+
# 1. Load serialized artifacts
|
| 111 |
+
feature_extractor = joblib.load("feature_extractor.pkl")
|
| 112 |
+
classifier = joblib.load("prompt_router.pkl")
|
| 113 |
+
mlb = joblib.load("label_binarizer.pkl")
|
| 114 |
+
thresholds = np.load("thresholds.npy")
|
| 115 |
+
|
| 116 |
+
def predict_prompt_labels(prompt: str, return_scores: bool = False):
|
| 117 |
+
# Transform input text into combined TF-IDF + structural feature matrix
|
| 118 |
+
X = feature_extractor.transform(pd.Series([prompt]))
|
| 119 |
+
|
| 120 |
+
# Predict probabilities for each binary classifier in the ensemble
|
| 121 |
+
probs = np.array(classifier.predict_proba(X))
|
| 122 |
+
scores = np.array([p[0][1] if np.ndim(p) == 2 else p[1] for p in probs])
|
| 123 |
+
|
| 124 |
+
# Apply calibrated decision thresholds
|
| 125 |
+
predictions = (scores >= thresholds).astype(int)
|
| 126 |
+
|
| 127 |
+
# Fallback to top-scoring class if no threshold is met
|
| 128 |
+
if predictions.sum() == 0:
|
| 129 |
+
predictions[np.argmax(scores)] = 1
|
| 130 |
+
|
| 131 |
+
labels = list(mlb.inverse_transform(predictions.reshape(1, -1))[0])
|
| 132 |
+
|
| 133 |
+
if return_scores:
|
| 134 |
+
score_dict = {label: round(float(score), 4) for label, score in zip(mlb.classes_, scores)}
|
| 135 |
+
return labels, score_dict
|
| 136 |
+
|
| 137 |
+
return labels
|
| 138 |
+
|
| 139 |
+
# Example usage
|
| 140 |
+
query = "Design a distributed real-time fraud detection pipeline with Apache Flink and Kafka."
|
| 141 |
+
labels, scores = predict_prompt_labels(query, return_scores=True)
|
| 142 |
+
print("Predicted labels:", labels)
|
| 143 |
+
# Output: ['architecture-heavy', 'hard', 'infrastructure', 'interactive', 'long-output', 'premium', 'realtime', 'reasoning-intensive']
|
| 144 |
+
```
|
| 145 |
+
|
| 146 |
+
## Intended Use & Integration
|
| 147 |
+
|
| 148 |
+
- **LLM Routing Middleware**: Classify incoming prompts to route between small/nano (e.g. 8B–9B), medium (e.g. 30B–70B), and large/frontier (e.g. 120B–405B) models.
|
| 149 |
+
- **Pre-Inference Token Budgeting**: Forecast expected output token lengths (`short-output`, `medium-output`, `long-output`) before generation to prevent token overspend.
|
| 150 |
+
- **Domain Specialization**: Direct code queries to coding models, debugging queries to specialized debug agents, and theoretical research questions to reasoning models.
|
| 151 |
+
|
| 152 |
+
## Limitations
|
| 153 |
+
|
| 154 |
+
- **Domain Scope**: The training dataset focuses on technical engineering prompts (software engineering, cloud infrastructure, mathematics, algorithms). Predictions on general casual conversation or creative fiction may be less accurate.
|
| 155 |
+
- **Language**: English prompts only (`language: en`).
|
| 156 |
+
|
| 157 |
+
## License
|
| 158 |
+
|
| 159 |
+
This model is distributed under the **MIT License**.
|