Part IV: Machine Learning Essentials

Chapter 16

Machine Learning Fundamentals for Petroleum Engineers

schedule19 min readfitness_center4 exercises
auto_awesomeAI Key Takeaways

Get the TL;DR and the key concepts before you dive in — or as a quick review after.

Every chapter so far has used Python to implement physics you already knew: Darcy's law, material balance, Beggs-Brill, Arps decline. You wrote the equation; the computer evaluated it. Machine learning flips that around. You give the computer examples, thousands of rows where the answer is known, and it learns the relationship for you, even when no clean equation exists.

That is exactly the situation petroleum engineers face every day. What is the equation that maps a gamma-ray, density, neutron, and resistivity log to core porosity? There isn't one; there are crossplots, local calibrations, and engineering judgment. What predicts which wells will go to high water cut first, or which drilling parameters cause stick-slip? These are pattern-recognition problems hiding in data you already collect. Machine learning is the toolkit for turning that data into predictions.

This chapter is deliberately fundamental. We build one model, predicting porosity from wireline logs, and use it to teach the entire workflow: framing the problem, splitting data honestly, fitting and evaluating a model, engineering features, and recognizing overfitting. Master this loop on one problem and every other supervised-learning task in the next two chapters is a variation on it. (The deep end, with neural networks, sequence models, and LLMs, is a book of its own; here we stay with the workhorse methods that cover the vast majority of real petroleum ML.)

infoWhat You'll Learn

  • The supervised ML workflow: features and labels, train/test splits, fit, predict, evaluate
  • How to read the standard regression metrics, R² and RMSE, in engineering terms
  • Feature engineering: turning raw logs into physics-aware inputs
  • Cross-validation: getting an honest performance estimate from limited data
  • The bias–variance tradeoff: spotting and curing overfitting and underfitting

lightbulbDataset Used in This Chapter

We generate a realistic, self-contained training set in the first code cell, 700 depth samples from OML 58 wells with gamma ray, bulk density, neutron porosity, and deep resistivity logs paired with core-measured porosity. It is synthetic (so every cell runs with no downloads) but built from real petrophysical relationships, including the gas effect (light gas in the pores lowers the density log, faking high porosity) that makes porosity-from-logs a genuine inverse problem.

What Is Machine Learning? (For Engineers)

A machine-learning model is a function y^=f(x)\hat{y} = f(\mathbf{x}) whose parameters are fit from data rather than derived from first principles. You supply a table: each row is one example, the input columns are features (x\mathbf{x}), and the column you want to predict is the label or target (yy). The algorithm adjusts ff until its predictions match the known labels as closely as possible, then you use ff on new rows where the label is unknown.

There are three broad families:

  • Supervised learning: you have labelled examples (logs and core porosity) and learn to predict the label. This is the large majority of practical petroleum ML, and the focus of this chapter and the next.
  • Unsupervised learning: you have features but no labels, and you look for structure: clustering wells into rock types, flagging anomalous sensor readings (Chapter 18).
  • Reinforcement learning: an agent learns by trial and reward (e.g. real-time drilling-parameter control). Powerful but specialised; out of scope here.

Our running problem is supervised regression: predict continuous core porosity from four wireline logs. We build a realistic training set below. If petrophysics is new to you, read make_log_dataset as a black box on the first pass (four logs go in, core porosity comes out) and come back to the one line that matters, the bulk-density equation, in a moment.

main.py

Each row is one half-foot of logged section, and porosity is reported as v/v, the fraction of the rock's volume that is pore space, so 0.26 v/v means roughly a quarter of the rock is open pores. The model never sees the rock, only the four log readings, and must learn the mapping back to porosity. Notice there is no single column that is porosity: density comes closest, but in gas zones it lies (gas lowers RHOB, faking high porosity), which is exactly why we need several logs and a model that can combine them.

The one line worth reading inside the generator is the bulk-density equation, RHOB = rho_ma*(1-phi) + rho_fl*phi. Bulk density is a volume-weighted average: rock matrix (sandstone ≈ 2.65 g/cc, rising slightly with shale, the 0.03*Vsh term in the code) fills the part with no pores, and fluid (brine ≈ 1.0, gas ≈ 0.35 g/cc) fills the rest. That density contrast is the entire reason a density log can see porosity at all, and it is the physical law every porosity estimate inverts. The remaining coefficients in the generator are tuned to give log responses of realistic magnitude, not exact field constants; only the bulk-density line is a true physical law. One honest caveat follows: because the synthetic core porosity and the model share the same forward physics, the R² scores in this chapter are an optimistic ceiling. Real logs carry tool error, borehole washouts, and thin beds the model never saw, so a field model will score lower; the workflow transfers exactly, the easy 0.9 does not. In a real project you would swap make_log_dataset for lasio.read("well.las") to load the logs and a core-report CSV for PHI_core (Chapter 3); everything downstream of this cell is identical.

