deep-river 0.3.1__tar.gz → 0.3.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.
Files changed (40) hide show
  1. {deep_river-0.3.1 → deep_river-0.3.2}/PKG-INFO +142 -191
  2. deep_river-0.3.2/README.md +257 -0
  3. {deep_river-0.3.1 → deep_river-0.3.2}/deep_river/__version__.py +1 -1
  4. deep_river-0.3.2/deep_river/classification/__init__.py +22 -0
  5. {deep_river-0.3.1 → deep_river-0.3.2}/deep_river/classification/classifier.py +33 -20
  6. {deep_river-0.3.1 → deep_river-0.3.2}/deep_river/classification/rolling_classifier.py +47 -33
  7. deep_river-0.3.2/deep_river/classification/zoo.py +545 -0
  8. {deep_river-0.3.1 → deep_river-0.3.2}/deep_river/regression/__init__.py +6 -4
  9. deep_river-0.3.2/deep_river/regression/multioutput.py +343 -0
  10. {deep_river-0.3.1 → deep_river-0.3.2}/deep_river/regression/regressor.py +30 -13
  11. {deep_river-0.3.1 → deep_river-0.3.2}/deep_river/regression/rolling_regressor.py +30 -7
  12. deep_river-0.3.2/deep_river/regression/zoo.py +489 -0
  13. {deep_river-0.3.1 → deep_river-0.3.2}/deep_river/utils/estimator_checks.py +1 -37
  14. {deep_river-0.3.1 → deep_river-0.3.2}/deep_river/utils/tensor_conversion.py +24 -12
  15. {deep_river-0.3.1 → deep_river-0.3.2}/deep_river.egg-info/PKG-INFO +142 -191
  16. {deep_river-0.3.1 → deep_river-0.3.2}/pyproject.toml +10 -2
  17. deep_river-0.3.1/README.md +0 -306
  18. deep_river-0.3.1/deep_river/classification/__init__.py +0 -20
  19. deep_river-0.3.1/deep_river/classification/zoo.py +0 -310
  20. deep_river-0.3.1/deep_river/regression/multioutput.py +0 -126
  21. deep_river-0.3.1/deep_river/regression/zoo.py +0 -288
  22. {deep_river-0.3.1 → deep_river-0.3.2}/LICENSE +0 -0
  23. {deep_river-0.3.1 → deep_river-0.3.2}/deep_river/__init__.py +0 -0
  24. {deep_river-0.3.1 → deep_river-0.3.2}/deep_river/anomaly/__init__.py +0 -0
  25. {deep_river-0.3.1 → deep_river-0.3.2}/deep_river/anomaly/ae.py +0 -0
  26. {deep_river-0.3.1 → deep_river-0.3.2}/deep_river/anomaly/probability_weighted_ae.py +0 -0
  27. {deep_river-0.3.1 → deep_river-0.3.2}/deep_river/anomaly/rolling_ae.py +0 -0
  28. {deep_river-0.3.1 → deep_river-0.3.2}/deep_river/anomaly/scaler.py +0 -0
  29. {deep_river-0.3.1 → deep_river-0.3.2}/deep_river/base.py +0 -0
  30. {deep_river-0.3.1 → deep_river-0.3.2}/deep_river/utils/__init__.py +0 -0
  31. {deep_river-0.3.1 → deep_river-0.3.2}/deep_river/utils/hooks.py +0 -0
  32. {deep_river-0.3.1 → deep_river-0.3.2}/deep_river/utils/inspect.py +0 -0
  33. {deep_river-0.3.1 → deep_river-0.3.2}/deep_river/utils/params.py +0 -0
  34. {deep_river-0.3.1 → deep_river-0.3.2}/deep_river/utils/test_estimators.py +0 -0
  35. {deep_river-0.3.1 → deep_river-0.3.2}/deep_river/utils/test_tensor_conversion.py +0 -0
  36. {deep_river-0.3.1 → deep_river-0.3.2}/deep_river.egg-info/SOURCES.txt +0 -0
  37. {deep_river-0.3.1 → deep_river-0.3.2}/deep_river.egg-info/dependency_links.txt +0 -0
  38. {deep_river-0.3.1 → deep_river-0.3.2}/deep_river.egg-info/requires.txt +0 -0
  39. {deep_river-0.3.1 → deep_river-0.3.2}/deep_river.egg-info/top_level.txt +0 -0
  40. {deep_river-0.3.1 → deep_river-0.3.2}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: deep_river
