ai-helpdesk-api / services /classifier_v3.py
github-actions
deploy: sync from github main
6d32f12
Raw
History Blame Contribute Delete
2.96 kB
import os
import torch
import torch.nn as nn
import pickle
import json
from transformers import BertTokenizerFast, BertModel
# Paths
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
MODEL_DIR = os.path.join(BASE_DIR, "models", "classifier-v3")
class MultiOutputClassifierV3(nn.Module):
def __init__(self, num_labels_per_output: dict):
super().__init__()
self.bert = BertModel.from_pretrained("bert-base-uncased")
hidden = self.bert.config.hidden_size
self.dropout = nn.Dropout(0.3)
self.heads = nn.ModuleDict()
for name, n_labels in num_labels_per_output.items():
self.heads[name] = nn.Sequential(
nn.Linear(hidden, 256),
nn.ReLU(),
nn.Dropout(0.1),
nn.Linear(256, n_labels)
)
def forward(self, input_ids, attention_mask):
outputs = self.bert(input_ids=input_ids, attention_mask=attention_mask)
pooled_output = outputs.pooler_output
pooled_output = self.dropout(pooled_output)
logits = {name: head(pooled_output) for name, head in self.heads.items()}
return logits
class ClassifierServiceV3:
def __init__(self):
self.device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
self.model = None
config_path = os.path.join(MODEL_DIR, "model_config.json")
if not os.path.exists(config_path):
print(f"[V3 Service] Model not found yet at {MODEL_DIR}")
return
with open(config_path, "r") as f:
self.num_labels = json.load(f)
with open(os.path.join(MODEL_DIR, "label_encoders.pkl"), "rb") as f:
self.label_encoders = pickle.load(f)
self.model = MultiOutputClassifierV3(self.num_labels).to(self.device)
self.model.load_state_dict(torch.load(os.path.join(MODEL_DIR, "model.pt"), map_location=self.device))
self.model.eval()
self.tokenizer = BertTokenizerFast.from_pretrained(MODEL_DIR)
print("[INFO] Classifier Service V3 (Power Model) Loaded.")
def predict(self, text: str):
if self.model is None: return {"error": "V3 Model not loaded"}
inputs = self.tokenizer(text, return_tensors="pt", truncation=True, padding=True, max_length=256).to(self.device)
with torch.no_grad():
logits = self.model(inputs["input_ids"], inputs["attention_mask"])
results = {}
for col, le in self.label_encoders.items():
probs = torch.softmax(logits[col], dim=1)
conf, pred_idx = torch.max(probs, dim=1)
results[col] = {
"prediction": le.inverse_transform([pred_idx.item()])[0],
"confidence": float(conf.item())
}
if "Priority" in results: results["priority"] = results.pop("Priority")
return results
classifier_v3 = ClassifierServiceV3()