Overview¶
XFlow connects data sources, preprocessing, training, and evaluation for scientific machine learning. It grew out of physics research. Application logic stays in Python functions and classes, with each part of the workflow responsible for one job:
Provider: select raw files or database records.
Pipeline: turn each raw item into a sample using ordered callables.
Trainer: run a supplied model, optimizer, and loss over batches.
Evaluation: run inference and pass predictions to hooks.
Use these parts independently or combine them. A pipeline does not choose a model, and a trainer does not decide how to load files. Native framework objects remain accessible.
Install¶
Use Python 3.12 for this example:
python -m pip install "xflow-py[ml_torch]"
For a local checkout, run python -m pip install -e ".[ml_torch]" from the
repository root. pip install xflow-py installs the data and configuration
tools without a training backend. The ml_tf extra provides TensorFlow
dataset adapters. XFlow’s concrete trainer is TorchTrainer, which uses PyTorch.
Provider to training¶
This runnable example creates 64 CSV files, each containing one input and one
target. It fits a linear model to y = 2x + 1 and writes a checkpoint and
history to xflow-demo-run.
from pathlib import Path
import numpy as np
import torch
from torch.utils.data import DataLoader
from xflow import FileProvider, PyTorchPipeline, TorchTrainer
# Make a small regression dataset: one CSV row (input, target) per file.
data_dir = Path("xflow-demo-data")
data_dir.mkdir(exist_ok=True)
for i, x in enumerate(np.linspace(-1, 1, 64)):
np.savetxt(data_dir / f"{i:03d}.csv", [[x, 2 * x + 1]], delimiter=",")
def load_sample(path):
row = np.loadtxt(path, delimiter=",", dtype=np.float32)
return torch.from_numpy(row[:1]), torch.from_numpy(row[1:])
provider = FileProvider(data_dir, extensions=".csv")
train_source, val_source = provider.split(ratio=0.8, seed=42)
train = PyTorchPipeline(train_source, transforms=[load_sample], skip_errors=False)
val = PyTorchPipeline(val_source, transforms=[load_sample], skip_errors=False)
train_loader = DataLoader(train.to_framework_dataset(), batch_size=8, shuffle=True)
val_loader = DataLoader(val.to_framework_dataset(), batch_size=8)
model = torch.nn.Linear(1, 1)
trainer = TorchTrainer(
model=model,
data_pipeline=train,
output_dir="xflow-demo-run",
optimizer=torch.optim.SGD(model.parameters(), lr=0.1),
criterion=torch.nn.MSELoss(),
device="cpu",
)
history = trainer.fit(epochs=10, train_loader=train_loader, val_loader=val_loader)
trainer.save_history()
trainer.save_model()
print(history["val_loss"][-1])
For an existing dataset, replace the creation loop and load_sample.
FileProvider returns paths; PyTorchPipeline applies the loader on access;
the native DataLoader shuffles and batches the resulting tensor pairs.
Pass loaders explicitly to fit(). This example uses the default
num_workers=0.
DataPipeline offers lazy Python iteration; InMemoryPipeline processes
samples once and keeps them in memory. BaseModel and BaseTrainer are
abstract interfaces. A native torch.nn.Module is enough for TorchTrainer.
Compose and extend¶
Transforms are callables. pipe processes one sample; flow applies the same
steps lazily to an iterable; compose packages steps into a reusable callable.
A list of transforms applies positionally to tuple components, with None
passing a component through:
from xflow import compose, consume, flow, pipe
scale_input = compose([lambda x: x / 255.0, None])
assert scale_input((255.0, "label")) == (1.0, "label")
assert pipe((2, 3, "label"), [consume(2, sum), None]) == (5, "label")
assert list(flow([1, 2], lambda x: x * 2)) == [2, 4]
Add a custom transform directly to transforms or register it for use in
configuration. Import its module before building the configured pipeline:
from xflow.data.transform import TransformRegistry, build_transforms_from_config
@TransformRegistry.register("scale_pair")
def scale_pair(sample, factor=1.0):
x, y = sample
return x * factor, y
transforms = build_transforms_from_config([
{"name": "scale_pair", "params": {"factor": 0.5}}
])
For a new data source, implement DataProvider from xflow.data.provider:
__call__() returns items, __len__() reports their count, and subsample()
returns a subset provider. A training callback can subclass Callback from
xflow.trainers.trainer and implement hooks such as on_epoch_end(ctx).
Keep custom extensions in your own package. The repository’s
xflow.extensions modules are excluded from published wheels.
See Core API for the core contracts.