Spaces:
Running
Running
MilesCranmer
commited on
Commit
•
d16abb4
1
Parent(s):
fc67c56
Rename loss => elementwise_loss and full_objective => loss_function
Browse files- README.md +1 -1
- docs/examples.md +4 -4
- docs/options.md +7 -7
- example.py +1 -1
- examples/pysr_demo.ipynb +1 -1
- pysr/__init__.py +1 -3
- pysr/deprecated.py +41 -36
- pysr/julia_helpers.py +1 -9
- pysr/param_groupings.yml +2 -2
- pysr/sr.py +21 -16
- pysr/test/test.py +2 -2
README.md
CHANGED
@@ -214,7 +214,7 @@ model = PySRRegressor(
|
|
214 |
],
|
215 |
extra_sympy_mappings={"inv": lambda x: 1 / x},
|
216 |
# ^ Define operator for SymPy as well
|
217 |
-
|
218 |
# ^ Custom loss function (julia syntax)
|
219 |
)
|
220 |
```
|
|
|
214 |
],
|
215 |
extra_sympy_mappings={"inv": lambda x: 1 / x},
|
216 |
# ^ Define operator for SymPy as well
|
217 |
+
elementwise_loss="loss(prediction, target) = (prediction - target)^2",
|
218 |
# ^ Custom loss function (julia syntax)
|
219 |
)
|
220 |
```
|
docs/examples.md
CHANGED
@@ -144,7 +144,7 @@ but there are still some additional steps you can take to reduce the effect of n
|
|
144 |
|
145 |
One thing you could do, which we won't detail here, is to create a custom log-likelihood
|
146 |
given some assumed noise model. By passing weights to the fit function, and
|
147 |
-
defining a custom loss function such as `
|
148 |
you can define any sort of log-likelihood you wish. (However, note that it must be bounded at zero)
|
149 |
|
150 |
However, the simplest thing to do is preprocessing, just like for feature selection. To do this,
|
@@ -380,7 +380,7 @@ end
|
|
380 |
model = PySRRegressor(
|
381 |
niterations=100,
|
382 |
binary_operators=["*", "+", "-"],
|
383 |
-
|
384 |
)
|
385 |
```
|
386 |
|
@@ -462,7 +462,7 @@ let's also create a custom loss function
|
|
462 |
that looks at the error in log-space:
|
463 |
|
464 |
```python
|
465 |
-
|
466 |
scatter_loss = abs(log((abs(prediction)+1e-20) / (abs(target)+1e-20)))
|
467 |
sign_loss = 10 * (sign(prediction) - sign(target))^2
|
468 |
return scatter_loss + sign_loss
|
@@ -476,7 +476,7 @@ Now let's define our model:
|
|
476 |
model = PySRRegressor(
|
477 |
binary_operators=["+", "-", "*", "/"],
|
478 |
unary_operators=["square"],
|
479 |
-
|
480 |
complexity_of_constants=2,
|
481 |
maxsize=25,
|
482 |
niterations=100,
|
|
|
144 |
|
145 |
One thing you could do, which we won't detail here, is to create a custom log-likelihood
|
146 |
given some assumed noise model. By passing weights to the fit function, and
|
147 |
+
defining a custom loss function such as `elementwise_loss="myloss(x, y, w) = w * (x - y)^2"`,
|
148 |
you can define any sort of log-likelihood you wish. (However, note that it must be bounded at zero)
|
149 |
|
150 |
However, the simplest thing to do is preprocessing, just like for feature selection. To do this,
|
|
|
380 |
model = PySRRegressor(
|
381 |
niterations=100,
|
382 |
binary_operators=["*", "+", "-"],
|
383 |
+
loss_function=objective,
|
384 |
)
|
385 |
```
|
386 |
|
|
|
462 |
that looks at the error in log-space:
|
463 |
|
464 |
```python
|
465 |
+
elementwise_loss = """function loss_fnc(prediction, target)
|
466 |
scatter_loss = abs(log((abs(prediction)+1e-20) / (abs(target)+1e-20)))
|
467 |
sign_loss = 10 * (sign(prediction) - sign(target))^2
|
468 |
return scatter_loss + sign_loss
|
|
|
476 |
model = PySRRegressor(
|
477 |
binary_operators=["+", "-", "*", "/"],
|
478 |
unary_operators=["square"],
|
479 |
+
elementwise_loss=elementwise_loss,
|
480 |
complexity_of_constants=2,
|
481 |
maxsize=25,
|
482 |
niterations=100,
|
docs/options.md
CHANGED
@@ -243,7 +243,7 @@ train the parameters within JAX (and is differentiable).
|
|
243 |
|
244 |
The default loss is mean-square error, and weighted mean-square error.
|
245 |
One can pass an arbitrary Julia string to define a custom loss, using,
|
246 |
-
e.g., `
|
247 |
see the
|
248 |
[Losses](https://milescranmer.github.io/SymbolicRegression.jl/dev/losses/)
|
249 |
page for SymbolicRegression.jl.
|
@@ -253,26 +253,26 @@ Here are some additional examples:
|
|
253 |
abs(x-y) loss
|
254 |
|
255 |
```python
|
256 |
-
PySRRegressor(...,
|
257 |
```
|
258 |
|
259 |
Note that the function name doesn't matter:
|
260 |
|
261 |
```python
|
262 |
-
PySRRegressor(...,
|
263 |
```
|
264 |
|
265 |
With weights:
|
266 |
|
267 |
```python
|
268 |
-
model = PySRRegressor(...,
|
269 |
model.fit(..., weights=weights)
|
270 |
```
|
271 |
|
272 |
Weights can be used in arbitrary ways:
|
273 |
|
274 |
```python
|
275 |
-
model = PySRRegressor(..., weights=weights,
|
276 |
model.fit(..., weights=weights)
|
277 |
```
|
278 |
|
@@ -280,13 +280,13 @@ Built-in loss (faster) (see [losses](https://astroautomata.com/SymbolicRegressio
|
|
280 |
This one computes the L3 norm:
|
281 |
|
282 |
```python
|
283 |
-
PySRRegressor(...,
|
284 |
```
|
285 |
|
286 |
Can also uses these losses for weighted (weighted-average):
|
287 |
|
288 |
```python
|
289 |
-
model = PySRRegressor(..., weights=weights,
|
290 |
model.fit(..., weights=weights)
|
291 |
```
|
292 |
|
|
|
243 |
|
244 |
The default loss is mean-square error, and weighted mean-square error.
|
245 |
One can pass an arbitrary Julia string to define a custom loss, using,
|
246 |
+
e.g., `elementwise_loss="myloss(x, y) = abs(x - y)^1.5"`. For more details,
|
247 |
see the
|
248 |
[Losses](https://milescranmer.github.io/SymbolicRegression.jl/dev/losses/)
|
249 |
page for SymbolicRegression.jl.
|
|
|
253 |
abs(x-y) loss
|
254 |
|
255 |
```python
|
256 |
+
PySRRegressor(..., elementwise_loss="f(x, y) = abs(x - y)^1.5")
|
257 |
```
|
258 |
|
259 |
Note that the function name doesn't matter:
|
260 |
|
261 |
```python
|
262 |
+
PySRRegressor(..., elementwise_loss="loss(x, y) = abs(x * y)")
|
263 |
```
|
264 |
|
265 |
With weights:
|
266 |
|
267 |
```python
|
268 |
+
model = PySRRegressor(..., elementwise_loss="myloss(x, y, w) = w * abs(x - y)")
|
269 |
model.fit(..., weights=weights)
|
270 |
```
|
271 |
|
272 |
Weights can be used in arbitrary ways:
|
273 |
|
274 |
```python
|
275 |
+
model = PySRRegressor(..., weights=weights, elementwise_loss="myloss(x, y, w) = abs(x - y)^2/w^2")
|
276 |
model.fit(..., weights=weights)
|
277 |
```
|
278 |
|
|
|
280 |
This one computes the L3 norm:
|
281 |
|
282 |
```python
|
283 |
+
PySRRegressor(..., elementwise_loss="LPDistLoss{3}()")
|
284 |
```
|
285 |
|
286 |
Can also uses these losses for weighted (weighted-average):
|
287 |
|
288 |
```python
|
289 |
+
model = PySRRegressor(..., weights=weights, elementwise_loss="LPDistLoss{3}()")
|
290 |
model.fit(..., weights=weights)
|
291 |
```
|
292 |
|
example.py
CHANGED
@@ -18,7 +18,7 @@ model = PySRRegressor(
|
|
18 |
],
|
19 |
extra_sympy_mappings={"inv": lambda x: 1 / x},
|
20 |
# ^ Define operator for SymPy as well
|
21 |
-
|
22 |
# ^ Custom loss function (julia syntax)
|
23 |
)
|
24 |
|
|
|
18 |
],
|
19 |
extra_sympy_mappings={"inv": lambda x: 1 / x},
|
20 |
# ^ Define operator for SymPy as well
|
21 |
+
elementwise_loss="loss(x, y) = (x - y)^2",
|
22 |
# ^ Custom loss function (julia syntax)
|
23 |
)
|
24 |
|
examples/pysr_demo.ipynb
CHANGED
@@ -576,7 +576,7 @@
|
|
576 |
"outputs": [],
|
577 |
"source": [
|
578 |
"model = PySRRegressor(\n",
|
579 |
-
"
|
580 |
" niterations=20,\n",
|
581 |
" populations=20, # Use more populations\n",
|
582 |
" binary_operators=[\"+\", \"*\"],\n",
|
|
|
576 |
"outputs": [],
|
577 |
"source": [
|
578 |
"model = PySRRegressor(\n",
|
579 |
+
" elementwise_loss=\"myloss(x, y, w) = w * abs(x - y)\", # Custom loss function with weights.\n",
|
580 |
" niterations=20,\n",
|
581 |
" populations=20, # Use more populations\n",
|
582 |
" binary_operators=[\"+\", \"*\"],\n",
|
pysr/__init__.py
CHANGED
@@ -4,10 +4,9 @@
|
|
4 |
from .julia_import import jl, SymbolicRegression # isort:skip
|
5 |
|
6 |
from . import sklearn_monkeypatch
|
7 |
-
from .deprecated import best, best_callable, best_row, best_tex, pysr
|
8 |
from .export_jax import sympy2jax
|
9 |
from .export_torch import sympy2torch
|
10 |
-
from .julia_helpers import install
|
11 |
from .sr import PySRRegressor
|
12 |
|
13 |
# This file is created by setuptools_scm during the build process:
|
@@ -28,5 +27,4 @@ __all__ = [
|
|
28 |
"best_tex",
|
29 |
"pysr",
|
30 |
"__version__",
|
31 |
-
"install",
|
32 |
]
|
|
|
4 |
from .julia_import import jl, SymbolicRegression # isort:skip
|
5 |
|
6 |
from . import sklearn_monkeypatch
|
7 |
+
from .deprecated import best, best_callable, best_row, best_tex, install, pysr
|
8 |
from .export_jax import sympy2jax
|
9 |
from .export_torch import sympy2torch
|
|
|
10 |
from .sr import PySRRegressor
|
11 |
|
12 |
# This file is created by setuptools_scm during the build process:
|
|
|
27 |
"best_tex",
|
28 |
"pysr",
|
29 |
"__version__",
|
|
|
30 |
]
|
pysr/deprecated.py
CHANGED
@@ -2,6 +2,22 @@
|
|
2 |
import warnings
|
3 |
|
4 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
5 |
def pysr(X, y, weights=None, **kwargs): # pragma: no cover
|
6 |
from .sr import PySRRegressor
|
7 |
|
@@ -55,39 +71,28 @@ def best_callable(*args, **kwargs): # pragma: no cover
|
|
55 |
)
|
56 |
|
57 |
|
58 |
-
|
59 |
-
""
|
60 |
-
|
61 |
-
|
62 |
-
|
63 |
-
|
64 |
-
|
65 |
-
|
66 |
-
|
67 |
-
|
68 |
-
|
69 |
-
|
70 |
-
|
71 |
-
|
72 |
-
|
73 |
-
|
74 |
-
|
75 |
-
|
76 |
-
|
77 |
-
|
78 |
-
|
79 |
-
|
80 |
-
|
81 |
-
|
82 |
-
|
83 |
-
"""
|
84 |
-
# Turn this into a dict:
|
85 |
-
deprecated_kwargs = {}
|
86 |
-
for line in deprecation_string.splitlines():
|
87 |
-
line = line.replace(" ", "")
|
88 |
-
if line == "":
|
89 |
-
continue
|
90 |
-
old, new = line.split("=>")
|
91 |
-
deprecated_kwargs[old] = new
|
92 |
-
|
93 |
-
return deprecated_kwargs
|
|
|
2 |
import warnings
|
3 |
|
4 |
|
5 |
+
def install(*args, **kwargs):
|
6 |
+
del args, kwargs
|
7 |
+
warnings.warn(
|
8 |
+
"The `install` function has been removed. "
|
9 |
+
"PySR now uses the `juliacall` package to install its dependencies automatically at import time. "
|
10 |
+
)
|
11 |
+
|
12 |
+
|
13 |
+
def init_julia(*args, **kwargs):
|
14 |
+
del args, kwargs
|
15 |
+
warnings.warn(
|
16 |
+
"The `init_julia` function has been removed. "
|
17 |
+
"Julia is now initialized automatically at import time."
|
18 |
+
)
|
19 |
+
|
20 |
+
|
21 |
def pysr(X, y, weights=None, **kwargs): # pragma: no cover
|
22 |
from .sr import PySRRegressor
|
23 |
|
|
|
71 |
)
|
72 |
|
73 |
|
74 |
+
DEPRECATED_KWARGS = {
|
75 |
+
"fractionReplaced": "fraction_replaced",
|
76 |
+
"fractionReplacedHof": "fraction_replaced_hof",
|
77 |
+
"npop": "population_size",
|
78 |
+
"hofMigration": "hof_migration",
|
79 |
+
"shouldOptimizeConstants": "should_optimize_constants",
|
80 |
+
"weightAddNode": "weight_add_node",
|
81 |
+
"weightDeleteNode": "weight_delete_node",
|
82 |
+
"weightDoNothing": "weight_do_nothing",
|
83 |
+
"weightInsertNode": "weight_insert_node",
|
84 |
+
"weightMutateConstant": "weight_mutate_constant",
|
85 |
+
"weightMutateOperator": "weight_mutate_operator",
|
86 |
+
"weightSwapOperands": "weight_swap_operands",
|
87 |
+
"weightRandomize": "weight_randomize",
|
88 |
+
"weightSimplify": "weight_simplify",
|
89 |
+
"crossoverProbability": "crossover_probability",
|
90 |
+
"perturbationFactor": "perturbation_factor",
|
91 |
+
"batchSize": "batch_size",
|
92 |
+
"warmupMaxsizeBy": "warmup_maxsize_by",
|
93 |
+
"useFrequency": "use_frequency",
|
94 |
+
"useFrequencyInTournament": "use_frequency_in_tournament",
|
95 |
+
"ncyclesperiteration": "ncycles_per_iteration",
|
96 |
+
"loss": "elementwise_loss",
|
97 |
+
"full_objective": "loss_function",
|
98 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pysr/julia_helpers.py
CHANGED
@@ -1,9 +1,9 @@
|
|
1 |
"""Functions for initializing the Julia environment and installing deps."""
|
2 |
-
import warnings
|
3 |
|
4 |
import numpy as np
|
5 |
from juliacall import convert as jl_convert # type: ignore
|
6 |
|
|
|
7 |
from .julia_import import jl
|
8 |
|
9 |
jl.seval("using Serialization: Serialization")
|
@@ -15,14 +15,6 @@ PythonCall = jl.PythonCall
|
|
15 |
jl.seval("using SymbolicRegression: plus, sub, mult, div, pow")
|
16 |
|
17 |
|
18 |
-
def install(*args, **kwargs):
|
19 |
-
del args, kwargs
|
20 |
-
warnings.warn(
|
21 |
-
"The `install` function has been removed. "
|
22 |
-
"PySR now uses the `juliacall` package to install its dependencies automatically at import time. "
|
23 |
-
)
|
24 |
-
|
25 |
-
|
26 |
def _escape_filename(filename):
|
27 |
"""Turn a path into a string with correctly escaped backslashes."""
|
28 |
str_repr = str(filename)
|
|
|
1 |
"""Functions for initializing the Julia environment and installing deps."""
|
|
|
2 |
|
3 |
import numpy as np
|
4 |
from juliacall import convert as jl_convert # type: ignore
|
5 |
|
6 |
+
from .deprecated import init_julia, install
|
7 |
from .julia_import import jl
|
8 |
|
9 |
jl.seval("using Serialization: Serialization")
|
|
|
15 |
jl.seval("using SymbolicRegression: plus, sub, mult, div, pow")
|
16 |
|
17 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
18 |
def _escape_filename(filename):
|
19 |
"""Turn a path into a string with correctly escaped backslashes."""
|
20 |
str_repr = str(filename)
|
pysr/param_groupings.yml
CHANGED
@@ -10,8 +10,8 @@
|
|
10 |
- population_size
|
11 |
- ncycles_per_iteration
|
12 |
- The Objective:
|
13 |
-
-
|
14 |
-
-
|
15 |
- model_selection
|
16 |
- dimensional_constraint_penalty
|
17 |
- Working with Complexities:
|
|
|
10 |
- population_size
|
11 |
- ncycles_per_iteration
|
12 |
- The Objective:
|
13 |
+
- elementwise_loss
|
14 |
+
- loss_function
|
15 |
- model_selection
|
16 |
- dimensional_constraint_penalty
|
17 |
- Working with Complexities:
|
pysr/sr.py
CHANGED
@@ -25,7 +25,7 @@ from sklearn.utils import check_array, check_consistent_length, check_random_sta
|
|
25 |
from sklearn.utils.validation import _check_feature_names_in, check_is_fitted
|
26 |
|
27 |
from .denoising import denoise, multi_denoise
|
28 |
-
from .deprecated import
|
29 |
from .export_jax import sympy2jax
|
30 |
from .export_latex import sympy2latex, sympy2latextable, sympy2multilatextable
|
31 |
from .export_numpy import sympy2numpy
|
@@ -268,7 +268,7 @@ class PySRRegressor(MultiOutputMixin, RegressorMixin, BaseEstimator):
|
|
268 |
arguments are treated the same way, and the max of each
|
269 |
argument is constrained.
|
270 |
Default is `None`.
|
271 |
-
|
272 |
String of Julia code specifying an elementwise loss function.
|
273 |
Can either be a loss from LossFunctions.jl, or your own loss
|
274 |
written as a function. Examples of custom written losses include:
|
@@ -284,11 +284,11 @@ class PySRRegressor(MultiOutputMixin, RegressorMixin, BaseEstimator):
|
|
284 |
`ModifiedHuberLoss()`, `L2MarginLoss()`, `ExpLoss()`,
|
285 |
`SigmoidLoss()`, `DWDMarginLoss(q)`.
|
286 |
Default is `"L2DistLoss()"`.
|
287 |
-
|
288 |
Alternatively, you can specify the full objective function as
|
289 |
a snippet of Julia code, including any sort of custom evaluation
|
290 |
(including symbolic manipulations beforehand), and any sort
|
291 |
-
of loss function or regularizations. The default `
|
292 |
used in SymbolicRegression.jl is roughly equal to:
|
293 |
```julia
|
294 |
function eval_loss(tree, dataset::Dataset{T,L}, options)::L where {T,L}
|
@@ -637,7 +637,7 @@ class PySRRegressor(MultiOutputMixin, RegressorMixin, BaseEstimator):
|
|
637 |
... "inv(x) = 1/x", # Custom operator (julia syntax)
|
638 |
... ],
|
639 |
... model_selection="best",
|
640 |
-
...
|
641 |
... )
|
642 |
>>> model.fit(X, y)
|
643 |
>>> model
|
@@ -675,8 +675,8 @@ class PySRRegressor(MultiOutputMixin, RegressorMixin, BaseEstimator):
|
|
675 |
timeout_in_seconds: Optional[float] = None,
|
676 |
constraints: Optional[Dict[str, Union[int, Tuple[int, int]]]] = None,
|
677 |
nested_constraints: Optional[Dict[str, Dict[str, int]]] = None,
|
678 |
-
|
679 |
-
|
680 |
complexity_of_operators: Optional[Dict[str, Union[int, float]]] = None,
|
681 |
complexity_of_constants: Union[int, float] = 1,
|
682 |
complexity_of_variables: Union[int, float] = 1,
|
@@ -769,8 +769,8 @@ class PySRRegressor(MultiOutputMixin, RegressorMixin, BaseEstimator):
|
|
769 |
self.timeout_in_seconds = timeout_in_seconds
|
770 |
self.early_stop_condition = early_stop_condition
|
771 |
# - Loss parameters
|
772 |
-
self.
|
773 |
-
self.
|
774 |
self.complexity_of_operators = complexity_of_operators
|
775 |
self.complexity_of_constants = complexity_of_constants
|
776 |
self.complexity_of_variables = complexity_of_variables
|
@@ -849,11 +849,10 @@ class PySRRegressor(MultiOutputMixin, RegressorMixin, BaseEstimator):
|
|
849 |
# Once all valid parameters have been assigned handle the
|
850 |
# deprecated kwargs
|
851 |
if len(kwargs) > 0: # pragma: no cover
|
852 |
-
deprecated_kwargs = make_deprecated_kwargs_for_pysr_regressor()
|
853 |
for k, v in kwargs.items():
|
854 |
# Handle renamed kwargs
|
855 |
-
if k in
|
856 |
-
updated_kwarg_name =
|
857 |
setattr(self, updated_kwarg_name, v)
|
858 |
warnings.warn(
|
859 |
f"{k} has been renamed to {updated_kwarg_name} in PySRRegressor. "
|
@@ -1251,8 +1250,10 @@ class PySRRegressor(MultiOutputMixin, RegressorMixin, BaseEstimator):
|
|
1251 |
"to True and `procs` to 0 will result in non-deterministic searches. "
|
1252 |
)
|
1253 |
|
1254 |
-
if self.
|
1255 |
-
raise ValueError(
|
|
|
|
|
1256 |
|
1257 |
# NotImplementedError - Values that could be supported at a later time
|
1258 |
if self.optimizer_algorithm not in VALID_OPTIMIZER_ALGORITHMS:
|
@@ -1587,9 +1588,13 @@ class PySRRegressor(MultiOutputMixin, RegressorMixin, BaseEstimator):
|
|
1587 |
complexity_of_operators_str += ")"
|
1588 |
complexity_of_operators = jl.seval(complexity_of_operators_str)
|
1589 |
|
1590 |
-
custom_loss = jl.seval(
|
|
|
|
|
|
|
|
|
1591 |
custom_full_objective = jl.seval(
|
1592 |
-
str(self.
|
1593 |
)
|
1594 |
|
1595 |
early_stop_condition = jl.seval(
|
|
|
25 |
from sklearn.utils.validation import _check_feature_names_in, check_is_fitted
|
26 |
|
27 |
from .denoising import denoise, multi_denoise
|
28 |
+
from .deprecated import DEPRECATED_KWARGS
|
29 |
from .export_jax import sympy2jax
|
30 |
from .export_latex import sympy2latex, sympy2latextable, sympy2multilatextable
|
31 |
from .export_numpy import sympy2numpy
|
|
|
268 |
arguments are treated the same way, and the max of each
|
269 |
argument is constrained.
|
270 |
Default is `None`.
|
271 |
+
elementwise_loss : str
|
272 |
String of Julia code specifying an elementwise loss function.
|
273 |
Can either be a loss from LossFunctions.jl, or your own loss
|
274 |
written as a function. Examples of custom written losses include:
|
|
|
284 |
`ModifiedHuberLoss()`, `L2MarginLoss()`, `ExpLoss()`,
|
285 |
`SigmoidLoss()`, `DWDMarginLoss(q)`.
|
286 |
Default is `"L2DistLoss()"`.
|
287 |
+
loss_function : str
|
288 |
Alternatively, you can specify the full objective function as
|
289 |
a snippet of Julia code, including any sort of custom evaluation
|
290 |
(including symbolic manipulations beforehand), and any sort
|
291 |
+
of loss function or regularizations. The default `loss_function`
|
292 |
used in SymbolicRegression.jl is roughly equal to:
|
293 |
```julia
|
294 |
function eval_loss(tree, dataset::Dataset{T,L}, options)::L where {T,L}
|
|
|
637 |
... "inv(x) = 1/x", # Custom operator (julia syntax)
|
638 |
... ],
|
639 |
... model_selection="best",
|
640 |
+
... elementwise_loss="loss(x, y) = (x - y)^2", # Custom loss function (julia syntax)
|
641 |
... )
|
642 |
>>> model.fit(X, y)
|
643 |
>>> model
|
|
|
675 |
timeout_in_seconds: Optional[float] = None,
|
676 |
constraints: Optional[Dict[str, Union[int, Tuple[int, int]]]] = None,
|
677 |
nested_constraints: Optional[Dict[str, Dict[str, int]]] = None,
|
678 |
+
elementwise_loss: Optional[str] = None,
|
679 |
+
loss_function: Optional[str] = None,
|
680 |
complexity_of_operators: Optional[Dict[str, Union[int, float]]] = None,
|
681 |
complexity_of_constants: Union[int, float] = 1,
|
682 |
complexity_of_variables: Union[int, float] = 1,
|
|
|
769 |
self.timeout_in_seconds = timeout_in_seconds
|
770 |
self.early_stop_condition = early_stop_condition
|
771 |
# - Loss parameters
|
772 |
+
self.elementwise_loss = elementwise_loss
|
773 |
+
self.loss_function = loss_function
|
774 |
self.complexity_of_operators = complexity_of_operators
|
775 |
self.complexity_of_constants = complexity_of_constants
|
776 |
self.complexity_of_variables = complexity_of_variables
|
|
|
849 |
# Once all valid parameters have been assigned handle the
|
850 |
# deprecated kwargs
|
851 |
if len(kwargs) > 0: # pragma: no cover
|
|
|
852 |
for k, v in kwargs.items():
|
853 |
# Handle renamed kwargs
|
854 |
+
if k in DEPRECATED_KWARGS:
|
855 |
+
updated_kwarg_name = DEPRECATED_KWARGS[k]
|
856 |
setattr(self, updated_kwarg_name, v)
|
857 |
warnings.warn(
|
858 |
f"{k} has been renamed to {updated_kwarg_name} in PySRRegressor. "
|
|
|
1250 |
"to True and `procs` to 0 will result in non-deterministic searches. "
|
1251 |
)
|
1252 |
|
1253 |
+
if self.elementwise_loss is not None and self.loss_function is not None:
|
1254 |
+
raise ValueError(
|
1255 |
+
"You cannot set both `elementwise_loss` and `loss_function`."
|
1256 |
+
)
|
1257 |
|
1258 |
# NotImplementedError - Values that could be supported at a later time
|
1259 |
if self.optimizer_algorithm not in VALID_OPTIMIZER_ALGORITHMS:
|
|
|
1588 |
complexity_of_operators_str += ")"
|
1589 |
complexity_of_operators = jl.seval(complexity_of_operators_str)
|
1590 |
|
1591 |
+
custom_loss = jl.seval(
|
1592 |
+
str(self.elementwise_loss)
|
1593 |
+
if self.elementwise_loss is not None
|
1594 |
+
else "nothing"
|
1595 |
+
)
|
1596 |
custom_full_objective = jl.seval(
|
1597 |
+
str(self.loss_function) if self.loss_function is not None else "nothing"
|
1598 |
)
|
1599 |
|
1600 |
early_stop_condition = jl.seval(
|
pysr/test/test.py
CHANGED
@@ -80,7 +80,7 @@ class TestPipeline(unittest.TestCase):
|
|
80 |
multithreading=False,
|
81 |
turbo=True,
|
82 |
early_stop_condition="stop_if(loss, complexity) = loss < 1e-10 && complexity == 1",
|
83 |
-
|
84 |
function my_objective(tree::Node{T}, dataset::Dataset{T}, options::Options) where T
|
85 |
prediction, flag = eval_tree_array(tree, dataset.X, options)
|
86 |
!flag && return T(Inf)
|
@@ -100,7 +100,7 @@ class TestPipeline(unittest.TestCase):
|
|
100 |
model = PySRRegressor(
|
101 |
**self.default_test_kwargs,
|
102 |
early_stop_condition="stop_if(loss, complexity) = loss < 1e-4 && complexity == 3",
|
103 |
-
|
104 |
precision=64,
|
105 |
parsimony=0.01,
|
106 |
warm_start=True,
|
|
|
80 |
multithreading=False,
|
81 |
turbo=True,
|
82 |
early_stop_condition="stop_if(loss, complexity) = loss < 1e-10 && complexity == 1",
|
83 |
+
loss_function="""
|
84 |
function my_objective(tree::Node{T}, dataset::Dataset{T}, options::Options) where T
|
85 |
prediction, flag = eval_tree_array(tree, dataset.X, options)
|
86 |
!flag && return T(Inf)
|
|
|
100 |
model = PySRRegressor(
|
101 |
**self.default_test_kwargs,
|
102 |
early_stop_condition="stop_if(loss, complexity) = loss < 1e-4 && complexity == 3",
|
103 |
+
elementwise_loss="my_loss(prediction, target) = (prediction - target)^2",
|
104 |
precision=64,
|
105 |
parsimony=0.01,
|
106 |
warm_start=True,
|