%23%20%2F%2F%2F%20script%0A%23%20requires-python%20%3D%20%22%3E%3D3.11%22%0A%23%20dependencies%20%3D%20%5B%0A%23%20%20%20%20%20%22mlflow%22%2C%0A%23%20%20%20%20%20%22numpy%22%2C%0A%23%20%20%20%20%20%22polars%22%2C%0A%23%20%20%20%20%20%22scikit-learn%22%2C%0A%23%20%20%20%20%20%22yohou%22%2C%0A%23%20%20%20%20%20%22yohou-mlflow%22%2C%0A%23%20%5D%0A%23%20%2F%2F%2F%0A%0Aimport%20marimo%0A%0A__generated_with%20%3D%20%220.25.1%22%0Aapp%20%3D%20marimo.App(width%3D%22medium%22)%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_()%3A%0A%20%20%20%20import%20marimo%20as%20mo%0A%0A%20%20%20%20return%20(mo%2C)%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%20Scheduled%20Forecasting%20with%20a%20Model%20Registry%0A%0A%20%20%20%20In%20this%20notebook%2C%20we%20will%20run%20the%20loop%20a%20scheduled%20forecasting%20job%20runs.%0A%20%20%20%20We%20will%20fit%20a%20forecaster%2C%20register%20it%20in%20an%20MLflow%20model%20registry%2C%20load%20it%0A%20%20%20%20back%20as%20a%20later%20run%20would%2C%20feed%20it%20a%20week%20of%20new%20observations%2C%20forecast%20the%0A%20%20%20%20next%20week%2C%20and%20register%20the%20updated%20forecaster%20for%20the%20following%20run.%0A%0A%20%20%20%20**Prerequisites**%3A%20basic%20familiarity%20with%20yohou's%20%60fit%60%20and%20%60predict%60.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_()%3A%0A%20%20%20%20import%20datetime%20as%20dt%0A%20%20%20%20import%20shutil%0A%20%20%20%20import%20tempfile%0A%20%20%20%20from%20pathlib%20import%20Path%0A%0A%20%20%20%20import%20mlflow%0A%20%20%20%20import%20numpy%20as%20np%0A%20%20%20%20import%20polars%20as%20pl%0A%20%20%20%20from%20sklearn.linear_model%20import%20Ridge%0A%20%20%20%20from%20yohou.point%20import%20PointReductionForecaster%0A%0A%20%20%20%20import%20yohou_mlflow%0A%0A%20%20%20%20return%20(%0A%20%20%20%20%20%20%20%20Path%2C%0A%20%20%20%20%20%20%20%20PointReductionForecaster%2C%0A%20%20%20%20%20%20%20%20Ridge%2C%0A%20%20%20%20%20%20%20%20dt%2C%0A%20%20%20%20%20%20%20%20mlflow%2C%0A%20%20%20%20%20%20%20%20np%2C%0A%20%20%20%20%20%20%20%20pl%2C%0A%20%20%20%20%20%20%20%20shutil%2C%0A%20%20%20%20%20%20%20%20tempfile%2C%0A%20%20%20%20%20%20%20%20yohou_mlflow%2C%0A%20%20%20%20)%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%201.%20Create%20some%20data%0A%0A%20%20%20%20We%20create%20six%20months%20of%20daily%20demand%20with%20a%20weekly%20pattern%2C%20and%20keep%20the%0A%20%20%20%20last%20week%20aside%3A%20it%20plays%20the%20part%20of%20the%20observations%20that%20arrive%20after%0A%20%20%20%20the%20model%20was%20registered.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(dt%2C%20np%2C%20pl)%3A%0A%20%20%20%20days%20%3D%20pl.datetime_range(dt.datetime(2024%2C%201%2C%201)%2C%20dt.datetime(2024%2C%206%2C%2030)%2C%20%221d%22%2C%20eager%3DTrue)%0A%20%20%20%20steps%20%3D%20np.arange(len(days))%0A%20%20%20%20demand%20%3D%20pl.DataFrame(%7B%22time%22%3A%20days%2C%20%22demand%22%3A%20100%20%2B%2010%20*%20np.sin(2%20*%20np.pi%20*%20steps%20%2F%207)%20%2B%20steps%20*%200.1%7D)%0A%20%20%20%20history%2C%20next_week%20%3D%20demand%5B%3A-7%5D%2C%20demand%5B-7%3A%5D%0A%20%20%20%20history.tail(3)%0A%20%20%20%20return%20history%2C%20next_week%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%202.%20Fit%20a%20forecaster%0A%0A%20%20%20%20We%20fit%20a%20forecaster%20that%20predicts%20the%20next%20seven%20days.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(PointReductionForecaster%2C%20Ridge%2C%20history)%3A%0A%20%20%20%20forecaster%20%3D%20PointReductionForecaster(estimator%3DRidge())%0A%20%20%20%20forecaster.fit(history%2C%20forecasting_horizon%3D7)%0A%20%20%20%20forecaster.predict()%0A%20%20%20%20return%20(forecaster%2C)%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20Notice%20the%20%60vintage_time%60%20column%3A%20every%20row%20was%20forecast%20from%2023%20June%2C%20the%0A%20%20%20%20last%20day%20the%20forecaster%20has%20seen.%0A%0A%20%20%20%20%23%23%203.%20Register%20it%0A%0A%20%20%20%20We%20keep%20the%20registry%20in%20a%20temporary%20directory%2C%20which%20the%20last%20cell%20deletes%2C%0A%20%20%20%20and%20log%20the%20forecaster%20as%20version%201%20of%20%60daily-demand%60.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(Path%2C%20forecaster%2C%20mlflow%2C%20tempfile%2C%20yohou_mlflow)%3A%0A%20%20%20%20workdir%20%3D%20Path(tempfile.mkdtemp())%0A%20%20%20%20mlflow.set_tracking_uri(f%22sqlite%3A%2F%2F%2F%7Bworkdir%20%2F%20'mlflow.db'%7D%22)%0A%20%20%20%20experiment_id%20%3D%20mlflow.create_experiment(%22registry-loop%22%2C%20artifact_location%3D(workdir%20%2F%20%22artifacts%22).as_uri())%0A%20%20%20%20mlflow.set_experiment(experiment_id%3Dexperiment_id)%0A%0A%20%20%20%20with%20mlflow.start_run()%3A%0A%20%20%20%20%20%20%20%20first%20%3D%20yohou_mlflow.log_model(forecaster%2C%20name%3D%22forecaster%22%2C%20registered_model_name%3D%22daily-demand%22)%0A%20%20%20%20first.registered_model_version%0A%20%20%20%20return%20(workdir%2C)%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20Before%20the%20model%20was%20kept%2C%20Yohou-MLflow%20loaded%20it%20back%20once%20and%20checked%20that%0A%20%20%20%20it%20predicts%20exactly%20what%20%60forecaster%60%20predicts.%0A%0A%20%20%20%20%23%23%204.%20Load%20it%20in%20a%20later%20run%0A%0A%20%20%20%20A%20scheduled%20job%20starts%20from%20the%20registry%2C%20so%20we%20load%20version%201.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(forecaster%2C%20yohou_mlflow)%3A%0A%20%20%20%20loaded%20%3D%20yohou_mlflow.load_model(%22models%3A%2Fdaily-demand%2F1%22)%0A%20%20%20%20same_as_registered%20%3D%20loaded.predict().equals(forecaster.predict())%0A%20%20%20%20assert%20same_as_registered%0A%20%20%20%20same_as_registered%0A%20%20%20%20return%20(loaded%2C)%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%205.%20Observe%20the%20new%20week%20and%20forecast%0A%0A%20%20%20%20The%20week%20we%20kept%20aside%20has%20now%20happened.%20We%20give%20it%20to%20the%20loaded%0A%20%20%20%20forecaster%20and%20forecast%20again.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(loaded%2C%20next_week)%3A%0A%20%20%20%20loaded.observe(next_week)%0A%20%20%20%20forecast%20%3D%20loaded.predict()%0A%20%20%20%20forecast%0A%20%20%20%20return%20(forecast%2C)%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20Notice%20that%20the%20vintage%20moved%20to%2030%20June%20and%20the%20forecast%20now%20covers%20the%0A%20%20%20%20first%20week%20of%20July%3A%20%60observe%60%20updated%20what%20the%20forecaster%20has%20seen%20without%0A%20%20%20%20refitting%20it.%0A%0A%20%20%20%20%23%23%206.%20Register%20the%20updated%20forecaster%0A%0A%20%20%20%20The%20next%20run%20must%20start%20where%20this%20one%20stopped%2C%20so%20we%20register%20the%20updated%0A%20%20%20%20forecaster%20as%20version%202%20and%20check%20that%20it%20remembers%20the%20week%20it%20observed.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(forecast%2C%20loaded%2C%20mlflow%2C%20yohou_mlflow)%3A%0A%20%20%20%20with%20mlflow.start_run()%3A%0A%20%20%20%20%20%20%20%20yohou_mlflow.log_model(loaded%2C%20name%3D%22forecaster%22%2C%20registered_model_name%3D%22daily-demand%22)%0A%20%20%20%20latest%20%3D%20yohou_mlflow.load_model(%22models%3A%2Fdaily-demand%2F2%22)%0A%20%20%20%20continues%20%3D%20latest.predict().equals(forecast)%0A%20%20%20%20assert%20continues%0A%20%20%20%20%7B%22observed%20up%20to%22%3A%20latest.observed_time_%2C%20%22same%20forecast%22%3A%20continues%7D%0A%20%20%20%20return%20(latest%2C)%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%207.%20Clean%20up%0A%0A%20%20%20%20We%20delete%20the%20temporary%20directory%20that%20held%20the%20registry.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(latest%2C%20shutil%2C%20workdir)%3A%0A%20%20%20%20assert%20latest%20is%20not%20None%20%20%23%20runs%20only%20after%20version%202%20was%20registered%20and%20loaded%0A%20%20%20%20shutil.rmtree(workdir%2C%20ignore_errors%3DTrue)%0A%20%20%20%20assert%20not%20workdir.exists()%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%20What%20We%20Built%0A%0A%20%20%20%20We%20ran%20a%20scheduled%20forecasting%20loop%20against%20a%20model%20registry%3A%20we%20logged%20and%0A%20%20%20%20registered%20a%20fitted%20forecaster%20with%20%60yohou_mlflow.log_model%60%2C%20loaded%20it%20with%0A%20%20%20%20%60yohou_mlflow.load_model%60%2C%20updated%20it%20with%20a%20week%20of%20new%20observations%2C%20and%0A%20%20%20%20registered%20the%20result%20as%20version%202%2C%20which%20continues%20exactly%20where%20the%0A%20%20%20%20previous%20run%20stopped.%0A%0A%20%20%20%20%23%23%20Next%20Steps%0A%0A%20%20%20%20-%20%5BHow%20to%20check%20a%20model%20before%20deploying%5D(%2Fpages%2Fhow-to%2Fcheck-before-deploying%2F)%0A%20%20%20%20-%20%5BHow%20to%20predict%20through%20MLflow's%20generic%20interface%5D(%2Fpages%2Fhow-to%2Fpredict-with-pyfunc%2F)%0A%20%20%20%20-%20%5BHow%20to%20keep%20the%20registry%20small%5D(%2Fpages%2Fhow-to%2Fkeep-the-registry-small%2F)%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20app.run()%0A
5d533a4f27a88bd31a90b471a290229a