Skip to content

Commit 0aedbdb

Browse files
committed
update zh quick start
1 parent fbe91a9 commit 0aedbdb

File tree

6 files changed

+307
-156
lines changed

6 files changed

+307
-156
lines changed

docs/quick_start.zh.md

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
快速开始
2+
=======
3+
4+
`HyperParameter` 是一个配置参数管理框架,为Python应用提供超参配置与参数调优等功能。可通过如下命令快速安装:
5+
6+
```shell
7+
pip install hyperparameter
8+
```
9+
10+
主要特性:
11+
12+
1. `param_scope` 上下文,向Python应用提供线程安全的、可嵌套的参数管理上下文;提供对象化的树状参数管理,并支持默认值;
13+
14+
```python
15+
>>> from hyperparameter import param_scope
16+
>>> with param_scope(param1=1) as ps:
17+
... print(f"param1={ps.param1}, param2={ps.param2('undefined')}")
18+
param1=1, param2=undefined
19+
20+
```
21+
22+
2. `auto_param` 装饰器,自动将函数(或者class)的默认参数转化为超参,并接受`param_scope`的参数控制;
23+
24+
```python
25+
>>> from hyperparameter import auto_param, param_scope
26+
>>> @auto_param
27+
... def foo(a, b="default"):
28+
... print(f"a={a}, b={b}")
29+
30+
>>> foo(0)
31+
a=0, b=default
32+
33+
>>> with param_scope(**{"foo.b": "modified"}):
34+
... foo(0)
35+
a=0, b=modified
36+
37+
```
38+
39+
超参配置
40+
-------
41+
42+
1. 通过`param_scope`可以直接读取超参配置,而无需任何配置:
43+
44+
```python
45+
>>> from hyperparameter import param_scope
46+
>>> def foo():
47+
... # read parameter from param_scope
48+
... p = param_scope.param("default")
49+
... p2 = param_scope.namespace.param2("default2")
50+
... print(f"p={p}, p2={p2}")
51+
52+
```
53+
54+
在上述函数`foo`中,尝试访问名为`param`的超参,超参默认值为`default``param_scope`首先尝试从上下文中读取同名参数并返回给调用者,若超参未定义则返回默认值。为了更好的组织参数,也可以给参数名添加命名空间`namespace.param`。命名空间也支持嵌套多层,比如`namespace.subspace.param`
55+
56+
57+
2. 通过`param_scope`传递超参
58+
59+
```python
60+
# call `foo` with default parameter
61+
>>> foo()
62+
p=default, p2=default2
63+
64+
# call `foo` with modified parameter
65+
>>> with param_scope("namespace.param2=modified"):
66+
... foo()
67+
p=default, p2=modified
68+
69+
```
70+
71+
通过`with param_scope(...)`传递参数的时候支持两种语法,字符串语法与字典语法。字典语法如下所示:
72+
73+
```python
74+
# call `foo` with modified parameter
75+
>>> with param_scope(**{
76+
... "param": "modified",
77+
... "namespace": {"param2": "modified2"}
78+
... }):
79+
... foo()
80+
p=modified, p2=modified2
81+
82+
```
83+
字典语法适合配合配置文件使用。
84+
85+
86+
3. `param_scope`可以穿透多层函数调用传递参数:
87+
88+
```python
89+
>>> def bar():
90+
... foo()
91+
92+
# call `foo` within nested function call
93+
>>> with param_scope("namespace.param2=modified"):
94+
... bar()
95+
p=default, p2=modified
96+
97+
```
98+
99+
### manage hyper-parameters with `param_scope`
100+
101+
`param_scope` create a `HyperParameter` object for the context it manages:
102+
103+
```python
104+
>>> from hyperparameter import param_scope
105+
>>> with param_scope(param1=1, param2="A") as ps:
106+
... ps.param1
107+
1
108+
109+
```
110+
111+
自动超参
112+
-------
113+
114+
1. `auto_param` 可以自动为函数(或者class)添加超参配置功能
115+
116+
```python
117+
>>> from hyperparameter import auto_param
118+
>>> @auto_param
119+
... def foo(param, param1=1):
120+
... print(f"param={param}, param1={param1}")
121+
122+
>>> foo(0)
123+
param=0, param1=1
124+
125+
```
126+
127+
2. 通过`param_scope``auto_param`传递参数:
128+
129+
```python
130+
>>> with param_scope(**{"foo.param1": 0}):
131+
... foo(0)
132+
param=0, param1=0
133+
134+
```

hyperparameter/__init__.py

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,28 @@
1-
from .hyperparameter import HyperParameter
2-
from .hyperparameter import param_scope, reads, writes, all_params
3-
from .hyperparameter import auto_param, set_auto_param_callback
4-
from .hyperparameter import dynamic_dispatch, lazy_dispatch, suggest_from
1+
from .hyperparameter import (
2+
auto_param,
3+
dynamic_dispatch,
4+
HyperParameter,
5+
param_scope,
6+
set_auto_param_callback,
7+
)
8+
from .tracker import all_params, reads, writes
9+
from .tune import suggest_from, lazy_dispatch
510

611
__all__ = [
12+
# base class for hyper-parameters
713
"HyperParameter",
14+
# api for parameter configuration
15+
"auto_param",
16+
"param_scope",
817
"dynamic_dispatch",
18+
"set_auto_param_callback",
19+
# api for parameter tuning
920
"suggest_from",
1021
"lazy_dispatch",
11-
"param_scope",
22+
# api for parameter tracking
1223
"reads",
1324
"writes",
1425
"all_params",
15-
"auto_param",
16-
"set_auto_param_callback",
1726
]
1827

