Skip to content

Commit 6d13a75

Browse files
committed
model is paralizable
1 parent a5cb405 commit 6d13a75

4 files changed

Lines changed: 52 additions & 8 deletions

File tree

openml/flows/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from .flow import OpenMLFlow
2-
from .sklearn_converter import sklearn_to_flow, flow_to_sklearn
2+
from .sklearn_converter import sklearn_to_flow, flow_to_sklearn, model_is_paralizable
33
from .functions import get_flow, list_flows, flow_exists
44

55
__all__ = ['OpenMLFlow', 'create_flow_from_model', 'get_flow', 'list_flows',
6-
'sklearn_to_flow', 'flow_to_sklearn', 'flow_exists']
6+
'sklearn_to_flow', 'flow_to_sklearn', 'flow_exists', 'model_is_paralizable']

openml/flows/sklearn_converter.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -536,6 +536,33 @@ def _serialize_cross_validator(o):
536536

537537
return ret
538538

539+
def model_is_paralizable(model):
540+
def check(param_dict):
541+
for param, value in param_dict.items():
542+
# n_jobs is scikitlearn parameter for paralizing jobs
543+
if 'n_jobs' in param.split('__')[-1]:
544+
# 0 = illegal value (?), 1 = use one core, n = use n cores
545+
# -1 = use all available cores -> this makes it hard to
546+
# measure runtime in a fair way
547+
if value != 1:
548+
return False
549+
return True
550+
551+
if not (isinstance(model, sklearn.base.BaseEstimator) or
552+
isinstance(model, sklearn.model_selection._search.BaseSearchCV)):
553+
raise ValueError('model should be BaseEstimator or BaseSearchCV')
554+
555+
# check the parameters for n_jobs
556+
if check(model.get_params()) == False:
557+
return False
558+
559+
# check if the njobs is not in the optimization trace
560+
# this would be error by the user, so we can throw it as a courtesy
561+
if isinstance(model, sklearn.model_selection._search.BaseSearchCV):
562+
if check(model.get_params()) == False:
563+
raise PyOpenMLError('openml-python should not be used to '
564+
'optimize the n_jobs parameter.')
565+
return True
539566

540567
def _deserialize_cross_validator(value, **kwargs):
541568
model_name = value['name']

openml/runs/functions.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
from ..exceptions import PyOpenMLError
1212
from .. import config
13-
from ..flows import sklearn_to_flow, get_flow, flow_exists
13+
from ..flows import sklearn_to_flow, get_flow, flow_exists, model_is_paralizable
1414
from ..setups import setup_exists
1515
from ..exceptions import OpenMLCacheException, OpenMLServerException
1616
from ..util import URLError, version_complies
@@ -160,6 +160,7 @@ def _run_task_get_arffcontent(model, task, class_labels):
160160
user_defined_measures = defaultdict(lambda: defaultdict(dict))
161161

162162
rep_no = 0
163+
can_measure_runtime = version_complies(3, 3) and model_is_paralizable(model)
163164
# TODO use different iterator to only provide a single iterator (less
164165
# methods, less maintenance, less confusion)
165166
for rep in task.iterate_repeats():
@@ -174,11 +175,11 @@ def _run_task_get_arffcontent(model, task, class_labels):
174175

175176
try:
176177
# for measuring runtime. Only available since Python 3.3
177-
if version_complies(3, 3):
178+
if can_measure_runtime:
178179
modelfit_starttime = time.process_time()
179180
model_fold.fit(trainX, trainY)
180181

181-
if version_complies(3, 3):
182+
if can_measure_runtime:
182183
modelfit_duration = (time.process_time() - modelfit_starttime) * 1000
183184
user_defined_measures['usercpu_time_millis_training'][rep_no][fold_no] = modelfit_duration
184185
except AttributeError as e:
@@ -192,12 +193,12 @@ def _run_task_get_arffcontent(model, task, class_labels):
192193
else:
193194
model_classes = model_fold.classes_
194195

195-
if version_complies(3, 3):
196+
if can_measure_runtime:
196197
modelpredict_starttime = time.process_time()
197198

198199
ProbaY = model_fold.predict_proba(testX)
199200
PredY = model_fold.predict(testX)
200-
if version_complies(3, 3):
201+
if can_measure_runtime:
201202
modelpredict_duration = (time.process_time() - modelpredict_starttime) * 1000
202203
user_defined_measures['usercpu_time_millis_testing'][rep_no][fold_no] = modelpredict_duration
203204
user_defined_measures['usercpu_time_millis'][rep_no][fold_no] = modelfit_duration + modelpredict_duration

tests/test_flows/test_sklearn.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@
2525
import sklearn.tree
2626

2727
from openml.flows import OpenMLFlow, sklearn_to_flow, flow_to_sklearn
28-
from openml.flows.sklearn_converter import _format_external_version, _check_dependencies
28+
from openml.flows.sklearn_converter import _format_external_version, \
29+
_check_dependencies, model_is_paralizable
2930
from openml.exceptions import PyOpenMLError
3031

3132
this_directory = os.path.dirname(os.path.abspath(__file__))
@@ -555,3 +556,18 @@ def test_illegal_parameter_names_featureunion(self):
555556
('OneHotEncoder', sklearn.preprocessing.OneHotEncoder(sparse=False, handle_unknown='ignore'))
556557
]
557558
self.assertRaises(ValueError, sklearn.pipeline.FeatureUnion, transformer_list=transformer_list)
559+
560+
def test_paralizable_check(self):
561+
models = [
562+
sklearn.ensemble.RandomForestClassifier(),
563+
sklearn.ensemble.RandomForestClassifier(n_jobs=5),
564+
sklearn.ensemble.RandomForestClassifier(n_jobs=-1),
565+
sklearn.pipeline.Pipeline(steps=[('bag', sklearn.ensemble.BaggingClassifier(n_jobs=1))]),
566+
sklearn.pipeline.Pipeline(steps=[('bag', sklearn.ensemble.BaggingClassifier(n_jobs=5))]),
567+
sklearn.pipeline.Pipeline(steps=[('bag', sklearn.ensemble.BaggingClassifier(n_jobs=-1))])
568+
]
569+
570+
answers = [True, False, False, True, False, False]
571+
572+
for i in range(len(models)):
573+
assert(model_is_paralizable(models[i]) == answers[i])

0 commit comments

Comments
 (0)