From 3330102f1e031300922bbc82d1c561f2e6c05f1c Mon Sep 17 00:00:00 2001 From: Jakub Date: Mon, 15 Apr 2019 13:32:03 +0200 Subject: [PATCH] Neptune 3.0 (#27) (#28) * adjusted to neptune-client * dropped tests --- README.md | 4 - docs/conf.py | 4 +- docs/examples/examples_index.rst | 2 - .../interactive_compare_experiments.ipynb | 2 +- docs/examples/local_notebooks.ipynb | 206 ------- .../local_notebooks_11_0.png | Bin 22104 -> 0 bytes docs/examples/log_matplotlib.ipynb | 127 ---- docs/examples/log_model_diagnostics.ipynb | 41 +- docs/examples/monitor_fastai.ipynb | 26 +- docs/examples/monitor_lgbm.ipynb | 60 +- docs/examples/project_progress.ipynb | 4 +- docs/examples/run_hyperparameter_search.ipynb | 544 ------------------ docs/examples/sync_with_json.ipynb | 55 +- docs/examples/telegram_bot.ipynb | 23 +- docs/index.rst | 7 +- docs/overview.rst | 52 -- docs/user_guide/monitoring/notebooks.rst | 6 - docs_requirements.txt | 18 +- neptunecontrib/api/utils.py | 6 +- neptunecontrib/bots/telegram_bot.py | 11 +- neptunecontrib/hpo/utils.py | 93 +-- neptunecontrib/monitoring/fastai.py | 33 +- neptunecontrib/monitoring/lightgbm.py | 9 +- neptunecontrib/monitoring/notebooks.py | 264 --------- neptunecontrib/monitoring/reporting.py | 119 ++-- neptunecontrib/monitoring/skopt.py | 137 +++-- neptunecontrib/monitoring/utils.py | 66 --- neptunecontrib/sync/with_json.py | 126 ++-- requirements.txt | 3 +- setup.py | 2 +- test_requirements.txt | 2 +- tests/neptunecontrib/__init__.py | 15 + .../monitoring/test_config.yaml | 3 - .../monitoring/test_lightgbm.py | 100 ---- .../monitoring/test_monitoring_utils.py | 54 -- .../monitoring/test_notebooks.py | 48 -- tests/neptunecontrib/sync/__init__.py | 15 + tests/neptunecontrib/sync/json_exp.json | 1 + .../test_with_json.py} | 20 +- 39 files changed, 471 insertions(+), 1837 deletions(-) delete mode 100644 docs/examples/local_notebooks.ipynb delete mode 100644 docs/examples/local_notebooks_files/local_notebooks_11_0.png delete mode 100644 docs/examples/log_matplotlib.ipynb delete mode 100644 docs/examples/run_hyperparameter_search.ipynb delete mode 100644 docs/user_guide/monitoring/notebooks.rst delete mode 100644 neptunecontrib/monitoring/notebooks.py create mode 100644 tests/neptunecontrib/__init__.py delete mode 100644 tests/neptunecontrib/monitoring/test_config.yaml delete mode 100644 tests/neptunecontrib/monitoring/test_lightgbm.py delete mode 100644 tests/neptunecontrib/monitoring/test_monitoring_utils.py delete mode 100644 tests/neptunecontrib/monitoring/test_notebooks.py create mode 100644 tests/neptunecontrib/sync/__init__.py create mode 100644 tests/neptunecontrib/sync/json_exp.json rename tests/neptunecontrib/{monitoring/objects_factory.py => sync/test_with_json.py} (52%) diff --git a/README.md b/README.md index ccbbe1b..d98d064 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,6 @@ # neptune-contrib [![Build Status](https://travis-ci.org/neptune-ml/neptune-contrib.svg?branch=master)](https://travis-ci.org/neptune-ml/neptune-contrib) -# Note -This lib is compatible with `neptune-cli` and some part may not work with `neptune-client`. -It will be updated for `neptune-client` soon (ETA `4.7.2019`). - # Documentation See [neptune-contrib documentation site](https://neptune-contrib.readthedocs.io) diff --git a/docs/conf.py b/docs/conf.py index 510f86e..38d2994 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -27,9 +27,9 @@ author = 'Neptune Dev Team' # The short X.Y version -version = '0.2' +version = '0.4' # The full version, including alpha/beta/rc tags -release = '0.2.0' +release = '0.4.0' # -- General configuration --------------------------------------------------- diff --git a/docs/examples/examples_index.rst b/docs/examples/examples_index.rst index 2d9a873..c2580c7 100644 --- a/docs/examples/examples_index.rst +++ b/docs/examples/examples_index.rst @@ -1,12 +1,10 @@ .. toctree:: Interactive experiment run comparison Hyper parameter comparison - Run skopt/hyperopt hyperparameter sweep Log model diagnostics Monitor lightGBM training Monitor fast.ai training Log matplotlib charts to neptune - Work with local notebooks Visualize project progress Sync experiments with Neptune via json file Telegram bot diff --git a/docs/examples/interactive_compare_experiments.ipynb b/docs/examples/interactive_compare_experiments.ipynb index 7c5a358..2661fce 100644 --- a/docs/examples/interactive_compare_experiments.ipynb +++ b/docs/examples/interactive_compare_experiments.ipynb @@ -186,7 +186,7 @@ ], "metadata": { "kernelspec": { - "display_name": "neptunecontrib_py36", + "display_name": "scraping", "language": "python", "name": "python3" }, diff --git a/docs/examples/local_notebooks.ipynb b/docs/examples/local_notebooks.ipynb deleted file mode 100644 index 8c4515e..0000000 --- a/docs/examples/local_notebooks.ipynb +++ /dev/null @@ -1,206 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Local notebook workflow\n", - "## Prerequisites\n", - "### Neptune configuration file neptune.yaml\n", - "\n", - "```yaml\n", - "project: neptune-ml/neptune-examples\n", - " \n", - "parameters:\n", - " power: 3\n", - "```\n", - "### Offline neptune context" - ] - }, - { - "cell_type": "code", - "execution_count": 79, - "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "neptune: Executing in Offline Mode.\n" - ] - } - ], - "source": [ - "import neptune\n", - "\n", - "ctx = neptune.Context()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Typical Problems\n", - "\n", - "Working with on neptune code in local notebooks can be tricky.\n", - "\n", - "For example, normally you cannot access hyperparameters with:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "ctx.params" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "or use the `.channel_send()` method:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "ctx.channel_send('random_channel', i, 2 + i**2)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Create a local notebook Context wrapper\n", - "Fortunatelly there is a simple solution to those problems.\n", - "Use the `LocalNotebookContext` object from the `neptune-contrib` package:" - ] - }, - { - "cell_type": "code", - "execution_count": 80, - "metadata": {}, - "outputs": [], - "source": [ - "from neptunecontrib.monitoring.notebooks import LocalNotebookContext\n", - "ctx = NotebookContext(ctx, config_filepath='neptune.yaml') " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "To instantiate it you need to pass the \"normal\" Neptune `Context` object and the path to your neptune config file.\n", - "\n", - "## Access `.params` \n", - "Now your params are easily available:" - ] - }, - { - "cell_type": "code", - "execution_count": 81, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "AttrDict({'power': 3})" - ] - }, - "execution_count": 81, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "ctx.params" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Use `.channel_send` \n", - "And you can monitor your numerical channels in the notebook output with no problems!" - ] - }, - { - "cell_type": "code", - "execution_count": 82, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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BYwdO5b169cTHR3Ne++9hzGGuLg47r//fgoKCggLC2PWrFm0b9+eUaNGcdZZZ/HLL78wevRoHnjggf/53EmTJtG6dWvi4+MZNGgQ1157Lffddx+HDx+mcePGzJw5k169ejFr1iwWL17MoUOH2LZtG1dddRXPPvssADNnzuSf//wn7du3p2fPnjRq1AiAlJQUbrnlFjIyMmjTpg0zZ86kc+fOTJo0icaNG7N582ZSUlKYOXMms2fPZsWKFcTExDBr1qwa/56++uorHn30UcrKyggLC+P7778HYOPGjYwaNeo3x3mAK6+8kt27d1NYWMi9997L1KlTgYqrq/fee+//nEtMmjSJFi1aEBsby759+3j22WcZO3YsAM899xyLFi2iqKiIq666qvKcSUQ8y7XTV9IxJJjXr49u0M+t60ZRPm/Lli3cdNNNxMfH88ILLxAbG0tiYiI//vgjiYmJlePCwsL49ddfuf322yun9TzxxBOMGDGC+Ph4Ro8eza5duwCIi4tj5syZrFq1ipUrV/LWW28RHx8PQHJyMvfeey+JiYls3ryZefPm8fPPP/P888/zj3/8o8acTz31FC1btmTdunUkJiZy7rnnAnDw4EGGDh3K2rVrGTlyJG+99RZAZeEcHx/P+PHjKw+2AJs3b+brr79m9erVPPHEE5SUlACQlJTEnXfeyYYNGwgJCeHDDz8EYOrUqfznP/8hLi6O559/njvuuKOu/vpFxEe8uWwbv+7K5fIB7Z2OIm6uumPoQw89RLdu3UhISOC5554DID4+npdeeomNGzeyfft2fvnlF0pKSrj77rv54IMPiIuL45ZbbuGxxx6rfO/c3Fx+/PHHagvaI7Zu3cp3333HCy+8QO/evVm2bBnx8fE8+eSTPProo5XjEhISWLhwIevWrWPhwoXs3r2btLQ0pk2bxi+//MK3337Lxo0bK8ffdddd3HTTTSQmJnL99ddXFpoAOTk5/PDDD7z44otcccUV/PGPf2TDhg2sW7eOhISEanNmZGRw66238uGHH7J27Vref//9yudqOs7PmDGDuLg4YmNjeeWVV8jKygJqPpcASEtL4+eff+bzzz/n4YcfBuCbb74hKSmJ1atXk5CQQFxcHMuWLTv+P66IuJWSsnI2peXTMaRxg3/2ca/UeqrjfWtbX04//XSGDh0KwKJFi5g+fTqlpaWkpaWx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\n", - "text/plain": [ - "
" - ] - }, - "metadata": { - "needs_background": "light" - }, - "output_type": "display_data" - } - ], - "source": [ - "for i in range(100):\n", - " ctx.channel_send('random_channel', i, 2 + i**2)\n", - " ctx.channel_send('other_random_channel', y=5 + i**7)\n", - " ctx.channel_send('text_channel','blabla')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Run your local Notebook in Neptune\n", - "Once you are finished developing your model and you can run your notebook in a top-to-bottom manner \n", - "you can switch to running \"normal\" neptune job.\n", - "\n", - "### Convert to `.py` and run\n", - "Use `jupyter nbconvert` option:\n", - " \n", - "```bash\n", - "jupyter nbconvert --to script main.py; neptune run --config neptune.yaml main.py\n", - "```\n", - "\n", - "You should get something like this:\n", - "\n", - "![image](https://gist.githubusercontent.com/jakubczakon/f754769a39ea6b8fa9728ede49b9165c/raw/d7395f0dfdd9b1dbe1a93ecf03dfc901071ad472/local_nb_workflow.png)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "neptunecontrib_py36", - "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.6.8" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/docs/examples/local_notebooks_files/local_notebooks_11_0.png b/docs/examples/local_notebooks_files/local_notebooks_11_0.png deleted file mode 100644 index 2b7e865389025e56d26766905eacfbe250de84a5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 22104 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 diff --git a/docs/examples/log_matplotlib.ipynb b/docs/examples/log_matplotlib.ipynb deleted file mode 100644 index d006a05..0000000 --- a/docs/examples/log_matplotlib.ipynb +++ /dev/null @@ -1,127 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Log matplotlib figure to neptune\n", - "## Create figure\n", - "Create your `matplotlib.figure.Figure` object.\n", - "\n", - "It can be a confusion matrix, distrubution or anything else that you want to plot." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import matplotlib.pyplot as plt\n", - "import numpy as np\n", - "import seaborn as sns\n", - "fig = plt.figure(figsize=(16,12))\n", - "sns.distplot(np.random.random(100))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Convert to PIL\n", - "Neptune accepts `PIL.Image` objects so you need to convert it from figure.\n", - "\n", - "You can use the `neptunecontrib.monitoring.utils.fig2pil` helper function." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from neptunecontrib.monitoring.uitls import fig2pil\n", - "\n", - "pil_figure = fig2pil(fig)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Creatae `neptune.Image`\n", - "Now you can create your `neptune.Image` object. \n", - "Specify`name` and `description`." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import neptune\n", - "\n", - "neptune_figure = neptune.Image(name='chart', description='', data=pil_figure)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Log to neptune\n", - "Now you can log it to neptune with the `.channel_send` function." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "ctx = neptune.Context()\n", - "\n", - "ctx.channel_send('chart_logs', neptune_figure)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Explore in neptune\n", - "Go to the experiment to check it out.\n", - "For example you can [go to this experiment]().\n", - "\n", - "![image](https://gist.githubusercontent.com/jakubczakon/f754769a39ea6b8fa9728ede49b9165c/raw/6d6cf39a53bad438af2bcb89094bd78629884a8e/contrib_log_matplotlib.png)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "scraping", - "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.6.8" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/docs/examples/log_model_diagnostics.ipynb b/docs/examples/log_model_diagnostics.ipynb index a76273e..fe107d4 100644 --- a/docs/examples/log_model_diagnostics.ipynb +++ b/docs/examples/log_model_diagnostics.ipynb @@ -45,7 +45,8 @@ "source": [ "import neptune\n", "\n", - "ctx = neptune.Context()" + "neptune.init(project_qualified_name='USER_NAME/PROJECT_NAME')\n", + "neptune.create_experiment()" ] }, { @@ -63,7 +64,7 @@ "source": [ "from neptunecontrib.monitoring.reporting import send_binary_classification_report\n", "\n", - "send_binary_classification_report(ctx, y_test, y_test_pred, threshold=0.5)" + "send_binary_classification_report(y_test, y_test_pred, threshold=0.5)" ] }, { @@ -85,7 +86,7 @@ "source": [ "from neptunecontrib.monitoring.reporting import send_confusion_matrix\n", "\n", - "send_confusion_matrix(ctx, y_test, y_test_pred[:, 1] > 0.5)" + "send_confusion_matrix(y_test, y_test_pred[:, 1] > 0.5)" ] }, { @@ -154,6 +155,40 @@ "send_prediction_distribution(y_test, y_test_pred[:, 1])" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Stop Neptune experiment" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "neptune.stop()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "You can also put everything in the `with` block.\n", + "For example:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "with neptune.create_experiment():\n", + " send_prediction_distribution(y_test, y_test_pred[:, 1])" + ] + }, { "cell_type": "markdown", "metadata": {}, diff --git a/docs/examples/monitor_fastai.ipynb b/docs/examples/monitor_fastai.ipynb index 359f706..9294154 100644 --- a/docs/examples/monitor_fastai.ipynb +++ b/docs/examples/monitor_fastai.ipynb @@ -42,8 +42,11 @@ "source": [ "import neptune\n", "from neptunecontrib.monitoring.fastai import NeptuneMonitor\n", - "ctx = neptune.Context()\n", - "monitor = NeptuneMonitor(ctx=ctx)" + "\n", + "neptune.init(project_qualified_name='USER_NAME/PROJECT_NAME')\n", + "\n", + "with neptune.create_experiment():\n", + " monitor = NeptuneMonitor()" ] }, { @@ -59,10 +62,10 @@ "metadata": {}, "outputs": [], "source": [ - "learn = create_cnn(data, models.resnet18, \n", - " metrics=accuracy, \n", - " callbacks=[neptune_monitor])\n", - "learn.fit_one_cycle(20, 1e-2)" + " learn = create_cnn(data, models.resnet18, \n", + " metrics=accuracy, \n", + " callbacks=[neptune_monitor])\n", + " learn.fit_one_cycle(20, 1e-2)" ] }, { @@ -94,7 +97,7 @@ "import neptune\n", "from neptunecontrib.monitoring.fastai import NeptuneMonitor\n", "\n", - "ctx = neptune.Context()\n", + "neptune.init(project_qualified_name='USER_NAME/PROJECT_NAME')\n", "\n", "mnist = untar_data(URLs.MNIST_TINY)\n", "tfms = get_transforms(do_flip=False)\n", @@ -106,15 +109,16 @@ " .databunch()\n", " .normalize(imagenet_stats))\n", "\n", - "neptune_monitor = NeptuneMonitor(ctx=ctx)\n", - "learn = create_cnn(data, models.resnet18, metrics=accuracy, callbacks=[neptune_monitor])\n", - "learn.fit_one_cycle(20, 1e-2)" + "with neptune.create_experiment():\n", + " neptune_monitor = NeptuneMonitor()\n", + " learn = create_cnn(data, models.resnet18, metrics=accuracy, callbacks=[neptune_monitor])\n", + " learn.fit_one_cycle(20, 1e-2)" ] } ], "metadata": { "kernelspec": { - "display_name": "neptunecontrib py36", + "display_name": "scraping", "language": "python", "name": "python3" }, diff --git a/docs/examples/monitor_lgbm.ipynb b/docs/examples/monitor_lgbm.ipynb index bacbc38..2c654e6 100644 --- a/docs/examples/monitor_lgbm.ipynb +++ b/docs/examples/monitor_lgbm.ipynb @@ -49,7 +49,10 @@ "import neptune\n", "from neptunecontrib.monitoring.lightgbm import neptune_monitor\n", "\n", - "ctx = neptune.Context()\n", + "neptune.init(project_qualified_name='USER_NAME/PROJECT_NAME')\n", + "\n", + "neptune.create_experiment()\n", + "\n", "monitor = neptune_monitor()" ] }, @@ -75,6 +78,39 @@ " )" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Stop Neptune experiment" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "neptune.stop()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "You can also put everything in the `with` block:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "with neptune.create_experiment():\n", + " ..." + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -101,8 +137,11 @@ "import lightgbm as lgb\n", "from sklearn.model_selection import train_test_split\n", "from sklearn.datasets import load_wine\n", + "import neptune\n", "from neptunecontrib.monitoring.lightgbm import neptune_monitor\n", "\n", + "neptune.init(project_qualified_name='USER_NAME/PROJECT_NAME')\n", + "\n", "data = load_wine()\n", "X_train, X_test, y_train, y_test = train_test_split(data.data, data.target, test_size=0.1)\n", "lgb_train = lgb.Dataset(X_train, y_train)\n", @@ -116,21 +155,22 @@ " 'feature_fraction': 0.9\n", " }\n", "\n", - "monitor = neptune_monitor()\n", + "with neptune.create_experiment():\n", + " monitor = neptune_monitor()\n", "\n", - "gbm = lgb.train(params,\n", - " lgb_train,\n", - " num_boost_round=500,\n", - " valid_sets=[lgb_train, lgb_eval],\n", - " valid_names=['train','valid'],\n", - " callbacks=[monitor],\n", - " )" + " gbm = lgb.train(params,\n", + " lgb_train,\n", + " num_boost_round=500,\n", + " valid_sets=[lgb_train, lgb_eval],\n", + " valid_names=['train','valid'],\n", + " callbacks=[monitor],\n", + " )" ] } ], "metadata": { "kernelspec": { - "display_name": "Python 3", + "display_name": "scraping", "language": "python", "name": "python3" }, diff --git a/docs/examples/project_progress.ipynb b/docs/examples/project_progress.ipynb index 84f2e46..d0dbdd3 100644 --- a/docs/examples/project_progress.ipynb +++ b/docs/examples/project_progress.ipynb @@ -16,7 +16,7 @@ "metadata": {}, "outputs": [], "source": [ - "from neptunelib.session import Session\n", + "from neptune.sessions import Session\n", "\n", "session = Session()\n", "project = session.get_projects('neptune-ml')['neptune-ml/Salt-Detection']\n", @@ -241,7 +241,7 @@ ], "metadata": { "kernelspec": { - "display_name": "neptunecontrib_py36", + "display_name": "scraping", "language": "python", "name": "python3" }, diff --git a/docs/examples/run_hyperparameter_search.ipynb b/docs/examples/run_hyperparameter_search.ipynb deleted file mode 100644 index a303aab..0000000 --- a/docs/examples/run_hyperparameter_search.ipynb +++ /dev/null @@ -1,544 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Run skopt/hyperopt hyper parameter sweep in neptune\n", - "\n", - "## Prerequisites\n", - "\n", - "### Training and evaluation script\n", - "In order to run hyperparameter search you need to have a python script that runs model training and evaluation\n", - "based on the parameters passed. \n", - "\n", - "We assume that you pass parameters via `ctx.params.YOUR_PARAM_NAME` and save the evaluation score in the `ctx.properties['YOUR_METRIC']`." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import neptune\n", - "import lightgbm as lgb\n", - "from sklearn.datasets import load_wine\n", - "from sklearn.model_selection import train_test_split\n", - "\n", - "ctx = neptune.Context()\n", - "\n", - "data = load_wine()\n", - "X_train, X_test, y_train, y_test = train_test_split(data.data, data.target,\n", - " test_size=0.2, random_state=1234)\n", - "lgb_train = lgb.Dataset(X_train, y_train)\n", - "lgb_eval = lgb.Dataset(X_test, y_test, reference=lgb_train)\n", - "\n", - "params = {'boosting_type': ctx.params.boosting_type,\n", - " 'objective': ctx.params.objective,\n", - " 'num_class': ctx.params.num_class,\n", - " 'num_leaves': ctx.params.num_leaves,\n", - " 'max_depth': ctx.params.max_depth,\n", - " 'learning_rate': ctx.params.learning_rate,\n", - " 'feature_fraction': ctx.params.feature_fraction}\n", - "\n", - "gbm = lgb.train(params,\n", - " lgb_train,\n", - " num_boost_round=ctx.params.num_boost_round,\n", - " valid_sets=[lgb_train, lgb_eval],\n", - " valid_names=['train', 'valid'],\n", - " )\n", - "\n", - "eval_loss= gbm.best_score['valid']['multi_logloss']\n", - "ctx.properties['eval_loss'] = eval_loss" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Configuration file\n", - "This is not strictly necessary but it makes things cleaner. \n", - "It is a good idea to define hyperparameters and properties in a neptune configuration file.\n", - "\n", - "Let's call it `neptune.yaml`" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "project: neptune-ml/neptune-examples\n", - "\n", - "metric:\n", - " channel: 'eval_loss'\n", - " goal: minimize\n", - "\n", - "parameters:\n", - " boosting_type: 'gbdt'\n", - " objective: 'multiclass'\n", - " num_class: 3\n", - " num_boost_round: 10\n", - " learning_rate: 0.1\n", - " num_leaves: 10\n", - " max_depth: 10\n", - " feature_fraction: 0.9" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Scikit-Optimize parameter sweep\n", - "\n", - "### Imports" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import neptune\n", - "import skopt\n", - "import neptunecontrib.hpo.utils as hp_utils\n", - "import neptunecontrib.monitoring.skopt as sk_monitor\n", - "\n", - "ctx = neptune.Context()\n", - "ctx.tags.append('skopt_forest_search')\n", - "\n", - "METRIC_CHANNEL_NAME = 'eval_loss'\n", - "PROJECT_NAME = 'neptune-ml/neptune-examples'\n", - "N_CALLS = 50\n", - "N_RANDOM_STARTS = 10" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Define search space\n", - "Scikit optimize search space is a list of skopt `Dimension` objects." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "space = [skopt.space.Integer(10, 60, name='num_leaves'),\n", - " skopt.space.Integer(2, 30, name='max_depth'),\n", - " skopt.space.Real(0.1, 0.9, name='feature_fraction', prior='uniform')]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Define objective function\n", - "Every optimization algorithm needs an objective function to optimize.\n", - "You can convert your `neptune run` command into such a function using `neptunecontrib.hpo.utils.make_objective` helper.\n", - "Using the `@use_named_args(space)` decorator helps with the parameter formatting." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "@skopt.utils.use_named_args(space)\n", - "def objective(**params):\n", - " return hp_utils.make_objective(params,\n", - " command=['neptune run --config neptune.yaml','train_evaluate.py'],\n", - " metric_channel_name=METRIC_CHANNEL_NAME,\n", - " project_name=PROJECT_NAME)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Define NeptuneMonitor to observe metrics during training\n", - "All you need to do in order to monitor the optimization process in neptune is add the `NeptuneMonitor` callback." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "monitor = sk_monitor.NeptuneMonitor(ctx)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Run skopt optimization\n", - "Run the algorithm of your choice. I will use the `forest_minimize`." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "results = skopt.forest_minimize(objective, space, callback=[monitor],\n", - " base_estimator='ET',\n", - " n_calls=N_CALLS,\n", - " n_random_starts=N_RANDOM_STARTS)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Log best parameters and diagnostic charts to Neptune\n", - "After the training has been finished you can log the best score and parameters and skopt diagnostic charts\n", - "to neptune." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "ctx.channel_send(METRIC_CHANNEL_NAME, results.fun)\n", - "sk_monitor.send_best_parameters(results, ctx)\n", - "sk_monitor.send_plot_convergence(results, ctx)\n", - "sk_monitor.send_plot_evaluations(results, ctx)\n", - "sk_monitor.send_plot_objective(results, ctx)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Full scikit-optimize script" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import neptune\n", - "import skopt\n", - "import neptunecontrib.hpo.utils as hp_utils\n", - "import neptunecontrib.monitoring.skopt as sk_monitor\n", - "\n", - "ctx = neptune.Context()\n", - "ctx.tags.append('skopt_forest_search')\n", - "\n", - "METRIC_CHANNEL_NAME = 'eval_loss'\n", - "PROJECT_NAME = 'neptune-ml/neptune-examples'\n", - "N_CALLS = 50\n", - "N_RANDOM_STARTS = 10\n", - "\n", - "space = [skopt.space.Integer(10, 60, name='num_leaves'),\n", - " skopt.space.Integer(2, 30, name='max_depth'),\n", - " skopt.space.Real(0.1, 0.9, name='feature_fraction', prior='uniform')]\n", - "\n", - "@skopt.utils.use_named_args(space)\n", - "def objective(**params):\n", - " return hp_utils.make_objective(params,\n", - " command=['neptune run --config neptune.yaml','train_evaluate.py'],\n", - " metric_channel_name=METRIC_CHANNEL_NAME,\n", - " project_name=PROJECT_NAME)\n", - "\n", - "monitor = sk_monitor.NeptuneMonitor(ctx)\n", - "\n", - "results = skopt.forest_minimize(objective, space, callback=[monitor],\n", - " base_estimator='ET',\n", - " n_calls=N_CALLS,\n", - " n_random_starts=N_RANDOM_STARTS)\n", - "\n", - "ctx.channel_send(METRIC_CHANNEL_NAME, results.fun)\n", - "sk_monitor.send_best_parameters(results, ctx)\n", - "sk_monitor.send_plot_convergence(results, ctx)\n", - "sk_monitor.send_plot_evaluations(results, ctx)\n", - "sk_monitor.send_plot_objective(results, ctx)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Explore optimization process in Neptune\n", - "You can [explore the experiment here](https://app.neptune.ml/o/neptune-ml/org/neptune-examples/e/NEP-380/channels) if you want to.\n", - "\n", - "Check the training curve and hyperparameter logs.\n", - "\n", - "![image6](https://gist.githubusercontent.com/jakubczakon/f754769a39ea6b8fa9728ede49b9165c/raw/0efc32299dbce7dd438e3b5811e933f0caa78504/contrib_hyper1.png)\n", - "\n", - "![image5](https://gist.githubusercontent.com/jakubczakon/f754769a39ea6b8fa9728ede49b9165c/raw/0b94c0fc94f9e7a685ea89bc1c6307355b747253/contrib_hyper3.png)\n", - "\n", - "Explore hyperparameter diagnostics charts like `plot_objective` from the skopt package.\n", - "\n", - "![image4](https://gist.githubusercontent.com/jakubczakon/f754769a39ea6b8fa9728ede49b9165c/raw/0b94c0fc94f9e7a685ea89bc1c6307355b747253/contrib_hyper4.png)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Hyperopt parameter sweep\n", - "### Imports" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from collections import OrderedDict\n", - "\n", - "from hyperopt import hp, tpe, fmin, Trials\n", - "import neptune\n", - "import skopt\n", - "from sklearn.externals import joblib\n", - "import neptunecontrib.hpo.utils as hp_utils\n", - "import neptunecontrib.monitoring.skopt as sk_monitor\n", - "\n", - "ctx = neptune.Context()\n", - "ctx.tags.append('tpe_search')\n", - "\n", - "METRIC_CHANNEL_NAME = 'eval_loss'\n", - "PROJECT_NAME = 'neptune-ml/neptune-examples'\n", - "TRIALS_PATH = 'trials.pkl'\n", - "N_CALLS = 50" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Define search space\n", - "Normally you define your search space in hyperopt by simply creating a dict.\n", - "However, we want to make sure that the names are in the same order to be able\n", - "to do some formatting later." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "space = OrderedDict(num_leaves=hp.choice('num_leaves', range(10, 60, 1)),\n", - " max_depth=hp.choice('max_depth', range(2, 30, 1)),\n", - " feature_fraction=hp.uniform('feature_fraction', 0.1, 0.9)\n", - " )" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Define objective function\n", - "Every optimization algorithm needs an objective function to optimize. You can convert your neptune run command into such a function using neptunecontrib.hpo.utils.make_objective helper." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "def objective(params):\n", - " return hp_utils.make_objective(params,\n", - " command=['neptune run --config neptune.yaml','train_evaluate.py'],\n", - " metric_channel_name=METRIC_CHANNEL_NAME,\n", - " project_name=PROJECT_NAME)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Run hyperopt optimization\n", - "Run the TPE algoright from via `fmin` function." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "trials = Trials()\n", - "_ = fmin(objective, space, trials=trials, algo=tpe.suggest, max_evals=N_CALLS)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Log best parameters and diagnostic charts to Neptune\n", - "After the training has been finished you can log the best score and parameters and skopt diagnostic charts\n", - "to neptune. \n", - "\n", - "You need to convert `hyperopt.Trials` object into `scipy.optimize.OptimizeResult`" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "results = hp_utils.hyperopt2skopt(trials, space)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Send parameters and diagnostic charts to neptune" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "ctx.channel_send(METRIC_CHANNEL_NAME, results.fun)\n", - "sk_monitor.send_runs(results, ctx)\n", - "sk_monitor.send_best_parameters(results, ctx)\n", - "sk_monitor.send_plot_convergence(results, ctx)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Explore optimization process in Neptune\n", - "You can [explore the experiment here](https://app.neptune.ml/o/neptune-ml/org/neptune-examples/e/NEP-431/channels) if you want to.\n", - "\n", - "Check the training curve and hyperparameter logs.\n", - "\n", - "![image3](https://gist.githubusercontent.com/jakubczakon/f754769a39ea6b8fa9728ede49b9165c/raw/3ec8efcf705507ddbe8aac709130460b451c2868/contrib_hyper21.png)\n", - "\n", - "![image2](https://gist.githubusercontent.com/jakubczakon/f754769a39ea6b8fa9728ede49b9165c/raw/3ec8efcf705507ddbe8aac709130460b451c2868/contrib_hyper22.png)\n", - "\n", - "Explore hyperparameter diagnostics charts.\n", - "\n", - "![image1](https://gist.githubusercontent.com/jakubczakon/f754769a39ea6b8fa9728ede49b9165c/raw/3ec8efcf705507ddbe8aac709130460b451c2868/contrib_hyper23.png)\n", - "\n", - "**Note**\n", - "There is no callbacks in hyperopt only the trials object.\n", - "It means that all the scores/hyperparamters are logged to neptune after the training has been finished.\n", - "In order not to loose all that information if the process fails mid-way through I would suggest putting\n", - "the `fmin` function into a `try catch` block:\n", - "\n", - "```python\n", - "try:\n", - " trials = Trials()\n", - " _ = fmin(objective, space, trials=trials, algo=tpe.suggest, max_evals=N_CALLS)\n", - " joblib.dump(trials, TRIALS_PATH)\n", - " \n", - " results = hp_utils.hyperopt2skopt(trials, space)\n", - "\n", - " ctx.channel_send(METRIC_CHANNEL_NAME, results.fun)\n", - " sk_monitor.send_runs(results, ctx)\n", - " sk_monitor.send_best_parameters(results, ctx)\n", - " sk_monitor.send_plot_convergence(results, ctx)\n", - "\n", - "except Exception as e:\n", - " print('Process failed saving current trials to {}'.format(TRIALS_PATH))\n", - " joblib.dump(trials, TRIALS_PATH)\n", - " \n", - " results = hp_utils.hyperopt2skopt(trials, space)\n", - "\n", - " ctx.channel_send(METRIC_CHANNEL_NAME, results.fun)\n", - " sk_monitor.send_runs(results, ctx)\n", - " sk_monitor.send_best_parameters(results, ctx)\n", - " sk_monitor.send_plot_convergence(results, ctx)\n", - " raise(e)\n", - "```" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Full hyperopt script" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import neptune\n", - "import skopt\n", - "from sklearn.externals import joblib\n", - "import neptunecontrib.hpo.utils as hp_utils\n", - "import neptunecontrib.monitoring.skopt as sk_monitor\n", - "\n", - "ctx = neptune.Context()\n", - "ctx.tags.append('tpe_search')\n", - "\n", - "METRIC_CHANNEL_NAME = 'eval_loss'\n", - "PROJECT_NAME = 'neptune-ml/neptune-examples'\n", - "TRIALS_PATH = 'trials.pkl'\n", - "N_CALLS = 50\n", - "\n", - "space = OrderedDict(num_leaves=hp.choice('num_leaves', range(10, 60, 1)),\n", - " max_depth=hp.choice('max_depth', range(2, 30, 1)),\n", - " feature_fraction=hp.uniform('feature_fraction', 0.1, 0.9)\n", - " )\n", - "\n", - "def objective(params):\n", - " return hp_utils.make_objective(params,\n", - " command=['neptune run --config neptune.yaml','train_evaluate.py'],\n", - " metric_channel_name=METRIC_CHANNEL_NAME,\n", - " project_name=PROJECT_NAME)\n", - "\n", - "trials = Trials()\n", - "_ = fmin(objective, space, trials=trials, algo=tpe.suggest, max_evals=N_CALLS)\n", - "\n", - "results = hp_utils.hyperopt2skopt(trials, space)\n", - "\n", - "ctx.channel_send(METRIC_CHANNEL_NAME, results.fun)\n", - "sk_monitor.send_runs(results, ctx)\n", - "sk_monitor.send_best_parameters(results, ctx)\n", - "sk_monitor.send_plot_convergence(results, ctx)\n", - "sk_monitor.send_plot_evaluations(results, ctx)" - ] - } - ], - "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.6.8" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/docs/examples/sync_with_json.ipynb b/docs/examples/sync_with_json.ipynb index 9eecf1f..e630fa2 100644 --- a/docs/examples/sync_with_json.ipynb +++ b/docs/examples/sync_with_json.ipynb @@ -12,18 +12,34 @@ "For example your `experiment_data.json` could look like this:\n", "\n", "```json\n", - " {\"name\": \"baseline\", \n", - " \"parameters\": {\"lr\": 0.1, \n", - " \"batch_size\": 32\n", - " }, \n", - " \"tags\": [\"base\", \"solution-1\", \"pytorch\"], \n", - " \"channels\": {\"log_loss\": {\"x\": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10], \n", - " \"y\": [0, 1, 4, 9, 16, 25, 36, 49, 64, 81, 100]\n", - " }\n", - " }, \n", - " \"properties\": {\"data_version\": \"version_1\"\n", - " }\n", - " }\n", + "{\n", + " 'name':'example',\n", + " 'description':'json tracking experiment',\n", + " 'params':{'lr':0.1,\n", + " 'batch_size': 128,\n", + " 'dropount': 0.5\n", + " },\n", + " 'properties':{'data_version':'1231ffwefef9',\n", + " 'data_path': '/mnt/path/to/data'\n", + " },\n", + " 'tags':['resnet','no_preprocessing'],\n", + " 'upload_source_files':['train.sh'],\n", + " 'send_metric':{'log_loss': {'x': [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10],\n", + " 'y': [0, 1, 4, 9, 16, 25, 36, 49, 64, 81, 100]\n", + " },\n", + " 'accuracy':{'x': [0, 4, 5, 8, 9],\n", + " 'y': [0.23, 0.47, 0.62, 0.89, 0.92]\n", + " }\n", + " },\n", + " 'send_text':{ 'hash':{'x': [0, 4, 5, 8, 9],\n", + " 'y': ['123123','as32e132', '123sdads', '123asdasd',' asd324132a']\n", + " },\n", + " },\n", + " 'send_image':{'heatmaps':{'x': [0,1,2],\n", + " 'y': ['img1.png', 'img2.png', 'img3.png']\n", + " },\n", + " },\n", + "}\n", "```\n", "\n", "## Sync with Neptune\n", @@ -40,15 +56,24 @@ "Now your experiment data is safely logged to Neptune and you can easily explore it.\n", "Go to the [experiment link](https://app.neptune.ml/o/neptune-ml/org/neptune-examples/e/NEP-506/charts) to see for yourself!\n", "\n", - "![image](https://gist.githubusercontent.com/jakubczakon/f754769a39ea6b8fa9728ede49b9165c/raw/18b476c5ba53b7f7c67f8c61eba1a22a98862f83/contrib_sync1.png)\n", + "![image](https://gist.githubusercontent.com/jakubczakon/f754769a39ea6b8fa9728ede49b9165c/raw/0f914ef66ec5821a83eec687c772fd2692a4d424/json1.png)\n", "\n", - "![image1](https://gist.githubusercontent.com/jakubczakon/f754769a39ea6b8fa9728ede49b9165c/raw/18b476c5ba53b7f7c67f8c61eba1a22a98862f83/contrib_sync2.png)" + "![image1](https://gist.githubusercontent.com/jakubczakon/f754769a39ea6b8fa9728ede49b9165c/raw/0f914ef66ec5821a83eec687c772fd2692a4d424/json2.png)\n", + "\n", + "![image2](https://gist.githubusercontent.com/jakubczakon/f754769a39ea6b8fa9728ede49b9165c/raw/0f914ef66ec5821a83eec687c772fd2692a4d424/json3.png)" ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] } ], "metadata": { "kernelspec": { - "display_name": "neptunecontrib_py36", + "display_name": "scraping", "language": "python", "name": "python3" }, diff --git a/docs/examples/telegram_bot.ipynb b/docs/examples/telegram_bot.ipynb index fbcfc36..210ccfd 100644 --- a/docs/examples/telegram_bot.ipynb +++ b/docs/examples/telegram_bot.ipynb @@ -19,13 +19,17 @@ "* Copy the bot token\n", "\n", "### Get neptune api token\n", - " * Run the following command:\n", + "* Go to Neptune app and click on the `Get API Token` button on the top right.\n", + "\n", + "![image](https://gist.githubusercontent.com/jakubczakon/f754769a39ea6b8fa9728ede49b9165c/raw/e3776e605fea1fd5377c3ec748ba87b71cd8ef12/get_api_token.png)\n", "\n", - "```bash\n", - "neptune account api-token get\n", - "```\n", "\n", - " * Copy neptune token\n", + "## Set NEPTUNE_API_TOKEN environment variable\n", + "Go to your console and run:\n", + "\n", + "```\n", + "export NEPTUNE_API_TOKEN='your_long_api_token'\n", + "```\n", "\n", "## Fire up your bot\n", "Now all you need is to send this data to your Neptune project.\n", @@ -43,11 +47,18 @@ "\n", "![image1](https://gist.githubusercontent.com/jakubczakon/f754769a39ea6b8fa9728ede49b9165c/raw/2e6f056574d035c6d744fc5d89f1ed55bf155abf/contrib_bot2.png)" ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] } ], "metadata": { "kernelspec": { - "display_name": "neptunecontrib_py36", + "display_name": "scraping", "language": "python", "name": "python3" }, diff --git a/docs/index.rst b/docs/index.rst index 1a0bc59..dc9bed6 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -1,12 +1,8 @@ neptune-contrib: open-source contributions to Neptune.ml =========================================== -**Note** -This lib is compatible with `neptune-cli` and some part may not work with `neptune-client`. -It will be updated for `neptune-client` soon (ETA `4.7.2019`). - This library is a collection of helpers and extensions that make working -with `Neptune website`_ more effective and better. It is build on top of neptune-cli +with `Neptune website`_ more effective and better. It is build on top of neptune-client and neptune-lib and gives you option to do things like: * interactive visualizations of experiment runs or hyperparameters * running hyper parameter sweeps in scikit-optimize, hyperopt or any other tool you like @@ -35,7 +31,6 @@ And the best thing is you can extend it yourself or... tell us to do it for you api.utils hpo.utils bots.telegram_bot - monitoring.notebooks monitoring.lightgbm monitoring.fastai monitoring.reporting diff --git a/docs/overview.rst b/docs/overview.rst index 83f04e1..b4a6985 100644 --- a/docs/overview.rst +++ b/docs/overview.rst @@ -10,37 +10,6 @@ This library contains tools that help you with: Some cool functions are: -**neptunecontrib.monitoring.notebooks.LocalNotebookContext** - -which lets you develop in your local notebooks. - -.. code:: ipython3 - - from neptunecontrib.monitoring.notebooks import LocalNotebookContext - ctx = NotebookContext(ctx, config_filepath='neptune.yaml') - -You can now access hyperparamters: - -.. code:: ipython3 - - ctx.params - -.. parsed-literal:: - - AttrDict({'power': 3}) - -And monitor training just as if you were -running cloud notebooks. - -.. code:: ipython3 - - for i in range(100): - ctx.channel_send('random_channel', i, 2 + i**2) - ctx.channel_send('other_random_channel', y=5 + i**7) - ctx.channel_send('text_channel','blabla') - -.. image:: examples/local_notebooks_files/local_notebooks_11_0.png - **neptunecontrib.viz.experiments.channel_curve_compare** which lets you visualize learning curves on an interactive chart. You can choose which curves you want to view by clicking on the legend, select the x range on the top chart and view it in detail on the bottom. @@ -91,29 +60,8 @@ which lets you visualize learning curves on an interactive chart. You can choose - -**neptunecontrib.hpo.utils.make_objective** -which lets you convert commands like - -.. code-block:: console - - neptune run --config neptune.yaml main.py - -into python function that return a score. - -.. code:: ipython3 - - import neptunecontrib.hpo.utils as hp_utils - - def objective(params): - return hp_utils.make_objective(params, - command=['neptune run --config neptune.yaml','main.py'], - metric_channel_name=METRIC_CHANNEL_NAME, - project_name=PROJECT_NAME) - - **neptunecontrib.hpo.utils.hyperopt2skopt** which lets you convert hyperopt.Trails object into scipy.optimize.OptimizeResults and visualize it with scikit-optimize diagnostic tools. diff --git a/docs/user_guide/monitoring/notebooks.rst b/docs/user_guide/monitoring/notebooks.rst deleted file mode 100644 index beedf72..0000000 --- a/docs/user_guide/monitoring/notebooks.rst +++ /dev/null @@ -1,6 +0,0 @@ -Local Notebooks -=========== - -.. automodule:: neptunecontrib.monitoring.notebooks - :members: - :show-inheritance: \ No newline at end of file diff --git a/docs_requirements.txt b/docs_requirements.txt index 3fb12fa..81dcf43 100644 --- a/docs_requirements.txt +++ b/docs_requirements.txt @@ -1,17 +1,3 @@ -oauthlib -PyJWT -neptune-cli -neptune-lib -requests>=2.20.0 -requests-oauthlib>=1.0.0 -pandas -bravado -Pillow==5.4.1 -matplotlib -attrdict==2.0.0 -ipython -altair==2.3.0 -scikit-optimize==0.5.2 -retrying==1.3.3 -hyperopt==0.1.1 +sphinx-rtd-theme==0.4.3 +sphinx==1.8.5 nbsphinx==0.4.2 diff --git a/neptunecontrib/api/utils.py b/neptunecontrib/api/utils.py index bbe7051..2c2175a 100644 --- a/neptunecontrib/api/utils.py +++ b/neptunecontrib/api/utils.py @@ -30,7 +30,7 @@ def concat_experiments_on_channel(experiments, channel_name): ['id','x_log_loss','y_log_loss'] columns. Args: - experiments(list): list of `neptunelib.api.Experiment` objects. + experiments(list): list of `neptune.experiments.Experiment` objects. channel_name(str): name of the channel for which we want to extract values. Returns: @@ -40,7 +40,7 @@ def concat_experiments_on_channel(experiments, channel_name): Examples: Instantiate a session. - >>> from neptunelib.api.session import Session + >>> from neptune.sessions import Session >>> session = Session() Fetch a project and a list of experiments. @@ -93,7 +93,7 @@ def extract_project_progress_info(leadearboard, metric_colname, time_colname='fi Examples: Instantiate a session. - >>> from neptunelib.api.session import Session + >>> from neptune.sessions import Session >>> session = Session() Fetch a project and the experiment view of that project. diff --git a/neptunecontrib/bots/telegram_bot.py b/neptunecontrib/bots/telegram_bot.py index 826730b..8bdfacf 100644 --- a/neptunecontrib/bots/telegram_bot.py +++ b/neptunecontrib/bots/telegram_bot.py @@ -16,7 +16,7 @@ """Spins of a Neptune bot with which you can interact on telegram -You can see which experiments are running, check the best experiemnts based +You can see which experiments are running, check the best experiements based on defined metric and even plot it in Telegram. Full list of options: @@ -34,8 +34,11 @@ Attributes: telegram_api_token(str): Your telegram bot api token. You can pass it either as --telegram_api_token or -t. - neptune_api_token(str): Your neptune api token. + neptune_api_token(str): Your neptune api token. If you + set the NEPTUNE_API_TOKEN environemnt variable, you + don't have to pass it here. You can pass it either as --neptune_api_token or -n. + Default None. Example: Spin off your bot. @@ -59,7 +62,7 @@ import argparse from io import BytesIO -from neptunelib.session import Session +from neptune.sessions import Session import matplotlib.pyplot as plt import pandas as pd from telegram.ext import Updater @@ -296,7 +299,7 @@ def _no_project_selected(self, bot, update): def parse_args(): parser = argparse.ArgumentParser() parser.add_argument('-t', '--telegram_api_token') - parser.add_argument('-n', '--neptune_api_token') + parser.add_argument('-n', '--neptune_api_token', default=None) return parser.parse_args() diff --git a/neptunecontrib/hpo/utils.py b/neptunecontrib/hpo/utils.py index 7decdc5..397b470 100644 --- a/neptunecontrib/hpo/utils.py +++ b/neptunecontrib/hpo/utils.py @@ -14,71 +14,9 @@ # limitations under the License. # -import random -import string -import subprocess - -from neptunelib.session import Session import pandas as pd from scipy.optimize import OptimizeResult import skopt -from retrying import retry - - -def make_objective(param_set, command, metric_channel_name, project_name, tag='trash'): - """Converts `neptune run` script to a python function that returns a score. - - Helper function that wraps your train_evaluate_model.py script into a function - that takes hyper parameters as input and returns the score on a specified metric. - - Args: - param_set(dict): Dictionary of parameter name parameter value. The objective - function is evaluated on those parameters. - command(list):bash neptune command represented as a list. It is assumed - that the .py script path is a last element. - E.g. ['neptune run --config neptune.yaml', 'main.py'] - metric_channel_name(str): name of the property where the evaluation - result is stored. It is crucial that the single run result is stored - as property.E.g. ctx.properties[metric_channel_name] = 0.8. - project_name(str): NAME_SPACE/PROJECT_NAME. E.g. 'neptune-ml/neptune-examples' - tag(str): Tag that should be added to the single run experiment. - It is useful to add this tag so that you can group or get rid of the single run - experiments in your project. Default is 'trash'. - - Returns: - float: Score that was reported in the `ctx.properties[metric_channel_name]` - of the run of train_evaluate_model.py script. - - - Examples: - Prepare the space of hyperparameters to search over. - - >>> space = [skopt.space.Integer(10, 60, name='num_leaves'), - skopt.space.Integer(2, 30, name='max_depth'), - skopt.space.Real(0.1, 0.9, name='feature_fraction', prior='uniform')] - - Define the objective. - - >>> @skopt.utils.use_named_args(space) - def objective(**params): - return hp_utils.make_objective(params, - command=['neptune run --config neptune.yaml', - 'train_evaluate.py'], - metric_channel_name='valid_accuracy', - project_name='neptune-ml/neptune-examples') - - Run skopt training. - - >>> results = skopt.forest_minimize(objective, space, - base_estimator='ET', n_calls=100, n_random_starts=10) - - """ - command, exp_id_tag = _create_command_and_id_tag(command, param_set, tag) - - subprocess.call(command, shell=True) - - score = _get_score(exp_id_tag, metric_channel_name, project_name) - return score def hyperopt2skopt(trials, space): @@ -155,7 +93,7 @@ def df2result(df, metric_col, param_cols, param_types=None): Examples: Instantiate a session. - >>> from neptunelib.api.session import Session + >>> from neptune.sessions import Session >>> session = Session() Fetch a project and a list of experiments. @@ -298,32 +236,3 @@ def _convert_space_hop_skopt(space): raise NotImplementedError skopt_space = skopt.Space(dimensions) return skopt_space - - -def _create_command_and_id_tag(command, param_set, tag): - command.insert(-1, '--tag {}'.format(tag)) - for name, value in param_set.items(): - command.insert(-1, "--parameter {}:{}".format(name, value)) - - exp_id_tag = _get_random_string() - command.insert(-1, '--tag {}'.format(exp_id_tag)) - - command = " ".join(command) - return command, exp_id_tag - - -def _get_random_string(length=64): - x = ''.join(random.choice(string.ascii_lowercase + string.digits) - for _ in range(length)) - return x - - -@retry(stop_max_attempt_number=50, wait_random_min=1000, wait_random_max=5000) -def _get_score(exp_id_tag, metric_name, project_name): - namespace = project_name.split('/')[0] - - session = Session() - project = session.get_projects(namespace)[project_name] - experiment = project.get_experiments(tag=[exp_id_tag])[0] - score = float(experiment.properties[metric_name].tolist()[0]) - return score diff --git a/neptunecontrib/monitoring/fastai.py b/neptunecontrib/monitoring/fastai.py index d9746ba..303e53c 100644 --- a/neptunecontrib/monitoring/fastai.py +++ b/neptunecontrib/monitoring/fastai.py @@ -16,6 +16,8 @@ import sys +import neptune + if sys.version_info[0] == 3 and sys.version_info[1] >= 6: from fastai.callbacks import LearnerCallback else: @@ -55,37 +57,38 @@ class NeptuneMonitor(LearnerCallback): >>> .databunch() >>> .normalize(imagenet_stats)) - Define neptune monitor callback: + Now, create Neptune experiment, instantiate the monitor and pass + it to callbacks. >>> import neptune >>> from neptunecontrib.monitoring.fastai import NeptuneMonitor - >>> ctx = neptune.Context() - >>> monitor = NeptuneMonitor(ctx=ctx) - - Pass neptune monitor callback to the learner: - - >>> learn = create_cnn(data, models.resnet18, - >>> metrics=accuracy, - >>> callbacks=[neptune_monitor]) - >>> learn.fit_one_cycle(20, 1e-2) + >>> + >>> neptune.init(qualified_project_name='USER_NAME/PROJECT_NAME') + >>> + >>> with neptune.create_experiment(): + >>> monitor = NeptuneMonitor() + >>> learn = create_cnn(data, models.resnet18, + >>> metrics=accuracy, + >>> callbacks=[neptune_monitor]) + >>> learn.fit_one_cycle(20, 1e-2) Note: you need to have the fastai library installed on your computer to use this module. """ - def __init__(self, ctx, learn=None, prefix=''): - self._ctx = ctx + def __init__(self, experiment=None, learn=None, prefix=''): + self._exp = experiment if experiment else neptune self._prefix = prefix if learn is not None: super().__init__(learn) def on_epoch_end(self, **kwargs): - self._ctx.channel_send(self._prefix + 'train_smooth_loss', float(kwargs['smooth_loss'])) + self._exp.send_metric(self._prefix + 'train_smooth_loss', float(kwargs['smooth_loss'])) metric_values = kwargs['last_metrics'] metric_names = ['valid_last_loss'] + kwargs['metrics'] for metric_value, metric_name in zip(metric_values, metric_names): metric_name = getattr(metric_name, '__name__', metric_name) - self._ctx.channel_send(self._prefix + metric_name, float(metric_value)) + self._exp.send_metric(self._prefix + metric_name, float(metric_value)) def on_batch_end(self, **kwargs): - self._ctx.channel_send('{}last_loss'.format(self._prefix), float(kwargs['last_loss'])) + self._exp.send_metric('{}last_loss'.format(self._prefix), float(kwargs['last_loss'])) diff --git a/neptunecontrib/monitoring/lightgbm.py b/neptunecontrib/monitoring/lightgbm.py index 0d604c9..b361680 100644 --- a/neptunecontrib/monitoring/lightgbm.py +++ b/neptunecontrib/monitoring/lightgbm.py @@ -14,7 +14,10 @@ # limitations under the License. # -def neptune_monitor(ctx, prefix=''): +import neptune + + +def neptune_monitor(experiment=None, prefix=''): """Logs lightGBM learning curves to Neptune. Goes over the list of metrics and valid_sets passed to the `lgb.train` @@ -74,9 +77,11 @@ def neptune_monitor(ctx, prefix=''): >>> monitor = neptune_monitor(prefix) """ + _exp = experiment if experiment else neptune + def callback(env): for name, loss_name, loss_value, _ in env.evaluation_result_list: channel_name = '{}{}_{}'.format(prefix, name, loss_name) - ctx.channel_send(channel_name, x=env.iteration, y=loss_value) + _exp.send_metric(channel_name, x=env.iteration, y=loss_value) return callback diff --git a/neptunecontrib/monitoring/notebooks.py b/neptunecontrib/monitoring/notebooks.py deleted file mode 100644 index ac799f2..0000000 --- a/neptunecontrib/monitoring/notebooks.py +++ /dev/null @@ -1,264 +0,0 @@ -# -# Copyright (c) 2019, Neptune Labs Sp. z o.o. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -import os - -import yaml -from attrdict import AttrDict -import matplotlib.pyplot as plt -from IPython.display import clear_output - -from neptunecontrib.monitoring.utils import is_offline_context - - -def LocalNotebookContext(context, config_filepath, figsize=(16, 12), max_cols=2): - """Wrapper for the Neptune context that lets you work in local notebooks. - - This wrapper makes it possible to access parameters and plot numerical channels - to the notebook without any modifications to your code. During the development phase - you can simply work in notebook and explore learning curves just as you would do in - the Neptune-app. - - Args: - context('neptune.Context'): Neptune context. - config_filepath(str): filepath to the Neptune configuration file. - figsize(tuple): The size of the figure on where the numerical channels are plotted. - Default is (16,12). - max_cols(int): Number of charts that should be plotted in one row. Default is 2. - - Examples: - Create Neptune context instance: - - >>> import neptune - >>> ctx = neptune.Context() - - Create Local Neptune context: - - >>> ctx = LocalNotebookContext(ctx, config_filepath='neptune.yaml') - - Access parameters from code: - - >>> ctx.params - - Send values to numerical channels and plot: - - >>> ctx.channel_send('loss', x=1, y=0.92) - - Note: - After the development process is over you run your notebook as a - python script and send the metrics to the Neptune app. - Simply run the following command. - - $ jupyter nbconvert --to script main.py; neptune run --config neptune.yaml main.py - """ - if is_offline_context(context): - return LocalPatchedContext(context=context, config_filepath=config_filepath, figsize=figsize, max_cols=max_cols) - else: - return context - - -class LocalPatchedContext(object): - """Wrapper for the Neptune context that lets you work in local notebooks. - - This wrapper makes it possible to access parameters and plot numerical channels - to the notebook without any modifications to your code. During the development phase - you can simply work in notebook and explore learning curves just as you would do in - the Neptune-app. - - Args: - context('neptune.Context'): Neptune context. - config_filepath(str): filepath to the Neptune configuration file. - figsize(tuple): The size of the figure on where the numerical channels are plotted. - Default is (16,12). - max_cols(int): Number of charts that should be plotted in one row. Default is 2. - - Attributes: - context('neptune.Context'): Neptune context. - config(`ParameterConfig): ParameterConfig object containt the parsed `parameters` section - from the Neptune configuration file. - numeric_channels(dict): Dictionary containing `channel_name` x,y values pairs where - every y value was convertible to float. - other_channels(dict): Dictionary containing `channel_name` x,y values pairs where - not every y value was convertible to float. - - Examples: - Create Neptune context instance: - - >>> import neptune - >>> ctx = neptune.Context() - - Create Local Neptune context: - - >>> ctx = LocalNotebookContext(ctx, 'neptune.yaml') - - Access parameters from code: - - >>> ctx.params - - Send values to numerical channels and plot: - - >>> ctx.channel_send('loss', x=1, y=0.92) - - Note: - After the development process is over you run your notebook as a - python script and send the metrics to the Neptune app. - Simply run the following command. - - $ jupyter nbconvert --to script main.py; neptune run --config neptune.yaml main.py - """ - - def __init__(self, context, config_filepath, figsize=(16, 12), max_cols=2): - self.context = context - self.config = ParameterConfig(config_filepath) - self.numeric_channels = LocalNotebookChannels(figsize=figsize, max_cols=max_cols) - self.other_channels = {} - - @property - def params(self): - """Parameters from config - - Examples: - >>> import neptune - >>> ctx = neptune.Context() - >>> ctx = LocalNotebookContext(ctx, config_filepath='neptune.yaml') - >>> ctx.params - AttrDict({'lr': 0.2}) - """ - return self.config.params - - def channel_send(self, channel_name, x=None, y=None): - """ Local notebook substitute for the in-app channel_send. - - Every value that is send via this method is automatically plotted in - your notebook and added to the `numeric_channels` dictionary under the `channel_name` key. - If the y value cannot be converted to `float` it will not be plotted but will be - added to the `other_channels` dictionary under the `channel_name` key. - - Args: - channel_name(str): The name of the channel - x(int): The iteration or epoch. If None it will be infered from the y values. - y(float or obj): The values that should be plotted. It is usually a loss or a metric. - If the value passed to y cannot be converted to `float` it will be treated as `other_channel` - and not displayed. - - Examples: - Create Neptune context instance: - - >>> import neptune - >>> ctx = neptune.Context() - - Create Local Neptune context: - - >>> ctx = LocalNotebookContext(ctx, 'neptune.yaml') - - Access parameters from code: - - >>> ctx.params - - Send values to numerical channels and plot: - - >>> ctx.channel_send('loss', x=1, y=0.92) - - Note: - If `x=None` it will be infered from the y values. - - """ - try: - y = float(y) - self.numeric_channels[channel_name] = self.numeric_channels.get(channel_name, - LocalNotebookChannel(channel_name)) - self.numeric_channels[channel_name].update({'x': x, 'y': y}) - self.numeric_channels.plot() - except Exception: - self.other_channels[channel_name] = self.other_channels.get(channel_name, - LocalNotebookChannel(channel_name)) - self.other_channels[channel_name].update({'x': x, 'y': y}) - - -class ParameterConfig(object): - """Parsed Neptune configuration file. - - It is a simplified configuration file containg only the parsed parameters - section. - - Args: - filepath(str): Filepath to the Neptune configuration file. - - Attributes: - filepath(str): Filepath to the Neptune configuration file. - config(`attrdict.AttrDict`): Attribute dictionary containing the `parameters` from the - configuration file. - - Examples: - Create a config from `.yaml` file. - - >>> config = ParameterConfig('neptune.yaml') - - Access parameters from code: - - >>> config.params - AttrDict({'lr': 0.2}) - - """ - - def __init__(self, filepath): - assert os.path.exists(filepath), 'Specified Neptune configuration file {} does not exist'.format(filepath) - - self.filepath = filepath - self.config = ParameterConfig.read_yaml(filepath) - - assert 'parameters' in self.config.keys(), 'Specified Neptune configuration file {} \ - is missing the parameters section'.format(filepath) - - self.params = self.config.parameters - - @staticmethod - def read_yaml(filepath): - with open(filepath) as f: - config = yaml.load(f) - assert config, 'Specified Neptune configuration file {} is empty'.format(filepath) - return AttrDict(config) - - -class LocalNotebookChannels(dict): - def __init__(self, figsize, max_cols): - super().__init__(self) - self.figsize = figsize - self.max_cols = max_cols - - def plot(self): - clear_output(wait=True) - plt.figure(figsize=self.figsize) - for metric_id, (channel_name, channel) in enumerate(self.items()): - plt.subplot((len(self) + 1) // self.max_cols + 1, self.max_cols, metric_id + 1) - plt.plot(channel.x, channel.y, label=channel_name) - plt.legend() - plt.show() - - -class LocalNotebookChannel(object): - def __init__(self, channel_name): - self.channel_name = channel_name - self.y = [] - self.x = [] - - def update(self, metrics): - self.x.append(metrics['x']) - self.y.append(metrics['y']) - - if None in self.x: - self.x = list(range(len(self.y))) - \ No newline at end of file diff --git a/neptunecontrib/monitoring/reporting.py b/neptunecontrib/monitoring/reporting.py index ea3e1d7..0db7c9d 100644 --- a/neptunecontrib/monitoring/reporting.py +++ b/neptunecontrib/monitoring/reporting.py @@ -14,16 +14,17 @@ # limitations under the License. # +import tempfile + import matplotlib.pyplot as plt import neptune import pandas as pd import seaborn as sns from scikitplot.metrics import plot_roc, plot_precision_recall, plot_confusion_matrix -from neptunecontrib.monitoring.utils import fig2pil - -def send_binary_classification_report(ctx, y_true, y_pred, +def send_binary_classification_report(y_true, y_pred, + experiment=None, threshold=0.5, figsize=(16, 12), channel_name='classification report'): @@ -33,10 +34,10 @@ def send_binary_classification_report(ctx, y_true, y_pred, prediction distribution charts and logs it to the 'classification report' channel in Neptune. Args: - ctx(`neptune.Context`): Neptune context. y_true (array-like, shape (n_samples)): Ground truth (correct) target values. y_pred (array-like, shape (n_samples, 2)): Predictions both for negative and positive class in the float format. + experiment(`neptune.experiments.Experiment`): Neptune experiment. Default is None. threshold(float): threshold to be applied for the class asignment. figsize(tuple): size of the matplotlib.pyplot figure object channel_name(str): name of the neptune channel. Default is 'classification report'. @@ -62,22 +63,30 @@ def send_binary_classification_report(ctx, y_true, y_pred, >>> import neptune >>> from neptunecontrib.monitoring.reporting import send_binary_classification_report >>> - >>> ctx = neptune.Context() - >>> send_binary_classification_report(ctx, y_test, y_test_pred) + >>> neptune.init(qualified_project_name='USER_NAME/PROJECT_NAME') + >>> with neptune.create_experiment(): + >>> send_binary_classification_report(y_test, y_test_pred) """ + + _exp = experiment if experiment else neptune + fig = plot_binary_classification_report(y_true, y_pred, threshold=threshold, figsize=figsize) - npt_pred_dist = neptune.Image(name='chart', description='', data=fig2pil(fig)) - ctx.channel_send(channel_name, npt_pred_dist) + with tempfile.NamedTemporaryFile(suffix='.png') as f: + fig.savefig(f.name) + _exp.send_image(channel_name, f.name) -def send_prediction_distribution(ctx, y_true, y_pred, figsize=(16, 12), channel_name='prediction distribution'): +def send_prediction_distribution(y_true, y_pred, + experiment=None, + figsize=(16, 12), + channel_name='prediction distribution'): """Creates prediction distribution chart and logs it in Neptune. Args: - ctx(`neptune.Context`): Neptune context. y_true (array-like, shape (n_samples)): Ground truth (correct) target values. - y_pred (array-like, shape (n_samples)): Predictions both for the positive class in the float format. + y_pred (array-like, shape (n_samples)): Predictions for the positive class in the float format. + experiment(`neptune.experiments.Experiment`): Neptune experiment. Default is None. figsize(tuple): size of the matplotlib.pyplot figure object channel_name(str): name of the neptune channel. Default is 'prediction distribution'. @@ -102,24 +111,34 @@ def send_prediction_distribution(ctx, y_true, y_pred, figsize=(16, 12), channel_ >>> import neptune >>> from neptunecontrib.monitoring.reporting import send_prediction_distribution >>> - >>> ctx = neptune.Context() - >>> send_prediction_distribution(ctx, y_test, y_test_pred[:, 1]) + >>> neptune.init(qualified_project_name='USER_NAME/PROJECT_NAME') + >>> + >>> with neptune.create_experiment(): + >>> send_prediction_distribution(ctx, y_test, y_test_pred[:, 1]) """ + + _exp = experiment if experiment else neptune + fig, ax = plt.subplots(figsize=figsize) plot_prediction_distribution(y_true, y_pred, ax=ax) - npt_pred_dist = neptune.Image(name='chart', description='', data=fig2pil(fig)) - ctx.channel_send(channel_name, npt_pred_dist) + with tempfile.NamedTemporaryFile(suffix='.png') as f: + fig.savefig(f.name) + _exp.send_image(channel_name, f.name) -def send_roc_auc_curve(ctx, y_true, y_pred, figsize=(16, 12), channel_name='ROC AUC curve'): + +def send_roc_auc_curve(y_true, y_pred, + experiment=None, + figsize=(16, 12), + channel_name='ROC AUC curve'): """Creates ROC AUC curve and logs it in Neptune. Args: - ctx(`neptune.Context`): Neptune context. y_true (array-like, shape (n_samples)): Ground truth (correct) target values. y_pred (array-like, shape (n_samples, 2)): Predictions both for negative and positive class in the float format. + experiment(`neptune.experiments.Experiment`): Neptune experiment. Default is None. figsize(tuple): size of the matplotlib.pyplot figure object channel_name(str): name of the neptune channel. Default is 'ROC AUC curve'. @@ -144,23 +163,33 @@ def send_roc_auc_curve(ctx, y_true, y_pred, figsize=(16, 12), channel_name='ROC >>> import neptune >>> from neptunecontrib.monitoring.reporting import send_roc_auc_curve >>> - >>> ctx = neptune.Context() - >>> send_roc_auc_curve(ctx, y_test, y_test_pred) + >>> neptune.init(qualified_project_name='USER_NAME/PROJECT_NAME') + >>> + >>> with neptune.create_experiment(): + >>> send_roc_auc_curve(ctx, y_test, y_test_pred) """ + + _exp = experiment if experiment else neptune + fig, ax = plt.subplots(figsize=figsize) plot_roc(y_true, y_pred, ax=ax) - npt_roc_auc = neptune.Image(name='chart', description='', data=fig2pil(fig)) - ctx.channel_send(channel_name, npt_roc_auc) + with tempfile.NamedTemporaryFile(suffix='.png') as f: + fig.savefig(f.name) + _exp.send_image(channel_name, f.name) -def send_confusion_matrix(ctx, y_true, y_pred, figsize=(16, 12), channel_name='confusion_matrix'): + +def send_confusion_matrix(y_true, y_pred, + experiment=None, + figsize=(16, 12), + channel_name='confusion_matrix'): """Creates ROC AUC curve and logs it in Neptune. Args: - ctx(`neptune.Context`): Neptune context. y_true (array-like, shape (n_samples)): Ground truth (correct) target values. y_pred (array-like, shape (n_samples)): Positive class predictions in the binary format. + experiment(`neptune.experiments.Experiment`): Neptune experiment. Default is None. figsize(tuple): size of the matplotlib.pyplot figure object channel_name(str): name of the neptune channel. Default is 'ROC AUC curve'. @@ -185,17 +214,26 @@ def send_confusion_matrix(ctx, y_true, y_pred, figsize=(16, 12), channel_name='c >>> import neptune >>> from neptunecontrib.monitoring.reporting import send_confusion_matrix >>> - >>> ctx = neptune.Context() - >>> send_confusion_matrix(ctx, y_test, y_test_pred[:, 1] > 0.5) + >>> neptune.init(qualified_project_name='USER_NAME/PROJECT_NAME') + >>> + >>> with neptune.create_experiment(): + >>> send_confusion_matrix(ctx, y_test, y_test_pred[:, 1] > 0.5) """ fig, ax = plt.subplots(figsize=figsize) plot_confusion_matrix(y_true, y_pred, ax=ax) - npt_conf_matrix = neptune.Image(name='chart', description='', data=fig2pil(fig)) - ctx.channel_send(channel_name, npt_conf_matrix) + _exp = experiment if experiment else neptune + + with tempfile.NamedTemporaryFile(suffix='.png') as f: + fig.savefig(f.name) + _exp.send_image(channel_name, f.name) -def send_precision_recall(ctx, y_true, y_pred, figsize=(16, 12), channel_name='precision_recall_curve'): + +def send_precision_recall(y_true, y_pred, + experiment=None, + figsize=(16, 12), + channel_name='precision_recall_curve'): """Creates precision recall curve and logs it in Neptune. Args: @@ -203,6 +241,7 @@ def send_precision_recall(ctx, y_true, y_pred, figsize=(16, 12), channel_name='p y_true (array-like, shape (n_samples)): Ground truth (correct) target values. y_pred (array-like, shape (n_samples, 2)): Predictions both for negative and positive class in the float format. + experiment(`neptune.experiments.Experiment`): Neptune experiment. Default is None. figsize(tuple): size of the matplotlib.pyplot figure object channel_name(str): name of the neptune channel. Default is 'ROC AUC curve'. @@ -227,14 +266,21 @@ def send_precision_recall(ctx, y_true, y_pred, figsize=(16, 12), channel_name='p >>> import neptune >>> from neptunecontrib.monitoring.reporting import send_precision_recall >>> - >>> ctx = neptune.Context() - >>> send_precision_recall(ctx, y_test, y_test_pred) + >>> neptune.init(qualified_project_name='USER_NAME/PROJECT_NAME') + >>> + >>> with neptune.create_experiment(): + >>> send_precision_recall(ctx, y_test, y_test_pred) """ + + _exp = experiment if experiment else neptune + fig, ax = plt.subplots(figsize=figsize) plot_precision_recall(y_true, y_pred, ax=ax) - npt_roc_auc = neptune.Image(name='chart', description='', data=fig2pil(fig)) - ctx.channel_send(channel_name, npt_roc_auc) + + with tempfile.NamedTemporaryFile(suffix='.png') as f: + fig.savefig(f.name) + _exp.send_image(channel_name, f.name) def plot_binary_classification_report(y_true, y_pred, threshold=0.5, figsize=(16, 12)): @@ -269,13 +315,11 @@ def plot_binary_classification_report(y_true, y_pred, threshold=0.5, figsize=(16 >>> >>> y_test_pred = model.predict_proba(X_test) - Log classification report to Neptune. + Plot binary classification report. - >>> import neptune - >>> from neptunecontrib.monitoring.reporting import send_binary_classification_report + >>> from neptunecontrib.monitoring.reporting import plot_binary_classification_report >>> - >>> ctx = neptune.Context() - >>> send_binary_classification_report(ctx, y_test, y_test_pred) + >>> plot_binary_classification_report(y_test, y_test_pred) """ fig, axs = plt.subplots(2, 2, figsize=figsize) @@ -322,7 +366,6 @@ def plot_prediction_distribution(y_true, y_pred, ax=None, figsize=None): Plot prediction distribution. - >>> import neptune >>> from neptunecontrib.monitoring.reporting import plot_prediction_distribution >>> >>> plot_prediction_distribution(y_test, y_test_pred[:, 1]) diff --git a/neptunecontrib/monitoring/skopt.py b/neptunecontrib/monitoring/skopt.py index 10e3089..ccf24d8 100644 --- a/neptunecontrib/monitoring/skopt.py +++ b/neptunecontrib/monitoring/skopt.py @@ -14,10 +14,13 @@ # limitations under the License. # +import tempfile + +import matplotlib.pyplot as plt import neptune import skopt.plots as sk_plots -from neptunecontrib.monitoring.utils import fig2pil, axes2fig +from neptunecontrib.monitoring.utils import axes2fig class NeptuneMonitor: @@ -28,8 +31,10 @@ class NeptuneMonitor: >>> import neptune >>> import neptunecontrib.monitoring.skopt as sk_utils - >>> ctx = neptune.Context() - >>> monitor = sk_utils.NeptuneMonitor(ctx) + >>> + >>> neptune.init(qualified_project_name='USER_NAME/PROJECT_NAME') + >>> + >>> monitor = sk_utils.NeptuneMonitor() Run skopt training passing monitor as a a callback @@ -39,25 +44,25 @@ class NeptuneMonitor: """ - def __init__(self, ctx): - self._ctx = ctx + def __init__(self, experiment=None): + self._exp = experiment if experiment else neptune self._iteration = 0 def __call__(self, res): - self._ctx.channel_send('hyperparameter_search_score', - x=self._iteration, y=res.func_vals[-1]) - self._ctx.channel_send('search_parameters', - x=self._iteration, y=NeptuneMonitor._get_last_params(res)) + self._exp.send_metric('run_score', + x=self._iteration, y=res.func_vals[-1]) + self._exp.send_text('run_parameters', + x=self._iteration, y=NeptuneMonitor._get_last_params(res)) self._iteration += 1 @staticmethod def _get_last_params(res): param_vals = res.x_iters[-1] named_params = _format_to_named_params(param_vals, res) - return named_params + return str(named_params) -def send_runs(results, ctx): +def send_runs(results, experiment=None): """Logs runs results and parameters to neptune. Text channel `hyperparameter_search_score` is created and a list of tuples (name, value) @@ -66,7 +71,7 @@ def send_runs(results, ctx): Args: results('scipy.optimize.OptimizeResult'): Results object that is typically an output of the function like `skopt.forest_minimize(...)` - ctx(`neptune.Context`): Neptune context. + experiment(`neptune.experiments.Experiment`): Neptune experiment. Default is None. Examples: Run skopt training. @@ -78,18 +83,23 @@ def send_runs(results, ctx): >>> import neptune >>> import neptunecontrib.monitoring.skopt as sk_utils - >>> ctx = neptune.Context() - >>> sk_monitor.send_best_parameters(results, ctx) + >>> + >>> neptune.init(qualified_project_name='USER_NAME/PROJECT_NAME') + >>> + >>> sk_monitor.send_best_parameters(results) """ + + _exp = experiment if experiment else neptune + for loss, params in zip(results.func_vals, results.x_iters): - ctx.channel_send('hyperparameter_search_score', y=loss) + _exp.send_metric('run_score', y=loss) named_params = _format_to_named_params(params, results) - ctx.channel_send('search_parameters', named_params) + _exp.send_text('run_parameters', str(named_params)) -def send_best_parameters(results, ctx): +def send_best_parameters(results, experiment=None): """Logs best_parameters list to neptune. Text channel `best_parameters` is created and a list of tuples (name, value) @@ -98,7 +108,7 @@ def send_best_parameters(results, ctx): Args: results('scipy.optimize.OptimizeResult'): Results object that is typically an output of the function like `skopt.forest_minimize(...)` - ctx(`neptune.Context`): Neptune context. + experiment(`neptune.experiments.Experiment`): Neptune experiment. Default is None. Examples: Run skopt training. @@ -110,16 +120,19 @@ def send_best_parameters(results, ctx): >>> import neptune >>> import neptunecontrib.monitoring.skopt as sk_utils - >>> ctx = neptune.Context() - >>> sk_monitor.send_best_parameters(results, ctx) + >>> + >>> neptune.init(qualified_project_name='USER_NAME/PROJECT_NAME') + >>> + >>> sk_monitor.send_best_parameters(results) """ - param_vals = results.x - named_params = _format_to_named_params(param_vals, results) - ctx.channel_send('best_parameters', named_params) + _exp = experiment if experiment else neptune + named_params = _format_to_named_params(results.x, results) + _exp.set_property('best_parameters', str(named_params)) -def send_plot_convergence(results, ctx): + +def send_plot_convergence(results, experiment=None, channel_name='convergence'): """Logs skopt plot_convergence figure to neptune. Image channel `convergence` is created and the output of the @@ -129,7 +142,7 @@ def send_plot_convergence(results, ctx): Args: results('scipy.optimize.OptimizeResult'): Results object that is typically an output of the function like `skopt.forest_minimize(...)` - ctx(`neptune.Context`): Neptune context. + experiment(`neptune.experiments.Experiment`): Neptune experiment. Default is None. Examples: Run skopt training. @@ -141,18 +154,24 @@ def send_plot_convergence(results, ctx): >>> import neptune >>> import neptunecontrib.monitoring.skopt as sk_utils - >>> ctx = neptune.Context() - >>> sk_monitor.send_plot_convergence(results, ctx) + >>> + >>> neptune.init(qualified_project_name='USER_NAME/PROJECT_NAME') + >>> + >>> sk_monitor.send_plot_convergence(results) """ - convergence = fig2pil(axes2fig(sk_plots.plot_convergence(results))) - ctx.channel_send('convergence', neptune.Image( - name='convergence', - description="plot_convergence from skopt", - data=convergence)) + _exp = experiment if experiment else neptune + + fig, ax = plt.subplots(figsize=(16, 12)) + sk_plots.plot_convergence(results, ax=ax) + + with tempfile.NamedTemporaryFile(suffix='.png') as f: + fig.savefig(f.name) + _exp.send_image(channel_name, f.name) -def send_plot_evaluations(results, ctx): + +def send_plot_evaluations(results, experiment=None, channel_name='evaluations'): """Logs skopt plot_evaluations figure to neptune. Image channel `evaluations` is created and the output of the @@ -162,7 +181,7 @@ def send_plot_evaluations(results, ctx): Args: results('scipy.optimize.OptimizeResult'): Results object that is typically an output of the function like `skopt.forest_minimize(...)` - ctx(`neptune.Context`): Neptune context. + experiment(`neptune.experiments.Experiment`): Neptune experiment. Default is None. Examples: Run skopt training. @@ -174,18 +193,23 @@ def send_plot_evaluations(results, ctx): >>> import neptune >>> import neptunecontrib.monitoring.skopt as sk_utils - >>> ctx = neptune.Context() - >>> sk_monitor.send_plot_evaluations(results, ctx) + >>> + >>> neptune.init(qualified_project_name='USER_NAME/PROJECT_NAME') + >>> + >>> sk_monitor.send_plot_evaluations(results) """ - evaluations = fig2pil(axes2fig(sk_plots.plot_evaluations(results, bins=10))) - ctx.channel_send('evaluations', neptune.Image( - name='evaluations', - description="plot_evaluations from skopt", - data=evaluations)) + _exp = experiment if experiment else neptune + + fig = plt.figure(figsize=(16, 12)) + fig = axes2fig(sk_plots.plot_evaluations(results, bins=10), fig=fig) + + with tempfile.NamedTemporaryFile(suffix='.png') as f: + fig.savefig(f.name) + _exp.send_image(channel_name, f.name) -def send_plot_objective(results, ctx): +def send_plot_objective(results, experiment=None, channel_name='objective'): """Logs skopt plot_objective figure to neptune. Image channel `objective` is created and the output of the @@ -195,35 +219,36 @@ def send_plot_objective(results, ctx): Args: results('scipy.optimize.OptimizeResult'): Results object that is typically an output of the function like `skopt.forest_minimize(...)` - ctx(`neptune.Context`): Neptune context. + experiment(`neptune.experiments.Experiment`): Neptune experiment. Default is None. Examples: Run skopt training. >>> results = skopt.forest_minimize(objective, space, - base_estimator='ET', n_calls=100, n_random_starts=10) + >>> base_estimator='ET', n_calls=100, n_random_starts=10) Send skopt plot_objective figure to neptune. >>> import neptune >>> import neptunecontrib.monitoring.skopt as sk_utils - >>> ctx = neptune.Context() - >>> sk_monitor.send_plot_objective(results, ctx) + >>> + >>> neptune.init(qualified_project_name='USER_NAME/PROJECT_NAME') + >>> + >>> sk_monitor.send_plot_objective(results) """ + + _exp = experiment if experiment else neptune + fig = plt.figure(figsize=(16, 12)) + try: - objective = fig2pil(axes2fig(sk_plots.plot_objective(results))) - ctx.channel_send('objective', neptune.Image( - name='objective', - description="plot_objective from skopt", - data=objective)) + fig = axes2fig(sk_plots.plot_objective(results), fig=fig) + with tempfile.NamedTemporaryFile(suffix='.png') as f: + fig.savefig(f.name) + _exp.send_image(channel_name, f.name) except Exception as e: print('Could not create ans objective chart due to error: {}'.format(e)) def _format_to_named_params(params, result): - param_names = [dim.name for dim in result.space.dimensions] - named_params = [] - for name, val in zip(param_names, params): - named_params.append((name, val)) - return named_params + return ([(dimension.name, param) for dimension, param in zip(result.space, params)]) diff --git a/neptunecontrib/monitoring/utils.py b/neptunecontrib/monitoring/utils.py index 1716d5d..3c69d23 100644 --- a/neptunecontrib/monitoring/utils.py +++ b/neptunecontrib/monitoring/utils.py @@ -17,72 +17,6 @@ from itertools import product import matplotlib.pyplot as plt -import numpy as np -from PIL import Image - - -def fig2pil(fig): - """Converts matplotlib fig to PIL.Image - - Args: - fig(`matplotlib.pyplot.figure`): Any matplotlib figure. - - Returns: - `PIL.Image`: figure, converted to PIL Image. - - Examples: - Create a figure: - - >>> import matplotlib.pyplot as plt - >>> import numpy as np - >>> import seaborn as sns - >>> fig = plt.figure(figsize=(16,12)) - >>> sns.distplot(np.random.random(100)) - - Convert to PIL. - - >>> pil_figure = fig2pil(fig) - - Note: - On some machines, using this function has cause matplotlib errors. - What helped every time was to change matplotlib backend by adding the following snippet - towards the top of your script: - - >>> import matplotlib - >>> matplotlib.use('Agg') - """ - fig.canvas.draw() - - w, h = fig.canvas.get_width_height() - buf = np.frombuffer(fig.canvas.tostring_argb(), dtype=np.uint8) - buf.shape = (w, h, 4) - buf = np.roll(buf, 3, axis=2) - - w, h, _ = buf.shape - return Image.frombytes("RGBA", (w, h), buf.tostring()) - - -def is_offline_context(context): - """Checks whether the context is offline. - - Args: - context(`neptune.Context`): Neptune context. - - Returns: - bool: Whether or not the context is offline. - - Examples: - Create a Neptune context: - - >>> import neptune - >>> context = neptune.Context() - - Check if it is offline: - - >>> is_offline_context(context) - True - """ - return context.params.__class__.__name__ == 'OfflineContextParams' def axes2fig(axes, fig=None): diff --git a/neptunecontrib/sync/with_json.py b/neptunecontrib/sync/with_json.py index 60b22fd..9e572c5 100644 --- a/neptunecontrib/sync/with_json.py +++ b/neptunecontrib/sync/with_json.py @@ -25,97 +25,99 @@ You can pass it either as --filepath or -f. project_name(str): Full name of the project. E.g. "neptune-ml/neptune-examples", You can pass it either as --project_name or -p. + neptune_api_token(str): Neptune api token. If you have NEPTUNE_API_TOKEN environment + variable set to your API token you can skip this parameter. + You can pass it either as --neptune_api_token or -t defaults to NEPTUNE_API_TOKEN. Example: Run the experiment and create experiment json in any language. For example, lets say your `experiment_data.json` is - >>> {"name": "baseline", - >>> "parameters": {"lr": 0.1, - >>> "batch_size": 32 - >>> }, - >>> "tags": ["base", "solution-1", "pytorch"], - >>> "channels": {"log_loss": {"x": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10], - >>> "y": [0, 1, 4, 9, 16, 25, 36, 49, 64, 81, 100] - >>> } - >>> }, - >>> "properties": {"data_version": "version_1" - >>> } - >>> } + ``` + { + 'name': 'example', + 'description': 'json tracking experiment', + 'params': {'lr': 0.1, + 'batch_size': 128, + 'dropount': 0.5 + }, + 'properties': {'data_version': '1231ffwefef9', + 'data_path': '/mnt/path/to/data' + }, + 'tags': ['resnet', 'no_preprocessing'], + 'upload_source_files': ['train.sh'], + 'send_metric': {'log_loss': {'x': [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10], + 'y': [0, 1, 4, 9, 16, 25, 36, 49, 64, 81, 100] + }, + 'accuracy': {'x': [0, 4, 5, 8, 9], + 'y': [0.23, 0.47, 0.62, 0.89, 0.92] + } + }, + 'send_text': {'hash': {'x': [0, 4, 5, 8, 9], + 'y': ['123123', 'as32e132', '123sdads', '123asdasd', ' asd324132a'] + }, + }, + 'send_image': {'heatmaps': {'x': [0, 1, 2], + 'y': ['img1.png', 'img2.png', 'img3.png'] + }, + }, + } + ``` Now you can sync your file with neptune. $ python neptunecontrib.sync.with_json \ + --neptune_api_token 'ey7123qwwskdnaqsojnd1ru0129e12e==' --project_name neptune-ml/neptune-examples \ --filepath experiment_data.json + +Note: + If you keep your neptune api token in the NEPTUNE_API_TOKEN environment variable + you can skip the --neptune_api_token + """ import argparse import json -from subprocess import call + +import neptune def main(arguments): - with open('neptune_sync_main.py', 'w') as main_file: - write_main_content(main_file, arguments.filepath) + with open(arguments.filepath, 'r') as fp: + json_exp = json.load(fp) + + neptune.init(api_token=arguments.neptune_api_token, + project_qualified_name=arguments.project_name) + + with neptune.create_experiment(name=json_exp['name'], + description=json_exp['description'], + params=json_exp['params'], + properties=json_exp['properties'], + tags=json_exp['tags'], + upload_source_files=json_exp['upload_source_files']): - with open('neptune_sync_config.yaml', 'w') as config_file: - write_config_content(config_file, arguments.filepath) + for name, channel_xy in json_exp['send_metric'].items(): + for x, y in zip(channel_xy['x'], channel_xy['y']): + neptune.send_metric(name, x=x, y=y) - call('neptune run \ - --exclude neptune_sync_main.py \ - --project {} \ - --config neptune_sync_config.yaml \ - neptune_sync_main.py'.format(arguments.project_name), shell=True) - call('rm neptune_sync_config.yaml neptune_sync_main.py', shell=True) + for name, channel_xy in json_exp['send_text'].items(): + for x, y in zip(channel_xy['x'], channel_xy['y']): + neptune.send_text(name, x=x, y=y) + + for name, channel_xy in json_exp['send_image'].items(): + for x, y in zip(channel_xy['x'], channel_xy['y']): + neptune.send_image(name, x=x, y=y) def parse_args(): parser = argparse.ArgumentParser() parser.add_argument('-f', '--filepath') + parser.add_argument('-t', '--neptune_api_token', default=None) parser.add_argument('-p', '--project_name') return parser.parse_args() -def write_main_content(main_file, experiment_filepath): - main_content = """ -import neptune -import json - -ctx = neptune.Context() - -with open('{}', 'r') as fp: - data = json.load(fp) - -if 'channels' in data.keys(): - for name, channel in data['channels'].items(): - for x, y in zip(channel['x'], channel['y']): - ctx.channel_send(name, x, y) - -if 'properties' in data.keys(): - for name, value in data['properties'].items(): - ctx.properties[name] = value - -if 'tags' in data.keys(): - ctx.tags.extend(data['tags']) -""" - - main_file.write(main_content.format(experiment_filepath)) - - -def write_config_content(config_file, experiment_filepath): - with open(experiment_filepath, 'r') as fp: - data = json.load(fp) - - if 'name' in data.keys(): - config_file.write('name: {}\n\n'.format(data['name'])) - - if 'parameters' in data.keys(): - config_file.write('parameters:\n') - for name, value in data['parameters'].items(): - config_file.write(' {}: {}\n'.format(name, value)) - - if __name__ == '__main__': args = parse_args() main(args) diff --git a/requirements.txt b/requirements.txt index a53a1de..1c04cb7 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,6 @@ oauthlib PyJWT -neptune-cli -neptune-lib +neptune-client requests>=2.20.0 requests-oauthlib>=1.0.0 pandas diff --git a/setup.py b/setup.py index ec44a86..b0ccec6 100644 --- a/setup.py +++ b/setup.py @@ -10,7 +10,7 @@ def main(): requirements = [r.strip() for r in f] setup( name='neptune-contrib', - version='0.3.1', + version='0.4.0', description='Neptune Python library contributions', author='neptune.ml', author_email='contact@neptune.ml', diff --git a/test_requirements.txt b/test_requirements.txt index 65b6f31..4ed9bc8 100644 --- a/test_requirements.txt +++ b/test_requirements.txt @@ -1,4 +1,4 @@ -neptune-cli +neptune-client mock==2.0.0 pylint==1.9.4 pytest==4.1.0 diff --git a/tests/neptunecontrib/__init__.py b/tests/neptunecontrib/__init__.py new file mode 100644 index 0000000..62a86a5 --- /dev/null +++ b/tests/neptunecontrib/__init__.py @@ -0,0 +1,15 @@ +# +# Copyright (c) 2019, Neptune Labs Sp. z o.o. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# diff --git a/tests/neptunecontrib/monitoring/test_config.yaml b/tests/neptunecontrib/monitoring/test_config.yaml deleted file mode 100644 index ec6ee7e..0000000 --- a/tests/neptunecontrib/monitoring/test_config.yaml +++ /dev/null @@ -1,3 +0,0 @@ -parameters: - lr: 0.01 - model: 'resnet18' \ No newline at end of file diff --git a/tests/neptunecontrib/monitoring/test_lightgbm.py b/tests/neptunecontrib/monitoring/test_lightgbm.py deleted file mode 100644 index 23ee484..0000000 --- a/tests/neptunecontrib/monitoring/test_lightgbm.py +++ /dev/null @@ -1,100 +0,0 @@ -# -# Copyright (c) 2019, Neptune Labs Sp. z o.o. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -import unittest - -import neptune -import lightgbm as lgb - -from neptunecontrib.monitoring.lightgbm import neptune_monitor -from tests.neptunecontrib.monitoring.objects_factory import get_dategen - - -class TestNeptuneMonitor(unittest.TestCase): - def setUp(self): - self.params = {'boosting_type': 'gbdt', - 'objective': 'multiclass', - 'num_class': 3, - 'num_leaves': 5, - 'learning_rate': 0.05, - 'feature_fraction': 0.2 - } - - self.ctx = neptune.Context() - - def test_k_fold_logging(self): - self.ctx.reset_all_channels() - - for fold_id, (lgb_train, lgb_eval) in enumerate(get_dategen()): - monitor = neptune_monitor(ctx=self.ctx, prefix='{}_'.format(fold_id)) - - gbm = lgb.train(self.params, - lgb_train, - num_boost_round=3, - valid_sets=[lgb_train, lgb_eval], - valid_names=['train', 'valid'], - callbacks=[monitor], - ) - - self.assertCountEqual(['0_train_multi_logloss', - '0_valid_multi_logloss', - '1_train_multi_logloss', - '1_valid_multi_logloss', - '2_train_multi_logloss', - '2_valid_multi_logloss'], self.ctx.job._channels.keys()) - - def test_no_valid_names(self): - self.ctx.reset_all_channels() - - for lgb_train, lgb_eval in get_dategen(): - monitor = neptune_monitor(ctx=self.ctx) - - gbm = lgb.train(self.params, - lgb_train, - num_boost_round=3, - valid_sets=[lgb_train, lgb_eval], - callbacks=[monitor], - ) - break - - self.assertCountEqual(['training_multi_logloss', - 'valid_1_multi_logloss'], self.ctx.job._channels.keys()) - - def test_logging_multiple_metrics(self): - self.ctx.reset_all_channels() - - params = self.params - params['metric'] = ['multi_logloss', 'multi_error'] - for lgb_train, lgb_eval in get_dategen(): - monitor = neptune_monitor(ctx=self.ctx) - - gbm = lgb.train(self.params, - lgb_train, - num_boost_round=3, - valid_sets=[lgb_train, lgb_eval], - valid_names=['train', 'valid'], - callbacks=[monitor], - ) - break - - self.assertCountEqual(['train_multi_error', - 'train_multi_logloss', - 'valid_multi_error', - 'valid_multi_logloss'], self.ctx.job._channels.keys()) - - -if __name__ == '__main__': - unittest.main() diff --git a/tests/neptunecontrib/monitoring/test_monitoring_utils.py b/tests/neptunecontrib/monitoring/test_monitoring_utils.py deleted file mode 100644 index 149223f..0000000 --- a/tests/neptunecontrib/monitoring/test_monitoring_utils.py +++ /dev/null @@ -1,54 +0,0 @@ -# -# Copyright (c) 2019, Neptune Labs Sp. z o.o. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -import unittest - -import numpy as np -import matplotlib.pyplot as plt -from PIL.Image import Image -import seaborn as sns - -from neptunecontrib.monitoring.utils import fig2pil, is_offline_context - - -class TestFig2Pil(unittest.TestCase): - def test_matplotlib_figure(self): - # when - figure = plt.figure() - pil_figure = fig2pil(figure) - - # then - self.assertIsInstance(pil_figure, Image) - - def test_seaborn_distplot(self): - # when - figure = plt.figure() - sns.distplot(np.random.random(100)) - pil_figure = fig2pil(figure) - - # then - self.assertIsInstance(pil_figure, Image) - - -class TestIsOfflineContext(unittest.TestCase): - def test_dummy(self): - # when - - # then - self.assertEqual({}, {}) - -if __name__ == '__main__': - unittest.main() diff --git a/tests/neptunecontrib/monitoring/test_notebooks.py b/tests/neptunecontrib/monitoring/test_notebooks.py deleted file mode 100644 index 6b95f22..0000000 --- a/tests/neptunecontrib/monitoring/test_notebooks.py +++ /dev/null @@ -1,48 +0,0 @@ -# -# Copyright (c) 2019, Neptune Labs Sp. z o.o. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -import unittest - -import neptune -import numpy as np - -from neptunecontrib.monitoring.notebooks import LocalNotebookContext - - -class TestLocalNotebookContext(unittest.TestCase): - CONFIG_FILEPATH = 'tests/neptunecontrib/monitoring/test_config.yaml' - - def test_get_params(self): - # when - ctx = neptune.Context() - ctx = LocalNotebookContext(ctx, config_filepath=TestLocalNotebookContext.CONFIG_FILEPATH) - - # then - self.assertEqual({'lr': 0.01, 'model': 'resnet18'}, ctx.params) - - def test_x_none(self): - # when - ctx = neptune.Context() - ctx = LocalNotebookContext(ctx, config_filepath=TestLocalNotebookContext.CONFIG_FILEPATH) - for i in range(10): - ctx.channel_send('test_channel', y=np.random.random()) - - # then - self.assertEqual(list(range(10)), ctx.numeric_channels['test_channel'].x) - - -if __name__ == '__main__': - unittest.main() diff --git a/tests/neptunecontrib/sync/__init__.py b/tests/neptunecontrib/sync/__init__.py new file mode 100644 index 0000000..62a86a5 --- /dev/null +++ b/tests/neptunecontrib/sync/__init__.py @@ -0,0 +1,15 @@ +# +# Copyright (c) 2019, Neptune Labs Sp. z o.o. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# diff --git a/tests/neptunecontrib/sync/json_exp.json b/tests/neptunecontrib/sync/json_exp.json new file mode 100644 index 0000000..e859e04 --- /dev/null +++ b/tests/neptunecontrib/sync/json_exp.json @@ -0,0 +1 @@ +{"name": "example", "description": "json tracking experiment", "params": {"lr": 0.1, "batch_size": 128, "dropount": 0.5}, "properties": {"data_version": "1231ffwefef9", "data_path": "/mnt/path/to/data"}, "tags": ["resnet", "no_preprocessing"], "upload_source_files": [], "send_metric": {"log_loss": {"x": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10], "y": [0, 1, 4, 9, 16, 25, 36, 49, 64, 81, 100]}, "accuracy": {"x": [0, 4, 5, 8, 9], "y": [0.23, 0.47, 0.62, 0.89, 0.92]}}, "send_text": {"hash": {"x": [0, 4, 5, 8, 9], "y": ["123123", "as32e132", "123sdads", "123asdasd", " asd324132a"]}}, "send_image": {}} \ No newline at end of file diff --git a/tests/neptunecontrib/monitoring/objects_factory.py b/tests/neptunecontrib/sync/test_with_json.py similarity index 52% rename from tests/neptunecontrib/monitoring/objects_factory.py rename to tests/neptunecontrib/sync/test_with_json.py index 226998f..d34bd4b 100644 --- a/tests/neptunecontrib/monitoring/objects_factory.py +++ b/tests/neptunecontrib/sync/test_with_json.py @@ -14,19 +14,13 @@ # limitations under the License. # -import lightgbm as lgb -from sklearn.model_selection import KFold -from sklearn.datasets import load_wine +import unittest -def get_dategen(): - data = load_wine() - cv = KFold(n_splits=3, random_state=1234) - for train_idx, test_idx in cv.split(data.target): - X_train, X_test = data.data[train_idx], data.data[test_idx] - y_train, y_test = data.target[train_idx], data.target[test_idx] +class TestMain(unittest.TestCase): + def test_dummy(self): + self.assertEqual(True, True) - lgb_train = lgb.Dataset(X_train, y_train) - lgb_eval = lgb.Dataset(X_test, y_test, reference=lgb_train) - - yield lgb_train, lgb_eval +if __name__ == '__main__': + unittest.main() + \ No newline at end of file