19-
VERSION = "0.3.3"
28+
VERSION = "0.4.0"

hyperparameter/hyperparameter.py

Lines changed: 4 additions & 148 deletions
Original file line numberDiff line numberDiff line change
@@ -1,111 +1,9 @@
11
import inspect
2-
from operator import getitem
32
import threading
4-
from typing import Any, Callable, Dict, List, Optional, Set
3+
from typing import Any, Callable, Dict, Set
54

6-
7-
def _sorted_set(s):
8-
retval = list(s)
9-
retval.sort()
10-
return retval
11-
12-
13-
class _Tracker:
14-
"""parameter access tracker
15-
16-
Examples
17-
--------
18-
>>> _tracker.clear()
19-
>>> ps = param_scope(a=1, b={'c': 2})
20-
>>> reads(), writes() # empty read/write trackers
21-
([], [])
22-
23-
Only access through accessor is tracked, raw access
24-
to the parameter is not tracked.
25-
>>> ps.a, ps().b.c(3)
26-
(1, 2)
27-
>>> reads()
28-
['b.c']
29-
30-
>>> ps.a = 1
31-
>>> ps().a.b.c = 1
32-
>>> writes()
33-
['a.b.c']
34-
"""
35-
36-
def __init__(self) -> None:
37-
self._get: Set[str] = set()
38-
self._put: Set[str] = set()
39-
40-
def clear(self):
41-
self._get.clear()
42-
self._put.clear()
43-
44-
def read(self, key: Optional[str] = None) -> Optional[List[str]]:
45-
return self._get.add(key) if key else _sorted_set(self._get)
46-
47-
def write(self, key: Optional[str] = None) -> Optional[List[str]]:
48-
return self._put.add(key) if key else _sorted_set(self._put)
49-
50-
def all(self):
51-
return _sorted_set(self._get.union(self._put))
52-
53-
54-
_tracker = _Tracker()
55-
56-
57-
def reads():
58-
return _tracker.read()
59-
60-
61-
def writes():
62-
return _tracker.write()
63-
64-
65-
def all_params():
66-
return _tracker.all()
67-
68-
69-
class Suggester:
70-
def __init__(self, callback: Callable) -> None:
71-
self._callback = callback
72-
73-
def __call__(self):
74-
return self._callback()
75-
76-
77-
def suggest_from(callback: Callable) -> Suggester:
78-
"""Suggest parameter from a callback function
79-
80-
Examples
81-
--------
82-
>>> class ValueWrapper:
83-
... def __init__(self, lst):
84-
... self._lst = lst
85-
... self._offset = 0
86-
... def __call__(self):
87-
... index, self._offset = self._offset % len(self._lst), self._offset + 1
88-
... return self._lst[index]
89-
90-
>>> with param_scope(suggested = suggest_from(ValueWrapper([1,2,3]))) as ps:
91-
... ps().suggested()
92-
... ps().suggested()
93-
... ps().suggested()
94-
1
95-
2
96-
3
97-
"""
98-
return Suggester(callback)
99-
100-
101-
def _unwrap_suggester(func):
102-
def wrapper(*args, **kwargs):
103-
retval = func(*args, **kwargs)
104-
if isinstance(retval, Suggester):
105-
return retval()
106-
return retval
107-
108-
return wrapper
5+
from .tracker import _tracker
6+
from .tune import unwrap_suggester
1097

1108

1119
class _Accessor(dict):
@@ -117,7 +15,7 @@ def __init__(self, root, path=None, scope=None):
11715
self._path = path
11816
self._scope = scope
11917

120-
@_unwrap_suggester
18+
@unwrap_suggester
12119
def get_or_else(self, default: Any = None):
12220
"""Get value for the parameter, or get default value if the parameter is not defined."""
12321
if self._scope is not None:
@@ -217,48 +115,6 @@ def dynamic_dispatch(func, name=None, index=None):
217115
return new_class(func, name, index)
218116

219117

220-
class LazyDispatch:
221-
"""Dynamic call dispatcher
222-
223-
Examples
224-
--------
225-
226-
>>> class ExampleObj:
227-
... def get_42(self, offset):
228-
... return 42+offset
229-
230-
>>> lazy_obj = lazy_dispatch(ExampleObj())
231-
>>> lazy_obj.get_42(0)()
232-
42
233-
"""
234-
235-
def __init__(self, obj: Any, name=None, index=None):
236-
self._obj = obj
237-
self._name = name
238-
self._index = index
239-
240-
def __call__(self, *args, **kws) -> Any:
241-
obj = self._obj
242-
for n in self._name.split("."):
243-
obj = getattr(obj, n)
244-
if self._index is not None:
245-
obj = getitem(obj, self._index)
246-
return Suggester(lambda: obj(*args, **kws))
247-
248-
def __getattr__(self, name: str) -> Any:
249-
if self._name is not None:
250-
name = f"{self._name}.{name}"
251-
return lazy_dispatch(self._obj, name, self._index)
252-
253-
def __getitem__(self, index):
254-
return lazy_dispatch(self._obj, self._name, index)
255-
256-
257-
def lazy_dispatch(obj, name=None, index=None):
258-
"""Wraps an object for lazy dispatch"""
259-
return LazyDispatch(obj, name, index)
260-
261-
262118
class HyperParameter(dict):
263119
"""HyperParameter is an extended dict designed for parameter storage.
264120

0 commit comments

Comments
 (0)