ahzamkidwai commited on
Commit
df1ed9d
·
1 Parent(s): 4eb56a9

Deploy model-service from GitHub

Browse files
Files changed (4) hide show
  1. .gitignore +43 -0
  2. Dockerfile +13 -0
  3. app.py +144 -0
  4. requirements.txt +31 -0
.gitignore ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Python cache
2
+ __pycache__/
3
+ *.py[cod]
4
+ *.pyo
5
+ *.pyd
6
+
7
+ # Virtual environments
8
+ .env
9
+ .venv
10
+ env/
11
+ venv/
12
+ ENV/
13
+
14
+ # FastAPI / Uvicorn
15
+ *.log
16
+
17
+ # Jupyter / IPython
18
+ .ipynb_checkpoints
19
+ *.ipynb
20
+
21
+ # Pytest / Coverage
22
+ .pytest_cache/
23
+ .coverage
24
+ htmlcov/
25
+
26
+ # IDE files
27
+ .vscode/
28
+ .idea/
29
+ *.swp
30
+
31
+ # System files
32
+ .DS_Store
33
+ Thumbs.db
34
+
35
+ # Model / Large files
36
+ *.pt
37
+ *.bin
38
+ *.h5
39
+ *.ckpt
40
+
41
+ # Hugging Face cache
42
+ ~/.cache/huggingface
43
+ .cache/
Dockerfile ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # ---------- Runtime Stage ----------
2
+ FROM python:3.12-slim
3
+
4
+ WORKDIR /app
5
+
6
+ # Copy installed packages from builder stage
7
+ COPY requirements.txt .
8
+ RUN pip install --no-cache-dir -r requirements.txt
9
+
10
+ COPY . .
11
+
12
+ # Hugging Face provides $PORT automatically
13
+ CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "7860"]
app.py ADDED
@@ -0,0 +1,144 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import FastAPI
2
+ from pydantic import BaseModel
3
+ from typing import List, Optional
4
+ from transformers import AutoTokenizer, AutoModelForTokenClassification, pipeline
5
+ import torch
6
+
7
+ # ----------------------------
8
+ # FastAPI Initialization
9
+ # ----------------------------
10
+ app = FastAPI(title="Resume NER Service", version="2.0")
11
+
12
+ # ----------------------------
13
+ # Load Model
14
+ # ----------------------------
15
+ MODEL_NAME = "yashpwr/resume-ner-bert-v2"
16
+
17
+ print("Loading model... (this may take a minute)")
18
+ tokenizer = AutoTokenizer.from_pretrained(MODEL_NAME)
19
+ model = AutoModelForTokenClassification.from_pretrained(MODEL_NAME)
20
+
21
+ # Hugging Face pipeline (simple mode)
22
+ ner_pipeline = pipeline(
23
+ "token-classification",
24
+ model=model,
25
+ tokenizer=tokenizer,
26
+ aggregation_strategy="simple"
27
+ )
28
+
29
+ print("Model loaded successfully!")
30
+
31
+ # ----------------------------
32
+ # Request & Response Schemas
33
+ # ----------------------------
34
+ class ResumeText(BaseModel):
35
+ text: str
36
+ confidence_threshold: float = 0.5
37
+ mode: str = "simple" # "simple" | "advanced"
38
+
39
+ class Entity(BaseModel):
40
+ label: str
41
+ text: str
42
+ start: int
43
+ end: int
44
+ confidence: float
45
+
46
+ # ----------------------------
47
+ # Advanced Extraction Function
48
+ # ----------------------------
49
+ def extract_entities_with_confidence(text: str, confidence_threshold: float = 0.5):
50
+ """Custom NER extraction with confidence + offsets."""
51
+ inputs = tokenizer(
52
+ text,
53
+ return_tensors="pt",
54
+ truncation=True,
55
+ max_length=256,
56
+ padding=True,
57
+ return_offsets_mapping=True
58
+ )
59
+
60
+ with torch.no_grad():
61
+ outputs = model(**inputs)
62
+ predictions = torch.argmax(outputs.logits, dim=2)
63
+ probabilities = torch.softmax(outputs.logits, dim=2)
64
+
65
+ entities = []
66
+ current_entity = None
67
+ offset_mapping = inputs["offset_mapping"][0]
68
+
69
+ for i, (pred, offset) in enumerate(zip(predictions[0], offset_mapping)):
70
+ label = model.config.id2label[pred.item()]
71
+ confidence = probabilities[0][i][pred].item()
72
+
73
+ # Skip special tokens
74
+ if offset[0] == 0 and offset[1] == 0:
75
+ continue
76
+
77
+ if label.startswith("B-"):
78
+ if current_entity and current_entity["confidence"] >= confidence_threshold:
79
+ entities.append(current_entity)
80
+
81
+ entity_type = label[2:]
82
+ current_entity = {
83
+ "label": entity_type,
84
+ "text": text[offset[0]:offset[1]],
85
+ "start": offset[0],
86
+ "end": offset[1],
87
+ "confidence": confidence,
88
+ }
89
+
90
+ elif label.startswith("I-") and current_entity:
91
+ entity_type = label[2:]
92
+ if entity_type == current_entity["label"]:
93
+ current_entity["text"] += " " + text[offset[0]:offset[1]]
94
+ current_entity["end"] = offset[1]
95
+ current_entity["confidence"] = min(current_entity["confidence"], confidence)
96
+
97
+ elif label == "O":
98
+ if current_entity and current_entity["confidence"] >= confidence_threshold:
99
+ entities.append(current_entity)
100
+ current_entity = None
101
+
102
+ if current_entity and current_entity["confidence"] >= confidence_threshold:
103
+ entities.append(current_entity)
104
+
105
+ return entities
106
+
107
+ # ----------------------------
108
+ # API Endpoint
109
+ # ----------------------------
110
+ @app.post("/extract", response_model=List[Entity])
111
+ def extract_entities(resume: ResumeText):
112
+ """
113
+ Extract entities from resume text.
114
+ Mode = "simple" -> uses pipeline
115
+ Mode = "advanced" -> custom extraction with confidence scores
116
+ """
117
+ if resume.mode == "simple":
118
+ results = ner_pipeline(resume.text)
119
+ entities = [
120
+ Entity(
121
+ label=r["entity_group"],
122
+ text=r["word"],
123
+ start=r["start"],
124
+ end=r["end"],
125
+ confidence=r["score"],
126
+ )
127
+ for r in results if r["score"] >= resume.confidence_threshold
128
+ ]
129
+ else:
130
+ entities = [
131
+ Entity(**entity)
132
+ for entity in extract_entities_with_confidence(
133
+ resume.text, resume.confidence_threshold
134
+ )
135
+ ]
136
+
137
+ return entities
138
+
139
+ # ----------------------------
140
+ # Health Check
141
+ # ----------------------------
142
+ @app.get("/health")
143
+ def health_check():
144
+ return {"status": "OK", "model": MODEL_NAME}
requirements.txt ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # fastapi
2
+ # uvicorn
3
+ # transformers
4
+ # torch
5
+ # pydantic
6
+
7
+
8
+ fastapi
9
+ uvicorn[standard]
10
+ transformers
11
+ torch
12
+ pydantic
13
+ python-multipart
14
+ accelerate
15
+
16
+
17
+
18
+ # Web framework
19
+ # fastapi>=0.110.0
20
+ # uvicorn[standard]>=0.29.0
21
+
22
+ # ML / Hugging Face
23
+ # transformers>=4.40.0
24
+ # torch>=2.2.0
25
+
26
+ # Data validation
27
+ # pydantic>=2.7.0
28
+
29
+ # Optional: useful extras
30
+ # python-multipart # if later you want to upload files (PDF, DOCX)
31
+ # gunicorn # alternative production server