Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
4e172f3
Added TabPFN implementation
martilut May 14, 2025
b79c8d1
Added TabPFN Strategy
martilut May 14, 2025
42c6180
Added TabPFN default parameters
martilut May 14, 2025
d7e62b5
Added TabPFN search space
martilut May 14, 2025
0fe2e69
Added TabPFN in model repository
martilut May 14, 2025
f046413
Added TabPFN in requirements.txt
martilut May 14, 2025
d448bb9
Fixed parameters in TabPFN Regression
martilut May 14, 2025
6a23dcd
Added TabPFN into models list in automation_features.rst
martilut May 14, 2025
ca181af
Added TabPFN as exception in test on constant data
martilut May 14, 2025
7525d03
Added tests for TabPFN classification and regression strategies
martilut May 14, 2025
63c5ce3
Fixed default parameters for TabPFN classification and regression
martilut May 16, 2025
fe89d46
Automated autopep8 fixes
github-actions[bot] May 16, 2025
75054b2
Fixed TabPFN version
martilut May 16, 2025
a3fad43
Merge branch 'tabpfn-impl' of https://github.com/aimclub/FEDOT into t…
martilut May 16, 2025
245e9b4
Added AutoTabPFN implementation
martilut May 20, 2025
83d6608
Added GPU support and fixed prediction output format
martilut May 20, 2025
51af4e9
Fixed tests for TabPFN
martilut May 20, 2025
e1f29d2
Added features size check
martilut Jun 19, 2025
bfd8a08
Added non-default ans cpu tags for TabPFN
martilut Jul 16, 2025
921eae3
Updated max_samples parameter
martilut Jul 16, 2025
e179182
Updated all max_samples parameters
martilut Jul 16, 2025
0d0e389
Added TabPFN into GPU preset
martilut Jul 16, 2025
49f1877
Updated tests
martilut Jul 16, 2025
360758e
Automated autopep8 fixes
github-actions[bot] Jul 21, 2025
d41aff1
Remove 3.8 from builds
martilut Aug 4, 2025
f984f63
Optimize imports
martilut Aug 4, 2025
811c617
Added TabPFN path in FEDOT dir
martilut Aug 4, 2025
b4f0fe0
Change Python version from 3.8 to 3.9 in publish_pypi.yml
martilut Aug 4, 2025
120341f
Change Python version from 3.8 to 3.9 in .readthedocs.yml
martilut Aug 4, 2025
061378f
Replace tabpfn-requirements to requirements[extra]
martilut Aug 4, 2025
f052e9e
Change sphinx version to match Python 3.9
martilut Aug 4, 2025
e62b5b2
Change sphinx version to 5.0.0
martilut Aug 4, 2025
254b8f6
Rollback to init sphinx version
martilut Aug 4, 2025
5c67de9
Change sphinx version to 5.3.0
martilut Aug 4, 2025
68d1ad3
Fix model_path assignment
martilut Aug 5, 2025
1b89dae
Remove Python 3.8 from .travis.yml
martilut Aug 5, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/integration-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ jobs:
timeout-minutes: 95
strategy:
matrix:
python-version: [ 3.8, 3.9, '3.10' ]
python-version: [ 3.9, '3.10' ]

steps:
- name: Checkout branch
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/publish_pypi.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: [ 3.8 ]
python-version: [ 3.9 ]

steps:
- uses: actions/checkout@v2
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/unit-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ jobs:
timeout-minutes: 15
strategy:
matrix:
python-version: [ 3.8, 3.9, '3.10' ]
python-version: [ 3.9, '3.10' ]

steps:
- uses: actions/checkout@v2
Expand Down
2 changes: 1 addition & 1 deletion .readthedocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ version: 2
build:
os: ubuntu-22.04
tools:
python: "3.8"
python: "3.9"

# Build documentation in the docs/ directory with Sphinx
sphinx:
Expand Down
1 change: 0 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
language: python

python:
- "3.8"
- "3.9"
- "3.10"

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,10 @@ Apart from that there are other options whose names speak for themselves: ``'sta
`polyfit`,Polynomial approximation,Forecasting
`stl_arima`,STL Decomposition with ARIMA,Forecasting
`ts_naive_average`,Naive Average,Forecasting
`tabpfn`,TabPFN classifier,Classification
`tabpfnreg`,TabPFN regressor,Regression,
`autotabpfn`,AutoTabPFN classifier,Classification
`autotabpfnreg`,AutoTabPFN regressor,Regression,