3
- Version: 0.3.1
3
+ Version: 0.3.2
4
4
  Summary: Online Deep Learning for river
5
5
  Author-email: Cedric Kulbach <cedric.kulbach@googlemail.com>, Lucas Cazzonelli <Cazzonelli@fzi.de>, Hoang-Anh Ngo <hoang-anh.ngo@alumni.polytechnique.org>, Max Halford <maxhalford25@gmail.com>, Saulo Martiello Mastelini <saulomastelini@gmail.com>
6
6
  License: BSD-3-Clause
@@ -70,8 +70,9 @@ Dynamic: license-file
70
70
  </p>
71
71
  <p align="center">
72
72
  <img alt="PyPI" src="https://img.shields.io/pypi/v/deep-river">
73
- <a href="https://codecov.io/gh/online-ml/deep-river" >
74
- <img src="https://codecov.io/gh/online-ml/deep-river/branch/master/graph/badge.svg?token=ZKUIISZAYA"/>
73
+ <!-- Single Coverage Badge (Shields.io) -->
74
+ <a href="https://app.codecov.io/gh/online-ml/deep-river" >
75
+ <img alt="Coverage" src="https://img.shields.io/codecov/c/github/online-ml/deep-river?branch=master&logo=codecov&label=coverage" />
75
76
  </a>
76
77
  <img alt="PyPI - Python Version" src="https://img.shields.io/pypi/pyversions/deep-river">
77
78
  <img alt="PyPI - Downloads" src="https://img.shields.io/pypi/dm/deep-river">
@@ -136,209 +137,160 @@ We build the development of neural networks on top of the <a href="https://www.r
136
137
  The following example creates a simple MLP architecture based on PyTorch and incrementally predicts and trains on the website phishing dataset.
137
138
  For further examples check out the <a href="https://online-ml.github.io/deep-river">Documentation</a>.
138
139
 
140
+
139
141
  ### Classification
140
142
 
141
143
  ```python
142
- >> > from river import metrics, datasets, preprocessing, compose
143
- >> > from deep_river import classification
144
- >> > from torch import nn
145
- >> > from torch import optim
146
- >> > from torch import manual_seed
147
-
148
- >> > _ = manual_seed(42)
149
-
150
- >> >
151
-
152
- class MyModule(nn.Module):
153
-
154
-
155
- ...
156
-
157
-
158
- def __init__(self, n_features):
159
-
160
-
161
- ...
162
- super(MyModule, self).__init__()
163
- ...
164
- self.dense0 = nn.Linear(n_features, 5)
165
- ...
166
- self.nonlin = nn.ReLU()
167
- ...
168
- self.dense1 = nn.Linear(5, 2)
169
- ...
170
- self.softmax = nn.Softmax(dim=-1)
171
- ...
172
- ...
173
-
174
-
175
- def forward(self, X, **kwargs):
176
-
177
-
178
- ...
179
- X = self.nonlin(self.dense0(X))
180
- ...
181
- X = self.nonlin(self.dense1(X))
182
- ...
183
- X = self.softmax(X)
184
- ...
185
- return X
186
-
187
- >> > model_pipeline = compose.Pipeline(
188
- ...
189
- preprocessing.StandardScaler(),
190
- ...
191
- classification.Classifier(module=MyModule(10), loss_fn='binary_cross_entropy',
192
- optimizer_fn='adam')
144
+ >>> import random, numpy as np
145
+ >>> from river import metrics, datasets, preprocessing, compose
146
+ >>> from deep_river.classification import Classifier
147
+ >>> from torch import nn, manual_seed
148
+ >>> _ = manual_seed(42); random.seed(42); np.random.seed(42)
149
+ >>> first_x, _ = next(iter(datasets.Phishing()))
150
+ >>> n_features = len(first_x)
151
+ >>> class MyModule(nn.Module):
152
+ ... def __init__(self, n_features):
153
+ ... super().__init__()
154
+ ... self.net = nn.Sequential(
155
+ ... nn.Linear(n_features, 16),
156
+ ... nn.ReLU(),
157
+ ... nn.Linear(16, 2)
158
+ ... )
159
+ ... def forward(self, x):
160
+ ... return self.net(x)
161
+ >>> model = compose.Pipeline(
162
+ ... preprocessing.StandardScaler(),
163
+ ... Classifier(
164
+ ... module=MyModule(n_features),
165
+ ... loss_fn='cross_entropy',
166
+ ... optimizer_fn='adam',
167
+ ... lr=1e-3,
168
+ ... is_class_incremental=True
169
+ ... )
193
170
  ... )
194
-
195
- >> > dataset = datasets.Phishing()
196
- >> > metric = metrics.Accuracy()
197
-
198
- >> > for x, y in dataset:
199
- ...
200
- y_pred = model_pipeline.predict_one(x) # make a prediction
201
- ...
202
- metric.update(y, y_pred) # update the metric
203
- ...
204
- model_pipeline.learn_one(x, y) # make the model learn
205
- >> > print(f"Accuracy: {metric.get():.4f}")
206
- Accuracy: 0.7264
171
+ >>> metric = metrics.Accuracy()
172
+ >>> for i, (x, y) in enumerate(datasets.Phishing().take(200)):
173
+ ... if i > 0:
174
+ ... y_pred = model.predict_one(x)
175
+ ... metric.update(y, y_pred)
176
+ ... model.learn_one(x, y)
177
+ >>> assert 0.5 <= metric.get() <= 1.0
178
+ >>> print(f"Accuracy: {metric.get():.4f}") # doctest: +ELLIPSIS
179
+ Accuracy: ...
207
180
 
208
181
  ```
