From 173db8da9fe39946a53b31780b737907145fd22b Mon Sep 17 00:00:00 2001 From: Theofilos Papapanagiotou Date: Tue, 22 Mar 2022 13:11:14 +0100 Subject: [PATCH 01/10] add catboost support --- README.md | 2 + docs/examples/catboost/README.ipynb | 190 +++++++++++++++++ docs/examples/catboost/README.md | 104 +++++++++ docs/examples/catboost/model-settings.json | 8 + docs/examples/catboost/model.cbm | Bin 0 -> 6640 bytes docs/examples/catboost/settings.json | 3 + docs/examples/index.md | 1 + docs/runtimes/catboost.md | 3 + runtimes/catboost/LICENSE | 201 ++++++++++++++++++ runtimes/catboost/README.md | 23 ++ .../catboost/mlserver_catboost/__init__.py | 3 + .../catboost/mlserver_catboost/catboost.py | 35 +++ .../catboost/mlserver_catboost/version.py | 1 + runtimes/catboost/setup.py | 44 ++++ runtimes/catboost/tests/conftest.py | 53 +++++ runtimes/catboost/tests/test_catboost.py | 48 +++++ .../tests/testdata/inference-request.json | 11 + setup.cfg | 125 +++++++++++ 18 files changed, 855 insertions(+) create mode 100644 docs/examples/catboost/README.ipynb create mode 100644 docs/examples/catboost/README.md create mode 100644 docs/examples/catboost/model-settings.json create mode 100644 docs/examples/catboost/model.cbm create mode 100644 docs/examples/catboost/settings.json create mode 100644 docs/runtimes/catboost.md create mode 100644 runtimes/catboost/LICENSE create mode 100644 runtimes/catboost/README.md create mode 100644 runtimes/catboost/mlserver_catboost/__init__.py create mode 100644 runtimes/catboost/mlserver_catboost/catboost.py create mode 100644 runtimes/catboost/mlserver_catboost/version.py create mode 100644 runtimes/catboost/setup.py create mode 100644 runtimes/catboost/tests/conftest.py create mode 100644 runtimes/catboost/tests/test_catboost.py create mode 100644 runtimes/catboost/tests/testdata/inference-request.json diff --git a/README.md b/README.md index 53c328f2a..b5afb3f18 100644 --- a/README.md +++ b/README.md @@ -75,6 +75,7 @@ Out of the box, MLServer provides support for: | XGBoost | ✅ | [MLServer XGBoost](./runtimes/xgboost) | | Spark MLlib | ✅ | [MLServer MLlib](./runtimes/mllib) | | LightGBM | ✅ | [MLServer LightGBM](./runtimes/lightgbm) | +| CatBoost | ✅ | [MLServer CatBoost](./runtimes/catboost) | | Tempo | ✅ | [`github.com/SeldonIO/tempo`](https://github.com/SeldonIO/tempo) | | MLflow | ✅ | [MLServer MLflow](./runtimes/mlflow) | | Alibi-Detect | ✅ | [MLServer Alibi Detect](./runtimes/alibi-detect) | @@ -91,6 +92,7 @@ MLServer to start serving your machine learning models. - [Serving a `scikit-learn` model](./docs/examples/sklearn/README.md) - [Serving a `xgboost` model](./docs/examples/xgboost/README.md) - [Serving a `lightgbm` model](./docs/examples/lightgbm/README.md) +- [Serving a `catboost` model](./docs/examples/catboost/README.md) - [Serving a `tempo` pipeline](./docs/examples/tempo/README.md) - [Serving a custom model](./docs/examples/custom/README.md) - [Serving an `alibi-detect` model](./docs/examples/alibi-detect/README.md) diff --git a/docs/examples/catboost/README.ipynb b/docs/examples/catboost/README.ipynb new file mode 100644 index 000000000..cd731f67f --- /dev/null +++ b/docs/examples/catboost/README.ipynb @@ -0,0 +1,190 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Serving CatBoost models\n", + "\n", + "Out of the box, `mlserver` supports the deployment and serving of `catboost` models.\n", + "By default, it will assume that these models have been [serialised using the `save_model()` method](https://catboost.ai/en/docs/concepts/python-reference_catboost_save_model).\n", + "\n", + "In this example, we will cover how we can train and serialise a simple model, to then serve it using `mlserver`." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Training\n", + "\n", + "To test the CatBoost Server, first we need to generate a simple CatBoost model using Python." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "from catboost import CatBoostClassifier\n", + "\n", + "train_data = np.random.randint(0, 100, size=(100, 10))\n", + "train_labels = np.random.randint(0, 2, size=(100))\n", + "\n", + "model = CatBoostClassifier(iterations=2,\n", + " depth=2,\n", + " learning_rate=1,\n", + " loss_function='Logloss',\n", + " verbose=True)\n", + "model.fit(train_data, train_labels)\n", + "model.save_model('model.cbm')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Our model will be persisted as a file named `model.cbm`." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Serving\n", + "\n", + "Now that we have trained and saved our model, the next step will be to serve it using `mlserver`. \n", + "For that, we will need to create 2 configuration files: \n", + "\n", + "- `settings.json`: holds the configuration of our server (e.g. ports, log level, etc.).\n", + "- `model-settings.json`: holds the configuration of our model (e.g. input type, runtime to use, etc.)." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### `settings.json`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%%writefile settings.json\n", + "{\n", + " \"debug\": \"true\"\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### `model-settings.json`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%%writefile model-settings.json\n", + "{\n", + " \"name\": \"catboost\",\n", + " \"implementation\": \"mlserver_catboost.CatboostModel\",\n", + " \"parameters\": {\n", + " \"uri\": \"./model.cbm\",\n", + " \"version\": \"v0.1.0\"\n", + " }\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Start serving our model\n", + "\n", + "Now that we have our config in-place, we can start the server by running `mlserver start .`. This needs to either be ran from the same directory where our config files are or pointing to the folder where they are.\n", + "\n", + "```shell\n", + "mlserver start .\n", + "```\n", + "\n", + "Since this command will start the server and block the terminal, waiting for requests, this will need to be ran in the background on a separate terminal." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Send test inference request\n", + "\n", + "We now have our model being served by `mlserver`.\n", + "To make sure that everything is working as expected, let's send a request from our test set.\n", + "\n", + "For that, we can use the Python types that `mlserver` provides out of box, or we can build our request manually." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "pycharm": { + "name": "#%%\n" + } + }, + "outputs": [], + "source": [ + "import requests\n", + "import numpy as np\n", + "\n", + "test_data = np.random.randint(0, 100, size=(1, 10))\n", + "\n", + "x_0 = test_data[0:1]\n", + "inference_request = {\n", + " \"inputs\": [\n", + " {\n", + " \"name\": \"predict-prob\",\n", + " \"shape\": x_0.shape,\n", + " \"datatype\": \"FP32\",\n", + " \"data\": x_0.tolist()\n", + " }\n", + " ]\n", + "}\n", + "\n", + "endpoint = \"http://localhost:8080/v2/models/catboost/versions/v0.1.0/infer\"\n", + "response = requests.post(endpoint, json=inference_request)\n", + "\n", + "print(response.json())" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.8" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} \ No newline at end of file diff --git a/docs/examples/catboost/README.md b/docs/examples/catboost/README.md new file mode 100644 index 000000000..c4a6c0768 --- /dev/null +++ b/docs/examples/catboost/README.md @@ -0,0 +1,104 @@ +# Serving CatBoost models + +Out of the box, `mlserver` supports the deployment and serving of `catboost` models. +By default, it will assume that these models have been [serialised using the `save_model()` method](https://catboost.ai/en/docs/concepts/python-reference_catboost_save_model). + +In this example, we will cover how we can train and serialise a simple model, to then serve it using `mlserver`. + +## Training + +To test the CatBoost Server, first we need to generate a simple CatBoost model using Python. + + +```python +import numpy as np +from catboost import CatBoostClassifier + +train_data = np.random.randint(0, 100, size=(100, 10)) +train_labels = np.random.randint(0, 2, size=(100)) + +model = CatBoostClassifier(iterations=2, + depth=2, + learning_rate=1, + loss_function='Logloss', + verbose=True) +model.fit(train_data, train_labels) +model.save_model('model.cbm') +``` + +Our model will be persisted as a file named `model.cbm`. + +## Serving + +Now that we have trained and saved our model, the next step will be to serve it using `mlserver`. +For that, we will need to create 2 configuration files: + +- `settings.json`: holds the configuration of our server (e.g. ports, log level, etc.). +- `model-settings.json`: holds the configuration of our model (e.g. input type, runtime to use, etc.). + +### `settings.json` + + +```python +%%writefile settings.json +{ + "debug": "true" +} +``` + +### `model-settings.json` + + +```python +%%writefile model-settings.json +{ + "name": "catboost", + "implementation": "mlserver_catboost.CatboostModel", + "parameters": { + "uri": "./model.cbm", + "version": "v0.1.0" + } +} +``` + +### Start serving our model + +Now that we have our config in-place, we can start the server by running `mlserver start .`. This needs to either be ran from the same directory where our config files are or pointing to the folder where they are. + +```shell +mlserver start . +``` + +Since this command will start the server and block the terminal, waiting for requests, this will need to be ran in the background on a separate terminal. + +### Send test inference request + +We now have our model being served by `mlserver`. +To make sure that everything is working as expected, let's send a request from our test set. + +For that, we can use the Python types that `mlserver` provides out of box, or we can build our request manually. + + +```python +import requests +import numpy as np + +test_data = np.random.randint(0, 100, size=(1, 10)) + +x_0 = test_data[0:1] +inference_request = { + "inputs": [ + { + "name": "predict-prob", + "shape": x_0.shape, + "datatype": "FP32", + "data": x_0.tolist() + } + ] +} + +endpoint = "http://localhost:8080/v2/models/catboost/versions/v0.1.0/infer" +response = requests.post(endpoint, json=inference_request) + +print(response.json()) +``` diff --git a/docs/examples/catboost/model-settings.json b/docs/examples/catboost/model-settings.json new file mode 100644 index 000000000..7967b0c63 --- /dev/null +++ b/docs/examples/catboost/model-settings.json @@ -0,0 +1,8 @@ +{ + "name": "catboost", + "implementation": "mlserver_catboost.CatboostModel", + "parameters": { + "uri": "./model.cbm", + "version": "v0.1.0" + } +} \ No newline at end of file diff --git a/docs/examples/catboost/model.cbm b/docs/examples/catboost/model.cbm new file mode 100644 index 0000000000000000000000000000000000000000..874dac119dbf44a6e848a503e6d7e6b717779605 GIT binary patch literal 6640 zcmeHMON<;x8E!)Yi4{d5G7uz7v^qH$w6i-sJ3B80?93V`j%;IP*Ga_6Dphw^&s4U% ztJ9C!oj69Easr7%4!{M0k~18T5K<)PfW#pOI7A2#C%6Q`0U-{EAT8ftRXsDi;3Ozq zkZ8Hw(|=X{^}ipr+vBVLJD+i!GfwlIb{=x>aqe>-a83hs4&(6C4j_*6F^qSx@GQp5 z4>`^o7%pI6$M?f{UrKmT$FV5Nt11!+oB57&{f|dS7qO1^_wc^uMWW&|5fRG^72}6D zig{9sH7`)AtYkJ}DzBu9eY#UZstk+Z~fE0}6 zET8eDrv10AbXZ)dl<^r3oB(bGx5`PVYDi|(UGoybi;To+Xz+m}6;&ZaEm#%2Ai_${ z1T#P)%TjPfO%hd>AR^}lPigsWY!)Eq11}D1Fp%2iz_54I9M2`65sAWJ10L+M5G#ornT%DDdZ4!A#Y9x^hv6iGt1|miRuw9r z6OLTlm4K0_mIUvbA~6%3sN8F>`8U`0+Ur~T?c@KO5ELl41dTPpC6Ik+hCUcE$B*A8 zvBQUM6WIJ_Ax}cm^HfFfiJg6!?}9^c1v_R_Rk4&GLYFVWT**1`LHvY@lzS*EIIB;{ zQqqlfdz$~h$`&eLvB3|kvj2(=!(SS&HHy-~AyJbF#hOISL;^c_Ruq9MDUgxFQ>YI` zS)K^)h+8a9)Jhz9u|`~9Iw*W*%1Z`+Sq8gE%vdC1j)=~5ctlL(h-8!lC~6~=;M`Gr zeb5=wGuYhp+r8ls&W)&|4k!$lh#fp2LYu&n@UmnXPX$??_Chbzumd3{(~6+5`9^@+ z3%yZ>oC5|y5Cx?JSqfH$3aM(DGicI~#CTEgIg?ogv!@l_$wG)| zj-0Sl>p)1@Cxxo>#teXJRf?i);wf;;NrrG9tt{1I6o=JaOrkNZrNK~~w))m_95N$i zBaY*wMq}PsK8aKX;-7Z((J`MxlkbC_a^Na@%@0bMqGFmTCMkrr!G#LLx+p{z3I>yfn*@lqH0fiqM2uyq zQgXXkWD!#_I{@bvPp%RfBC^87N>NZSHu$x7Hn~KzgtTsUmjaQU&NC|)7$~4){{+%y z*6h6w0AyebUgkEyds}2um|dCCJX!USeE$deC87Uk{A$@!mRuT}R(4jW0ko;1P|u&g z`|9_BTPswgIVD4_VY|#a0b3~Jia_c2M0uHF#sagk8e_qYqN!ZaHh)bYh~+o3{Y)K@ zdpD+x3Z3dFDv3Q8@PucR8f7Kz1LcWGsY-dt#41v1iE=shi zfu^inQoV!OMC_8=5f(wk9;&>?4#)G03rcR^q)L;jPZac-c69Rp_byB;L@0{DMF!FS zYwtkeuMnV*$||p^>X9|Xi#05sWPV)~yFdc!0`g-?5s{-*K5RDnWY>87?y&8nzCH@%5< zFzsIyv=M<1RwO)6O%-psNM*)!-o=~F)HPPYvj`=532|kUYN}y$oQMORE`mDT7euMw z?oj*C(OZoKHYqEp3VS)C4%)I=X(mhxgwnNKP2RPl@`s)Furq{7ns({xvpZA-M@$mk~**p6jU7m!{TYoKxw$1;>e;)q(VlPX{B z`cU}V+Q1%2sWXcayFr32YE1K2@p8ZMm+k8}z@7g6t0(K)f9z)Nv7oK_j+|m z3d>MrG)$P7@Dx@x6S+d7qJtpx2t8}sw0E9&`?k4zFzhdO-+v0okcqg(#_VHKds9 z4|8Kcb1GyOgfHE1Yl&LQ03(A8hz`AfVdSQ;Jt(BUKA}C3eO1)BRLN0(nTp1We$RV` zA6!CLTPOTkp6FT$evQHusQ|vPLV>EQysn_&q)zENX3yK9I}vd?0x8F_ta(>-L46a3 zlJUHx#!q8JiSCVh6tbv_M@WfLdmHgJ-&T4tQDI}nCF5mqcvuBTS%L%M`&t?&1Z=_U z1jZpGZWJbz)D}9BSxjxVHveN9T!C038g(^xz9NkUUsgp+Ls<`lW)jDgh-hr_-NYfQ zaLAp*%=f%)YanB~l9+2P;1{HCM9}x=o2XIn2yw%vE z#`mx))_9wy`>x2AX2mFdP*R1;fm7=R2^98%c1>T{!6o5#&Vv~CL>m?y=fcs^(G+m< z%{>g}Yh_#j~walujd8Z2CNI$3q@R-K}1`9}Tyn-qt|$hW$RE-EMa)^21JFZyF<{hSQNd zMbs=eQcf3*dg6&1w3UIrQEx|MmcoWc z?hYK4dLvgy02fQ$lYGT4}?7OKgt+Q?;&0xuKghTeHq zGrrMy=Q?OvV9_nA#X~oNK(Mhs=NlTXS5e9!%Ne;1f|i~XMGI%thl@p;VDdHyWaMfv zY3>Rwy31a-yY6qTw_E*Q3xva~Lif=@uXQ6pf~(z~tJmF2g>;|hnd|rQZ`A3IIs^B{ z?zRgnYrBVo9_w{a>!X}c!hRYG8PZ5e7ZhYb&gY${w1qu@d-gNVBhFdep+AOu_BE&N zbe#eA!Sb97&PA+roKqNo!*~bdR~TfI=Kwr|@c_olfKe{|2L{>weSpz#@{PAK{)z$6 zv4?EiJlhw{d(*y0_U+sEIr~0u-)-~$+C}{)Jm2{?J-@#5`m4`8{khlQzH{ye-+SYg zzx?LT{cnBY8$bN*@6SK-clp@|fBBO;7wey;_dfmO^M81F^8ECb-`(M#`uV$Wo%!i` zpm4yFe(1eAD2C8OFr;YBhlQO2>=a<9z}rdoXp4U6?-#`XFED bool: + model_uri = await get_model_uri( + self._settings, wellknown_filenames=WELLKNOWN_MODEL_FILENAMES + ) + + self._model = CatBoostClassifier() + self._model.load_model(model_uri) + self.ready = True + return self.ready + + async def predict(self, payload: types.InferenceRequest) -> types.InferenceResponse: + decoded = self.decode_request(payload, default_codec=NumpyRequestCodec) + prediction = self._model.predict(decoded) + + return types.InferenceResponse( + model_name=self.name, + model_version=self.version, + outputs=[NumpyCodec.encode(name="predict", payload=prediction)], + ) diff --git a/runtimes/catboost/mlserver_catboost/version.py b/runtimes/catboost/mlserver_catboost/version.py new file mode 100644 index 000000000..109fa1c51 --- /dev/null +++ b/runtimes/catboost/mlserver_catboost/version.py @@ -0,0 +1 @@ +__version__ = "1.1.0.dev1" diff --git a/runtimes/catboost/setup.py b/runtimes/catboost/setup.py new file mode 100644 index 000000000..7645eab60 --- /dev/null +++ b/runtimes/catboost/setup.py @@ -0,0 +1,44 @@ +import os + +from typing import Dict +from setuptools import setup, find_packages + +ROOT_PATH = os.path.dirname(__file__) +PKG_NAME = "mlserver-catboost" +PKG_PATH = os.path.join(ROOT_PATH, PKG_NAME.replace("-", "_")) + + +def _load_version() -> str: + version = "" + version_path = os.path.join(PKG_PATH, "version.py") + with open(version_path) as fp: + version_module: Dict[str, str] = {} + exec(fp.read(), version_module) + version = version_module["__version__"] + + return version + + +def _load_description() -> str: + readme_path = os.path.join(ROOT_PATH, "README.md") + with open(readme_path) as fp: + return fp.read() + + +setup( + name=PKG_NAME, + version=_load_version(), + url="https://github.com/SeldonIO/MLServer.git", + author="Theofilos Papapanagiotou", + author_email="theofilos@gmail.com", + description="CatBoost runtime for MLServer", + packages=find_packages(exclude=["tests", "tests.*"]), + install_requires=[ + "mlserver", + "catboost", + "pandas", + ], + long_description=_load_description(), + long_description_content_type="text/markdown", + license="Apache 2.0", +) diff --git a/runtimes/catboost/tests/conftest.py b/runtimes/catboost/tests/conftest.py new file mode 100644 index 000000000..d2bddfe0f --- /dev/null +++ b/runtimes/catboost/tests/conftest.py @@ -0,0 +1,53 @@ +import pytest +import os +import numpy as np +from catboost import CatBoostClassifier, Pool + +from mlserver.settings import ModelSettings, ModelParameters +from mlserver.types import InferenceRequest + +from mlserver_catboost import CatboostModel + +TESTS_PATH = os.path.dirname(__file__) +TESTDATA_PATH = os.path.join(TESTS_PATH, "testdata") + + +@pytest.fixture +def model_uri(tmp_path) -> str: + + train_data = np.random.randint(0, 100, size=(100, 10)) + train_labels = np.random.randint(0, 2, size=(100)) + + model = CatBoostClassifier(iterations=2, + depth=2, + learning_rate=1, + loss_function='Logloss', + verbose=True) + model.fit(train_data, train_labels) + + model_uri = os.path.join(tmp_path, "catboost-model.bst") + model.save_model(model_uri) + + return model_uri + + +@pytest.fixture +def model_settings(model_uri: str) -> ModelSettings: + return ModelSettings( + name="catboost-model", + parameters=ModelParameters(uri=model_uri, version="v1.2.3"), + ) + + +@pytest.fixture +async def model(model_settings: ModelSettings) -> CatboostModel: + model = CatboostModel(model_settings) + await model.load() + + return model + + +@pytest.fixture +def inference_request() -> InferenceRequest: + payload_path = os.path.join(TESTDATA_PATH, "inference-request.json") + return InferenceRequest.parse_file(payload_path) diff --git a/runtimes/catboost/tests/test_catboost.py b/runtimes/catboost/tests/test_catboost.py new file mode 100644 index 000000000..0817db456 --- /dev/null +++ b/runtimes/catboost/tests/test_catboost.py @@ -0,0 +1,48 @@ +import pytest +import os +from catboost import CatBoostClassifier + +from mlserver.settings import ModelSettings +from mlserver.codecs import CodecError +from mlserver.types import RequestInput, InferenceRequest + +from mlserver_catboost import CatboostModel +from mlserver_catboost.catboost import WELLKNOWN_MODEL_FILENAMES + + +def test_load(model: CatboostModel): + assert model.ready + assert type(model._model) == CatBoostClassifier + + +@pytest.mark.parametrize("fname", WELLKNOWN_MODEL_FILENAMES) +async def test_load_folder(fname, model_uri: str, model_settings: ModelSettings): + model_folder = os.path.dirname(model_uri) + model_path = os.path.join(model_folder, fname) + os.rename(model_uri, model_path) + + model_settings.parameters.uri = model_path # type: ignore + + model = CatboostModel(model_settings) + await model.load() + + assert model.ready + assert type(model._model) == CatBoostClassifier + + +async def test_predict(model: CatboostModel, inference_request: InferenceRequest): + response = await model.predict(inference_request) + + assert len(response.outputs) == 1 + assert 0 <= response.outputs[0].data[0] <= 1 + + +async def test_multiple_inputs_error( + model: CatboostModel, inference_request: InferenceRequest +): + inference_request.inputs.append( + RequestInput(name="input-1", shape=[1, 2], data=[[0, 1]], datatype="FP32") + ) + + with pytest.raises(CodecError): + await model.predict(inference_request) diff --git a/runtimes/catboost/tests/testdata/inference-request.json b/runtimes/catboost/tests/testdata/inference-request.json new file mode 100644 index 000000000..7bc1688b8 --- /dev/null +++ b/runtimes/catboost/tests/testdata/inference-request.json @@ -0,0 +1,11 @@ +{ + "inputs": [ + { + "name": "input-0", + "shape": [10], + "datatype": "INT32", + "data": [[56, 2, 85, 72, 4, 87, 26, 50, 32, 19]] + } + ], + "outputs": [{ "name": "predict" }] +} diff --git a/setup.cfg b/setup.cfg index 33ce25db4..75f7d2ba3 100644 --- a/setup.cfg +++ b/setup.cfg @@ -16,3 +16,128 @@ exclude = ./**/dist ./venv ./**/venv + +[mypy] +ignore_missing_imports = True +plugins = pydantic.mypy + +[tool:pytest] +asyncio_mode = auto + +[tox:tox] +basepython = py3 +envlist = + py3 + all + sklearn + xgboost + lightgbm + catboost + mlflow + alibiexplain + alibidetect + all-runtimes + +[testenv] +deps = -r{toxinidir}/requirements/dev.txt +commands = pytest {posargs} {toxinidir}/tests + +[testenv:all] +deps = + -e{toxinidir}[all] + -r{toxinidir}/requirements/dev.txt +commands = pytest {posargs} {toxinidir}/tests + +[testenv:sklearn] +deps = + -e{toxinidir} + -e{toxinidir}/runtimes/sklearn + -r{toxinidir}/requirements/dev.txt +commands = pytest {posargs} {toxinidir}/runtimes/sklearn + +[testenv:xgboost] +deps = + -e{toxinidir} + -e{toxinidir}/runtimes/xgboost + -r{toxinidir}/requirements/dev.txt +commands = pytest {posargs} {toxinidir}/runtimes/xgboost + +[testenv:lightgbm] +deps = + -e{toxinidir} + -e{toxinidir}/runtimes/lightgbm + -r{toxinidir}/requirements/dev.txt +commands = pytest {posargs} {toxinidir}/runtimes/lightgbm + +[testenv:catboost] +deps = + -e{toxinidir} + -e{toxinidir}/runtimes/catboost + -r{toxinidir}/requirements/dev.txt +commands = pytest {posargs} {toxinidir}/runtimes/catboost + +[testenv:mlflow] +deps = + -e{toxinidir} + -e{toxinidir}/runtimes/mlflow + -r{toxinidir}/requirements/dev.txt + -r{toxinidir}/runtimes/mlflow/requirements-dev.txt +commands = pytest {posargs} {toxinidir}/runtimes/mlflow + +[testenv:alibiexplain] +deps = + -e{toxinidir}/runtimes/alibi-explain + -e{toxinidir} + -r{toxinidir}/requirements/dev.txt + -r{toxinidir}/runtimes/alibi-explain/requirements-dev.txt +commands = pytest {posargs} {toxinidir}/runtimes/alibi-explain + +[testenv:alibidetect] +deps = + -e{toxinidir}/runtimes/alibi-detect + -e{toxinidir} + -r{toxinidir}/requirements/dev.txt +commands = pytest {posargs} {toxinidir}/runtimes/alibi-detect + +[testenv:all-runtimes] +deps = + -e{toxinidir}/runtimes/alibi-explain + -e{toxinidir}/runtimes/alibi-detect + -e{toxinidir}/runtimes/sklearn + -e{toxinidir}/runtimes/xgboost + -e{toxinidir}/runtimes/mllib + -e{toxinidir}/runtimes/lightgbm + -e{toxinidir}/runtimes/catboost + -e{toxinidir}/runtimes/mlflow + -e{toxinidir}[all] + -r{toxinidir}/requirements/dev.txt + -r{toxinidir}/runtimes/mlflow/requirements-dev.txt + -r{toxinidir}/runtimes/alibi-explain/requirements-dev.txt +commands = pytest {posargs} + +[testenv:licenses] +deps = + -e{toxinidir}[all] + -e{toxinidir}/runtimes/sklearn + -e{toxinidir}/runtimes/xgboost + -e{toxinidir}/runtimes/mllib + -e{toxinidir}/runtimes/lightgbm + -e{toxinidir}/runtimes/catboost + -e{toxinidir}/runtimes/mlflow + -e{toxinidir}/runtimes/alibi-explain + -e{toxinidir}/runtimes/alibi-detect + -r{toxinidir}/runtimes/mlflow/requirements-dev.txt + -r{toxinidir}/runtimes/alibi-explain/requirements-dev.txt + -r{toxinidir}/requirements/dev.txt + -r{toxinidir}/requirements/docker.txt +commands = + pip-licenses \ + --from=mixed \ + --format=csv \ + --output-file=./licenses/license_info.csv + pip-licenses \ + --from=mixed \ + --format=plain-vertical \ + --with-license-file \ + --no-license-path \ + --output-file=./licenses/license.txt From 045ba6b818de9a9fb48055f95f0cf7ec9597901b Mon Sep 17 00:00:00 2001 From: Theofilos Papapanagiotou Date: Tue, 22 Mar 2022 13:37:31 +0100 Subject: [PATCH 02/10] add readthedocs index for catboost --- docs/runtimes/catboost.md | 2 +- docs/runtimes/index.md | 21 ++++++++++----------- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/docs/runtimes/catboost.md b/docs/runtimes/catboost.md index 7bb1b40b7..5c7c78e22 100644 --- a/docs/runtimes/catboost.md +++ b/docs/runtimes/catboost.md @@ -1,3 +1,3 @@ -```{include} ../../runtimes/catbost/README.md +```{include} ../../runtimes/catboost/README.md :relative-docs: ../../docs/ ``` diff --git a/docs/runtimes/index.md b/docs/runtimes/index.md index 4259c4324..590cd4b78 100644 --- a/docs/runtimes/index.md +++ b/docs/runtimes/index.md @@ -22,17 +22,15 @@ class in your `model-settings.json` file. ## Included Inference Runtimes -| Framework | Package Name | Implementation Class | Example | Documentation | -| ------------- | ------------------------ | -------------------------------------------- | ---------------------------------------------------------- | ---------------------------------------------------------------- | -| Scikit-Learn | `mlserver-sklearn` | `mlserver_sklearn.SKLearnModel` | [Scikit-Learn example](../examples/sklearn/README.md) | [MLServer SKLearn](./sklearn) | -| XGBoost | `mlserver-xgboost` | `mlserver_xgboost.XGBoostModel` | [XGBoost example](../examples/xgboost/README.md) | [MLServer XGBoost](./xgboost) | -| HuggingFace | `mlserver-huggingface` | `mlserver_huggingface.HuggingFaceRuntime` | [HuggingFace example](../examples/huggingface/README.md) | [MLServer HuggingFace](./huggingface) | -| Spark MLlib | `mlserver-mllib` | `mlserver_mllib.MLlibModel` | Coming Soon | [MLServer MLlib](./mllib) | -| LightGBM | `mlserver-lightgbm` | `mlserver_lightgbm.LightGBMModel` | [LightGBM example](../examples/lightgbm/README.md) | [MLServer LightGBM](./lightgbm) | -| Tempo | `tempo` | `tempo.mlserver.InferenceRuntime` | [Tempo example](../examples/tempo/README.md) | [`github.com/SeldonIO/tempo`](https://github.com/SeldonIO/tempo) | -| MLflow | `mlserver-mlflow` | `mlserver_mlflow.MLflowRuntime` | [MLflow example](../examples/mlflow/README.md) | [MLServer MLflow](./mlflow) | -| Alibi-Detect | `mlserver-alibi-detect` | `mlserver_alibi_detect.AlibiDetectRuntime` | [Alibi-detect example](../examples/alibi-detect/README.md) | [MLServer Alibi-Detect](./alibi-detect) | -| Alibi-Explain | `mlserver-alibi-explain` | `mlserver_alibi_explain.AlibiExplainRuntime` | Coming Soon | [MLServer Alibi-Explain](./alibi-explain) | +| Framework | Package Name | Implementation Class | Example | Documentation | +| ------------ | ----------------------- | ------------------------------------------ | ---------------------------------------------------------- | ---------------------------------------------------------------- | +| Scikit-Learn | `mlserver-sklearn` | `mlserver_sklearn.SKLearnModel` | [Scikit-Learn example](../examples/sklearn/README.md) | [MLServer SKLearn](./sklearn) | +| XGBoost | `mlserver-xgboost` | `mlserver_xgboost.XGBoostModel` | [XGBoost example](../examples/xgboost/README.md) | [MLServer XGBoost](./xgboost) | +| Spark MLlib | `mlserver-mllib` | `mlserver_mllib.MLlibModel` | Coming Soon | [MLServer MLlib](./mllib) | +| LightGBM | `mlserver-lightgbm` | `mlserver_lightgbm.LightGBMModel` | [LightGBM example](../examples/lightgbm/README.md) | [MLServer LightGBM](./lightgbm) | +| Tempo | `tempo` | `tempo.mlserver.InferenceRuntime` | [Tempo example](../examples/tempo/README.md) | [`github.com/SeldonIO/tempo`](https://github.com/SeldonIO/tempo) | +| MLflow | `mlserver-mlflow` | `mlserver_mlflow.MLflowRuntime` | [MLflow example](../examples/mlflow/README.md) | [MLServer MLflow](./mlflow) | +| Alibi-Detect | `mlserver-alibi-detect` | `mlserver_alibi_detect.AlibiDetectRuntime` | [Alibi-detect example](../examples/alibi-detect/README.md) | [MLServer Alibi-Detect](./alibi-detect) | ```{toctree} :hidden: @@ -44,6 +42,7 @@ MLflow <./mlflow> Tempo Spark MLlib <./mllib> LightGBM <./lightgbm> +Custom <./custom> Alibi-Detect <./alibi-detect> Alibi-Explain <./alibi-explain> HuggingFace <./huggingface> From b24ae1626cfd3d20f0bb75989958b0d93b5f350e Mon Sep 17 00:00:00 2001 From: Theofilos Papapanagiotou Date: Tue, 22 Mar 2022 13:52:21 +0100 Subject: [PATCH 03/10] add readthedocs index for catboost --- runtimes/catboost/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtimes/catboost/README.md b/runtimes/catboost/README.md index 3e8984790..4f2d1c557 100644 --- a/runtimes/catboost/README.md +++ b/runtimes/catboost/README.md @@ -16,7 +16,7 @@ this [worked out example](../../docs/examples/catboost/README.md). ## Content Types If no [content type](../../docs/user-guide/content-type) is present on the -request or metadata, the LightGBM runtime will try to decode the payload as +request or metadata, the CatBoost runtime will try to decode the payload as a [NumPy Array](../../docs/user-guide/content-type). To avoid this, either send a different content type explicitly, or define the correct one as part of your [model's From 8ad524a61ffc5b5c10e3391fe966ffc8a68f4be6 Mon Sep 17 00:00:00 2001 From: Theofilos Papapanagiotou Date: Wed, 23 Mar 2022 11:12:46 +0100 Subject: [PATCH 04/10] lint --- runtimes/catboost/tests/conftest.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/runtimes/catboost/tests/conftest.py b/runtimes/catboost/tests/conftest.py index d2bddfe0f..e43d51a80 100644 --- a/runtimes/catboost/tests/conftest.py +++ b/runtimes/catboost/tests/conftest.py @@ -1,7 +1,7 @@ import pytest import os import numpy as np -from catboost import CatBoostClassifier, Pool +from catboost import CatBoostClassifier from mlserver.settings import ModelSettings, ModelParameters from mlserver.types import InferenceRequest @@ -18,11 +18,9 @@ def model_uri(tmp_path) -> str: train_data = np.random.randint(0, 100, size=(100, 10)) train_labels = np.random.randint(0, 2, size=(100)) - model = CatBoostClassifier(iterations=2, - depth=2, - learning_rate=1, - loss_function='Logloss', - verbose=True) + model = CatBoostClassifier( + iterations=2, depth=2, learning_rate=1, loss_function="Logloss", verbose=True + ) model.fit(train_data, train_labels) model_uri = os.path.join(tmp_path, "catboost-model.bst") From c632e3720c099460e2d58c73dd702a7cdaacbbd8 Mon Sep 17 00:00:00 2001 From: Theofilos Papapanagiotou Date: Thu, 24 Mar 2022 15:47:15 +0100 Subject: [PATCH 05/10] review fixes --- docs/runtimes/index.md | 1 + runtimes/catboost/mlserver_catboost/catboost.py | 2 +- runtimes/catboost/setup.py | 1 - runtimes/catboost/tests/conftest.py | 11 +++++++++-- 4 files changed, 11 insertions(+), 4 deletions(-) diff --git a/docs/runtimes/index.md b/docs/runtimes/index.md index 590cd4b78..f526bc582 100644 --- a/docs/runtimes/index.md +++ b/docs/runtimes/index.md @@ -28,6 +28,7 @@ class in your `model-settings.json` file. | XGBoost | `mlserver-xgboost` | `mlserver_xgboost.XGBoostModel` | [XGBoost example](../examples/xgboost/README.md) | [MLServer XGBoost](./xgboost) | | Spark MLlib | `mlserver-mllib` | `mlserver_mllib.MLlibModel` | Coming Soon | [MLServer MLlib](./mllib) | | LightGBM | `mlserver-lightgbm` | `mlserver_lightgbm.LightGBMModel` | [LightGBM example](../examples/lightgbm/README.md) | [MLServer LightGBM](./lightgbm) | +| CatBoost | `mlserver-catboost` | `mlserver_catboost.CatboostModel` | [CatBoost example](../examples/catboost/README.md) | [MLServer CatBoost](./catboost) | | Tempo | `tempo` | `tempo.mlserver.InferenceRuntime` | [Tempo example](../examples/tempo/README.md) | [`github.com/SeldonIO/tempo`](https://github.com/SeldonIO/tempo) | | MLflow | `mlserver-mlflow` | `mlserver_mlflow.MLflowRuntime` | [MLflow example](../examples/mlflow/README.md) | [MLServer MLflow](./mlflow) | | Alibi-Detect | `mlserver-alibi-detect` | `mlserver_alibi_detect.AlibiDetectRuntime` | [Alibi-detect example](../examples/alibi-detect/README.md) | [MLServer Alibi-Detect](./alibi-detect) | diff --git a/runtimes/catboost/mlserver_catboost/catboost.py b/runtimes/catboost/mlserver_catboost/catboost.py index 675e9edd7..c8cb2e856 100644 --- a/runtimes/catboost/mlserver_catboost/catboost.py +++ b/runtimes/catboost/mlserver_catboost/catboost.py @@ -6,7 +6,7 @@ from mlserver.codecs import NumpyCodec, NumpyRequestCodec -WELLKNOWN_MODEL_FILENAMES = ["model.bst"] +WELLKNOWN_MODEL_FILENAMES = ["model.cbm", "model.bin"] class CatboostModel(MLModel): diff --git a/runtimes/catboost/setup.py b/runtimes/catboost/setup.py index 7645eab60..eacb92d8a 100644 --- a/runtimes/catboost/setup.py +++ b/runtimes/catboost/setup.py @@ -36,7 +36,6 @@ def _load_description() -> str: install_requires=[ "mlserver", "catboost", - "pandas", ], long_description=_load_description(), long_description_content_type="text/markdown", diff --git a/runtimes/catboost/tests/conftest.py b/runtimes/catboost/tests/conftest.py index e43d51a80..d3bcf8419 100644 --- a/runtimes/catboost/tests/conftest.py +++ b/runtimes/catboost/tests/conftest.py @@ -1,7 +1,7 @@ import pytest import os import numpy as np -from catboost import CatBoostClassifier +from catboost import CatBoostClassifier #, CatBoostRegressor, CatBoostRanker from mlserver.settings import ModelSettings, ModelParameters from mlserver.types import InferenceRequest @@ -21,9 +21,16 @@ def model_uri(tmp_path) -> str: model = CatBoostClassifier( iterations=2, depth=2, learning_rate=1, loss_function="Logloss", verbose=True ) + # TODO: add a selector for the regressor/ranker classes + # model = CatBoostRegressor( + # iterations=2, depth=2, learning_rate=1, loss_function="RMSE", verbose=True + # ) + # model = CatBoostRanker( + # iterations=2, depth=2, learning_rate=1, loss_function="RMSE", verbose=True + # ) model.fit(train_data, train_labels) - model_uri = os.path.join(tmp_path, "catboost-model.bst") + model_uri = os.path.join(tmp_path, "model.cbm") model.save_model(model_uri) return model_uri From a82d69eecb4feb513ce62fc0295a2550041c4b36 Mon Sep 17 00:00:00 2001 From: krishanbhasin-gc Date: Wed, 20 Sep 2023 13:43:21 +0100 Subject: [PATCH 06/10] adjust to use poetry --- runtimes/catboost/README.md | 2 +- .../catboost/mlserver_catboost/version.py | 2 +- runtimes/catboost/pyproject.toml | 22 +++ runtimes/catboost/setup.py | 43 ------ runtimes/catboost/tests/conftest.py | 3 +- runtimes/catboost/tox.ini | 11 ++ setup.cfg | 125 ------------------ 7 files changed, 36 insertions(+), 172 deletions(-) create mode 100644 runtimes/catboost/pyproject.toml delete mode 100644 runtimes/catboost/setup.py create mode 100644 runtimes/catboost/tox.ini diff --git a/runtimes/catboost/README.md b/runtimes/catboost/README.md index 4f2d1c557..3dfd52b51 100644 --- a/runtimes/catboost/README.md +++ b/runtimes/catboost/README.md @@ -1,6 +1,6 @@ # CatBoost runtime for MLServer -This package provides a MLServer runtime compatible with CatBoost. +This package provides a MLServer runtime compatible with CatBoost's `CatboostClassifier`. ## Usage diff --git a/runtimes/catboost/mlserver_catboost/version.py b/runtimes/catboost/mlserver_catboost/version.py index 109fa1c51..84568d209 100644 --- a/runtimes/catboost/mlserver_catboost/version.py +++ b/runtimes/catboost/mlserver_catboost/version.py @@ -1 +1 @@ -__version__ = "1.1.0.dev1" +__version__ = "1.4.0.dev3" diff --git a/runtimes/catboost/pyproject.toml b/runtimes/catboost/pyproject.toml new file mode 100644 index 000000000..266e5d5c2 --- /dev/null +++ b/runtimes/catboost/pyproject.toml @@ -0,0 +1,22 @@ +[tool.poetry] +name = "mlserver-catboost" +version = "1.4.0.dev3" +description = "Catboost runtime for MLServer" +authors = ["Theofilos Papapanagiotou "] +maintainers = ["Seldon Technologies Ltd. "] +license = "Apache-2.0" +readme = "README.md" +packages = [{include = "mlserver_catboost"}] + +[tool.poetry.dependencies] +python = "^3.8.1,<3.12" +scikit-learn = "*" +joblib = "*" +mlserver = "*" + +[tool.poetry.group.dev.dependencies] +mlserver = {path = "../..", develop = true} + +[build-system] +requires = ["poetry-core"] +build-backend = "poetry.core.masonry.api" diff --git a/runtimes/catboost/setup.py b/runtimes/catboost/setup.py deleted file mode 100644 index eacb92d8a..000000000 --- a/runtimes/catboost/setup.py +++ /dev/null @@ -1,43 +0,0 @@ -import os - -from typing import Dict -from setuptools import setup, find_packages - -ROOT_PATH = os.path.dirname(__file__) -PKG_NAME = "mlserver-catboost" -PKG_PATH = os.path.join(ROOT_PATH, PKG_NAME.replace("-", "_")) - - -def _load_version() -> str: - version = "" - version_path = os.path.join(PKG_PATH, "version.py") - with open(version_path) as fp: - version_module: Dict[str, str] = {} - exec(fp.read(), version_module) - version = version_module["__version__"] - - return version - - -def _load_description() -> str: - readme_path = os.path.join(ROOT_PATH, "README.md") - with open(readme_path) as fp: - return fp.read() - - -setup( - name=PKG_NAME, - version=_load_version(), - url="https://github.com/SeldonIO/MLServer.git", - author="Theofilos Papapanagiotou", - author_email="theofilos@gmail.com", - description="CatBoost runtime for MLServer", - packages=find_packages(exclude=["tests", "tests.*"]), - install_requires=[ - "mlserver", - "catboost", - ], - long_description=_load_description(), - long_description_content_type="text/markdown", - license="Apache 2.0", -) diff --git a/runtimes/catboost/tests/conftest.py b/runtimes/catboost/tests/conftest.py index d3bcf8419..2807d4b70 100644 --- a/runtimes/catboost/tests/conftest.py +++ b/runtimes/catboost/tests/conftest.py @@ -1,7 +1,7 @@ import pytest import os import numpy as np -from catboost import CatBoostClassifier #, CatBoostRegressor, CatBoostRanker +from catboost import CatBoostClassifier # , CatBoostRegressor, CatBoostRanker from mlserver.settings import ModelSettings, ModelParameters from mlserver.types import InferenceRequest @@ -14,7 +14,6 @@ @pytest.fixture def model_uri(tmp_path) -> str: - train_data = np.random.randint(0, 100, size=(100, 10)) train_labels = np.random.randint(0, 2, size=(100)) diff --git a/runtimes/catboost/tox.ini b/runtimes/catboost/tox.ini new file mode 100644 index 000000000..21f3266d4 --- /dev/null +++ b/runtimes/catboost/tox.ini @@ -0,0 +1,11 @@ +[tox] +isolated_build = true + +[testenv] +allowlist_externals = poetry +commands_pre = + poetry install --sync --no-root + poetry install -C {toxinidir}/../../ +commands = + python -m pytest {posargs} \ + {toxinidir}/tests diff --git a/setup.cfg b/setup.cfg index 75f7d2ba3..33ce25db4 100644 --- a/setup.cfg +++ b/setup.cfg @@ -16,128 +16,3 @@ exclude = ./**/dist ./venv ./**/venv - -[mypy] -ignore_missing_imports = True -plugins = pydantic.mypy - -[tool:pytest] -asyncio_mode = auto - -[tox:tox] -basepython = py3 -envlist = - py3 - all - sklearn - xgboost - lightgbm - catboost - mlflow - alibiexplain - alibidetect - all-runtimes - -[testenv] -deps = -r{toxinidir}/requirements/dev.txt -commands = pytest {posargs} {toxinidir}/tests - -[testenv:all] -deps = - -e{toxinidir}[all] - -r{toxinidir}/requirements/dev.txt -commands = pytest {posargs} {toxinidir}/tests - -[testenv:sklearn] -deps = - -e{toxinidir} - -e{toxinidir}/runtimes/sklearn - -r{toxinidir}/requirements/dev.txt -commands = pytest {posargs} {toxinidir}/runtimes/sklearn - -[testenv:xgboost] -deps = - -e{toxinidir} - -e{toxinidir}/runtimes/xgboost - -r{toxinidir}/requirements/dev.txt -commands = pytest {posargs} {toxinidir}/runtimes/xgboost - -[testenv:lightgbm] -deps = - -e{toxinidir} - -e{toxinidir}/runtimes/lightgbm - -r{toxinidir}/requirements/dev.txt -commands = pytest {posargs} {toxinidir}/runtimes/lightgbm - -[testenv:catboost] -deps = - -e{toxinidir} - -e{toxinidir}/runtimes/catboost - -r{toxinidir}/requirements/dev.txt -commands = pytest {posargs} {toxinidir}/runtimes/catboost - -[testenv:mlflow] -deps = - -e{toxinidir} - -e{toxinidir}/runtimes/mlflow - -r{toxinidir}/requirements/dev.txt - -r{toxinidir}/runtimes/mlflow/requirements-dev.txt -commands = pytest {posargs} {toxinidir}/runtimes/mlflow - -[testenv:alibiexplain] -deps = - -e{toxinidir}/runtimes/alibi-explain - -e{toxinidir} - -r{toxinidir}/requirements/dev.txt - -r{toxinidir}/runtimes/alibi-explain/requirements-dev.txt -commands = pytest {posargs} {toxinidir}/runtimes/alibi-explain - -[testenv:alibidetect] -deps = - -e{toxinidir}/runtimes/alibi-detect - -e{toxinidir} - -r{toxinidir}/requirements/dev.txt -commands = pytest {posargs} {toxinidir}/runtimes/alibi-detect - -[testenv:all-runtimes] -deps = - -e{toxinidir}/runtimes/alibi-explain - -e{toxinidir}/runtimes/alibi-detect - -e{toxinidir}/runtimes/sklearn - -e{toxinidir}/runtimes/xgboost - -e{toxinidir}/runtimes/mllib - -e{toxinidir}/runtimes/lightgbm - -e{toxinidir}/runtimes/catboost - -e{toxinidir}/runtimes/mlflow - -e{toxinidir}[all] - -r{toxinidir}/requirements/dev.txt - -r{toxinidir}/runtimes/mlflow/requirements-dev.txt - -r{toxinidir}/runtimes/alibi-explain/requirements-dev.txt -commands = pytest {posargs} - -[testenv:licenses] -deps = - -e{toxinidir}[all] - -e{toxinidir}/runtimes/sklearn - -e{toxinidir}/runtimes/xgboost - -e{toxinidir}/runtimes/mllib - -e{toxinidir}/runtimes/lightgbm - -e{toxinidir}/runtimes/catboost - -e{toxinidir}/runtimes/mlflow - -e{toxinidir}/runtimes/alibi-explain - -e{toxinidir}/runtimes/alibi-detect - -r{toxinidir}/runtimes/mlflow/requirements-dev.txt - -r{toxinidir}/runtimes/alibi-explain/requirements-dev.txt - -r{toxinidir}/requirements/dev.txt - -r{toxinidir}/requirements/docker.txt -commands = - pip-licenses \ - --from=mixed \ - --format=csv \ - --output-file=./licenses/license_info.csv - pip-licenses \ - --from=mixed \ - --format=plain-vertical \ - --with-license-file \ - --no-license-path \ - --output-file=./licenses/license.txt From dab1e0f2ab060af25441dcaa770f68c306fa207e Mon Sep 17 00:00:00 2001 From: krishanbhasin-gc Date: Wed, 20 Sep 2023 14:04:37 +0100 Subject: [PATCH 07/10] fix docs links --- docs/runtimes/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/runtimes/index.md b/docs/runtimes/index.md index f526bc582..fd3524d3b 100644 --- a/docs/runtimes/index.md +++ b/docs/runtimes/index.md @@ -43,7 +43,7 @@ MLflow <./mlflow> Tempo Spark MLlib <./mllib> LightGBM <./lightgbm> -Custom <./custom> +Catboost <./catboost> Alibi-Detect <./alibi-detect> Alibi-Explain <./alibi-explain> HuggingFace <./huggingface> From 5ffd1e0530706b0132eee4d5bf1bc74883727424 Mon Sep 17 00:00:00 2001 From: krishanbhasin-gc Date: Wed, 20 Sep 2023 14:18:25 +0100 Subject: [PATCH 08/10] make CI also run catboost tests --- .github/workflows/tests.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 23f9193ea..4518c67e1 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -92,6 +92,7 @@ jobs: - huggingface - alibi-explain - alibi-detect + - catboost is-pr: - ${{ github.event_name == 'pull_request' }} exclude: From fc1a445334568cc6a85c49ed3a2ea427cb113f49 Mon Sep 17 00:00:00 2001 From: krishanbhasin-gc Date: Wed, 20 Sep 2023 14:22:53 +0100 Subject: [PATCH 09/10] fixup! adjust to use poetry --- runtimes/catboost/pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtimes/catboost/pyproject.toml b/runtimes/catboost/pyproject.toml index 266e5d5c2..bc83e8981 100644 --- a/runtimes/catboost/pyproject.toml +++ b/runtimes/catboost/pyproject.toml @@ -10,7 +10,7 @@ packages = [{include = "mlserver_catboost"}] [tool.poetry.dependencies] python = "^3.8.1,<3.12" -scikit-learn = "*" +catboost = "*" joblib = "*" mlserver = "*" From ca0c6eccd3aad91a883de2dd46f1283ba047abab Mon Sep 17 00:00:00 2001 From: krishanbhasin-gc Date: Wed, 20 Sep 2023 14:31:44 +0100 Subject: [PATCH 10/10] fixup! make CI also run catboost tests --- runtimes/catboost/tests/conftest.py | 1 + 1 file changed, 1 insertion(+) diff --git a/runtimes/catboost/tests/conftest.py b/runtimes/catboost/tests/conftest.py index 2807d4b70..7fba251cc 100644 --- a/runtimes/catboost/tests/conftest.py +++ b/runtimes/catboost/tests/conftest.py @@ -39,6 +39,7 @@ def model_uri(tmp_path) -> str: def model_settings(model_uri: str) -> ModelSettings: return ModelSettings( name="catboost-model", + implementation=CatboostModel, parameters=ModelParameters(uri=model_uri, version="v1.2.3"), )