Spaces:
Sleeping
Sleeping
Commit ·
4e1f088
1
Parent(s): e7919f5
Fix Dockerfile: copy all files before pip install
Browse files- .gitignore +3 -0
- Dockerfile +1 -3
- PROJECT_REPORT.md +0 -424
.gitignore
CHANGED
|
@@ -30,6 +30,9 @@ artifact/*
|
|
| 30 |
# Data
|
| 31 |
*.csv
|
| 32 |
|
|
|
|
|
|
|
|
|
|
| 33 |
# Notebook outputs
|
| 34 |
notebook/*.html
|
| 35 |
notebook/.ipynb_checkpoints/
|
|
|
|
| 30 |
# Data
|
| 31 |
*.csv
|
| 32 |
|
| 33 |
+
# Reports
|
| 34 |
+
PROJECT_REPORT.md
|
| 35 |
+
|
| 36 |
# Notebook outputs
|
| 37 |
notebook/*.html
|
| 38 |
notebook/.ipynb_checkpoints/
|
Dockerfile
CHANGED
|
@@ -2,12 +2,10 @@ FROM python:3.12-slim
|
|
| 2 |
|
| 3 |
WORKDIR /app
|
| 4 |
|
| 5 |
-
COPY
|
| 6 |
RUN pip install --no-cache-dir --upgrade pip \
|
| 7 |
&& pip install --no-cache-dir -r requirements.txt
|
| 8 |
|
| 9 |
-
COPY . .
|
| 10 |
-
|
| 11 |
EXPOSE 7860
|
| 12 |
|
| 13 |
CMD ["python", "app.py"]
|
|
|
|
| 2 |
|
| 3 |
WORKDIR /app
|
| 4 |
|
| 5 |
+
COPY . .
|
| 6 |
RUN pip install --no-cache-dir --upgrade pip \
|
| 7 |
&& pip install --no-cache-dir -r requirements.txt
|
| 8 |
|
|
|
|
|
|
|
| 9 |
EXPOSE 7860
|
| 10 |
|
| 11 |
CMD ["python", "app.py"]
|
PROJECT_REPORT.md
DELETED
|
@@ -1,424 +0,0 @@
|
|
| 1 |
-
# Project Report: US Visa Approval Prediction System
|
| 2 |
-
|
| 3 |
-
## CRISP-DM Framework Mapping
|
| 4 |
-
|
| 5 |
-
This project follows the CRISP-DM (Cross-Industry Standard Process for Data Mining) methodology. The table below maps each CRISP-DM phase to the corresponding report sections.
|
| 6 |
-
|
| 7 |
-
| CRISP-DM Phase | Report Section | Key Activities |
|
| 8 |
-
|----------------|---------------|----------------|
|
| 9 |
-
| **Business Understanding** | Section 1 | Defined the problem (PERM prediction + explainability), identified stakeholders (applicants, attorneys), established success criteria (F1 score, actionable SHAP insights) |
|
| 10 |
-
| **Data Understanding** | Sections 2 & 3 | Profiled the EasyVisa dataset (25,480 records), analyzed class imbalance (66.8/33.2), examined feature distributions and relationships |
|
| 11 |
-
| **Data Preparation** | Section 4 | Engineered `company_age`, selected encoding strategies per feature type, built scikit-learn ColumnTransformer pipeline, applied SMOTEENN to training set |
|
| 12 |
-
| **Modeling** | Section 5 | Evaluated Random Forest, Gradient Boosting, and XGBoost via GridSearchCV; built VotingEnsemble; tuned on F1 |
|
| 13 |
-
| **Evaluation** | Sections 5.4 & 5.5 | Confusion matrix analysis, per-class precision/recall/F1, confidence calibration assessment |
|
| 14 |
-
| **Deployment** | Section 7 | FastAPI backend, Docker containerization, Hugging Face Spaces hosting, SHAP-based explanation UI |
|
| 15 |
-
|
| 16 |
-
---
|
| 17 |
-
|
| 18 |
-
## 1. Problem Statement
|
| 19 |
-
|
| 20 |
-
When a U.S. employer wants to hire a foreign worker permanently, they must file a PERM (Program Electronic Review Management) labor certification with the Department of Labor. The DOL either **certifies** or **denies** each case. The process is opaque — applicants and immigration attorneys have limited visibility into which factors drive outcomes.
|
| 21 |
-
|
| 22 |
-
**Goal**: Build a machine learning system that predicts whether a PERM application will be certified or denied, and explains *why* — giving applicants actionable insight into their case strength before they file.
|
| 23 |
-
|
| 24 |
-
### Why This Matters
|
| 25 |
-
|
| 26 |
-
- PERM processing takes 6–18 months. A denial means restarting from scratch.
|
| 27 |
-
- Legal fees for a PERM filing range from $5,000–$15,000. Predicting denial risk upfront saves time and money.
|
| 28 |
-
- Explainability (not just a yes/no prediction) helps attorneys identify weak points and strengthen applications before submission.
|
| 29 |
-
|
| 30 |
-
---
|
| 31 |
-
|
| 32 |
-
## 2. Dataset
|
| 33 |
-
|
| 34 |
-
**Source**: EasyVisa dataset — historical PERM labor certification records (Phani Srinivas et al., published on Kaggle).
|
| 35 |
-
|
| 36 |
-
| Property | Value |
|
| 37 |
-
|----------|-------|
|
| 38 |
-
| Total records | 25,480 |
|
| 39 |
-
| Features | 12 columns (10 used for prediction) |
|
| 40 |
-
| Target | `case_status` (Certified / Denied) |
|
| 41 |
-
| Class distribution | 66.8% Certified (17,021) / 33.2% Denied (8,459) |
|
| 42 |
-
|
| 43 |
-
### 2.1 Feature Descriptions
|
| 44 |
-
|
| 45 |
-
| Feature | Type | Description |
|
| 46 |
-
|---------|------|-------------|
|
| 47 |
-
| `case_id` | ID | Unique identifier (dropped before training) |
|
| 48 |
-
| `continent` | Categorical | Applicant's continent of origin (Asia, Europe, Africa, N. America, S. America, Oceania) |
|
| 49 |
-
| `education_of_employee` | Ordinal | Highest education level (High School, Bachelor's, Master's, Doctorate) |
|
| 50 |
-
| `has_job_experience` | Binary | Whether applicant has prior relevant work experience (Y/N) |
|
| 51 |
-
| `requires_job_training` | Binary | Whether the position requires additional training (Y/N) |
|
| 52 |
-
| `no_of_employees` | Numeric | Number of employees at the sponsoring company |
|
| 53 |
-
| `yr_of_estab` | Numeric | Year the company was established (transformed to `company_age`) |
|
| 54 |
-
| `region_of_employment` | Categorical | U.S. region where the job is located (West, Northeast, South, Midwest, Island) |
|
| 55 |
-
| `prevailing_wage` | Numeric | DOL-determined wage for the position |
|
| 56 |
-
| `unit_of_wage` | Categorical | Pay period (Hour, Week, Month, Year) |
|
| 57 |
-
| `full_time_position` | Binary | Whether the position is full-time (Y/N) |
|
| 58 |
-
| `case_status` | Target | Certified (0) or Denied (1) |
|
| 59 |
-
|
| 60 |
-
### 2.2 Dropped Columns
|
| 61 |
-
|
| 62 |
-
- **`case_id`**: Unique identifier with no predictive value.
|
| 63 |
-
- **`yr_of_estab`**: Replaced with `company_age` (current year minus establishment year) — a relative measure that doesn't become stale over time.
|
| 64 |
-
|
| 65 |
-
---
|
| 66 |
-
|
| 67 |
-
## 3. Exploratory Data Analysis
|
| 68 |
-
|
| 69 |
-
Full EDA code with interactive visualizations is available in `notebook/eda.ipynb`. Key figures referenced below:
|
| 70 |
-
|
| 71 |
-
- **Figure 1** (Section 3.1): Class distribution bar chart and pie chart — visualizes the 66.8/33.2 Certified/Denied split.
|
| 72 |
-
- **Figure 2** (Section 3.2): Certification rate by category for each categorical feature — side-by-side count plots and horizontal bar charts showing how approval rate varies across education levels, continents, regions, etc.
|
| 73 |
-
- **Figure 3** (Section 3.3): Numeric feature distributions — histograms, box plots by status, and KDE overlays for `no_of_employees`, `prevailing_wage`, and `company_age`.
|
| 74 |
-
- **Figure 4** (Section 3.3): Correlation heatmap — shows feature-to-feature and feature-to-target correlations.
|
| 75 |
-
- **Figure 5** (Section 5.4): Confusion matrix — heatmap visualization of the 2x2 prediction outcome matrix (see table below).
|
| 76 |
-
- **Figure 6** (Section 5.5): Model comparison bar chart — Denied F1 across Random Forest, Gradient Boosting, XGBoost, and VotingEnsemble.
|
| 77 |
-
|
| 78 |
-
### 3.1 Class Balance
|
| 79 |
-
|
| 80 |
-
The dataset has a **2.01:1 imbalance** — 17,021 Certified (66.8%) vs 8,459 Denied (33.2%). This is significant because:
|
| 81 |
-
- A naive classifier predicting "Certified" for every case achieves 66.8% accuracy while catching zero denials.
|
| 82 |
-
- We need a metric that penalizes missing denials. This ruled out accuracy as the primary metric (see Section 5.2).
|
| 83 |
-
|
| 84 |
-
### 3.2 Feature Analysis
|
| 85 |
-
|
| 86 |
-
**Education**: Strong ordinal relationship with approval. Doctorate holders have the highest certification rate; High School the lowest. This makes intuitive sense — PERM is designed for positions requiring specialized skills.
|
| 87 |
-
|
| 88 |
-
**Job Experience**: Applicants with prior experience (`Y`) are certified at a meaningfully higher rate. Experience signals that the worker already possesses the skills the employer claims to need.
|
| 89 |
-
|
| 90 |
-
**Job Training**: Requiring training (`Y`) correlates with higher denial rates. If the applicant needs training, DOL may question whether they truly meet the job requirements.
|
| 91 |
-
|
| 92 |
-
**Prevailing Wage**: Higher wages correlate with certification. High-wage positions are typically specialized roles where it's harder to find qualified U.S. workers — exactly what PERM is designed for.
|
| 93 |
-
|
| 94 |
-
**Company Size**: Larger employers have slightly higher certification rates, likely due to more established HR and legal processes for PERM filings.
|
| 95 |
-
|
| 96 |
-
**Full-Time Position**: Full-time roles are certified at a higher rate than part-time, suggesting DOL views full-time offers as more credible employer commitments.
|
| 97 |
-
|
| 98 |
-
**Continent**: Asia represents the largest applicant group. Approval rates vary by continent, though this likely reflects confounding factors (education levels, industry mix) rather than geographic bias per se. See Section 9 for ethical considerations.
|
| 99 |
-
|
| 100 |
-
**Region of Employment**: Regional variation exists but is modest. West and Northeast have the most filings, reflecting tech and finance industry concentration.
|
| 101 |
-
|
| 102 |
-
### 3.3 Numeric Feature Distributions
|
| 103 |
-
|
| 104 |
-
- **`no_of_employees`**: Heavily right-skewed. Most employers are small-to-mid size, with a long tail of large corporations. This skew motivated the power transformation in preprocessing.
|
| 105 |
-
- **`prevailing_wage`**: Varies dramatically by `unit_of_wage`. A $35/hour wage and a $72,800/year wage are roughly equivalent, but the raw numbers differ by 2,000x. The model handles this through the combination of `prevailing_wage` and `unit_of_wage` features.
|
| 106 |
-
- **`company_age`**: Right-skewed. Applied power transformation to normalize.
|
| 107 |
-
|
| 108 |
-
### 3.4 Missing Values
|
| 109 |
-
|
| 110 |
-
No missing values in the dataset. All 25,480 records are complete across all 12 columns.
|
| 111 |
-
|
| 112 |
-
---
|
| 113 |
-
|
| 114 |
-
## 4. Data Preprocessing
|
| 115 |
-
|
| 116 |
-
### 4.1 Feature Engineering
|
| 117 |
-
|
| 118 |
-
**`company_age`**: Computed as `current_year - yr_of_estab`. This relative feature ages gracefully — a company established in 1990 is "34 years old" in 2024, not "1990" (an arbitrary number to the model).
|
| 119 |
-
|
| 120 |
-
### 4.2 Encoding Strategy
|
| 121 |
-
|
| 122 |
-
Different encoding strategies were chosen based on feature semantics:
|
| 123 |
-
|
| 124 |
-
| Strategy | Features | Why |
|
| 125 |
-
|----------|----------|-----|
|
| 126 |
-
| **Ordinal Encoding** | `education_of_employee`, `has_job_experience`, `requires_job_training`, `full_time_position` | These have a natural order or binary values. Ordinal encoding preserves the ranking (High School < Bachelor's < Master's < Doctorate). |
|
| 127 |
-
| **One-Hot Encoding** | `continent`, `unit_of_wage`, `region_of_employment` | These are nominal — no inherent order. One-hot encoding avoids imposing a false ordinal relationship. |
|
| 128 |
-
| **Power Transform** | `no_of_employees`, `company_age` | Both are heavily right-skewed. Power transformation (Yeo-Johnson) normalizes the distribution, helping tree-based models make better split decisions. |
|
| 129 |
-
| **Passthrough** | `prevailing_wage` | Already well-distributed enough for tree models. No transformation needed. |
|
| 130 |
-
|
| 131 |
-
### 4.3 Preprocessing Pipeline
|
| 132 |
-
|
| 133 |
-
The preprocessing is implemented as a scikit-learn `ColumnTransformer` with three named transformers:
|
| 134 |
-
1. **`Transformer`**: PowerTransformer on `no_of_employees` and `company_age`
|
| 135 |
-
2. **`OrdinalEncoder`**: On `has_job_experience`, `requires_job_training`, `full_time_position`, `education_of_employee`
|
| 136 |
-
3. **`OneHotEncoder`**: On `continent`, `unit_of_wage`, `region_of_employment`
|
| 137 |
-
|
| 138 |
-
This pipeline is serialized alongside the model in `model.pkl` (wrapped in a `visaModel` object), ensuring identical preprocessing at inference time.
|
| 139 |
-
|
| 140 |
-
---
|
| 141 |
-
|
| 142 |
-
## 5. Model Training and Evaluation
|
| 143 |
-
|
| 144 |
-
### 5.1 Handling Class Imbalance: SMOTEENN
|
| 145 |
-
|
| 146 |
-
The 2:1 class imbalance (Certified:Denied) means the model sees twice as many positive cases during training. Without intervention, it learns to favor "Certified" predictions.
|
| 147 |
-
|
| 148 |
-
**SMOTEENN** was chosen over simpler alternatives:
|
| 149 |
-
|
| 150 |
-
| Method | What It Does | Why Not Sufficient |
|
| 151 |
-
|--------|-------------|-------------------|
|
| 152 |
-
| Random oversampling | Duplicates minority samples | Creates exact copies, leads to overfitting |
|
| 153 |
-
| SMOTE alone | Synthesizes new minority samples via interpolation | Can create noisy samples near the decision boundary |
|
| 154 |
-
| Random undersampling | Removes majority samples | Throws away useful data |
|
| 155 |
-
| **SMOTEENN** | SMOTE + Edited Nearest Neighbors cleanup | Synthesizes new minority samples, then removes noisy samples from both classes that are misclassified by their neighbors |
|
| 156 |
-
|
| 157 |
-
SMOTEENN was applied **only to the training set**. The test set was left untouched to reflect real-world class distribution. This prevents data leakage — evaluating on resampled test data would give inflated metrics.
|
| 158 |
-
|
| 159 |
-
### 5.2 Metric Selection: F1 Score
|
| 160 |
-
|
| 161 |
-
**Why not accuracy?**
|
| 162 |
-
A model predicting "Certified" for every case scores 66.8% accuracy. That's useless — it catches zero denials.
|
| 163 |
-
|
| 164 |
-
**Why F1 over precision or recall alone?**
|
| 165 |
-
- Pure precision: "Only predict denied when very sure" — misses many actual denials.
|
| 166 |
-
- Pure recall: "Flag everything as denied" — too many false alarms.
|
| 167 |
-
- **F1 balances both**: It's the harmonic mean of precision and recall, penalizing models that sacrifice one for the other.
|
| 168 |
-
|
| 169 |
-
F1 was used as the scoring metric in GridSearchCV, meaning hyperparameters were tuned to maximize this balance.
|
| 170 |
-
|
| 171 |
-
### 5.3 Model Selection
|
| 172 |
-
|
| 173 |
-
Three models were evaluated via GridSearchCV with 5-fold cross-validation:
|
| 174 |
-
|
| 175 |
-
| Model | Why Considered |
|
| 176 |
-
|-------|---------------|
|
| 177 |
-
| **Random Forest** | Robust baseline, handles mixed feature types well, resistant to overfitting |
|
| 178 |
-
| **Gradient Boosting** | Sequential error correction, often outperforms RF on structured data |
|
| 179 |
-
| **XGBoost** | Optimized gradient boosting with regularization, typically best-in-class for tabular data |
|
| 180 |
-
|
| 181 |
-
**Hyperparameter search space** (from `config/model.yaml`):
|
| 182 |
-
|
| 183 |
-
- **Random Forest**: `n_estimators` [100, 200, 300], `max_depth` [10, 20, 30], `max_features` [sqrt, log2]
|
| 184 |
-
- **Gradient Boosting**: `n_estimators` [100, 200, 300], `learning_rate` [0.05, 0.1, 0.2], `max_depth` [3, 5, 7]
|
| 185 |
-
- **XGBoost**: `n_estimators` [100, 200, 300], `learning_rate` [0.05, 0.1, 0.2], `max_depth` [3, 5, 7]
|
| 186 |
-
|
| 187 |
-
After individual evaluation, the best-performing models were combined into a **soft-voting ensemble** (`VotingClassifier` with `voting='soft'`), which averages class probability outputs from each model. The ensemble was selected as the final model because it smooths out individual model weaknesses and produces better-calibrated probability estimates.
|
| 188 |
-
|
| 189 |
-
### 5.4 Results
|
| 190 |
-
|
| 191 |
-
The final **VotingEnsemble** achieved a **weighted F1 of 76%** on the held-out test set (20% stratified split, 5,096 samples).
|
| 192 |
-
|
| 193 |
-
**Confusion Matrix**:
|
| 194 |
-
|
| 195 |
-
| | Predicted Certified | Predicted Denied | Total |
|
| 196 |
-
|---|---:|---:|---:|
|
| 197 |
-
| **Actual Certified** | 3,026 (TN) | 378 (FP) | 3,404 |
|
| 198 |
-
| **Actual Denied** | 422 (FN) | 1,270 (TP) | 1,692 |
|
| 199 |
-
| **Total** | 3,448 | 1,648 | 5,096 |
|
| 200 |
-
|
| 201 |
-
**Per-Class Metrics**:
|
| 202 |
-
|
| 203 |
-
| Class | Precision | Recall | F1 Score | Support |
|
| 204 |
-
|-------|-----------|--------|----------|---------|
|
| 205 |
-
| Certified | 87.8% | 88.9% | 88.3% | 3,404 |
|
| 206 |
-
| Denied | 77.1% | 75.1% | 76.0% | 1,692 |
|
| 207 |
-
| **Weighted Avg** | **84.2%** | **84.3%** | **84.2%** | **5,096** |
|
| 208 |
-
|
| 209 |
-
**Reading the confusion matrix**:
|
| 210 |
-
- **3,026 True Negatives**: Certified cases correctly predicted as Certified.
|
| 211 |
-
- **1,270 True Positives**: Denied cases correctly predicted as Denied — the model catches 75.1% of actual denials.
|
| 212 |
-
- **378 False Positives**: Certified cases wrongly flagged as Denied — 11.1% of certified applications receive a false alarm. For an advisory tool, this is acceptable: a false "denied" prediction with low confidence and weak SHAP reasons is easily dismissed.
|
| 213 |
-
- **422 False Negatives**: Denied cases the model missed — predicted Certified but were actually Denied. This is the costliest error type. The 24.9% miss rate means roughly 1 in 4 denials goes undetected.
|
| 214 |
-
|
| 215 |
-
**Overall accuracy**: 84.3%, well above the 66.8% naive baseline.
|
| 216 |
-
|
| 217 |
-
### 5.5 Single Model vs. Ensemble Comparison
|
| 218 |
-
|
| 219 |
-
| Model | Denied F1 | Certified F1 | Weighted F1 |
|
| 220 |
-
|-------|-----------|-------------|-------------|
|
| 221 |
-
| Random Forest | 72.4% | 86.1% | 81.6% |
|
| 222 |
-
| Gradient Boosting | 74.8% | 87.5% | 83.3% |
|
| 223 |
-
| XGBoost | 75.3% | 87.9% | 83.7% |
|
| 224 |
-
| **VotingEnsemble** | **76.0%** | **88.3%** | **84.2%** |
|
| 225 |
-
|
| 226 |
-
XGBoost was the strongest individual model. The ensemble provided a modest but consistent improvement (+0.7% Denied F1 over XGBoost alone). The ensemble was selected as the final model for two reasons:
|
| 227 |
-
1. Soft voting averages probabilities across models, producing smoother and better-calibrated confidence scores.
|
| 228 |
-
2. The marginal F1 gain, while small, comes at negligible inference cost since all three models are fast on CPU.
|
| 229 |
-
|
| 230 |
-
---
|
| 231 |
-
|
| 232 |
-
## 6. Explainability
|
| 233 |
-
|
| 234 |
-
A prediction alone ("approved" or "denied") isn't useful to an applicant. They need to know **why** — which factors helped, which hurt, and what they can change.
|
| 235 |
-
|
| 236 |
-
### 6.1 SHAP TreeExplainer
|
| 237 |
-
|
| 238 |
-
**SHAP (SHapley Additive exPlanations)** was chosen for per-prediction explanations. Specifically, `TreeExplainer` — a fast, exact algorithm for tree-based models.
|
| 239 |
-
|
| 240 |
-
**How it works**: For each prediction, SHAP computes a value for every feature, representing how much that feature pushed the prediction toward "Certified" or "Denied" relative to the average case.
|
| 241 |
-
|
| 242 |
-
**Implementation challenge**: The preprocessing pipeline transforms 10 raw features into ~20 encoded features (due to one-hot encoding). SHAP operates on these transformed features. We built a mapping (`_build_feature_mapping` in `shap_explainer.py`) that aggregates transformed-feature SHAP values back to their original feature names. For example, the 6 one-hot columns for `continent` are summed back into a single SHAP value for "continent."
|
| 243 |
-
|
| 244 |
-
**Interpretation**:
|
| 245 |
-
- Negative SHAP value → pushes toward Certified (strength)
|
| 246 |
-
- Positive SHAP value → pushes toward Denied (weakness)
|
| 247 |
-
- Magnitude indicates strength: >1.0 = strong, 0.3–1.0 = moderate, <0.3 = slight
|
| 248 |
-
|
| 249 |
-
### 6.2 Rule-Based Fallback
|
| 250 |
-
|
| 251 |
-
SHAP can occasionally fail (edge cases in model structure, unseen feature combinations). A rule-based fallback (`analyze_features()` in `prediction_pipeline.py`) provides heuristic explanations based on known patterns in the data:
|
| 252 |
-
- Education level thresholds
|
| 253 |
-
- Wage benchmarks (annualized)
|
| 254 |
-
- Company size and age breakpoints
|
| 255 |
-
- Binary feature impacts
|
| 256 |
-
|
| 257 |
-
This ensures every prediction receives an explanation, even if SHAP encounters an error.
|
| 258 |
-
|
| 259 |
-
### 6.3 Confidence Scoring
|
| 260 |
-
|
| 261 |
-
The model outputs class probabilities via `predict_proba()`. The confidence score is `max(P(Certified), P(Denied)) * 100`.
|
| 262 |
-
|
| 263 |
-
The UI communicates confidence levels:
|
| 264 |
-
- **>80%**: High confidence — prediction is reliable
|
| 265 |
-
- **60–80%**: Moderate confidence — interpret with caution
|
| 266 |
-
- **<60%**: Low confidence — borderline case, prediction may be unreliable
|
| 267 |
-
|
| 268 |
-
---
|
| 269 |
-
|
| 270 |
-
## 7. Deployment
|
| 271 |
-
|
| 272 |
-
### 7.1 Architecture
|
| 273 |
-
|
| 274 |
-
| Component | Technology | Purpose |
|
| 275 |
-
|-----------|-----------|---------|
|
| 276 |
-
| Backend | FastAPI + Uvicorn | Async API with automatic request validation |
|
| 277 |
-
| Frontend | Jinja2 + vanilla JS | Single-page prediction UI with SHAP results |
|
| 278 |
-
| Model serving | Pickle + class-level cache | Load once, serve many requests |
|
| 279 |
-
| Container | Docker (python:3.12-slim) | Reproducible deployment environment |
|
| 280 |
-
| Hosting | Hugging Face Spaces | Free Docker SDK hosting, port 7860 |
|
| 281 |
-
|
| 282 |
-
### 7.2 API Endpoints
|
| 283 |
-
|
| 284 |
-
| Endpoint | Method | Purpose |
|
| 285 |
-
|----------|--------|---------|
|
| 286 |
-
| `/` | GET | Serves the prediction UI |
|
| 287 |
-
| `/predict` | POST | JSON API — accepts 10 features, returns prediction + confidence + SHAP insights |
|
| 288 |
-
| `/` | POST | Form submission fallback — returns rendered HTML with result |
|
| 289 |
-
|
| 290 |
-
### 7.3 Model Serving
|
| 291 |
-
|
| 292 |
-
The model is loaded once via `visaModel` (defined in `entity/estimator.py`) and cached as a class variable. The `visaModel` object wraps both the preprocessing `ColumnTransformer` and the trained `VotingClassifier`, ensuring end-to-end consistency from raw input to prediction. The SHAP explainer is similarly cached after first use.
|
| 293 |
-
|
| 294 |
-
---
|
| 295 |
-
|
| 296 |
-
## 8. Design Decisions and Trade-offs
|
| 297 |
-
|
| 298 |
-
### 8.1 Why Tree-Based Models Over Deep Learning?
|
| 299 |
-
|
| 300 |
-
- **Dataset size**: 25,480 records is small for deep learning. Tree-based models consistently outperform neural networks on tabular data at this scale (Grinsztajn et al., 2022).
|
| 301 |
-
- **Interpretability**: SHAP TreeExplainer provides exact, fast explanations for tree models. Neural network explanations (LIME, gradient-based) are approximations and significantly slower.
|
| 302 |
-
- **Training time**: The full GridSearchCV pipeline trains in under 10 minutes on CPU. No GPU infrastructure needed.
|
| 303 |
-
|
| 304 |
-
### 8.2 Why F1 Over F2?
|
| 305 |
-
|
| 306 |
-
F2 weights recall higher than precision — useful when missing a denial is much worse than a false alarm. We considered both:
|
| 307 |
-
- **F1 = 76%**: Balanced precision (77.1%) and recall (75.1%) for the Denied class.
|
| 308 |
-
- **F2**: Would push recall higher at the cost of more false alarms on Certified cases.
|
| 309 |
-
|
| 310 |
-
For an informational tool (not a decision system), F1's balance is appropriate. Users see a confidence score and detailed SHAP analysis — a false alarm with low confidence and weak reasons is easily dismissed by the user.
|
| 311 |
-
|
| 312 |
-
### 8.3 Why SMOTEENN Over Class Weights?
|
| 313 |
-
|
| 314 |
-
Both address class imbalance. SMOTEENN creates synthetic minority samples and cleans noisy boundary samples. Class weighting (e.g., `scale_pos_weight` in XGBoost) adjusts the loss function.
|
| 315 |
-
|
| 316 |
-
SMOTEENN was chosen because:
|
| 317 |
-
- It physically balances the dataset, giving the model equal exposure to both classes
|
| 318 |
-
- The ENN cleanup step removes noisy samples near the decision boundary, improving generalization
|
| 319 |
-
- Empirically, it produced better F1 than `scale_pos_weight` alone on this dataset
|
| 320 |
-
|
| 321 |
-
### 8.4 Why FastAPI Over Flask?
|
| 322 |
-
|
| 323 |
-
Both are Python web frameworks capable of serving the prediction API. FastAPI was chosen for several reasons:
|
| 324 |
-
|
| 325 |
-
- **Async-native**: FastAPI is built on Starlette and supports `async/await` natively. Model loading and SHAP computation can be CPU-intensive; async request handling prevents one slow prediction from blocking the entire server.
|
| 326 |
-
- **Automatic validation**: Pydantic models (`PredictRequest`) validate incoming JSON against the expected schema and return clear 422 errors for malformed requests — no manual validation code needed.
|
| 327 |
-
- **Built-in OpenAPI docs**: FastAPI auto-generates interactive API documentation at `/docs`, useful for testing the `/predict` endpoint during development without a frontend.
|
| 328 |
-
- **Performance**: FastAPI consistently benchmarks faster than Flask for JSON APIs due to its ASGI foundation vs Flask's WSGI.
|
| 329 |
-
|
| 330 |
-
Flask would have worked, but FastAPI provided validation, documentation, and async support with less boilerplate.
|
| 331 |
-
|
| 332 |
-
---
|
| 333 |
-
|
| 334 |
-
## 9. Ethical Considerations
|
| 335 |
-
|
| 336 |
-
### 9.1 Continent as a Feature
|
| 337 |
-
|
| 338 |
-
The model uses `continent` (applicant's continent of origin) as an input feature. This is effectively a proxy for nationality and ethnicity, raising fairness concerns in a visa prediction context.
|
| 339 |
-
|
| 340 |
-
**Why it was retained**: Continent correlates with approval rates in the historical data. Removing it would reduce model accuracy without eliminating the bias — other features (education system, wage levels) partially encode the same information. Excluding the feature would make the model less accurate without making it more fair.
|
| 341 |
-
|
| 342 |
-
**Mitigation through transparency**: SHAP explanations explicitly surface when continent is influencing a prediction. If a user sees "Applicant from [continent] moderately works against approval," they can recognize this as a data pattern rather than a recommendation. This transparency allows users to critically evaluate the model's reasoning rather than accepting it blindly.
|
| 343 |
-
|
| 344 |
-
**Limitations**: The model reflects historical DOL decision patterns, which may contain systemic biases. It should never be used as a decision-making tool — only as an informational aid. We recommend that any production deployment include regular fairness audits using SHAP to monitor whether continent-based disparities are growing or shrinking over time.
|
| 345 |
-
|
| 346 |
-
### 9.2 Broader Concerns
|
| 347 |
-
|
| 348 |
-
- **Not legal advice**: The UI explicitly states this is an informational tool, not a guarantee of outcome.
|
| 349 |
-
- **Feedback loops**: If attorneys use the tool to selectively file "likely certified" cases, the resulting data could reinforce existing biases.
|
| 350 |
-
- **Access equity**: The tool is freely hosted to avoid creating an information advantage only for well-resourced applicants.
|
| 351 |
-
|
| 352 |
-
---
|
| 353 |
-
|
| 354 |
-
## 10. Limitations
|
| 355 |
-
|
| 356 |
-
1. **Historical data only**: The model reflects patterns from past DOL decisions. Policy changes, new regulations, or shifting priorities are not captured.
|
| 357 |
-
|
| 358 |
-
2. **Limited feature set**: Only 10 features are used. Real PERM applications involve hundreds of fields (job description, recruitment steps, attorney information, etc.). The model captures broad patterns, not case-specific nuances.
|
| 359 |
-
|
| 360 |
-
3. **No temporal awareness**: The model treats all historical records equally. Recent trends may differ from older patterns.
|
| 361 |
-
|
| 362 |
-
4. **Confidence calibration**: The `predict_proba` values from the VotingClassifier are not perfectly calibrated probabilities. The confidence percentage is indicative, not a true probability.
|
| 363 |
-
|
| 364 |
-
5. **Not legal advice**: PERM outcomes depend on factors beyond what any model can capture (quality of documentation, specific DOL officer review, recruitment evidence, etc.).
|
| 365 |
-
|
| 366 |
-
---
|
| 367 |
-
|
| 368 |
-
## 11. Future Improvements
|
| 369 |
-
|
| 370 |
-
- **Probability calibration**: Apply Platt scaling or isotonic regression to produce better-calibrated confidence scores.
|
| 371 |
-
- **Feature expansion**: Incorporate job title/SOC code, NAICS industry code, and wage ratio (offered wage / prevailing wage) if data becomes available.
|
| 372 |
-
- **Temporal weighting**: Weight recent cases higher during training to capture evolving DOL decision patterns.
|
| 373 |
-
- **Fairness auditing**: Implement automated SHAP-based fairness metrics to monitor continent-driven disparities across model versions.
|
| 374 |
-
- **A/B testing**: Compare SHAP explanations with rule-based explanations in user studies to measure which format is more actionable.
|
| 375 |
-
- **Retraining pipeline**: Automated retraining when new DOL disclosure data is published (quarterly).
|
| 376 |
-
|
| 377 |
-
---
|
| 378 |
-
|
| 379 |
-
## 12. Team Contributions
|
| 380 |
-
|
| 381 |
-
**Group 3** — US Visa Approval Prediction System using Machine Learning, MLOps & AWS
|
| 382 |
-
|
| 383 |
-
The project was divided into 7 modules. Each team member owned a primary module and contributed to additional modules as listed below.
|
| 384 |
-
|
| 385 |
-
| Member | Primary Module | All Modules | Key Contributions |
|
| 386 |
-
|--------|---------------|-------------|-------------------|
|
| 387 |
-
| Muhammad Asim | Module 1: Data Collection & Preprocessing | 1, 5 & 6 | Data pipeline (ingestion, validation, transformation), feature engineering (`company_age`), encoding strategy, SMOTEENN resampling, cloud infrastructure (AWS S3, Docker), MLOps & CI/CD pipeline |
|
| 388 |
-
| Syed Measum | Module 2: Machine Learning | 1, 2 & 7 | Model training (Random Forest, Gradient Boosting, XGBoost), GridSearchCV hyperparameter tuning, VotingEnsemble construction, evaluation metrics (confusion matrix, F1), data collection support, documentation |
|
| 389 |
-
| Muhammad Tayyab | Module 3: Backend API | 3, 4 & 7 | FastAPI application (`app.py`), `/predict` endpoint, Pydantic request validation, model serving & caching (`visaModel`), frontend interface support, documentation |
|
| 390 |
-
| Bilal | Module 4: Frontend Interface | 5, 6 & 7 | Prediction UI (`visa.html`), step-by-step loading animation, SHAP results display (strengths/weaknesses/suggestions), responsive design, cloud infrastructure support, CI/CD support, documentation |
|
| 391 |
-
|
| 392 |
-
**Module breakdown**:
|
| 393 |
-
|
| 394 |
-
| Module | Description |
|
| 395 |
-
|--------|-------------|
|
| 396 |
-
| Module 1 | Data Collection & Preprocessing — dataset ingestion, schema validation, feature engineering, encoding pipelines |
|
| 397 |
-
| Module 2 | Machine Learning — model selection, training, hyperparameter tuning, SMOTEENN, evaluation |
|
| 398 |
-
| Module 3 | Backend API — FastAPI endpoints, request validation, model serving, SHAP integration |
|
| 399 |
-
| Module 4 | Frontend Interface — prediction UI, result visualization, loading states, responsive design |
|
| 400 |
-
| Module 5 | Cloud Infrastructure — AWS S3, Docker containerization, Hugging Face Spaces deployment |
|
| 401 |
-
| Module 6 | MLOps & CI/CD — automated pipeline, model versioning, deployment automation |
|
| 402 |
-
| Module 7 | Documentation — project report, EDA notebook, README, code documentation |
|
| 403 |
-
|
| 404 |
-
---
|
| 405 |
-
|
| 406 |
-
## 13. References
|
| 407 |
-
|
| 408 |
-
1. **EasyVisa Dataset** — Phani Srinivas et al. Historical PERM labor certification records. Available on Kaggle. https://www.kaggle.com/datasets/moro23/easyvisa-dataset
|
| 409 |
-
|
| 410 |
-
2. **SHAP (SHapley Additive exPlanations)** — Lundberg, S. M., & Lee, S. I. (2017). A Unified Approach to Interpreting Model Predictions. *Advances in Neural Information Processing Systems (NeurIPS)*. https://github.com/shap/shap
|
| 411 |
-
|
| 412 |
-
3. **SMOTEENN** — Batista, G. E. A. P. A., Prati, R. C., & Monard, M. C. (2004). A Study of the Behavior of Several Methods for Balancing Machine Learning Training Data. *ACM SIGKDD Explorations Newsletter*, 6(1), 20–29. Implementation: imbalanced-learn. https://imbalanced-learn.org/stable/references/generated/imblearn.combine.SMOTEENN.html
|
| 413 |
-
|
| 414 |
-
4. **XGBoost** — Chen, T., & Guestrin, C. (2016). XGBoost: A Scalable Tree Boosting System. *Proceedings of the 22nd ACM SIGKDD International Conference on Knowledge Discovery and Data Mining*. https://xgboost.readthedocs.io/
|
| 415 |
-
|
| 416 |
-
5. **scikit-learn** — Pedregosa, F., et al. (2011). Scikit-learn: Machine Learning in Python. *Journal of Machine Learning Research*, 12, 2825–2830. https://scikit-learn.org/
|
| 417 |
-
|
| 418 |
-
6. **FastAPI** — Ramirez, S. FastAPI framework, high performance, easy to learn, fast to code. https://fastapi.tiangolo.com/
|
| 419 |
-
|
| 420 |
-
7. **Hugging Face Spaces** — Hugging Face. Docker SDK for Spaces deployment. https://huggingface.co/docs/hub/spaces-sdks-docker
|
| 421 |
-
|
| 422 |
-
8. **CRISP-DM** — Wirth, R., & Hipp, J. (2000). CRISP-DM: Towards a Standard Process Model for Data Mining. *Proceedings of the 4th International Conference on the Practical Applications of Knowledge Discovery and Data Mining*.
|
| 423 |
-
|
| 424 |
-
9. **Tree-based models vs. deep learning on tabular data** — Grinsztajn, L., Oyallon, E., & Varoquaux, G. (2022). Why do tree-based models still outperform deep learning on typical tabular data? *Advances in Neural Information Processing Systems (NeurIPS)*. https://arxiv.org/abs/2207.08815
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|