Image prediction#
The image prediction example shows a remote application with trainer, predictor, and end-user services. It combines service implementations, custom metrics, and a degradation policy in emulation mode.
Use it when you want to understand:
how to attach concrete service implementations to an application,
how to configure a remote simulation,
how to collect custom metrics from remote service behaviour.
The full code lives in the examples/image_prediction directory.
Run it from the repository root with:
uv run image-prediction
Application#
Application code
from .services import (
EndService,
PredictorService,
TrainerService,
)
from eclypse.graph import Application
from eclypse.utils.constants import MAX_LATENCY
image_app = Application("ImagePrediction", include_default_assets=True)
image_app.add_service(
EndService("EndService"),
cpu=1,
gpu=0,
ram=0.5,
storage=0.5,
availability=0.9,
processing_time=5,
)
image_app.add_service(
PredictorService("PredictorService"),
cpu=1,
gpu=1,
ram=16.0,
storage=64.0,
availability=0.9,
)
image_app.add_service(
TrainerService("TrainerService"),
cpu=16,
gpu=4,
ram=16.0,
storage=2.0,
availability=0.9,
)
image_app.add_edge(
"EndService",
"PredictorService",
latency=MAX_LATENCY,
bandwidth=20.0,
symmetric=True,
)
image_app.add_edge(
"PredictorService",
"TrainerService",
latency=MAX_LATENCY,
bandwidth=100.0,
symmetric=True,
)
Simulation#
Simulation code
from .application import image_app as app
from .metrics import get_metrics
from .services.utils import (
BASE_PATH,
STEP_EVERY_MS,
STEPS,
)
from .update_policy import DegradePolicy
from eclypse.builders.infrastructure import get_star
from eclypse.placement.strategies import RandomStrategy
from eclypse.simulation import (
Simulation,
SimulationConfig,
)
def main() -> None:
"""Run the Image Prediction example."""
seed = 2
sim_config = SimulationConfig(
seed=seed,
max_steps=STEPS,
step_every_ms=STEP_EVERY_MS,
include_default_metrics=False,
events=get_metrics(),
log_to_file=True,
path=BASE_PATH,
# Pass a RemoteBootstrap instance here to request specific Ray resources.
remote=True,
)
sim = Simulation(
get_star(
infrastructure_id="IPInfr",
n_clients=5,
seed=seed,
update_policies=DegradePolicy(epochs=STEPS),
include_default_assets=True,
resource_init="max",
symmetric=True,
),
simulation_config=sim_config,
)
strategy = RandomStrategy(spread=True)
sim.register(app, strategy)
sim.run()
if __name__ == "__main__":
main()
Metrics#
Metrics code
import os
import psutil
from eclypse.report.metrics import metric
from eclypse.report.metrics.defaults import featured_latency
from eclypse.utils.constants import DRIVING_EVENT
@metric.service(name="img_counter", remote=True)
def get_img_counter(self):
if self.id == "EndService":
if self.img_counter == 0:
self.logger.warning("No images were processed")
return 0
return self.img_counter
return None
@metric.service(name="accuracy", remote=True)
def get_acc(self):
if self.id == "EndService" and self.img_counter != 0:
acc = self.correct / self.img_counter
self.correct = 0
self.img_counter = 0
return acc
return None
@metric.service(name="model_change", remote=True)
def is_changed(self):
if self.id == "PredictorService":
if self.new_model_signal:
self.new_model_signal = False
return 1
return None
@metric.simulation(name="cpu_usage", activates_on=[DRIVING_EVENT, "stop"])
class CPUMonitor:
def __init__(self):
self.process = None
def __call__(self, event):
if self.process is None:
self.process = psutil.Process(os.getpid())
return self.process.cpu_percent(interval=0.1)
@metric.service(
name="remote_cpu_usage",
activates_on=[DRIVING_EVENT, "stop"],
remote=True,
)
class RemoteCPUMonitor:
def __init__(self):
self.process = None
def __call__(self, event):
if self.process is None:
self.process = psutil.Process(os.getpid())
return self.process.cpu_percent(interval=0.1)
@metric.simulation(
name="memory_usage",
activates_on=[DRIVING_EVENT, "stop"],
)
class MemoryMonitor:
def __init__(self):
self.process = None
def __call__(self, event):
if self.process is None:
self.process = psutil.Process(os.getpid())
memory_usage = self.process.memory_info().rss
return memory_usage / (1024 * 1024) # Convert to MB
@metric.service(
name="remote_memory_usage",
activates_on=[DRIVING_EVENT, "stop"],
remote=True,
)
class RemoteMemoryMonitor:
def __init__(self):
self.process = None
def __call__(self, event):
if self.process is None:
self.process = psutil.Process(os.getpid())
memory_usage = self.process.memory_info().rss
return memory_usage / (1024 * 1024)
def get_metrics():
return [
get_img_counter,
get_acc,
is_changed,
featured_latency,
CPUMonitor(),
MemoryMonitor(),
RemoteCPUMonitor(),
RemoteMemoryMonitor(),
]
What to inspect#
This example is useful when the application behaviour matters, not only the placement. Inspect the custom metrics together with the service implementations to see how emulated runtime state becomes reportable simulation data.
Related concepts: