Hunt Scoring¶
ML-Based Hunt Scoring System¶
The ml_hunt_score is an article-level score produced by a RandomForest classifier operating on text chunks. It complements the keyword-based threat_hunting_score (0-100) with a model-trained perspective.
How It Works¶
- Chunk analysis: Articles are split into chunks (default: 1000 chars, 200-char overlap).
- ML prediction: The RandomForest classifier labels each chunk "Huntable" or "Not Huntable" with a confidence score (0-1).
- Score aggregation: Chunk-level predictions aggregate into an article-level score (0-100).
Metric Options (Historical Reference, Not Runtime-Selectable)¶
Historical reference only. The per-article
ml_hunt_scoreaggregate was retired.calculate_ml_hunt_scoreandupdate_article_ml_hunt_scoreno longer exist inChunkAnalysisService;store_chunk_analysisdoes not writeml_hunt_scoreto article metadata. Existing rows retain the field as legacy data. No config key or CLI flag exposes metric selection; this section documents the options that existed when the feature was active.
1. weighted_average (was the default)¶
Formula: average(ml_confidence for huntable chunks) * 100
Average confidence across chunks where ML predicts "Huntable."
Pros: - Simple to interpret - Focuses on confidence quality, not chunk count - Not affected by article length - Returns 0 if no huntable chunks
Cons: - Ignores the proportion of huntable chunks - A single high-confidence chunk can inflate the score
Use case: Identifying articles with high-quality huntable content.
2. proportion_weighted¶
Formula: (huntable_chunks / total_chunks) * average_confidence * 100
Combines the proportion of huntable chunks with average confidence.
Pros: - Balances quantity and quality - Rewards articles with more huntable content
Cons: - Can mislead when an article has many low-confidence huntable chunks - Harder to interpret than a simple average
Use case: When rewarding articles with substantial huntable coverage matters.
3. confidence_sum_normalized¶
Formula: sum(ml_confidence for huntable chunks) / total_chunks * 100
Sum of huntable confidences, normalized by total chunk count.
Pros: - Accounts for both count and confidence - Normalization prevents length bias
Cons: - Can be low even with high-quality chunks if the article is long - Less intuitive than other metrics
Use case: Penalizing long articles with sparse huntable content.
4. top_percentile¶
Formula: 75th_percentile(ml_confidence for huntable chunks) * 100
Uses the 75th percentile confidence to reduce outlier impact.
Pros: - Robust to outliers - Reflects typical quality rather than extremes
Cons: - Ignores lower-confidence chunks - May miss articles with consistent moderate confidence
Use case: Filtering noise and focusing on typical prediction quality.
5. user_proposed¶
Formula: sum(confidences > 0.5) / count(confidences > 0.5) * 100
Average confidence of chunks above a 50% threshold.
Pros: - Filters low-confidence predictions - Threshold-based logic is simple to reason about
Cons:
- Division-by-zero risk if no chunks exceed the threshold
- Ignores chunks just below the cutoff
- Bypasses the ml_prediction field; relies on confidence threshold only
Use case: When only high-confidence predictions matter.
Implementation¶
Note: Per-article
ml_hunt_scoreaggregation was retired.store_chunk_analysisstores chunk-level predictions inchunk_analysis_resultsbut no longer computes or writes an article-levelml_hunt_score. Legacy articles that were scored before the retirement retainml_hunt_scorein theirarticle_metadata; new chunk analysis runs do not produce this field.
Recalculation¶
The ML vs Hunt Comparison Dashboard (POST /api/ml-model-performance/backfill) reruns chunk-level predictions across all articles with hunt_score > 50. It does not rewrite ml_hunt_score; chunk analysis results are the current scoring surface.
Comparison with Keyword-Based Score¶
| Aspect | Keyword score (threat_hunting_score) |
ML score (ml_hunt_score) |
|---|---|---|
| Method | Pattern matching (~114 perfect discriminators; ~528 total keywords) | RandomForest classification |
| Granularity | Article-level | Chunk-level aggregation |
| Range | 0-100 | 0-100 |
| Speed | Fast (regex) | Slower (ML inference) |
| Training | Rule-based | Trained on user feedback |
| Use case | Initial filtering | Quality assessment |
Best Practices¶
- Use both scores: keyword score for fast filtering, ML score for quality assessment.
- Monitor correlation: track how ML scores correlate with keyword scores over time.
- Retrain regularly: update the model as new feedback accumulates.
- Tune thresholds: adjust workflow trigger thresholds based on observed score distributions.
Troubleshooting¶
No ML Hunt Score¶
Symptom: ml_hunt_score missing from article metadata.
Causes:
- Article has no chunk analysis results (requires threat_hunting_score > 50)
- Chunk analysis has not been run yet
- All chunks predicted as "Not Huntable"
Resolution: Run chunk analysis first, then recalculate ML scores.
Score Seems Too Low or Too High¶
Symptom: ML scores do not match expectations.
Causes: - Model version mismatch between chunks and current model - Chunk size or overlap settings differ from training
Resolution: - Inspect chunk analysis details via the ML vs Hunt Comparison Dashboard - Verify model version consistency across chunk records
ML vs Hunt Comparison Dashboard¶
The ML vs Hunt Comparison Dashboard compares RandomForest predictions against the keyword hunt score across model versions. Use it to monitor model drift, trigger retraining, run evaluations, and process backfill.
Accessing the Dashboard¶
- Open the main menu.
- Click ML vs Hunt Comparison.
- The dashboard loads with current model statistics.
Classification Timeline Data¶
GET /api/model/classification-timeline computes how ML and hunt score predictions align across model versions, tracking four categories:
| Category | Definition |
|---|---|
| Agreement (Both Huntable) | Both ML and Hunt classify the chunk as huntable |
| ML Only | Only ML classifies as huntable |
| Hunt Only | Only Hunt classifies as huntable |
| Neither | Both systems classify as not huntable |
Interpretation: - Increasing Agreement: model is converging with hunt scoring - Decreasing ML Only: model is becoming more conservative - Stable Neither: consistent filtering of non-huntable content
Model Retraining¶
How It Works¶
Retraining is cumulative: each session builds on all prior data.
- Original training data (baseline model)
- All previous feedback (from every prior retraining session)
- All previous annotations (from every prior retraining session)
- New feedback and annotations (since last retraining)
Prerequisites¶
- User feedback collected via the chunk debugging interface
- At least one unused feedback or annotation sample (the Retrain button disables if the count is zero)
Retraining Steps¶
- Check the Available feedback count in the retraining panel.
- Click Retrain Model.
- Monitor the progress bar.
- Review the new model version and performance metrics.
After retraining, new feedback is marked "used." Previously used feedback stays in the training dataset and is reused in subsequent sessions.
Model Evaluation¶
Running an Evaluation¶
- Click Evaluate Current Model.
- Monitor the progress bar.
- Review results in the metrics panel.
Metrics¶
| Metric | Definition |
|---|---|
| Accuracy | Overall percentage of correct predictions |
| Precision (Huntable) | Of predicted huntable: fraction that were actually huntable |
| Recall (Huntable) | Of actually huntable: fraction that were predicted huntable |
| F1 Score (Huntable) | Harmonic mean of precision and recall |
| Confusion Matrix | Breakdown of true/false positives and negatives |
Test Set¶
- Size: 317 annotated chunks (the committed
eval_set.csv; was 240 before the 2026-05 fixture sync) - Source:
config/labeled_chunks/source files, built intooutputs/evaluation_data/eval_set.csvbyscripts/prepare_eval_set.py - Labeling: manually annotated by users
Performance Visualization¶
Accuracy Trends Chart¶
A single line chart plots Accuracy %, Precision (Huntable) %, Recall (Huntable) %, and F1 (Huntable) % across model versions (v0, v1, v2, ...). Use this to confirm that retraining is improving the model, not degrading it.
Backfill Processing¶
Processes articles with hunt_score > 50 to populate chunk analysis data for the comparison dashboard.
Steps¶
- Check the Eligible count in the backfill panel.
- Click Process All Eligible Articles.
- Monitor the progress bar.
- Review the processing summary.
Eligibility Criteria¶
hunt_score > 50(default threshold)- Not already processed for chunk analysis
- Passes quality thresholds
API Endpoints¶
Model Management (/api/model/*)¶
| Method | Path | Description |
|---|---|---|
GET |
/api/model/versions |
List all model versions with performance metrics |
GET |
/api/model/classification-timeline |
Classification trends data for the time series chart |
POST |
/api/model/retrain |
Trigger model retraining |
POST |
/api/model/evaluate |
Evaluate current model on the test set |
Feedback Management¶
| Method | Path | Description |
|---|---|---|
GET |
/api/model/feedback-count |
Count of available feedback and annotation samples |
POST |
/api/feedback/chunk-classification |
Submit user feedback for a chunk |
Data Processing: Canonical Router (/api/ml-model-performance/*)¶
| Method | Path | Description |
|---|---|---|
GET |
/api/ml-model-performance/eligible-count |
Count of articles eligible for chunk analysis |
POST |
/api/ml-model-performance/backfill |
Process eligible articles |
GET |
/api/ml-model-performance/summary |
Dashboard summary statistics |
GET |
/api/ml-model-performance/stats |
Detailed comparison statistics |
Configuration¶
Content Filter Settings¶
Three settings influence dashboard metrics:
| Setting | Default | Effect |
|---|---|---|
min_confidence |
0.7 (70%) |
Chunks below this confidence threshold are not considered huntable |
quality_threshold |
0.5 (50%) |
Articles below this quality score are not chunked |
cost_threshold |
0.1 (10%) |
Articles with estimated processing cost above this value are skipped |
Data Scope¶
- Only articles with
hunt_score > 50are eligible for chunk analysis. - Only chunks with ML confidence ≥ 70% are counted as huntable.
Troubleshooting¶
"Retrain Model" Button Disabled¶
Cause: No unused feedback or annotation samples available. Resolution: Provide feedback via the chunk debugging interface, then check the "Available feedback" count.
Chart Not Loading¶
Cause: JavaScript error or no data for the selected model version. Resolution: Refresh the page and check the browser console for errors. Verify all containers are running.
Evaluation Fails¶
Cause: Missing test data or model file not found.
Resolution: Confirm that article_annotations has data and that outputs/evaluation_data/eval_set.csv exists. Verify model artifact files are present.
Backfill Processing Fails¶
Cause: No eligible articles or runtime error during processing.
Resolution: Check the eligible count. Review server logs for the specific error. Confirm articles have hunt_score > 50.
Performance¶
- Charts with large datasets may take several seconds to render.
- Clear the browser cache if charts display stale or broken data.
- Inspect API responses in browser developer tools if data looks wrong.
Best Practices¶
Before Retraining¶
- Run an evaluation to record the current model's baseline metrics.
- Confirm there is sufficient new feedback (visible in the retraining panel).
- Watch classification trends for drift before triggering retraining.
Monitoring¶
- Check the dashboard weekly for metric trends.
- Watch for sudden shifts in Agreement or ML Only categories; these signal distribution changes.
- Prioritize high-quality, representative feedback over volume.
Last updated: 2026-07-05 Last reviewed: 2026-09-01