Skip to content

Commit 5a85b8b

Browse files
author
Themba Gqaza
committed
added training results
1 parent 75f9104 commit 5a85b8b

18 files changed

Lines changed: 689 additions & 1166 deletions

Makefile

Lines changed: 16 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -5,58 +5,54 @@
55
PYTHON := python3
66
SRC := src
77
DATA := $(SRC)/data
8+
MAPPED_DATA := $(SRC)/mapped_data
89
ARTIFACTS := $(SRC)/artifacts
910

1011
VENV := .venv
1112
UV := uv
1213

13-
# Default encoder (can override: make train ENCODER=all-MiniLM-L6-v2)
14-
ENCODER ?= BAAI/bge-m3
15-
16-
.PHONY: help venv install lint typecheck test datasets train thresholds serve clean
14+
.PHONY: help install lint typecheck test datasets train serve serve-dev clean
1715

1816
help:
1917
@echo "MomConnect Intent Classifier v2.0"
2018
@echo "--------------------------------"
21-
@echo "make venv - create virtualenv with uv"
2219
@echo "make install - install deps"
2320
@echo "make lint - run ruff lint + format check"
2421
@echo "make typecheck - run mypy type checker"
2522
@echo "make test - run pytest"
26-
@echo "make datasets - standardise labels in YAML (+ subintent) and emit JSONL"
23+
@echo "make datasets - emit JSONL from source YAML"
2724
@echo "make train - train new model"
28-
@echo "make thresholds - tune thresholds"
29-
@echo "make serve - run Flask app (dev mode)"
25+
@echo "make serve-dev - run Flask app for LOCAL development"
26+
@echo "make serve - run Flask app with Gunicorn (for production)"
3027
@echo "make clean - remove build artifacts"
3128

32-
venv:
33-
$(UV) venv $(VENV)
34-
3529
install:
3630
$(UV) pip install -r requirements.txt
3731

3832
lint:
3933
ruff check $(SRC)
40-
ruff format --check $(SRC)
34+
ruff format $(SRC)
4135

4236
typecheck:
4337
mypy $(SRC)
4438

4539
test:
46-
pytest -q --disable-warnings
40+
pytest -vv --disable-warnings
4741

48-
# --- NEW: build datasets (annotate YAML in place + write samples.*.jsonl) ---
4942
datasets:
50-
$(PYTHON) $(SRC)/data/build_datasets.py --data-dir $(DATA) --emit-jsonl --out-dir $(SRC)/mapped_data
43+
$(PYTHON) $(SRC)/data/build_datasets.py --data-dir $(DATA) --emit-jsonl --out-dir $(MAPPED_DATA)
5144

5245
train:
53-
$(PYTHON) $(SRC)/train.py --data-dir $(DATA) --artifacts-dir $(ARTIFACTS) --encoder $(ENCODER)
46+
$(PYTHON) $(SRC)/train_model.py --data-path $(MAPPED_DATA)/samples.train.jsonl --artifacts-dir $(ARTIFACTS)
5447

55-
thresholds:
56-
$(PYTHON) $(SRC)/fit_thresholds.py --model-dir $$(ls -td $(ARTIFACTS)/mcic-* | head -1)
48+
# Use this for local development on macOS to avoid MPS/forking issues
49+
serve-dev:
50+
FLASK_APP=src/application.py poetry run flask run -p 5001
5751

52+
# This is for production-like environments (e.g., Linux containers)
5853
serve:
59-
FLASK_APP=$(SRC)/application.py FLASK_ENV=development $(PYTHON) -m flask run --host=0.0.0.0 --port=8000
54+
OBJC_DISABLE_INITIALIZE_FORK_SAFETY=YES gunicorn --workers 2 --bind 0.0.0.0:5001 --preload src.application:app
6055

