silver-run 0.1.0__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.
- silver_run-0.1.0/CHANGELOG.md +33 -0
- silver_run-0.1.0/CONTRIBUTING.md +115 -0
- silver_run-0.1.0/LICENSE +12 -0
- silver_run-0.1.0/MANIFEST.in +6 -0
- silver_run-0.1.0/PKG-INFO +319 -0
- silver_run-0.1.0/README.md +287 -0
- silver_run-0.1.0/pyproject.toml +47 -0
- silver_run-0.1.0/setup.cfg +4 -0
- silver_run-0.1.0/src/silver_run/__init__.py +22 -0
- silver_run-0.1.0/src/silver_run/backend.py +8 -0
- silver_run-0.1.0/src/silver_run/checkpoints.py +30 -0
- silver_run-0.1.0/src/silver_run/models.py +40 -0
- silver_run-0.1.0/src/silver_run/training.py +128 -0
- silver_run-0.1.0/src/silver_run.egg-info/PKG-INFO +319 -0
- silver_run-0.1.0/src/silver_run.egg-info/SOURCES.txt +18 -0
- silver_run-0.1.0/src/silver_run.egg-info/dependency_links.txt +1 -0
- silver_run-0.1.0/src/silver_run.egg-info/requires.txt +9 -0
- silver_run-0.1.0/src/silver_run.egg-info/top_level.txt +1 -0
- silver_run-0.1.0/tests/__init__.py +1 -0
- silver_run-0.1.0/tests/test_run.py +308 -0
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [0.1.0] - 2024-08-04
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
- Initial release of silver-run
|
|
12
|
+
- Backend-neutral training lifecycle management
|
|
13
|
+
- Event logging and inspection for training runs
|
|
14
|
+
- Checkpoint management with pluggable storage backends
|
|
15
|
+
- Async/await support for modern Python training workflows
|
|
16
|
+
- In-memory checkpoint store for local development
|
|
17
|
+
- Full training state machine (created, running, paused, stopped, cancelled, completed, failed)
|
|
18
|
+
- Comprehensive test suite with async test coverage
|
|
19
|
+
- Support for Python 3.8-3.12
|
|
20
|
+
|
|
21
|
+
### Features
|
|
22
|
+
- `TrainingRun` - Main class for managing training lifecycle
|
|
23
|
+
- `TrainingBackend` - Abstract interface for training implementations
|
|
24
|
+
- `CheckpointStore` - Pluggable checkpoint storage interface
|
|
25
|
+
- `MemoryCheckpointStore` - In-memory checkpoint implementation
|
|
26
|
+
- `TrainingContext` - Context object passed to training backends
|
|
27
|
+
- State management (start, pause, resume, stop, cancel, complete, fail)
|
|
28
|
+
- Event emission and logging with timestamps
|
|
29
|
+
- Checkpoint creation and retrieval
|
|
30
|
+
- Backend execution with automatic state management
|
|
31
|
+
- Pause/resume functionality for long-running training
|
|
32
|
+
|
|
33
|
+
## [Unreleased]
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
# Contributing to silver-run
|
|
2
|
+
|
|
3
|
+
Thank you for your interest in contributing to silver-run! This document provides guidelines and instructions for contributing to the project.
|
|
4
|
+
|
|
5
|
+
## Development Setup
|
|
6
|
+
|
|
7
|
+
### Prerequisites
|
|
8
|
+
- Python 3.8 or higher
|
|
9
|
+
- Git
|
|
10
|
+
- Virtual environment (recommended)
|
|
11
|
+
|
|
12
|
+
### Setting Up Development Environment
|
|
13
|
+
|
|
14
|
+
1. **Clone the repository**
|
|
15
|
+
```bash
|
|
16
|
+
git clone https://github.com/adfgdartec/silver-run.git
|
|
17
|
+
cd silver-run
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
2. **Create a virtual environment**
|
|
21
|
+
```bash
|
|
22
|
+
python -m venv venv
|
|
23
|
+
source venv/bin/activate # On Windows: venv\Scripts\activate
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
3. **Install development dependencies**
|
|
27
|
+
```bash
|
|
28
|
+
pip install -e ".[dev]"
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
4. **Run tests**
|
|
32
|
+
```bash
|
|
33
|
+
pytest
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
5. **Run tests with coverage**
|
|
37
|
+
```bash
|
|
38
|
+
pytest --cov=silver_run --cov-report=html
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Code Style
|
|
42
|
+
|
|
43
|
+
We use the following tools to maintain code quality:
|
|
44
|
+
|
|
45
|
+
- **flake8** for linting
|
|
46
|
+
- **mypy** for type checking
|
|
47
|
+
- **pytest** for testing
|
|
48
|
+
|
|
49
|
+
Run all quality checks:
|
|
50
|
+
```bash
|
|
51
|
+
flake8 src/ tests/
|
|
52
|
+
mypy src/
|
|
53
|
+
pytest
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Making Changes
|
|
57
|
+
|
|
58
|
+
1. **Create a branch**
|
|
59
|
+
```bash
|
|
60
|
+
git checkout -b feature/your-feature-name
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
2. **Make your changes**
|
|
64
|
+
- Write clear, descriptive commit messages
|
|
65
|
+
- Add tests for new functionality
|
|
66
|
+
- Update documentation as needed
|
|
67
|
+
|
|
68
|
+
3. **Run tests**
|
|
69
|
+
```bash
|
|
70
|
+
pytest
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
4. **Submit a pull request**
|
|
74
|
+
- Describe your changes clearly
|
|
75
|
+
- Reference any related issues
|
|
76
|
+
- Ensure all tests pass
|
|
77
|
+
|
|
78
|
+
## Testing
|
|
79
|
+
|
|
80
|
+
We aim for high test coverage. When adding new features:
|
|
81
|
+
|
|
82
|
+
- Write unit tests for new functions
|
|
83
|
+
- Test async functions properly
|
|
84
|
+
- Test edge cases and error conditions
|
|
85
|
+
- Ensure existing tests still pass
|
|
86
|
+
|
|
87
|
+
### Test Structure
|
|
88
|
+
```
|
|
89
|
+
tests/
|
|
90
|
+
├── __init__.py
|
|
91
|
+
└── test_run.py
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## Documentation
|
|
95
|
+
|
|
96
|
+
- Update docstrings for any modified functions
|
|
97
|
+
- Add examples for new features
|
|
98
|
+
- Update README.md if user-facing changes are made
|
|
99
|
+
|
|
100
|
+
## Release Process
|
|
101
|
+
|
|
102
|
+
Releases are managed by maintainers:
|
|
103
|
+
|
|
104
|
+
1. Update version in `pyproject.toml`
|
|
105
|
+
2. Update `CHANGELOG.md`
|
|
106
|
+
3. Create a GitHub release
|
|
107
|
+
4. Package will be automatically published to PyPI
|
|
108
|
+
|
|
109
|
+
## Questions?
|
|
110
|
+
|
|
111
|
+
Feel free to open an issue for questions or discussions about contributions.
|
|
112
|
+
|
|
113
|
+
## License
|
|
114
|
+
|
|
115
|
+
By contributing, you agree that your contributions will be licensed under the Apache-2.0 License.
|
silver_run-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
Apache License
|
|
2
|
+
Version 2.0, January 2004
|
|
3
|
+
|
|
4
|
+
Copyright 2026 Silver Contributors
|
|
5
|
+
|
|
6
|
+
Licensed under the Apache License, Version 2.0. You may obtain a copy of the
|
|
7
|
+
License at https://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
|
|
9
|
+
Unless required by applicable law or agreed to in writing, software distributed
|
|
10
|
+
under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
|
11
|
+
CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
|
12
|
+
specific language governing permissions and limitations under the License.
|
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: silver-run
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Backend-neutral ML run lifecycle, events, and checkpoints for Silver.
|
|
5
|
+
License-Expression: Apache-2.0
|
|
6
|
+
Project-URL: Homepage, https://github.com/adfgdartec/silver-run
|
|
7
|
+
Project-URL: Repository, https://github.com/adfgdartec/silver-run
|
|
8
|
+
Project-URL: Issues, https://github.com/adfgdartec/silver-run/issues
|
|
9
|
+
Keywords: machine-learning,training,checkpoint,experiments,python
|
|
10
|
+
Classifier: Development Status :: 3 - Alpha
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: Intended Audience :: Science/Research
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
20
|
+
Requires-Python: >=3.8
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
License-File: LICENSE
|
|
23
|
+
Provides-Extra: dev
|
|
24
|
+
Requires-Dist: pytest>=7.0.0; extra == "dev"
|
|
25
|
+
Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
|
|
26
|
+
Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
|
|
27
|
+
Requires-Dist: flake8>=6.0.0; extra == "dev"
|
|
28
|
+
Requires-Dist: mypy>=1.0.0; extra == "dev"
|
|
29
|
+
Requires-Dist: build>=0.10.0; extra == "dev"
|
|
30
|
+
Requires-Dist: twine>=4.0.0; extra == "dev"
|
|
31
|
+
Dynamic: license-file
|
|
32
|
+
|
|
33
|
+
# silver-run
|
|
34
|
+
|
|
35
|
+
[](https://www.python.org/downloads/)
|
|
36
|
+
[](LICENSE)
|
|
37
|
+
[](tests/)
|
|
38
|
+
[](https://flake8.pycqa.org/)
|
|
39
|
+
|
|
40
|
+
Backend-neutral ML run lifecycle, events, and checkpoints for Silver. A Python package designed for ML researchers who need flexible training orchestration across different frameworks.
|
|
41
|
+
|
|
42
|
+
## Installation
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
pip install silver-run
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Quick Start
|
|
49
|
+
|
|
50
|
+
```python
|
|
51
|
+
from silver_run import TrainingRun, TrainingBackend, TrainingContext
|
|
52
|
+
import asyncio
|
|
53
|
+
|
|
54
|
+
class MyBackend(TrainingBackend):
|
|
55
|
+
async def run(self, context: TrainingContext):
|
|
56
|
+
for epoch in range(10):
|
|
57
|
+
if context.should_stop():
|
|
58
|
+
break
|
|
59
|
+
# Your training logic here
|
|
60
|
+
context.emit({
|
|
61
|
+
"kind": "epoch",
|
|
62
|
+
"epoch": epoch,
|
|
63
|
+
"metrics": {"loss": 0.5 - epoch * 0.05}
|
|
64
|
+
})
|
|
65
|
+
await asyncio.sleep(0.1)
|
|
66
|
+
|
|
67
|
+
async def main():
|
|
68
|
+
run = TrainingRun()
|
|
69
|
+
backend = MyBackend()
|
|
70
|
+
final_state = await run.execute(backend)
|
|
71
|
+
print(f"Run finished with state: {final_state.value}")
|
|
72
|
+
|
|
73
|
+
asyncio.run(main())
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Features
|
|
77
|
+
|
|
78
|
+
- **Training Lifecycle Management**: Full state machine (created, running, paused, stopped, cancelled, completed, failed)
|
|
79
|
+
- **Event Logging**: Comprehensive event tracking with timestamps for training observability
|
|
80
|
+
- **Checkpoint Management**: Pluggable storage backends for model checkpointing
|
|
81
|
+
- **Backend-Agnostic**: Works with PyTorch, TensorFlow, JAX, or any custom training framework
|
|
82
|
+
- **Async/Await Support**: Modern Python async patterns for concurrent training
|
|
83
|
+
- **Pause/Resume**: Control long-running training jobs with pause and resume functionality
|
|
84
|
+
- **Type Safety**: Full type hints for better IDE support and fewer bugs
|
|
85
|
+
|
|
86
|
+
## Use Cases
|
|
87
|
+
|
|
88
|
+
### PyTorch Training Integration
|
|
89
|
+
|
|
90
|
+
```python
|
|
91
|
+
from silver_run import TrainingRun, TrainingBackend, TrainingContext
|
|
92
|
+
import torch
|
|
93
|
+
import asyncio
|
|
94
|
+
|
|
95
|
+
class PyTorchBackend(TrainingBackend):
|
|
96
|
+
def __init__(self, model, optimizer, train_loader):
|
|
97
|
+
self.model = model
|
|
98
|
+
self.optimizer = optimizer
|
|
99
|
+
self.train_loader = train_loader
|
|
100
|
+
|
|
101
|
+
async def run(self, context: TrainingContext):
|
|
102
|
+
for epoch in range(10):
|
|
103
|
+
if context.should_stop():
|
|
104
|
+
break
|
|
105
|
+
|
|
106
|
+
self.model.train()
|
|
107
|
+
total_loss = 0
|
|
108
|
+
|
|
109
|
+
for batch_idx, (data, target) in enumerate(self.train_loader):
|
|
110
|
+
self.optimizer.zero_grad()
|
|
111
|
+
output = self.model(data)
|
|
112
|
+
loss = torch.nn.functional.cross_entropy(output, target)
|
|
113
|
+
loss.backward()
|
|
114
|
+
self.optimizer.step()
|
|
115
|
+
total_loss += loss.item()
|
|
116
|
+
|
|
117
|
+
# Emit epoch completion event
|
|
118
|
+
context.emit({
|
|
119
|
+
"kind": "epoch",
|
|
120
|
+
"epoch": epoch,
|
|
121
|
+
"metrics": {"loss": total_loss / len(self.train_loader)}
|
|
122
|
+
})
|
|
123
|
+
|
|
124
|
+
# Checkpoint every 5 epochs
|
|
125
|
+
if epoch % 5 == 0:
|
|
126
|
+
await context.checkpoint({
|
|
127
|
+
"epoch": epoch,
|
|
128
|
+
"model_state_dict": self.model.state_dict(),
|
|
129
|
+
"optimizer_state_dict": self.optimizer.state_dict()
|
|
130
|
+
})
|
|
131
|
+
|
|
132
|
+
async def main():
|
|
133
|
+
model = torch.nn.Linear(10, 2)
|
|
134
|
+
optimizer = torch.optim.Adam(model.parameters())
|
|
135
|
+
train_loader = [...] # Your data loader
|
|
136
|
+
|
|
137
|
+
run = TrainingRun()
|
|
138
|
+
backend = PyTorchBackend(model, optimizer, train_loader)
|
|
139
|
+
final_state = await run.execute(backend)
|
|
140
|
+
|
|
141
|
+
# Review events
|
|
142
|
+
for event in run.events():
|
|
143
|
+
print(f"{event.kind}: {event.data}")
|
|
144
|
+
|
|
145
|
+
asyncio.run(main())
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### Training with Pause/Resume
|
|
149
|
+
|
|
150
|
+
```python
|
|
151
|
+
from silver_run import TrainingRun, TrainingBackend
|
|
152
|
+
import asyncio
|
|
153
|
+
|
|
154
|
+
class LongRunningBackend(TrainingBackend):
|
|
155
|
+
async def run(self, context: TrainingContext):
|
|
156
|
+
for step in range(1000):
|
|
157
|
+
if context.should_stop():
|
|
158
|
+
break
|
|
159
|
+
|
|
160
|
+
# Simulate training step
|
|
161
|
+
await asyncio.sleep(0.01)
|
|
162
|
+
|
|
163
|
+
# Emit progress
|
|
164
|
+
if step % 100 == 0:
|
|
165
|
+
context.emit({
|
|
166
|
+
"kind": "progress",
|
|
167
|
+
"step": step,
|
|
168
|
+
"total": 1000
|
|
169
|
+
})
|
|
170
|
+
|
|
171
|
+
async def main():
|
|
172
|
+
run = TrainingRun()
|
|
173
|
+
backend = LongRunningBackend()
|
|
174
|
+
|
|
175
|
+
# Start training in background
|
|
176
|
+
training_task = asyncio.create_task(run.execute(backend))
|
|
177
|
+
|
|
178
|
+
# Pause after some time
|
|
179
|
+
await asyncio.sleep(0.5)
|
|
180
|
+
run.pause()
|
|
181
|
+
print("Training paused")
|
|
182
|
+
|
|
183
|
+
# Resume after some time
|
|
184
|
+
await asyncio.sleep(0.5)
|
|
185
|
+
run.resume()
|
|
186
|
+
print("Training resumed")
|
|
187
|
+
|
|
188
|
+
# Wait for completion
|
|
189
|
+
final_state = await training_task
|
|
190
|
+
print(f"Training finished: {final_state.value}")
|
|
191
|
+
|
|
192
|
+
asyncio.run(main())
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
### Custom Checkpoint Storage
|
|
196
|
+
|
|
197
|
+
```python
|
|
198
|
+
from silver_run import TrainingRun, CheckpointStore, Checkpoint
|
|
199
|
+
import asyncio
|
|
200
|
+
|
|
201
|
+
class S3CheckpointStore(CheckpointStore):
|
|
202
|
+
def __init__(self, bucket, prefix):
|
|
203
|
+
self.bucket = bucket
|
|
204
|
+
self.prefix = prefix
|
|
205
|
+
self.checkpoints = {}
|
|
206
|
+
|
|
207
|
+
async def save(self, checkpoint: Checkpoint):
|
|
208
|
+
# Save to S3
|
|
209
|
+
key = f"{self.prefix}/{checkpoint.id}"
|
|
210
|
+
print(f"Saving checkpoint to S3: {key}")
|
|
211
|
+
self.checkpoints[checkpoint.id] = checkpoint
|
|
212
|
+
|
|
213
|
+
async def latest(self):
|
|
214
|
+
if not self.checkpoints:
|
|
215
|
+
return None
|
|
216
|
+
return list(self.checkpoints.values())[-1]
|
|
217
|
+
|
|
218
|
+
async def get(self, id: str):
|
|
219
|
+
return self.checkpoints.get(id)
|
|
220
|
+
|
|
221
|
+
async def main():
|
|
222
|
+
store = S3CheckpointStore("my-bucket", "checkpoints")
|
|
223
|
+
run = TrainingRun(options=TrainingRunOptions(checkpoint_store=store))
|
|
224
|
+
|
|
225
|
+
# Use custom checkpoint store
|
|
226
|
+
await run.checkpoint({"model": "state"}, "checkpoint-1")
|
|
227
|
+
latest = await run.latest_checkpoint()
|
|
228
|
+
print(f"Latest checkpoint: {latest.id}")
|
|
229
|
+
|
|
230
|
+
asyncio.run(main())
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
## Advanced Usage
|
|
234
|
+
|
|
235
|
+
### Event Filtering and Analysis
|
|
236
|
+
|
|
237
|
+
```python
|
|
238
|
+
from silver_run import TrainingRun
|
|
239
|
+
|
|
240
|
+
# Filter events by type
|
|
241
|
+
def get_epoch_events(run):
|
|
242
|
+
return [e for e in run.events() if e.kind == "epoch"]
|
|
243
|
+
|
|
244
|
+
def get_error_events(run):
|
|
245
|
+
return [e for e in run.events() if e.kind == "error"]
|
|
246
|
+
|
|
247
|
+
# Analyze training progression
|
|
248
|
+
def analyze_training(run):
|
|
249
|
+
epoch_events = get_epoch_events(run)
|
|
250
|
+
losses = [e.data.get("metrics", {}).get("loss") for e in epoch_events]
|
|
251
|
+
|
|
252
|
+
if losses:
|
|
253
|
+
print(f"Initial loss: {losses[0]}")
|
|
254
|
+
print(f"Final loss: {losses[-1]}")
|
|
255
|
+
print(f"Loss reduction: {losses[0] - losses[-1]}")
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
### Multi-Run Experiments
|
|
259
|
+
|
|
260
|
+
```python
|
|
261
|
+
from silver_run import TrainingRun
|
|
262
|
+
import asyncio
|
|
263
|
+
|
|
264
|
+
async def run_experiment(config):
|
|
265
|
+
run = TrainingRun()
|
|
266
|
+
backend = MyBackend(config)
|
|
267
|
+
return await run.execute(backend)
|
|
268
|
+
|
|
269
|
+
async def main():
|
|
270
|
+
configs = [
|
|
271
|
+
{"learning_rate": 0.001},
|
|
272
|
+
{"learning_rate": 0.01},
|
|
273
|
+
{"learning_rate": 0.1}
|
|
274
|
+
]
|
|
275
|
+
|
|
276
|
+
results = await asyncio.gather(*[
|
|
277
|
+
run_experiment(config) for config in configs
|
|
278
|
+
])
|
|
279
|
+
|
|
280
|
+
for config, result in zip(configs, results):
|
|
281
|
+
print(f"LR {config['learning_rate']}: {result.value}")
|
|
282
|
+
|
|
283
|
+
asyncio.run(main())
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
## Requirements
|
|
287
|
+
|
|
288
|
+
- Python 3.8+
|
|
289
|
+
|
|
290
|
+
## Development
|
|
291
|
+
|
|
292
|
+
```bash
|
|
293
|
+
# Install development dependencies
|
|
294
|
+
pip install -e ".[dev]"
|
|
295
|
+
|
|
296
|
+
# Run tests
|
|
297
|
+
pytest
|
|
298
|
+
|
|
299
|
+
# Run tests with coverage
|
|
300
|
+
pytest --cov=silver_run --cov-report=html
|
|
301
|
+
|
|
302
|
+
# Run linting
|
|
303
|
+
flake8 src/ tests/
|
|
304
|
+
mypy src/
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
## Contributing
|
|
308
|
+
|
|
309
|
+
Contributions are welcome! Please see [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
|
|
310
|
+
|
|
311
|
+
## License
|
|
312
|
+
|
|
313
|
+
Apache-2.0 - see [LICENSE](LICENSE) file for details.
|
|
314
|
+
|
|
315
|
+
## Related Packages
|
|
316
|
+
|
|
317
|
+
- [silver-data](https://github.com/adfgdartec/silver-data) - Dataset handling
|
|
318
|
+
- [silver-diagnostics](https://github.com/adfgdartec/silver-diagnostics) - ML diagnostics
|
|
319
|
+
- [silver-adapters](https://github.com/adfgdartec/silver-adapters) - Framework adapters
|