zerograd 0.2.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- zerograd/__init__.py +48 -0
- zerograd/_http.py +86 -0
- zerograd/_progress.py +49 -0
- zerograd/_remote.py +594 -0
- zerograd/_ui.py +267 -0
- zerograd/_version.py +1 -0
- zerograd/client.py +59 -0
- zerograd/curve.py +239 -0
- zerograd/errors.py +70 -0
- zerograd/estimators.py +152 -0
- zerograd/local/__init__.py +5 -0
- zerograd/local/_errors.py +10 -0
- zerograd/local/championship.py +482 -0
- zerograd/local/engine.py +728 -0
- zerograd/local/preprocess.py +273 -0
- zerograd/local/threshold.py +91 -0
- zerograd/local/validation.py +156 -0
- zerograd/models/__init__.py +34 -0
- zerograd/models/_vendor/__init__.py +1 -0
- zerograd/models/_vendor/fomo0d/LICENSE +21 -0
- zerograd/models/_vendor/fomo0d/__init__.py +1 -0
- zerograd/models/_vendor/fomo0d/encoders.py +279 -0
- zerograd/models/_vendor/fomo0d/layer.py +249 -0
- zerograd/models/_vendor/fomo0d/transformer.py +369 -0
- zerograd/models/_vendor/fomo0d/utils.py +342 -0
- zerograd/models/_vendor/zeus/LICENSE +176 -0
- zerograd/models/_vendor/zeus/NOTICE +1 -0
- zerograd/models/_vendor/zeus/__init__.py +1 -0
- zerograd/models/_vendor/zeus/encoders.py +21 -0
- zerograd/models/_vendor/zeus/layer.py +150 -0
- zerograd/models/_vendor/zeus/model_utils.py +42 -0
- zerograd/models/_vendor/zeus/zeus.py +164 -0
- zerograd/models/catalog.py +237 -0
- zerograd/models/commercial.py +267 -0
- zerograd/models/noncommercial.py +221 -0
- zerograd/models/registry.py +272 -0
- zerograd/models/unsupervised.py +234 -0
- zerograd/privacy.py +308 -0
- zerograd/unsupervised/__init__.py +27 -0
- zerograd/unsupervised/chain.py +134 -0
- zerograd/unsupervised/table.py +157 -0
- zerograd/unsupervised/tasks.py +618 -0
- zerograd-0.2.0.dist-info/METADATA +395 -0
- zerograd-0.2.0.dist-info/RECORD +47 -0
- zerograd-0.2.0.dist-info/WHEEL +4 -0
- zerograd-0.2.0.dist-info/licenses/LICENSE +201 -0
- zerograd-0.2.0.dist-info/licenses/NOTICE +29 -0
zerograd/__init__.py
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"""ZeroGrad: predictions over your tabular data with foundation models. Nothing to train.
|
|
2
|
+
|
|
3
|
+
from zerograd import ZeroGrad
|
|
4
|
+
zg = ZeroGrad() # local, on your machine
|
|
5
|
+
cap = zg.create("titanic-survival", examples=train_df, target="Survived")
|
|
6
|
+
cap.evaluation # every model on the same holdout
|
|
7
|
+
cap.predict(new_df)
|
|
8
|
+
|
|
9
|
+
zg = ZeroGrad(remote=True) # the ZeroGrad platform (ZEROGRAD_API_KEY)
|
|
10
|
+
|
|
11
|
+
scikit-learn: ZeroGradClassifier / ZeroGradRegressor. Honest evaluation: zg.cross_validate(df, target, groups=, time=).
|
|
12
|
+
Then cap.optimize_threshold("cost", cost_fn=20) and cap.importance(test_df).
|
|
13
|
+
|
|
14
|
+
No target? zg.anomalies(df), zg.impute(df), zg.synthesize(df) and zg.cluster(df) run locally (zerograd.unsupervised).
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
from ._remote import Capability, Pipeline, RemoteZeroGrad, Run
|
|
18
|
+
from ._version import __version__
|
|
19
|
+
from .client import ZeroGrad
|
|
20
|
+
from .curve import ContextCurve
|
|
21
|
+
from .errors import (
|
|
22
|
+
APIError,
|
|
23
|
+
AuthError,
|
|
24
|
+
LimitError,
|
|
25
|
+
NotFoundError,
|
|
26
|
+
RunFailedError,
|
|
27
|
+
UsageWarning,
|
|
28
|
+
ValidationError,
|
|
29
|
+
ZeroGradError,
|
|
30
|
+
)
|
|
31
|
+
from .estimators import ZeroGradClassifier, ZeroGradRegressor
|
|
32
|
+
from .local import LocalCapability, LocalZeroGrad, load
|
|
33
|
+
from .local.threshold import ThresholdResult
|
|
34
|
+
from .local.validation import CVResult
|
|
35
|
+
from .models import LicenseGuardError, NonCommercialModelWarning, list_models, list_tasks, model_info, register_model
|
|
36
|
+
from .privacy import SensitiveDataWarning, Vault, drop_sensitive, scan
|
|
37
|
+
from .unsupervised import AnomalyResult, ClusterResult, ImputeResult, SyntheticResult
|
|
38
|
+
|
|
39
|
+
__all__ = [
|
|
40
|
+
"ZeroGrad", "LocalZeroGrad", "RemoteZeroGrad", "LocalCapability", "Capability", "Run", "Pipeline", "load", "ContextCurve",
|
|
41
|
+
"list_models", "list_tasks", "model_info", "register_model", "NonCommercialModelWarning", "LicenseGuardError",
|
|
42
|
+
"ZeroGradError", "AuthError", "NotFoundError", "ValidationError", "LimitError",
|
|
43
|
+
"RunFailedError", "APIError", "UsageWarning",
|
|
44
|
+
"Vault", "scan", "drop_sensitive", "SensitiveDataWarning",
|
|
45
|
+
"AnomalyResult", "ImputeResult", "SyntheticResult", "ClusterResult",
|
|
46
|
+
"ZeroGradClassifier", "ZeroGradRegressor", "CVResult", "ThresholdResult",
|
|
47
|
+
"__version__",
|
|
48
|
+
]
|
zerograd/_http.py
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import random
|
|
4
|
+
import time
|
|
5
|
+
import warnings
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
import httpx
|
|
9
|
+
|
|
10
|
+
from ._version import __version__
|
|
11
|
+
from .errors import APIError, UsageWarning, ZeroGradError, from_response
|
|
12
|
+
|
|
13
|
+
RETRY_STATUS = {429, 502, 503, 504}
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class Transport:
|
|
17
|
+
"""httpx with auth, retries (cold starts, 429, 5xx gateways) and error mapping."""
|
|
18
|
+
|
|
19
|
+
def __init__(self, base_url: str, api_key: str, timeout: float, http_client: httpx.Client | None, max_retries: int):
|
|
20
|
+
self.base_url = base_url.rstrip("/")
|
|
21
|
+
self.max_retries = max_retries
|
|
22
|
+
headers = {"Authorization": f"Bearer {api_key}", "User-Agent": f"zerograd-python/{__version__}"}
|
|
23
|
+
if http_client is not None:
|
|
24
|
+
http_client.headers.update(headers)
|
|
25
|
+
self.http = http_client
|
|
26
|
+
else:
|
|
27
|
+
self.http = httpx.Client(base_url=self.base_url, headers=headers, timeout=timeout)
|
|
28
|
+
self._warned = False
|
|
29
|
+
|
|
30
|
+
def request(self, method: str, path: str, *, raw: bool = False, **kwargs: Any) -> Any:
|
|
31
|
+
url = path if path.startswith("http") else self.base_url + path
|
|
32
|
+
last: Exception | None = None
|
|
33
|
+
for attempt in range(self.max_retries + 1):
|
|
34
|
+
try:
|
|
35
|
+
resp = self.http.request(method, url, **kwargs)
|
|
36
|
+
except httpx.TransportError as exc: # connection reset, cold start timeouts
|
|
37
|
+
last = exc
|
|
38
|
+
self._sleep(attempt)
|
|
39
|
+
continue
|
|
40
|
+
if resp.status_code in RETRY_STATUS and attempt < self.max_retries:
|
|
41
|
+
if not (resp.status_code == 429 and _code(resp) == "usage_limit"):
|
|
42
|
+
self._sleep(attempt, resp.headers.get("retry-after"))
|
|
43
|
+
continue
|
|
44
|
+
self._usage_warning(resp)
|
|
45
|
+
if resp.status_code >= 400:
|
|
46
|
+
try:
|
|
47
|
+
body = resp.json()
|
|
48
|
+
except ValueError:
|
|
49
|
+
body = resp.text
|
|
50
|
+
raise from_response(resp.status_code, body)
|
|
51
|
+
if raw:
|
|
52
|
+
return resp
|
|
53
|
+
return resp.json() if resp.content else None
|
|
54
|
+
raise APIError(f"Could not reach {self.base_url}: {last}") from last
|
|
55
|
+
|
|
56
|
+
def _sleep(self, attempt: int, retry_after: str | None = None) -> None:
|
|
57
|
+
if retry_after and retry_after.isdigit():
|
|
58
|
+
time.sleep(min(float(retry_after), 30))
|
|
59
|
+
else:
|
|
60
|
+
time.sleep(min(0.5 * 2**attempt, 8) + random.random() * 0.25)
|
|
61
|
+
|
|
62
|
+
def _usage_warning(self, resp: httpx.Response) -> None:
|
|
63
|
+
msg = resp.headers.get("x-zerograd-usage-warning")
|
|
64
|
+
if msg and not self._warned:
|
|
65
|
+
self._warned = True
|
|
66
|
+
warnings.warn(f"{msg} At the limit, predict raises LimitError.", UsageWarning, stacklevel=4)
|
|
67
|
+
|
|
68
|
+
def put_chunk(self, url: str, data: bytes, headers: dict[str, str]) -> httpx.Response:
|
|
69
|
+
"""Upload chunks go to a session URL (Cloud Storage in production). Never send our API key there."""
|
|
70
|
+
if url.startswith(self.base_url):
|
|
71
|
+
return self.http.put(url, content=data, headers=headers)
|
|
72
|
+
for attempt in range(self.max_retries + 1):
|
|
73
|
+
try:
|
|
74
|
+
return httpx.put(url, content=data, headers=headers, timeout=300)
|
|
75
|
+
except httpx.TransportError:
|
|
76
|
+
if attempt == self.max_retries:
|
|
77
|
+
raise
|
|
78
|
+
self._sleep(attempt)
|
|
79
|
+
raise ZeroGradError("unreachable")
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def _code(resp: httpx.Response) -> str | None:
|
|
83
|
+
try:
|
|
84
|
+
return resp.json().get("error")
|
|
85
|
+
except ValueError:
|
|
86
|
+
return None
|
zerograd/_progress.py
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import sys
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Bar:
|
|
7
|
+
"""A one-line progress bar on stderr that also prints finished stages as ✓ lines. Works in notebooks."""
|
|
8
|
+
|
|
9
|
+
def __init__(self, enabled: bool = True, width: int = 28):
|
|
10
|
+
self.enabled = enabled
|
|
11
|
+
self.width = width
|
|
12
|
+
self.seen = 0
|
|
13
|
+
self.last = ""
|
|
14
|
+
|
|
15
|
+
def update(self, run: dict) -> None:
|
|
16
|
+
if not self.enabled:
|
|
17
|
+
return
|
|
18
|
+
log = run.get("log") or []
|
|
19
|
+
active = run.get("status") in ("queued", "running")
|
|
20
|
+
# Finished stages become ✓ lines; the latest one stays on the bar while the run is active.
|
|
21
|
+
upto = len(log) - 1 if active else len(log)
|
|
22
|
+
for entry in log[self.seen : upto]:
|
|
23
|
+
self._line(f"✓ {entry['message']}")
|
|
24
|
+
self.seen = max(self.seen, upto)
|
|
25
|
+
if active:
|
|
26
|
+
p = float(run.get("progress") or 0)
|
|
27
|
+
filled = int(round(p * self.width))
|
|
28
|
+
text = f"{run.get('message', '')[:60]:<60} {'█' * filled}{'░' * (self.width - filled)} {p:>4.0%}"
|
|
29
|
+
if text != self.last:
|
|
30
|
+
sys.stderr.write("\r" + text)
|
|
31
|
+
sys.stderr.flush()
|
|
32
|
+
self.last = text
|
|
33
|
+
|
|
34
|
+
def _line(self, text: str) -> None:
|
|
35
|
+
if self.last:
|
|
36
|
+
sys.stderr.write("\r" + " " * (len(self.last) + 2) + "\r")
|
|
37
|
+
self.last = ""
|
|
38
|
+
sys.stderr.write(text + "\n")
|
|
39
|
+
sys.stderr.flush()
|
|
40
|
+
|
|
41
|
+
def done(self, text: str | None = None) -> None:
|
|
42
|
+
if not self.enabled:
|
|
43
|
+
return
|
|
44
|
+
if self.last:
|
|
45
|
+
sys.stderr.write("\r" + " " * (len(self.last) + 2) + "\r")
|
|
46
|
+
self.last = ""
|
|
47
|
+
if text:
|
|
48
|
+
sys.stderr.write(text + "\n")
|
|
49
|
+
sys.stderr.flush()
|