.. csv-table:: Available models implementations
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import os

import numpy as np
from tabpfn import TabPFNClassifier, TabPFNRegressor
from tabpfn_extensions.post_hoc_ensembles.sklearn_interface import AutoTabPFNClassifier, AutoTabPFNRegressor
from typing import Optional
from fedot.core.data.data import InputData, OutputData
from fedot.core.operations.evaluation.operation_implementations.implementation_interfaces import ModelImplementation
from fedot.core.operations.operation_parameters import OperationParameters
from fedot.core.utils import default_fedot_data_dir


class FedotTabPFNImplementation(ModelImplementation):
__operation_params = [
'enable_categorical',
'max_samples',
'max_features',
'model_path'
]

def __init__(self, params: Optional[OperationParameters] = None):
super().__init__(params)

self.model_params = {
k: v for k, v in self.params.to_dict().items() if k not in self.__operation_params
}

model_path = self.params.get('model_path', None)
Copy link
Copy Markdown
Collaborator

@dmitryglhf dmitryglhf Aug 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: А где используется этот параметр?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

model_path передается в model_params, а затем попадает в инициализацию класса TabPFN. В новом коммите пофиксила, чтобы передавался путь к папке кэша, если model_path = "auto"

if model_path == "auto":
self.model_params['model_path'] = os.path.join(default_fedot_data_dir(), 'tabpfn')
elif model_path is not None:
self.model_params['model_path'] = model_path

self.model = None
self.classes_ = None

def fit(self, input_data: InputData):
self.model.categorical_features_indices = input_data.categorical_idx

if self.params.get('enable_categorical'):
input_data = input_data.get_not_encoded_data()

self.model.fit(X=input_data.features, y=input_data.target)

return self.model

def predict(self, input_data: InputData) -> OutputData:
if self.params.get('enable_categorical'):
input_data = input_data.get_not_encoded_data()

prediction = self.model.predict(input_data.features)

output_data = self._convert_to_output(
input_data=input_data,
predict=prediction
)
return output_data

def predict_proba(self, input_data: InputData):
if self.params.get('enable_categorical'):
input_data = input_data.get_not_encoded_data()

prediction = self.model.predict_proba(input_data.features)
output_data = self._convert_to_output(
input_data=input_data,
predict=prediction
)
return output_data


class FedotTabPFNClassificationImplementation(FedotTabPFNImplementation):
def __init__(self, params: Optional[OperationParameters] = None):
super().__init__(params)
self.model = TabPFNClassifier(**self.model_params)

def fit(self, input_data: InputData):
self.classes_ = np.unique(np.array(input_data.target))
return super().fit(input_data=input_data)


class FedotTabPFNRegressionImplementation(FedotTabPFNImplementation):
def __init__(self, params: Optional[OperationParameters] = None):
super().__init__(params)
self.model = TabPFNRegressor(**self.model_params)


class FedotAutoTabPFNClassificationImplementation(FedotTabPFNImplementation):
def __init__(self, params: Optional[OperationParameters] = None):
super().__init__(params)
self.model = AutoTabPFNClassifier(**self.model_params)

def fit(self, input_data: InputData):
self.classes_ = np.unique(np.array(input_data.target))
return super().fit(input_data=input_data)


class FedotAutoTabPFNRegressionImplementation(FedotTabPFNImplementation):
def __init__(self, params: Optional[OperationParameters] = None):
super().__init__(params)
self.model = AutoTabPFNRegressor(**self.model_params)
94 changes: 94 additions & 0 deletions fedot/core/operations/evaluation/tabpfn.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
from typing import Optional

from fedot.core.data.data import InputData, OutputData
from fedot.core.operations.evaluation.evaluation_interfaces import EvaluationStrategy
from fedot.core.operations.evaluation.operation_implementations.models.tabpfn import \
FedotTabPFNClassificationImplementation, FedotTabPFNRegressionImplementation, \
FedotAutoTabPFNClassificationImplementation, FedotAutoTabPFNRegressionImplementation
from fedot.core.operations.operation_parameters import OperationParameters
from fedot.core.repository.tasks import TaskTypesEnum
from fedot.utilities.random import ImplementationRandomStateHandler


