-
-
Notifications
You must be signed in to change notification settings - Fork 1k
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
[ENH] Neural network widget that works in a separate thread #2958
Conversation
The scikit-learn class in the background does not support callbacks, so I made its verbose output the callback.
@ales-erjavec Could you check this PR and comment, please? Please, also see the changes to the widget's cancel(). There I needed to process events so that the progress bars updates worked OK when I used the cancel button. |
if learner.kwargs["solver"] != "lbfgs": | ||
# enable verbose printouts within scikit and redirect them | ||
with patch.dict(learner.kwargs, {"verbose": True}),\ | ||
patch("builtins.print", print_callback): |
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.
Time -->
Thread-1
-|patch|-----|exit|--------
Thread-2
----|patch|--------|exit|--
^ ^
stores restores
patched print the patched print of Thread-1
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.
Whoops. Thank you. Did not know that patch mutates state globally....
The alternative to your last commit would be to set |
@kernc Yes, that would certainly be an option. But then I find automatic stopping due to too tiny updates problematic: we would have to detect it and avoid running partial_fit again. |
What about learning rate schedule? From what I can tell the 'adam' solver uses learning rate decay, so any model trained in such manner could be very different then one trained in a single run. |
MLPClassifier n_iters_ was made a property which calls a callback.
Codecov Report
@@ Coverage Diff @@
## master #2958 +/- ##
==========================================
+ Coverage 81.86% 81.88% +0.02%
==========================================
Files 329 329
Lines 56785 56905 +120
==========================================
+ Hits 46486 46597 +111
- Misses 10299 10308 +9 |
@ales-erjavec, what do you think about this attempt? |
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.
I do not particularly like the idea of raising exceptions from n_iter_ setter.
|
||
def callback(iteration): | ||
if task.cancelled: | ||
raise CancelThreadException() # this stop the thread |
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.
The comment (and exception name) are not correct. This does not stop the thread. It cancels/interrupts the task.
self._task = None | ||
# threads use signals to run functions in the main thread and some | ||
# can still be quoued (perhaps change) | ||
qApp.processEvents() |
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.
Remove this. If needed route the progress updates via an intermediary QObject maybe like this
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.
Thanks! I haven't thought of this.
I do not like faking n_iter_ either, but haven't thought of anything better here... Feel free to decide that this is too dirty to get into Orange. |
return learner(data) | ||
try: | ||
return learner(data) | ||
except CancelTaskException: |
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.
What is the difference between former (no try-except) and this?
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.
The later does not capture the exception instance's __traceback__
in the resulting Future (which we know will be discarded).
Issue
In general, long computation shouldn't leave the GUI unresponsive. But the real issue is that I am not sure I can write threaded widgets, so I tried.
Description of changes
Implemented with the help of @ales-erjavec's example from the documentation with minor changes. Callbacks with scikit-learn are very hackish though. Does anybody have a better idea?
Just to test stopping, I also added a cancel button to the widget. Perhaps all interruptible widget should get a stop icon in the status bar and perhaps also some actionable "icon" on the canvas.
Includes