The ML Pipeline

Almost every supervised-learning task follows the same five steps:

  1. Split the data into a training set (to fit the model) and a held-out test set (to judge it honestly).
  2. Choose a model.
  3. Fit it on the training features and labels.
  4. Predict on the test features.
  5. Evaluate by comparing predictions to the test labels.

The cardinal rule: never evaluate on data the model trained on. A model can memorise its training set and look perfect while being useless on new wells. The held-out test set is your only honest estimate of field performance. We also wrap the model in a StandardScaler, because the features span wildly different ranges: GR in the hundreds of API units, porosity near zero. For a plain least-squares fit this scaling does not change the predictions, but it becomes essential the moment you regularise the model or use a method that measures distances between samples, so it is worth making the habit now.

main.py

An of about 0.90 means the model explains ~90% of the variance in core porosity, strong for a log-only estimate. The RMSE of 0.0185 v/v means a typical prediction lands within about 1.85 porosity units of core (one porosity unit (p.u.) is 0.01 v/v) in the ballpark of a hand-built petrophysical transform on these clean synthetic logs, and far faster to apply across thousands of feet. Crucially, the train and test R² are close: the model is generalising, not memorising. We will see what failure looks like shortly.

infoReading R² and RMSE like an engineer

is unitless and scaled 0–1 (it can go negative for a model worse than predicting the mean): "what fraction of the variation did we capture?" RMSE is in the units of the target, here porosity, so it answers "how far off is a typical prediction?" Always report both: R² tells you the model is useful; RMSE tells you whether that usefulness clears your engineering tolerance.

Feature Engineering for Petroleum Data

Raw logs are not always the best inputs. Feature engineering is the craft of transforming them into quantities that expose the signal, using petrophysical domain knowledge. Three classics:

  • Density porosity ϕD=(ρmaρb)/(ρmaρfl)\phi_D = (\rho_{ma}-\rho_b)/(\rho_{ma}-\rho_{fl}) re-expresses RHOB directly in porosity units.
  • Neutron–density separation ϕNϕD\phi_N - \phi_D is the gas/shale indicator: large positive in shale, negative in gas.
  • log(RT) linearises resistivity, which spans orders of magnitude. Resistivity carries fluid information because hydrocarbons are poor conductors: RT rises where gas or oil displaces conductive brine in the pores, which is how it helps flag the gas zones that fool density alone.

How much do features matter? We compare an honest cross-validated score (train on most of the data, test on the held-out rest, rotate, and average; the full method is the next section) for one log, all four logs, and all four plus degree-2 interaction terms: products like RHOB × NPHI that let a linear model read two logs jointly. That product is the only way a linear model can catch the gas effect, where density and neutron move in opposite directions, a pattern invisible to either log on its own.

main.py

Density alone gets you to ~0.77: useful, but it is fooled by gas and shale. Adding the other three logs jumps the score to ~0.92, because each log carries information the others lack: GR flags shale, NPHI and RT disambiguate gas. The degree-2 terms nudge it further by giving the linear model the products of logs, capturing the joint density–neutron gas signature. The lesson: good features encode physics the model cannot invent on its own.

Model Evaluation and Cross-Validation

A single train/test split has a problem: the score depends on which 25% of wells landed in the test set. Get an easy split and you flatter the model; get a hard one and you libel it. That number is not academic: quote the flattering split and an asset team over-trusts log porosity on dozens of un-cored wells; quote the unlucky one and a sound model gets shelved. On a field where each cored well cost millions, you get one shot at that call. k-fold cross-validation removes the luck. Split the data into k equal folds; train on k−1 of them and test on the held-out fold; rotate so every sample is tested exactly once; average the k scores. You get a performance estimate and its variability.

main.py
Cross-validated predicted vs. core porosity for the linear model. Points hug the 1:1 line; the scatter is the model's irreducible error (~1.85 p.u. RMSE), with the widest misses in the gas-affected sands.
Cross-validated predicted vs. core porosity for the linear model. Points hug the 1:1 line; the scatter is the model's irreducible error (~1.85 p.u. RMSE), with the widest misses in the gas-affected sands.

The five folds agree to within a fraction of a percent (the ± is tiny), so we can trust the ~0.92 estimate; it is not an artefact of one lucky split. The crossplot is the petrophysicist's favourite diagnostic: tight scatter around the 1:1 line means a trustworthy model, and the points that stray furthest are concentrated in the gas sands, telling you exactly where the physics is hardest.

Overfitting, Underfitting, and Regularization

The central tension in machine learning is the bias–variance tradeoff:

  • An underfit (high-bias) model is too simple to capture the relationship; it scores poorly on both train and test.
  • An overfit (high-variance) model memorises the training data, including its noise; it scores near-perfectly on train and badly on test.

The cure is regularization: deliberately limiting model complexity. Watch the gap between train and test R² as we move from a linear model to an unconstrained decision tree, a depth-limited tree, and a random forest.

