Spaces:
Sleeping
Skill Extractor Optimization Plan
Current Performance
- Inference time: 40-60 seconds per request
- Hardware: 2 vCPUs, 18GB RAM
- Model: Qwen2.5-1.5B-Instruct (Q4_K_M quantization)
Optimization Strategies (Ranked by Impact)
1. Switch to Smaller/Faster Model ⚡⚡⚡
Impact: 3-5x speedup | Effort: Medium
Options:
Qwen2.5-0.5B-Instruct-Q4_K_M (~1.5GB vs 1GB)
- Pros: Much faster inference, lower memory usage
- Cons: Slightly less accurate on complex skills
- Expected speedup: 3-4x
Phi-3-mini-4k-instruct-Q4_K_M (~2GB)
- Pros: Optimized for speed, good reasoning
- Cons: Different model, may need prompt tuning
- Expected speedup: 2-3x
Gemma-2-2b-it-Q4_K_M (~1.6GB)
- Pros: Fast, good instruction following
- Cons: Slightly larger than 0.5B options
- Expected speedup: 2-3x
Implementation:
- Update Dockerfile to download new model
- Change default model path in
get_instance() - Test accuracy on sample job descriptions
2. Remove Grammar Constraint ⚡⚡
Impact: 20-30% speedup | Effort: Low
Current bottleneck: JSON grammar validation adds significant overhead
Approach:
- Remove
grammar=self.grammarfromcreate_chat_completion() - Use simpler regex-based JSON parsing
- Add fallback for malformed output
Trade-off: Slightly less structured output, but parsing is robust
3. Aggressive Prompt Compression ⚡⚡
Impact: 15-25% speedup | Effort: Low
Current prompt: ~200 tokens Optimized prompt: ~80 tokens
Changes:
# Current (verbose)
"Extract skills from the job posting as short 2-4 word phrases. "
"Ignore company boilerplate. Never output fragments. "
"required_skills = explicitly mandatory. "
"nice_to_have_skills = preferred/optional. "
"Include mandatory degrees/certifications in required_skills."
# Optimized (concise)
"Extract skills as 2-4 word phrases. "
"required=mandatory, nice_to_have=optional. "
"Include degrees/certs in required."
4. Reduce Output Tokens ⚡
Impact: 10-15% speedup | Effort: Low
Current: _MAX_TOKENS = 64
Optimized: _MAX_TOKENS = 32
Most skill extractions need <20 tokens. 32 provides safety margin.
5. Add Response Caching ⚡⚡
Impact: Near-instant for duplicates | Effort: Medium
Strategy:
- Hash job_description + job_title
- Cache results in memory (dict) or Redis
- Set TTL of 24-48 hours
- Cache hit rate typically 20-30% for job boards
Implementation:
_cache = {}
def extract(self, job_description: str, job_title: str = ""):
key = hashlib.md5(f"{job_title}:{job_description}".encode()).hexdigest()
if key in _cache:
return _cache[key]
result = # ... extraction logic
_cache[key] = result
return result
6. Hybrid Approach: LLM + Rule-Based ⚡⚡⚡
Impact: 5-10x speedup for common patterns | Effort: High
Strategy:
- Build regex/keyword-based extractor for common skills
- Use LLM only for complex/ambiguous cases
- 60-80% of requests can be handled by rules
Rule-based examples:
- "Python", "Java", "JavaScript" → programming languages
- "AWS", "Azure", "GCP" → cloud platforms
- "Docker", "Kubernetes" → devops tools
- "Bachelor's", "Master's" → degrees
Fallback: If rule extraction fails or confidence low, use LLM
7. Optimize Context Window ⚡
Impact: 5-10% speedup | Effort: Low
Current: n_ctx = 1024
Optimized: Dynamic based on input length
Strategy:
- Calculate input tokens
- Set
n_ctx = input_tokens + _MAX_TOKENS + 100 - Minimum 512, maximum 2048
8. Batch Processing (For Bulk Operations) ⚡⚡
Impact: 2-3x throughput | Effort: Medium
Use case: Processing multiple job descriptions
Strategy:
- Add
/extract_batchendpoint - Process 5-10 descriptions in parallel
- Use threading with model instance lock
9. Use GPU Acceleration ⚡⚡⚡
Impact: 5-10x speedup | Effort: Low (if GPU available)
Current: CPU-only inference Optimized: GPU offloading
Implementation:
- Already have
n_gpu_layers=-1in code - Ensure CUDA-compatible llama-cpp-python is installed
- If no GPU available, this has no effect
Docker changes:
# Install CUDA toolkit if GPU available
RUN pip install llama-cpp-python --extra-index-url https://abetlen.github.io/llama-cpp-python/whl/cu124
10. Alternative: Use vLLM or TensorRT-LLM ⚡⚡⚡
Impact: 2-5x speedup | Effort: High
Strategy:
- Replace llama-cpp-python with vLLM
- Better batching, optimized inference
- Requires more setup but significant speed gains
Trade-off: More complex deployment, larger memory footprint
Recommended Implementation Order
Phase 1: Quick Wins (Total: 2-3x speedup)
- Remove grammar constraint (20-30%)
- Compress prompt (15-25%)
- Reduce max tokens to 32 (10-15%)
- Optimize context window (5-10%)
Expected result: 15-25 seconds per request
Phase 2: Model Change (Total: 3-5x from baseline)
- Switch to Qwen2.5-0.5B or Phi-3-mini
Expected result: 8-15 seconds per request
Phase 3: Advanced Optimizations
- Add response caching
- Implement hybrid rule-based approach
- Enable GPU acceleration (if available)
Expected result: 3-8 seconds per request (with cache hits near-instant)
Alternative: Non-LLM Approach
Pure Rule-Based Extraction
Speed: <100ms | Accuracy: 70-80%
Strategy:
- Build comprehensive skill taxonomy (500-1000 known skills)
- Use fuzzy matching (Levenshtein, TF-IDF)
- Add NLP for context (spaCy, NLTK)
- No LLM required
Pros:
- Extremely fast
- Predictable costs
- No model management
Cons:
- Lower accuracy on novel skills
- Requires manual taxonomy maintenance
- Misses contextual nuances
Performance Targets
| Approach | Target Time | Accuracy | Effort |
|---|---|---|---|
| Current | 40-60s | 85-90% | - |
| Phase 1 | 15-25s | 85-90% | Low |
| Phase 1+2 | 8-15s | 80-85% | Medium |
| Phase 1+2+3 | 3-8s | 80-85% | High |
| Hybrid | 1-5s | 75-85% | High |
| Rule-based | <0.1s | 70-80% | High |
Next Steps
- Implement Phase 1 (grammar removal, prompt compression)
- Test accuracy on sample job descriptions
- If acceptable, deploy and monitor
- If too slow, proceed to Phase 2 (model change)
- Consider hybrid approach for production scale