class TabPFNStrategy(EvaluationStrategy):
_operations_by_types = {
'tabpfn': FedotTabPFNClassificationImplementation,
'tabpfnreg': FedotTabPFNRegressionImplementation,
'autotabpfn': FedotAutoTabPFNClassificationImplementation,
'autotabpfnreg': FedotAutoTabPFNRegressionImplementation,
}

def __init__(self, operation_type: str, params: Optional[OperationParameters] = None):
self.operation_impl = self._convert_to_operation(operation_type)
super().__init__(operation_type, params)
self.device = params.get('device', 'auto')
self.max_samples = params.get('max_samples', 1000)
self.max_features = params.get('max_features', 500)

def fit(self, train_data: InputData):
check_data_size(
data=train_data,
device=self.device,
max_samples=self.max_samples,
max_features=self.max_features,
)
if train_data.task.task_type == TaskTypesEnum.ts_forecasting:
raise ValueError('Time series forecasting not supported for TabPFN')

operation_implementation = self.operation_impl(self.params_for_fit)

with ImplementationRandomStateHandler(implementation=operation_implementation):
operation_implementation.fit(train_data)

return operation_implementation

def predict(self, trained_operation, predict_data: InputData) -> OutputData:
raise NotImplementedError()


class TabPFNClassificationStrategy(TabPFNStrategy):
def __init__(self, operation_type: str, params: Optional[OperationParameters] = None):
super().__init__(operation_type, params)

def predict(self, trained_operation, predict_data: InputData) -> OutputData:
if self.output_mode == 'labels':
output = trained_operation.predict(predict_data)
elif self.output_mode in ['probs', 'full_probs', 'default']:
n_classes = len(trained_operation.classes_)
output = trained_operation.predict_proba(predict_data)
if n_classes < 2:
raise ValueError('Data set contain only 1 target class. Please reformat your data.')
elif (n_classes == 2 and self.output_mode != 'full_probs'
and len(output.predict.shape) > 1):
output.predict = output.predict[:, 1]
else:
raise ValueError(f'Output model {self.output_mode} is not supported')

return output


class TabPFNRegressionStrategy(TabPFNStrategy):
def __init__(self, operation_type: str, params: Optional[OperationParameters] = None):
super().__init__(operation_type, params)

def predict(self, trained_operation, predict_data: InputData) -> OutputData:
return trained_operation.predict(predict_data)


def check_data_size(
data: InputData,
device: str = "auto",
max_samples: int = 1000,
max_features: int = 500,
) -> bool:
if data.features.shape[0] > max_samples:
raise ValueError(
f"Input data has too many samples ({data.features.shape[0]}), "
f"maximum is {max_samples} for device '{device}'"
)
if data.features.shape[1] > max_features:
raise ValueError(
f"Input data has too many features ({data.features.shape[1]}), "
f"maximum is {max_features}"
)
return True
39 changes: 39 additions & 0 deletions fedot/core/pipelines/tuning/search_space.py
Original file line number Diff line number Diff line change
Expand Up @@ -930,6 +930,45 @@ def get_parameters_dict(self):
'hyperopt-dist': hp.choice,
'sampling-scope': [['euclidean', 'manhattan', 'cosine']],
'type': 'categorical'}
},
'tabpfn': {
'n_estimators': {
'hyperopt-dist': hp.uniformint,
'sampling-scope': [1, 10],
'type': 'discrete'
},
'softmax_temperature': {
'hyperopt-dist': hp.uniform,
'sampling-scope': [0.0, 1.0],
'type': 'continuous'
},
'balance_probabilities': {
'hyperopt-dist': hp.choice,
'sampling-scope': [[True, False]],
'type': 'categorical'
},
'average_before_softmax': {
'hyperopt-dist': hp.choice,
'sampling-scope': [[True, False]],
'type': 'categorical'
},
},
'tabpfnreg': {
'n_estimators': {
'hyperopt-dist': hp.uniformint,
'sampling-scope': [1, 10],
'type': 'discrete'
},
'softmax_temperature': {
'hyperopt-dist': hp.uniform,
'sampling-scope': [0.0, 1.0],
'type': 'continuous'
},
'average_before_softmax': {
'hyperopt-dist': hp.choice,
'sampling-scope': [[True, False]],
'type': 'categorical'
},
}
}

Expand Down
Loading