Minkey Chang

Data Scientist

All posts

Driving through underground rocks

Geosteering is the problem of working out where a drill bit sits inside a stack of rock layers nobody can see. You get a gamma-ray log along the horizontal well and a vertical reference log from nearby — a lookup table saying at depth TT inside the layer package, the rock reads B(T)B(T). Matching one against the other locates the bit. A particle filter does this well. What it cannot do is tell you how wrong it is, and that turned out to be the whole problem.


One scalar, and a filter that cannot see it

The tracker is a small state-space model: the state is position and dip rate — how fast the layers rise or fall along the well — the transition is an AR(1) on dip, and the emission compares measured gamma-ray to B()B(\cdot) at the candidate depth. Five hundred particles, systematic resampling, 128 seeds combined by path likelihood.

Its error decomposes almost exactly. Measured to within 3×1043\times10^{-4}:

T^(m)T(m)  =  cs(m)  +  r(m),cgˉtrkgˉtrue\hat{T}(m) - T(m) \;=\; c\,s(m) \;+\; r(m), \qquad c \equiv \bar{g}_{\text{trk}} - \bar{g}_{\text{true}}

where ss is fractional distance along the well. One scalar per well — the dip error cc — multiplied by how far you have travelled, plus shape error rr. An oracle that removes only csc\,s takes pooled error from 9.8 ft to 5.1 ft. Almost everything worth fixing is that one number.

The filter cannot find it by looking harder. Its 128 seeds explore about ±0.0011\pm 0.0011 ft/ft of dip while the actual error is 0.00330.0033; going from 8 seeds to 128 moves the dip error from 0.00220.0022 to 0.00200.0020, nowhere near the 1/K1/\sqrt{K} you would hope for. The error is common to every seed, because it comes from the transition prior quietly pulling each well toward one typical dip. More sampling buys nothing. This is information-limited, not variance-limited — a distinction worth testing early, since it decides whether compute helps at all.


Why post-hoc self-correction fails

The obvious repair is to scan: perturb the recovered trajectory by a candidate dip offset δ\delta and keep whichever δ\delta matches the reference log best. It does not work, and how it fails is the part I would keep.

Three controls, the same cost function, differing only in how much freedom the shape has:

  • Scan δ\delta on the filter's own trajectory: correlation with the true dip error +0.06. A coin flip.
  • Scan δ\delta while re-fitting the shape under a narrowed prior: +0.50, but the trajectory itself breaks.
  • Scan δ\delta against the true trajectory: recovered at RMS 0.00140.0014.

The signal is there; the shape degrees of freedom hide it. Shape error is large enough to swamp the ramp being scanned for, the reference log is quasi-periodic so periodicity manufactures a false minimum every cycle, and — the part that generalises — the filter has already been graded on this exact exam paper, so its derivative at zero vanishes by construction. Minimising over shape is the concealment: every candidate dip scores alike, because each one is first handed its own best-fitting shape.

An estimator cannot audit itself with the objective it was fitted on. The correction has to come from somewhere else.


Simulation-based inference, and what it actually needed

So estimate cc directly. A simulator is available: take a well's true trajectory, tilt it by a sampled δg\delta g, re-render the gamma-ray, and the label is exact by construction. That is simulation-based inference — train an amortised estimator on simulated pairs, then apply it to real wells. Three things decided whether it worked, and none of them was the network.

The noise geometry. My first observation model treated reference-log mismatch as noise along the well. It is not. It is a map error: the reference log is wrong about a particular bed, and it is wrong the same way every time the well re-crosses that bed. Modelling it as a slow process along depth instead, with gain drift and realistic gaps, was the difference between simulated data that transferred and simulated data that scored worse than predicting nothing.

The representation. Same targets, same wells, three inputs. A 2-D alignment image — candidate depths against position, carrying the misfit surface itself — scored +0.248. Hand-built scalar features scored +0.105. A raw 1-D gamma-ray sequence scored +0.021. Roughly a tenfold difference from the input alone, and four earlier attempts had all been raw-1-D, so the representation was part of what had been failing.

The width of the perturbation. Widening the sampled δg\delta g from 0.004 to 0.010 moved tail correlation from +0.38 to +0.69. The wells that matter have c|c| up to 0.019, and a narrow prior never simulates them. You have to cover the tail to learn it.

Two closing notes, both worth more than the wins. The calibration was fiction: simulation-based calibration showed severe overconfidence, and about 70% of it traced to an ODE sampler taking eight Euler steps instead of forty. The point estimates survived; every uncertainty claim did not. Written plainly, I had been doing point-chasing with an SBI-shaped wrapper around it. And the training script never called zero_grad() — gradients accumulated across every batch and every epoch, and the resulting instability had been diagnosed for weeks as an architecture problem.

One more thing that is easy to miss. Feeding the estimate back into the filter as a prior nudge recovers about a quarter of what applying it directly to the filter's output does; the likelihood averaging washes a rate-prior out. If a correction is worth making, make it where it survives.


Bottom line

A well-specified filter can be excellent and still carry a systematic error it is structurally blind to, and neither more sampling nor post-hoc scanning will surface it. Simulation-based inference is a reasonable way in — but the work is in the simulator's noise geometry, the representation you hand the network, and how wide you sample the quantity you are trying to estimate. Get those wrong and the architecture will not save you. Get them right and a small network is enough.


References

  1. Sequential Monte Carlo — Doucet, A., Godsill, S., & Andrieu, C. (2000). On Sequential Monte Carlo Sampling Methods for Bayesian Filtering. Statistics and Computing, 10(3), 197–208.

  2. Simulation-based inference — Cranmer, K., Brehmer, J., & Louppe, G. (2020). The Frontier of Simulation-Based Inference. PNAS, 117(48), 30055–30062.

  3. Simulation-based calibration — Talts, S., Betancourt, M., Simpson, D., Vehtari, A., & Gelman, A. (2018). Validating Bayesian Inference Algorithms with Simulation-Based Calibration. Preprint.

  4. Amortised posterior estimation — Papamakarios, G., & Murray, I. (2016). Fast ε-free Inference of Simulation Models with Bayesian Conditional Density Estimation. NeurIPS.

  5. Competition — ROGII: Wellbore Geology Prediction. Kaggle, 2026.

Other posts