hypod


Namehypod JSON
Version 0.1.2 PyPI version JSON
download
home_page
SummaryHypod: A dataclass-based hyperparameter managing system
upload_time2024-02-15 05:34:05
maintainer
docs_urlNone
author
requires_python>=3.8
licenseMIT License Copyright (c) 2023 Jiseob Kim Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
keywords hyperparameter dataclass command-line
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # Hypod: A dataclass-based hyperparameter managing system

## Overview
Deep learning models are often composed of multiple networks, where each of the networks is composed of multiple layers, and the class of each layer or its hyperparameters differ by experiments. `Hypod` simplifies managing this complex hierarchy of hyperparameters utilizing the built-in [`dataclass`](https://docs.python.org/3/library/dataclasses.html).

`dataclass` derives the following benefits.
* Defining a group of hyperaparameters is easy with **type annotation** support.
* Creating it is easy with the auto-defined `__init__`.
* Many IDE's (e.g., PyCharm, VSCode) support "jump to the definition".

However, difficulties for using it "as-is" in hyperparameter-managing are:
* Handling nested dataclasses is not so good.
* Parsing strings or sys.argv to create a dataclass is not natively supported.
* Switching between multiple child dataclasses using a simple "tag" is cumbersome.

`Hypod` will handle all these difficulties for you with following advantages.
* Fast, lightweight implementation using the built-in `dataclass` and [descriptors](https://docs.python.org/3/howto/descriptor.html)
* Minimal dependency
* Type-checking (based on annotation)
* Parsing a stringified object or YAML to create the corresponding nested dataclass
* Ability to auto-make the corresponding module (e.g., layer, network), when defined as an inner class of that module.

### Comparison with other packages
* [Hydra](https://github.com/facebookresearch/hydra): Hydra is a popular package with the same purpose. Its _structured config_ mode allows defining a config with `dataclass` as well. However, Hydra converts a config into `DictConfig` object (from another package `omegaconf`) even when it is originally defined with `dataclass`. Thus, all the operations (modifying, getting values, merging) are done with `DictConfig` and this brings the following drawbacks compared to Hypod, which uses `dataclass` object all the time.
  * `DictConfig` is not type annotated.
  * "Go to the definition" in IDE cannot be done with `DictConfig`.
  * Inheritance strcuture of configs cannot be checked.
  * Complex value interpolation is difficult or impossible to implement. In Hypod `dataclass`, it can be done using built-in [`__post_init__()`](https://docs.python.org/3/library/dataclasses.html#post-init-processing) function (see `examples/advanced_usage.py`).


### Etymology
Hypod stands for **"A Pod of Hyperparameters, or A Hyperparameters-Pod"**. You can put various Hyperparameters as a Pod and compose them with other Pods as you wish.

## Quick start
### Install the package
`pip install hypod`

### Basic Usage

```python
from dataclasses import dataclass
from hypod import hypod


@dataclass
@hypod
class LayerHP:
    in_features: int
    out_features: int
    init_scale: float = 0.9


@dataclass
@hypod
class NetworkHP:
    num_layers: int
    layer_hp: LayerHP


if __name__ == "__main__":
    # Hypod can be created in the same manner as dataclass.
    net_hp1 = NetworkHP(num_layers=3, layer_hp=LayerHP(64, 32))
    print(net_hp1)
    # Hypod can be also created from parsing stringified objects.
    net_hp2 = NetworkHP(num_layers="3", layer_hp=LayerHP("64", 32))
    print(net_hp2)
    # Hypod class itself can be also created from a dictionary.
    net_hp3 = NetworkHP(num_layers=3, layer_hp=dict(in_features=64, out_features=32))
    print(net_hp3)

```

### Command-line Usage
With the Hypod classes defined the same as above, define the main function as follows.

```python
# main.py
from hypod import hypod_main


@hypod_main()  # parses sys.argv to construct the hypod in the first argument, `net_hp`
def main(net_hp: NetworkHP):
    print(net_hp)


if __name__ == "__main__":
    main()
```
Then, on the command-line, type as follows to obtain the same results as before.
`python main.py num_layers=3 layer_hp.in_features=64 layer_hp.out_features=32`


### Inheritance
Hypod can be subclassed with a **tag**.

```python
from dataclasses import dataclass

from hypod import hypod


@dataclass
@hypod
class Data:
    path: str
    batch_size: int = 4


@dataclass
@hypod
class FFHQData(Data, tag="ffhq"):
    path: str = "/path/to/FFHQ"
    meta: str = "Flicker-Faces HQ Data, containing 70k images"


@dataclass
@hypod
class FFHQDataLargeBatch(FFHQData, tag="ffhq_lg"):
    batch_size: int = 16


@dataclass
@hypod
class Model:
    data: Data
    net: NetworkHP


if __name__ == "__main__":
    model_with_ffhq = Model(
        data="ffhq", net=NetworkHP(num_layers=3, layer_hp=LayerHP(64, 32)),
    )
    print(model_with_ffhq)

    model_with_ffhq_lg = Model(
        data=dict(_tag="ffhq_lg"),  # Also can be created from dict with "_tag" key.
        net=NetworkHP(num_layers=3, layer_hp=LayerHP(64, 32)),
    )
    print(model_with_ffhq_lg)

    model_with_cifar10 = Model(
        data=dict(path="/path/to/cifar10"),
        net=NetworkHP(num_layers=3, layer_hp=LayerHP(64, 32)),
    )
    print(model_with_cifar10)
```

### Command-line Usage with Inheritance
With the Hypod classes defined the same as above, define the main function as follows.

```python
# main.py
from hypod import hypod_main


@hypod_main()  # parses sys.argv to construct the hypod in the first argument, `model`
def main(model: Model):
    print(model)


if __name__ == "__main__":
    main()
```
Then, in the command-line type as follows to obtain the same results as before.
`python main.py model.data=ffhq model.net.num_layers=3 model.net.layer_hp.in_features=64 model.net.layer_hp.out_features=32`
I.e., tag can be fed in the CLI as the root argument.

### Advanced Usage (Value Interpolation)
* Please refer to `advanced_usage.py` in the `examples` directory.
* Please note that manually updating the fields of a Hypod object is prohibited. You should always use [`replace`](https://docs.python.org/3/library/dataclasses.html#dataclasses.replace) which will automatically call `__post_init__()` to correctly process the value interpolation.



### FAQ
* (Q) When creating Hypod from dict, how does it know which hypod to create? 
  * (A) Via type annotation.
* (Q) Is `typing.Union` supported? 
  * (A) Yes. But when it is a union of `str` or `dict`, there are ambiguities in parsing the stringified objects. A warning or an error will be raised in this case.
* (Q) Does tag-based creation still work when `typing.Union` is used?
  * (A) Yes. The tag search will be done over all the types in the union.
* (Q) Are `typing.List` or other generic types supported?
  * (A) Yes.
* (Q) How can I use value interpolation?
  * (A) `dataclass` provides [`__post_init__()`](https://docs.python.org/3/library/dataclasses.html#post-init-processing) function where you can define interpolation rules in *Python*. E.g., if you want to make a hyperparameter `a` that has to be twice the value of `b` in some Hypod, write `self.a = 2 * self.b` in `__post_init__()` of that Hypod.
* (Q) Why not using `typing.dataclass_transform()` in implementing Hypod?
  * (A) Because requirement of Python>=3.11 is too strict for now.

            

Raw data

            {
    "_id": null,
    "home_page": "",
    "name": "hypod",
    "maintainer": "",
    "docs_url": null,
    "requires_python": ">=3.8",
    "maintainer_email": "",
    "keywords": "hyperparameter,dataclass,command-line",
    "author": "",
    "author_email": "Jiseob Kim <justjest@gmail.com>",
    "download_url": "https://files.pythonhosted.org/packages/7d/d1/fb7d34d4a141fb299afdf25160e4b89da7913d844b2275cb2020fa52b19c/hypod-0.1.2.tar.gz",
    "platform": null,
    "description": "# Hypod: A dataclass-based hyperparameter managing system\n\n## Overview\nDeep learning models are often composed of multiple networks, where each of the networks is composed of multiple layers, and the class of each layer or its hyperparameters differ by experiments. `Hypod` simplifies managing this complex hierarchy of hyperparameters utilizing the built-in [`dataclass`](https://docs.python.org/3/library/dataclasses.html).\n\n`dataclass` derives the following benefits.\n* Defining a group of hyperaparameters is easy with **type annotation** support.\n* Creating it is easy with the auto-defined `__init__`.\n* Many IDE's (e.g., PyCharm, VSCode) support \"jump to the definition\".\n\nHowever, difficulties for using it \"as-is\" in hyperparameter-managing are:\n* Handling nested dataclasses is not so good.\n* Parsing strings or sys.argv to create a dataclass is not natively supported.\n* Switching between multiple child dataclasses using a simple \"tag\" is cumbersome.\n\n`Hypod` will handle all these difficulties for you with following advantages.\n* Fast, lightweight implementation using the built-in `dataclass` and [descriptors](https://docs.python.org/3/howto/descriptor.html)\n* Minimal dependency\n* Type-checking (based on annotation)\n* Parsing a stringified object or YAML to create the corresponding nested dataclass\n* Ability to auto-make the corresponding module (e.g., layer, network), when defined as an inner class of that module.\n\n### Comparison with other packages\n* [Hydra](https://github.com/facebookresearch/hydra): Hydra is a popular package with the same purpose. Its _structured config_ mode allows defining a config with `dataclass` as well. However, Hydra converts a config into `DictConfig` object (from another package `omegaconf`) even when it is originally defined with `dataclass`. Thus, all the operations (modifying, getting values, merging) are done with `DictConfig` and this brings the following drawbacks compared to Hypod, which uses `dataclass` object all the time.\n  * `DictConfig` is not type annotated.\n  * \"Go to the definition\" in IDE cannot be done with `DictConfig`.\n  * Inheritance strcuture of configs cannot be checked.\n  * Complex value interpolation is difficult or impossible to implement. In Hypod `dataclass`, it can be done using built-in [`__post_init__()`](https://docs.python.org/3/library/dataclasses.html#post-init-processing) function (see `examples/advanced_usage.py`).\n\n\n### Etymology\nHypod stands for **\"A Pod of Hyperparameters, or A Hyperparameters-Pod\"**. You can put various Hyperparameters as a Pod and compose them with other Pods as you wish.\n\n## Quick start\n### Install the package\n`pip install hypod`\n\n### Basic Usage\n\n```python\nfrom dataclasses import dataclass\nfrom hypod import hypod\n\n\n@dataclass\n@hypod\nclass LayerHP:\n    in_features: int\n    out_features: int\n    init_scale: float = 0.9\n\n\n@dataclass\n@hypod\nclass NetworkHP:\n    num_layers: int\n    layer_hp: LayerHP\n\n\nif __name__ == \"__main__\":\n    # Hypod can be created in the same manner as dataclass.\n    net_hp1 = NetworkHP(num_layers=3, layer_hp=LayerHP(64, 32))\n    print(net_hp1)\n    # Hypod can be also created from parsing stringified objects.\n    net_hp2 = NetworkHP(num_layers=\"3\", layer_hp=LayerHP(\"64\", 32))\n    print(net_hp2)\n    # Hypod class itself can be also created from a dictionary.\n    net_hp3 = NetworkHP(num_layers=3, layer_hp=dict(in_features=64, out_features=32))\n    print(net_hp3)\n\n```\n\n### Command-line Usage\nWith the Hypod classes defined the same as above, define the main function as follows.\n\n```python\n# main.py\nfrom hypod import hypod_main\n\n\n@hypod_main()  # parses sys.argv to construct the hypod in the first argument, `net_hp`\ndef main(net_hp: NetworkHP):\n    print(net_hp)\n\n\nif __name__ == \"__main__\":\n    main()\n```\nThen, on the command-line, type as follows to obtain the same results as before.\n`python main.py num_layers=3 layer_hp.in_features=64 layer_hp.out_features=32`\n\n\n### Inheritance\nHypod can be subclassed with a **tag**.\n\n```python\nfrom dataclasses import dataclass\n\nfrom hypod import hypod\n\n\n@dataclass\n@hypod\nclass Data:\n    path: str\n    batch_size: int = 4\n\n\n@dataclass\n@hypod\nclass FFHQData(Data, tag=\"ffhq\"):\n    path: str = \"/path/to/FFHQ\"\n    meta: str = \"Flicker-Faces HQ Data, containing 70k images\"\n\n\n@dataclass\n@hypod\nclass FFHQDataLargeBatch(FFHQData, tag=\"ffhq_lg\"):\n    batch_size: int = 16\n\n\n@dataclass\n@hypod\nclass Model:\n    data: Data\n    net: NetworkHP\n\n\nif __name__ == \"__main__\":\n    model_with_ffhq = Model(\n        data=\"ffhq\", net=NetworkHP(num_layers=3, layer_hp=LayerHP(64, 32)),\n    )\n    print(model_with_ffhq)\n\n    model_with_ffhq_lg = Model(\n        data=dict(_tag=\"ffhq_lg\"),  # Also can be created from dict with \"_tag\" key.\n        net=NetworkHP(num_layers=3, layer_hp=LayerHP(64, 32)),\n    )\n    print(model_with_ffhq_lg)\n\n    model_with_cifar10 = Model(\n        data=dict(path=\"/path/to/cifar10\"),\n        net=NetworkHP(num_layers=3, layer_hp=LayerHP(64, 32)),\n    )\n    print(model_with_cifar10)\n```\n\n### Command-line Usage with Inheritance\nWith the Hypod classes defined the same as above, define the main function as follows.\n\n```python\n# main.py\nfrom hypod import hypod_main\n\n\n@hypod_main()  # parses sys.argv to construct the hypod in the first argument, `model`\ndef main(model: Model):\n    print(model)\n\n\nif __name__ == \"__main__\":\n    main()\n```\nThen, in the command-line type as follows to obtain the same results as before.\n`python main.py model.data=ffhq model.net.num_layers=3 model.net.layer_hp.in_features=64 model.net.layer_hp.out_features=32`\nI.e., tag can be fed in the CLI as the root argument.\n\n### Advanced Usage (Value Interpolation)\n* Please refer to `advanced_usage.py` in the `examples` directory.\n* Please note that manually updating the fields of a Hypod object is prohibited. You should always use [`replace`](https://docs.python.org/3/library/dataclasses.html#dataclasses.replace) which will automatically call `__post_init__()` to correctly process the value interpolation.\n\n\n\n### FAQ\n* (Q) When creating Hypod from dict, how does it know which hypod to create? \n  * (A) Via type annotation.\n* (Q) Is `typing.Union` supported? \n  * (A) Yes. But when it is a union of `str` or `dict`, there are ambiguities in parsing the stringified objects. A warning or an error will be raised in this case.\n* (Q) Does tag-based creation still work when `typing.Union` is used?\n  * (A) Yes. The tag search will be done over all the types in the union.\n* (Q) Are `typing.List` or other generic types supported?\n  * (A) Yes.\n* (Q) How can I use value interpolation?\n  * (A) `dataclass` provides [`__post_init__()`](https://docs.python.org/3/library/dataclasses.html#post-init-processing) function where you can define interpolation rules in *Python*. E.g., if you want to make a hyperparameter `a` that has to be twice the value of `b` in some Hypod, write `self.a = 2 * self.b` in `__post_init__()` of that Hypod.\n* (Q) Why not using `typing.dataclass_transform()` in implementing Hypod?\n  * (A) Because requirement of Python>=3.11 is too strict for now.\n",
    "bugtrack_url": null,
    "license": "MIT License  Copyright (c) 2023 Jiseob Kim  Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:  The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.  THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.",
    "summary": "Hypod: A dataclass-based hyperparameter managing system",
    "version": "0.1.2",
    "project_urls": {
        "repository": "https://github.com/delta-func/hypod"
    },
    "split_keywords": [
        "hyperparameter",
        "dataclass",
        "command-line"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "c18d46c8e4d647286d442b9e95b3d040799043e1c61e561b68f52b33aeaaa18d",
                "md5": "1ab04ba73230fc9404ea8ca65e8ed6bb",
                "sha256": "740c7b5aeddb3c4a5167e1848e3b3a66d19b781af900fddc3ce318d135b554f8"
            },
            "downloads": -1,
            "filename": "hypod-0.1.2-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "1ab04ba73230fc9404ea8ca65e8ed6bb",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.8",
            "size": 10749,
            "upload_time": "2024-02-15T05:34:01",
            "upload_time_iso_8601": "2024-02-15T05:34:01.631196Z",
            "url": "https://files.pythonhosted.org/packages/c1/8d/46c8e4d647286d442b9e95b3d040799043e1c61e561b68f52b33aeaaa18d/hypod-0.1.2-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "7dd1fb7d34d4a141fb299afdf25160e4b89da7913d844b2275cb2020fa52b19c",
                "md5": "585ea065957ca451ee1470275f16de3c",
                "sha256": "38bb74f44ea5b96a02af6207f8a677958dd891ce9e65f28b22a392b27342aa14"
            },
            "downloads": -1,
            "filename": "hypod-0.1.2.tar.gz",
            "has_sig": false,
            "md5_digest": "585ea065957ca451ee1470275f16de3c",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.8",
            "size": 12670,
            "upload_time": "2024-02-15T05:34:05",
            "upload_time_iso_8601": "2024-02-15T05:34:05.228884Z",
            "url": "https://files.pythonhosted.org/packages/7d/d1/fb7d34d4a141fb299afdf25160e4b89da7913d844b2275cb2020fa52b19c/hypod-0.1.2.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2024-02-15 05:34:05",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "delta-func",
    "github_project": "hypod",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": false,
    "requirements": [],
    "lcname": "hypod"
}
        
Elapsed time: 0.19355s