├── README.md ├── src ├── predict_flow.py ├── models.py └── train_flow.py ├── .gitignore └── check.ipynb /README.md: -------------------------------------------------------------------------------- 1 | # AI Metaflow Template 2 | 3 | ## Train Pipeline 4 | 5 | ```bash 6 | python src/train_flow.py --environment=conda run 7 | ``` 8 | 9 | ## Test Pipeline 10 | 11 | ```bash 12 | python src/predict_flow.py --environment=conda run --vector '[5.8, 2.8, 5.1, 2.4]' 13 | ``` -------------------------------------------------------------------------------- /src/predict_flow.py: -------------------------------------------------------------------------------- 1 | from metaflow import FlowSpec, step, Flow, Parameter, JSONType, conda_base, project, get_namespace 2 | 3 | 4 | @project(name='iris_project') 5 | @conda_base(python='3.10.11', libraries={'scikit-learn': '1.5.1'}) 6 | class ClassifierPredictFlow(FlowSpec): 7 | vector = Parameter('vector', type=JSONType, required=True) 8 | 9 | @step 10 | def start(self): 11 | print('NAMESPACE IS', get_namespace()) 12 | 13 | run = Flow('ClassifierTrainFlow').latest_run 14 | self.train_run_id = run.pathspec 15 | 16 | print("ClassifierTrainFlow run_id:", self.train_run_id) 17 | 18 | self.model = run['end'].task.data.model 19 | print("Input vector", self.vector) 20 | 21 | self.next(self.end) 22 | 23 | @step 24 | def end(self): 25 | print("Predicted class", self.model.predict([self.vector])[0]) 26 | 27 | if __name__ == '__main__': 28 | ClassifierPredictFlow() -------------------------------------------------------------------------------- /src/models.py: -------------------------------------------------------------------------------- 1 | from sklearn.neighbors import KNeighborsClassifier 2 | from sklearn import svm 3 | 4 | 5 | class BaseModel(): 6 | name = "BaseModel" 7 | 8 | def fit(self, X, y) -> None: 9 | raise NotImplementedError() 10 | 11 | def predict(self, X) -> None: 12 | raise NotImplementedError() 13 | 14 | def eval(self, X, y) -> None: 15 | raise NotImplementedError() 16 | 17 | 18 | class ModelKNN(BaseModel): 19 | name = "ModelKNN" 20 | 21 | def __init__(self) -> None: 22 | super().__init__() 23 | self.model = KNeighborsClassifier() 24 | 25 | def fit(self, X, y): 26 | self.model.fit(X, y) 27 | 28 | def predict(self, X): 29 | return self.model.predict(X) 30 | 31 | def eval(self, X, y): 32 | return self.model.score(X, y) 33 | 34 | 35 | class ModelSVM(BaseModel): 36 | name = "ModelSVM" 37 | 38 | def __init__(self) -> None: 39 | super().__init__() 40 | self.model = svm.SVC(kernel='poly') 41 | 42 | def fit(self, X, y): 43 | self.model.fit(X, y) 44 | 45 | def predict(self, X): 46 | return self.model.predict(X) 47 | 48 | def eval(self, X, y): 49 | return self.model.score(X, y) 50 | -------------------------------------------------------------------------------- /src/train_flow.py: -------------------------------------------------------------------------------- 1 | from metaflow import FlowSpec, step, conda_base, project, get_namespace, conda 2 | 3 | 4 | @project(name='iris_project') 5 | @conda_base(python='3.10.11', libraries={'scikit-learn': '1.5.1'}) 6 | class ClassifierTrainFlow(FlowSpec): 7 | 8 | @conda(libraries={"matplotlib": "3.9.1"}) 9 | @step 10 | def start(self): 11 | from io import BytesIO 12 | from sklearn import datasets 13 | from sklearn.model_selection import train_test_split 14 | from models import ModelKNN, ModelSVM 15 | import matplotlib.pyplot as plt 16 | 17 | print('NAMESPACE IS', get_namespace()) 18 | 19 | self.models = [ModelKNN(), ModelSVM()] 20 | 21 | X, y = datasets.load_iris(return_X_y=True) 22 | self.train_data, self.test_data, self.train_labels, self.test_labels = \ 23 | train_test_split(X, y, test_size=0.2, random_state=0) 24 | 25 | plt.scatter(self.train_data[:, 0], self.train_data[:, 1]) 26 | fig = plt.gcf() 27 | buf = BytesIO() 28 | fig.savefig(buf) 29 | self.vis = buf.getvalue() 30 | 31 | self.next(self.train, foreach="models") 32 | 33 | @step 34 | def train(self): 35 | self.model = self.input 36 | self.model.fit(self.train_data, self.train_labels) 37 | 38 | self.next(self.eval) 39 | 40 | @step 41 | def eval(self): 42 | self.score = self.model.eval(self.test_data, self.test_labels) 43 | 44 | self.next(self.select_best_model) 45 | 46 | @step 47 | def select_best_model(self, inputs): 48 | self.results = sorted( 49 | [(inp.model, inp.model.name, inp.score) for inp in inputs], 50 | key=lambda x: -x[-1], 51 | ) 52 | self.model = self.results[0][0] 53 | self.next(self.end) 54 | 55 | @step 56 | def end(self): 57 | print('Scores:') 58 | print('\n'.join('%s %f' % res[1:] for res in self.results)) 59 | 60 | if __name__ == '__main__': 61 | ClassifierTrainFlow() -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.toptal.com/developers/gitignore/api/python,visualstudiocode,venv 2 | # Edit at https://www.toptal.com/developers/gitignore?templates=python,visualstudiocode,venv 3 | 4 | ### Python ### 5 | # Byte-compiled / optimized / DLL files 6 | __pycache__/ 7 | *.py[cod] 8 | *$py.class 9 | 10 | # C extensions 11 | *.so 12 | 13 | # Distribution / packaging 14 | .Python 15 | build/ 16 | develop-eggs/ 17 | dist/ 18 | downloads/ 19 | eggs/ 20 | .eggs/ 21 | lib/ 22 | lib64/ 23 | parts/ 24 | sdist/ 25 | var/ 26 | wheels/ 27 | share/python-wheels/ 28 | *.egg-info/ 29 | .installed.cfg 30 | *.egg 31 | MANIFEST 32 | 33 | # PyInstaller 34 | # Usually these files are written by a python script from a template 35 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 36 | *.manifest 37 | *.spec 38 | 39 | # Installer logs 40 | pip-log.txt 41 | pip-delete-this-directory.txt 42 | 43 | # Unit test / coverage reports 44 | htmlcov/ 45 | .tox/ 46 | .nox/ 47 | .coverage 48 | .coverage.* 49 | .cache 50 | nosetests.xml 51 | coverage.xml 52 | *.cover 53 | *.py,cover 54 | .hypothesis/ 55 | .pytest_cache/ 56 | cover/ 57 | 58 | # Translations 59 | *.mo 60 | *.pot 61 | 62 | # Django stuff: 63 | *.log 64 | local_settings.py 65 | db.sqlite3 66 | db.sqlite3-journal 67 | 68 | # Flask stuff: 69 | instance/ 70 | .webassets-cache 71 | 72 | # Scrapy stuff: 73 | .scrapy 74 | 75 | # Sphinx documentation 76 | docs/_build/ 77 | 78 | # PyBuilder 79 | .pybuilder/ 80 | target/ 81 | 82 | # Jupyter Notebook 83 | .ipynb_checkpoints 84 | 85 | # IPython 86 | profile_default/ 87 | ipython_config.py 88 | 89 | # pyenv 90 | # For a library or package, you might want to ignore these files since the code is 91 | # intended to run in multiple environments; otherwise, check them in: 92 | # .python-version 93 | 94 | # pipenv 95 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 96 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 97 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 98 | # install all needed dependencies. 99 | #Pipfile.lock 100 | 101 | # poetry 102 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. 103 | # This is especially recommended for binary packages to ensure reproducibility, and is more 104 | # commonly ignored for libraries. 105 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control 106 | #poetry.lock 107 | 108 | # pdm 109 | # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. 110 | #pdm.lock 111 | # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it 112 | # in version control. 113 | # https://pdm.fming.dev/#use-with-ide 114 | .pdm.toml 115 | 116 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm 117 | __pypackages__/ 118 | 119 | # Celery stuff 120 | celerybeat-schedule 121 | celerybeat.pid 122 | 123 | # SageMath parsed files 124 | *.sage.py 125 | 126 | # Environments 127 | .env 128 | .venv 129 | env/ 130 | venv/ 131 | ENV/ 132 | env.bak/ 133 | venv.bak/ 134 | 135 | # Spyder project settings 136 | .spyderproject 137 | .spyproject 138 | 139 | # Rope project settings 140 | .ropeproject 141 | 142 | # mkdocs documentation 143 | /site 144 | 145 | # mypy 146 | .mypy_cache/ 147 | .dmypy.json 148 | dmypy.json 149 | 150 | # Pyre type checker 151 | .pyre/ 152 | 153 | # pytype static type analyzer 154 | .pytype/ 155 | 156 | # Cython debug symbols 157 | cython_debug/ 158 | 159 | # PyCharm 160 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can 161 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore 162 | # and can be added to the global gitignore or merged into this file. For a more nuclear 163 | # option (not recommended) you can uncomment the following to ignore the entire idea folder. 164 | #.idea/ 165 | 166 | ### Python Patch ### 167 | # Poetry local configuration file - https://python-poetry.org/docs/configuration/#local-configuration 168 | poetry.toml 169 | 170 | # ruff 171 | .ruff_cache/ 172 | 173 | # LSP config files 174 | pyrightconfig.json 175 | 176 | ### venv ### 177 | # Virtualenv 178 | # http://iamzed.com/2009/05/07/a-primer-on-virtualenv/ 179 | [Bb]in 180 | [Ii]nclude 181 | [Ll]ib 182 | [Ll]ib64 183 | [Ll]ocal 184 | [Ss]cripts 185 | pyvenv.cfg 186 | pip-selfcheck.json 187 | 188 | ### VisualStudioCode ### 189 | .vscode/* 190 | !.vscode/settings.json 191 | !.vscode/tasks.json 192 | !.vscode/launch.json 193 | !.vscode/extensions.json 194 | !.vscode/*.code-snippets 195 | 196 | # Local History for Visual Studio Code 197 | .history/ 198 | 199 | # Built Visual Studio Code Extensions 200 | *.vsix 201 | 202 | ### VisualStudioCode Patch ### 203 | # Ignore all local history of files 204 | .history 205 | .ionide 206 | 207 | # End of https://www.toptal.com/developers/gitignore/api/python,visualstudiocode,venv 208 | 209 | 210 | .metaflow -------------------------------------------------------------------------------- /check.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 40, 6 | "metadata": {}, 7 | "outputs": [], 8 | "source": [ 9 | "import sys\n", 10 | "sys.path.append(\"./src\")\n", 11 | "\n", 12 | "from metaflow import Flow\n", 13 | "from IPython.display import Image" 14 | ] 15 | }, 16 | { 17 | "cell_type": "code", 18 | "execution_count": 41, 19 | "metadata": {}, 20 | "outputs": [], 21 | "source": [ 22 | "run = Flow('ClassifierTrainFlow').latest_run" 23 | ] 24 | }, 25 | { 26 | "cell_type": "code", 27 | "execution_count": 42, 28 | "metadata": {}, 29 | "outputs": [ 30 | { 31 | "data": { 32 | "text/plain": [ 33 | "" 34 | ] 35 | }, 36 | "execution_count": 42, 37 | "metadata": {}, 38 | "output_type": "execute_result" 39 | } 40 | ], 41 | "source": [ 42 | "run['start'].task.data" 43 | ] 44 | }, 45 | { 46 | "cell_type": "code", 47 | "execution_count": 43, 48 | "metadata": {}, 49 | "outputs": [ 50 | { 51 | "data": { 52 | "image/png": "iVBORw0KGgoAAAANSUhEUgAAAoAAAAHgCAYAAAA10dzkAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjkuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/TGe4hAAAACXBIWXMAAA9hAAAPYQGoP6dpAAA+2UlEQVR4nO3df3Bc1X3//9dKsr12kDeRG1nr2uCFgMlagfhHVJRgp9TBOCL6tpNMpmGQwaHx2IqdAC6NI5KJqk6JSksnJNOOwBpCA2rgM1M7GdS4AifgH8nYI4zsYLGO4ykr2x+yqkqUrMQPWUh7v3/oI0Wr3ZV2V6u7d+95Pmb2j706V/d97rk/3nt/nOOxLMsSAAAAjFGU7wAAAABgLxJAAAAAw5AAAgAAGIYEEAAAwDAkgAAAAIYhAQQAADAMCSAAAIBhSAABAAAMQwIIAABgGBJAAAAAw5AAAgAAGIYEEAAAwDAkgAAAAIYhAQQAADAMCSAAAIBhSAABAAAMQwIIAABgGBJAAAAAw5AAAgAAGIYEEAAAwDAkgAAAAIYhAQQAADAMCSAAAIBhSAABAAAMQwIIAABgGBJAAAAAw5AAAgAAGIYEEAAAwDAkgAAAAIYhAQQAADAMCSAAAIBhSAABAAAMQwIIAABgGBJAAAAAw5AAAgAAGIYEEAAAwDAkgAAAAIYhAQQAADAMCSAAAIBhSAABAAAMQwIIAABgGBJAAAAAw5AAAgAAGIYEEAAAwDAkgAAAAIYhAQQAADAMCSAAAIBhSAABAAAMQwIIAABgGBJAAAAAw5AAAgAAGIYEEAAAwDAkgAAAAIYhAQQAADAMCSAAAIBhXJkANjc3y+Px6L777ktZ5vDhw/J4PAmfX/3qV/YFCgAAkAcl+Q4g115++WXt27dPN9xwQ1rlz507p8WLF098/+AHP5j2smKxmH7zm9+otLRUHo8n41gBAID9LMvS4OCgli1bpqIiV14Lm5GrEsC33npLd955p1pbW/X3f//3ac1TXl6u97///Vkt7ze/+Y1WrFiR1bwAACC/Ll26pOXLl+c7jLxwVQK4a9cu3X777frUpz6VdgK4Zs0aDQ0NKRgM6pvf/KZuueWWlGUvX76sy5cvT3y3LEvS2AY0+SoiAABwroGBAa1YsUKlpaX5DiVvXJMAPvvss+rq6tLLL7+cVnm/3699+/Zp3bp1unz5sp5++mlt2rRJhw8f1saNG5PO09zcrKampoTpixcvJgEEAKDAmPz4lscav4xVwC5duqT169frhRde0I033ihJ+tM//VN99KMf1aOPPpr2/6mtrZXH49Fzzz2X9O9TrwCO/4KIRqMkgAAAFIiBgQH5fD6jz9+uePLxlVdeUV9fn9atW6eSkhKVlJToyJEj+t73vqeSkhKNjo6m9X9uuukmnT9/PuXfFyxYMHG1j6t+AACgULniFvCmTZt05syZuGlf/OIXdf3112vv3r0qLi5O6/+cOnVKfr9/LkIEAABwDFckgKWlpaqsrIyb9r73vU9LliyZmN7Q0KA33nhDTz31lCTp0Ucf1cqVK7V69WoNDw+rra1N+/fv1/79+22PHwAAwE6uSADTEYlEdPHixYnvw8PDeuCBB/TGG29o4cKFWr16tX7yk5+opqYmj1ECAADMPVe8BJIvPEQKAEDh4fztkpdAAAAAkD4SQAAAAMMY8wwggLk3GrPUGe5X3+CQyku9qgqUqbjI3I5WAcCpSAAB5ERHd0RN7SFFokMT0/w+rxprg9pSSfdKAOAk3AIGMGsd3RHVt3XFJX+S1BsdUn1blzq6I3mKDACQDAkggFkZjVlqag8pWXcC49Oa2kMajdHhAAA4BQkggFnpDPcnXPmbzJIUiQ6pM9xvX1AAgGmRAAKYlb7B1MlfNuUAAHOPBBDArJSXenNaDgAw90gAAcxKVaBMfp9XqTp78WjsbeCqQJmdYQEApkECCGBWios8aqwNSlJCEjj+vbE2SH+AAOAgJIAAZm1LpV8tdWtV4Yu/zVvh86qlbi39AAKAw9ARNICc2FLp163BCkYCAYACQAIIIGeKizyqvmZJvsMAAMyAW8AAAACGIQEEAAAwDAkgAACAYUgAAQAADEMCCAAAYBgSQAAAAMOQAAIAABiGBBAAAMAwJIAAAACGIQEEAAAwDAkgAACAYUgAAQAADEMCCAAAYBgSQAAAAMOQAAIAABiGBBAAAMAwJIAAAACGIQEEAAAwDAkgAACAYUgAAQAADEMCCAAAYBgSQAAAAMOQAAIAABiGBBAAAMAwJIAAAACGIQEEAAAwDAkgAACAYUgAAQAADEMCCAAAYBgSQAAAAMOU5DsAwG1GY5Y6w/3qGxxSealXVYEyFRd58h0WAAATSACBHOrojqipPaRIdGhimt/nVWNtUFsq/XmMDACAP+AWMJAjHd0R1bd1xSV/ktQbHVJ9W5c6uiN5igwAgHgkgEAOjMYsNbWHZCX52/i0pvaQRmPJSgAAYC8SQCAHOsP9CVf+JrMkRaJD6gz32xcUAAApkAACOdA3mDr5y6YcAABziQQQyIHyUm9OywEAMJdIAIEcqAqUye/zKlVnLx6NvQ1cFSizMywAAJIiAQRyoLjIo8baoCQlJIHj3xtrg/QHCABwBBJAIEe2VPrVUrdWFb7427wVPq9a6tbSDyAAwDHoCBrIoS2Vft0arGAkEACAo5EAAjlWXORR9TVL8h0GAAApcQsYAADAMCSAAAAAhuEWMIC8GY1ZPC8JAHngyiuAzc3N8ng8uu+++6Ytd+TIEa1bt05er1dXX321HnvsMXsCBKCO7ohufvhF3dF6Qvc+e1p3tJ7QzQ+/qI7uSL5DAwDXc10C+PLLL2vfvn264YYbpi0XDodVU1OjDRs26NSpU3rwwQf11a9+Vfv377cpUsBcHd0R1bd1JYyf3BsdUn1bF0kgAMwxVyWAb731lu688061trbqAx/4wLRlH3vsMV155ZV69NFH9eEPf1hf+tKXdM899+iRRx6xKVrATKMxS03tIVlJ/jY+rak9pNFYshIAgFxwVQK4a9cu3X777frUpz41Y9njx49r8+bNcdNuu+02nTx5Uu+9917SeS5fvqyBgYG4D4DMdIb7E678TWZJikSH1Bnuty8oADCMaxLAZ599Vl1dXWpubk6rfG9vr5YuXRo3benSpRoZGdGbb76ZdJ7m5mb5fL6Jz4oVK2YdN2CavsHUyV825QAAmXNFAnjp0iXde++9amtrk9frnXmG/8fjiX/b0LKspNPHNTQ0KBqNTnwuXbqUfdCAocpL09tH0y0HAMicK7qBeeWVV9TX16d169ZNTBsdHdXRo0f1L//yL7p8+bKKi4vj5qmoqFBvb2/ctL6+PpWUlGjJkuSjOCxYsEALFizIfQUAg1QFyuT3edUbHUr6HKBHY+MnVwXK7A4NAIzhiiuAmzZt0pkzZ3T69OmJz/r163XnnXfq9OnTCcmfJFVXV+vQoUNx01544QWtX79e8+bNsyt0wDjFRR411gYljSV7k41/b6wN0h8gAMwhVySApaWlqqysjPu8733v05IlS1RZWSlp7PbtXXfdNTHPzp07deHCBe3Zs0dnz57V97//fT3xxBN64IEH8lUNwBhbKv1qqVurCl/8bd4Kn1ctdWu1pdKfp8gAwAyuuAWcjkgkoosXL058DwQCOnjwoO6//37967/+q5YtW6bvfe97+tznPpfHKAFzbKn069ZgBSOBAEAeeKzxNx+QsYGBAfl8PkWjUS1evDjf4QAAgDRw/nbJLWAAAACkjwQQAADAMMY8Awi4yWjM4tk5AEDWSACBAtPRHVFTeyhuODW/z6vG2iBvzwIA0sItYKCAdHRHVN/WlTCWbm90SPVtXerojuQpMgBAISEBBArEaMxSU3so6egZ49Oa2kMajfFiPwBgeiSAQIHoDPcnXPmbzJIUiQ6pM9xvX1AAgIJEAggUiL7B1MlfNuUAAOYiAQQKRHmpd+ZCGZQDAJiLBBAoEFWBMvl9XqXq7MWjsbeBqwJldoYFAChAJIBAgSgu8qixNihJCUng+PfG2iD9AQIAZkQCCBSQLZV+tdStVYUv/jZvhc+rlrq19AMIAEgLHUEDBWZLpV+3BisYCQQAkDUSQKAAFRd5VH3NknyHAQAoUNwCBgAAMAwJIAAAgGG4BQzMYHgkpqeP9+hC/zu6qmyRtlav1PwSfjslMxqzeDYRcDk79nOOJXOPBBCYRvPBkFqPhTV5eN2HDp7V9g0BNdQE8xeYA3V0R9TUHoobrs7v86qxNsjbyYBL2LGfcyyxB5cxgBSaD4b0+NH45E+SYpb0+NGwmg+G8hOYA3V0R1Tf1pUwVnFvdEj1bV3q6I7kKTIAuWLHfs6xxD4kgEASwyMxtR4LT1um9VhYwyMxmyJyrtGYpab2kKwkfxuf1tQe0ujUTBpAwbBjP+dYYi8SQCCJp4/3JFz5mypmjZUzXWe4P+HX+mSWpEh0SJ3hfvuCApBTduznHEvsRQIIJHGh/52clnOzvsHUB+xsygFwHjv2c44l9iIBBJK4qmxRTsu5WXmpd+ZCGZQD4Dx27OccS+xFAggksbV6pWbqcaDIM1bOdFWBMvl9XqVaXR6NvcFXFSizMywAOWTHfs6xxF4kgEAS80uKtH1DYNoy2zcE6A9QY8PSNdaOdYkz9cA9/r2xNkgfXkABs2M/51hiL85eQAoNNUHt2BhIuBJY5JF2bKQfwMm2VPrVUrdWFb74WzMVPq9a6tbSdxfgAnbs5xxL7OOxLIv3qbM0MDAgn8+naDSqxYsX5zsczBFGAkkfvfcD7ueGkUA4f5MAzgobEAAAhYfzN7eAAQAAjEMCCAAAYJiSfAcAOJ0Tn3fhWTsAwGyQAALT6OiOqKk9FDc8kd/nVWNtMGdvo2W6DDtiAgC4G7eAgRQ6uiOqb+tKGJuyNzqk+rYudXRHbF+GHTEBANyPBBBIYjRmqak9pGSvyI9Pa2oPaTSW/Uv0mS7DjpgAAGYgAQSS6Az3J1xlm8ySFIkOqTPcb9sy7IgJAGAGEkAgib7B1IlWNuVysQw7YgIAmIEEEEiivNQ7c6EMyuViGXbEBAAwAwkgkERVoEx+nzdhQPJxHo29eVsVKLNtGXbEBAAwAwkgkERxkUeNtUFJSki4xr831gZn1fdepsuwIyYAgBlIAIEUtlT61VK3VhW++FuqFT6vWurW5qTPvUyXYUdMAAD381iWRZ8RWWIwaTMwEggAuAvnb0YCAWZUXORR9TVLHLUMO2ICALgXt4ABAAAMQwIIAABgGG4Bwyg8OwcgVzieoJCRAMIYHd0RNbWH4oZT8/u8aqwN8vYsgIxwPEGh4xYwjNDRHVF9W1fCWLq90SHVt3WpozuSp8gAFBqOJ3ADEkC43mjMUlN7SMn6Oxqf1tQe0miMHpEATI/jCdyCBBCu1xnuT/ilPpklKRIdUme4376gABQkjidwCxJAuF7fYOqDdTblAJiL4wncggQQrlde6p25UAblAJiL4wncggQQrlcVKJPf51Wqzhk8Gnt7rypQZmdYAAoQxxO4BQkgXK+4yKPG2qAkJRy0x7831gbpvwvAjDiewC1IAGGELZV+tdStVYUv/rZMhc+rlrq19NsFIG0cT+AGHsuyeFc9SwMDA/L5fIpGo1q8eHG+w0Ea6LkfQK5wPClcnL8ZCQSGKS7yqPqaJfkOA4ALcDxBIeMWMAAAgGFIAAEAAAzDLWAYJZtndoZHYnr6eI8u9L+jq8oWaWv1Ss0vSf3byanPBTk1LgCA/VyRALa0tKilpUU9PT2SpNWrV+tb3/qWPv3pTyctf/jwYd1yyy0J08+ePavrr79+LkNFHnV0R9TUHoobxsnv86qxNpjyrb3mgyG1Hgtr8rCeDx08q+0bAmqoCeZkGXZwalwAgPxwxS3g5cuX6x/+4R908uRJnTx5Un/2Z3+mP//zP9drr7027Xznzp1TJBKZ+Fx77bU2RQy7dXRHVN/WlTCGZ290SPVtXerojiTM03wwpMePxid/khSzpMePhtV8MDTrZdjBqXEBAPLHFQlgbW2tampqdN111+m6667TQw89pCuuuEInTpyYdr7y8nJVVFRMfIqLi22KGHYajVlqag8pWX9H49Oa2kManZTpDY/E1HosPO3/bT0W1vBILOtl2MGpcQEA8ssVCeBko6OjevbZZ/X222+rurp62rJr1qyR3+/Xpk2b9NJLL834vy9fvqyBgYG4D5yvM9yfcPVrMktSJDqkznD/xLSnj/ckXPmbKmaNlct2GXZwalwAgPxyTQJ45swZXXHFFVqwYIF27typH/3oRwoGE5/RkiS/3699+/Zp//79OnDggFatWqVNmzbp6NGj0y6jublZPp9v4rNixYq5qApyrG8wdQKUqtyF/nfSmme8XDbLsINT4wIA5JcrXgKRpFWrVun06dP6/e9/r/379+vuu+/WkSNHkiaBq1at0qpVqya+V1dX69KlS3rkkUe0cePGlMtoaGjQnj17Jr4PDAyQBBaA8lLvzIWmlLuqbFFa84yXy2YZdnBqXACA/HLNFcD58+frQx/6kNavX6/m5mbdeOON+u53v5v2/DfddJPOnz8/bZkFCxZo8eLFcR84X1WgTH6fN2Hg9nEejb0RWxUom5i2tXqlZuohpcgzVi7bZdjBqXEBAPLLNQngVJZl6fLly2mXP3XqlPx+usNwo+Iijxprx64ET02Exr831gbj+sSbX1Kk7RsC0/7f7RsCE/0BZrMMOzg1LgBAfrkiAXzwwQd17Ngx9fT06MyZM/rGN76hw4cP684775Q0duv2rrvumij/6KOP6sc//rHOnz+v1157TQ0NDdq/f792796drypgjm2p9Kulbq0qfPG3Oit8XrXUrU3aF15DTVA7NgYSrgQWeaQdGxP7AcxmGXZwalwAgPxxxTOA//M//6OtW7cqEonI5/PphhtuUEdHh2699VZJUiQS0cWLFyfKDw8P64EHHtAbb7yhhQsXavXq1frJT36impqafFUBNthS6detwYqMRsNoqAnqrzdfn/ZIINksww5OjQsAkB8ey7LoACxLAwMD8vl8ikajPA8IAECB4PztklvAAAAASB8JIAAAgGFc8Qwg5t5ozJrz58fsWIYdcdlRj+GRWNrPJZrOru3KidsJAKRCAogZdXRH1NQeihtSzO/zqrE2mLM3SO1Yhh1x2VGP5oMhtR4Lxw1V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Bz+oz33uc3aEm1OZ1H1cobf57373O33iE5/QLbfcov/6r/9SeXm5/vu//1vvf//7U87jlnbPpu7jCr3dX375ZY2Ojk587+7u1q233qrPf/7zScu7pc0zrfe4Qm9vSXr44Yf12GOP6Qc/+IFWr16tkydP6otf/KJ8Pp/uvffepPO4pd3nhAXXGhwctK699lrr0KFD1ic/+Unr3nvvTVn2pZdesiRZv/vd72yLby40NjZaN954Y9rlv/a1r1nXX3993LQdO3ZYN910U44jm3uZ1t0tbb53717r5ptvzmget7R7NnV3S7tPde+991rXXHONFYvFkv7dLW0+1Uz1dlN733777dY999wTN+2zn/2sVVdXl3Iet7Z7LnAL2MV27dql22+/XZ/61KfSnmfNmjXy+/3atGmTXnrppTmMbu6cP39ey5YtUyAQ0Be+8AW9/vrrKcseP35cmzdvjpt222236eTJk3rvvffmOtScy6Tu4wq9zZ977jmtX79en//851VeXq41a9aotbV12nnc0u7Z1H1cobf7ZMPDw2pra9M999wjj8eTtIxb2nyydOo9zg3tffPNN+tnP/uZfv3rX0uSfvnLX+rnP/+5ampqUs7jxnbPFRJAl3r22WfV1dWl5ubmtMr7/X7t27dP+/fv14EDB7Rq1Spt2rRJR48eneNIc+tP/uRP9NRTT+n5559Xa2urent79fGPf1y//e1vk5bv7e3V0qVL46YtXbpUIyMjevPNN+0IOWcyrbtb2vz1119XS0uLrr32Wj3//PPauXOnvvrVr+qpp55KOY9b2j2burul3Sf78Y9/rN///vfatm1byjJuafPJ0qm3m9p77969uuOOO3T99ddr3rx5WrNmje677z7dcccdKedxY7vnTL4vQSL3Ll68aJWXl1unT5+emDbTLeBkPvOZz1i1tbU5js5eb731lrV06VLrn//5n5P+/dprr7W+/e1vx037+c9/bkmyIpGIHSHOmZnqnkwhtvm8efOs6urquGlf+cpXpr3F45Z2z6buyRRiu0+2efNm6zOf+cy0ZdzS5pOlU+9kCrW9n3nmGWv58uXWM888Y7366qvWU089ZZWVlVn/9m//lnIeN7Z7rnAF0IVeeeUV9fX1ad26dSopKVFJSYmOHDmi733veyopKYl7gHg6N910k86fPz/H0c6t973vffrIRz6Ssh4VFRXq7e2Nm9bX16eSkhItWbLEjhDnzEx1T6YQ29zv9ysYDMZN+/CHP6yLFy+mnMct7Z5N3ZMpxHYfd+HCBf30pz/Vl770pWnLuaXNx6Vb72QKtb3/5m/+Rl//+tf1hS98QR/5yEe0detW3X///dPe6XJbu+cSCaALbdq0SWfOnNHp06cnPuvXr9edd96p06dPp/VmqCSdOnVKfr9/jqOdW5cvX9bZs2dT1qO6ulqHDh2Km/bCCy9o/fr1mjdvnh0hzpmZ6p5MIbb5Jz7xCZ07dy5u2q9//WtdddVVKedxS7tnU/dkCrHdxz355JMqLy/X7bffPm05t7T5uHTrnUyhtvc777yjoqL4tKW4uHjabmDc1u45le9LkLDH1FvAX//6162tW7dOfP/Od75j/ehHP7J+/etfW93d3dbXv/51S5K1f//+PESbvb/+67+2Dh8+bL3++uvWiRMnrM985jNWaWmp1dPTY1lWYr1ff/11a9GiRdb9999vhUIh64knnrDmzZtn/cd//Ee+qpC1TOvuljbv7Oy0SkpKrIceesg6f/689e///u/WokWLrLa2tokybm33bOrulna3LMsaHR21rrzySmvv3r0Jf3Nrm1tWZvV2U3vffffd1h//8R9b//mf/2mFw2HrwIED1h/90R9ZX/va1ybKuLndc40E0BBTE8C7777b+uQnPznx/eGHH7auueYay+v1Wh/4wAesm2++2frJT35if6Cz9Jd/+ZeW3++35s2bZy1btsz67Gc/a7322msTf59ab8uyrMOHD1tr1qyx5s+fb61cudJqaWmxOercyLTubmlzy7Ks9vZ2q7Ky0lqwYIF1/fXXW/v27Yv7u5vbPdO6u6ndn3/+eUuSde7cuYS/ubnNM6m3m9p7YGDAuvfee60rr7zS8nq91tVXX2194xvfsC5fvjxRxs3tnmsey7KsfF6BBAAAgL14BhAAAMAwJIAAAACGIQEEAAAwDAkgAACAYUgAAQAADEMCCAAAYBgSQAAAAMOQAAIAABiGBBAAAMAwJIAAAACGIQEEAAAwDAkgAACAYUgAAQAADEMCCAAAYBgSQAAAAMOQAAIAABiGBBAAAMAwJIAAAACGIQEEAAAwDAkgAACAYUgAAQAADEMCCAAAYBgSQAAAAMOQAAIAABiGBBAAAMAwJIAAAACGIQEEAAAwDAkgAACAYUgAAQAADEMCCAAAYBgSQAAAAMP8/2VlusMmbiWPAAAAAElFTkSuQmCC", 53 | "text/plain": [ 54 | "" 55 | ] 56 | }, 57 | "execution_count": 43, 58 | "metadata": {}, 59 | "output_type": "execute_result" 60 | } 61 | ], 62 | "source": [ 63 | "Image(run['start'].task.data.vis)" 64 | ] 65 | }, 66 | { 67 | "cell_type": "code", 68 | "execution_count": 44, 69 | "metadata": {}, 70 | "outputs": [], 71 | "source": [ 72 | "model = run['eval'].task.data.model" 73 | ] 74 | }, 75 | { 76 | "cell_type": "code", 77 | "execution_count": 45, 78 | "metadata": {}, 79 | "outputs": [ 80 | { 81 | "data": { 82 | "text/plain": [ 83 | "array([2])" 84 | ] 85 | }, 86 | "execution_count": 45, 87 | "metadata": {}, 88 | "output_type": "execute_result" 89 | } 90 | ], 91 | "source": [ 92 | "model.predict([[5.8, 2.8, 5.1, 2.4]])" 93 | ] 94 | }, 95 | { 96 | "cell_type": "code", 97 | "execution_count": null, 98 | "metadata": {}, 99 | "outputs": [], 100 | "source": [] 101 | } 102 | ], 103 | "metadata": { 104 | "kernelspec": { 105 | "display_name": "venv", 106 | "language": "python", 107 | "name": "python3" 108 | }, 109 | "language_info": { 110 | "codemirror_mode": { 111 | "name": "ipython", 112 | "version": 3 113 | }, 114 | "file_extension": ".py", 115 | "mimetype": "text/x-python", 116 | "name": "python", 117 | "nbconvert_exporter": "python", 118 | "pygments_lexer": "ipython3", 119 | "version": "3.10.11" 120 | } 121 | }, 122 | "nbformat": 4, 123 | "nbformat_minor": 2 124 | } 125 | --------------------------------------------------------------------------------