Repository: tinkoff-ai/etna (language: python)

Issue:
Add default `params_to_tune` for decomposition transforms
### 🚀 Feature Request

Add default `params_to_tune` for decomposition transforms

### Proposal

Suggested grid for `STLTransform`:
```python
{
    "model": CategoricalDistribution(["arima", "holt"]),
    "robust": CategoricalDistribution([False, True]),
}
```
It is expected that `period` is set by the user.

Suggested grid for `LinearTrendTransform`:
```python
{
    "poly_degree": IntUniformDistribution(low=1, high=2),
}
```

Suggested grid for `TheilSenTrendTransform`:
```python
{
    "poly_degree": IntUniformDistribution(low=1, high=2),
}
```

Suggested grid for `ChangePointsSegmentationTransform`. We should set default value for `change_points_model` like in `ChangePointsTrendTransform`. We tune parameters only if default values for `change_points_model` and `per_interval_model` are set:
```python
{
    "change_points_model.change_points_model.model": CategoricalDistribution(["l1", "l2", "normal", "rbf", "cosine", "linear", "clinear", "ar", "mahlanobis", "rank"]),
    "change_points_model.n_bkps": IntUniformDistribution(low=5, high=30),
}
```

Suggested grid for `ChangePointsTrendTransform`. We tune parameters only if default values for `change_points_model` and `per_interval_model` are set:
```python
{
    "change_points_model.change_points_model.model": CategoricalDistribution(["l1", "l2", "normal", "rbf", "cosine", "linear", "clinear", "ar", "mahlanobis", "rank"]),
    "change_points_model.n_bkps": IntUniformDistribution(low=5, high=30)
}
```

Suggested grid for `ChangePointsLevelTransform`. We tune parameters only if default values for `change_points_model` and `per_interval_model` are set:
```python
{
    "change_points_model.change_points_model.model": CategoricalDistribution(["l1", "l2", "normal", "rbf", "cosine", "linear", "clinear", "ar", "mahlanobis", "rank"]),
    "change_points_model.n_bkps": IntUniformDistribution(low=5, high=30),
}
```
We can't tune `per_interval_model` here, because possible values are classes instead of serializable types.

Suggested grid for `TrendTransform`. We should set default value for `change_points_model` like in `ChangePointsTrendTransform`. We tune parameters only if default values for `change_points_model` and `per_interval_model` are set:
```python
{
    "change_points_model.change_points_model.model": CategoricalDistribution(["l1", "l2", "normal", "rbf", "cosine", "linear", "clinear", "ar", "mahlanobis", "rank"]),
    "change_points_model.n_bkps": IntUniformDistribution(low=5, high=30),
}
```

To discuss:
- Do we want to tune more parameters of `LinearTrendTransform` and `TheilSenTrendTransform` that are related to the model itself?
  - It was decided not to.
- What should we do with `change_points_model` and `per_interval_model`? It look that there are a lot of problems with them.
- Do we want to try `MeanPerIntervalModel` and `MedianPerIntervalModel` for `TrendTransform`?


### Test cases

Look at #1221. Check empty grid as well.

### Additional context

_No response_
Add default `params_to_tune` for decomposition transforms
### 🚀 Feature Request

Add default `params_to_tune` for decomposition transforms

### Proposal

Suggested grid for `STLTransform`:
```python
{
    "model": CategoricalDistribution(["arima", "holt"]),
    "robust": CategoricalDistribution([False, True]),
}
```
It is expected that `period` is set by the user.

Suggested grid for `LinearTrendTransform`:
```python
{
    "poly_degree": IntUniformDistribution(low=1, high=2),
}
```

Suggested grid for `TheilSenTrendTransform`:
```python
{
    "poly_degree": IntUniformDistribution(low=1, high=2),
}
```

Suggested grid for `ChangePointsSegmentationTransform`. We should set default value for `change_points_model` like in `ChangePointsTrendTransform`. We tune parameters only if default values for `change_points_model` and `per_interval_model` are set:
```python
{
    "change_points_model.change_points_model.model": CategoricalDistribution(["l1", "l2", "normal", "rbf", "cosine", "linear", "clinear", "ar", "mahlanobis", "rank"]),
    "change_points_model.n_bkps": IntUniformDistribution(low=5, high=30),
}
```

Suggested grid for `ChangePointsTrendTransform`. We tune parameters only if default values for `change_points_model` and `per_interval_model` are set:
```python
{
    "change_points_model.change_points_model.model": CategoricalDistribution(["l1", "l2", "normal", "rbf", "cosine", "linear", "clinear", "ar", "mahlanobis", "rank"]),
    "change_points_model.n_bkps": IntUniformDistribution(low=5, high=30)
}
```

Suggested grid for `ChangePointsLevelTransform`. We tune parameters only if default values for `change_points_model` and `per_interval_model` are set:
```python
{
    "change_points_model.change_points_model.model": CategoricalDistribution(["l1", "l2", "normal", "rbf", "cosine", "linear", "clinear", "ar", "mahlanobis", "rank"]),
    "change_points_model.n_bkps": IntUniformDistribution(low=5, high=30),
}
```
We can't tune `per_interval_model` here, because possible values are classes instead of serializable types.

Suggested grid for `TrendTransform`. We should set default value for `change_points_model` like in `ChangePointsTrendTransform`. We tune parameters only if default values for `change_points_model` and `per_interval_model` are set:
```python
{
    "change_points_model.change_points_model.model": CategoricalDistribution(["l1", "l2", "normal", "rbf", "cosine", "linear", "clinear", "ar", "mahlanobis", "rank"]),
    "change_points_model.n_bkps": IntUniformDistribution(low=5, high=30),
}
```