6156
clean:
62-
rm -rf $(ARTIFACTS)/*.pkl $(ARTIFACTS)/*.json __pycache__ .pytest_cache .mypy_cache
57+
rm -rf $(ARTIFACTS) __pycache__ .pytest_cache .mypy_cache
58+

README.md

Lines changed: 108 additions & 110 deletions
Original file line numberDiff line numberDiff line change
@@ -1,156 +1,154 @@
1-
# MomConnect Intent Classifier
1+
## MomConnect Intent Classifier
22

3-
The **MomConnect Intent Classifier** labels inbound messages from mothers into high-level intents.
4-
It is used by the Department of Health to triage and route messages appropriately (e.g. *service feedback*, *sensitive exits like baby loss or opt-outs*, etc.).
3+
A machine learning service to classify inbound user messages for the *MomConnect*. This service identifies user intents such as service feedback and sensitive topics like baby loss, enabling the platform to provide appropriate and timely responses.
54

6-
⚠️ **Note:** This service is **internal only** and not exposed outside the cluster.
75

8-
---
6+
### How It Works: A Hybrid Approach to Intent Classification
7+
This classifier uses a modern, two-stage process to understand user messages with both accuracy and precision. This design ensures that broad categories are handled robustly by a data-driven model, while critical, specific cases are managed with transparent and reliable rules.
98

10-
## Features
9+
_A simplified flowchart of the classification process._
1110

12-
- **Sentence embeddings** (via [SentenceTransformers](https://www.sbert.net/))
13-
- **Linear classifier head** trained on embeddings
14-
- **Threshold-based decisioning** per intent
15-
- **Policy-driven biasing**:
16-
- *Sensitive exits (baby loss, opt-out)* → prioritise **recall**
17-
- *Service feedback* → prioritise **precision**
18-
- *Noise/Spam* → strict filtering
19-
- **Automatic enrichment**:
20-
- Service feedback → sentiment polarity (positive/negative/neutral)
21-
- Sensitive exit → bereavement vs generic opt-out
22-
- **Review band**: low-confidence predictions are flagged as `NEEDS_REVIEW`
11+
1. Broad Understanding (Machine Learning): First, the user's message is converted into a sophisticated numerical representation (an "embedding") using a SentenceTransformer model. A trained machine learning model then reads this embedding to classify the message into one of four broad parent categories: `FEEDBACK`, `SENSITIVE_EXIT`, `NOISE_SPAM`, or `OTHER`.
2312

24-
---
13+
2. Specific Details (*Enrichment Rules*): Once the main category is known, the system applies a set of precise rules to determine the specific sub-intent.
2514

26-
## Development
15+
- If the message is `FEEDBACK`, a sentiment analysis model determines if it's a `COMPLIMENT` or `COMPLAINT`.
2716

28-
This project uses [Poetry](https://python-poetry.org/docs/#installation) for packaging and dependency management.
17+
- If it's `SENSITIVE_EXIT`, carefully curated patterns detect if it's about `BABY_LOSS` or an `OPTOUT` request.
2918

30-
1. Ensure you’re running **Python 3.11+**:
31-
```bash
32-
python --version
33-
Install dependencies:
3419

35-
bash
36-
Copy
37-
Edit
38-
poetry install
39-
Running the Flask worker
40-
Set the environment variables and start the app:
4120

42-
bash
43-
Copy
44-
Edit
45-
export NLU_USERNAME=your-username
46-
export NLU_PASSWORD=your-password
21+
### Model Development & Training
22+
This section is for anyone involved in managing the data, training the model, or evaluating its performance.
4723

48-
poetry run flask --app src.application run
49-
Code Quality
50-
Autoformat + Linting:
24+
**Data & Schema**
5125

52-
bash
53-
Copy
54-
Edit
55-
poetry run ruff format .
56-
poetry run ruff check .
57-
poetry run mypy --install-types
58-
Tests:
26+
The ground truth for this model lives in `src/data/nlu.yaml`. This file contains all the training examples, which are then processed by `src/data/build_datasets.py` to create the final training files.
5927

60-
bash
61-
Copy
62-
Edit
63-
poetry run pytest
64-
Regenerating Embeddings
65-
Delete the existing embeddings JSON in src/data/
28+
- To regenerate the training data from the source YAML files, run
29+
```bash
30+
make datasets # Which runs: poetry run python src/data/build_datasets.py --emit-jsonl
31+
```
32+
This will create clean .jsonl files (e.g., samples.train.jsonl) in the src/mapped_data/ directory.
33+
34+
- To train a new version of the model, run
35+
```bash
36+
make train # Which runs: poetry run python src/train_model.py
37+
```
38+
This script executes the complete pipeline: it loads the processed data, encodes the text, trains the classifier, and saves all model artifacts to the `src/artifacts/ directory`.
6639

67-
Update training examples in nlu.yaml
6840

69-
Run the Flask app:
41+
### Evaluation & Threshold Tuning
7042

71-
bash
72-
Copy
73-
Edit
74-
poetry run flask --app src.application run
75-
→ Embeddings will be regenerated.
43+
Model performance is measured against a hold-out test set (`test.yaml`). Confidence thresholds are defined in `src/artifacts/thresholds.json` and can be tuned by analyzing the model's performance on the validation set to balance the needs of each intent (e.g., prioritizing recall for `SENSITIVE_EXIT`).
7644
77-
Threshold Tuning
78-
Thresholds live in artifacts/thresholds.json.
79-
They can be tuned with validation data via:
8045
81-
bash
82-
Copy
83-
Edit
84-
poetry run python src/fit_thresholds.py \
85-
--model-dir artifacts/ \
86-
--nlu-path src/data/nlu.yaml \
87-
--validation-path src/data/validation.yaml
88-
Policy:
46+
### API, Deployment & Integration
47+
This section is for engineers responsible for deploying and integrating the service, and for QA who need to test the API.
8948
90-
Sensitive exits → low threshold (recall focus)
49+
**Setup and Installation**
9150
92-
Service feedback → higher threshold (precision focus)
51+
- Install dependencies:
52+
```bash
53+
make install # Which runs: poetry install
54+
```
9355
94-
Noise → strict threshold
56+
- Activate the virtual environment:
57+
```
58+
poetry shell
59+
```
9560
96-
Other → balanced
9761
98-
Review band → learned dynamically from disagreement zone
62+
#### Running the API Service
63+
The application is a standard Flask service. Do not use the built-in Flask development server for production.
9964
100-
Editor Configuration
101-
For VS Code:
65+
- For production or staging, use a WSGI server like Gunicorn:
10266
103-
Install the Python and Ruff extensions.
67+
```
68+
gunicorn --workers 2 --bind 0.0.0.0:5001 src.application:app
69+
```
10470
105-
Settings:
71+
- For local development, you can use the Flask dev server:
10672
107-
"python.linting.mypyEnabled": true
73+
```
74+
poetry run flask --app src.application run
75+
```
10876
109-
"python.formatting.provider": "black"
77+
_You will need to set the `NLU_USERNAME` and `NLU_PASSWORD` environment variables for authentication._
11078
111-
"editor.formatOnSave": true
11279
113-
Release Process
114-
Merge all PRs & complete QA.
80+
#### API Endpoints
81+
The service provides two specific, authenticated endpoints. Authentication is handled via HTTP Basic Auth.
11582
116-
Update release notes in CHANGELOG.md.
83+
1. **Baby Loss Detection**
11784
118-
On main branch:
85+
This endpoint analyzes a message to determine if it relates to baby loss. It is optimized for high recall to ensure sensitive cases are not missed.
11986
120-
Update version in pyproject.toml
87+
- Request: GET /nlu/babyloss/
88+
- Query Parameters
89+
90+
Parameter Type Required Description
91+
question string Yes The user's message text to be classified.
92+
12193

122-
Replace UNRELEASED with release version + date in CHANGELOG.md
94+
- Responses:
12395

124-
Commit + tag:
96+
- `200 OK` (Success): The babyloss key will be true if the intent is detected, and false otherwise.
12597

126-
bash
127-
Copy
128-
Edit
129-
git tag v0.2.1
130-
git push origin main --tags
131-
Post-release:
98+
{
99+
"babyloss": true,
100+
"model_version": "2025-09-29-v1",
101+
"parent_label": "SENSITIVE_EXIT",
102+
"sub_intent": "BABY_LOSS",
103+
"probability": 0.98,
104+
"review_status": "CLASSIFIED"
105+
}
106+
- `400 Bad Request`: Returned if the question parameter is missing.
132107

133-
Increment version in pyproject.toml (e.g. 0.2.2.dev0)
108+
2. **Feedback Detection**
134109

135-
Add new UNRELEASED header to CHANGELOG.md
110+
This endpoint analyzes a message to determine if it is a compliment or a complaint.
136111

137-
Running in Production
138-
A Docker image is published for deployment.
112+
- Request: GET /nlu/feedback/
113+
- Query Parameters
114+
115+
Parameter Type Required Description
116+
question string Yes The user's message text to be classified.
117+
139118
140-
Required environment variables:
119+
- Responses:
141120
142-
Variable Description
143-
NLU_USERNAME Username for API requests
144-
NLU_PASSWORD Password for API requests
145-
SENTRY_DSN Sentry DSN for error reporting
121+
- `200 OK`: The primary intent key will be COMPLIMENT, COMPLAINT, or None.
146122
147-
License
148-
MIT
123+
{
124+
"intent": "COMPLIMENT",
125+
"model_version": "2025-09-29-v1",
126+
"parent_label": "FEEDBACK",
127+
"probability": 0.99,
128+
"review_status": "CLASSIFIED"
129+
}
149130
150-
yaml
151-
Copy
152-
Edit
131+
- `401 Unauthorized`: Returned for any endpoint if authentication is incorrect.
153132
154-
---
155133
156-
Do you also want me to add an **API Quickstart** section (sample `curl` + example JSON response) so QA/SxD can test it directly without spinning up notebooks?
134+
### Testing and Code Quality
135+
Run these commands to ensure code quality and correctness.
136+
137+
- Run Unit Tests:
138+
```
139+
make test # Which runs: poetry run pytest -vv
140+
```
141+
142+
- Run Static Type Checking:
143+
```
144+
make typecheck # Which runs: poetry run mypy .
145+
```
146+
147+
- Run Linter/Formatter:
148+
```
149+
make lint # Which runs: poetry run ruff check --fix . && poetry run ruff format .
150+
```
151+
152+
153+
### Production Deployment
154+
For production, the Dockerfile should be configured to run the application using the Gunicorn command. The number of workers (--workers 4) should be adjusted based on the resources of the environment. The `src/artifacts directory`, which contains the trained model, must be included in the final container image.

0 commit comments

Comments
 (0)