-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Add the tensorboard.summary
public entry point
#562
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
Conversation
Nice. Could we note in plugin docs to update |
"note in plugin docs"—what plugin docs? We don't have a readme for It should be a rare thing for a summary to be added to TensorBoard core, and it may well be the case that we want to offer a plugin in |
Looks nice. Could the test verify that the _pb method is present too? Maybe with an exclusions set for those summaries that don't have a _pb method (although that should tend towards the empty set). |
Sure. @chihuahua what's the status of writing |
f1f0712
to
c316b15
Compare
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.
LGTM. Please wait for Dandelion to approve.
self.assertIsInstance(getattr(summary, plugin), collections.Callable) | ||
|
||
def test_plugins_export_pb_functions(self): | ||
for plugin in STANDARD_PLUGINS: |
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.
Our unit test best practices guide actually recommends against using things like for loops in unit tests. One notable exception is the Go language. But generally speaking, folks at Google err on the side of just having a lot of boilerplate to test each thing, so when the test runner tells you which method failed, you know the plugin for sure. You also wouldn't need to have special case logic for the pr_curve plugin.
But this is fine. I don't feel too strongly about it.
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 never really understood that logic. I agree that the burden of being "clearly correct" is stronger for test code than main code, but this loop is clearly correct. (It looks like we have 130 for-loops in test code currently.) (Plus, why should the Go language be any different?)
I thought about setting a custom message, but the case in which this will fail will almost always be the getattr
failing, not that the user exports something that is not a function, and in that case the name of the plugin that failed will appear in the error message. (It's very frustrating that a custom message replaces the default message instead of appending to it, or I'd use custom messages much more often.)
scalar_pb = _scalar_summary.pb | ||
|
||
text = _text_summary.op | ||
text_pb = _text_summary.pb |
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 is a fantastic design. I like it a lot. You also followed good best practices for delegate imports.
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.
Cool; thanks. I know that you had some reservations about doing this carefully, so I'm glad that you approve.
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.
Out of curiosity…do you have a reference for these best practices for delegate imports? I just did what felt natural here, but I'd be interested to see what other people think.
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.
Will await Dandelion's approval. Thanks for reviewing!
self.assertIsInstance(getattr(summary, plugin), collections.Callable) | ||
|
||
def test_plugins_export_pb_functions(self): | ||
for plugin in STANDARD_PLUGINS: |
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 never really understood that logic. I agree that the burden of being "clearly correct" is stronger for test code than main code, but this loop is clearly correct. (It looks like we have 130 for-loops in test code currently.) (Plus, why should the Go language be any different?)
I thought about setting a custom message, but the case in which this will fail will almost always be the getattr
failing, not that the user exports something that is not a function, and in that case the name of the plugin that failed will appear in the error message. (It's very frustrating that a custom message replaces the default message instead of appending to it, or I'd use custom messages much more often.)
scalar_pb = _scalar_summary.pb | ||
|
||
text = _text_summary.op | ||
text_pb = _text_summary.pb |
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.
Cool; thanks. I know that you had some reservations about doing this carefully, so I'm glad that you approve.
Here's how the best practices guide justifies it:
So it seems like you were correct in the beginning, until the pr_curve plugin broke your assumption and extra logic had to be added. |
Go gets to be special because it's Go. Without exceptions or macros, it's probably not possible to write unit tests the normal way. So they'll usually just have some complicated function that loops through an array of test vector objects. |
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.
Looks good, modulo one small nit :) Thanks William!
tensorboard/summary_test.py
Outdated
# graph. This set should ideally be empty; any entries here should be | ||
# considered temporary. | ||
PLUGINS_WITHOUT_PB_FUNCTIONS = frozenset([ | ||
'pr_curve', # TODO(@chihuahua): Fix 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.
Nit: Instead of making this a raw TODO, can we link it to a GitHub issue?
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.
Sure.
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.
Added reference to #445, which already exists and is assigned to @chihuahua . Will merge once Travis finishes.
c316b15
to
4808b57
Compare
Summary: Resolves #561. Test Plan: Ran the `build_pip_package.sh` script to build the Python 2 package, then created a new Python 2 virtualenv in which I installed only the latest nightly TensorFlow release (2017-09-22) and the generated wheel. Then wrote ```py import tensorflow as tf from tensorboard import summary result = tf.Session().run(summary.scalar('shelby', tf.constant(3))) s = tf.Summary() s.ParseFromString(result) print(s) ``` and verified that the result was a proper scalar summary. I then repeated the process using `pip install tensorflow==1.3.0` instead of the nightly version to ensure that the module works for normal TensorFlow users. (Note: the audio summary will not work because TensorFlow 1.3 lacks 22730fd4c633a74e59c03ff76dc92e6ae2d5d020. This is fine; users can continue to use the old API for that one.) wchargin-branch: summary-module
4808b57
to
4db1c24
Compare
Summary: Resolves tensorflow#561. Test Plan: Ran the `build_pip_package.sh` script to build the Python 2 package, then created a new Python 2 virtualenv in which I installed only the latest nightly TensorFlow release (2017-09-22) and the generated wheel. Then wrote ```py import tensorflow as tf from tensorboard import summary result = tf.Session().run(summary.scalar('shelby', tf.constant(3))) s = tf.Summary() s.ParseFromString(result) print(s) ``` and verified that the result was a proper scalar summary. I then repeated the process using `pip install tensorflow==1.3.0` instead of the nightly version to ensure that the module works for normal TensorFlow users. (Note: the audio summary will not work because TensorFlow 1.3 lacks 22730fd4c633a74e59c03ff76dc92e6ae2d5d020. This is fine; users can continue to use the old API for that one.) wchargin-branch: summary-module
Summary: Resolves #561. Test Plan: Ran the `build_pip_package.sh` script to build the Python 2 package, then created a new Python 2 virtualenv in which I installed only the latest nightly TensorFlow release (2017-09-22) and the generated wheel. Then wrote ```py import tensorflow as tf from tensorboard import summary result = tf.Session().run(summary.scalar('shelby', tf.constant(3))) s = tf.Summary() s.ParseFromString(result) print(s) ``` and verified that the result was a proper scalar summary. I then repeated the process using `pip install tensorflow==1.3.0` instead of the nightly version to ensure that the module works for normal TensorFlow users. (Note: the audio summary will not work because TensorFlow 1.3 lacks 22730fd4c633a74e59c03ff76dc92e6ae2d5d020. This is fine; users can continue to use the old API for that one.) wchargin-branch: summary-module
Summary:
Resolves #561.
Test Plan:
Ran the
build_pip_package.sh
script to build the Python 2 package,then created a new Python 2 virtualenv in which I installed only the
latest nightly TensorFlow release (2017-09-22) and the generated wheel.
Then wrote
and verified that the result was a proper scalar summary.
I then repeated the process using
pip install tensorflow==1.3.0
instead of the nightly version to ensure that the module works for
normal TensorFlow users. (Note: the audio summary will not work because
TensorFlow 1.3 lacks 22730fd4c633a74e59c03ff76dc92e6ae2d5d020. This is
fine; users can continue to use the old API for that one.)
wchargin-branch: summary-module