Note
Go to the end to download the full example code.
Comparing network architectures (CNN, ResNet, FCN)#
EMInverter1D ships three 1-D
architectures — a plain CNN, a residual network (ResNet), and a fully
connected network (FCN). Which one to use is an empirical question, so this
example trains all three on the same dataset and lays the results out as
a comparison grid: convergence curves, a metrics table, prediction
residuals, and per-layer error bars.
Train the three architectures#
Each is trained with the same epochs/batch size. We keep the fitted inverter and its training history for the comparisons below.
from pycsamt.ai.inversion.inv1d import EMInverter1D
ARCHS = ["cnn1d", "resnet", "fcn"]
inverters, histories = {}, {}
for arch in ARCHS:
inv = EMInverter1D(arch=arch, n_layers=4, solver="mt1d")
inv.fit(train, epochs=6 if _DOCS else 25, batch_size=64, verbose=False)
inverters[arch] = inv
histories[arch] = inv._history
Convergence, overlaid#
plot_convergence() accepts a list of histories
and overlays them, so the learning curves are directly comparable on one
axis.

<matplotlib.legend.Legend object at 0x7f2aa3943020>
A metrics table#
The pycsamt.ai.training metric helpers score each architecture on
the held-out test set. We report them on the resistivity parameters
(log10 rho, the primary target — all on one scale) rather than mixing in
layer thicknesses which live in metres and would dominate the numbers.
from pycsamt.ai import mae, r2, rmse
N_LAYERS = 4
rho = slice(0, N_LAYERS) # log10-resistivity columns
rows = []
for arch in ARCHS:
yp = inverters[arch].predict(test.X)
rows.append(
(
arch,
rmse(test.y[:, rho], yp[:, rho]),
mae(test.y[:, rho], yp[:, rho]),
r2(test.y[:, rho], yp[:, rho]),
)
)
print(f"{'arch':>8} {'RMSE':>8} {'MAE':>8} {'R^2':>8} (log10 resistivity)")
for arch, rm, ma, rr in rows:
print(f"{arch:>8} {rm:8.4f} {ma:8.4f} {rr:8.3f}")
best = min(rows, key=lambda r: r[1])[0]
print("best (lowest resistivity RMSE):", best)
arch RMSE MAE R^2 (log10 resistivity)
cnn1d 0.8679 0.7061 0.369
resnet 0.9655 0.8193 0.219
fcn 0.9827 0.8211 0.191
best (lowest resistivity RMSE): cnn1d
Residuals of the best model#
plot_residuals() plots predicted-minus-true per
parameter for the best architecture. Tight, unbiased clouds centred on
zero mean the network is neither over- nor under-estimating that
parameter.

Text(0.5, 1.02, 'Prediction residuals — cnn1d')
Per-layer error#
plot_layer_errors() breaks the error down by layer,
revealing the classic MT/AMT depth-resolution trade-off: shallow layers
are recovered tightly, deeper layers less so.

Takeaway. On this synthetic task the three architectures land close together, with ResNet and CNN typically edging out the plain FCN. On real data, run exactly this grid on your own training set to pick an architecture — then quantify confidence with Uncertainty and calibration.
Total running time of the script: (0 minutes 1.916 seconds)