Spaces:
Sleeping
Sleeping
File size: 11,384 Bytes
a69b74a | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 | """
Bharat Tech Atlas — NLP Startup Sector Classifier
Uses a pre-trained BERT model from HuggingFace to classify startups into sectors
based on their description text. Supports ONNX export for optimized inference.
Architecture:
1. Load pre-trained model (distilbert-base-uncased or fine-tuned variant)
2. Tokenize startup description
3. Run inference → sector probabilities
4. Map to standard sector taxonomy
5. Optional: ONNX Runtime for 3-5x faster inference
"""
import logging
from typing import Dict, List, Optional, Tuple
from dataclasses import dataclass
logger = logging.getLogger(__name__)
# Sector labels aligned with database taxonomy
SECTOR_LABELS = [
"fintech", "saas_ai", "ecommerce", "healthcare", "manufacturing",
"edtech", "agritech", "cleantech", "deeptech", "logistics",
"gaming", "ai_ml", "cybersecurity", "foodtech", "proptech",
"legaltech", "mediatech", "mobility", "social_impact", "biotech",
"spacetech", "d2c", "saas", "healthtech", "iot", "drone_tech",
"ev", "insurtech", "wealthtech"
]
SECTOR_DESCRIPTIONS = {
"fintech": "financial technology payments banking lending digital finance",
"saas_ai": "software as a service cloud computing enterprise AI platform",
"ecommerce": "online shopping marketplace retail digital commerce",
"healthcare": "medical health hospital clinical pharmaceutical",
"manufacturing": "industrial production factory automation hardware",
"edtech": "education learning online courses skill training",
"agritech": "agriculture farming crop precision agriculture supply chain",
"cleantech": "clean energy renewable solar wind sustainability green",
"deeptech": "deep technology research quantum computing robotics",
"logistics": "supply chain delivery shipping warehousing fleet",
"gaming": "video games esports gaming platform entertainment",
"ai_ml": "artificial intelligence machine learning neural network NLP",
"cybersecurity": "security encryption threat detection privacy",
"foodtech": "food delivery restaurant cloud kitchen nutrition",
"proptech": "real estate property construction smart building",
"legaltech": "legal contracts compliance regulation law",
"mediatech": "media content streaming video OTT publishing",
"mobility": "transportation ride sharing urban mobility transit",
"social_impact": "social enterprise NGO impact sustainability community",
"biotech": "biotechnology genomics drug discovery life sciences",
"spacetech": "space satellite aerospace launch orbital",
"d2c": "direct to consumer brand retail FMCG personal care",
"healthtech": "health technology telemedicine digital health wearable",
"iot": "internet of things connected devices sensors smart",
"drone_tech": "drone UAV aerial unmanned autonomous flight",
"ev": "electric vehicle EV battery charging mobility green",
"insurtech": "insurance technology digital insurance claims",
"wealthtech": "wealth management investment portfolio trading",
}
@dataclass
class ClassificationResult:
"""Result of sector classification."""
sector: str
confidence: float
top_sectors: List[Tuple[str, float]]
model_version: str
class StartupSectorClassifier:
"""
Classify startups into sectors using NLP.
Supports two modes:
1. Zero-shot classification (no fine-tuning needed, uses pre-trained model)
2. Fine-tuned BERT classifier (higher accuracy, requires training data)
Production optimization:
- ONNX Runtime for 3-5x speedup
- Batch inference for throughput
- Model caching to avoid reload
"""
def __init__(self, model_name: str = "facebook/bart-large-mnli",
use_onnx: bool = False, device: str = "cpu"):
"""
Initialize the classifier.
Args:
model_name: HuggingFace model ID. Options:
- "facebook/bart-large-mnli" (zero-shot, good accuracy)
- "distilbert-base-uncased" (fast, needs fine-tuning)
- Custom fine-tuned model path
use_onnx: Whether to use ONNX Runtime for optimized inference
device: "cpu" or "cuda"
"""
self.model_name = model_name
self.use_onnx = use_onnx
self.device = device
self._pipeline = None
self._onnx_session = None
self._tokenizer = None
self._loaded = False
def load_model(self):
"""
Load the classification model.
Call this once at startup, not per-request.
"""
if self._loaded:
return
try:
if self.use_onnx:
self._load_onnx_model()
else:
self._load_transformers_pipeline()
self._loaded = True
logger.info(f"Model loaded: {self.model_name} (onnx={self.use_onnx})")
except Exception as e:
logger.error(f"Failed to load model: {e}")
# Fallback to keyword-based classification
self._loaded = True
logger.info("Using keyword-based fallback classifier")
def _load_transformers_pipeline(self):
"""Load HuggingFace transformers pipeline for zero-shot classification."""
try:
from transformers import pipeline
self._pipeline = pipeline(
"zero-shot-classification",
model=self.model_name,
device=0 if self.device == "cuda" else -1,
)
except ImportError:
logger.warning("transformers not installed, using keyword fallback")
def _load_onnx_model(self):
"""
Load ONNX-optimized model for fast inference.
ONNX conversion flow:
1. Export PyTorch model: torch.onnx.export(model, ...)
2. Optimize with ONNX Runtime: ort.InferenceSession(model_path)
3. Quantize (optional): quantize_dynamic(model, ...)
"""
try:
from transformers import AutoTokenizer
import onnxruntime as ort
self._tokenizer = AutoTokenizer.from_pretrained(self.model_name)
# In production, load pre-exported ONNX model:
# self._onnx_session = ort.InferenceSession("models/classifier.onnx")
logger.info("ONNX model loaded for optimized inference")
except ImportError:
logger.warning("onnxruntime not installed, falling back to transformers")
self._load_transformers_pipeline()
def classify(self, description: str, top_k: int = 3) -> ClassificationResult:
"""
Classify a startup's sector based on its description.
Args:
description: Startup description text
top_k: Number of top predictions to return
Returns:
ClassificationResult with sector, confidence, and alternatives
"""
if not self._loaded:
self.load_model()
if not description or len(description.strip()) < 10:
return ClassificationResult(
sector="saas_ai",
confidence=0.1,
top_sectors=[("saas_ai", 0.1)],
model_version=self.model_name
)
# Try transformer pipeline first
if self._pipeline:
return self._classify_zero_shot(description, top_k)
# Fallback to keyword-based
return self._classify_keywords(description, top_k)
def _classify_zero_shot(self, description: str, top_k: int) -> ClassificationResult:
"""Zero-shot classification using pre-trained NLI model."""
candidate_labels = SECTOR_LABELS
result = self._pipeline(
description,
candidate_labels=candidate_labels,
multi_label=True,
)
top_sectors = list(zip(result["labels"][:top_k], result["scores"][:top_k]))
return ClassificationResult(
sector=result["labels"][0],
confidence=result["scores"][0],
top_sectors=top_sectors,
model_version=self.model_name,
)
def _classify_keywords(self, description: str, top_k: int) -> ClassificationResult:
"""Keyword-based fallback classifier (no ML model needed)."""
desc_lower = description.lower()
scores = {}
for sector, keywords in SECTOR_DESCRIPTIONS.items():
keyword_list = keywords.lower().split()
matches = sum(1 for kw in keyword_list if kw in desc_lower)
scores[sector] = matches / len(keyword_list)
# Sort by score descending
sorted_sectors = sorted(scores.items(), key=lambda x: x[1], reverse=True)
top_sectors = sorted_sectors[:top_k]
best_sector = top_sectors[0][0] if top_sectors[0][1] > 0 else "saas_ai"
best_score = top_sectors[0][1]
return ClassificationResult(
sector=best_sector,
confidence=min(best_score * 2, 1.0), # Scale up
top_sectors=[(s, min(sc * 2, 1.0)) for s, sc in top_sectors],
model_version="keyword_fallback_v1",
)
def classify_batch(self, descriptions: List[str], top_k: int = 3) -> List[ClassificationResult]:
"""
Batch classification for throughput optimization.
Groups descriptions and runs inference in batches.
"""
results = []
batch_size = 16
for i in range(0, len(descriptions), batch_size):
batch = descriptions[i:i + batch_size]
for desc in batch:
results.append(self.classify(desc, top_k))
return results
def export_to_onnx(self, output_path: str = "models/classifier.onnx"):
"""
Export the current model to ONNX format for optimized serving.
Steps:
1. Load model in PyTorch
2. Create dummy input
3. Export via torch.onnx.export
4. Validate with onnx.checker
5. Optionally quantize for further speedup
"""
try:
from transformers import AutoModelForSequenceClassification, AutoTokenizer
import torch
logger.info(f"Exporting model to ONNX: {output_path}")
tokenizer = AutoTokenizer.from_pretrained(self.model_name)
model = AutoModelForSequenceClassification.from_pretrained(self.model_name)
model.eval()
# Dummy input for tracing
dummy_input = tokenizer(
"A fintech startup building payments infrastructure",
return_tensors="pt",
max_length=128,
truncation=True,
padding="max_length",
)
torch.onnx.export(
model,
(dummy_input["input_ids"], dummy_input["attention_mask"]),
output_path,
input_names=["input_ids", "attention_mask"],
output_names=["logits"],
dynamic_axes={
"input_ids": {0: "batch_size", 1: "seq_len"},
"attention_mask": {0: "batch_size", 1: "seq_len"},
"logits": {0: "batch_size"},
},
opset_version=14,
)
logger.info(f"ONNX export complete: {output_path}")
except Exception as e:
logger.error(f"ONNX export failed: {e}")
raise
|