Statistical Lithology Classifier (Phase 3) -- Purity-Weighted XGBoost
Same XGBoost approach as Phase 2, with one change: Phase 2 treats every WellStrat-picked interval as 100% pure the rock type it's labelled -- but checking the database shows only 30.6% of the intervals behind these classes actually are 100% pure; roughly a quarter are below 75% pure, real interbedding and mixing, not noise. Phase 3 weights each training sample by its own recorded purity (WellStrat's litho1_perc), so a 75%-pure Sandstone interval counts for 0.75x a training example, not the same as a genuinely 100%-pure one. A hard cutoff (only train on 100%-pure intervals) was considered and rejected -- it would leave Coal, already the thinnest class, with just 14 rows database-wide, below the minimum needed to train on it at all. Weighting lets impure intervals still contribute, in proportion to how trustworthy their label actually is.
What "accuracy" means here: Same WellStrat ground truth as Phase 2, same caveat about training directly on the records being checked against (not independent proof of geology). The difference here is TRAINING only: how much each sample counts while learning. Evaluation against the held-out test wells is identical to Phase 2's, so the two models' accuracy scores are directly comparable.
This version's training details
GR-identity-verified, >=10% coverage, labelled WellStrat interval), PLUS (2026-08-08) a curve-completeness requirement: a well only trains this model if it also carries the full 'standard suite' -- Gamma Ray, Caliper, Bulk Density, Sonic, Deep Resistivity, Neutron Porosity, and Density Correction. One exception: any well containing Coal is always included, regardless of how many curves it has - excluding thin-curve wells would risk losing most of its training examples entirely. Purity-weighted: each training sample's weight adapted to the percentage of lithology recorded within the interval, as per the OGA/NSTA WellStrat DB convention. Feature set expanded to include shallow, Rxo (flushed-zone), and medium resistivity plus, where available, NMR (Nuclear Magnetic Resonance) porosity, bound-water, free-fluid, and T2 curves. This version also reads caliper (borehole diameter) -- hole washout is an indirect lithology indicator. Curve-level quality control added (2026-08-09) based on the results of an extended review pass across every curve type feeding this model.
- nmr_curves
- ['nmr_porosity', 'nmr_bound_water', 'nmr_free_fluid_index', 'nmr_t2']
- random_seed
- 42
- extra_curves
- ['shallow_log_res', 'rxo_log_res', 'med_log_res']
- test_fraction
- 0.2
- caliper_curves
- ['caliper']
- purity_weighting
- True
- min_purity_cutoff
- 0.0
- min_train_n_per_class
- 15
- standard_suite_curves
- ['gamma_ray', 'caliper', 'bulk_density', 'comp_sonic', 'deep_resistivity', 'neutron_por', 'density_correction']
- gr_coverage_pct_threshold
- 10
- curve_qc_exclusions_active
- 55
- standard_suite_coal_exception
- True
Last run: 2026-08-09 14:27:59 UTC
Confusion Matrix
Each row is a real, known rock type. Each column is what the classifier guessed. Reading across the "Sandstone" row shows: of all the samples that really are Sandstone, what did the classifier call them?
Darker cells = higher count within that row (row-normalised shading). "Unclassified" is a declined answer, not a wrong guess -- shaded separately.
How this result was generated
+ other logs if available"] rolling["Look at the trend nearby,
not just one number"] context["Which rock formation,
plus roughly where the well is"] purity["Weight each sample by how PURE
its recorded rock type actually was"] split["Split wells: most for learning,
some held back for testing"] tree1["Tree 1: a chain of yes/no
questions about the curve values"] err1["Check which depths
Tree 1 still gets wrong"] tree2["Tree 2: built specifically
to fix Tree 1's mistakes"] repeat["...continue for 200 trees,
each one fixing what's left over"] ensemble["Add up all 200 trees'
small votes into one answer"] predict["Guess the rock type
for the held-back wells"] score["Compare guesses
to the real answer"] curves --> rolling --> context --> purity --> split --> tree1 --> err1 --> tree2 --> repeat --> ensemble --> predict --> score
Hover a step for a plain-language explanation.
References
Learning Curve -- Accuracy vs. Training Wells
From the most recent learning-curve sweep: accuracy against a single FIXED held-out test set, at each training-well-count step. The highlighted point is where adding more wells stopped meaningfully helping -- that step's model is the one currently used for predictions.
What do these terms mean?
- Fixed test set
- A group of wells set aside once, at the very start, and never used for training. Every model at every size is checked against this exact same group, so the comparison between sizes is fair -- like giving everyone the same exam.
- Sizes / nested growth
- Each size builds on the last: "100 wells" is the same 50 wells from the "50" size plus 50 new ones, never a completely different random batch. This means any change in accuracy between sizes reflects what the extra wells actually added.
- Repeats
- Each size is trained several times with a different random selection of wells, then averaged. A single attempt can get lucky or unlucky depending on which wells happen to be picked -- averaging smooths that out.
- Peak / promoted
- The smallest well count where accuracy stopped meaningfully improving with more data. The model trained at that point is automatically saved as the one used for real predictions elsewhere on the site, replacing "always train on every well."