182
+
209
183
  ### Multi Target Regression
210
184
 
211
185
  ```python
212
- >> > from river import evaluate, compose
213
- >> > from river import metrics
214
- >> > from river import preprocessing
215
- >> > from river import stream
216
- >> > from sklearn import datasets
217
- >> > from torch import nn
218
- >> > from deep_river.regression.multioutput import MultiTargetRegressor
219
-
220
- >> >
221
-
222
- class MyModule(nn.Module):
223
-
224
-
225
- ...
226
-
227
-
228
- def __init__(self, n_features):
229
-
230
-
231
- ...
232
- super(MyModule, self).__init__()
233
- ...
234
- self.dense0 = nn.Linear(n_features, 3)
235
- ...
236
- ...
237
-
238
-
239
- def forward(self, X, **kwargs):
240
-
241
-
242
- ...
243
- X = self.dense0(X)
244
- ...
245
- return X
246
-
247
- >> > dataset = stream.iter_sklearn_dataset(
248
- ...
249
- dataset = datasets.load_linnerud(),
250
- ...
251
- shuffle = True,
252
- ...
253
- seed = 42
186
+ >>> import random, numpy as np
187
+ >>> from river import stream, metrics, preprocessing, compose
188
+ >>> from sklearn import datasets as sk_datasets
189
+ >>> from deep_river.regression import MultiTargetRegressor
190
+ >>> from torch import nn, manual_seed
191
+ >>> _ = manual_seed(42); random.seed(42); np.random.seed(42)
192
+ >>> linnerud_stream = stream.iter_sklearn_dataset(sk_datasets.load_linnerud(), shuffle=True, seed=42)
193
+ >>> first_x, first_y = next(iter(linnerud_stream))
194
+ >>> n_features, n_outputs = len(first_x), len(first_y)
195
+ >>> class TinyNet(nn.Module):
196
+ ... def __init__(self, n_features, n_outputs):
197
+ ... super().__init__()
198
+ ... self.net = nn.Sequential(
199
+ ... nn.Linear(n_features, 16),
200
+ ... nn.ReLU(),
201
+ ... nn.Linear(16, n_outputs)
202
+ ... )
203
+ ... def forward(self, x):
204
+ ... return self.net(x)
205
+ >>> model = compose.Pipeline(
206
+ ... preprocessing.StandardScaler(), # feature scaling stabilizes training
207
+ ... MultiTargetRegressor(
208
+ ... module=TinyNet(n_features, n_outputs),
209
+ ... loss_fn='mse', optimizer_fn='adam', lr=5e-3,
210
+ ... is_feature_incremental=False, is_target_incremental=False,
211
+ ... gradient_clip_value=1.0,
254
212
  ... )
255
- >> > model = compose.Pipeline(
256
- ...
257
- preprocessing.StandardScaler(),
258
- ...
259
- MultiTargetRegressorInitialized(
260
- ...
261
- module = MyModule(10),
262
- ...
263
- loss_fn = 'mse',
264
- ...
265
- lr = 0.3,
266
- ...
267
- optimizer_fn = 'sgd',
268
- ... ))
269
- >> > metric = metrics.multioutput.MicroAverage(metrics.MAE())
270
- >> > ev = evaluate.progressive_val_score(dataset, model, metric)
271
- >> > print(f"MicroAverage(MAE): {metric.get():.2f}")
272
- MicroAverage(MAE): 34.31
213
+ ... )
214
+ >>> mae_micro = metrics.multioutput.MicroAverage(metrics.MAE())
215
+ >>> mae_macro = metrics.multioutput.MacroAverage(metrics.MAE())
216
+ >>> rmse_micro = metrics.multioutput.MicroAverage(metrics.RMSE())
217
+ >>> # Recreate the iterator (the first sample was consumed to infer shapes)
218
+ >>> linnerud_stream = stream.iter_sklearn_dataset(sk_datasets.load_linnerud(), shuffle=True, seed=42)
219
+ >>> for i, (x, y_dict) in enumerate(linnerud_stream):
220
+ ... if i > 0:
221
+ ... y_pred = model.predict_one(x)
222
+ ... mae_micro.update(y_dict, y_pred)
223
+ ... mae_macro.update(y_dict, y_pred)
224
+ ... rmse_micro.update(y_dict, y_pred)
225
+ ... model.learn_one(x, y_dict)
226
+ >>> assert 0.0 <= mae_micro.get() < 300.0
227
+ >>> assert 0.0 <= mae_macro.get() < 300.0
228
+ >>> assert 0.0 <= rmse_micro.get() < 400.0
229
+ >>> print({
230
+ ... 'MAE_micro': round(mae_micro.get(), 4),
231
+ ... 'MAE_macro': round(mae_macro.get(), 4),
232
+ ... 'RMSE_micro': round(rmse_micro.get(), 4)
233
+ ... }) # doctest: +ELLIPSIS
234
+ {'MAE_micro': ..., 'MAE_macro': ..., 'RMSE_micro': ...}
273
235
 
274
236
  ```
