{"id":16392578,"url":"https://github.com/mbarbetti/optunapi","last_synced_at":"2025-08-03T23:05:55.239Z","repository":{"id":48428912,"uuid":"357996871","full_name":"mbarbetti/optunapi","owner":"mbarbetti","description":":package: API to distribute hyperparameters optimization through HTTP requests","archived":false,"fork":false,"pushed_at":"2022-05-19T15:29:38.000Z","size":121,"stargazers_count":0,"open_issues_count":2,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-07-04T04:07:02.345Z","etag":null,"topics":["distributed-computing","http-api-client","http-requests","machine-learning","optimization","parallel-computing"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mbarbetti.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2021-04-14T18:00:30.000Z","updated_at":"2023-02-14T10:25:18.000Z","dependencies_parsed_at":"2022-09-26T17:31:17.316Z","dependency_job_id":null,"html_url":"https://github.com/mbarbetti/optunapi","commit_stats":null,"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"purl":"pkg:github/mbarbetti/optunapi","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mbarbetti%2Foptunapi","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mbarbetti%2Foptunapi/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mbarbetti%2Foptunapi/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mbarbetti%2Foptunapi/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mbarbetti","download_url":"https://codeload.github.com/mbarbetti/optunapi/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mbarbetti%2Foptunapi/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":268625009,"owners_count":24280188,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-08-03T02:00:12.545Z","response_time":2577,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["distributed-computing","http-api-client","http-requests","machine-learning","optimization","parallel-computing"],"created_at":"2024-10-11T04:50:40.527Z","updated_at":"2025-08-03T23:05:55.216Z","avatar_url":"https://github.com/mbarbetti.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e\n  \u003cimg src=\"https://raw.githubusercontent.com/mbarbetti/optunapi/main/.github/images/optunapi-logo.png\" width=\"800\"/\u003e\n\u003c/div\u003e\n\n\u003ch3 align=\"center\"\u003e\n  \u003cem\u003eAPI to distribute hyperparameters optimization through HTTP requests\u003c/em\u003e\n\u003c/h3\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://pypi.python.org/pypi/optunapi/\"\u003e\u003cimg alt=\"PyPI - Python versions\" src=\"https://img.shields.io/pypi/pyversions/optunapi\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://pypi.python.org/pypi/optunapi/\"\u003e\u003cimg alt=\"PyPI - Version\" src=\"https://img.shields.io/pypi/v/optunapi\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://pypi.python.org/pypi/optunapi/\"\u003e\u003cimg alt=\"PyPI - Status\" src=\"https://img.shields.io/pypi/status/optunapi\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://pypi.python.org/pypi/optunapi/\"\u003e\u003cimg alt=\"PyPI - Downloads\" src=\"https://img.shields.io/pypi/dm/optunapi.svg\"\u003e\u003c/a\u003e\n  \u003c!--\n  \u003ca href=\"https://github.com/mbarbetti/optunapi/issues\"\u003e\u003cimg alt=\"GitHub - Issues\" src=\"https://img.shields.io/github/issues/mbarbetti/optunapi\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://github.com/mbarbetti/optunapi/pulls\"\u003e\u003cimg alt=\"GitHub - Pull-requests\" src=\"https://img.shields.io/github/issues-pr/mbarbetti/optunapi\"\u003e\u003c/a\u003e\n  --\u003e\n  \u003ca href=\"https://github.com/mbarbetti/optunapi/network/members\"\u003e\u003cimg alt=\"GitHub - Forks\" src=\"https://badgen.net/github/forks/mbarbetti/optunapi\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://github.com/mbarbetti/optunapi/stargazers/\"\u003e\u003cimg alt=\"GitHub - Stars\" src=\"https://img.shields.io/github/stars/mbarbetti/optunapi\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://zenodo.org/badge/latestdoi/357996871\"\u003e\u003cimg alt=\"DOI\" src=\"https://zenodo.org/badge/357996871.svg\"\u003e\u003c/a\u003e\n\u003c/p\u003e\n\n_OptunAPI_ is a simple API designed for Machine Learning applications that allows to distribute an automatic \nhyperparameters optimization over different machines through **HTTP requests**. Each set of hyperparameters \ncan be studied independently since the minima research does't require any gradients computation, but instead \nis performed through a **Bayesian optimization** based on [Optuna](https://optuna.org/). The machine running \nOptuna manages centrally the optimization studies -- the so-called \"Optuna-server\" -- providing sets of \nhyperparameters and assessing them by the scores evaluated and sent back by the single computing instance, \nnamed \"Trainer-client\". The HTTP requests underlying such client-server system are powered by [FastAPI](https://fastapi.tiangolo.com).\n\n## Key Features\n\nOptunAPI inherits most of the modern functionalities of Optuna and FastAPI:\n\n- **Lightweight and versatile**\n  - OptunAPI is entirely written in Python and has few dependencies.\n- **Easy to configure**\n  - For hyperparameters sampling, OptunAPI relies on [configuration files](#configuration-file) easy to set up.\n- **Easy to integrate**\n  - The hyperparameters values can be easily recover [decoding the HTTP response content](#trainer-client) from the server.\n- **Easy parallelization**\n  - Different machines can run the hyperparameters study in parallel, centrally coordinated by the server.\n- **Efficient optimization algorithms**\n  - The optimization task is headed by Optuna and its state-of-the-art algorithms.\n- **Quick visualization for study analysis**\n  - _TODO_ - OptunAPI provides a set of reports to monitor the status of the hyperparameters study.\n\n## Key Components\n\nTo understand how OptunAPI works, we need to spend a couple of words about its components:\n\n- [`Study`](https://optuna.readthedocs.io/en/stable/reference/generated/optuna.study.Study.html#optuna.study.Study) and\n  [`Trial`](https://optuna.readthedocs.io/en/stable/reference/generated/optuna.trial.Trial.html#optuna.trial.Trial) objects\n  from Optuna\n- Optuna's Ask-and-Tell interface\n- HTTP requests to map the hyperparameters space\n\n### Study and Trial\n\nA _study_ corresponds to an optimization task, i.e., a set of trials. This object provides interfaces to run a new\n[`Trial`](https://optuna.readthedocs.io/en/stable/reference/generated/optuna.trial.Trial.html#optuna.trial.Trial)\nand access trials' history. OptunAPI is designed so that, when the first machine ask for a hyperparameters set, it\nstarts a new study ([`create_study()`](https://optuna.readthedocs.io/en/stable/reference/generated/optuna.study.create_study.html#optuna.study.create_study)) identified according to the HTTP request submitted. Any other machines referring \nto the same optimization session don't initialize a new study, but recover the previous one ([`load_study()`](https://optuna.readthedocs.io/en/stable/reference/generated/optuna.study.load_study.html#optuna.study.load_study)) contributing \nto mapping the hyperparameters space.\n\nA _trial_ allows to prepare a particular set of hyperparameters and evaluate its capability of optimizing a objective\nfunction, not necessarily available in an explicit form as in the case of very complex Machine Learning algorithms.\nThis object provides the following interfaces to get parameter suggestion: \n\n- [`optuna.trial.Trial.suggest_categorical()`](https://optuna.readthedocs.io/en/stable/reference/generated/optuna.trial.Trial.html#optuna.trial.Trial.suggest_categorical) for categorical parameters\n- [`optuna.trial.Trial.suggest_int()`](https://optuna.readthedocs.io/en/stable/reference/generated/optuna.trial.Trial.html#optuna.trial.Trial.suggest_int) for integer parameters\n- [`optuna.trial.Trial.suggest_float()`](https://optuna.readthedocs.io/en/stable/reference/generated/optuna.trial.Trial.html#optuna.trial.Trial.suggest_float) for floating point parameters\n\nWith optional arguments of `step` and `log`, we can discretize or take the logarithm of integer and floating point parameters.\nThe following code block is taken from the [Optuna tutorial](https://optuna.readthedocs.io/en/stable/tutorial/10_key_features/002_configurations.html) and shows a standard use of these features:\n\n```Python\nimport optuna\n\ndef objective (trial):\n    # Categorical parameter\n    optimizer = trial.suggest_categorical ('optimizer', ['RMSprop', 'Adam'])\n\n    # Integer parameter\n    num_layers = trial.suggest_int ('num_layers', 1, 3)\n\n    # Integer parameter (log)\n    num_channels = trial.suggest_int ('num_channels', 32, 512, log = True)\n\n    # Integer parameter (discretized)\n    num_units = trial.suggest_int ('num_units', 10, 100, step = 5)\n\n    # Floating point parameter\n    dropout_rate = trial.suggest_float ('dropout_rate', 0.0, 1.0)\n\n    # Floating point parameter (log)\n    learning_rate = trial.suggest_float ('learning_rate', 1e-5, 1e-2, log = True)\n\n    # Floating point parameter (discretized)\n    drop_path_rate = trial.suggest_float ('drop_path_rate', 0.0, 1.0, step = 0.1)\n```\n\nOptunAPI uses these methods internally and requires only a [configuration file](#configuration-file) \ncorrectly filled to run the studies.\n\n### Ask-and-Tell Interface\n\nThe Optuna's _Ask-and-Tell_ interface provides a more flexible interface for hyperparameter optimization\nbased on the two following methods:\n\n- [`optuna.study.Study.ask()`](https://optuna.readthedocs.io/en/stable/reference/generated/optuna.study.Study.html#optuna.study.Study.ask) creates a trial that can sample hyperparameters\n- [`optuna.study.Study.tell()`](https://optuna.readthedocs.io/en/stable/reference/generated/optuna.study.Study.html#optuna.study.Study.tell) finishes the trial by passing `trial` and an objective value\n\nOptunAPI uses these methods in two different moments. When a machine ask for a set of hyperparameters,\nthat set belongs to a trial resulting from an _ask_ instance. Then, once the objective function was\nevaluated with that particular set of hyperparameters, the machine sends a new request encoding the\nobjective value allowing to close the corresponding trial with a _tell_ instance.\n\n### HTTP Requests\n\nOptunAPI provides a simple Python module to run a server able to centrally manage the optimization studies:\n[`optuna/optuna/server.py`](https://github.com/mbarbetti/optunapi/blob/main/optunapi/server.py). It is\nequipped with a set of _path operation functions_ relying on the FastAPI ecosystem:\n\n- `ping_server`\n  - the _path_ is `/optunapi/ping`\n  - the _operation_ is `GET`\n  - the _function_ allows to verify if the server is running\n- `read_hparams`\n  - the _path_ is `/optuna/hparams/{model_name}` (`model_name` is a _path parameter_)\n  - the _operation_ is `GET`\n  - the _function_ allows to start (or load) an Optuna study and send sets of hyperparameters\n- `send_score`\n  - the _path_ is `/optuna/score/{model_name}?trail_id=TRIAL_ID\u0026score=SCORE` (with _query parameters_)\n  - the _operation_ is `GET`\n  - the _function_ allows to finish the trial identified by `trial_id` with the `score` value\n\n## Requirements\n\nPython 3.6+\n\nOptunAPI is based on two modern and highly performant frameworks:\n\n- [Optuna](https://optuna.org/) for the optimization parts.\n- [FastAPI](https://fastapi.tiangolo.com) for the HTTP requests parts.\n\n## Installation\n\nOptunAPI is a [public repository](https://github.com/mbarbetti/optunapi) on GitHub.\n\n\u003cdiv class=\"termy\"\u003e\n\n```console\n$ git clone https://github.com/mbarbetti/optunapi.git\n\n---\u003e 100%\n```\n\n\u003c/div\u003e\n\nTo run and use OptunAPI it's preferable to create a virtual environment with Python 3.6+ and install Optuna and FastAPI within it.\n\n\u003cdiv class=\"termy\"\u003e\n\n```console\n$ pip install optuna fastapi\n\n---\u003e 100%\n```\n\n\u003c/div\u003e\n\nStanding on the shoulder of FastAPI, OptunAPI needs an ASGI server to run the so-called Optuna-server, \nsuch as [Uvicorn](https://www.uvicorn.org) or [Hypercorn](https://gitlab.com/pgjones/hypercorn).\n\n\u003cdiv class=\"termy\"\u003e\n\n```console\n$ pip install uvicorn[standard]\n\n---\u003e 100%\n```\n\n\u003c/div\u003e\n\n## Example\n\n### Configuration file\n\nThe high-level functions provided by Optuna [to suggest values for the hyperparameters](#study-and-trial) \nare replaced with an appropriate _configuration file_ in OptunAPI. Referring to the example reported in\nthe [Optuna tutorial](https://optuna.readthedocs.io/en/stable/tutorial/10_key_features/002_configurations.html),\nwhat follows is the corresponding YAML configuration file:\n\n```YAML\n# Categorical parameter\noptimizer:\n  name    : optimizer\n  type    : categorical\n  choices : \n            - RMSprop\n            - Adam\n\n# Integer parameter\nnum_layers:\n  name : num_layers\n  type : int\n  low  : 1\n  high : 3\n\n# Integer parameter (log)\nnum_channels:\n  name : num_channels\n  type : int\n  low  : 32\n  high : 52\n  log  : True\n\n# Integer parameter (discretized)\nnum_units:\n  name : num_units\n  type : int\n  low  : 10\n  high : 100\n  step : 5\n\n# Floating point parameter\ndropout_rate:\n  name : dropout_rate\n  type : float\n  low  : 0.0\n  high : 1.0\n\n# Floating point parameter (log)\nlearning_rate:\n  name : learning_rale\n  type : float\n  low  : 1e-5\n  high : 1e-2\n  log  : True\n\n# Floating point parameter (discretized)\ndrop_path_rate:\n  name : drop_path_rate\n  type : float\n  low  : 0.0\n  high : 1.0\n  step : 0.1\n```\n\n### Optuna-server\n\nPrepared the configuration file for the optimization session and saved it into \n[`optunapi/optunapi/config`](https://github.com/mbarbetti/optunapi/tree/main/optunapi/config),\nwe are ready to run the Optuna-server.\n\n\u003cdiv class=\"termy\"\u003e\n\n```console\n$ uvicorn server:optunapi\n\nINFO:     Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)\nINFO:     Started reloader process [28720]\nINFO:     Started server process [28722]\nINFO:     Waiting for application startup.\nINFO:     Application startup complete.\n```\n\n\u003c/div\u003e\n\n\u003cdetails markdown=\"1\"\u003e\n\u003csummary\u003eWhat does the command \u003ccode\u003euvicorn server:optunapi\u003c/code\u003e mean?\u003c/summary\u003e\n\nThe command `uvicorn server:optunapi` refers to:\n\n* `server`: the file `server.py` (the Python \"module\") in \n  [optunapi/optunapi](https://github.com/mbarbetti/optunapi/tree/main/optunapi).\n* `optunapi`: the object created inside of `server.py` with the line `optunapi = FastAPI()`.\n\n\u003c/details\u003e\n\nNote that Uvicorn sets `127.0.0.1` and `8000` as default values for the server IP and port.\nTo change the defaults it's enough launching the previous command with the arguments \n`--host` and `--port` followed by the chosen values.\n\n### Trainer-client\n\nThe optimization session is managed by an Optuna _study_, initialized with the first client HTTP request, \nor loaded and expanded by any other connecting machines. To refer to a particular optimization session a \nclient has to encode the name of the corresponding configuration file within its HTTP request.\n\nConsider the simple use-case provided by OptunAPI, where we want to find the minimum of a 2D-paraboloid:\n[`optunapi/tests/simple_client.py`](https://github.com/mbarbetti/optunapi/blob/main/tests/simple_client.py).\nSince the provided configuration file is named `optuna-test.yaml`, then the GET request submitted by the client \nto receive the hyperparameters set has to contain the string `'optuna-test'`:\n\n```Python\nimport requests\n\nHOST = 'http://127.0.0.1:8000'\n\nread_hparams = requests.get (HOST + '/optunapi/hparams/optunapi-test')\nhp_req = read_hparams.json()\n\nTRIAL_ID = hp_req ['trial_id']\nPARAMS   = hp_req [ 'params' ]\n```\n\nWhat happens behind the scenes is that the above HTTP request calls an _ask_ instance to the Optuna \n_study_, stored in [`optunapi/optunapi/db`](https://github.com/mbarbetti/optunapi/tree/main/optunapi/db) \nonce created and named `optunapi-test.db`. As already said, an _ask_ instance is a _trial_ equipped with \na set of hyperparameters and the client can recover those values decoding the corresponding HTTP response. \nIn the example above, `hp_req` is a dictionary containing, among others, the identifier number of the current \n_trial_ (`TRIAL_ID`) and a dictionary for the hyperparameters values (`PARAMS`). \n\nHaving accessed to the hyperparameters values, we can perform whatever learning algorithm one prefers and \nevaluate the associated training score, that will be used as _objective value_ to finish the _trial_ instance.\nThis is done with a new GET request referring to the same optimization session (again, `'optunapi-test'` in the path) \nand passing `TRIAL_ID` and `SCORE` as query parameters:\n\n```Python\nimport requests\n\nHOST = 'http://127.0.0.1:8000'\n\nsend_score = requests.get (HOST + '/optunapi/score/optunapi-test?trial_id=TRIAL_ID\u0026score=SCORE')\nscore_req  = send_score.json()\n\nBEST_TRIAL_ID = score_req ['best_score_id']\nBEST_PARAMS   = score_req [ 'best_params' ]\n```\n\nEach running client allows to refine the search for minima performed by the Optuna algorithms, focusing \non smaller and smaller space portion and enhancing the mapping of the hyperparameters space.\n\n## Securing HTTP requests\n\nOptunAPI is designed to be used within a VPN not directly opened to the public Internet. On the other hand, \nopening the Optuna-server to Internet allows to exploit easily a wide variety of computing resources, from \non-premises machines to instances deriving from different cloud computing services (AWS, Azure, GCP, etc.).\nSuch design raises a security issue since anyone can submit a request to the server or catch its response, \nopening the system to cyberattack.\n\nA possible solution to this issue relies on the SSH protocol. The idea is to set up the Optuna-server\nas a _private server_ (from the perspective of `REMOTE SERVER`) not directly visible from the outside \n(`LOCAL CLIENT`’s perspective). This configuration, schematically represented in the sketch below, \nallows a _local client_ to still access the _private server_ passing through the _remote server_ \nauthenticating with SSH credentials. \n\n```\n    ----------------------------------------------------------------------\n\n                                |\n    -------------+              |    +----------+               +---------\n        LOCAL    |              |    |  REMOTE  |               | PRIVATE\n        CLIENT   | \u003c== SSH ========\u003e |  SERVER  | \u003c== local ==\u003e | SERVER\n    -------------+              |    +----------+               +---------\n                                |\n                             FIREWALL (only port 22 is open)\n\n    ----------------------------------------------------------------------\n```\n\nOptunAPI provides a very simple implementation of this scheme: \n[`optunapi/tests/secured_client.py`](https://github.com/mbarbetti/optunapi/blob/main/tests/secured_client.py).\nIt is based on [sshtunnel](https://github.com/pahaz/sshtunnel/) and allows to submit a HTTP request to the\n_private server_ after having specifying our SSH credentials (`ssh_username`, `ssh_pkey`).\n\n```Python\nimport sshtunnel\nimport requests\n\nwith sshtunnel.open_tunnel (\n  (REMOTE_SERVER_IP, 22),\n  ssh_username = 'mbarbetti',\n  ssh_pkey = '/home/mbarbetti/.ssh/id_rsa',\n  remote_bind_address = (PRIVATE_SERVER_IP, PRIVATE_SERVER_PORT),\n  local_bind_address  = ('127.0.0.1', 10022)\n) as tunnel:\n  ping_server = requests.get ('http://localhost:10022/optunapi/ping')\n  ping_msg = ping_server.json()\n  print (ping_msg)\n```\n\n\u003cdetails markdown=\"1\"\u003e\n\u003csummary\u003eHow to run the server in this case?\u003c/summary\u003e\n\nIn this configuration the Optuna-server acts as _private server_, \nthen its IP and port are the ones declared within the `with` statement:\n\n```\n$ uvicorn server:optunapi --host PRIVATE_SERVER_IP --port PRIVATE_SERVER_PORT\n```\n\u003c/details\u003e\n\n## License\n\nThis project is licensed under the terms of the MIT license.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmbarbetti%2Foptunapi","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmbarbetti%2Foptunapi","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmbarbetti%2Foptunapi/lists"}