A multi-label text classifier that assigns risk categories to pet food recall
and safety-alert records.
Built as an academic assignment for an Information Retrieval course.
Model Description
This model combines a frozen sentence transformer encoder with a trained
sklearn classifier head:
Recall text
→ frozen SentenceTransformer (sentence-transformers/all-MiniLM-L6-v2)
→ fixed-size embedding (dim 384)
→ OneVsRestClassifier(LogisticRegression) ← this repository
→ predicted risk labels
The transformer encoder (sentence-transformers/all-MiniLM-L6-v2) is not
fine-tuned and is not included in this repository. It is loaded at
inference time via the sentence-transformers library. Only the trained
classifier head (classifier.joblib), per-label thresholds
(thresholds.json), and label metadata (label_columns.json) are stored here.
Task
Supervised multi-label text classification.
Given a structured text constructed from brand name, product description, and
recall reason, the model predicts one or more of three risk categories.
A record may carry a single label or multiple labels when several risk types
are described.
Input Format
Build the input string using this template before encoding:
Brand: Example Brand. Product: Dry dog food. Recall reason: May be contaminated with Salmonella.
Output Labels
Label
Description
PATHOGEN_CONTAMINATION
Bacterial or pathogen-related contamination (Salmonella, Listeria, E. coli, etc.)
CHEMICAL_OR_NUTRITIONAL_RISK
Chemical, mycotoxin, heavy metal, feed additive, vitamin, or mineral-level risks
PHYSICAL_OR_QUALITY_ISSUE
Foreign material, labeling, packaging, import, inspection, or process-control issues
Label order used in all arrays: label_columns.json.
Per-Label Prediction Thresholds
Thresholds were tuned on the validation split and are stored in thresholds.json.
Label
Threshold
PATHOGEN_CONTAMINATION
0.50
CHEMICAL_OR_NUTRITIONAL_RISK
0.50
PHYSICAL_OR_QUALITY_ISSUE
0.40
If no label clears its threshold, the label with the highest probability score
is returned as a low-confidence fallback.
Usage
python
1import json
2import joblib
3import numpy as np
4from sentence_transformers import SentenceTransformer
56# Load artifacts7clf = joblib.load("classifier.joblib")8thresholds = json.load(open("thresholds.json"))["thresholds"]9labels = json.load(open("label_columns.json"))1011# Build input text12text =(13"Brand: Example Brand. "14"Product: Dry dog food. "15"Recall reason: May be contaminated with Salmonella."16)1718# Encode with frozen transformer19encoder = SentenceTransformer("sentence-transformers/all-MiniLM-L6-v2")20embedding = encoder.encode([text])# shape (1, 384)2122# Predict23proba = clf.predict_proba(embedding)[0]# shape (3,)24predicted =[l for i, l inenumerate(labels)if proba[i]>= thresholds[l]]25low_confidence =False26ifnot predicted:27 predicted =[labels[int(np.argmax(proba))]]28 low_confidence =True2930print(predicted, low_confidence)
The src/predict.py script in the source repository wraps this pipeline and
outputs clean JSON.
Validation Results (model selection)
Four classifiers were trained and compared on the validation split (15 examples).
Logistic Regression was selected because it achieved the best validation macro F1
while also supporting predict_proba and per-label threshold tuning.
Model
Micro F1
Macro F1
Weighted F1
Hamming Loss
Subset Accuracy
Dummy baseline
0.516
0.232
0.348
0.333
0.467
Logistic Regression
0.941
0.933
0.950
0.044
0.867
Linear SVC
0.903
0.857
0.893
0.067
0.800
Random Forest
0.941
0.933
0.950
0.044
0.867
Final Test Results
The selected model was evaluated once on the held-out test set (15 examples).
No training, threshold tuning, or model selection changes were made after
inspecting test results.
Overall
Metric
Value
Micro F1
0.875
Macro F1
0.841
Weighted F1
0.881
Hamming Loss
0.089
Subset Accuracy
0.800
Per-Label
Label
Precision
Recall
F1
Support
PATHOGEN_CONTAMINATION
1.000
1.000
1.000
8
CHEMICAL_OR_NUTRITIONAL_RISK
1.000
0.750
0.857
4
PHYSICAL_OR_QUALITY_ISSUE
0.600
0.750
0.667
4
The test set contains only 15 rows, so metrics are sensitive to 1–2 examples.
Results should be interpreted as assignment-scale evidence, not robust
production performance estimates.
Training Data
103 labeled records from official public pet food recall and safety-alert
portals (FDA, EU RASFF, UK FSA, Canada CFIA, openFDA). Labels were assigned
using transparent rule-based keyword patterns. 14 uncertain records were
excluded rather than forced into a label.
Split
Rows
Train
73
Validation
15
Test
15
See the companion dataset repository for full split details and source
attribution.
Small training set — 73 training rows; model may not generalise beyond
this vocabulary and source mix.
Small test set — 15 rows; per-label metrics are sensitive to 1–2 examples.
Rule-based labels — not manually annotated by domain experts.
Consolidated taxonomy — three labels combine six more specific risk types.
Source heterogeneity — records from five sources with different terminology.
Frozen encoder — the transformer is not fine-tuned on recall text.
Not a safety authority tool — the model predicts text categories, not
product safety.
Educational Disclaimer
This model is for educational purposes only.
It does not provide veterinary advice, legal advice, product safety
certification, official recall interpretation, or medical or nutritional
recommendations.
Always consult official recall notices and qualified professionals for
authoritative information about pet food safety.