275
237
 
276
238
  ### Anomaly Detection
277
239
 
278
240
  ```python
279
- >> > from deep_river.anomaly import Autoencoder
280
- >> > from river import metrics
281
- >> > from river.datasets import CreditCard
282
- >> > from torch import nn
283
- >> > import math
284
- >> > from river.compose import Pipeline
285
- >> > from river.preprocessing import MinMaxScaler
286
-
287
- >> > dataset = CreditCard().take(5000)
288
- >> > metric = metrics.RollingROCAUC(window_size=5000)
289
-
290
- >> >
291
-
292
- class MyAutoEncoder(nn.Module):
293
-
294
-
295
- ...
296
-
297
-
298
- def __init__(self, n_features, latent_dim=3):
299
-
300
-
301
- ...
302
- super(MyAutoEncoder, self).__init__()
303
- ...
304
- self.linear1 = nn.Linear(n_features, latent_dim)
305
- ...
306
- self.nonlin = nn.LeakyReLU()
307
- ...
308
- self.linear2 = nn.Linear(latent_dim, n_features)
309
- ...
310
- self.sigmoid = nn.Sigmoid()
311
- ...
312
- ...
313
-
314
-
315
- def forward(self, X, **kwargs):
316
-
317
-
318
- ...
319
- X = self.linear1(X)
320
- ...
321
- X = self.nonlin(X)
322
- ...
323
- X = self.linear2(X)
324
- ...
325
- return self.sigmoid(X)
326
-
327
- >> > ae = AutoencoderInitialized(module=MyAutoEncoder(10), lr=0.005)
328
- >> > scaler = MinMaxScaler()
329
- >> > model = Pipeline(scaler, ae)
330
-
331
- >> > for x, y in dataset:
332
- ...
333
- score = model.score_one(x)
334
- ...
335
- model.learn_one(x=x)
336
- ...
337
- metric.update(y, score)
338
- ...
339
- >> > print(f"Rolling ROCAUC: {metric.get():.4f}")
340
- Rolling
341
- ROCAUC: 0.8901
241
+ >>> import random, numpy as np
242
+ >>> from torch import nn, manual_seed
243
+ >>> from river import metrics
244
+ >>> from deep_river.anomaly import Autoencoder
245
+ >>> _ = manual_seed(42); random.seed(42); np.random.seed(42)
246
+ >>> # Create a deterministic synthetic stream: normals (y=0) vs. anomalies (y=1)
247
+ >>> def synthetic_stream(n_norm=2000, n_anom=200, n_features=8, seed=42):
248
+ ... rng = np.random.default_rng(seed)
249
+ ... # Normals around 0, anomalies shifted
250
+ ... X_norm = rng.normal(loc=0.0, scale=1.0, size=(n_norm, n_features))
251
+ ... y_norm = np.zeros(n_norm, dtype=int)
252
+ ... X_anom = rng.normal(loc=5.0, scale=1.0, size=(n_anom, n_features))
253
+ ... y_anom = np.ones(n_anom, dtype=int)
254
+ ... # Interleave anomalies every k steps to keep streaming flavour
255
+ ... k = max(1, n_norm // n_anom)
256
+ ... X, Y = [], []
257
+ ... i_norm = i_anom = 0
258
+ ... for t in range(n_norm + n_anom):
259
+ ... take_anom = (t % k == 0) and (i_anom < n_anom)
260
+ ... if take_anom:
261
+ ... X.append(X_anom[i_anom]); Y.append(y_anom[i_anom]); i_anom += 1
262
+ ... else:
263
+ ... if i_norm < n_norm:
264
+ ... X.append(X_norm[i_norm]); Y.append(y_norm[i_norm]); i_norm += 1
265
+ ... elif i_anom < n_anom:
266
+ ... X.append(X_anom[i_anom]); Y.append(y_anom[i_anom]); i_anom += 1
267
+ ... for row, label in zip(X, Y):
268
+ ... yield {f"f{i}": float(v) for i, v in enumerate(row)}, int(label)
269
+ >>> n_features = 8
270
+ >>> class MyAutoEncoder(nn.Module):
271
+ ... def __init__(self, n_features, latent_dim=4):
272
+ ... super().__init__()
273
+ ... self.encoder = nn.Sequential(
274
+ ... nn.Linear(n_features, latent_dim),
275
+ ... nn.LeakyReLU()
276
+ ... )
277
+ ... self.decoder = nn.Sequential(
278
+ ... nn.Linear(latent_dim, n_features),
279
+ ... nn.Sigmoid()
280
+ ... )
281
+ ... def forward(self, x):
282
+ ... z = self.encoder(x)
283
+ ... return self.decoder(z)
284
+ >>> ae = Autoencoder(module=MyAutoEncoder(n_features), lr=5e-3, optimizer_fn='adam')
285
+ >>> metric = metrics.ROCAUC()
286
+ >>> # Train only on normal samples to keep the model focused on the normal manifold
287
+ >>> for x, y in synthetic_stream(n_norm=2000, n_anom=200, n_features=n_features, seed=42):
288
+ ... score = ae.score_one(x)
289
+ ... if y == 0:
290
+ ... ae.learn_one(x)
291
+ ... metric.update(y, score)
292
+ >>> print(f"ROCAUC: {metric.get():.4f}") # doctest: +ELLIPSIS
293
+ ROCAUC: ...
342
294
 
343
295
  ```
