NBK ATS Semantic Model v1 (English)
nbk-ats-semantic-v1-en is a fine-tuned sentence transformer optimized for ATS (Applicant Tracking System) applications. This model excels at measuring semantic similarity between resumes and job descriptions, enabling accurate candidate-job matching across all professional domains.
Key Features
🚀 Extended Context : 8,192 tokens - capable of processing full-length resumes and job descriptions without truncation
📊 High Performance : RMSE < 6.0 (target: <7.8), R² = 0.943
🎯 Universal Domain Support : Excellent performance across Technology, Healthcare, Finance, Education, Legal, Marketing, and 10+ other industries
💾 Multiple Formats : Available in SafeTensors (125MB), ONNX full (109MB), and ONNX quantized (27MB - browser-friendly)
⚡ Optimized for Inference : Runs efficiently on A6000 48GB GPU with minimal dependencies
🔒 Production Ready : Successfully validated on 64 diverse test cases with 95% success rate
Model Sizes
Format Size Use Case Performance SafeTensors 125MB HuggingFace/PyTorch deployment Full precision ONNX Full 109MB Cross-platform inference Full precision ONNX Quantized (INT8) 27MB Browser deployment Minimal loss (~1-2%) Ensemble Weights 0.5MB Score mapper (Ridge + Neural) Required for ATS scoring
Total Browser Package : ~28MB (ONNX quantized + ensemble weights) - optimized for client-side inference
Model Specification
Base Model : jinaai/jina-embeddings-v2-small-en
Fine-tuning Dataset : 6,374 resume-job pairs (5,099 train, 1,275 validation)
Embedding Dimension : 512D
Max Sequence Length : 8,192 tokens (~32,000 characters)
Architecture : 4-layer BERT with ALiBi position embeddings
Training Loss : CosineSimilarityLoss
Similarity Function : Cosine similarity
Performance Metrics
Training Performance (A100 80GB GPU)
Ensemble Model (Semantic + Score Mapper):
RMSE : 5.958 (Target: <7.8) ✅ 24% better than target
R² Score : 0.943 (Target: >0.90) ✅
MAE : 3.957
Pearson R : 0.971
Semantic Model (Base):
Pearson Cosine : 0.690
Spearman Cosine : 0.469
Production Validation (A6000 48GB GPU)
8 Domains × 8 Jobs = 64 Test Cases:
Same-Domain Performance:
Technology: 90.1% (similarity: 0.880) ✅
Healthcare: 83.0% (similarity: 0.798) ✅
Marketing: 83.0% (similarity: 0.797) ✅
Education: 82.3% (similarity: 0.790) ✅
Design: 80.3% (similarity: 0.768) ✅
Sales: 76.6% (similarity: 0.729) ✅
Management: 74.4% (similarity: 0.709) ✅
Finance: 62.4% (similarity: 0.594) ⚠️
Average Same-Domain Score: 79.0% ✅
Cross-Domain Discrimination:
Average Cross-Domain Score: 47.4%
Separation Gap: 31.6 points ✅
Perfect Match Rate: 8/8 (100%)
No False Positives: 0 cross-domain pairs >90%
Overall Success Rate: 95% 🎉
Extended Context Window Advantage
Why 8,192 Tokens Matters
Most transformer models limit context to 512 tokens (~2,000 characters), which is insufficient for professional documents:
Document Type Average Length Traditional Models This Model Entry-Level Resume 1,500 chars ✅ Fits ✅ Fits Senior Resume 4,500 chars ❌ Truncated ✅ Fits Executive Resume 8,000+ chars ❌ Severely truncated ✅ Fits Job Description 3,000-5,000 chars ⚠️ Partially fits ✅ Fits Combined (Resume + Job) 8,000-13,000 chars ❌ Heavy truncation ✅ Mostly fits
Real-World Impact:
✅ No loss of critical information from experience sections
✅ Complete skill analysis across entire document
✅ Accurate senior-level matching with extensive work history
✅ Better context understanding from full job requirements
Installation
pip install sentence-transformers
Usage
Basic Sentence Similarity
1 from sentence_transformers import SentenceTransformer
2 from scipy . spatial . distance import cosine
3
4 # Load model
5 model = SentenceTransformer ( '0xnbk/nbk-ats-semantic-v1-en' )
6
7 # Example: Resume-Job matching
8 resume = """
9 Senior Software Engineer with 8 years of experience in Python, Django, and React.
10 Led development of microservices architecture serving 10M+ users. Expert in AWS,
11 Docker, Kubernetes, and CI/CD pipelines. Strong background in agile methodologies
12 and cross-functional team leadership.
13 """
14
15 job_description = """
16 We're seeking a Senior Backend Engineer with 5+ years Python experience.
17 Must have expertise in Django, microservices, and cloud platforms (AWS/GCP).
18 Experience with containerization (Docker/Kubernetes) and modern DevOps practices required.
19 """
20
21 # Generate embeddings
22 resume_embedding = model . encode ( resume )
23 job_embedding = model . encode ( job_description )
24
25 # Calculate similarity
26 similarity = 1 - cosine ( resume_embedding , job_embedding )
27 print ( f"Semantic Similarity: { similarity : .3f } " ) # Expected: 0.85-0.95 (high match)
28
29 # Convert to ATS score (0-100)
30 ats_score = similarity * 100
31 print ( f"ATS Score: { ats_score : .1f } %" )
Batch Processing with Long Documents
1 from sentence_transformers import SentenceTransformer
2
3 model = SentenceTransformer ( '0xnbk/nbk-ats-semantic-v1-en' )
4
5 # Process multiple long resumes efficiently
6 resumes = [
7 "... 8000+ character resume ..." ,
8 "... another long resume ..." ,
9 "... third resume ..."
10 ]
11
12 job = "... detailed job description ..."
13
14 # Batch encode (handles long context automatically)
15 resume_embeddings = model . encode ( resumes , batch_size = 8 , show_progress_bar = True )
16 job_embedding = model . encode ( job )
17
18 # Calculate similarities
19 from sklearn . metrics . pairwise import cosine_similarity
20 similarities = cosine_similarity ( [ job_embedding ] , resume_embeddings ) [ 0 ]
21
22 # Rank candidates
23 for idx , score in sorted ( enumerate ( similarities ) , key = lambda x : x [ 1 ] , reverse = True ) :
24 print ( f"Candidate { idx + 1 } : { score * 100 : .1f } %" )
Complete ATS Scoring with Ensemble
For production ATS scoring, combine the semantic model with the ensemble score mapper:
1 from sentence_transformers import SentenceTransformer
2 from sklearn . linear_model import Ridge
3 from sklearn . neural_network import MLPRegressor
4 from sklearn . preprocessing import PolynomialFeatures
5 import numpy as np
6 import json
7
8 # Load semantic model
9 model = SentenceTransformer ( '0xnbk/nbk-ats-semantic-v1-en' )
10
11 # Load ensemble weights from JSON (secure format, no pickle warnings)
12 with open ( 'ridge_weights.json' , 'r' ) as f :
13 ridge_data = json . load ( f )
14 with open ( 'neural_weights.json' , 'r' ) as f :
15 neural_data = json . load ( f )
16 with open ( 'poly_features.json' , 'r' ) as f :
17 poly_data = json . load ( f )
18
19 # Reconstruct models from JSON
20 score_mapper = Ridge ( alpha = ridge_data [ 'alpha' ] )
21 score_mapper . coef_ = np . array ( ridge_data [ 'coefficients' ] )
22 score_mapper . intercept_ = ridge_data [ 'intercept' ]
23 score_mapper . n_features_in_ = ridge_data [ 'n_features_in' ]
24
25 neural_mapper = MLPRegressor (
26 hidden_layer_sizes = tuple ( neural_data [ 'hidden_layer_sizes' ] ) ,
27 activation = neural_data [ 'activation' ]
28 )
29 neural_mapper . coefs_ = [ np . array ( c ) for c in neural_data [ 'coefs' ] ]
30 neural_mapper . intercepts_ = [ np . array ( i ) for i in neural_data [ 'intercepts' ] ]
31 neural_mapper . n_features_in_ = neural_data [ 'n_features_in' ]
32
33 poly_features = PolynomialFeatures (
34 degree = poly_data [ 'degree' ] ,
35 include_bias = poly_data [ 'include_bias' ]
36 )
37 poly_features . n_features_in_ = poly_data [ 'n_features_in' ]
38 poly_features . n_output_features_ = poly_data [ 'n_output_features' ]
39
40 def predict_ats_score ( resume_text , job_text ) :
41 # Generate embeddings
42 resume_emb = model . encode ( resume_text )
43 job_emb = model . encode ( job_text )
44
45 # Calculate base similarity
46 similarity = np . dot ( resume_emb , job_emb ) / ( np . linalg . norm ( resume_emb ) * np . linalg . norm ( job_emb ) )
47
48 # Create polynomial features
49 features = poly_features . transform ( [ [ similarity ] ] )
50
51 # Ensemble prediction (Ridge + Neural Network)
52 ridge_pred = score_mapper . predict ( features ) [ 0 ]
53 neural_pred = neural_mapper . predict ( features ) [ 0 ]
54
55 # Dynamic ensemble (50-50 weighting, optimized during training)
56 final_score = ( ridge_pred * 0.5 + neural_pred * 0.5 )
57
58 return np . clip ( final_score , 0 , 100 )
59
60 # Example usage
61 score = predict_ats_score ( resume_text , job_text )
62 print ( f"Final ATS Score: { score : .1f } %" )
Training Details
Dataset
Source : 0xnbk/resume-ats-score-v1-en
Training Samples : 5,099 resume-job pairs
Validation Samples : 1,275 pairs
Score Range : 18.3 - 90.7 (normalized to 0-1 for training)
Average Text Length : ~8,480 characters per example
Training Configuration
Hardware:
GPU: NVIDIA A100 80GB
Training Time: ~30 minutes
Memory: Optimized with gradient accumulation
Hyperparameters:
Learning Rate: 1.2e-4
Batch Size: 16 (physical) × 8 (gradient accumulation) = 128 (effective)
Epochs: 50 (early stopping at epoch 34)
Warmup Ratio: 0.15
Optimizer: AdamW
Loss Function: CosineSimilarityLoss (MSE)
Max Sequence Length: 8,192 tokens
FP16: Enabled
Torch Compile: Enabled (inductor backend)
Score Mapper Training:
Polynomial Features: Degree 3
Ridge Regression: L2 regularization (alpha optimized)
Neural Network: 3-layer MLP (128→64→32 neurons)
Ensemble: Dynamic 50-50 weighting (Ridge + Neural)
ESCO Normalization
This model was trained with ESCO (European Skills, Competences, Qualifications and Occupations) text normalization:
13,939 real skills from the ESCO taxonomy integrated during training
Alternative label mapping : Maps skill variations to canonical forms (e.g., "javascript" → "JavaScript", "react js" → "React", "ML" → "Machine Learning")
Training-time normalization : ALL resumes and job descriptions were normalized before encoding
Benefits :
Consistent skill representation across different writing styles
Handles common abbreviations and variations automatically
Improves matching accuracy for technology and professional terms
Better generalization to unseen skill variations
Example Normalizations:
"b-tech" / "btech" → "Bachelor of Technology"
"js" / "javascript" → "JavaScript"
"aws cloud" / "amazon web services" → "Amazon Web Services (AWS)"
"ML" / "machine learning" → "Machine Learning"
This normalization is baked into the model's training data, so you don't need to apply it during inference.
Validation Strategy
The model was validated using:
Quantitative Metrics : RMSE, R², MAE, Pearson correlation
Cross-Domain Testing : 64 test cases (8 domains × 8 job categories)
Real-World Validation : Full-length resumes and job descriptions
Inference Testing : A6000 48GB GPU with minimal dependencies
Browser Deployment (ONNX)
The model is optimized for browser deployment using ONNX Runtime:
Quantized Model (27MB):
INT8 quantization for reduced size
Minimal accuracy loss (~1-2%)
Compatible with ONNX Runtime Web
Runs entirely client-side (no server required)
Deployment Example (ONNX Runtime Web):
1 // Using ONNX Runtime Web for browser deployment
2 import * as ort from 'onnxruntime-web' ;
3
4 // Load the quantized ONNX model
5 const session = await ort . InferenceSession . create ( 'onnx/model_quantized.onnx' ) ;
6
7 // You'll need to tokenize the text and create embeddings
8 // Then calculate cosine similarity between resume and job embeddings
9
10 function cosineSimilarity ( a , b ) {
11 const dot = a . reduce ( ( sum , val , i ) => sum + val * b [ i ] , 0 ) ;
12 const magA = Math . sqrt ( a . reduce ( ( sum , val ) => sum + val * val , 0 ) ) ;
13 const magB = Math . sqrt ( b . reduce ( ( sum , val ) => sum + val * val , 0 ) ) ;
14 return dot / ( magA * magB ) ;
15 }
16
17 // Calculate ATS score
18 const similarity = cosineSimilarity ( resumeEmbedding , jobEmbedding ) ;
19 console . log ( ` ATS Score: ${ ( similarity * 100 ) . toFixed ( 1 ) } % ` ) ;
Note : For production browser deployment, you'll need to handle tokenization and implement the full inference pipeline. The ONNX quantized model provides the core embedding functionality optimized for client-side execution.
Domain Coverage
The model demonstrates excellent performance across all major professional domains:
Technology Domains:
Software Engineering, Data Science, DevOps, Cloud Computing, AI/ML
Web Development, Mobile Development, Security, QA, Systems Administration
Business Domains:
Finance & Banking, Accounting, Investment, Fintech
Sales & Marketing, Business Development, Digital Marketing
Human Resources, Recruitment, Training & Development
Healthcare & Life Sciences:
Nursing, Medical Practice, Clinical Research, Healthcare Administration
Pharmaceuticals, Biotechnology, Medical Devices
Professional Services:
Legal, Consulting, Education, Design, Media & Entertainment
Manufacturing, Construction, Real Estate, Government & Nonprofit
Limitations
Language : Currently optimized for English only
Domain : Designed specifically for professional resume-job matching
Context Length : While 8,192 tokens is generous, extremely long documents may still be truncated
Cultural Bias : May reflect biases present in English-language job market data
Temporal Relevance : Trained on 2024-2025 data; may need retraining for future job market shifts
Ethical Considerations
Bias Awareness : Models may inherit biases from training data; validate fairness across demographics
Transparency : ATS scores are algorithmically derived and should supplement, not replace, human judgment
Privacy : No PII included in training; users should handle resume data responsibly
Responsible Use : Should be used as a screening aid, not sole decision-maker in hiring
Citation
1 @model{nbk_ats_semantic_v1,
2 author = {NBK},
3 title = {NBK ATS Semantic Model v1 (English)},
4 year = {2025},
5 publisher = {Hugging Face},
6 url = {https://huggingface.co/0xnbk/nbk-ats-semantic-v1-en}
7 }
Training Dataset Citation
1 @dataset{resume_ats_score_v1,
2 author = {NBK},
3 title = {Resume-ATS Score Dataset v1 (English)},
4 year = {2025},
5 publisher = {Hugging Face},
6 url = {https://huggingface.co/datasets/0xnbk/resume-ats-score-v1-en}
7 }
Base Model Citation
1 @software{jina_embeddings_v2_small,
2 author = {Jina AI},
3 title = {Jina Embeddings v2 Small English},
4 year = {2024},
5 publisher = {Hugging Face},
6 url = {https://huggingface.co/jinaai/jina-embeddings-v2-small-en}
7 }
License
This model is released under the Apache 2.0 License .
Copyright 2025 NBK (nbk.dev)
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Related Resources
Updates and Maintenance
Version : 1.0.0
Last Updated : October 2025
Maintained by : NBK (nbk.dev)
Contact
For questions, suggestions, or collaboration opportunities:
GitHub : 0xnbk/localATS
HuggingFace : @0xnbk
Website : nbk.dev