From 0a5efd1e4ec02db1cffca260beb6207341751342 Mon Sep 17 00:00:00 2001
From: jsamantaucd <159275418+jsamantaucd@users.noreply.github.com>
Date: Mon, 22 Jul 2024 16:27:09 +0100
Subject: [PATCH 01/15] test commit
---
README.md | 1 +
1 file changed, 1 insertion(+)
diff --git a/README.md b/README.md
index 2761e7ddf02..d09dff0cd89 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,4 @@
+Adding ARIMA
[](https://github.com/bentoml/BentoML)
From dcffce9bad12f75caf16803d2dcebc04dc84f1b7 Mon Sep 17 00:00:00 2001
From: jsamanta_ucd
Date: Mon, 22 Jul 2024 17:31:24 +0100
Subject: [PATCH 02/15] Update Readme
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index d09dff0cd89..1496256861a 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-Adding ARIMA
+Adding ARIMA custom runner
[](https://github.com/bentoml/BentoML)
From 5a15c19e2ea2747eeb9a62b5aa8ecf9b062c5201 Mon Sep 17 00:00:00 2001
From: jsamanta_ucd
Date: Mon, 22 Jul 2024 18:05:34 +0100
Subject: [PATCH 03/15] Add custom arima model runner
---
examples/custom_runner/arima_model/README.md | 0
.../custom_runner/arima_model/bentofile.yaml | 0
.../arima_model/requirements.txt | 0
examples/custom_runner/arima_model/service.py | 48 ++++++++++++++++
examples/custom_runner/arima_model/train.py | 55 +++++++++++++++++++
5 files changed, 103 insertions(+)
create mode 100644 examples/custom_runner/arima_model/README.md
create mode 100644 examples/custom_runner/arima_model/bentofile.yaml
create mode 100644 examples/custom_runner/arima_model/requirements.txt
create mode 100644 examples/custom_runner/arima_model/service.py
create mode 100644 examples/custom_runner/arima_model/train.py
diff --git a/examples/custom_runner/arima_model/README.md b/examples/custom_runner/arima_model/README.md
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/examples/custom_runner/arima_model/bentofile.yaml b/examples/custom_runner/arima_model/bentofile.yaml
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/examples/custom_runner/arima_model/requirements.txt b/examples/custom_runner/arima_model/requirements.txt
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/examples/custom_runner/arima_model/service.py b/examples/custom_runner/arima_model/service.py
new file mode 100644
index 00000000000..d2e833d2827
--- /dev/null
+++ b/examples/custom_runner/arima_model/service.py
@@ -0,0 +1,48 @@
+import numpy as np
+import typing
+import bentoml
+from bentoml.io import JSON
+from bentoml.io import NumpyNdarray
+from statsmodels.tsa.arima.model import ARIMA
+
+arima_model_runner = bentoml.picklable_model.get("arima_forecast_model:latest")
+history_data = arima_model_runner.custom_objects["historical_data"]
+
+# Custom runner for ARIMA model
+class ARIMAForecastRunnable(bentoml.Runnable):
+ SUPPORTED_RESOURCES = ("cpu",)
+ SUPPORTS_CPU_MULTI_THREADING = False
+
+ def __init__(self):
+ self.arima = ARIMA
+
+ @bentoml.Runnable.method(batchable=False)
+ def forecast(self ,test_data):
+ predictions = []
+ history_data = arima_model_runner.custom_objects["historical_data"]
+
+ # Define the ARIMA tuning parameters
+ model = ARIMA(history_data, order=(5, 1, 0))
+ model_fit = model.fit()
+ output = model_fit.forecast()
+ y_pred = output[0]
+ predictions.append(y_pred)
+ obs = test_data # single test value
+ history_data.append(obs)
+ print("New history data: ",history_data)
+ # Save model with BentoML
+ bentoml.picklable_model.save_model('arima_forecast_model',
+ model,
+ custom_objects= {"historical_data": history_data},
+ signatures={"predict": {"batchable": True}},
+ )
+ return y_pred
+
+
+arima_forecast_custom_runner = bentoml.Runner(ARIMAForecastRunnable)
+
+svc = bentoml.Service("arima_model_forecast", runners=[arima_forecast_custom_runner])
+
+@svc.api(input=NumpyNdarray(dtype="float"), output=JSON())
+def predict(input_data: np.ndarray) -> typing.List[float]:
+ return arima_forecast_custom_runner.forecast.run(input_data)
\ No newline at end of file
diff --git a/examples/custom_runner/arima_model/train.py b/examples/custom_runner/arima_model/train.py
new file mode 100644
index 00000000000..ad50571dbd2
--- /dev/null
+++ b/examples/custom_runner/arima_model/train.py
@@ -0,0 +1,55 @@
+# import libraries
+import numpy as np
+import pandas as pd
+import bentoml
+import statsmodels.api as sm
+from sklearn.metrics import mean_squared_error
+from statsmodels.tsa.arima.model import ARIMA
+
+def main():
+ # Load the dataset
+ data = sm.datasets.sunspots.load_pandas().data
+ # Prepare dataset
+ data.index = pd.Index(sm.tsa.datetools.dates_from_range("1700", "2008"))
+ data.index.freq = data.index.inferred_freq
+ del data["YEAR"]
+
+ # Split into train and test sets
+ X = data.values
+ size = int(len(X) * 0.66)
+ train, test = X[0:size], X[size:len(X)]
+ # Create a list of records to train ARIMA
+ history = [x for x in train]
+ # Create a list to store the predicted values
+ predictions = list()
+
+ # Iterate over the test data
+ for t in range(len(test)):
+ model = ARIMA(history, order=(5, 1, 0))
+ # fit the model and create forecast
+ model_fit = model.fit()
+ output = model_fit.forecast()
+ yhat = output[0]
+ predictions.append(yhat)
+ obs = test[t]
+ history.append(obs)
+
+ y_test = test
+ y_pred = predictions
+
+ # Calculate root mean squared error
+ rmse = np.sqrt(mean_squared_error(y_test, y_pred))
+ print("Root Mean Squared Error:", rmse)
+
+ # Save model with BentoML
+ saved_model = bentoml.picklable_model.save_model(
+ "arima_forecast_model",
+ model,
+ custom_objects= {"historical_data": history,},
+ signatures={"predict": {"batchable": True}},
+ )
+
+ print(f"Model saved: {saved_model}")
+
+if __name__ == "__main__":
+ main()
\ No newline at end of file
From 326c0b2452fe476914bcb319e4fb768ebf828216 Mon Sep 17 00:00:00 2001
From: jsamanta_ucd
Date: Tue, 23 Jul 2024 11:06:24 +0100
Subject: [PATCH 04/15] Update service.py
---
examples/custom_runner/arima_model/service.py | 20 +++++++++----------
1 file changed, 9 insertions(+), 11 deletions(-)
diff --git a/examples/custom_runner/arima_model/service.py b/examples/custom_runner/arima_model/service.py
index d2e833d2827..2b275d1e519 100644
--- a/examples/custom_runner/arima_model/service.py
+++ b/examples/custom_runner/arima_model/service.py
@@ -5,21 +5,19 @@
from bentoml.io import NumpyNdarray
from statsmodels.tsa.arima.model import ARIMA
-arima_model_runner = bentoml.picklable_model.get("arima_forecast_model:latest")
-history_data = arima_model_runner.custom_objects["historical_data"]
-
# Custom runner for ARIMA model
class ARIMAForecastRunnable(bentoml.Runnable):
SUPPORTED_RESOURCES = ("cpu",)
SUPPORTS_CPU_MULTI_THREADING = False
def __init__(self):
- self.arima = ARIMA
+ super().__init__()
@bentoml.Runnable.method(batchable=False)
- def forecast(self ,test_data):
+ def forecast(self, test_data):
predictions = []
- history_data = arima_model_runner.custom_objects["historical_data"]
+ arima_model = bentoml.picklable_model.get("arima_forecast_model:latest")
+ history_data = arima_model.custom_objects["historical_data"]
# Define the ARIMA tuning parameters
model = ARIMA(history_data, order=(5, 1, 0))
@@ -27,9 +25,9 @@ def forecast(self ,test_data):
output = model_fit.forecast()
y_pred = output[0]
predictions.append(y_pred)
- obs = test_data # single test value
+ obs = test_data
+ # Update history with single test value
history_data.append(obs)
- print("New history data: ",history_data)
# Save model with BentoML
bentoml.picklable_model.save_model('arima_forecast_model',
model,
@@ -39,10 +37,10 @@ def forecast(self ,test_data):
return y_pred
-arima_forecast_custom_runner = bentoml.Runner(ARIMAForecastRunnable)
+arima_model = bentoml.Runner(ARIMAForecastRunnable)
-svc = bentoml.Service("arima_model_forecast", runners=[arima_forecast_custom_runner])
+svc = bentoml.Service("arima_model_forecast", runners=[arima_model])
@svc.api(input=NumpyNdarray(dtype="float"), output=JSON())
def predict(input_data: np.ndarray) -> typing.List[float]:
- return arima_forecast_custom_runner.forecast.run(input_data)
\ No newline at end of file
+ return arima_model.forecast.run(input_data)
\ No newline at end of file
From 0899b4c1c956d67a1e08493c9f1a378307fb07ec Mon Sep 17 00:00:00 2001
From: jsamanta_ucd
Date: Wed, 24 Jul 2024 12:13:58 +0100
Subject: [PATCH 05/15] Update arima config files
---
examples/custom_runner/arima_model/README.md | 53 +++++++++++++++++++
.../custom_runner/arima_model/bentofile.yaml | 5 ++
.../arima_model/requirements.txt | 2 +
examples/custom_runner/arima_model/service.py | 6 +--
examples/custom_runner/arima_model/train.py | 1 +
5 files changed, 64 insertions(+), 3 deletions(-)
diff --git a/examples/custom_runner/arima_model/README.md b/examples/custom_runner/arima_model/README.md
index e69de29bb2d..1409df02ab5 100644
--- a/examples/custom_runner/arima_model/README.md
+++ b/examples/custom_runner/arima_model/README.md
@@ -0,0 +1,53 @@
+# Serving ARIMA model with BentoML
+
+This project demonstrates how to use a continuous learning ARIMA model
+for a time-series data and use it as a prediction service in BentoML.
+
+## Requirements
+
+Install requirements with:
+
+```bash
+pip install -r ./requirements.txt
+```
+
+## Instruction
+
+1. Train and save model:
+
+```bash
+python ./train.py
+```
+
+2. Run the service:
+
+```bash
+bentoml serve service.py:svc
+```
+
+## Test the endpoint
+
+Open in browser http://0.0.0.0:3000 to submit input images via the web UI.
+
+```bash
+curl -X 'POST' 'http://0.0.0.0:3000/predict' -H 'accept: application/json' -H 'Content-Type: application/json' -d '[20]'
+```
+
+Sample result:
+```
+14.584079431935686
+```
+
+## Build Bento
+
+Build Bento using the bentofile.yaml which contains all the configurations required:
+
+```bash
+bentoml build -f ./bentofile.yaml
+```
+
+Once the Bento is built, containerize it as a Docker image for deployment:
+
+```bash
+bentoml containerize arima_forecast_model:latest
+```
diff --git a/examples/custom_runner/arima_model/bentofile.yaml b/examples/custom_runner/arima_model/bentofile.yaml
index e69de29bb2d..ae0093afdb0 100644
--- a/examples/custom_runner/arima_model/bentofile.yaml
+++ b/examples/custom_runner/arima_model/bentofile.yaml
@@ -0,0 +1,5 @@
+service: "service.py:svc"
+include:
+- "service.py"
+python:
+ requirements_txt: ./requirements.txt
diff --git a/examples/custom_runner/arima_model/requirements.txt b/examples/custom_runner/arima_model/requirements.txt
index e69de29bb2d..9ec1666c910 100644
--- a/examples/custom_runner/arima_model/requirements.txt
+++ b/examples/custom_runner/arima_model/requirements.txt
@@ -0,0 +1,2 @@
+bentoml>=1.0.0
+statsmodels
\ No newline at end of file
diff --git a/examples/custom_runner/arima_model/service.py b/examples/custom_runner/arima_model/service.py
index 2b275d1e519..dd75429adf2 100644
--- a/examples/custom_runner/arima_model/service.py
+++ b/examples/custom_runner/arima_model/service.py
@@ -37,10 +37,10 @@ def forecast(self, test_data):
return y_pred
-arima_model = bentoml.Runner(ARIMAForecastRunnable)
+arima_forecast_runner = bentoml.Runner(ARIMAForecastRunnable)
-svc = bentoml.Service("arima_model_forecast", runners=[arima_model])
+svc = bentoml.Service("arima_model_forecast", runners=[arima_forecast_runner])
@svc.api(input=NumpyNdarray(dtype="float"), output=JSON())
def predict(input_data: np.ndarray) -> typing.List[float]:
- return arima_model.forecast.run(input_data)
\ No newline at end of file
+ return arima_forecast_runner.forecast.run(input_data)
\ No newline at end of file
diff --git a/examples/custom_runner/arima_model/train.py b/examples/custom_runner/arima_model/train.py
index ad50571dbd2..168d82f87fb 100644
--- a/examples/custom_runner/arima_model/train.py
+++ b/examples/custom_runner/arima_model/train.py
@@ -32,6 +32,7 @@ def main():
yhat = output[0]
predictions.append(yhat)
obs = test[t]
+ # update history with test data
history.append(obs)
y_test = test
From e545931e709a679aab6cb9e868216fe0dff93b3c Mon Sep 17 00:00:00 2001
From: jsamanta_ucd
Date: Wed, 24 Jul 2024 12:58:47 +0100
Subject: [PATCH 06/15] Update train and service files
---
examples/custom_runner/arima_model/service.py | 2 ++
examples/custom_runner/arima_model/train.py | 2 +-
2 files changed, 3 insertions(+), 1 deletion(-)
diff --git a/examples/custom_runner/arima_model/service.py b/examples/custom_runner/arima_model/service.py
index dd75429adf2..67c3ee452b0 100644
--- a/examples/custom_runner/arima_model/service.py
+++ b/examples/custom_runner/arima_model/service.py
@@ -5,6 +5,8 @@
from bentoml.io import NumpyNdarray
from statsmodels.tsa.arima.model import ARIMA
+arima_model = bentoml.picklable_model.get("arima_forecast_model:latest")
+
# Custom runner for ARIMA model
class ARIMAForecastRunnable(bentoml.Runnable):
SUPPORTED_RESOURCES = ("cpu",)
diff --git a/examples/custom_runner/arima_model/train.py b/examples/custom_runner/arima_model/train.py
index 168d82f87fb..bebd5ee1305 100644
--- a/examples/custom_runner/arima_model/train.py
+++ b/examples/custom_runner/arima_model/train.py
@@ -47,7 +47,7 @@ def main():
"arima_forecast_model",
model,
custom_objects= {"historical_data": history,},
- signatures={"predict": {"batchable": True}},
+ signatures={"predict": {"batchable": True}},
)
print(f"Model saved: {saved_model}")
From 606aea5cdd2b5905a92f8168a416a517af7f1ce8 Mon Sep 17 00:00:00 2001
From: jsamanta_ucd
Date: Wed, 24 Jul 2024 13:32:18 +0100
Subject: [PATCH 07/15] update service.py
---
examples/custom_runner/arima_model/service.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/examples/custom_runner/arima_model/service.py b/examples/custom_runner/arima_model/service.py
index 67c3ee452b0..c6061fdf54b 100644
--- a/examples/custom_runner/arima_model/service.py
+++ b/examples/custom_runner/arima_model/service.py
@@ -41,7 +41,7 @@ def forecast(self, test_data):
arima_forecast_runner = bentoml.Runner(ARIMAForecastRunnable)
-svc = bentoml.Service("arima_model_forecast", runners=[arima_forecast_runner])
+svc = bentoml.Service("arima_model_forecast", runners=[arima_forecast_runner], models=[arima_model])
@svc.api(input=NumpyNdarray(dtype="float"), output=JSON())
def predict(input_data: np.ndarray) -> typing.List[float]:
From e7bec351db69e3f2d1f4c1ffa4b711f49f05f9d9 Mon Sep 17 00:00:00 2001
From: jsamanta_ucd
Date: Thu, 25 Jul 2024 12:25:41 +0100
Subject: [PATCH 08/15] update service
---
examples/custom_runner/arima_model/service.py | 39 ++++++-------------
examples/custom_runner/arima_model/train.py | 1 -
2 files changed, 12 insertions(+), 28 deletions(-)
diff --git a/examples/custom_runner/arima_model/service.py b/examples/custom_runner/arima_model/service.py
index c6061fdf54b..11b7b428bf7 100644
--- a/examples/custom_runner/arima_model/service.py
+++ b/examples/custom_runner/arima_model/service.py
@@ -3,9 +3,6 @@
import bentoml
from bentoml.io import JSON
from bentoml.io import NumpyNdarray
-from statsmodels.tsa.arima.model import ARIMA
-
-arima_model = bentoml.picklable_model.get("arima_forecast_model:latest")
# Custom runner for ARIMA model
class ARIMAForecastRunnable(bentoml.Runnable):
@@ -14,35 +11,23 @@ class ARIMAForecastRunnable(bentoml.Runnable):
def __init__(self):
super().__init__()
+ self.model = bentoml.picklable_model.get("arima_forecast_model:latest")
@bentoml.Runnable.method(batchable=False)
- def forecast(self, test_data):
- predictions = []
- arima_model = bentoml.picklable_model.get("arima_forecast_model:latest")
- history_data = arima_model.custom_objects["historical_data"]
-
- # Define the ARIMA tuning parameters
- model = ARIMA(history_data, order=(5, 1, 0))
- model_fit = model.fit()
- output = model_fit.forecast()
- y_pred = output[0]
- predictions.append(y_pred)
- obs = test_data
- # Update history with single test value
- history_data.append(obs)
- # Save model with BentoML
- bentoml.picklable_model.save_model('arima_forecast_model',
- model,
- custom_objects= {"historical_data": history_data},
- signatures={"predict": {"batchable": True}},
- )
+ def forecast(self, values):
+ # Load trained arima model from bentoml
+ arima_model = self.model.load_model()
+ model_fit = arima_model.fit()
+ y_pred = model_fit.forecast(int(values))
return y_pred
arima_forecast_runner = bentoml.Runner(ARIMAForecastRunnable)
-svc = bentoml.Service("arima_model_forecast", runners=[arima_forecast_runner], models=[arima_model])
+svc = bentoml.Service("arima_model_forecast", runners=[arima_forecast_runner])
+
+@svc.api(input=NumpyNdarray(dtype="int"), output=JSON())
+def predict_forecast(input_data: np.ndarray):
+ print(input_data)
+ return arima_forecast_runner.forecast.run(input_data[0])
-@svc.api(input=NumpyNdarray(dtype="float"), output=JSON())
-def predict(input_data: np.ndarray) -> typing.List[float]:
- return arima_forecast_runner.forecast.run(input_data)
\ No newline at end of file
diff --git a/examples/custom_runner/arima_model/train.py b/examples/custom_runner/arima_model/train.py
index bebd5ee1305..6e6511959e8 100644
--- a/examples/custom_runner/arima_model/train.py
+++ b/examples/custom_runner/arima_model/train.py
@@ -46,7 +46,6 @@ def main():
saved_model = bentoml.picklable_model.save_model(
"arima_forecast_model",
model,
- custom_objects= {"historical_data": history,},
signatures={"predict": {"batchable": True}},
)
From 44db8aea95deae0715ac5443fd20858bbf308bf4 Mon Sep 17 00:00:00 2001
From: jsamanta_ucd
Date: Thu, 25 Jul 2024 15:54:36 +0100
Subject: [PATCH 09/15] update service.py
---
examples/custom_runner/arima_model/service.py | 13 +++++++------
1 file changed, 7 insertions(+), 6 deletions(-)
diff --git a/examples/custom_runner/arima_model/service.py b/examples/custom_runner/arima_model/service.py
index 11b7b428bf7..7970748e05f 100644
--- a/examples/custom_runner/arima_model/service.py
+++ b/examples/custom_runner/arima_model/service.py
@@ -4,6 +4,8 @@
from bentoml.io import JSON
from bentoml.io import NumpyNdarray
+arima_model = bentoml.picklable_model.get("arima_forecast_model:latest")
+
# Custom runner for ARIMA model
class ARIMAForecastRunnable(bentoml.Runnable):
SUPPORTED_RESOURCES = ("cpu",)
@@ -11,23 +13,22 @@ class ARIMAForecastRunnable(bentoml.Runnable):
def __init__(self):
super().__init__()
- self.model = bentoml.picklable_model.get("arima_forecast_model:latest")
+ self.model = arima_model
@bentoml.Runnable.method(batchable=False)
- def forecast(self, values):
+ def forecast(self, values_to_forecast):
# Load trained arima model from bentoml
arima_model = self.model.load_model()
model_fit = arima_model.fit()
- y_pred = model_fit.forecast(int(values))
- return y_pred
+ predictions = model_fit.forecast(int(values_to_forecast))
+ return predictions
arima_forecast_runner = bentoml.Runner(ARIMAForecastRunnable)
-svc = bentoml.Service("arima_model_forecast", runners=[arima_forecast_runner])
+svc = bentoml.Service("arima_model_forecast", runners=[arima_forecast_runner], models=[arima_model])
@svc.api(input=NumpyNdarray(dtype="int"), output=JSON())
def predict_forecast(input_data: np.ndarray):
- print(input_data)
return arima_forecast_runner.forecast.run(input_data[0])
From 9ce3fbee755925aaee14a956e4b5dd1625ce3ceb Mon Sep 17 00:00:00 2001
From: jsamanta_ucd
Date: Fri, 26 Jul 2024 15:35:01 +0100
Subject: [PATCH 10/15] update README
---
examples/custom_runner/arima_model/README.md | 17 ++++++++++++-----
examples/custom_runner/arima_model/service.py | 1 -
2 files changed, 12 insertions(+), 6 deletions(-)
diff --git a/examples/custom_runner/arima_model/README.md b/examples/custom_runner/arima_model/README.md
index 1409df02ab5..8ae7985e2e1 100644
--- a/examples/custom_runner/arima_model/README.md
+++ b/examples/custom_runner/arima_model/README.md
@@ -1,7 +1,7 @@
# Serving ARIMA model with BentoML
-This project demonstrates how to use a continuous learning ARIMA model
-for a time-series data and use it as a prediction service in BentoML.
+This project shows how to apply a continuous learning ARIMA model
+for time-series data in BentoML to forecasts future values.
## Requirements
@@ -27,15 +27,22 @@ bentoml serve service.py:svc
## Test the endpoint
-Open in browser http://0.0.0.0:3000 to submit input images via the web UI.
+Open in browser http://0.0.0.0:3000 to predict forecast.
```bash
-curl -X 'POST' 'http://0.0.0.0:3000/predict' -H 'accept: application/json' -H 'Content-Type: application/json' -d '[20]'
+curl -X 'POST' 'http://0.0.0.0:3000/predict' -H 'accept: application/json' -H 'Content-Type: application/json' -d '[5]'
```
Sample result:
```
-14.584079431935686
+[
+ 21.32297249948254,
+ 39.103166807895505,
+ 51.62030696797619,
+ 57.742863144656305,
+ 57.316390331155915
+]
+
```
## Build Bento
diff --git a/examples/custom_runner/arima_model/service.py b/examples/custom_runner/arima_model/service.py
index 7970748e05f..9eab3f71997 100644
--- a/examples/custom_runner/arima_model/service.py
+++ b/examples/custom_runner/arima_model/service.py
@@ -23,7 +23,6 @@ def forecast(self, values_to_forecast):
predictions = model_fit.forecast(int(values_to_forecast))
return predictions
-
arima_forecast_runner = bentoml.Runner(ARIMAForecastRunnable)
svc = bentoml.Service("arima_model_forecast", runners=[arima_forecast_runner], models=[arima_model])
From 0c8c530886bfc97a4c3f4c5e90240f5d8430e617 Mon Sep 17 00:00:00 2001
From: jsamanta_ucd
Date: Tue, 30 Jul 2024 11:41:26 +0100
Subject: [PATCH 11/15] Update readme
---
examples/custom_runner/arima_model/README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/examples/custom_runner/arima_model/README.md b/examples/custom_runner/arima_model/README.md
index 8ae7985e2e1..d38fb11ca4c 100644
--- a/examples/custom_runner/arima_model/README.md
+++ b/examples/custom_runner/arima_model/README.md
@@ -27,7 +27,7 @@ bentoml serve service.py:svc
## Test the endpoint
-Open in browser http://0.0.0.0:3000 to predict forecast.
+Open in browser http://0.0.0.0:3000 to predict forecast of 5 future values.
```bash
curl -X 'POST' 'http://0.0.0.0:3000/predict' -H 'accept: application/json' -H 'Content-Type: application/json' -d '[5]'
From 027d2649402156727c64572457e36acef903cbd8 Mon Sep 17 00:00:00 2001
From: jsamanta_ucd
Date: Tue, 30 Jul 2024 11:48:13 +0100
Subject: [PATCH 12/15] Update readme
---
README.md | 1 -
1 file changed, 1 deletion(-)
diff --git a/README.md b/README.md
index 1496256861a..2761e7ddf02 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,3 @@
-Adding ARIMA custom runner
[](https://github.com/bentoml/BentoML)
From 7f6eb2ff167021eae47b7b3462693175c5985b81 Mon Sep 17 00:00:00 2001
From: "pre-commit-ci[bot]"
<66853113+pre-commit-ci[bot]@users.noreply.github.com>
Date: Wed, 31 Jul 2024 10:55:30 +0000
Subject: [PATCH 13/15] ci: auto fixes from pre-commit.ci
For more information, see https://pre-commit.ci
---
examples/custom_runner/arima_model/README.md | 2 +-
.../arima_model/requirements.txt | 2 +-
examples/custom_runner/arima_model/service.py | 12 ++-
examples/custom_runner/arima_model/train.py | 93 ++++++++++---------
4 files changed, 58 insertions(+), 51 deletions(-)
diff --git a/examples/custom_runner/arima_model/README.md b/examples/custom_runner/arima_model/README.md
index d38fb11ca4c..e2803aa83ea 100644
--- a/examples/custom_runner/arima_model/README.md
+++ b/examples/custom_runner/arima_model/README.md
@@ -1,4 +1,4 @@
-# Serving ARIMA model with BentoML
+# Serving ARIMA model with BentoML
This project shows how to apply a continuous learning ARIMA model
for time-series data in BentoML to forecasts future values.
diff --git a/examples/custom_runner/arima_model/requirements.txt b/examples/custom_runner/arima_model/requirements.txt
index 9ec1666c910..fa64b8fe1f8 100644
--- a/examples/custom_runner/arima_model/requirements.txt
+++ b/examples/custom_runner/arima_model/requirements.txt
@@ -1,2 +1,2 @@
bentoml>=1.0.0
-statsmodels
\ No newline at end of file
+statsmodels
diff --git a/examples/custom_runner/arima_model/service.py b/examples/custom_runner/arima_model/service.py
index 9eab3f71997..ff1e76e5992 100644
--- a/examples/custom_runner/arima_model/service.py
+++ b/examples/custom_runner/arima_model/service.py
@@ -1,11 +1,12 @@
import numpy as np
-import typing
+
import bentoml
from bentoml.io import JSON
from bentoml.io import NumpyNdarray
arima_model = bentoml.picklable_model.get("arima_forecast_model:latest")
+
# Custom runner for ARIMA model
class ARIMAForecastRunnable(bentoml.Runnable):
SUPPORTED_RESOURCES = ("cpu",)
@@ -14,7 +15,7 @@ class ARIMAForecastRunnable(bentoml.Runnable):
def __init__(self):
super().__init__()
self.model = arima_model
-
+
@bentoml.Runnable.method(batchable=False)
def forecast(self, values_to_forecast):
# Load trained arima model from bentoml
@@ -23,11 +24,14 @@ def forecast(self, values_to_forecast):
predictions = model_fit.forecast(int(values_to_forecast))
return predictions
+
arima_forecast_runner = bentoml.Runner(ARIMAForecastRunnable)
-svc = bentoml.Service("arima_model_forecast", runners=[arima_forecast_runner], models=[arima_model])
+svc = bentoml.Service(
+ "arima_model_forecast", runners=[arima_forecast_runner], models=[arima_model]
+)
+
@svc.api(input=NumpyNdarray(dtype="int"), output=JSON())
def predict_forecast(input_data: np.ndarray):
return arima_forecast_runner.forecast.run(input_data[0])
-
diff --git a/examples/custom_runner/arima_model/train.py b/examples/custom_runner/arima_model/train.py
index 6e6511959e8..5a70d5d7df8 100644
--- a/examples/custom_runner/arima_model/train.py
+++ b/examples/custom_runner/arima_model/train.py
@@ -1,55 +1,58 @@
# import libraries
import numpy as np
import pandas as pd
-import bentoml
import statsmodels.api as sm
from sklearn.metrics import mean_squared_error
from statsmodels.tsa.arima.model import ARIMA
+import bentoml
+
+
def main():
- # Load the dataset
- data = sm.datasets.sunspots.load_pandas().data
- # Prepare dataset
- data.index = pd.Index(sm.tsa.datetools.dates_from_range("1700", "2008"))
- data.index.freq = data.index.inferred_freq
- del data["YEAR"]
-
- # Split into train and test sets
- X = data.values
- size = int(len(X) * 0.66)
- train, test = X[0:size], X[size:len(X)]
- # Create a list of records to train ARIMA
- history = [x for x in train]
- # Create a list to store the predicted values
- predictions = list()
-
- # Iterate over the test data
- for t in range(len(test)):
- model = ARIMA(history, order=(5, 1, 0))
- # fit the model and create forecast
- model_fit = model.fit()
- output = model_fit.forecast()
- yhat = output[0]
- predictions.append(yhat)
- obs = test[t]
- # update history with test data
- history.append(obs)
-
- y_test = test
- y_pred = predictions
-
- # Calculate root mean squared error
- rmse = np.sqrt(mean_squared_error(y_test, y_pred))
- print("Root Mean Squared Error:", rmse)
-
- # Save model with BentoML
- saved_model = bentoml.picklable_model.save_model(
- "arima_forecast_model",
- model,
- signatures={"predict": {"batchable": True}},
- )
-
- print(f"Model saved: {saved_model}")
+ # Load the dataset
+ data = sm.datasets.sunspots.load_pandas().data
+ # Prepare dataset
+ data.index = pd.Index(sm.tsa.datetools.dates_from_range("1700", "2008"))
+ data.index.freq = data.index.inferred_freq
+ del data["YEAR"]
+
+ # Split into train and test sets
+ X = data.values
+ size = int(len(X) * 0.66)
+ train, test = X[0:size], X[size : len(X)]
+ # Create a list of records to train ARIMA
+ history = [x for x in train]
+ # Create a list to store the predicted values
+ predictions = list()
+
+ # Iterate over the test data
+ for t in range(len(test)):
+ model = ARIMA(history, order=(5, 1, 0))
+ # fit the model and create forecast
+ model_fit = model.fit()
+ output = model_fit.forecast()
+ yhat = output[0]
+ predictions.append(yhat)
+ obs = test[t]
+ # update history with test data
+ history.append(obs)
+
+ y_test = test
+ y_pred = predictions
+
+ # Calculate root mean squared error
+ rmse = np.sqrt(mean_squared_error(y_test, y_pred))
+ print("Root Mean Squared Error:", rmse)
+
+ # Save model with BentoML
+ saved_model = bentoml.picklable_model.save_model(
+ "arima_forecast_model",
+ model,
+ signatures={"predict": {"batchable": True}},
+ )
+
+ print(f"Model saved: {saved_model}")
+
if __name__ == "__main__":
- main()
\ No newline at end of file
+ main()
From f3b2703a6400b9d0433f099337b024e2d985f760 Mon Sep 17 00:00:00 2001
From: jsamanta_ucd
Date: Wed, 18 Sep 2024 11:44:44 +0100
Subject: [PATCH 14/15] Update arima model with Bentoml version>=1.2
---
.../arima_model => arima-model}/README.md | 4 +-
.../bentofile.yaml | 2 +-
examples/arima-model/requirements.txt | 3 ++
examples/arima-model/service.py | 31 ++++++++++++++++
.../arima_model => arima-model}/train.py | 1 -
.../arima_model/requirements.txt | 2 -
examples/custom_runner/arima_model/service.py | 37 -------------------
7 files changed, 37 insertions(+), 43 deletions(-)
rename examples/{custom_runner/arima_model => arima-model}/README.md (91%)
rename examples/{custom_runner/arima_model => arima-model}/bentofile.yaml (66%)
create mode 100644 examples/arima-model/requirements.txt
create mode 100644 examples/arima-model/service.py
rename examples/{custom_runner/arima_model => arima-model}/train.py (99%)
delete mode 100644 examples/custom_runner/arima_model/requirements.txt
delete mode 100644 examples/custom_runner/arima_model/service.py
diff --git a/examples/custom_runner/arima_model/README.md b/examples/arima-model/README.md
similarity index 91%
rename from examples/custom_runner/arima_model/README.md
rename to examples/arima-model/README.md
index e2803aa83ea..396aa559b5f 100644
--- a/examples/custom_runner/arima_model/README.md
+++ b/examples/arima-model/README.md
@@ -22,7 +22,7 @@ python ./train.py
2. Run the service:
```bash
-bentoml serve service.py:svc
+bentoml serve
```
## Test the endpoint
@@ -30,7 +30,7 @@ bentoml serve service.py:svc
Open in browser http://0.0.0.0:3000 to predict forecast of 5 future values.
```bash
-curl -X 'POST' 'http://0.0.0.0:3000/predict' -H 'accept: application/json' -H 'Content-Type: application/json' -d '[5]'
+curl -X 'POST' 'http://0.0.0.0:3000/predict' -H 'accept: application/json' -H 'Content-Type: application/json' -d '{"data": [5]}'
```
Sample result:
diff --git a/examples/custom_runner/arima_model/bentofile.yaml b/examples/arima-model/bentofile.yaml
similarity index 66%
rename from examples/custom_runner/arima_model/bentofile.yaml
rename to examples/arima-model/bentofile.yaml
index ae0093afdb0..f94c4399eec 100644
--- a/examples/custom_runner/arima_model/bentofile.yaml
+++ b/examples/arima-model/bentofile.yaml
@@ -1,4 +1,4 @@
-service: "service.py:svc"
+service: "service.py:ArimaForecast"
include:
- "service.py"
python:
diff --git a/examples/arima-model/requirements.txt b/examples/arima-model/requirements.txt
new file mode 100644
index 00000000000..e6899d2190c
--- /dev/null
+++ b/examples/arima-model/requirements.txt
@@ -0,0 +1,3 @@
+bentoml>=1.2.0
+statsmodels
+scikit-learn
diff --git a/examples/arima-model/service.py b/examples/arima-model/service.py
new file mode 100644
index 00000000000..b2515715e8f
--- /dev/null
+++ b/examples/arima-model/service.py
@@ -0,0 +1,31 @@
+import numpy as np
+import bentoml
+
+@bentoml.service(
+ resources={
+ "cpu": "2",
+ "memory": "2Gi",
+ },
+)
+class ArimaForecast:
+ """
+ A simple ARIMA model to forecast future predictions
+ """
+
+ # Load in the class scope to declare the model as a dependency of the service
+ arima_model = bentoml.picklable_model.get("arima_forecast_model:latest")
+
+ def __init__(self):
+ """
+ Initialize the service by loading the model from the model store
+ """
+ self.model = self.arima_model.load_model()
+
+ @bentoml.api
+ def forecast(self, data: np.ndarray)-> np.ndarray:
+ """
+ Define API with input as number of forecasts to predict in the future
+ """
+ model_fit = self.model.fit()
+ predictions = model_fit.forecast(int(data))
+ return predictions
\ No newline at end of file
diff --git a/examples/custom_runner/arima_model/train.py b/examples/arima-model/train.py
similarity index 99%
rename from examples/custom_runner/arima_model/train.py
rename to examples/arima-model/train.py
index 5a70d5d7df8..a28128bb472 100644
--- a/examples/custom_runner/arima_model/train.py
+++ b/examples/arima-model/train.py
@@ -4,7 +4,6 @@
import statsmodels.api as sm
from sklearn.metrics import mean_squared_error
from statsmodels.tsa.arima.model import ARIMA
-
import bentoml
diff --git a/examples/custom_runner/arima_model/requirements.txt b/examples/custom_runner/arima_model/requirements.txt
deleted file mode 100644
index fa64b8fe1f8..00000000000
--- a/examples/custom_runner/arima_model/requirements.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-bentoml>=1.0.0
-statsmodels
diff --git a/examples/custom_runner/arima_model/service.py b/examples/custom_runner/arima_model/service.py
deleted file mode 100644
index ff1e76e5992..00000000000
--- a/examples/custom_runner/arima_model/service.py
+++ /dev/null
@@ -1,37 +0,0 @@
-import numpy as np
-
-import bentoml
-from bentoml.io import JSON
-from bentoml.io import NumpyNdarray
-
-arima_model = bentoml.picklable_model.get("arima_forecast_model:latest")
-
-
-# Custom runner for ARIMA model
-class ARIMAForecastRunnable(bentoml.Runnable):
- SUPPORTED_RESOURCES = ("cpu",)
- SUPPORTS_CPU_MULTI_THREADING = False
-
- def __init__(self):
- super().__init__()
- self.model = arima_model
-
- @bentoml.Runnable.method(batchable=False)
- def forecast(self, values_to_forecast):
- # Load trained arima model from bentoml
- arima_model = self.model.load_model()
- model_fit = arima_model.fit()
- predictions = model_fit.forecast(int(values_to_forecast))
- return predictions
-
-
-arima_forecast_runner = bentoml.Runner(ARIMAForecastRunnable)
-
-svc = bentoml.Service(
- "arima_model_forecast", runners=[arima_forecast_runner], models=[arima_model]
-)
-
-
-@svc.api(input=NumpyNdarray(dtype="int"), output=JSON())
-def predict_forecast(input_data: np.ndarray):
- return arima_forecast_runner.forecast.run(input_data[0])
From dc19767141c186ff45925cb30eb95c6e8ca892e3 Mon Sep 17 00:00:00 2001
From: "pre-commit-ci[bot]"
<66853113+pre-commit-ci[bot]@users.noreply.github.com>
Date: Wed, 18 Sep 2024 10:50:21 +0000
Subject: [PATCH 15/15] ci: auto fixes from pre-commit.ci
For more information, see https://pre-commit.ci
---
examples/arima-model/service.py | 6 ++++--
examples/arima-model/train.py | 1 +
2 files changed, 5 insertions(+), 2 deletions(-)
diff --git a/examples/arima-model/service.py b/examples/arima-model/service.py
index b2515715e8f..877f9e65157 100644
--- a/examples/arima-model/service.py
+++ b/examples/arima-model/service.py
@@ -1,6 +1,8 @@
import numpy as np
+
import bentoml
+
@bentoml.service(
resources={
"cpu": "2",
@@ -22,10 +24,10 @@ def __init__(self):
self.model = self.arima_model.load_model()
@bentoml.api
- def forecast(self, data: np.ndarray)-> np.ndarray:
+ def forecast(self, data: np.ndarray) -> np.ndarray:
"""
Define API with input as number of forecasts to predict in the future
"""
model_fit = self.model.fit()
predictions = model_fit.forecast(int(data))
- return predictions
\ No newline at end of file
+ return predictions
diff --git a/examples/arima-model/train.py b/examples/arima-model/train.py
index a28128bb472..5a70d5d7df8 100644
--- a/examples/arima-model/train.py
+++ b/examples/arima-model/train.py
@@ -4,6 +4,7 @@
import statsmodels.api as sm
from sklearn.metrics import mean_squared_error
from statsmodels.tsa.arima.model import ARIMA
+
import bentoml