344
296
 
@@ -361,7 +313,6 @@ To acknowledge the use of the `DeepRiver` library in your research, please refer
361
313
  }
362
314
  ```
363
315
 
364
-
365
316
  ## 🏫 Affiliations
366
317
 
367
318
  <p align="center">
@@ -0,0 +1,257 @@
1
+ <p align="center">
2
+ <img height="150px" src="https://raw.githubusercontent.com/online-ml/deep-river/master/docs/img/logo.png" alt="incremental dl logo">
3
+ </p>
4
+ <p align="center">
5
+ <img alt="PyPI" src="https://img.shields.io/pypi/v/deep-river">
6
+ <!-- Single Coverage Badge (Shields.io) -->
7
+ <a href="https://app.codecov.io/gh/online-ml/deep-river" >
8
+ <img alt="Coverage" src="https://img.shields.io/codecov/c/github/online-ml/deep-river?branch=master&logo=codecov&label=coverage" />
9
+ </a>
10
+ <img alt="PyPI - Python Version" src="https://img.shields.io/pypi/pyversions/deep-river">
11
+ <img alt="PyPI - Downloads" src="https://img.shields.io/pypi/dm/deep-river">
12
+ <img alt="GitHub" src="https://img.shields.io/github/license/online-ml/deep-river">
13
+ <a href="https://joss.theoj.org/papers/6a76784f55e8b041d71a7fa776eb386a"><img src="https://joss.theoj.org/papers/6a76784f55e8b041d71a7fa776eb386a/status.svg"></a>
14
+ <a href="https://doi.org/10.5281/zenodo.14601980"><img src="https://zenodo.org/badge/DOI/10.5281/zenodo.14601980.svg" alt="DOI"></a>
15
+ </p>
16
+ <p align="center">
17
+ deep-river is a Python library for online deep learning.
18
+ deep-river's ambition is to enable <a href="https://www.wikiwand.com/en/Online_machine_learning">online machine learning</a> for neural networks.
19
+ It combines the <a href="https://www.riverml.xyz">river</a> API with the capabilities of designing neural networks based on <a href="https://pytorch.org">PyTorch</a>.
20
+ </p>
21
+
22
+ ## 📚 [Documentation](https://online-ml.github.io/deep-river/)
23
+ The [documentation](https://online-ml.github.io/deep-river/) contains an overview of all features of this repository as well as the repository's full features list.
24
+ In each of these, the git repo reference is listed in a section that shows [examples](https://github.com/online-ml/deep-river/blob/master/docs/examples) of the features and functionality.
25
+ As we are always looking for further use cases and examples, feel free to contribute to the documentation or the repository itself via a pull request
26
+
27
+ ## 💈 Installation
28
+
29
+ ```shell
30
+ pip install deep-river
31
+ ```
32
+ or
33
+ ```shell
34
+ pip install "river[deep]"
35
+ ```
36
+ You can install the latest development version from GitHub as so:
37
+
38
+ ```shell
39
+ pip install https://github.com/online-ml/deep-river/archive/refs/heads/master.zip
40
+ ```
41
+
42
+ ### Development Environment
43
+
44
+ For contributing to deep-river, we recommend using [uv](https://docs.astral.sh/uv/) for fast dependency management and environment setup:
45
+
46
+ ```shell
47
+ # Install uv if you haven't already
48
+ curl -LsSf https://astral.sh/uv/install.sh | sh
49
+
50
+ # Clone the repository
51
+ git clone https://github.com/online-ml/deep-river.git
52
+ cd deep-river
53
+
54
+ # Install all dependencies (including dev dependencies)
55
+ uv sync --extra dev
56
+
57
+ # Run tests
58
+ make test
59
+
60
+ # Format code
61
+ make format
62
+
63
+ # Build documentation
64
+ make doc
65
+ ```
66
+
67
+ ## 🍫 Quickstart
68
+
69
+ We build the development of neural networks on top of the <a href="https://www.riverml.xyz">river API</a> and refer to the rivers design principles.
70
+ The following example creates a simple MLP architecture based on PyTorch and incrementally predicts and trains on the website phishing dataset.
71
+ For further examples check out the <a href="https://online-ml.github.io/deep-river">Documentation</a>.
72
+
73
+
74
+ ### Classification
75
+
76
+ ```python
77
+ >>> import random, numpy as np
78
+ >>> from river import metrics, datasets, preprocessing, compose
79
+ >>> from deep_river.classification import Classifier
80
+ >>> from torch import nn, manual_seed
81
+ >>> _ = manual_seed(42); random.seed(42); np.random.seed(42)
82
+ >>> first_x, _ = next(iter(datasets.Phishing()))
83
+ >>> n_features = len(first_x)
84
+ >>> class MyModule(nn.Module):
85
+ ... def __init__(self, n_features):
86
+ ... super().__init__()
87
+ ... self.net = nn.Sequential(
88
+ ... nn.Linear(n_features, 16),
89
+ ... nn.ReLU(),
90
+ ... nn.Linear(16, 2)
91
+ ... )
92
+ ... def forward(self, x):
93
+ ... return self.net(x)
94
+ >>> model = compose.Pipeline(
95
+ ... preprocessing.StandardScaler(),
96
+ ... Classifier(
97
+ ... module=MyModule(n_features),
98
+ ... loss_fn='cross_entropy',
99
+ ... optimizer_fn='adam',
100
+ ... lr=1e-3,
101
+ ... is_class_incremental=True
102
+ ... )
103
+ ... )
104
+ >>> metric = metrics.Accuracy()
105
+ >>> for i, (x, y) in enumerate(datasets.Phishing().take(200)):
106
+ ... if i > 0:
107
+ ... y_pred = model.predict_one(x)
108
+ ... metric.update(y, y_pred)
109
+ ... model.learn_one(x, y)
110
+ >>> assert 0.5 <= metric.get() <= 1.0
111
+ >>> print(f"Accuracy: {metric.get():.4f}") # doctest: +ELLIPSIS
112
+ Accuracy: ...
113
+
114
+ ```
115
+
116
+ ### Multi Target Regression
117
+
118
+ ```python
119
+ >>> import random, numpy as np
120
+ >>> from river import stream, metrics, preprocessing, compose
121
+ >>> from sklearn import datasets as sk_datasets
122
+ >>> from deep_river.regression import MultiTargetRegressor
123
+ >>> from torch import nn, manual_seed
124
+ >>> _ = manual_seed(42); random.seed(42); np.random.seed(42)
125
+ >>> linnerud_stream = stream.iter_sklearn_dataset(sk_datasets.load_linnerud(), shuffle=True, seed=42)
126
+ >>> first_x, first_y = next(iter(linnerud_stream))
127
+ >>> n_features, n_outputs = len(first_x), len(first_y)
128
+ >>> class TinyNet(nn.Module):
129
+ ... def __init__(self, n_features, n_outputs):
130
+ ... super().__init__()
131
+ ... self.net = nn.Sequential(
132
+ ... nn.Linear(n_features, 16),
133
+ ... nn.ReLU(),
134
+ ... nn.Linear(16, n_outputs)
135
+ ... )
136
+ ... def forward(self, x):
137
+ ... return self.net(x)
138
+ >>> model = compose.Pipeline(
139
+ ... preprocessing.StandardScaler(), # feature scaling stabilizes training
140
+ ... MultiTargetRegressor(
141
+ ... module=TinyNet(n_features, n_outputs),
142
+ ... loss_fn='mse', optimizer_fn='adam', lr=5e-3,
143
+ ... is_feature_incremental=False, is_target_incremental=False,
144
+ ... gradient_clip_value=1.0,
145
+ ... )
146
+ ... )
147
+ >>> mae_micro = metrics.multioutput.MicroAverage(metrics.MAE())
148
+ >>> mae_macro = metrics.multioutput.MacroAverage(metrics.MAE())
149
+ >>> rmse_micro = metrics.multioutput.MicroAverage(metrics.RMSE())
150
+ >>> # Recreate the iterator (the first sample was consumed to infer shapes)
151
+ >>> linnerud_stream = stream.iter_sklearn_dataset(sk_datasets.load_linnerud(), shuffle=True, seed=42)
152
+ >>> for i, (x, y_dict) in enumerate(linnerud_stream):
153
+ ... if i > 0:
154
+ ... y_pred = model.predict_one(x)
155
+ ... mae_micro.update(y_dict, y_pred)
156
+ ... mae_macro.update(y_dict, y_pred)
157
+ ... rmse_micro.update(y_dict, y_pred)
158
+ ... model.learn_one(x, y_dict)
159
+ >>> assert 0.0 <= mae_micro.get() < 300.0
160
+ >>> assert 0.0 <= mae_macro.get() < 300.0
161
+ >>> assert 0.0 <= rmse_micro.get() < 400.0
162
+ >>> print({
163
+ ... 'MAE_micro': round(mae_micro.get(), 4),
164
+ ... 'MAE_macro': round(mae_macro.get(), 4),
165
+ ... 'RMSE_micro': round(rmse_micro.get(), 4)
166
+ ... }) # doctest: +ELLIPSIS
167
+ {'MAE_micro': ..., 'MAE_macro': ..., 'RMSE_micro': ...}
168
+
169
+ ```
170
+
171
+ ### Anomaly Detection
172
+
173
+ ```python
174
+ >>> import random, numpy as np
175
+ >>> from torch import nn, manual_seed
176
+ >>> from river import metrics
177
+ >>> from deep_river.anomaly import Autoencoder
178
+ >>> _ = manual_seed(42); random.seed(42); np.random.seed(42)
179
+ >>> # Create a deterministic synthetic stream: normals (y=0) vs. anomalies (y=1)
180
+ >>> def synthetic_stream(n_norm=2000, n_anom=200, n_features=8, seed=42):
181
+ ... rng = np.random.default_rng(seed)
182
+ ... # Normals around 0, anomalies shifted
183
+ ... X_norm = rng.normal(loc=0.0, scale=1.0, size=(n_norm, n_features))
184
+ ... y_norm = np.zeros(n_norm, dtype=int)
185
+ ... X_anom = rng.normal(loc=5.0, scale=1.0, size=(n_anom, n_features))
186
+ ... y_anom = np.ones(n_anom, dtype=int)
187
+ ... # Interleave anomalies every k steps to keep streaming flavour
188
+ ... k = max(1, n_norm // n_anom)
189
+ ... X, Y = [], []
190
+ ... i_norm = i_anom = 0
191
+ ... for t in range(n_norm + n_anom):
192
+ ... take_anom = (t % k == 0) and (i_anom < n_anom)
193
+ ... if take_anom:
194
+ ... X.append(X_anom[i_anom]); Y.append(y_anom[i_anom]); i_anom += 1
195
+ ... else:
196
+ ... if i_norm < n_norm:
197
+ ... X.append(X_norm[i_norm]); Y.append(y_norm[i_norm]); i_norm += 1
198
+ ... elif i_anom < n_anom:
199
+ ... X.append(X_anom[i_anom]); Y.append(y_anom[i_anom]); i_anom += 1
200
+ ... for row, label in zip(X, Y):
201
+ ... yield {f"f{i}": float(v) for i, v in enumerate(row)}, int(label)
202
+ >>> n_features = 8
203
+ >>> class MyAutoEncoder(nn.Module):
204
+ ... def __init__(self, n_features, latent_dim=4):
205
+ ... super().__init__()
206
+ ... self.encoder = nn.Sequential(
207
+ ... nn.Linear(n_features, latent_dim),
208
+ ... nn.LeakyReLU()
209
+ ... )
210
+ ... self.decoder = nn.Sequential(
211
+ ... nn.Linear(latent_dim, n_features),
212
+ ... nn.Sigmoid()
213
+ ... )
214
+ ... def forward(self, x):
215
+ ... z = self.encoder(x)
216
+ ... return self.decoder(z)
217
+ >>> ae = Autoencoder(module=MyAutoEncoder(n_features), lr=5e-3, optimizer_fn='adam')
218
+ >>> metric = metrics.ROCAUC()
219
+ >>> # Train only on normal samples to keep the model focused on the normal manifold
220
+ >>> for x, y in synthetic_stream(n_norm=2000, n_anom=200, n_features=n_features, seed=42):
221
+ ... score = ae.score_one(x)
222
+ ... if y == 0:
223
+ ... ae.learn_one(x)
224
+ ... metric.update(y, score)
225
+ >>> print(f"ROCAUC: {metric.get():.4f}") # doctest: +ELLIPSIS
226
+ ROCAUC: ...
227
+
228
+ ```
229
+
230
+ ## 💬 Citation
231
+
232
+ To acknowledge the use of the `DeepRiver` library in your research, please refer to our [paper](https://joss.theoj.org/papers/10.21105/joss.07226) published on Journal of Open Source Software (JOSS):
233
+
234
+ ```bibtex
235
+ @article{Kulbach2025,
236
+ doi = {10.21105/joss.07226},
237
+ url = {https://doi.org/10.21105/joss.07226},
238
+ year = {2025},
239
+ publisher = {The Open Journal},
240
+ volume = {10},
241
+ number = {105},
242
+ pages = {7226},
243
+ author = {Cedric Kulbach and Lucas Cazzonelli and Hoang-Anh Ngo and Max Halford and Saulo Martiello Mastelini},
244
+ title = {DeepRiver: A Deep Learning Library for Data Streams},
245
+ journal = {Journal of Open Source Software}
246
+ }
247
+ ```
248
+
249
+ ## 🏫 Affiliations
250
+
251
+ <p align="center">
252
+ <img src="https://upload.wikimedia.org/wikipedia/de/thumb/4/44/Fzi_logo.svg/1200px-Fzi_logo.svg.png?raw=true" alt="FZI Logo" height="200"/>
253
+ </p>
254
+
255
+ <p align="center">
256
+ <img src="https://lieferbotnet.de/wp-content/uploads/2022/09/LieferBotNet-Logo.png?raw=true" alt="Lieferbot net" height="200"/>
257
+ </p>
@@ -1,3 +1,3 @@
1
- VERSION = (0, 3, 1)
1
+ VERSION = (0, 3, 2)
2
2
 
3
3
  __version__ = ".".join(map(str, VERSION)) # noqa: F401
@@ -0,0 +1,22 @@
1
+ from deep_river.classification.classifier import Classifier
2
+ from deep_river.classification.rolling_classifier import (
3
+ RollingClassifier,
4
+ )
5
+ from deep_river.classification.zoo import (
6
+ LogisticRegression,
7
+ LSTMClassifier,
8
+ MultiLayerPerceptron,
9
+ RNNClassifier,
10
+ )
11
+
12
+ """
13
+ This module contains the classifiers for the deep_river package.
14
+ """
15
+ __all__ = [
16
+ "Classifier",
17
+ "RollingClassifier",
18
+ "LogisticRegression",
19
+ "MultiLayerPerceptron",
20
+ "LSTMClassifier",
21
+ "RNNClassifier",
22
+ ]