Abner Ayala
03/25/2026, 9:37 PMclass SimpleModel:
def __init__(self, model_checkpoint_uri: str):
self.device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
self.model = load_model(model_uri).to(self.device)
self.model.eval()
def predict(self, images):
if len(images) == 0:
return []
images_tensor = torch.from_numpy(np.array(images.to_pylist())).to(self.device)
batch_size = images_tensor.shape[0]
with torch.inference_mode():
probabilities, features = self.model(images_tensor)
features = features.half().detach().cpu().numpy().tolist()
rounded_probabilities = probabilities.round(decimals=4).detach().cpu().numpy().tolist()
predicted_class_id = probabilities.argmax(dim=1).detach().cpu().tolist()
predicted_class_name = [CLASSES[p] for p in predicted_class_id]
predictions = []
for i in range(batch_size):
prediction = json.dumps({
"class_id": predicted_class_id[i],
"class_name": predicted_class_name[i],
"probability": rounded_probabilities[i],
})
predictions.append(prediction)
return predictions, features
During Runtime: Not working
ModelClass = daft.cls(EyesClosedClassifier, gpus=1, max_concurrency=cfg.num_gpus)
model = ModelClass(model_checkpoint_uri=cfg.pipeline.model.checkpoint_uri)
model_inference = daft.method.batch(
model.predict,
return_dtype=daft.DataType.struct({
"predictions": daft.DataType.string(),
"features": daft.DataType.list(daft.DataType.float32()),
}),
unnest=True,
batch_size=cfg.batch_size,
)Robert Howell
03/25/2026, 9:44 PMAbner Ayala
03/25/2026, 9:47 PMRobert Howell
03/25/2026, 10:02 PMRobert Howell
03/25/2026, 10:05 PMRobert Howell
03/25/2026, 10:07 PM_Extractor().extract at the end which gives you an Expression type backed by the underlying UDF, which is what allows you to use it in the DSL.Robert Howell
03/25/2026, 10:08 PMAbner Ayala
03/25/2026, 10:10 PMAbner Ayala
03/25/2026, 11:47 PMAbner Ayala
03/25/2026, 11:56 PMCory Grinstead
03/26/2026, 1:37 AMAbner Ayala
03/26/2026, 1:44 AMAbner Ayala
03/26/2026, 1:47 AMCory Grinstead
03/26/2026, 1:56 AMwith daft.execution_config_ctx(enable_dynamic_batching=True):
// df = ...Abner Ayala
03/26/2026, 1:58 AMAbner Ayala
03/26/2026, 3:11 PMinto_partitions(16) after the explode.
I also tried removing the expensive preprocessing and just use daft.resize just testing purposed but still gettin OOM.
I also try to manually set into_batches(10) before image download and decode but as soon as I do that I start getting too many spills and eventually get object lost error.
Running out of ideas 😕Cory Grinstead
03/26/2026, 5:54 PM1 or 2 just to see if it is in fact the udf OOMing.
model_inference = daft.method.batch(
model.predict,
return_dtype=daft.DataType.struct({
"predictions": daft.DataType.string(),
"features": daft.DataType.list(daft.DataType.float32()),
}),
unnest=True,
batch_size=1
)Abner Ayala
03/26/2026, 6:02 PMEverett Kleven
03/27/2026, 7:06 PMAbner Ayala
03/27/2026, 8:00 PM