To discuss:
- Do we want to tune more parameters of `LinearTrendTransform` and `TheilSenTrendTransform` that are related to the model itself?
  - It was decided not to.
- What should we do with `change_points_model` and `per_interval_model`? It look that there are a lot of problems with them.
- Do we want to try `MeanPerIntervalModel` and `MedianPerIntervalModel` for `TrendTransform`?


### Test cases

Look at #1221. Check empty grid as well.

### Additional context

_No response_


Discussion / hints:

Fix patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index a2539f434..696a2bd3e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -56,6 +56,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 - Add default `params_to_tune` for `TimeSeriesImputerTransform` ([#1232](https://github.com/tinkoff-ai/etna/pull/1232))
 - Add default `params_to_tune` for `DifferencingTransform`, `MedianTransform`, `MaxTransform`, `MinTransform`, `QuantileTransform`, `StdTransform`, `MeanTransform`, `MADTransform`, `MinMaxDifferenceTransform`, `SumTransform`, `BoxCoxTransform`, `YeoJohnsonTransform`, `MaxAbsScalerTransform`, `MinMaxScalerTransform`, `RobustScalerTransform` and `StandardScalerTransform` ([#1233](https://github.com/tinkoff-ai/etna/pull/1233))
 - Add default `params_to_tune` for `LabelEncoderTransform` ([#1242](https://github.com/tinkoff-ai/etna/pull/1242))
+- Add default `params_to_tune` for `ChangePointsSegmentationTransform`, `ChangePointsTrendTransform`, `ChangePointsLevelTransform`, `TrendTransform`, `LinearTrendTransform`, `TheilSenTrendTransform` and `STLTransform` ([#1243](https://github.com/tinkoff-ai/etna/pull/1243))
 ### Fixed
 - Fix bug in `GaleShapleyFeatureSelectionTransform` with wrong number of remaining features ([#1110](https://github.com/tinkoff-ai/etna/pull/1110))
 - `ProphetModel` fails with additional seasonality set ([#1157](https://github.com/tinkoff-ai/etna/pull/1157))
diff --git a/etna/transforms/decomposition/change_points_based/detrend.py b/etna/transforms/decomposition/change_points_based/detrend.py
index 9dd49301f..d9c1e9e0a 100644
--- a/etna/transforms/decomposition/change_points_based/detrend.py
+++ b/etna/transforms/decomposition/change_points_based/detrend.py
@@ -1,3 +1,4 @@
+from typing import Dict
 from typing import Optional
 
 import numpy as np
@@ -5,6 +6,7 @@
 from ruptures.detection import Binseg
 from sklearn.linear_model import LinearRegression
 
+from etna import SETTINGS
 from etna.transforms.decomposition.change_points_based.base import ReversibleChangePointsTransform
 from etna.transforms.decomposition.change_points_based.base import _OneSegmentChangePointsTransform
 from etna.transforms.decomposition.change_points_based.change_points_models import BaseChangePointsModelAdapter
@@ -13,6 +15,11 @@
 from etna.transforms.decomposition.change_points_based.per_interval_models import SklearnRegressionPerIntervalModel
 from etna.transforms.utils import match_target_quantiles
 
+if SETTINGS.auto_required:
+    from optuna.distributions import BaseDistribution
+    from optuna.distributions import CategoricalDistribution
+    from optuna.distributions import IntUniformDistribution
+
 
 class _OneSegmentChangePointsTrendTransform(_OneSegmentChangePointsTransform):
     """_OneSegmentChangePointsTransform subtracts multiple linear trend from series."""
@@ -38,7 +45,16 @@ def _apply_inverse_transformation(self, df: pd.DataFrame, transformed_series: pd
 
 
 class ChangePointsTrendTransform(ReversibleChangePointsTransform):
-    """ChangePointsTrendTransform uses :py:class:`ruptures.detection.Binseg` model as a change point detection model.
+    """Transform that makes a detrending of change-point intervals.
+
+    This class differs from :py:class:`~etna.transforms.decomposition.change_points_based.level.ChangePointsLevelTransform`
+    only by default values for ``change_points_model`` and ``per_interval_model``.
+
+    Transform divides each segment into intervals using ``change_points_model``.
+    Then a separate model is fitted on each interval using ``per_interval_model``.
+    Values predicted by the model are subtracted from each interval.
+
+    Evaluated function can be linear, mean, median, etc. Look at the signature to find out which models can be used.
 
     Warning
     -------
@@ -46,6 +62,12 @@ class ChangePointsTrendTransform(ReversibleChangePointsTransform):
     it uses information from the whole train part.
     """
 
+    _default_change_points_model = RupturesChangePointsModel(
+        change_points_model=Binseg(model="ar"),
+        n_bkps=5,
+    )
+    _default_per_interval_model = SklearnRegressionPerIntervalModel(model=LinearRegression())
+
     def __init__(
         self,
         in_column: str,
@@ -59,24 +81,21 @@ def __init__(
         in_column:
             name of column to apply transform to
         change_points_model:
-            model to get trend change points
+            model to get trend change points,
+            by default :py:class:`ruptures.detection.Binseg` in a wrapper with ``n_bkps=5`` is used
         per_interval_model:
-            model to process intervals of segment
+            model to process intervals of segment,
+            by default :py:class:`sklearn.linear_models.LinearRegression` in a wrapper is used
         """
         self.in_column = in_column
+
         self.change_points_model = (
-            change_points_model
-            if change_points_model is not None
-            else RupturesChangePointsModel(
-                change_points_model=Binseg(model="ar"),
-                n_bkps=5,
-            )
+            change_points_model if change_points_model is not None else self._default_change_points_model
         )
         self.per_interval_model = (
-            per_interval_model
-            if per_interval_model is not None
-            else SklearnRegressionPerIntervalModel(model=LinearRegression())
+            per_interval_model if per_interval_model is not None else self._default_per_interval_model
         )
+
         super().__init__(
             transform=_OneSegmentChangePointsTrendTransform(
                 in_column=self.in_column,
@@ -85,3 +104,30 @@ def __init__(
             ),
             required_features=[in_column],
         )
+
+    @property
+    def _is_change_points_model_default(self) -> bool:
+        # it can't see the difference between Binseg(model="ar") and Binseg(model="l1")
+        return self.change_points_model.to_dict() == self._default_change_points_model.to_dict()
+
+    def params_to_tune(self) -> Dict[str, "BaseDistribution"]:
+        """Get default grid for tuning hyperparameters.
+
+        If ``change_points_model`` is equal to default then this grid tunes parameters:
+        ``change_points_model.change_points_model.model``, ``change_points_model.n_bkps``.
+        Other parameters are expected to be set by the user.
+
+        Returns
+        -------
+        :
+            Grid to tune.
+        """
+        if self._is_change_points_model_default:
+            return {
+                "change_points_model.change_points_model.model": CategoricalDistribution(
+                    ["l1", "l2", "normal", "rbf", "cosine", "linear", "clinear", "ar", "mahalanobis", "rank"]
+                ),
+                "change_points_model.n_bkps": IntUniformDistribution(low=5, high=30),
+            }
+        else:
+            return {}
diff --git a/etna/transforms/decomposition/change_points_based/level.py b/etna/transforms/decomposition/change_points_based/level.py
index c2df1c5bd..41a3f7c8a 100644
--- a/etna/transforms/decomposition/change_points_based/level.py
+++ b/etna/transforms/decomposition/change_points_based/level.py
@@ -1,7 +1,9 @@
+from typing import Dict
 from typing import Optional
 
 from ruptures import Binseg
 
+from etna import SETTINGS
 from etna.transforms.decomposition.change_points_based.base import BaseChangePointsModelAdapter
 from etna.transforms.decomposition.change_points_based.base import ReversibleChangePointsTransform
 from etna.transforms.decomposition.change_points_based.change_points_models.ruptures_based import (
@@ -11,6 +13,11 @@
 from etna.transforms.decomposition.change_points_based.per_interval_models import MeanPerIntervalModel
 from etna.transforms.decomposition.change_points_based.per_interval_models import StatisticsPerIntervalModel
 
+if SETTINGS.auto_required:
+    from optuna.distributions import BaseDistribution
+    from optuna.distributions import CategoricalDistribution
+    from optuna.distributions import IntUniformDistribution
+
 
 class _OneSegmentChangePointsLevelTransform(_OneSegmentChangePointsTrendTransform):
     def __init__(
@@ -36,7 +43,16 @@ def __init__(
 
 
 class ChangePointsLevelTransform(ReversibleChangePointsTransform):
-    """ChangePointsLevelTransform uses :py:class:`ruptures.detection.Binseg` model as a change point detection model.
+    """Transform that makes a detrending of change-point intervals.
+
+    This class differs from :py:class:`~etna.transforms.decomposition.change_points_based.detrend.ChangePointsTrendTransform`
+    only by default values for ``change_points_model`` and ``per_interval_model``.
+
+    Transform divides each segment into intervals using ``change_points_model``.
+    Then a separate model is fitted on each interval using ``per_interval_model``.
+    Values predicted by the model are subtracted from each interval.
+
+    Evaluated function can be linear, mean, median, etc. Look at the signature to find out which models can be used.
 
     Warning
     -------
@@ -44,6 +60,12 @@ class ChangePointsLevelTransform(ReversibleChangePointsTransform):
     it uses information from the whole train part.
     """
 
+    _default_change_points_model = RupturesChangePointsModel(
+        change_points_model=Binseg(model="ar"),
+        n_bkps=5,
+    )
+    _default_per_interval_model = MeanPerIntervalModel()
+
     def __init__(
         self,
         in_column: str,
@@ -57,20 +79,21 @@ def __init__(
         in_column:
             name of column to apply transform to
         change_points_model:
-            model to get trend change points
+            model to get trend change points,
+            by default :py:class:`ruptures.detection.Binseg` in a wrapper with ``n_bkps=5`` is used
         per_interval_model:
-            model to process intervals of segment
+            model to process intervals of segment,
+            by default mean value is used to evaluate the interval
         """
         self.in_column = in_column
+
         self.change_points_model = (
-            change_points_model
-            if change_points_model is not None
-            else RupturesChangePointsModel(
-                change_points_model=Binseg(model="l2"),
-                n_bkps=5,
-            )
+            change_points_model if change_points_model is not None else self._default_change_points_model
+        )
+        self.per_interval_model = (
+            per_interval_model if per_interval_model is not None else self._default_per_interval_model
         )
-        self.per_interval_model = per_interval_model if per_interval_model is not None else MeanPerIntervalModel()
+
         super().__init__(
             transform=_OneSegmentChangePointsLevelTransform(
                 in_column=self.in_column,
@@ -79,3 +102,30 @@ def __init__(
             ),
             required_features=[in_column],
         )
+
+    @property
+    def _is_change_points_model_default(self) -> bool:
+        # it can't see the difference between Binseg(model="ar") and Binseg(model="l1")
+        return self.change_points_model.to_dict() == self._default_change_points_model.to_dict()
+
+    def params_to_tune(self) -> Dict[str, "BaseDistribution"]:
+        """Get default grid for tuning hyperparameters.
+
+        If ``change_points_model`` is equal to default then this grid tunes parameters:
+        ``change_points_model.change_points_model.model``, ``change_points_model.n_bkps``.
+        Other parameters are expected to be set by the user.
+
+        Returns
+        -------
+        :
+            Grid to tune.
+        """
+        if self._is_change_points_model_default:
+            return {
+                "change_points_model.change_points_model.model": CategoricalDistribution(
+                    ["l1", "l2", "normal", "rbf", "cosine", "linear", "clinear", "ar", "mahalanobis", "rank"]
+                ),
+                "change_points_model.n_bkps": IntUniformDistribution(low=5, high=30),
+            }
+        else:
+            return {}
diff --git a/etna/transforms/decomposition/change_points_based/per_interval_models/base.py b/etna/transforms/decomposition/change_points_based/per_interval_models/base.py
index 429ee0bf5..a657429dd 100644
--- a/etna/transforms/decomposition/change_points_based/per_interval_models/base.py
+++ b/etna/transforms/decomposition/change_points_based/per_interval_models/base.py
@@ -10,7 +10,7 @@ class PerIntervalModel(BaseMixin, ABC):
     """Class to handle intervals in change point based transforms.
 
     PerIntervalModel is a class to process intervals between change points
-    in `~etna.transforms.decomposition.change_points_based` transforms.
+    in :py:mod:`~etna.transforms.decomposition.change_points_based` transforms.
     """
 
     @abstractmethod
diff --git a/etna/transforms/decomposition/change_points_based/segmentation.py b/etna/transforms/decomposition/change_points_based/segmentation.py
index b279a4363..092a7cc05 100644
--- a/etna/transforms/decomposition/change_points_based/segmentation.py
+++ b/etna/transforms/decomposition/change_points_based/segmentation.py
@@ -1,13 +1,24 @@
+from typing import Dict
 from typing import Optional
 
 import numpy as np
 import pandas as pd
+from ruptures import Binseg
 
+from etna import SETTINGS
 from etna.transforms.decomposition.change_points_based.base import IrreversibleChangePointsTransform
 from etna.transforms.decomposition.change_points_based.base import _OneSegmentChangePointsTransform
 from etna.transforms.decomposition.change_points_based.change_points_models import BaseChangePointsModelAdapter
+from etna.transforms.decomposition.change_points_based.change_points_models.ruptures_based import (
+    RupturesChangePointsModel,
+)
 from etna.transforms.decomposition.change_points_based.per_interval_models import ConstantPerIntervalModel
 
+if SETTINGS.auto_required:
+    from optuna.distributions import BaseDistribution
+    from optuna.distributions import CategoricalDistribution
+    from optuna.distributions import IntUniformDistribution
+
 
 class _OneSegmentChangePointsSegmentationTransform(_OneSegmentChangePointsTransform):
     """_OneSegmentChangePointsSegmentationTransform make label encoder to change points."""
@@ -20,7 +31,7 @@ def __init__(self, in_column: str, out_column: str, change_points_model: BaseCha
             name of column to apply transform to
         out_column:
             result column name. If not given use ``self.__repr__()``
-        change_point_model:
+        change_points_model:
             model to get change points
         """
         self.out_column = out_column
@@ -46,7 +57,11 @@ def _apply_inverse_transformation(self, df: pd.DataFrame, transformed_series: pd
 
 
 class ChangePointsSegmentationTransform(IrreversibleChangePointsTransform):
-    """ChangePointsSegmentationTransform make label encoder to change points.
+    """Transform that makes label encoding of change-point intervals.
+
+    Transform divides each segment into intervals using ``change_points_model``.
+    Each interval is enumerated based on its index from the start of the segment.
+    New column is created with number of interval for each timestamp.
 
     Warning
     -------
@@ -54,10 +69,15 @@ class ChangePointsSegmentationTransform(IrreversibleChangePointsTransform):
     it uses information from the whole train part.
     """
 
+    _default_change_points_model = RupturesChangePointsModel(
+        change_points_model=Binseg(model="ar"),
+        n_bkps=5,
+    )
+
     def __init__(
         self,
         in_column: str,
-        change_points_model: BaseChangePointsModelAdapter,
+        change_points_model: BaseChangePointsModelAdapter = None,
         out_column: Optional[str] = None,
     ):
         """Init ChangePointsSegmentationTransform.
@@ -66,19 +86,51 @@ def __init__(
         ----------
         in_column:
             name of column to fit change point model
+        change_points_model:
+            model to get change points,
+            by default :py:class:`ruptures.detection.Binseg` in a wrapper with ``n_bkps=5`` is used
         out_column:
             result column name. If not given use ``self.__repr__()``
-        change_points_model:
-            model to get change points
         """
         self.in_column = in_column
         self.out_column = out_column if out_column is not None else self.__repr__()
-        self.change_points_model = change_points_model
+
+        self.change_points_model = (
+            change_points_model if change_points_model is not None else self._default_change_points_model
+        )
+
         super().__init__(
             transform=_OneSegmentChangePointsSegmentationTransform(
                 in_column=self.in_column,
-                out_column=self.out_column,
                 change_points_model=self.change_points_model,
+                out_column=self.out_column,
             ),
             required_features=[in_column],
         )
+
+    @property
+    def _is_change_points_model_default(self) -> bool:
+        # it can't see the difference between Binseg(model="ar") and Binseg(model="l1")
+        return self.change_points_model.to_dict() == self._default_change_points_model.to_dict()
+
+    def params_to_tune(self) -> Dict[str, "BaseDistribution"]:
+        """Get default grid for tuning hyperparameters.
+
+        If ``change_points_model`` is equal to default then this grid tunes parameters:
+        ``change_points_model.change_points_model.model``, ``change_points_model.n_bkps``.
+        Other parameters are expected to be set by the user.
+
+        Returns
+        -------
+        :
+            Grid to tune.
+        """
+        if self._is_change_points_model_default:
+            return {
+                "change_points_model.change_points_model.model": CategoricalDistribution(
+                    ["l1", "l2", "normal", "rbf", "cosine", "linear", "clinear", "ar", "mahalanobis", "rank"]
+                ),
+                "change_points_model.n_bkps": IntUniformDistribution(low=5, high=30),
+            }
+        else:
+            return {}
diff --git a/etna/transforms/decomposition/change_points_based/trend.py b/etna/transforms/decomposition/change_points_based/trend.py
index 4a66e94c2..2a435c1f7 100644
--- a/etna/transforms/decomposition/change_points_based/trend.py
+++ b/etna/transforms/decomposition/change_points_based/trend.py
@@ -1,13 +1,25 @@
+from typing import Dict
 from typing import Optional
 
 import pandas as pd
+from ruptures import Binseg
+from sklearn.linear_model import LinearRegression
 
+from etna import SETTINGS
 from etna.transforms.decomposition.change_points_based.base import IrreversibleChangePointsTransform
 from etna.transforms.decomposition.change_points_based.change_points_models import BaseChangePointsModelAdapter
+from etna.transforms.decomposition.change_points_based.change_points_models.ruptures_based import (
+    RupturesChangePointsModel,
+)
 from etna.transforms.decomposition.change_points_based.detrend import _OneSegmentChangePointsTrendTransform
 from etna.transforms.decomposition.change_points_based.per_interval_models import PerIntervalModel
 from etna.transforms.decomposition.change_points_based.per_interval_models import SklearnRegressionPerIntervalModel
 
+if SETTINGS.auto_required:
+    from optuna.distributions import BaseDistribution
+    from optuna.distributions import CategoricalDistribution
+    from optuna.distributions import IntUniformDistribution
+
 
 class _OneSegmentTrendTransform(_OneSegmentChangePointsTrendTransform):
     """_OneSegmentTrendTransform adds trend as a feature."""
@@ -48,10 +60,13 @@ def _apply_inverse_transformation(self, df: pd.DataFrame, transformed_series: pd
 
 
 class TrendTransform(IrreversibleChangePointsTransform):
-    """TrendTransform adds trend as a feature.
+    """Transform that adds trend as a feature.
+
+    Transform divides each segment into intervals using ``change_points_model``.
+    Then a separate model is fitted on each interval using ``per_interval_model``.
+    New column is created with values predicted by the model of each interval.
 
-    TrendTransform uses uses :py:class:`ruptures.detection.Binseg` model as a change point detection model
-    in _TrendTransform.
+    Evaluated function can be linear, mean, median, etc. Look at the signature to find out which models can be used.
 
     Warning
     -------
@@ -59,10 +74,16 @@ class TrendTransform(IrreversibleChangePointsTransform):
     it uses information from the whole train part.
     """
 
+    _default_change_points_model = RupturesChangePointsModel(
+        change_points_model=Binseg(model="ar"),
+        n_bkps=5,
+    )
+    _default_per_interval_model = SklearnRegressionPerIntervalModel(model=LinearRegression())
+
     def __init__(
         self,
         in_column: str,
-        change_points_model: BaseChangePointsModelAdapter,
+        change_points_model: BaseChangePointsModelAdapter = None,
         per_interval_model: Optional[PerIntervalModel] = None,
         out_column: Optional[str] = None,
     ):
@@ -72,16 +93,26 @@ def __init__(
         ----------
         in_column:
             name of column to apply transform to
+        change_points_model:
+            model to get trend change points,
+            by default :py:class:`ruptures.detection.Binseg` in a wrapper with ``n_bkps=5`` is used
+        per_interval_model:
+            model to process intervals of segment,
+            by default :py:class:`sklearn.linear_models.LinearRegression` in a wrapper is used
         out_column:
             name of added column.
             If not given, use ``self.__repr__()``
         """
         self.in_column = in_column
+        self.out_column = out_column
+
+        self.change_points_model = (
+            change_points_model if change_points_model is not None else self._default_change_points_model
+        )
         self.per_interval_model = (
-            SklearnRegressionPerIntervalModel() if per_interval_model is None else per_interval_model
+            per_interval_model if per_interval_model is not None else self._default_per_interval_model
         )
-        self.change_points_model = change_points_model
-        self.out_column = out_column
+
         super().__init__(
             transform=_OneSegmentTrendTransform(
                 in_column=self.in_column,
@@ -91,3 +122,30 @@ def __init__(
             ),
             required_features=[in_column],
         )
+
+    @property
+    def _is_change_points_model_default(self) -> bool:
+        # it can't see the difference between Binseg(model="ar") and Binseg(model="l1")
+        return self.change_points_model.to_dict() == self._default_change_points_model.to_dict()
+
+    def params_to_tune(self) -> Dict[str, "BaseDistribution"]:
+        """Get default grid for tuning hyperparameters.
+
+        If ``change_points_model`` is equal to default then this grid tunes parameters:
+        ``change_points_model.change_points_model.model``, ``change_points_model.n_bkps``.
+        Other parameters are expected to be set by the user.
+
+        Returns
+        -------
+        :
+            Grid to tune.
+        """
+        if self._is_change_points_model_default:
+            return {
+                "change_points_model.change_points_model.model": CategoricalDistribution(
+                    ["l1", "l2", "normal", "rbf", "cosine", "linear", "clinear", "ar", "mahalanobis", "rank"]
+                ),
+                "change_points_model.n_bkps": IntUniformDistribution(low=5, high=30),
+            }
+        else:
+            return {}
diff --git a/etna/transforms/decomposition/detrend.py b/etna/transforms/decomposition/detrend.py
index cbdf20e57..77290184b 100644
--- a/etna/transforms/decomposition/detrend.py
+++ b/etna/transforms/decomposition/detrend.py
@@ -1,3 +1,4 @@
+from typing import Dict
 from typing import List
 
 import numpy as np
@@ -8,13 +9,18 @@
 from sklearn.pipeline import Pipeline
 from sklearn.preprocessing import PolynomialFeatures
 
+from etna import SETTINGS
 from etna.transforms.base import OneSegmentTransform
 from etna.transforms.base import ReversiblePerSegmentWrapper
 from etna.transforms.utils import match_target_quantiles
 
+if SETTINGS.auto_required:
+    from optuna.distributions import BaseDistribution
+    from optuna.distributions import IntUniformDistribution
+
 
 class _OneSegmentLinearTrendBaseTransform(OneSegmentTransform):
-    """LinearTrendBaseTransform is a base class that implements trend subtraction and reconstruction feature."""
+    """Transform for one segment that implements trend subtraction and reconstruction feature."""
 
     def __init__(self, in_column: str, regressor: RegressorMixin, poly_degree: int = 1):
         """
@@ -137,8 +143,10 @@ def inverse_transform(self, df: pd.DataFrame) -> pd.DataFrame:
 
 
 class LinearTrendTransform(ReversiblePerSegmentWrapper):
-    """
-    Transform that uses :py:class:`sklearn.linear_model.LinearRegression` to find linear or polynomial trend in data.
+    """Transform that uses linear regression with polynomial features to make a detrending.
+
+    Transform fits a :py:class:`sklearn.linear_model.LinearRegression` with polynomial features on each segment.
+    Values predicted by the model are subtracted from each segment.
 
     Warning
     -------
@@ -174,10 +182,26 @@ def get_regressors_info(self) -> List[str]:
         """Return the list with regressors created by the transform."""
         return []
 
+    def params_to_tune(self) -> Dict[str, "BaseDistribution"]:
+        """Get default grid for tuning hyperparameters.
+
+        This grid tunes only ``poly_degree`` parameter. Other parameters are expected to be set by the user.
+
+        Returns
+        -------
+        :
+            Grid to tune.
+        """
+        return {
+            "poly_degree": IntUniformDistribution(low=1, high=2),
+        }
+
 
 class TheilSenTrendTransform(ReversiblePerSegmentWrapper):
-    """
-    Transform that uses :py:class:`sklearn.linear_model.TheilSenRegressor` to find linear or polynomial trend in data.
+    """Transform that uses Theil–Sen regression with polynomial features to make a detrending.
+
+    Transform fits a :py:class:`sklearn.linear_model.TheilSenRegressor` with polynomial features on each segment.
+    Values predicted by the model are subtracted from each segment.
 
     Warning
     -------
@@ -217,3 +241,17 @@ def __init__(self, in_column: str, poly_degree: int = 1, **regression_params):
     def get_regressors_info(self) -> List[str]:
         """Return the list with regressors created by the transform."""
         return []
+
+    def params_to_tune(self) -> Dict[str, "BaseDistribution"]:
+        """Get default grid for tuning hyperparameters.
+
+        This grid tunes only ``poly_degree`` parameter. Other parameters are expected to be set by the user.
+
+        Returns
+        -------
+        :
+            Grid to tune.
+        """
+        return {
+            "poly_degree": IntUniformDistribution(low=1, high=2),
+        }
diff --git a/etna/transforms/decomposition/stl.py b/etna/transforms/decomposition/stl.py
index 4b33a3ef7..554bb14b6 100644
--- a/etna/transforms/decomposition/stl.py
+++ b/etna/transforms/decomposition/stl.py
@@ -11,10 +11,15 @@
 from statsmodels.tsa.forecasting.stl import STLForecast
 from statsmodels.tsa.forecasting.stl import STLForecastResults
 
+from etna import SETTINGS
 from etna.transforms.base import OneSegmentTransform
 from etna.transforms.base import ReversiblePerSegmentWrapper
 from etna.transforms.utils import match_target_quantiles
 
+if SETTINGS.auto_required:
+    from optuna.distributions import BaseDistribution
+    from optuna.distributions import CategoricalDistribution
+
 
 class _OneSegmentSTLTransform(OneSegmentTransform):
     def __init__(
@@ -224,3 +229,18 @@ def __init__(
     def get_regressors_info(self) -> List[str]:
         """Return the list with regressors created by the transform."""
         return []
+
+    def params_to_tune(self) -> Dict[str, "BaseDistribution"]:
+        """Get default grid for tuning hyperparameters.
+
+        This grid tunes parameters: ``model``, ``robust``. Other parameters are expected to be set by the user.
+
+        Returns
+        -------
+        :
+            Grid to tune.
+        """
+        return {
+            "model": CategoricalDistribution(["arima", "holt"]),
+            "robust": CategoricalDistribution([False, True]),
+        }


Test patch:
diff --git a/tests/test_transforms/test_decomposition/test_change_points_based/test_detrend.py b/tests/test_transforms/test_decomposition/test_change_points_based/test_detrend.py
index 5fc21ee15..5edb3ca43 100644
--- a/tests/test_transforms/test_decomposition/test_change_points_based/test_detrend.py
+++ b/tests/test_transforms/test_decomposition/test_change_points_based/test_detrend.py
@@ -18,6 +18,7 @@
 from etna.transforms.decomposition import ChangePointsTrendTransform
 from etna.transforms.decomposition import RupturesChangePointsModel
 from etna.transforms.decomposition.change_points_based.detrend import _OneSegmentChangePointsTrendTransform
+from tests.test_transforms.utils import assert_sampling_is_valid
 from tests.test_transforms.utils import assert_transformation_equals_loaded_original
 
 
@@ -97,3 +98,35 @@ def test_get_features(example_tsds: TSDataset):
 def test_save_load(example_tsds):
     transform = ChangePointsTrendTransform(in_column="target")
     assert_transformation_equals_loaded_original(transform=transform, ts=example_tsds)
+
+
+@pytest.mark.parametrize(
+    "transform, expected_length",
+    [
+        (ChangePointsTrendTransform(in_column="target"), 2),
+        (
+            ChangePointsTrendTransform(
+                in_column="target",
+                change_points_model=RupturesChangePointsModel(
+                    change_points_model=Binseg(model="ar"),
+                    n_bkps=5,
+                ),
+            ),
+            2,
+        ),
+        (
+            ChangePointsTrendTransform(
+                in_column="target",
+                change_points_model=RupturesChangePointsModel(
+                    change_points_model=Binseg(model="ar"),
+                    n_bkps=10,
+                ),
+            ),
+            0,
+        ),
+    ],
+)
+def test_params_to_tune(transform, expected_length, example_tsds):
+    ts = example_tsds
+    assert len(transform.params_to_tune()) == expected_length
+    assert_sampling_is_valid(transform=transform, ts=ts)
diff --git a/tests/test_transforms/test_decomposition/test_change_points_based/test_level.py b/tests/test_transforms/test_decomposition/test_change_points_based/test_level.py
index 334d17171..56423407f 100644
--- a/tests/test_transforms/test_decomposition/test_change_points_based/test_level.py
+++ b/tests/test_transforms/test_decomposition/test_change_points_based/test_level.py
@@ -9,6 +9,7 @@
 from etna.transforms import ChangePointsLevelTransform
 from etna.transforms.decomposition.change_points_based.change_points_models import RupturesChangePointsModel
 from etna.transforms.decomposition.change_points_based.per_interval_models import MeanPerIntervalModel
+from tests.test_transforms.utils import assert_sampling_is_valid
 from tests.test_transforms.utils import assert_transformation_equals_loaded_original
 
 
@@ -70,3 +71,35 @@ def test_save_load(ts_with_local_levels):
         per_interval_model=MeanPerIntervalModel(),
     )
     assert_transformation_equals_loaded_original(transform=transform, ts=ts_with_local_levels)
+
+
+@pytest.mark.parametrize(
+    "transform, expected_length",
+    [
+        (ChangePointsLevelTransform(in_column="target"), 2),
+        (
+            ChangePointsLevelTransform(
+                in_column="target",
+                change_points_model=RupturesChangePointsModel(
+                    change_points_model=Binseg(model="ar"),
+                    n_bkps=5,
+                ),
+            ),
+            2,
+        ),
+        (
+            ChangePointsLevelTransform(
+                in_column="target",
+                change_points_model=RupturesChangePointsModel(
+                    change_points_model=Binseg(model="ar"),
+                    n_bkps=10,
+                ),
+            ),
+            0,
+        ),
+    ],
+)
+def test_params_to_tune(transform, expected_length, ts_with_local_levels):
+    ts = ts_with_local_levels
+    assert len(transform.params_to_tune()) == expected_length
+    assert_sampling_is_valid(transform=transform, ts=ts)
diff --git a/tests/test_transforms/test_decomposition/test_change_points_based/test_segmentation.py b/tests/test_transforms/test_decomposition/test_change_points_based/test_segmentation.py
index 9cda038ea..4f1045c46 100644
--- a/tests/test_transforms/test_decomposition/test_change_points_based/test_segmentation.py
+++ b/tests/test_transforms/test_decomposition/test_change_points_based/test_segmentation.py
@@ -11,6 +11,7 @@
 from etna.transforms import ChangePointsSegmentationTransform
 from etna.transforms.decomposition.change_points_based.change_points_models import RupturesChangePointsModel
 from etna.transforms.decomposition.change_points_based.segmentation import _OneSegmentChangePointsSegmentationTransform
+from tests.test_transforms.utils import assert_sampling_is_valid
 from tests.test_transforms.utils import assert_transformation_equals_loaded_original
 
 OUT_COLUMN = "result"
@@ -131,3 +132,35 @@ def test_save_load(simple_ar_ts):
         in_column="target", change_points_model=change_points_model, out_column=OUT_COLUMN
     )
     assert_transformation_equals_loaded_original(transform=transform, ts=simple_ar_ts)
+
+
+@pytest.mark.parametrize(
+    "transform, expected_length",
+    [
+        (ChangePointsSegmentationTransform(in_column="target"), 2),
+        (
+            ChangePointsSegmentationTransform(
+                in_column="target",
+                change_points_model=RupturesChangePointsModel(
+                    change_points_model=Binseg(model="ar"),
+                    n_bkps=5,
+                ),
+            ),
+            2,
+        ),
+        (
+            ChangePointsSegmentationTransform(
+                in_column="target",
+                change_points_model=RupturesChangePointsModel(
+                    change_points_model=Binseg(model="ar"),
+                    n_bkps=10,
+                ),
+            ),
+            0,
+        ),
+    ],
+)
+def test_params_to_tune(transform, expected_length, simple_ar_ts):
+    ts = simple_ar_ts
+    assert len(transform.params_to_tune()) == expected_length
+    assert_sampling_is_valid(transform=transform, ts=ts)
diff --git a/tests/test_transforms/test_decomposition/test_change_points_based/test_trend.py b/tests/test_transforms/test_decomposition/test_change_points_based/test_trend.py
index abd04d317..69576f4a1 100644
--- a/tests/test_transforms/test_decomposition/test_change_points_based/test_trend.py
+++ b/tests/test_transforms/test_decomposition/test_change_points_based/test_trend.py
@@ -11,6 +11,7 @@
 from etna.transforms.decomposition.change_points_based.change_points_models import RupturesChangePointsModel
 from etna.transforms.decomposition.change_points_based.per_interval_models import SklearnRegressionPerIntervalModel
 from etna.transforms.decomposition.change_points_based.trend import _OneSegmentTrendTransform
+from tests.test_transforms.utils import assert_sampling_is_valid
 from tests.test_transforms.utils import assert_transformation_equals_loaded_original
 
 DEFAULT_SEGMENT = "segment_1"
@@ -168,3 +169,35 @@ def test_save_load(example_tsds):
         change_points_model=RupturesChangePointsModel(change_points_model=Binseg(model="rbf"), n_bkps=5),
     )
     assert_transformation_equals_loaded_original(transform=transform, ts=example_tsds)
+
+
+@pytest.mark.parametrize(
+    "transform, expected_length",
+    [
+        (TrendTransform(in_column="target"), 2),
+        (
+            TrendTransform(
+                in_column="target",
+                change_points_model=RupturesChangePointsModel(
+                    change_points_model=Binseg(model="ar"),
+                    n_bkps=5,
+                ),
+            ),
+            2,
+        ),
+        (
+            TrendTransform(
+                in_column="target",
+                change_points_model=RupturesChangePointsModel(
+                    change_points_model=Binseg(model="ar"),
+                    n_bkps=10,
+                ),
+            ),
+            0,
+        ),
+    ],
+)
+def test_params_to_tune(transform, expected_length, example_tsds):
+    ts = example_tsds
+    assert len(transform.params_to_tune()) == expected_length
+    assert_sampling_is_valid(transform=transform, ts=ts)
diff --git a/tests/test_transforms/test_decomposition/test_detrend_transform.py b/tests/test_transforms/test_decomposition/test_detrend_transform.py
index 3c98d4397..91c8e866b 100644
--- a/tests/test_transforms/test_decomposition/test_detrend_transform.py
+++ b/tests/test_transforms/test_decomposition/test_detrend_transform.py
@@ -11,6 +11,7 @@
 from etna.transforms.decomposition import LinearTrendTransform
 from etna.transforms.decomposition import TheilSenTrendTransform
 from etna.transforms.decomposition.detrend import _OneSegmentLinearTrendBaseTransform
+from tests.test_transforms.utils import assert_sampling_is_valid
 from tests.test_transforms.utils import assert_transformation_equals_loaded_original
 
 DEFAULT_SEGMENT = "segment_1"
@@ -377,3 +378,13 @@ def test_fit_transform_with_nans(transformer, ts_with_nans, decimal):
 def test_save_load(transform, ts_two_segments_linear):
     ts = ts_two_segments_linear
     assert_transformation_equals_loaded_original(transform=transform, ts=ts)
+
+
+@pytest.mark.parametrize(
+    "transform",
+    [LinearTrendTransform(in_column="target"), TheilSenTrendTransform(in_column="target")],
+)
+def test_params_to_tune(transform, ts_two_segments_linear):
+    ts = ts_two_segments_linear
+    assert len(transform.params_to_tune()) > 0
+    assert_sampling_is_valid(transform=transform, ts=ts)
diff --git a/tests/test_transforms/test_decomposition/test_stl_transform.py b/tests/test_transforms/test_decomposition/test_stl_transform.py
index 325c5e16d..42ec48d06 100644
--- a/tests/test_transforms/test_decomposition/test_stl_transform.py
+++ b/tests/test_transforms/test_decomposition/test_stl_transform.py
@@ -6,6 +6,7 @@
 from etna.models import NaiveModel
 from etna.transforms.decomposition import STLTransform
 from etna.transforms.decomposition.stl import _OneSegmentSTLTransform
+from tests.test_transforms.utils import assert_sampling_is_valid
 from tests.test_transforms.utils import assert_transformation_equals_loaded_original
 
 
@@ -197,3 +198,10 @@ def test_fit_transform_with_nans_in_middle_raise_error(ts_with_nans):
 )
 def test_save_load(transform, ts_trend_seasonal):
     assert_transformation_equals_loaded_original(transform=transform, ts=ts_trend_seasonal)
+
+
+def test_params_to_tune(ts_trend_seasonal):
+    ts = ts_trend_seasonal
+    transform = STLTransform(in_column="target", period=7)
+    assert len(transform.params_to_tune()) > 0
+    assert_sampling_is_valid(transform=transform, ts=ts)