From 0ea0bf5aae2961be4edbe00c205bed01d293dce3 Mon Sep 17 00:00:00 2001 From: "A. Unique TensorFlower" Date: Tue, 6 Jun 2017 16:41:24 -0700 Subject: [PATCH] Add a frontend for viewing the first ops that exhibit bad values (NaN, +/- Inf). This helps the user identify problematic ops. Also moved the debugger data logic within tf-graph-info into a new tf-graph-debugger-data-card component. PiperOrigin-RevId: 158208679 --- tensorflow/BUILD | 2 + .../tf_graph_board/tf-graph-board.html | 20 +- .../components/tf_graph_common/render.ts | 40 ++ .../components/tf_graph_common/util.ts | 25 + .../tf-graph-dashboard.html | 33 +- .../tf_graph_debugger_data_card/BUILD | 44 ++ .../tf_graph_debugger_data_card/demo/BUILD | 24 + .../demo/index.html | 36 ++ .../tf-graph-debugger-data-card.html | 560 ++++++++++++++++++ .../components/tf_graph_info/BUILD | 2 + .../tf_graph_info/tf-graph-info.html | 254 +------- 11 files changed, 789 insertions(+), 251 deletions(-) create mode 100644 tensorflow/tensorboard/components/tf_graph_debugger_data_card/BUILD create mode 100644 tensorflow/tensorboard/components/tf_graph_debugger_data_card/demo/BUILD create mode 100644 tensorflow/tensorboard/components/tf_graph_debugger_data_card/demo/index.html create mode 100644 tensorflow/tensorboard/components/tf_graph_debugger_data_card/tf-graph-debugger-data-card.html diff --git a/tensorflow/BUILD b/tensorflow/BUILD index 42e5b921503..8b6224e1654 100644 --- a/tensorflow/BUILD +++ b/tensorflow/BUILD @@ -356,6 +356,8 @@ filegroup( "//tensorflow/tensorboard/components/tf_graph_controls/demo:all_files", "//tensorflow/tensorboard/components/tf_graph_dashboard:all_files", "//tensorflow/tensorboard/components/tf_graph_dashboard/demo:all_files", + "//tensorflow/tensorboard/components/tf_graph_debugger_data_card:all_files", + "//tensorflow/tensorboard/components/tf_graph_debugger_data_card/demo:all_files", "//tensorflow/tensorboard/components/tf_graph_info:all_files", "//tensorflow/tensorboard/components/tf_graph_info/demo:all_files", "//tensorflow/tensorboard/components/tf_graph_loader:all_files", diff --git a/tensorflow/tensorboard/components/tf_graph_board/tf-graph-board.html b/tensorflow/tensorboard/components/tf_graph_board/tf-graph-board.html index 79409ce2a0c..742bb63e045 100644 --- a/tensorflow/tensorboard/components/tf_graph_board/tf-graph-board.html +++ b/tensorflow/tensorboard/components/tf_graph_board/tf-graph-board.html @@ -138,7 +138,7 @@ paper-progress { render-hierarchy="{{renderHierarchy}}" devices-for-stats="[[devicesForStats]]" stats="[[stats]]" - selected-node="{{_selectedNode}}" + selected-node="{{selectedNode}}" highlighted-node="{{_highlightedNode}}" color-by="[[colorBy]]" color-by-params="{{colorByParams}}" @@ -153,13 +153,14 @@ paper-progress { graph-hierarchy="[[graphHierarchy]]" render-hierarchy="[[renderHierarchy]]" graph="[[graph]]" - selected-node="{{_selectedNode}}" + selected-node="{{selectedNode}}" selected-node-include="{{_selectedNodeInclude}}" highlighted-node="{{_highlightedNode}}" color-by="[[colorBy]]" color-by-params="[[colorByParams]]" debugger-data-enabled="[[debuggerDataEnabled]]" are-health-pills-loading="[[areHealthPillsLoading]]" + debugger-numeric-alerts="[[debuggerNumericAlerts]]" node-names-to-health-pills="[[nodeNamesToHealthPills]]" all-steps-mode-enabled="{{allStepsModeEnabled}}" specific-health-pill-step="{{specificHealthPillStep}}" @@ -198,6 +199,12 @@ Polymer({ debuggerDataEnabled: Boolean, // Whether health pills are currently being loaded. areHealthPillsLoading: Boolean, + // An array of alerts (in chronological order) provided by debugging libraries on when bad + // values (NaN, +/- Inf) appear. + debuggerNumericAlerts: { + type: Array, + notify: true, + }, // A mapping between node name to the tf.graph.scene.HealthPill to render. nodeNamesToHealthPills: Object, // Whether the user can request health pills for individual steps from the server. This can be @@ -217,7 +224,10 @@ Polymer({ // The step of health pills to show throughout the graph. healthPillStepIndex: Number, // Private API: Data routing between child components. - _selectedNode: String, + selectedNode: { + type: String, + notify: true, + }, // The enum value of the include property of the selected node. _selectedNodeInclude: Number, _highlightedNode: String @@ -226,7 +236,7 @@ Polymer({ 'node-toggle-extract': '_nodeToggleExtract' }, observers: [ - '_updateNodeInclude(_selectedNode)' + '_updateNodeInclude(selectedNode)' ], /** True if the progress is not complete yet (< 100 %). */ _isNotComplete: function(progress) { @@ -248,7 +258,7 @@ Polymer({ node ? node.include : tf.graph.InclusionType.UNSPECIFIED); }, _nodeToggleExtract: function() { - this._updateNodeInclude(this._selectedNode); + this._updateNodeInclude(this.selectedNode); } }); diff --git a/tensorflow/tensorboard/components/tf_graph_common/render.ts b/tensorflow/tensorboard/components/tf_graph_common/render.ts index 474e358ba95..4f28af481d4 100644 --- a/tensorflow/tensorboard/components/tf_graph_common/render.ts +++ b/tensorflow/tensorboard/components/tf_graph_common/render.ts @@ -1630,4 +1630,44 @@ function extractHighDegrees(renderNode: RenderGroupNodeInfo) { } }); } + +/** + * Expands nodes in the graph until the desired node is visible. + * + * @param scene The scene polymer component. + * @param renderHierarchy The render hierarchy. + * @param tensorName The name of a tensor. + * @return A string that is the name of the node representing the given tensor. + * Note that the original tensor name might differ from this returned node + * name. Specifically, for instance, the tensor name usually ends with an + * output slot index (such as :0), while the node name lacks that suffix. + */ +export function expandUntilNodeIsShown( + scene, renderHierarchy, tensorName: string) { + const splitTensorName = tensorName.split('/'); + + // Graph names do not take into account the output slot. Strip it. + const lastNodeNameMatch = + splitTensorName[splitTensorName.length - 1].match(/(.*):\d+/); + if (lastNodeNameMatch.length === 2) { + splitTensorName[splitTensorName.length - 1] = lastNodeNameMatch[1]; + } + + let nodeName = splitTensorName[0]; + let renderNode = renderHierarchy.getRenderNodeByName(nodeName); + for (let i = 1; i < splitTensorName.length; i++) { + // Op nodes are not expandable. + if (renderNode.node.type === tf.graph.NodeType.OP) { + break; + } + renderHierarchy.buildSubhierarchy(nodeName); + renderNode.expanded = true; + scene.setNodeExpanded(renderNode); + nodeName += '/' + splitTensorName[i]; + renderNode = renderHierarchy.getRenderNodeByName(nodeName); + } + + return renderNode.node.name; +} + } // close module tf.graph.render diff --git a/tensorflow/tensorboard/components/tf_graph_common/util.ts b/tensorflow/tensorboard/components/tf_graph_common/util.ts index 7f4d329e795..bee40ae713e 100644 --- a/tensorflow/tensorboard/components/tf_graph_common/util.ts +++ b/tensorflow/tensorboard/components/tf_graph_common/util.ts @@ -288,4 +288,29 @@ module tf.graph.util { return _.object(queryParams); } + + /** + * Given a timestamp in microseconds, return a human-friendly string denoting + * how long ago the timestamp was. + */ + export function computeHumanFriendlyTime(timeInMicroseconds: number) { + var timeDifferenceInMs = + +(new Date()) - +(new Date(timeInMicroseconds / 1e3)); + if (timeDifferenceInMs < 30000) { + return 'just now'; + } else if (timeDifferenceInMs < 60000) { + return Math.floor(timeDifferenceInMs / 1000) + ' seconds ago'; + } else if (timeDifferenceInMs < 120000) { + return 'a minute ago'; + } else if (timeDifferenceInMs < 3600000) { + return Math.floor(timeDifferenceInMs / 60000) + ' minutes ago'; + } else if (Math.floor(timeDifferenceInMs / 3600000) == 1) { + return 'an hour ago'; + } else if (timeDifferenceInMs < 86400000) { + return Math.floor(timeDifferenceInMs / 3600000) + ' hours ago'; + } else if (timeDifferenceInMs < 172800000) { + return 'yesterday'; + } + return Math.floor(timeDifferenceInMs / 86400000) + ' days ago'; + } } diff --git a/tensorflow/tensorboard/components/tf_graph_dashboard/tf-graph-dashboard.html b/tensorflow/tensorboard/components/tf_graph_dashboard/tf-graph-dashboard.html index 7e0ce2647bd..ba69882a232 100644 --- a/tensorflow/tensorboard/components/tf_graph_dashboard/tf-graph-dashboard.html +++ b/tensorflow/tensorboard/components/tf_graph_dashboard/tf-graph-dashboard.html @@ -78,11 +78,13 @@ out-hierarchy-params="{{_hierarchyParams}}" progress="[[_progress]]" debugger-data-enabled="[[debuggerDataEnabled]]" are-health-pills-loading="[[_areHealthPillsLoading]]" + debugger-numeric-alerts="[[_debuggerNumericAlerts]]" node-names-to-health-pills="[[_nodeNamesToHealthPills]]" all-steps-mode-enabled="{{allStepsModeEnabled}}" specific-health-pill-step="{{specificHealthPillStep}}" health-pill-step-index="[[_healthPillStepIndex]]" render-hierarchy="{{_renderHierarchy}}" + selected-node="{{_selectedNode}}" stats="[[_stats]]" > @@ -128,12 +130,20 @@ Polymer({ allStepsModeEnabled: Boolean, specificHealthPillStep: {type: Number, value: 0}, healthPillsToggledOn: {type: Boolean, value: true, observer: '_healthPillsToggledOnChanged'}, + _selectedNode: Object, _isAttached: Boolean, // Whether this dashboard is initialized. This dashboard should only be initialized once. _initialized: Boolean, // Whether health pills are currently being loaded, in which case we may want to say show a // spinner. _areHealthPillsLoading: Boolean, + // An array of alerts (in chronological order) provided by debugging libraries on when bad + // values (NaN, +/- Inf) appear. + _debuggerNumericAlerts: { + type: Array, + value: [], + notify: true, + }, // Maps the names of nodes to an array of health pills (HealthPillDatums). _nodeNamesToHealthPills: { type: Object, @@ -158,7 +168,7 @@ Polymer({ 'node-toggle-expand': '_handleNodeToggleExpand', }, observers: [ - '_maybeFetchHealthPills(allStepsModeEnabled, specificHealthPillStep)', + '_maybeFetchHealthPills(allStepsModeEnabled, specificHealthPillStep, _selectedNode)', '_maybeInitializeDashboard(backend, _isAttached)', ], attached: function() { @@ -212,7 +222,7 @@ Polymer({ }, _requestHealthPills: function() { this.set('_areHealthPillsLoading', true); - const requestId = ++this._healthPillRequestId; + var requestId = ++this._healthPillRequestId; if (this._healthPillStepRequestTimerId !== null) { // A request for health pills is already scheduled to be initiated. Clear it, and schedule a @@ -243,9 +253,17 @@ Polymer({ return; } - const specificStep = this.allStepsModeEnabled ? this.specificHealthPillStep : undefined; - this.backend.healthPills(this._renderHierarchy.getNamesOfRenderedOps(), specificStep).then( + var specificStep = this.allStepsModeEnabled ? this.specificHealthPillStep : undefined; + + var healthPillsPromise = this.backend.healthPills( + this._renderHierarchy.getNamesOfRenderedOps(), specificStep); + var alertsPromise = this.backend.debuggerNumericsAlerts(); + + Promise.all([healthPillsPromise, alertsPromise]).then( function(result) { + var healthPillsResult = result[0]; + var alertsResult = result[1]; + if (!this.healthPillsToggledOn) { // The user has opted to hide health pills via the toggle button. return; @@ -259,12 +277,13 @@ Polymer({ // Set the index for which step to show for the health pills. By default, show the last step. // A precondition we assume (that Tensorboard's reservoir sampling guarantees) is that all // node names should be mapped to the same number of steps. - for (let nodeName in result) { - this.set('_healthPillStepIndex', result[nodeName].length - 1); + for (var nodeName in healthPillsResult) { + this.set('_healthPillStepIndex', healthPillsResult[nodeName].length - 1); break; } - this.set('_nodeNamesToHealthPills', result); + this.set('_debuggerNumericAlerts', alertsResult); + this.set('_nodeNamesToHealthPills', healthPillsResult); this.set('_areHealthPillsLoading', false); this.set('_healthPillStepRequestTimerId', null); }.bind(this)); diff --git a/tensorflow/tensorboard/components/tf_graph_debugger_data_card/BUILD b/tensorflow/tensorboard/components/tf_graph_debugger_data_card/BUILD new file mode 100644 index 00000000000..c74aacd84e7 --- /dev/null +++ b/tensorflow/tensorboard/components/tf_graph_debugger_data_card/BUILD @@ -0,0 +1,44 @@ +package(default_visibility = ["//tensorflow:internal"]) + +load("//tensorflow/tensorboard:defs.bzl", "tensorboard_webcomponent_library") +load("//tensorflow/tensorboard:web.bzl", "ts_web_library") + +licenses(["notice"]) # Apache 2.0 + +ts_web_library( + name = "tf_graph_debugger_data_card", + srcs = [ + "tf-graph-debugger-data-card.html", + ], + path = "/tf-graph-debugger-data-card", + deps = [ + "//tensorflow/tensorboard/components/tf_dashboard_common", + "//tensorflow/tensorboard/components/tf_graph_common", + "//tensorflow/tensorboard/components/tf_imports:polymer", + "@org_polymer_paper_slider", + "@org_polymer_paper_spinner", + ], +) + +tensorboard_webcomponent_library( + name = "legacy", + srcs = [":tf_graph_debugger_data_card"], + destdir = "tf-graph-debugger-data-card", + deps = [ + "//tensorflow/tensorboard/components/tf_dashboard_common:legacy", + "//tensorflow/tensorboard/components/tf_graph_common:legacy", + "//third_party/javascript/polymer/v1/iron-collapse:lib", + "//third_party/javascript/polymer/v1/iron-list:lib", + "//third_party/javascript/polymer/v1/paper-icon-button:lib", + "//third_party/javascript/polymer/v1/paper-item:lib", + "//third_party/javascript/polymer/v1/paper-slider:lib", + "//third_party/javascript/polymer/v1/paper-spinner:lib", + "//third_party/javascript/polymer/v1/polymer:lib", + ], +) + +filegroup( + name = "all_files", + srcs = glob(["**"]), + tags = ["notsan"], +) diff --git a/tensorflow/tensorboard/components/tf_graph_debugger_data_card/demo/BUILD b/tensorflow/tensorboard/components/tf_graph_debugger_data_card/demo/BUILD new file mode 100644 index 00000000000..2395a671c3c --- /dev/null +++ b/tensorflow/tensorboard/components/tf_graph_debugger_data_card/demo/BUILD @@ -0,0 +1,24 @@ +package(default_visibility = ["//tensorflow:internal"]) + +load("//tensorflow/tensorboard:web.bzl", "ts_web_library") + +licenses(["notice"]) # Apache 2.0 + +# bazel run //third_party/tensorflow/tensorboard/components/tf_graph_debugger_data_card/demo +ts_web_library( + name = "demo", + srcs = ["index.html"] + glob(["data/**"]), + path = "/tf-graph-debugger-data-card/demo", + deps = [ + "//tensorflow/tensorboard/components/tf_graph_debugger_data_card", + "//tensorflow/tensorboard/components/tf_imports:webcomponentsjs", + "@org_polymer_iron_demo_helpers", + "@org_polymer_paper_styles", + ], +) + +filegroup( + name = "all_files", + srcs = glob(["**"]), + tags = ["notsan"], +) diff --git a/tensorflow/tensorboard/components/tf_graph_debugger_data_card/demo/index.html b/tensorflow/tensorboard/components/tf_graph_debugger_data_card/demo/index.html new file mode 100644 index 00000000000..934e4f86a83 --- /dev/null +++ b/tensorflow/tensorboard/components/tf_graph_debugger_data_card/demo/index.html @@ -0,0 +1,36 @@ + + + + + +TF Graph Info Demo + + + + diff --git a/tensorflow/tensorboard/components/tf_graph_debugger_data_card/tf-graph-debugger-data-card.html b/tensorflow/tensorboard/components/tf_graph_debugger_data_card/tf-graph-debugger-data-card.html new file mode 100644 index 00000000000..6cc99a327cb --- /dev/null +++ b/tensorflow/tensorboard/components/tf_graph_debugger_data_card/tf-graph-debugger-data-card.html @@ -0,0 +1,560 @@ + + + + + + + + + + + diff --git a/tensorflow/tensorboard/components/tf_graph_info/BUILD b/tensorflow/tensorboard/components/tf_graph_info/BUILD index 70b69dda93c..f3b1ed77230 100644 --- a/tensorflow/tensorboard/components/tf_graph_info/BUILD +++ b/tensorflow/tensorboard/components/tf_graph_info/BUILD @@ -17,6 +17,7 @@ ts_web_library( deps = [ "//tensorflow/tensorboard/components/tf_dashboard_common", "//tensorflow/tensorboard/components/tf_graph_common", + "//tensorflow/tensorboard/components/tf_graph_debugger_data_card", "//tensorflow/tensorboard/components/tf_imports:polymer", "@org_polymer_iron_collapse", "@org_polymer_iron_list", @@ -34,6 +35,7 @@ tensorboard_webcomponent_library( deps = [ "//tensorflow/tensorboard/components/tf_dashboard_common:legacy", "//tensorflow/tensorboard/components/tf_graph_common:legacy", + "//tensorflow/tensorboard/components/tf_graph_debugger_data_card:legacy", "//third_party/javascript/polymer/v1/iron-collapse:lib", "//third_party/javascript/polymer/v1/iron-list:lib", "//third_party/javascript/polymer/v1/paper-icon-button:lib", diff --git a/tensorflow/tensorboard/components/tf_graph_info/tf-graph-info.html b/tensorflow/tensorboard/components/tf_graph_info/tf-graph-info.html index b33e1e00d04..bac25b67f77 100644 --- a/tensorflow/tensorboard/components/tf_graph_info/tf-graph-info.html +++ b/tensorflow/tensorboard/components/tf_graph_info/tf-graph-info.html @@ -19,6 +19,7 @@ limitations under the License. + @@ -29,6 +30,9 @@ limitations under the License. margin: 0; padding: 0; display: block; + max-height: 650px; + overflow-x: hidden; + overflow-y: auto; } h2 { @@ -36,60 +40,6 @@ h2 { text-align: center; margin: 0; } - -.health-pill-legend { - padding: 15px; -} - -.health-pill-legend h2 { - text-align: left; -} - -.health-pill-entry { - margin: 10px 10px 10px 0; -} - -.health-pill-entry .color-preview { - width: 26px; - height: 26px; - border-radius: 3px; - display: inline-block; - margin: 0 10px 0 0; -} - -.health-pill-entry .color-label, .health-pill-entry .tensor-count { - color: #777; - display: inline-block; - height: 26px; - font-size: 22px; - line-height: 26px; - vertical-align: top; -} - -.health-pill-entry .tensor-count { - float: right; -} - -#health-pill-step-slider { - width: 100%; - margin: 0 0 0 -15px; - /* 31 comes from adding a padding of 15px from both sides of the paper-slider, subtracting - * 1px so that the slider width aligns with the image (the last slider marker takes up 1px), - * and adding 2px to account for a border of 1px on both sides of the image. 30 - 1 + 2. - * Apparently, the paper-slider lacks a mixin for those padding values. */ - width: calc(100% + 31px); -} - -#health-pills-loading-spinner { - width: 20px; - height: 20px; - vertical-align: top; -} - -#health-pill-step-number-input { - text-align: center; - vertical-align: top; -}