main.py
Train vs. test R² across model complexity. The unconstrained tree memorises the training set (train R²=1.0) but generalises worst; limiting depth or averaging many trees (random forest) closes the gap.
Train vs. test R² across model complexity. The unconstrained tree memorises the training set (train R²=1.0) but generalises worst; limiting depth or averaging many trees (random forest) closes the gap.

The unconstrained decision tree is the cautionary tale: a perfect training R² of 1.0 and the worst test score, because it memorised every training point, noise included. Capping the depth (regularization) sacrifices a little training fit for a big gain in generalisation. The random forest, hundreds of de-correlated trees averaged together, gets the best test score of all, because averaging cancels the individual trees' overfitting. This is why ensembles are the default choice for tabular petroleum data.

Putting It Together: A Field-Ready Porosity Log

The random forest won the bias–variance contest above, so we commit to it now: evaluate it properly, ask which logs it leaned on, and apply it the way you would in the field, turning the logs of an un-cored well into a continuous porosity curve.

main.py
Left: random-forest feature importances - bulk density dominates (it is the primary porosity log), with GR, RT, and NPHI supplying the shale and gas corrections. Right: the model's porosity log (green) over a new 60-ft interval, graded against the known core truth (grey). The model tracks the layered profile and reads the gas sand at 9018-9028 ft at its true ~0.26 v/v, rather than being fooled high by the gas-suppressed density.
Left: random-forest feature importances - bulk density dominates (it is the primary porosity log), with GR, RT, and NPHI supplying the shale and gas corrections. Right: the model's porosity log (green) over a new 60-ft interval, graded against the known core truth (grey). The model tracks the layered profile and reads the gas sand at 9018-9028 ft at its true ~0.26 v/v, rather than being fooled high by the gas-suppressed density.

The random forest reaches a test R² of about 0.91, the best of every model we tried, and its feature importances read as a sanity check, not a black box: bulk density dominates (it is the primary porosity measurement), while gamma ray, resistivity, and neutron supply the shale-volume and gas corrections. That ranking matches petrophysical intuition, which is how you build trust in a model. (One caveat: impurity-based importances tilt toward continuous, high-variance logs, so for a ranking you would defend in a review, confirm it with permutation importance.) Applied to the new interval, the model recovers the layered porosity profile, and reads the gas sand at its true ~0.26 v/v instead of being fooled by the gas-suppressed density, producing a continuous porosity curve in milliseconds, on a well nobody cored.

You have now walked the complete supervised-learning loop: frame the problem, split honestly, fit, evaluate with held-out data and cross-validation, engineer features, diagnose overfitting, and deploy. Chapter 17 reuses this exact loop for classification (predicting rock type instead of a number), and Chapter 18 drops the labels entirely.

Exercises

fitness_center
Exercise 16.1Practice

: Predict Water Saturation

Extend make_log_dataset (or write your own generator) to include a SW_core column for water saturation, then train and evaluate a model to predict SwS...

arrow_forward
codePythonSolve Nowarrow_forward
fitness_center
Exercise 16.2Practice

: Feature Engineering Bake-Off

Starting from the four raw logs, engineer at least three new features (e.g. density porosity, neutron–density separation, log resistivity, a gamma-ray...

arrow_forward
codePythonSolve Nowarrow_forward
fitness_center
Exercise 16.3Practice

: The Overfitting Curve

For a single DecisionTreeRegressor, sweep max_depth from 1 to 20. For each depth, record both the training R² and the 5-fold cross-validated R². Plot ...

arrow_forward
codePythonSolve Nowarrow_forward
fitness_center
Exercise 16.4Practice

: Honest vs. Dishonest Evaluation

Deliberately evaluate a model the wrong way: fit a random forest on the full dataset and score it on that same data. Compare the (inflated) score to a...

arrow_forward
codePythonSolve Nowarrow_forward

Summary

  • Machine learning fits a function from examples instead of from first principles: the right tool when the mapping (logs → porosity, parameters → dysfunction) has no clean equation but lives in your data.
  • The supervised workflow is one loop: split → choose → fit → predict → evaluate, and never judge a model on data it trained on.
  • R² and RMSE answer different questions: "what fraction of variance did we capture?" and "how far off is a typical prediction, in engineering units?" Report both.
  • Feature engineering encodes physics the model cannot invent: density porosity, neutron–density separation, and log-resistivity all sharpen the signal.
  • Cross-validation turns one lucky-or-unlucky split into a stable estimate with error bars, the difference between an asset team trusting log porosity on un-cored wells or shelving a sound model.
  • The bias–variance tradeoff governs everything: underfit models are too simple, overfit models memorise noise, and regularization (depth limits, ensembles) buys back the generalisation that makes a model useful in the field.

In the next chapter we keep this loop and change the target: from predicting a continuous number (regression) to predicting a category (classification), starting with lithofacies from logs.