-
-
Notifications
You must be signed in to change notification settings - Fork 710
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add Joblib frontend #211
Add Joblib frontend #211
Conversation
@ogrisel it appears that This appears when I do something like the following: with parallel_backend('distributed', ...):
result = Parallel()(delayed(func)(arg) for arg in seq)
result2 = Parallel()(delayed(func)(arg) for arg in seq2) |
5c7091a
to
3926d15
Compare
OK, I realize now that terminate is called after every call to @contextmanager
def parallel_backend(backend, n_jobs=-1, **backend_params):
if isinstance(backend, _basestring):
backend = BACKENDS[backend](**backend_params)
old_backend_and_jobs = getattr(_backend, 'backend_and_jobs', None)
try:
_backend.backend_and_jobs = (backend, n_jobs)
yield
finally:
# --WANT TO CALL CLEANUP CODE HERE--
if old_backend_and_jobs is None:
if getattr(_backend, 'backend_and_jobs', None) is not None:
del _backend.backend_and_jobs
else:
_backend.backend_and_jobs = old_backend_and_jobs However for the time being I don't particularly need this. The executor should clean itself up on garbage collection in the common case. |
OK, this seems fine to me as a first step. @ogrisel I've stolen your example from https://github.com/ogrisel/docker-distributed/blob/fec913938a67902a6477bb367dd76433babc3b47/examples/sklearn_parameter_search_joblib.ipynb and put it into the documentation. I've also cleaned up a couple things in the web UI to improve diagnostic visualization (I see from your commit history that you may have been playing with this.) Some thoughts for the future: It would be good to benchmark real applications using this. I suspect that we're doing some things inefficiently. In particular I'm curious about the following:
|
@ogrisel is there a reason why this might fail on Python 2.7?
|
Ah, nevermind. I suspect an |
|
||
def apply_async(self, func, *args, **kwargs): | ||
callback = kwargs.pop('callback', None) | ||
kwargs['pure'] = False |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe this could be passed as an additional pure=False
argument to DistributedBackend.__init__
to make it possible to enable pure function caching explicitly?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This will only become useful if we have a way to intelligently hash the batched-function-call objects. I think there are some more things that need to happen before this happens.
- We should probably remove the
AutoBatchingMixin
here - Joblib should send straight functions and args, rather than a wrapped object
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Indeed, let's keep pure=False
for now.
OK, merging for now. |
BTW, I think your comment in #211 (comment) highlights a real bug in how joblib currently manages the backend. I opened: joblib/joblib#335 to track this (not a big deal for distributed as the GC should do the right thing anyway). |
@mrocklin sorry for not having replied to your previous comments.
Yes I agree the closure-based syntax is making things complex. We could introduce a
It's hard to tell in general. AutoBatching in joblib was primarily implemented as a protection for naive users that would enable (multiprocessing-based) parallelism on a very large number of very short tasks and would see their program run much much slower than in sequential mode. For multiprocessnig, autobatching makes joblib parallel never be slower than sequential mode in practice. But it's not a magic bullet and won't guaranty linear scalability either. I don't know how useful it can be with the distributed backend.
You can call cross-validation of a with parallel_backend('distributed', scheduler='localhost:8786'):
model = RandomizedSeachCV(model, params, n_iter=30, cv=5)
scores = cross_val_score(model, X, y, cv=5)
print("CV score: %0.3f +/- %0.3f" % (np.mean(scores), np.std(scores)))
Yes ideally this is what we want but the scikit-learn API is really not helping us in that case. Note that joblib has a call_and_shelve feature that could be rendered plug-able at some point put scikit-learn does not use it at this point. |
Fixes #124 cc @ogrisel
It looks like there are issues with cleanly shutting down the executor. I'll look at these in a bit. This also needs docs.
Test cases to ensure proper Joblib operation are welcome.