Skip to content

make learners picklable - #264

Merged
basnijholt merged 23 commits into
masterfrom
pickle
Apr 24, 2020
Merged

make learners picklable#264
basnijholt merged 23 commits into
masterfrom
pickle

Conversation

@basnijholt

@basnijholtbasnijholt commented Apr 9, 2020

Copy link
Copy Markdown
Member

Description

I realized that in some cases it's very useful to pickle learners. For example to send it over the network when parallelizing code.

With these changes, most learners become picklable.

Checklist

  • Fixed style issues using pre-commit run --all (first install using pip install pre-commit)
  • pytest passed

Type of change

Check relevant option(s).

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • This change requires a documentation update

@basnijholt
basnijholtforce-pushed the pickle branch 3 times, most recently from b2965d8 to cb2cb25CompareApril 9, 2020 21:32
@basnijholtbasnijholt changed the title WIP: make learners picklablemake learners picklableApr 9, 2020
@basnijholt

basnijholt commented Apr 10, 2020

Copy link
Copy Markdown
MemberAuthor

@akhmerov and @jbweston, I wonder if we should also pickle adaptive.__version__ and warn the user that if they are loading a learner that is pickled with another version, that there is no guarantee that stuff's correct?

let's do that in a future PR.

@codecov-io

codecov-io commented Apr 10, 2020

Copy link
Copy Markdown

Codecov Report

Merging #264 into master will increase coverage by 0.66%.
The diff coverage is 94.23%.

Impacted file tree graph

@@ Coverage Diff @@## master #264 +/- ##
==========================================
+ Coverage 79.52% 80.18% +0.66% 
==========================================
Files 32 33 +1 Lines 4425 4522 +97 Branches 815 819 +4 ==========================================
+ Hits 3519 3626 +107 + Misses 779 773 -6 + Partials 127 123 -4 
Impacted FilesCoverage Δ
adaptive/tests/test_pickling.py88.67% <88.67%> (ø)
adaptive/learner/average_learner.py83.72% <100.00%> (+4.97%)⬆️
adaptive/learner/balancing_learner.py75.00% <100.00%> (+1.22%)⬆️
adaptive/learner/data_saver.py90.00% <100.00%> (+1.76%)⬆️
adaptive/learner/integrator_learner.py91.37% <100.00%> (+3.42%)⬆️
adaptive/learner/learner1D.py92.81% <100.00%> (+0.48%)⬆️
adaptive/learner/learner2D.py79.28% <100.00%> (+1.26%)⬆️
adaptive/learner/sequence_learner.py85.52% <100.00%> (-0.19%)⬇️
... and 4 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update a0b22ff...acc5400. Read the comment docs.

@basnijholt
basnijholtforce-pushed the pickle branch 2 times, most recently from 817cda9 to 303efaaCompareApril 10, 2020 17:34

@akhmerovakhmerov left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I think tests should be more strict with idempotency. For example the loss reported by the new learner should be exactly the same, the learner's response to .ask should be exactly the same, etc.

@basnijholt

Copy link
Copy Markdown
MemberAuthor

@akhmerov, good idea, I've added that in 54017f3.

Comment threadadaptive/tests/test_pickling.py Outdated
@akhmerov

Copy link
Copy Markdown
Contributor

@basnijholt any idea why tests fail?

@basnijholt

Copy link
Copy Markdown
MemberAuthor

The failure is intermittent and unrelated to these changes, also happens in master sometimes.

It seems like the distributed test keeps getting stuck on Windows and MacOS.

@akhmerov

Copy link
Copy Markdown
Contributor

But it seems like serialization tests are failing?

image

@basnijholt

Copy link
Copy Markdown
MemberAuthor

Oh, you are right! I assumed it were the same failures I had seen before.

I think that is fixed by bc65213.

Comment threadadaptive/tests/test_pickling.py Outdated

@jbwestonjbweston left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Good work, and thanks for taking feedback on board.

The refactors I proposed are nice-to-haves, but I understand if we just want to get this merged.

OTOH we use an unseeded random number generator in such a way that I cannot see why the tests pass.

For me, explaining/fixing this is a prerequisite for merging.

Comment threadadaptive/tests/test_pickling.py Outdated
Comment threadadaptive/tests/test_pickling.py Outdated
Comment threadadaptive/tests/test_pickling.py Outdated
Comment threadadaptive/tests/test_pickling.py Outdated
Comment threadadaptive/tests/test_pickling.py Outdated
Comment threadadaptive/tests/test_pickling.py Outdated
Comment threadadaptive/tests/test_pickling.py Outdated
Comment threadadaptive/learner/learnerND.py Outdated
Comment threadadaptive/learner/learnerND.py Outdated
@tlaeven

tlaeven commented Apr 23, 2020

Copy link
Copy Markdown

Using a cached function doesn't work in this branch, is this intentional?

Minimal example:

importadaptivefromfunctoolsimportlru_cacheadaptive.notebook_extension()
@lru_cache()defg(x):
returnx**2deff(x):
returnx-g(0)
learner=adaptive.Learner1D(f, [-1,1])
runner=adaptive.Runner(learner)
runner.live_info()

Raises an error:
loky.process_executor.BrokenProcessPool: A task has failed to un-serialize. Please ensure that the arguments of the function are all picklable.

Note

It does work when the cached function is imported from a module:
image

@basnijholt

basnijholt commented Apr 23, 2020

Copy link
Copy Markdown
MemberAuthor

@tlaeven, this is actually unrelated to these changes. The code you posted never worked, and has nothing to do with Adaptive:

fromfunctoolsimportlru_cacheimportlokyfromconcurrent.futuresimportProcessPoolExecutor@lru_cache()defg(x):
returnx**2deff(x):
returnx-g(0)
# BeforewithProcessPoolExecutor() asex:
fut=ex.submit(f, 0)
try:
fut.result()
exceptExceptionase:
print(f"ProcessPoolExecutor failed: {e}")
# Nowex=loky.get_reusable_executor()
fut=ex.submit(f, 0)
try:
fut.result()
exceptExceptionase:
print(f"loky failed: {e}")

which prints:

ProcessPoolExecutor failed: A process in the process pool was terminated abruptly while the future was running or pending.
loky failed: A task has failed to un-serialize. Please ensure that the arguments of the function are all picklable.

I guess the difference is because of basnijholt/adaptive-scheduler#39.

@basnijholt

Copy link
Copy Markdown
MemberAuthor

@jbweston, I've fixed all that was brought up.

The LearnerND takes some more work, so I moved those commits to #272.

I think it's ready to merge. Would you take a final look?

@jbwestonjbweston left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM! Nice work.

@basnijholt
basnijholt merged commit de0cc0c into masterApr 24, 2020
@basnijholt
basnijholt deleted the pickle branch April 24, 2020 16:04
@basnijholtbasnijholt mentioned this pull request May 19, 2020
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants

@basnijholt@codecov-io@akhmerov@tlaeven@jbweston