shadow-tools 0.4.0__tar.gz → 0.4.2__tar.gz
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.
- {shadow_tools-0.4.0 → shadow_tools-0.4.2}/PKG-INFO +14 -1
- {shadow_tools-0.4.0 → shadow_tools-0.4.2}/README.md +13 -0
- {shadow_tools-0.4.0 → shadow_tools-0.4.2}/pyproject.toml +4 -1
- {shadow_tools-0.4.0 → shadow_tools-0.4.2}/src/shadowtools/__init__.py +3 -2
- {shadow_tools-0.4.0 → shadow_tools-0.4.2}/src/shadowtools/training.py +60 -0
- shadow_tools-0.4.0/release-0.2.0/shadow_tools-0.2.0-py3-none-any.whl +0 -0
- shadow_tools-0.4.0/release-0.2.0/shadow_tools-0.2.0.tar.gz +0 -0
- shadow_tools-0.4.0/release-0.2.1/shadow_tools-0.2.1-py3-none-any.whl +0 -0
- shadow_tools-0.4.0/release-0.2.1/shadow_tools-0.2.1.tar.gz +0 -0
- shadow_tools-0.4.0/release-0.2.2/shadow_tools-0.2.2-py3-none-any.whl +0 -0
- shadow_tools-0.4.0/release-0.2.2/shadow_tools-0.2.2.tar.gz +0 -0
- shadow_tools-0.4.0/release-0.2.3/shadow_tools-0.2.3-py3-none-any.whl +0 -0
- shadow_tools-0.4.0/release-0.2.3/shadow_tools-0.2.3.tar.gz +0 -0
- shadow_tools-0.4.0/release-0.2.4/shadow_tools-0.2.4-py3-none-any.whl +0 -0
- shadow_tools-0.4.0/release-0.2.4/shadow_tools-0.2.4.tar.gz +0 -0
- shadow_tools-0.4.0/release-0.2.5/shadow_tools-0.2.5-py3-none-any.whl +0 -0
- shadow_tools-0.4.0/release-0.2.5/shadow_tools-0.2.5.tar.gz +0 -0
- shadow_tools-0.4.0/release-0.3.0/shadow_tools-0.3.0-py3-none-any.whl +0 -0
- shadow_tools-0.4.0/release-0.3.0/shadow_tools-0.3.0.tar.gz +0 -0
- shadow_tools-0.4.0/release-0.4.0/shadow_tools-0.4.0-py3-none-any.whl +0 -0
- shadow_tools-0.4.0/release-0.4.0/shadow_tools-0.4.0.tar.gz +0 -0
- {shadow_tools-0.4.0 → shadow_tools-0.4.2}/.gitignore +0 -0
- {shadow_tools-0.4.0 → shadow_tools-0.4.2}/LICENSE +0 -0
- {shadow_tools-0.4.0 → shadow_tools-0.4.2}/src/shadowtools/ai.py +0 -0
- {shadow_tools-0.4.0 → shadow_tools-0.4.2}/src/shadowtools/async_tools.py +0 -0
- {shadow_tools-0.4.0 → shadow_tools-0.4.2}/src/shadowtools/cli.py +0 -0
- {shadow_tools-0.4.0 → shadow_tools-0.4.2}/src/shadowtools/config.py +0 -0
- {shadow_tools-0.4.0 → shadow_tools-0.4.2}/src/shadowtools/data.py +0 -0
- {shadow_tools-0.4.0 → shadow_tools-0.4.2}/src/shadowtools/files.py +0 -0
- {shadow_tools-0.4.0 → shadow_tools-0.4.2}/src/shadowtools/hashing.py +0 -0
- {shadow_tools-0.4.0 → shadow_tools-0.4.2}/src/shadowtools/http.py +0 -0
- {shadow_tools-0.4.0 → shadow_tools-0.4.2}/src/shadowtools/ids.py +0 -0
- {shadow_tools-0.4.0 → shadow_tools-0.4.2}/src/shadowtools/network.py +0 -0
- {shadow_tools-0.4.0 → shadow_tools-0.4.2}/src/shadowtools/retry.py +0 -0
- {shadow_tools-0.4.0 → shadow_tools-0.4.2}/src/shadowtools/security.py +0 -0
- {shadow_tools-0.4.0 → shadow_tools-0.4.2}/src/shadowtools/text.py +0 -0
- {shadow_tools-0.4.0 → shadow_tools-0.4.2}/src/shadowtools/validation.py +0 -0
- {shadow_tools-0.4.0 → shadow_tools-0.4.2}/src/shadowtools/websocket.py +0 -0
- {shadow_tools-0.4.0 → shadow_tools-0.4.2}/tests/test_core.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: shadow-tools
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.2
|
|
4
4
|
Summary: A practical all-in-one Python toolkit for HTTP, WebSockets, files, data, validation, async work, and CLIs.
|
|
5
5
|
Project-URL: Homepage, https://github.com/WilliamLaurento/shadow-tools
|
|
6
6
|
Project-URL: Issues, https://github.com/WilliamLaurento/shadow-tools/issues
|
|
@@ -145,6 +145,19 @@ trainer = Trainer(config)
|
|
|
145
145
|
history = trainer.fit(train_loader, validation_loader)
|
|
146
146
|
```
|
|
147
147
|
|
|
148
|
+
For normal lists, use the short root-level API—no PyTorch imports or loaders:
|
|
149
|
+
|
|
150
|
+
```python
|
|
151
|
+
from shadowtools import train
|
|
152
|
+
|
|
153
|
+
model, history = train(
|
|
154
|
+
features=[[0, 1], [1, 1], [0, 0], [1, 0]],
|
|
155
|
+
labels=[1, 1, 0, 0],
|
|
156
|
+
epochs=10,
|
|
157
|
+
)
|
|
158
|
+
print(history)
|
|
159
|
+
```
|
|
160
|
+
|
|
148
161
|
```bash
|
|
149
162
|
shadow get https://api.github.com --json
|
|
150
163
|
shadow json-format settings.json
|
|
@@ -119,6 +119,19 @@ trainer = Trainer(config)
|
|
|
119
119
|
history = trainer.fit(train_loader, validation_loader)
|
|
120
120
|
```
|
|
121
121
|
|
|
122
|
+
For normal lists, use the short root-level API—no PyTorch imports or loaders:
|
|
123
|
+
|
|
124
|
+
```python
|
|
125
|
+
from shadowtools import train
|
|
126
|
+
|
|
127
|
+
model, history = train(
|
|
128
|
+
features=[[0, 1], [1, 1], [0, 0], [1, 0]],
|
|
129
|
+
labels=[1, 1, 0, 0],
|
|
130
|
+
epochs=10,
|
|
131
|
+
)
|
|
132
|
+
print(history)
|
|
133
|
+
```
|
|
134
|
+
|
|
122
135
|
```bash
|
|
123
136
|
shadow get https://api.github.com --json
|
|
124
137
|
shadow json-format settings.json
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "shadow-tools"
|
|
7
|
-
version = "0.4.
|
|
7
|
+
version = "0.4.2"
|
|
8
8
|
description = "A practical all-in-one Python toolkit for HTTP, WebSockets, files, data, validation, async work, and CLIs."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.10"
|
|
@@ -35,5 +35,8 @@ Issues = "https://github.com/WilliamLaurento/shadow-tools/issues"
|
|
|
35
35
|
[tool.hatch.build.targets.wheel]
|
|
36
36
|
packages = ["src/shadowtools"]
|
|
37
37
|
|
|
38
|
+
[tool.hatch.build]
|
|
39
|
+
exclude = ["*.zip", "release-*", "dist", "build", ".pytest_cache"]
|
|
40
|
+
|
|
38
41
|
[tool.pytest.ini_options]
|
|
39
42
|
testpaths = ["tests"]
|
|
@@ -10,12 +10,13 @@ from .network import generate_random_ips, generate_test_ips, is_subdomain_of, ma
|
|
|
10
10
|
from .retry import retry
|
|
11
11
|
from .security import audit_headers, make_password, redact_secrets, verify_file_hash
|
|
12
12
|
from .text import chunks, slugify, to_camel, to_snake
|
|
13
|
+
from .training import fine_tune, make_loaders, train
|
|
13
14
|
from .validation import is_email, is_url, require_keys
|
|
14
15
|
|
|
15
|
-
__version__ = "0.4.
|
|
16
|
+
__version__ = "0.4.2"
|
|
16
17
|
|
|
17
18
|
__all__ = [
|
|
18
19
|
"append_text", "audit_headers", "chunks", "ensure_dir", "env", "file_hash", "generate_random_ips", "generate_test_ips", "get", "hash_text", "is_email", "is_subdomain_of", "is_url", "make_password", "make_subdomain", "random_token", "read_csv",
|
|
19
|
-
"read_json", "read_text", "request", "require_env", "require_keys", "retry", "reverse_dns", "resolve_dns", "slugify", "timestamp_id", "to_bool", "to_camel", "to_snake", "uuid4", "write_csv",
|
|
20
|
+
"read_json", "read_text", "request", "require_env", "require_keys", "retry", "reverse_dns", "resolve_dns", "slugify", "timestamp_id", "to_bool", "to_camel", "to_snake", "train", "fine_tune", "make_loaders", "uuid4", "write_csv",
|
|
20
21
|
"write_json", "write_text", "redact_secrets", "verify_file_hash",
|
|
21
22
|
]
|
|
@@ -168,3 +168,63 @@ class Trainer:
|
|
|
168
168
|
def run_training(config: TrainingConfig | Mapping[str, Any], train_data: Iterable[Any], validation_data: Iterable[Any] | None = None) -> tuple[Trainer, list[dict[str, float]]]:
|
|
169
169
|
trainer = Trainer(config)
|
|
170
170
|
return trainer, trainer.fit(train_data, validation_data)
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
def make_loaders(features: Any, labels: Any, *, batch_size: int = 32,
|
|
174
|
+
validation_split: float = 0.2, shuffle: bool = True) -> tuple[Any, Any]:
|
|
175
|
+
"""Turn ordinary lists or tensors into PyTorch training and validation loaders."""
|
|
176
|
+
torch = Trainer._torch()
|
|
177
|
+
if not 0 < validation_split < 1:
|
|
178
|
+
raise ValueError("validation_split must be between 0 and 1.")
|
|
179
|
+
x = torch.as_tensor(features, dtype=torch.float32)
|
|
180
|
+
y = torch.as_tensor(labels, dtype=torch.long).flatten()
|
|
181
|
+
if x.ndim != 2:
|
|
182
|
+
raise ValueError("features must be a 2D list or tensor: rows by input values.")
|
|
183
|
+
if len(x) != len(y) or len(x) < 2:
|
|
184
|
+
raise ValueError("features and labels must have the same length of at least 2.")
|
|
185
|
+
if int(y.min()) < 0:
|
|
186
|
+
raise ValueError("labels must be zero or positive integer class IDs.")
|
|
187
|
+
order = torch.randperm(len(x)) if shuffle else torch.arange(len(x))
|
|
188
|
+
split = max(1, min(len(x) - 1, round(len(x) * (1 - validation_split))))
|
|
189
|
+
train_set = torch.utils.data.TensorDataset(x[order[:split]], y[order[:split]])
|
|
190
|
+
validation_set = torch.utils.data.TensorDataset(x[order[split:]], y[order[split:]])
|
|
191
|
+
return (
|
|
192
|
+
torch.utils.data.DataLoader(train_set, batch_size=batch_size, shuffle=shuffle),
|
|
193
|
+
torch.utils.data.DataLoader(validation_set, batch_size=batch_size),
|
|
194
|
+
)
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
def train(features: Any, labels: Any, *, hidden_sizes: list[int] | None = None,
|
|
198
|
+
epochs: int = 5, learning_rate: float = 1e-3, batch_size: int = 32,
|
|
199
|
+
validation_split: float = 0.2, device: str = "auto",
|
|
200
|
+
checkpoint_path: str | Path | None = None) -> tuple[Trainer, list[dict[str, float]]]:
|
|
201
|
+
"""Train a classifier from regular Python lists or tensors in one call."""
|
|
202
|
+
torch = Trainer._torch()
|
|
203
|
+
x = torch.as_tensor(features)
|
|
204
|
+
y = torch.as_tensor(labels).flatten()
|
|
205
|
+
if x.ndim != 2 or not len(y):
|
|
206
|
+
raise ValueError("features must be 2D and labels must not be empty.")
|
|
207
|
+
train_loader, validation_loader = make_loaders(features, labels, batch_size=batch_size, validation_split=validation_split)
|
|
208
|
+
config = TrainingConfig(
|
|
209
|
+
mode="from_scratch", epochs=epochs, learning_rate=learning_rate, batch_size=batch_size,
|
|
210
|
+
device=device, checkpoint_path=checkpoint_path,
|
|
211
|
+
architecture={"type": "mlp", "input_size": int(x.shape[1]),
|
|
212
|
+
"hidden_sizes": hidden_sizes or [64, 32],
|
|
213
|
+
"num_classes": int(y.max().item()) + 1},
|
|
214
|
+
)
|
|
215
|
+
return run_training(config, train_loader, validation_loader)
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
def fine_tune(model: Any, features: Any, labels: Any, *, epochs: int = 3,
|
|
219
|
+
learning_rate: float = 1e-4, batch_size: int = 32,
|
|
220
|
+
validation_split: float = 0.2, freeze_layers: tuple[str, ...] = (),
|
|
221
|
+
head_attr: str | None = None, num_classes: int | None = None,
|
|
222
|
+
device: str = "auto", checkpoint_path: str | Path | None = None) -> tuple[Trainer, list[dict[str, float]]]:
|
|
223
|
+
"""Fine-tune a supplied PyTorch classifier from regular lists or tensors."""
|
|
224
|
+
train_loader, validation_loader = make_loaders(features, labels, batch_size=batch_size, validation_split=validation_split)
|
|
225
|
+
config = TrainingConfig(
|
|
226
|
+
mode="fine_tune", model=model, epochs=epochs, learning_rate=learning_rate,
|
|
227
|
+
batch_size=batch_size, device=device, checkpoint_path=checkpoint_path,
|
|
228
|
+
freeze_layers=freeze_layers, head_attr=head_attr, num_classes=num_classes,
|
|
229
|
+
)
|
|
230
|
+
return run_training(config, train_loader, validation_loader)
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|