2.25. pycsamt.ai#
AI inversion, neural network architectures, training helpers, AI processing, model-zoo utilities, and diagnostic plotting.
Artificial intelligence and machine learning for EM processing and inversion.
2.25.1. Phase 2 additions#
EMInverter1D— full 1-D inversion pipeline (data loading → normalisation → training → prediction)nets— CNN1D, ResNet1D, FCN1D architecturestraining— EMDataset, EMTrainer, metricsplot— EMStyle, plot_compare, plot_convergence
2.25.2. Quick start (requires PyTorch)#
>>> from pycsamt.forward.batch import generate_dataset
>>> from pycsamt.ai.inversion import EMInverter1D
>>> ds = generate_dataset(n_samples=2_000, seed=0, n_layers=5)
>>> inv = EMInverter1D(arch="resnet", n_layers=5)
>>> inv.fit(ds, epochs=30)
>>> # Predict on new Z objects or ForwardResponse
>>> y_pred = inv.predict(X_test)
- class pycsamt.ai.BaseEMNet(arch='cnn1d', n_layers=5, solver='mt1d', device=None)#
Bases:
ABCAbstract base class for EM neural network estimators.
Subclasses must implement
fit(),predict(), and_build_network().All
fit/predictcalls accept either numpy arrays (the standard path) or EM domain objects (Z tensors, ForwardResponse, SiteCollection) which are coerced to arrays via the_coerce_input()hook.- Parameters:
- abstractmethod fit(X, y=None, **kwargs)#
Train the network.
- Parameters:
X (ndarray (n_samples, n_features) or path-like or ForwardDataset) – Training data. A string is treated as a path to a
.npzfile produced bygenerate_dataset().y (ndarray (n_samples, n_params) or None) – Targets. If X is a dataset object or path, y is ignored.
**kwargs – Solver-specific keyword arguments (e.g.
epochs,batch_size,lr).
- Return type:
self
- abstractmethod predict(X)#
Predict subsurface model parameters from data.
- score(X, y, *, metric='rmse')#
Evaluate prediction quality.
- Parameters:
X (ndarray) – Input features.
y (ndarray) – True model parameters.
metric ({'rmse', 'r2', 'mae', 'relative_rmse'}) – Evaluation metric.
- Returns:
score – Lower is better for RMSE/MAE; higher is better for R².
- Return type:
- fit_predict(X, y, **kwargs)#
Fit then predict on the same data (useful for diagnostics).
- save(path)#
Save the model weights and hyperparameters to path.
- Parameters:
path (str or Path) – Destination file path (a
.ptextension is used for PyTorch backends;.npzfor numpy-only models).- Return type:
None
- classmethod load(path)#
Load a model saved with
save().- Parameters:
path (str or Path)
- Returns:
estimator
- Return type:
instance of the calling class
- classmethod from_pretrained(name)#
Load a pre-trained model from the pycsamt model zoo.
Note
Pre-trained weights are hosted separately and will be downloaded on first call. This feature is scheduled for Phase 5 of the pycsamt AI roadmap.
- Parameters:
name (str) – Model identifier, e.g.
'mt1d-resnet-v1'.- Raises:
NotImplementedError – Until Phase 5 weights are published.
- Return type:
- class pycsamt.ai.BaseEMProcessor#
Bases:
ABCAbstract base class for ML-based EM data processing tasks.
Unlike
BaseEMNet, processors transform data to data rather than data to model parameters.Subclasses in
pycsamt.ai.processingincludeEMDenoiser,EMQCScorer, andAnomalyDetector.- abstractmethod fit(X, **kwargs)#
Fit the processor to training data.
- Parameters:
X (ndarray)
- Return type:
- abstractmethod transform(X)#
Apply the learned transformation.
- fit_transform(X, **kwargs)#
Fit then transform in one call.
- classmethod load(path)#
- Parameters:
- Return type:
- class pycsamt.ai.EMCheckpoint(params, weights=None, history=None, meta=None)#
Bases:
objectMinimal checkpoint for saving/loading EM network state.
Holds hyperparameters (serialised as JSON) alongside framework- specific weight data (numpy arrays or a torch state dict path).
- Parameters:
- classmethod load(path)#
Load from a
.npzcheckpoint file.- Parameters:
- Return type:
- class pycsamt.ai.EMInverter1D(arch='resnet', n_layers=5, solver='mt1d', *, device=None, log_thickness=True, include_phase=True, augment_noise=0.02, **net_kwargs)#
Bases:
BaseEMNet1-D EM neural-network inverter.
Supports MT, CSAMT (far-field), and TEM step-off data.
- Parameters:
arch ({'resnet', 'cnn1d', 'fcn'}) – Network architecture.
'resnet'(Liu 2021 style) gives the best accuracy on typical MT datasets.'cnn1d'(Puzyrev style) is faster to train.'fcn'(Moghadas style) handles variable input length.n_layers (int) – Number of earth layers to invert for.
solver ({'mt1d', 'csamt1d', 'tem1d'}) – EM method this inverter targets (determines default frequency grid for Z-object input coercion).
device (str or None) – Compute device. Auto-detects CUDA/MPS/CPU if
None.log_thickness (bool) – Apply log₁₀ to thickness in training targets. Strongly recommended when thicknesses span > 2 orders of magnitude.
include_phase (bool) – Include impedance phase in the input feature vector for MT/CSAMT. Setting to
Falsehalves the input size.augment_noise (float) – On-the-fly noise level added to training inputs each epoch.
net_kwargs (dict) – Extra keyword arguments forwarded to the network constructor (e.g.
channels=(64, 128, 256)for ResNet).
- fit(X, y=None, *, epochs=100, batch_size=256, lr=0.001, patience=20, val_frac=0.1, grad_clip=1.0, seed=None, verbose=True)#
Train the inverter on a
ForwardDatasetor a.npzfile path.- Parameters:
X (ForwardDataset or str or Path) – Training data. If y is also given, X and y are treated as raw feature / target numpy arrays.
y (ndarray or None) – Raw targets (only when X is an ndarray).
epochs (int) – Maximum training epochs.
batch_size (int)
lr (float) – Initial learning rate.
patience (int) – Early-stopping patience.
val_frac (float) – Fraction of training data used for validation.
grad_clip (float or None) – Gradient-norm clipping threshold.
seed (int or None) – Random seed for train/val split.
verbose (bool) – Print per-epoch summary.
- Return type:
self
- predict(X, *, as_log_rho=True)#
Predict model parameters for new data.
- Parameters:
X (ndarray (n_samples, n_features) | Z | list of Z | ForwardResponse) – Input data.
as_log_rho (bool) – If
True(default), the returned resistivity values are in log₁₀(Ω·m) space matching the training targets. IfFalse, resistivities are back-transformed to Ω·m.
- Returns:
y_pred – Parameter vector:
[log10(ρ), log10(h)](or linear ifas_log_rho=False).- Return type:
- predict_models(X)#
Predict and return a list of
LayeredModel.Resistivity and thickness are back-transformed from log space.
- Return type:
- predict_response(response)#
Convenience: invert a single
ForwardResponseand return the predictedLayeredModel.- Return type:
- save(path)#
Save weights + normaliser + hyperparameters to path.
- classmethod load(path)#
Load a saved inverter from path.
- Parameters:
- Return type:
- classmethod from_pretrained(name, *, cache_dir=None)#
Load a pre-trained model from the pycsamt model zoo.
Pre-trained weights are hosted at earthai-tech/pycsamt-models and are downloaded to
~/.pycsamt/model_zoo/on first call.- Parameters:
- Return type:
- Raises:
KeyError – If name is not in the model zoo registry.
RuntimeError – If the download fails (weights not yet publicly available).
Examples
>>> from pycsamt.ai.inversion import EMInverter1D >>> inv = EMInverter1D.from_pretrained( ... "mt1d-resnet-5layer-v1" ... )
- class pycsamt.ai.EMInverter2D(n_components=4, n_depth=40, n_stations=20, n_freqs=32, *, arch='unet', unet_depth=None, channels=None, dropout=0.2, device=None, log_rho_out=True, **net_kwargs)#
Bases:
BaseEMNetU-Net–based 2-D MT inversion estimator.
- Parameters:
n_components (int, default 4) – Number of input channels (EM data components per frequency and station).
n_depth (int, default 40) – Number of depth cells in the target resistivity section.
n_stations (int, default 20) – Number of stations along the profile.
n_freqs (int, default 32) – Number of frequency channels in the input data.
arch (str, default
'unet') – Network architecture. Only'unet'is supported in Phase 4.device (str or None) – Compute device.
log_rho_out (bool, default True) – If
True, targets and predictions are in log₁₀(ρ) scale.**net_kwargs – Additional keyword arguments forwarded to the architecture factory (e.g.
channels,dropout).unet_depth (int | None)
dropout (float)
- fit(X, y=None, *, epochs=100, batch_size=16, lr=0.001, patience=15, val_frac=0.1, grad_clip=1.0, seed=None, verbose=True, lambda_x=0.0, lambda_z=0.0, lambda_tv=0.0)#
Train the 2-D inversion network.
- Parameters:
X (ndarray (n_profiles, n_components, n_freqs, n_stations)) – Input data panels.
y (ndarray (n_profiles, n_depth, n_stations)) – Target 2-D log₁₀(ρ) sections.
epochs (int) – Standard training hyper-parameters.
batch_size (int) – Standard training hyper-parameters.
lr (float) – Standard training hyper-parameters.
patience (int) – Standard training hyper-parameters.
val_frac (float) – Standard training hyper-parameters.
grad_clip (float | None) – Standard training hyper-parameters.
seed (int | None) – Standard training hyper-parameters.
verbose (bool) – Standard training hyper-parameters.
lambda_x (float, default 0.0) – Weights for lateral-gradient, depth-gradient, and total- variation spatial regularization terms added to the plain data-fit MSE, matching the staged objective in
pycsamt.ai.losses(L_grad_x/L_grad_z/L_TV) but computed directly on normalized torch tensors so the combined loss stays differentiable — the NumPy implementations inpycsamt.ai.lossescannot participate in backpropagation. All zero (the default) reproduces the exact plain-MSE behaviour of earlier versions. Only supported on the PyTorch backend: passing a nonzero value while TensorFlow is active raisesNotImplementedError.lambda_z (float, default 0.0) – Weights for lateral-gradient, depth-gradient, and total- variation spatial regularization terms added to the plain data-fit MSE, matching the staged objective in
pycsamt.ai.losses(L_grad_x/L_grad_z/L_TV) but computed directly on normalized torch tensors so the combined loss stays differentiable — the NumPy implementations inpycsamt.ai.lossescannot participate in backpropagation. All zero (the default) reproduces the exact plain-MSE behaviour of earlier versions. Only supported on the PyTorch backend: passing a nonzero value while TensorFlow is active raisesNotImplementedError.lambda_tv (float, default 0.0) – Weights for lateral-gradient, depth-gradient, and total- variation spatial regularization terms added to the plain data-fit MSE, matching the staged objective in
pycsamt.ai.losses(L_grad_x/L_grad_z/L_TV) but computed directly on normalized torch tensors so the combined loss stays differentiable — the NumPy implementations inpycsamt.ai.lossescannot participate in backpropagation. All zero (the default) reproduces the exact plain-MSE behaviour of earlier versions. Only supported on the PyTorch backend: passing a nonzero value while TensorFlow is active raisesNotImplementedError.
- Return type:
self
- predict(X, *, as_log_rho=True)#
Predict 2-D resistivity sections.
- Parameters:
X (ndarray (n_profiles, n_components, n_freqs, n_stations))
as_log_rho (bool) – If
True(default) output is log₁₀(ρ); otherwise linear ρ.
- Returns:
rho_2d
- Return type:
ndarray (n_profiles, n_depth, n_stations)
- class pycsamt.ai.GCNInverter3D(n_features=40, n_layers=5, hidden=(256, 128, 64), dropout=0.1, device=None, **net_kwargs)#
Bases:
BaseEMNetGraph-convolutional 3-D MT inversion estimator.
Models the spatial context of a multi-station survey as a graph and applies spectral GCN message-passing before regressing per-station 1-D subsurface models. The result is a spatially coherent 3-D resistivity volume without any external graph library dependency.
- Parameters:
n_features (int, default 40) – Per-station input feature dimension. A typical choice is
2 × n_freqs(log10(ρ_a) + phase at each frequency).n_layers (int, default 5) – Number of depth layers per station. Output size per station is
2 * n_layers - 1(n_layers log10(ρ) + (n_layers-1) log10(h)).hidden (sequence of int, default (256, 128, 64)) – Width of each GCN message-passing layer.
dropout (float, default 0.1) – Dropout probability applied between GCN layers during training.
device (str or None) – Compute device (
'cpu','cuda','gpu:0', …).Noneauto-detects.**net_kwargs – Extra keyword arguments forwarded to
GCNNet.
- fit(X, y=None, adjacency=None, *, coords=None, radius=5000.0, epochs=100, batch_size=16, lr=0.001, patience=15, val_frac=0.15, grad_clip=1.0, seed=None, verbose=True)#
Train the 3-D GCN inversion network.
- Parameters:
X (ndarray (n_samples, n_stations, n_features) or) – (n_stations, n_features) Per-station MT feature matrices.
y (ndarray (n_samples, n_stations, 2*n_layers-1) or) – (n_stations, 2*n_layers-1) Target per-station model parameters (log10 scale recommended).
adjacency (ndarray (n_stations, n_stations), optional) – Pre-computed normalised adjacency matrix. If
None, coords and radius must be provided.coords (ndarray (n_stations, 2), optional) – Station (x, y) positions used to build the adjacency when adjacency is not given.
radius (float) – Maximum inter-station edge distance in the same units as coords; ignored when adjacency is supplied.
epochs (int) – Standard training hyper-parameters.
batch_size (int) – Standard training hyper-parameters.
lr (float) – Standard training hyper-parameters.
patience (int) – Standard training hyper-parameters.
val_frac (float) – Standard training hyper-parameters.
grad_clip (float | None) – Standard training hyper-parameters.
seed (int | None) – Standard training hyper-parameters.
verbose (bool) – Standard training hyper-parameters.
- Return type:
self
- predict(X, adjacency=None, *, coords=None, radius=5000.0, as_log_rho=True)#
Predict per-station subsurface models.
- Parameters:
X (ndarray (n_stations, n_features) or) – (n_samples, n_stations, n_features)
adjacency (ndarray (n_stations, n_stations), optional) – If
None, uses the adjacency stored fromfit().coords (ndarray (n_stations, 2), optional) – Build adjacency from coordinates when adjacency is absent.
radius (float) – Edge radius (used only when coords is supplied).
as_log_rho (bool) – Return log10(ρ) when
True; linear-scale ρ otherwise.
- Returns:
y_pred
- Return type:
ndarray (n_stations, n_out) or (n_samples, n_stations, n_out)
- predict_with_uncertainty(X, adjacency=None, *, coords=None, radius=5000.0, n_mc=30)#
MC-dropout uncertainty estimate for 3-D predictions.
Runs n_mc stochastic forward passes with dropout active and returns the mean and pointwise standard deviation.
- Parameters:
X (ndarray (..., n_stations, n_features))
adjacency (ndarray, optional)
coords (ndarray, optional)
radius (float)
n_mc (int) – Number of Monte-Carlo dropout samples.
- Returns:
mean (ndarray — same shape as
predict()output)std (ndarray — same shape)
- Return type:
- class pycsamt.ai.JointInverter(n_features_list, n_layers=5, *, growth_rate=32, n_dense_layers=6, hidden_dim=256, dropout=0.2, device=None, log_thickness=True, **net_kwargs)#
Bases:
BaseEMNetMulti-modal joint inversion estimator based on DRCNN.
- Parameters:
n_features_list (sequence of int) – Feature vector lengths for each modality. E.g.
(120, 48)for two modalities.n_layers (int, default 5) – Number of earth layers to invert for.
growth_rate (int, default 32) – Dense block growth rate.
n_dense_layers (int, default 6) – Sub-layers per dense block.
hidden_dim (int, default 256) – Encoded-feature dimension for each modality and fusion stage.
dropout (float, default 0.2)
device (str or None)
log_thickness (bool, default True) – Apply log₁₀ transform to thickness targets before normalisation.
**net_kwargs – Forwarded to
DRCNNNet.
- fit(X_list, y, *, epochs=100, batch_size=256, lr=0.001, patience=20, val_frac=0.1, grad_clip=1.0, seed=None, verbose=True)#
Train the joint inverter.
- Parameters:
X_list (list of ndarray) – Feature matrices for each modality, each
(n_samples, n_features_i). Lengths must match.y (ndarray (n_samples, 2*n_layers-1)) – Target model parameters: first
n_layerscolumns are log₁₀(ρ) or ρ; lastn_layers-1columns are thicknesses.epochs (int) – Standard training hyper-parameters.
batch_size (int) – Standard training hyper-parameters.
lr (float) – Standard training hyper-parameters.
patience (int) – Standard training hyper-parameters.
val_frac (float) – Standard training hyper-parameters.
grad_clip (float | None) – Standard training hyper-parameters.
seed (int | None) – Standard training hyper-parameters.
verbose (bool) – Standard training hyper-parameters.
- Return type:
self
- predict(X_list, *, as_log_rho=True)#
Predict subsurface model parameters.
- class pycsamt.ai.EnsembleInverter(base_estimator, n_estimators=5, seeds=None)#
Bases:
objectDeep ensemble of
EMInverter1Dmodels.- Parameters:
base_estimator (EMInverter1D) – Template estimator (unfitted). Its hyper-parameters are copied for every ensemble member.
n_estimators (int, default 5) – Number of ensemble members.
seeds (list of int or None) – Per-member random seeds. When
None, seeds[0, 1, …, n_estimators-1]are used.
Examples
>>> from pycsamt.ai.inversion import EMInverter1D, EnsembleInverter >>> base = EMInverter1D(arch="resnet", n_layers=5) >>> ens = EnsembleInverter(base, n_estimators=5) >>> ens.fit(ds, epochs=50) EnsembleInverter(n_estimators=5, fitted) >>> mean, std = ens.predict_with_uncertainty(X_test)
- fit(X, y=None, *, epochs=100, batch_size=256, lr=0.001, patience=20, val_frac=0.1, grad_clip=1.0, verbose=True)#
Train all ensemble members.
- Parameters:
X (ForwardDataset, path, or ndarray) – Training data (forwarded unchanged to each member’s
fit).y (ndarray or None)
epochs (int) – Forwarded to each member’s
fitcall; each member receives a uniqueseed.batch_size (int) – Forwarded to each member’s
fitcall; each member receives a uniqueseed.lr (float) – Forwarded to each member’s
fitcall; each member receives a uniqueseed.patience (int) – Forwarded to each member’s
fitcall; each member receives a uniqueseed.val_frac (float) – Forwarded to each member’s
fitcall; each member receives a uniqueseed.grad_clip (float | None) – Forwarded to each member’s
fitcall; each member receives a uniqueseed.verbose (bool) – Forwarded to each member’s
fitcall; each member receives a uniqueseed.
- Return type:
self
- predict(X, **kwargs)#
Return the mean ensemble prediction.
- Parameters:
X (ndarray (n_samples, n_features))
**kwargs (forwarded to each member’s
predict)
- Returns:
y_mean
- Return type:
- predict_with_uncertainty(X, _use_calibrated=True, **kwargs)#
Return (mean, std) ensemble prediction.
If
calibrate()has been called, the returnedstdis the calibrated standard deviation from thePosteriorCalibrator(i.e. the Gaussianization-flow recalibrated sigma). Pass_use_calibrated=Falseto retrieve the raw inter-member std.- Parameters:
X (ndarray (n_samples, n_features))
_use_calibrated (bool) – When
True(default) and a calibrator is attached, return calibrated sigma. Internal flag — not part of the public API.
- Returns:
mean (ndarray (n_samples, n_params))
std (ndarray (n_samples, n_params)) – Calibrated or raw inter-member standard deviation.
- Return type:
- predict_quantiles(X, q=(0.05, 0.25, 0.5, 0.75, 0.95), **kwargs)#
Return per-quantile predictions.
- Parameters:
X (ndarray (n_samples, n_features))
q (sequence of float) – Quantiles in (0, 1).
- Returns:
quantiles
- Return type:
- calibrate(X_cal, y_cal, *, alpha=0.1)#
Attach a
ConformalPredictorand aPosteriorCalibratorfitted on a held-out calibration set.After calling this method:
predict_intervals()returns conformal prediction bands with guaranteed \(\ge 1-\alpha\) marginal coverage (Vovk et al. 2005).predict_posterior()returns calibrated posterior samples via the Gaussianization normalising flow (Kuleshov et al. 2018).predict_with_uncertainty()returns the calibrated standard deviation instead of the raw inter-member std.
- Parameters:
X_cal (ndarray, shape (n_cal, n_features)) – Calibration inputs (must not overlap with the training set).
y_cal (ndarray, shape (n_cal, n_params)) – True parameter vectors for the calibration inputs.
alpha (float) – Default significance level for conformal intervals.
- Return type:
self
- predict_intervals(X, alpha=None)#
Conformal prediction intervals with guaranteed marginal coverage.
Requires a prior call to
calibrate().- Parameters:
X (ndarray, shape (n_samples, n_features))
alpha (float or None) – Significance level. Defaults to the value used in
calibrate().
- Returns:
center (ndarray, shape (n_samples, n_params))
lower (ndarray, shape (n_samples, n_params))
upper (ndarray, shape (n_samples, n_params))
- Return type:
- predict_posterior(X, n_samples=500, rng=None)#
Draw calibrated posterior samples via the Gaussianization normalising flow (Kuleshov et al. 2018).
Requires a prior call to
calibrate().- Parameters:
X (ndarray, shape (n_pts, n_features))
n_samples (int) – Number of posterior draws per input point.
rng (numpy.random.Generator or None)
- Returns:
draws
- Return type:
- coverage_diagnostics(X_test, y_test, alphas=None)#
Per-alpha empirical coverage table (requires prior
calibrate()).Useful for a reliability diagram: nominal coverage
1-alphaon the x-axis, actual coverage on the y-axis. A perfectly calibrated predictor lies on the diagonal.- Parameters:
X_test (ndarray)
y_test (ndarray)
alphas (sequence of float or None)
- Returns:
diag
- Return type:
dict {alpha: actual_coverage}
- score(X, y, *, metric='rmse')#
Score the ensemble mean prediction.
- Parameters:
X (ndarray (n_samples, n_features))
metric ({'rmse', 'mae', 'r2', 'relative_rmse'})
- Returns:
score
- Return type:
- coverage(X, y, *, n_sigma=1.96)#
Fraction of true values within ±n_sigma * std of the ensemble mean.
An uncertainty estimate is well-calibrated when
coverage(1.96) ≈ 0.95.- Parameters:
X (ndarray)
y (ndarray)
n_sigma (float)
- Returns:
coverage
- Return type:
float ∈ [0, 1]
- save(path)#
Save all ensemble members.
Creates a directory
<path>/containingmember_00.npz,member_01.npz, ….- Parameters:
path (str or Path)
- Return type:
None
- classmethod load(path, base_class=None)#
Load a saved ensemble.
- Parameters:
base_class (type or None) – Estimator class to use for loading. Defaults to
EMInverter1D.
- Return type:
- plot_uncertainty_profile(X, sample_idx=0, *, y_true=None, n_sigma=1.96, **plot_kwargs)#
Plot a 1-D resistivity profile with uncertainty bands.
- Parameters:
X (ndarray (n_samples, n_features))
sample_idx (int) – Which sample to plot.
y_true (ndarray (n_params,) or None) – True model vector for overlay.
n_sigma (float) – Band width in standard deviations.
**plot_kwargs – Forwarded to
plot_uncertainty_bands().
- Returns:
fig
- Return type:
Figure
- class pycsamt.ai.DUHIInverter2D(*, lambda_ai=1.0, sigma_ai_floor=0.05, reliability_floor=1e-06, prejudice_filename='DUHIPrejudice', grid_mapper=None, verbose=0)#
Bases:
PyCSAMTObjectPrepare an Occam2D project for DUHI physics refinement.
DUHIInverter2Dconverts observation reliability and ensemble AI uncertainty into solver-native Occam2D inputs. The object is a preparation-stage inversion component: it does not train a network and does not execute Occam2D.The preparation modifies a completed
InputBuilderproject in place. It performs four operations:replace nominal datum errors by reliability-weighted errors;
map AI mean and standard-deviation grids to Occam parameters;
optionally replace the startup vector with the AI mean; and
write uncertainty-dependent prejudice records and reference them from the Occam model file.
- Parameters:
lambda_ai (float, default 1.0) – Non-negative global multiplier for AI prejudice amplitudes. A value of zero writes no active prejudice records.
sigma_ai_floor (float, default 0.05) – Positive model-uncertainty floor in log10 resistivity. It prevents unbounded prejudice weights where ensemble spread is very small.
reliability_floor (float, default 1e-6) – Smallest observation reliability used in effective errors. It must lie in
(0, 1].prejudice_filename (str, default "DUHIPrejudice") – Solver-local prejudice filename. It must be a non-empty file name without directory components.
grid_mapper (callable, optional) – Function accepting
(grid, model, mesh, x_coordinates, z_coordinates)and returning a one-dimensional vector of lengthmodel.n_params. If omitted, the geometry-awaremap_ai_grid_to_occam()mapper is used.verbose (int or bool, default 0) – Verbosity level. Positive values print a compact completion message after preparation.
- Variables:
lambda_ai (float) – Global AI prejudice multiplier.
sigma_ai_floor (float) – Model-space uncertainty floor.
reliability_floor (float) – Data-space reliability floor.
prejudice_filename (str) – Native Occam prejudice filename.
grid_mapper (callable) – Active AI-grid to Occam-parameter mapping function.
verbose (int) – Integer verbosity level.
is_prepared (bool) – Whether
prepare()completed successfully.preparation (DUHIPreparation) – Most recent completed preparation result.
Notes
Instances are intentionally one-shot because the builder is modified in place. Reapplying reliability weights to an already prepared data table would compound error inflation. Create a new
DUHIInverter2Dand a freshInputBuilderfor each run.When AI coordinates are omitted, the default mapper infers uniform AI cell centres spanning the complete Occam horizontal and earth domains. Explicit coordinates are preferred for archived runs.
See also
EMInverter2DProduces learned 2-D model proposals.
EnsembleInverterProduces ensemble mean and predictive uncertainty.
OccamPrejudiceEncodes model targets and weights for the native solver.
pycsamt.models.occam2d.OccamRunnerExecutes a prepared Occam2D project.
Examples
Prepare a completed Occam2D project:
>>> from pycsamt.ai.inversion import DUHIInverter2D >>> inverter = DUHIInverter2D( ... lambda_ai=1.0, ... sigma_ai_floor=0.05, ... ) >>> result = inverter.prepare( ... builder, ... ai_mean=ensemble_mean, ... ai_std=ensemble_std, ... observation_reliability=reliability, ... ) >>> result.prejudice_file PosixPath('occam_run/DUHIPrejudice')
Supply a geometry-aware mapper:
>>> inverter = DUHIInverter2D( ... grid_mapper=physical_mesh_mapper, ... )
References
[DUHIInverter2D-1]deGroot-Hedlin, C., and Constable, S., “Occam’s inversion to generate smooth, two-dimensional models from magnetotelluric data”, Geophysics, 55(12), 1613-1624, 1990.
- validate()#
Validate DUHI preparation hyperparameters.
- Raises:
TypeError – Raised when
grid_mapperis not callable.ValueError – Raised when numerical controls are non-finite or outside their accepted ranges, or when
prejudice_filenameis empty or contains directory components.
- Return type:
None
See also
DUHIInverter2D.prepareValidates run-specific inputs after these controls.
Examples
>>> DUHIInverter2D(lambda_ai=0.5).validate()
- prepare(builder, *, ai_mean, ai_std, observation_reliability, ai_initialize=True, ai_x=None, ai_z=None)#
Apply both DUHI branches to completed Occam2D inputs.
- Parameters:
builder (InputBuilder) – Completed Occam2D input builder.
builder.is_readymust beTrueand its data, model, startup, configuration, and work directory must be populated. Compatible builder objects exposing the same interface are also accepted.ai_mean (array-like of float, shape (n_depth, n_horizontal)) – Finite ensemble-mean log10-resistivity grid.
ai_std (array-like of float, shape (n_depth, n_horizontal)) – Finite non-negative predictive standard-deviation grid. It must have the same shape as
ai_mean.observation_reliability (array-like of float, shape (n_data,)) – One reliability value in
[0, 1]for each Occam data row, in exactly the same order asdata_blocks.ai_initialize (bool, default True) – If
True, replaceStartup.param_valueswith the mapped AI mean. IfFalse, retain the existing startup vector while still writing the DUHI prejudice.ai_x (array-like of float, optional) – Horizontal coordinates of AI grid columns in the Occam mesh coordinate system. The length must equal
ai_mean.shape[1]. If omitted, uniform centres spanning the complete mesh width are inferred.ai_z (array-like of float, optional) – AI grid depth coordinates, positive downward from the earth surface. The length must equal
ai_mean.shape[0]. If omitted, uniform centres spanning the earth mesh depth are inferred.
- Returns:
Immutable summary containing generated paths, dimensions, effective-error bounds, prejudice-weight bounds, and the initialization choice.
- Return type:
- Raises:
RuntimeError – Raised when this one-shot object has already prepared a project.
TypeError – Raised when
builderdoes not expose the expected Occam2D input interface.ValueError – Raised when the builder is incomplete, AI grids are incompatible, reliability length is wrong, mapped vectors are invalid, or model/startup dimensions disagree.
Notes
The builder and its data, model, and startup objects are modified in place. The files are then rewritten sequentially using their standard Occam writers.
See also
apply_observation_reliabilityImplements the data-space transformation.
OccamPrejudice.from_denseBuilds the model-space sparse constraint.
DUHIInverter2D.preparationReturns the same result after successful preparation.
Examples
>>> result = inverter.prepare( ... builder, ... ai_mean=mean, ... ai_std=std, ... observation_reliability=reliability, ... ) >>> result.n_params 336
- property is_prepared: bool#
Return whether Occam2D inputs were prepared successfully.
Examples
>>> DUHIInverter2D().is_prepared False
- property preparation: DUHIPreparation#
Return the completed preparation summary.
- Returns:
Immutable result from the successful preparation.
- Return type:
- Raises:
RuntimeError – Raised when
prepare()has not completed.
Examples
>>> inverter.preparation DUHIPreparation(...)
- property prejudice: OccamPrejudice#
Return the generated sparse Occam prejudice object.
- Returns:
Generated model-space target and weight definition.
- Return type:
- Raises:
RuntimeError – Raised when
prepare()has not completed.
- property ai_mean_parameters: ndarray#
Return a copy of the mapped AI mean parameter vector.
- Returns:
AI mean in Occam layer-major parameter order.
- Return type:
numpy.ndarray of float, shape (n_params,)
- Raises:
RuntimeError – Raised when
prepare()has not completed.
- property ai_std_parameters: ndarray#
Return a copy of mapped AI standard deviations.
- Returns:
Predictive standard deviations in Occam parameter order.
- Return type:
numpy.ndarray of float, shape (n_params,)
- Raises:
RuntimeError – Raised when
prepare()has not completed.
- property prejudice_weights: ndarray#
Return a copy of mapped uncertainty-dependent weights.
- Returns:
Dense prejudice weights in Occam parameter order.
- Return type:
numpy.ndarray of float, shape (n_params,)
- Raises:
RuntimeError – Raised when
prepare()has not completed.
- summary(*, max_fields=None)#
Return a compact DUHI configuration and state summary.
- Parameters:
max_fields (int, optional) – Accepted for compatibility with
PyCSAMTObject. DUHI uses a fixed scientific summary and therefore ignores this value.- Returns:
One-line summary containing uncertainty controls and the preparation state.
- Return type:
Examples
>>> "unprepared" in DUHIInverter2D().summary() True
- class pycsamt.ai.DUHIPreparation(workdir, prejudice_file, data_file, model_file, startup_file, n_data, n_params, effective_error_min, effective_error_max, prejudice_weight_min, prejudice_weight_max, ai_initialized)#
Bases:
PyCSAMTObjectSummarize one completed DUHI Occam2D preparation.
- Parameters:
workdir (pathlib.Path) – Occam2D project directory modified by the preparation.
prejudice_file (pathlib.Path) – Written sparse prejudice file.
data_file (pathlib.Path) – Rewritten Occam data file containing effective errors.
model_file (pathlib.Path) – Rewritten model file referencing
prejudice_file.startup_file (pathlib.Path) – Rewritten startup file, optionally containing the AI mean.
n_data (int) – Number of Occam data rows weighted by reliability.
n_params (int) – Number of Occam model parameters receiving mapped AI values.
effective_error_min (float) – Minimum effective error after reliability weighting.
effective_error_max (float) – Maximum effective error after reliability weighting.
prejudice_weight_min (float) – Minimum mapped AI prejudice weight.
prejudice_weight_max (float) – Maximum mapped AI prejudice weight.
ai_initialized (bool) – Whether the AI mean replaced the startup parameter vector.
- Variables:
files (dict of str to pathlib.Path) – Mapping of solver input roles to generated paths.
Examples
A preparation result is returned by
DUHIInverter2D.prepare():result = inverter.prepare( builder, ai_mean=mean, ai_std=std, observation_reliability=reliability, ) result.files["prejudice"]
- pycsamt.ai.dimensionality_reliability(beta_deg, *, beta_scale_deg=5.0, minimum=0.0)#
Convert phase-tensor skew into compatibility with 2-D physics.
- Parameters:
beta_deg (array-like of float) – Phase-tensor skew angles in degrees. Non-finite entries receive the configured minimum reliability.
beta_scale_deg (float, default=5.0) – Positive skew scale \(\beta_0\). Reliability equals
exp(-1)at this absolute skew.minimum (float, default=0.0) – Lower bound in the closed interval
[0, 1].
- Returns:
Reliability values with the same shape as
beta_deg.- Return type:
- Raises:
ValueError – If the input is empty or scalar controls are invalid.
Examples
>>> dimensionality_reliability([0.0, 5.0]).round(6).tolist() [1.0, 0.367879]
- pycsamt.ai.combine_observation_reliability(measurement, dimensionality, *, minimum=0.0)#
Return bounded measurement-by-dimensionality reliability.
- Parameters:
measurement (array-like of float) – Reliability factors in
[0, 1]. Inputs must be broadcastable to one common shape.dimensionality (array-like of float) – Reliability factors in
[0, 1]. Inputs must be broadcastable to one common shape.minimum (float, default=0.0) – Lower bound applied after multiplication.
- Returns:
Broadcast product clipped to
[minimum, 1].- Return type:
- Raises:
ValueError – If inputs are empty, non-finite, outside
[0, 1], or cannot be broadcast together.
Examples
>>> combine_observation_reliability( ... [0.8, 0.5], [0.5, 0.2] ... ).tolist() [0.4, 0.1]
- class pycsamt.ai.ConformalPredictor(base_predictor, alpha=0.1, eps=1e-10)#
Bases:
objectSplit conformal prediction wrapper for calibrated regression intervals.
Given a fitted base predictor \(f(\mathbf{x})\) that also exposes per-output uncertainty estimates \(\boldsymbol{\sigma}(\mathbf{x})\) (e.g.
EnsembleInverter), this class fits normalised nonconformity scores on a held-out calibration set and uses them to produce prediction bands with provable marginal coverage.Normalised nonconformity score (per sample, over all output dims):
\[s_i = \max_j \frac{|y_{ij} - f(\mathbf{x}_i)_j|} {\sigma_{ij} + \varepsilon}\]Calibration quantile (finite-sample correction, Vovk et al. 2005):
\[\hat{q} = \text{Quantile}_{1-\alpha + \frac{1}{n_{\rm cal}+1}} (s_1, \ldots, s_{n_{\rm cal}})\]Prediction interval for a new point \(\mathbf{x}\):
\[\hat{C}_j(\mathbf{x}) = \bigl[f(\mathbf{x})_j - \hat{q}\,\sigma_j(\mathbf{x}),\; f(\mathbf{x})_j + \hat{q}\,\sigma_j(\mathbf{x})\bigr]\]Marginal coverage guarantee (Vovk et al. 2005):
\[P\!\bigl(y_j \in \hat{C}_j(\mathbf{x})\bigr) \;\ge\; 1 - \alpha \quad \forall j\]- Parameters:
base_predictor (any) – A fitted object exposing
predict_with_uncertainty(X) -> (mean, std)where both outputs have shape(n_samples, n_params).alpha (float) – Default coverage level
1-alpha. Can be overridden per-call.eps (float) – Floor added to \(\boldsymbol{\sigma}\) to prevent division by zero.
Examples
>>> cp = ConformalPredictor(ens_inverter) >>> cp.calibrate(X_cal, y_cal) >>> center, lo, hi = cp.predict_intervals(X_new)
- calibrate(X_cal, y_cal, alpha=None)#
Fit nonconformity scores on a held-out calibration set.
- Parameters:
X_cal (ndarray, shape (n_cal, n_features))
alpha (float or None) – Coverage level for this calibration run; defaults to
self.alpha.
- Return type:
self
- predict_intervals(X, alpha=None)#
Return calibrated prediction intervals.
- Parameters:
X (ndarray, shape (n_samples, n_features))
alpha (float or None) – Coverage level; defaults to the value used in
calibrate().
- Returns:
center (ndarray, shape (n_samples, n_params))
lower (ndarray, shape (n_samples, n_params))
upper (ndarray, shape (n_samples, n_params)) – The interval \([\text{center} - \hat{q}\,\sigma,\; \text{center} + \hat{q}\,\sigma]\) satisfies the marginal coverage guarantee.
- Return type:
- coverage(X_test, y_test, alpha=None)#
Empirical marginal coverage on a test set.
A well-calibrated predictor returns a value close to
1 - alpha.
- coverage_diagnostics(X_test, y_test, alphas=None)#
Coverage table across a range of significance levels.
Useful for a reliability diagram: plot
actual_coveragevsnominal_coverage = 1 - alpha.- Parameters:
X_test (ndarray)
y_test (ndarray)
alphas (sequence of float or None) – Significance levels to evaluate. Defaults to 25 values uniformly spaced in
[0.02, 0.50].
- Returns:
diag
- Return type:
dict {alpha: actual_coverage}
- class pycsamt.ai.PosteriorCalibrator(n_bins=100)#
Bases:
objectGaussianization normalising flow for calibrated posterior sampling.
Implements the regression calibration scheme of Kuleshov et al. (2018): a monotone recalibration function \(g: [0,1] \to [0,1]\) is learned from calibration-set residuals via isotonic regression (PAVA), then composed with the standard-normal quantile function to produce draws from a calibrated posterior.
Step 1 — fit the recalibration map on \((y_{\rm cal}, \hat{y}_{\rm cal}, \sigma_{\rm cal})\), one residual scale \(s_j\) per output parameter \(j\) so that pooling residuals across parameters is meaningful even when they carry incompatible units (e.g. log10-resistivity and linear-metre thickness):
\[z_{ij} = \frac{y_{ij} - \hat{y}_{ij}}{\sigma_{ij}}, \qquad s_j = \operatorname{std}_i(z_{ij}), \qquad p_i = \Phi\!\left(\frac{z_{ij}}{s_j}\right), \qquad g = \text{PAVA}\!\left(p_{(i)},\, \frac{i}{n_{\rm cal}}\right)\]where \(p_{(i)}\) are the sorted predicted CDF values and \(i/n_{\rm cal}\) are the empirical CDF targets.
Step 2 — calibrated posterior samples, with \(\sigma_j\) rescaled by the same per-parameter \(s_j\):
\[u \sim \mathcal{U}(0,1),\quad p^* = g^{-1}(u),\quad \theta^*_j = \hat{y}_j + \frac{\sigma_j}{s_j} \cdot \Phi^{-1}(p^*)\]The map \(g^{-1}\) (inverse PAVA) is computed via linear interpolation. Because the composition \(\Phi^{-1} \circ g^{-1} \circ \Phi\) is a monotone bijection \(\mathbb{R} \to \mathbb{R}\), this constitutes a one-dimensional normalising flow that transforms the empirical residual distribution into a standard Gaussian.
- Parameters:
n_bins (int) – Number of equally spaced CDF bins used as knots for the interpolated inverse map. Higher values give a smoother flow at the cost of slight over-smoothing in data-sparse tails.
Examples
>>> pc = PosteriorCalibrator() >>> pc.fit(y_cal, y_pred_cal, sigma_cal) >>> samples = pc.predict_posterior( ... y_pred_test, sigma_test ... ) >>> cal_std = pc.calibrated_std(sigma_raw_test)
References
Kuleshov V., Fenner N. & Ermon S. (2018) Accurate uncertainties for deep learning using calibrated regression. ICML, PMLR 80, 2796–2804.
- fit(y_cal, y_pred_cal, sigma_cal)#
Fit the Gaussianization recalibration map.
- Parameters:
- Return type:
self
- predict_posterior(y_pred, sigma_pred, n_samples=500, rng=None)#
Draw samples from the calibrated posterior via inverse-CDF sampling.
- Parameters:
y_pred (ndarray, shape (n_samples_pred, n_params)) – Point predictions (ensemble mean).
sigma_pred (ndarray, shape (n_samples_pred, n_params)) – Raw (uncalibrated) uncertainty estimates.
n_samples (int) – Number of posterior draws per input point.
rng (numpy.random.Generator or None) – Random number generator for reproducibility.
- Returns:
draws – Calibrated posterior samples.
draws.mean(axis=0) ≈ y_predanddraws.std(axis=0) ≈ calibrated_sigma.- Return type:
- calibrated_std(sigma_raw)#
Apply the global variance recalibration to raw sigma estimates.
sigma_scale(s_jin the class docstring) is the empirical standard deviation of the calibration-set standardised residualsz_cal = (y_cal - y_pred_cal) / sigma_raw. A raw ensemble sigma that is systematically under-dispersed (the common case for a small or early-stopped ensemble) hass_j > 1; reproducing the calibration set’s own actual residual spread around a newsigma_rawtherefore requires multiplying bys_j, not dividing by it.- Parameters:
sigma_raw (ndarray)
- Returns:
sigma_cal – Recalibrated standard deviation such that coverage is approximately nominal on the calibration distribution.
- Return type:
ndarray
- calibration_error(y_test, y_pred_test, sigma_test)#
Mean absolute calibration error (MACE, Kuleshov et al. 2018).
For a perfectly calibrated predictor, the fraction of test samples falling below the \(p\)-quantile of the predicted distribution should equal \(p\). MACE measures the average deviation:
\[\text{MACE} = \frac{1}{B} \sum_{b=1}^{B} |\hat{F}(q_b) - q_b|\]where \(B\) is the number of CDF evaluation points.
- pycsamt.ai.list_pretrained()#
Return a dict of available pre-trained models.
- Returns:
models – Mapping
name → description.- Return type:
Examples
>>> from pycsamt.ai._zoo import list_pretrained >>> for name, desc in list_pretrained().items(): ... print(f"{name:35s} {desc[:60]}")
- pycsamt.ai.get_pretrained_info(name)#
Return metadata for a registered pre-trained model.
- Parameters:
name (str) – Model identifier (see
list_pretrained()).- Returns:
info
- Return type:
- Raises:
KeyError – If name is not in the registry.
- pycsamt.ai.download_checkpoint(name, *, cache_dir=None, force=False, verbose=True)#
Download a pre-trained checkpoint to the local cache.
- Parameters:
name (str) – Model name (see
list_pretrained()).cache_dir (str or None) – Override the default cache location (
~/.pycsamt/model_zoo/).force (bool) – Re-download even if the file exists locally.
verbose (bool)
- Returns:
path – Local path of the downloaded
.npzfile.- Return type:
Path
- Raises:
KeyError – If name is not in the model zoo.
RuntimeError – If the download fails or the MD5 check fails.
- class pycsamt.ai.CNN1DNet(n_features, n_out, *, channels=(32, 64, 128), kernel_size=5, dropout=0.3)#
Bases:
objectFactory wrapper — call
build()to get annn.Module.Use
EMInverter1Dinstead of instantiating this class directly.- Parameters:
n_features (int) – Length of the input feature vector.
n_out (int) – Length of the output parameter vector (
2*n_layers - 1).channels (sequence of int) – Number of filters in each convolutional block.
kernel_size (int) – Convolutional kernel width (same padding applied).
dropout (float) – Dropout probability in the FC head.
- build()#
- class pycsamt.ai.ResNet1DNet(n_features, n_out, *, channels=(64, 128, 256), dropout=0.3, n_blocks=2)#
Bases:
objectFactory wrapper for the 1-D residual CNN.
- Parameters:
n_features (int) – Input feature-vector length.
n_out (int) – Output parameter vector length (
2*n_layers - 1).channels (sequence of int) – Filter counts for the three residual stages. Default
(64, 128, 256)replicates the I8RFCN paper.dropout (float) – Dropout before the final linear layer.
n_blocks (int) – Number of residual blocks per stage (default 2).
- build()#
Return the
nn.Module.
- class pycsamt.ai.FCN1DNet(n_features, n_out, *, channels=(32, 64, 128, 64), dropout=0.2)#
Bases:
objectFactory wrapper for the fully-convolutional 1-D EM network.
- Parameters:
- build()#
Return the
nn.Module.
- class pycsamt.ai.UNet2DNet(n_in, n_out=1, *, channels=(32, 64, 128, 256, 512), dropout=0.2)#
Bases:
objectFactory wrapper for the 2-D U-Net.
All heavy imports are deferred to
build().- Parameters:
n_in (int) – Input channels — typically
n_components(e.g. 4 for off-diagonal MT impedance:log|Zxy|,phi_xy,log|Zyx|,phi_yx).n_out (int) – Output channels — 1 for a single \(\\log_{10}(\\rho)\) section.
channels (tuple of int, default) –
(32, 64, 128, 256, 512)Channel widths at each encoder stage plus the bridge.len(channels) - 1determines the number of pooling/upsampling stages.dropout (float, default 0.2) – 2-D spatial dropout probability in each convolutional block.
Examples
>>> net = UNet2DNet(n_in=4, n_out=1) >>> model = net.build()
- class pycsamt.ai.DRCNNNet(n_features_list, n_out, *, growth_rate=32, n_layers=6, hidden_dim=256, dropout=0.2)#
Bases:
objectFactory wrapper for the Dense Residual CNN.
- Parameters:
n_features_list (sequence of int) – Feature vector length for each input modality. E.g.
(120, 48)for 120 MT features and 48 seismic features.n_out (int) – Output dimension (number of subsurface parameters to predict).
growth_rate (int, default 32) – New channels added by each sub-layer in a dense block.
n_layers (int, default 6) – Number of sub-layers per dense block.
hidden_dim (int, default 256) – Dimension of the encoded representation from each modality and the fusion block output.
dropout (float, default 0.2)
Examples
>>> # MT (120 features) + TEM (48 features) >>> drcnn = DRCNNNet((120, 48), n_out=9) >>> model = drcnn.build()
- class pycsamt.ai.GCNNet(n_features, n_out, hidden=(256, 128, 64), dropout=0.1)#
Bases:
objectFactory that builds a PyTorch or TensorFlow GCN.
Call
build()after import to obtain the framework module.- Parameters:
n_features (int) – Dimensionality of per-node input features (e.g. 2 × n_freqs).
n_out (int) – Per-node output size (2n-1 for an n-layer model: n resistivities + n-1 thicknesses).
hidden (sequence of int) – Hidden-layer widths for each GCN message-passing step.
dropout (float) – Dropout probability applied after each hidden GCN layer.
- build()#
Return a
torch.nn.Modulefor this architecture.- Return type:
torch.nn.Module
- build_tf()#
Return a
tf.keras.Modelfor this architecture.- Return type:
tf.keras.Model
- pycsamt.ai.build_adjacency(coords, radius, *, self_loops=True, normalise=True)#
Build a symmetric adjacency matrix from 2-D station coordinates.
- Parameters:
coords (ndarray, shape (n_stations, 2)) – Station (x, y) positions in any consistent unit (metres, degrees).
radius (float) – Maximum inter-station distance for an edge to exist. Uses the same unit as coords.
self_loops (bool) – If
True(default), add \(\tilde{A} = A + I\).normalise (bool) – If
True(default), apply symmetric normalisation \(\tilde{D}^{-1/2}\tilde{A}\tilde{D}^{-1/2}\).
- Returns:
A – Adjacency matrix, optionally normalised.
- Return type:
ndarray, shape (n_stations, n_stations), float32
- class pycsamt.ai.Normalizer(eps=1e-08)#
Bases:
objectZ-score normaliser that handles NaN values.
- Parameters:
eps (float) – Small constant added to std to avoid division by zero.
- Variables:
- inverse_transform(X_norm)#
Reverse z-score normalisation.
- class pycsamt.ai.EMDataset(forward_ds, *, n_layers=None, log_thickness=True, x_norm=None, y_norm=None, augment_noise=0.0)#
Bases:
objectPyTorch
Dataset-compatible wrapper for aForwardDataset.Handles normalisation, log₁₀ thickness transform, and optional on-the-fly noise augmentation.
- Parameters:
forward_ds (ForwardDataset) – Source data produced by
generate_dataset().n_layers (int or None) – If given, only samples with
meta.n_layers == n_layersare kept.Nonekeeps all samples (variable n_layers, NaN-padded).log_thickness (bool) – Apply log₁₀ to thickness values in y before normalising. Strongly recommended when thicknesses span > 2 orders of magnitude.
x_norm (Normalizer or None) – Pre-fitted normaliser for X. If
Nonea new one is fitted on this dataset (use on training split only).y_norm (Normalizer or None) – Pre-fitted normaliser for y.
augment_noise (float) – If > 0, add Gaussian noise (this level) to X on-the-fly each epoch.
- Variables:
- inverse_y(y_norm)#
Undo normalisation + log₁₀ thickness transform on predicted y.
Returns raw parameter vector:
[rho_0…rho_{n-1}, thick_0…thick_{n-2}]where rho values are in Ω·m (not log) and thicknesses in metres.
- class pycsamt.ai.EMTrainer(model, *, lr=0.001, weight_decay=1e-05, patience=20, min_delta=1e-05, batch_size=256, device='cpu', grad_clip=None, verbose=True)#
Bases:
objectTraining loop manager for EM 1-D inversion networks.
- Parameters:
model (nn.Module) – The PyTorch network to train.
lr (float) – Initial learning rate. Default 1e-3.
weight_decay (float) – L2 regularisation. Default 1e-5.
patience (int) – Early-stopping patience (epochs without val-loss improvement).
min_delta (float) – Minimum val-loss improvement to reset patience counter.
batch_size (int) – Mini-batch size.
device (str) – Compute device (
'cpu','cuda','mps').grad_clip (float or None) – If set, clip gradient norms to this value.
verbose (bool, int, or str) – Progress verbosity — see
pycsamt.utils.progress.normalize_verbose().0/Falsesilent,1/Trueanimated bar,2/"log"one printed line per summary,"auto"tty-detected.
- Variables:
- fit(train_ds, val_ds, epochs=100)#
Train the network.
- pycsamt.ai.rmse(y_true, y_pred)#
Root mean square error, ignoring NaN.
Operates element-wise on
y_trueandy_pred. Both arrays are assumed to be in log₁₀(Ω·m) or normalised space.
- pycsamt.ai.mae(y_true, y_pred)#
Mean absolute error, ignoring NaN.
- pycsamt.ai.r2(y_true, y_pred)#
Coefficient of determination R², ignoring NaN.
- pycsamt.ai.relative_rmse(y_true, y_pred)#
Normalised RMSE:
sqrt(mean((y_true - y_pred)² / y_true²)).Useful when comparing models with very different resistivity ranges.
- pycsamt.ai.depth_rmse(y_true, y_pred, n_layers, *, depth_weight=True)#
RMSE on the resistivity sub-vector only (first
n_layerscolumns), optionally weighted by layer index so that deeper (harder) layers have less influence on the score.
- pycsamt.ai.layer_rmse(y_true, y_pred)#
Per-column RMSE vector.
- pycsamt.ai.masked_mse_loss(pred, target)#
MSE loss that ignores
NaN(padding) entries in target.
- pycsamt.ai.summarise(y_true, y_pred, n_layers=None)#
Compute all scalar metrics and return as a dict.
- class pycsamt.ai.AugmentNoise(sigma=0.05, phase_sigma=None, clip=3.0)#
Bases:
_BaseAugmenterAdd multiplicative log-Gaussian noise to feature amplitudes.
For each sample a noise vector
\[\boldsymbol{\varepsilon} \sim \mathcal{N}(\mathbf{0},\, \sigma^2 \mathbf{I})\]is added to the feature vector. If
phase_sigmais given a separate (typically smaller) noise level is used on phase features (assumed to occupy the second half of the feature vector wheninclude_phase=True).
- class pycsamt.ai.AugmentStaticShift(shift_range=(0.3, 3.0), n_amp_features=None, per_sample=True)#
Bases:
_BaseAugmenterApply a random static shift to amplitude features.
Static shift is a common MT artefact: all apparent-resistivity values at one site are scaled by a constant factor due to local near-surface heterogeneity. This augmenter simulates the effect by multiplying the first
n_amp_featuresfeature columns by a site-specific log-uniform random factor.- Parameters:
shift_range ((lo, hi), default (0.3, 3.0)) – Range of the multiplicative factor in linear scale.
n_amp_features (int or None) – Number of amplitude columns at the start of the feature vector.
None→ shift all features (use when features are log₁₀ amplitude only, without phase).per_sample (bool) – If
Truedraw an independent shift for each sample; otherwise draw one shift per batch.
- class pycsamt.ai.AugmentFreqDrop(drop_rate=0.1, contiguous=False, fill_value=0.0)#
Bases:
_BaseAugmenterRandomly zero-out a fraction of frequency channels.
Simulates missing data at individual frequencies (bad data periods, cultural noise bursts, dead-band contamination).
- class pycsamt.ai.AugmentMixup(alpha=0.2)#
Bases:
_BaseAugmenterMixup augmentation (Zhang et al. 2018).
Randomly pairs samples within the batch and creates convex combinations:
\[\tilde{\mathbf{x}} = \lambda \mathbf{x}_i + (1-\lambda) \mathbf{x}_j, \quad \tilde{\mathbf{y}} = \lambda \mathbf{y}_i + (1-\lambda) \mathbf{y}_j\]where \(\lambda \sim \mathrm{Beta}(\alpha, \alpha)\).
- Parameters:
alpha (float, default 0.2) – Beta distribution shape parameter. Small values (≈0.1) produce near-original samples;
alpha=1.0is uniform mixing.
- class pycsamt.ai.Compose(augmenters, seed=None)#
Bases:
objectSequentially apply a list of augmenters.
- Parameters:
Examples
>>> aug = Compose( ... [ ... AugmentStaticShift(shift_range=(0.5, 2.0)), ... AugmentNoise(sigma=0.03), ... AugmentFreqDrop(drop_rate=0.05), ... ] ... ) >>> X_aug, y_aug = aug(X_train, y_train)
- class pycsamt.ai.RandomApply(augmenter, p=0.5, seed=None)#
Bases:
objectApply an augmenter with probability p.
Examples
>>> aug = RandomApply(AugmentMixup(alpha=0.3), p=0.4) >>> X_aug, y_aug = aug(X_train, y_train)
- class pycsamt.ai.EMStyle(overrides=None)#
Bases:
objectContext manager that applies the shared EM plot style.
- Parameters:
overrides (dict or None) – Additional
rcParamsto merge on top of the base style.
Examples
>>> with EMStyle(): ... fig, ax = plt.subplots(figsize=EM_FIGSIZE["double"]) ... ax.semilogy(period, rho_a)
- pycsamt.ai.em_context(**overrides)#
Lightweight context manager shorthand.
>>> with em_context(figure_dpi=150): ... make_plot()
- pycsamt.ai.add_colorbar(mappable, ax, label='$\\log_{10}(\\rho)$ (Ω·m)', *, size='4%', pad=0.08)#
Attach a right-side colorbar to ax using
make_axes_locatable.
- pycsamt.ai.plot_compare(true_models, pred_models, *, n_cols=5, max_sites=20, depth_max=None, log_scale=True, show_rmse=True, site_labels=None, figsize_per_panel=(2.2, 4.0), title=None, style=True)#
Multi-panel comparison of true and predicted 1-D resistivity profiles.
- Parameters:
true_models (list of LayeredModel or ndarray (n_sites, n_params)) – Ground-truth models.
pred_models (list of LayeredModel or ndarray (n_sites, n_params)) – Network-predicted models (same order as true_models).
n_cols (int) – Number of columns in the panel grid.
max_sites (int) – Maximum number of sites to plot (first max_sites are used).
depth_max (float or None) – Maximum depth axis value [m].
log_scale (bool) – Log₁₀ x-axis for resistivity.
show_rmse (bool) – Annotate each panel with per-site log₁₀(ρ) RMSE.
site_labels (list of str or None) – Panel titles (default:
'Site 0','Site 1', …).figsize_per_panel ((w, h)) – Size of each individual panel in inches.
title (str or None) – Figure suptitle.
- Returns:
fig
- Return type:
Figure
- pycsamt.ai.plot_profile_pair(true_model, pred_model, *, ax=None, depth_max=None, log_scale=True, show_rmse=True, legend=True, style=True)#
Plot a single true/predicted resistivity–depth pair on ax.
- Parameters:
true_model (LayeredModel or ndarray (n_params,))
pred_model (LayeredModel or ndarray (n_params,))
ax (Axes or None)
depth_max (float or None)
log_scale (bool)
show_rmse (bool)
legend (bool)
style (bool)
- Returns:
ax
- Return type:
Axes
- pycsamt.ai.plot_convergence(history, *, ax=None, log_scale=True, best_epoch=None, smoothing=0.0, show_lr=True, title='Training convergence', style=True)#
Plot train / validation loss curves from a trainer history dict.
- Parameters:
history (dict or list of dict) – A dict with keys
'train_loss'and'val_loss'(and optionally'lr'), as returned byhistory. A list of such dicts (from multiple runs) activates the mean ± 1-σ band mode.ax (Axes or None) – Target axes. If
None, a new figure/axes is created.log_scale (bool) – Log₁₀ y-axis for the loss.
best_epoch (int or None) – If given, draw a vertical dashed line at this epoch.
smoothing (float in [0, 1)) – Exponential moving average smoothing coefficient. 0 = no smoothing.
show_lr (bool) – Overlay learning rate on a twin y-axis (if
'lr'in history).title (str) – Axes title.
- Returns:
fig
- Return type:
Figure
- pycsamt.ai.plot_lr_schedule(lr_history, *, ax=None, title='Learning rate schedule', style=True)#
Standalone learning-rate schedule plot.
- pycsamt.ai.plot_section(rho_2d, *, depths=None, stations=None, depth_max=2000.0, station_spacing=1.0, log_scale=True, vmin=None, vmax=None, cmap=None, title='', xlabel='Station', ylabel='Depth (m)', show_sites=True, figsize=None, ax=None, style=True)#
Plot a 2-D resistivity section.
- Parameters:
rho_2d (ndarray, shape (n_depth, n_stations)) – Resistivity or log₁₀(ρ) 2-D model.
depths (ndarray (n_depth,) or None) – Depth values in metres. Default: linear 0 →
depth_max.stations (ndarray (n_stations,) or None) – Station positions (arbitrary units).
depth_max (float) – Maximum depth used when
depthsisNone.station_spacing (float) – Station interval when
stationsisNone.log_scale (bool) – Apply log₁₀ transform before plotting. Set
Falseifrho_2dalready contains log₁₀(ρ).vmin (float or None) – Colour scale limits.
vmax (float or None) – Colour scale limits.
cmap (str or None) – Matplotlib colormap name. Defaults to
'RdYlBu_r'.title (str)
xlabel (str)
ylabel (str)
show_sites (bool) – Draw site-marker triangles at the surface.
ax (Axes or None)
- Returns:
fig
- Return type:
Figure
- pycsamt.ai.plot_section_pair(true_2d, pred_2d, *, depths=None, stations=None, depth_max=2000.0, station_spacing=1.0, log_scale=True, vmin=None, vmax=None, cmap=None, show_difference=True, figsize=None, style=True)#
Side-by-side comparison of true and predicted 2-D resistivity sections.
Optionally shows the absolute difference (
show_difference=True).- Parameters:
true_2d (ndarray (n_depth, n_stations))
pred_2d (ndarray (n_depth, n_stations))
depths (see
plot_section())stations (see
plot_section())depth_max (see
plot_section())station_spacing (see
plot_section())log_scale (see
plot_section())vmin (float or None) – Shared colour scale. When
None, computed from true_2d.vmax (float or None) – Shared colour scale. When
None, computed from true_2d.cmap (str or None)
show_difference (bool) – Add a third panel with the signed difference.
style (bool)
- Returns:
fig
- Return type:
Figure
- pycsamt.ai.plot_pseudo_section(rho_a_2d, *, freqs=None, stations=None, station_spacing=1.0, log_freq=True, log_rho=True, vmin=None, vmax=None, cmap=None, component='xy', title='', figsize=None, ax=None, style=True)#
Apparent-resistivity or phase pseudo-section plot.
- Parameters:
rho_a_2d (ndarray (n_freqs, n_stations)) – Apparent resistivity or phase values.
freqs (ndarray (n_freqs,) or None) – Frequencies in Hz.
stations (ndarray (n_stations,) or None) – Station positions.
station_spacing (float)
log_freq (bool) – Use log₁₀(period) for the y-axis.
log_rho (bool) – Apply log₁₀ transform to the data.
vmin (float or None)
vmax (float or None)
cmap (str or None)
component (str) – Label for the colour bar (e.g.
'xy','yx','phase_xy').title (str)
ax (Axes or None)
style (bool)
- Returns:
fig
- Return type:
Figure
- pycsamt.ai.plot_confusion_matrix(y_true, y_pred, *, class_names=None, normalise=True, cmap='Blues', title='Confusion Matrix', figsize=None, ax=None, style=True)#
Plot a confusion matrix for a classification model.
- Parameters:
y_true (int ndarray (n_samples,)) – Ground-truth class labels.
y_pred (int ndarray (n_samples,)) – Predicted class labels.
class_names (list of str or None) – Display labels for each class. Defaults to
['1-D','2-D','3-D']for three-class problems.normalise (bool) – Show row-normalised (recall) fractions.
cmap (str) – Matplotlib colormap.
title (see
plot_section().)figsize (see
plot_section().)ax (see
plot_section().)style (see
plot_section().)
- Returns:
fig
- Return type:
Figure
- pycsamt.ai.plot_residuals(y_true, y_pred, *, param_names=None, n_cols=4, figsize_per_panel=(2.5, 2.5), style=True)#
Scatter plots of predicted vs. true for each model parameter.
Each panel shows the 1:1 line, coloured scatter, and per-parameter R² annotation.
- pycsamt.ai.plot_layer_errors(y_true, y_pred, n_layers, *, log_rho=True, ax=None, figsize=None, style=True)#
Per-layer mean absolute error bar chart.
- pycsamt.ai.plot_uncertainty_bands(x, y_pred, y_upper, y_lower, y_true=None, *, ax=None, xlabel='', ylabel='', title='Prediction with Uncertainty', figsize=None, style=True)#
1-D prediction curve with uncertainty bands.
Suitable for showing per-site model parameter predictions (e.g. resistivity vs. depth) with ±1σ or 10/90 percentile bands.
- Parameters:
x (ndarray (n_points,)) – X-axis values (e.g. depth or frequency).
y_pred (ndarray (n_points,)) – Central prediction.
y_upper (ndarray (n_points,)) – Upper and lower uncertainty bounds.
y_lower (ndarray (n_points,)) – Upper and lower uncertainty bounds.
y_true (ndarray (n_points,) or None) – Ground-truth values to overlay.
ax (Axes or None)
xlabel (str)
ylabel (str)
title (str)
style (bool)
- Returns:
fig
- Return type:
Figure
- pycsamt.ai.plot_feature_importance(importances, feature_names=None, *, top_n=20, horizontal=True, ax=None, figsize=None, title='Feature Importance', style=True)#
Horizontal bar chart of feature importances.
Compatible with scikit-learn
feature_importances_arrays and any non-negative importance measure.- Parameters:
- Returns:
fig
- Return type:
Figure
- class pycsamt.ai.EMDenoiser(n_freqs=None, n_components=4, *, channels=(64, 128, 64), dropout=0.1, device=None)#
Bases:
BaseEMProcessorConvolutional autoencoder for MT impedance tensor denoising.
The network is trained on clean (or synthetic) impedance data by adding controlled noise and learning to reconstruct the original signal. Supports both PyTorch and TensorFlow backends via
pycsamt.backends.- Parameters:
n_freqs (int or None, default None) – Number of frequency channels in the input. When
None, inferred fromX.shape[2]on the first call tofit().n_components ({4, 8}, default 4) – Feature channels per frequency: 4 = off-diagonal \(Z_{xy}\), \(Z_{yx}\) amplitudes and phases; 8 = all four tensor components.
channels (tuple of int, default (64, 128, 64)) – Channel widths for the encoder/decoder stages.
dropout (float, default 0.1) – Dropout probability in the encoder bottleneck.
device (str or None) –
"cpu","cuda","mps","/GPU:0", orNonefor auto-detect via the active backend.
Notes
When neither PyTorch nor TensorFlow is available the denoiser falls back to a per-channel Gaussian smoothing filter (requires scipy).
Call
prepare_z_features()to convert a site collection to the(n_samples, n_components, n_freqs)array expected here.Examples
>>> import numpy as np >>> X_clean = np.random.randn(200, 4, 32).astype("float32") >>> den = EMDenoiser() >>> den.fit(X_clean, epochs=5, verbose=False) EMDenoiser(n_freqs=32, n_components=4) >>> X_denoised = den.transform(X_clean)
- fit(X, *, noise_level=0.05, epochs=50, batch_size=64, lr=0.001, val_frac=0.1, seed=None, verbose=True)#
Train the denoiser on clean impedance data.
- Parameters:
X (ndarray, shape (n_samples, n_components, n_freqs)) – Clean training data.
noise_level (float) – Standard deviation of additive Gaussian noise relative to the per-channel standard deviation of
X.epochs (int) – Maximum number of training epochs.
batch_size (int)
lr (float) – Initial Adam learning rate.
val_frac (float) – Fraction of training samples held out for validation.
seed (int or None)
verbose (bool)
- Return type:
self
- transform(X)#
Apply the trained denoiser.
- Parameters:
X (ndarray, shape (n_samples, n_components, n_freqs)) – Noisy impedance data.
- Returns:
X_denoised
- Return type:
ndarray, same shape as input
- pycsamt.ai.prepare_z_features(sites, *, n_components=4, log_amp=True, freq_ref=None)#
Extract an impedance feature array from a site collection.
- Parameters:
sites (SiteCollection, dict, or list of Z objects) – MT impedance data in any format accepted by
ensure_sites().n_components ({4, 8}) – 4 uses off-diagonal components only; 8 includes diagonal.
log_amp (bool) – If
True, amplitudes are transformed as \(\log_{10}(|Z| + \varepsilon)\) before packing.freq_ref (ndarray or None) – Common frequency grid to interpolate onto. When
None, the grid of the first site is used.
- Returns:
X – Feature array ready for
EMDenoiser.- Return type:
ndarray, shape
(n_sites, n_components, n_freqs)
- class pycsamt.ai.EMQCScorer(contamination=0.05, snr_threshold=3.0, skew_threshold=0.3, score_threshold=0.5, use_ml=True, n_estimators=100, random_state=None)#
Bases:
BaseEMProcessorML-based per-frequency QC scorer for MT impedance data.
Combines hard thresholds on SNR and Swift skew with an
IsolationForestanomaly model fitted on extracted signal-quality features. The final quality score for each (site, frequency) cell is \(s \in [0, 1]\); observations belowscore_thresholdshould be rejected before inversion.- Parameters:
contamination (float, default 0.05) – Expected fraction of contaminated samples, passed directly to
sklearn.ensemble.IsolationForest.snr_threshold (float, default 3.0) – Observations with SNR below this value are hard-flagged as bad regardless of the ML score.
skew_threshold (float, default 0.3) – Observations with Swift skew above this value are hard-flagged.
score_threshold (float, default 0.5) – Quality scores below this value are considered bad.
use_ml (bool, default True) – When
False, only the rule-based thresholds are applied and the IsolationForest is not fitted.n_estimators (int, default 100) – Number of trees in the IsolationForest.
random_state (int or None)
Examples
>>> from pycsamt.ai.processing import EMQCScorer >>> scorer = EMQCScorer(snr_threshold=5.0, use_ml=False) >>> scorer.fit(sites) EMQCScorer(rule_only) >>> tbl = scorer.score_table(sites)
- fit(X, **kwargs)#
Fit the QC model on a training set.
- transform(X)#
Compute quality scores.
- score_table(sites)#
Return per-(site, frequency) QC table.
- Parameters:
sites (SiteCollection or compatible)
- Returns:
df – Columns: station, freq, snr, swift_skew, asym, phase_xy, phase_yx, score, flag (0=bad, 1=good).
- Return type:
DataFrame
- class pycsamt.ai.AnomalyDetector(n_features=None, latent_dim=32, *, channels=(128, 64), threshold_percentile=95.0, device=None)#
Bases:
BaseEMProcessorProfile-level unsupervised anomaly detector.
- Parameters:
n_features (int or None, default None) – Feature vector length per site (typically
n_freqs × n_components). WhenNone, inferred fromX.shape[1]on the first call tofit().latent_dim (int, default 32) – Bottleneck dimension of the autoencoder.
channels (tuple of int, default (128, 64)) – Hidden layer widths in the encoder (decoder is mirrored).
threshold_percentile (float, default 95.0) – Sites with reconstruction error above this percentile of the training distribution are flagged as anomalous.
device (str or None) – Compute device. Ignored when using the PCA fallback.
Notes
When neither PyTorch nor TensorFlow is installed the detector silently uses a PCA reconstruction model via scikit-learn. The interface is identical; only the accuracy differs.
Examples
>>> import numpy as np >>> X = np.random.randn(100, 120).astype("float32") >>> det = AnomalyDetector(latent_dim=16) >>> det.fit(X, epochs=10, verbose=False) AnomalyDetector(n_features=120, latent_dim=16) >>> scores = det.transform(X) >>> flags = det.flag_anomalies(X)
- fit(X, *, epochs=80, batch_size=32, lr=0.001, val_frac=0.1, seed=None, verbose=True)#
Train the anomaly detector on profile data.
- transform(X)#
Compute per-site reconstruction error (anomaly score).
- Parameters:
X (ndarray, shape (n_samples, n_features))
- Returns:
scores – Per-site mean squared reconstruction error.
- Return type:
- class pycsamt.ai.DimensionalityClassifier(hidden=(128, 64), dropout=0.2, n_classes=3, lr=0.001, device=None)#
Bases:
BaseEMProcessorMLP classifier for MT data dimensionality (1-D / 2-D / 3-D).
- Parameters:
hidden (tuple of int, default (128, 64)) – Hidden layer widths of the shared MLP backbone.
dropout (float, default 0.2) – Dropout probability in each hidden layer.
n_classes (int, default 3) – Number of dimensionality classes (0=1D, 1=2D, 2=3D).
lr (float, default 1e-3) – Default learning rate; can be overridden in
fit().device (str or None)
Notes
Falls back to a random-forest classifier (scikit-learn) when neither PyTorch nor TensorFlow is available.
Examples
>>> from pycsamt.ai.processing import DimensionalityClassifier >>> clf = DimensionalityClassifier() >>> clf.fit(X_train, y_train, epochs=30) DimensionalityClassifier(n_classes=3, torch) >>> clf.predict(X_test) array([0, 1, 2, ...]) >>> clf.predict_strike(X_2d) array([ 35., -12., ...])
- classmethod from_features_table(df, *, label_col='dim', strike_col=None, skew_th=3.0, ellipt_th=0.2, **fit_kwargs)#
Construct and train a classifier from a
phase_features_table()DataFrame.- Parameters:
df (DataFrame)
label_col (str or None) – Column for pre-computed labels;
None→ rule-based labels.strike_col (str or None) – Column for strike direction in degrees.
skew_th (float) – Rule-based thresholds (used when
label_colis absent).ellipt_th (float) – Rule-based thresholds (used when
label_colis absent).**fit_kwargs – Passed to
fit()(e.g.epochs=50).
- Return type:
- fit(X, y=None, *, strike=None, epochs=80, batch_size=256, lr=None, val_frac=0.15, seed=None, verbose=True)#
Train the dimensionality classifier.
- Parameters:
X (ndarray (n_samples, 5) or DataFrame)
y (int ndarray (n_samples,) or None)
strike (float ndarray (n_samples,) or None)
epochs (int) – Training hyper-parameters.
batch_size (int) – Training hyper-parameters.
lr (float | None) – Training hyper-parameters.
val_frac (float) – Training hyper-parameters.
seed (int | None) – Training hyper-parameters.
verbose (bool) – Training hyper-parameters.
- Return type:
self
- transform(X)#
Compute class probabilities.
- predict(X)#
Predict dimensionality class (0=1D, 1=2D, 2=3D).
- predict_strike(X)#
Predict geoelectric strike direction (degrees).
Returns
NaNfor non-2-D sites and when using the RF fallback.
2.25.3. AI Modules#
The AI inverters, newer data contracts, geological priors, objective functions, domain-gap tools, experiment records, and validation gates have dedicated references:
|
1-D CNN for EM inversion — Puzyrev (2019/2021) architecture. |
|
DRCNNNet — Dense Residual CNN for joint / multi-modal EM inversion. |
|
Fully Convolutional Network for EM inversion — Moghadas (2020) style. |
|
Graph Convolutional Network for spatial EM inversion. |
|
1-D Residual CNN for EM inversion — Liu et al. (2021) I8RFCN style. |
|
UNet2DNet — 2-D U-Net architecture for EM section inversion. |
|
AnomalyDetector — unsupervised profile-level anomaly detection. |
|
DimensionalityClassifier — MLP-based MT data dimensionality classifier. |
|
EMDenoiser — convolutional autoencoder for MT impedance tensor denoising. |
|
EMQCScorer — ML-based per-frequency quality control scorer. |
|
Data augmentation for EM training datasets. |
|
PyTorch Dataset wrapper and normalisation utilities for EM training data. |
|
Evaluation metrics for 1-D EM inversion networks. |
|
Training loop for EM neural networks. |
|
Side-by-side true vs predicted 1-D resistivity profile panels. |
|
Training convergence visualisation. |
|
Diagnostic plots for AI/ML model evaluation. |
|
2-D resistivity section and pseudo-section visualisation. |