tensorbored 2.21.0rc1769983804__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- tensorbored/__init__.py +112 -0
- tensorbored/_vendor/__init__.py +0 -0
- tensorbored/_vendor/bleach/__init__.py +125 -0
- tensorbored/_vendor/bleach/_vendor/__init__.py +0 -0
- tensorbored/_vendor/bleach/_vendor/html5lib/__init__.py +35 -0
- tensorbored/_vendor/bleach/_vendor/html5lib/_ihatexml.py +289 -0
- tensorbored/_vendor/bleach/_vendor/html5lib/_inputstream.py +918 -0
- tensorbored/_vendor/bleach/_vendor/html5lib/_tokenizer.py +1735 -0
- tensorbored/_vendor/bleach/_vendor/html5lib/_trie/__init__.py +5 -0
- tensorbored/_vendor/bleach/_vendor/html5lib/_trie/_base.py +40 -0
- tensorbored/_vendor/bleach/_vendor/html5lib/_trie/py.py +67 -0
- tensorbored/_vendor/bleach/_vendor/html5lib/_utils.py +159 -0
- tensorbored/_vendor/bleach/_vendor/html5lib/constants.py +2946 -0
- tensorbored/_vendor/bleach/_vendor/html5lib/filters/__init__.py +0 -0
- tensorbored/_vendor/bleach/_vendor/html5lib/filters/alphabeticalattributes.py +29 -0
- tensorbored/_vendor/bleach/_vendor/html5lib/filters/base.py +12 -0
- tensorbored/_vendor/bleach/_vendor/html5lib/filters/inject_meta_charset.py +73 -0
- tensorbored/_vendor/bleach/_vendor/html5lib/filters/lint.py +93 -0
- tensorbored/_vendor/bleach/_vendor/html5lib/filters/optionaltags.py +207 -0
- tensorbored/_vendor/bleach/_vendor/html5lib/filters/sanitizer.py +916 -0
- tensorbored/_vendor/bleach/_vendor/html5lib/filters/whitespace.py +38 -0
- tensorbored/_vendor/bleach/_vendor/html5lib/html5parser.py +2795 -0
- tensorbored/_vendor/bleach/_vendor/html5lib/serializer.py +409 -0
- tensorbored/_vendor/bleach/_vendor/html5lib/treeadapters/__init__.py +30 -0
- tensorbored/_vendor/bleach/_vendor/html5lib/treeadapters/genshi.py +54 -0
- tensorbored/_vendor/bleach/_vendor/html5lib/treeadapters/sax.py +50 -0
- tensorbored/_vendor/bleach/_vendor/html5lib/treebuilders/__init__.py +88 -0
- tensorbored/_vendor/bleach/_vendor/html5lib/treebuilders/base.py +417 -0
- tensorbored/_vendor/bleach/_vendor/html5lib/treebuilders/dom.py +239 -0
- tensorbored/_vendor/bleach/_vendor/html5lib/treebuilders/etree.py +343 -0
- tensorbored/_vendor/bleach/_vendor/html5lib/treebuilders/etree_lxml.py +392 -0
- tensorbored/_vendor/bleach/_vendor/html5lib/treewalkers/__init__.py +154 -0
- tensorbored/_vendor/bleach/_vendor/html5lib/treewalkers/base.py +252 -0
- tensorbored/_vendor/bleach/_vendor/html5lib/treewalkers/dom.py +43 -0
- tensorbored/_vendor/bleach/_vendor/html5lib/treewalkers/etree.py +131 -0
- tensorbored/_vendor/bleach/_vendor/html5lib/treewalkers/etree_lxml.py +215 -0
- tensorbored/_vendor/bleach/_vendor/html5lib/treewalkers/genshi.py +69 -0
- tensorbored/_vendor/bleach/_vendor/parse.py +1078 -0
- tensorbored/_vendor/bleach/callbacks.py +32 -0
- tensorbored/_vendor/bleach/html5lib_shim.py +757 -0
- tensorbored/_vendor/bleach/linkifier.py +633 -0
- tensorbored/_vendor/bleach/parse_shim.py +1 -0
- tensorbored/_vendor/bleach/sanitizer.py +638 -0
- tensorbored/_vendor/bleach/six_shim.py +19 -0
- tensorbored/_vendor/webencodings/__init__.py +342 -0
- tensorbored/_vendor/webencodings/labels.py +231 -0
- tensorbored/_vendor/webencodings/mklabels.py +59 -0
- tensorbored/_vendor/webencodings/x_user_defined.py +325 -0
- tensorbored/assets.py +36 -0
- tensorbored/auth.py +102 -0
- tensorbored/backend/__init__.py +0 -0
- tensorbored/backend/application.py +604 -0
- tensorbored/backend/auth_context_middleware.py +38 -0
- tensorbored/backend/client_feature_flags.py +113 -0
- tensorbored/backend/empty_path_redirect.py +46 -0
- tensorbored/backend/event_processing/__init__.py +0 -0
- tensorbored/backend/event_processing/data_ingester.py +276 -0
- tensorbored/backend/event_processing/data_provider.py +535 -0
- tensorbored/backend/event_processing/directory_loader.py +142 -0
- tensorbored/backend/event_processing/directory_watcher.py +272 -0
- tensorbored/backend/event_processing/event_accumulator.py +950 -0
- tensorbored/backend/event_processing/event_file_inspector.py +463 -0
- tensorbored/backend/event_processing/event_file_loader.py +292 -0
- tensorbored/backend/event_processing/event_multiplexer.py +521 -0
- tensorbored/backend/event_processing/event_util.py +68 -0
- tensorbored/backend/event_processing/io_wrapper.py +223 -0
- tensorbored/backend/event_processing/plugin_asset_util.py +104 -0
- tensorbored/backend/event_processing/plugin_event_accumulator.py +721 -0
- tensorbored/backend/event_processing/plugin_event_multiplexer.py +522 -0
- tensorbored/backend/event_processing/reservoir.py +266 -0
- tensorbored/backend/event_processing/tag_types.py +29 -0
- tensorbored/backend/experiment_id.py +71 -0
- tensorbored/backend/experimental_plugin.py +51 -0
- tensorbored/backend/http_util.py +263 -0
- tensorbored/backend/json_util.py +70 -0
- tensorbored/backend/path_prefix.py +67 -0
- tensorbored/backend/process_graph.py +74 -0
- tensorbored/backend/security_validator.py +202 -0
- tensorbored/compat/__init__.py +69 -0
- tensorbored/compat/proto/__init__.py +0 -0
- tensorbored/compat/proto/allocation_description_pb2.py +35 -0
- tensorbored/compat/proto/api_def_pb2.py +82 -0
- tensorbored/compat/proto/attr_value_pb2.py +80 -0
- tensorbored/compat/proto/cluster_pb2.py +58 -0
- tensorbored/compat/proto/config_pb2.py +271 -0
- tensorbored/compat/proto/coordination_config_pb2.py +45 -0
- tensorbored/compat/proto/cost_graph_pb2.py +87 -0
- tensorbored/compat/proto/cpp_shape_inference_pb2.py +70 -0
- tensorbored/compat/proto/debug_pb2.py +65 -0
- tensorbored/compat/proto/event_pb2.py +149 -0
- tensorbored/compat/proto/full_type_pb2.py +74 -0
- tensorbored/compat/proto/function_pb2.py +157 -0
- tensorbored/compat/proto/graph_debug_info_pb2.py +111 -0
- tensorbored/compat/proto/graph_pb2.py +41 -0
- tensorbored/compat/proto/histogram_pb2.py +39 -0
- tensorbored/compat/proto/meta_graph_pb2.py +254 -0
- tensorbored/compat/proto/node_def_pb2.py +61 -0
- tensorbored/compat/proto/op_def_pb2.py +81 -0
- tensorbored/compat/proto/resource_handle_pb2.py +48 -0
- tensorbored/compat/proto/rewriter_config_pb2.py +93 -0
- tensorbored/compat/proto/rpc_options_pb2.py +35 -0
- tensorbored/compat/proto/saved_object_graph_pb2.py +193 -0
- tensorbored/compat/proto/saver_pb2.py +38 -0
- tensorbored/compat/proto/step_stats_pb2.py +116 -0
- tensorbored/compat/proto/struct_pb2.py +144 -0
- tensorbored/compat/proto/summary_pb2.py +111 -0
- tensorbored/compat/proto/tensor_description_pb2.py +38 -0
- tensorbored/compat/proto/tensor_pb2.py +68 -0
- tensorbored/compat/proto/tensor_shape_pb2.py +46 -0
- tensorbored/compat/proto/tfprof_log_pb2.py +307 -0
- tensorbored/compat/proto/trackable_object_graph_pb2.py +90 -0
- tensorbored/compat/proto/types_pb2.py +105 -0
- tensorbored/compat/proto/variable_pb2.py +62 -0
- tensorbored/compat/proto/verifier_config_pb2.py +38 -0
- tensorbored/compat/proto/versions_pb2.py +35 -0
- tensorbored/compat/tensorflow_stub/__init__.py +38 -0
- tensorbored/compat/tensorflow_stub/app.py +124 -0
- tensorbored/compat/tensorflow_stub/compat/__init__.py +131 -0
- tensorbored/compat/tensorflow_stub/compat/v1/__init__.py +20 -0
- tensorbored/compat/tensorflow_stub/dtypes.py +692 -0
- tensorbored/compat/tensorflow_stub/error_codes.py +169 -0
- tensorbored/compat/tensorflow_stub/errors.py +507 -0
- tensorbored/compat/tensorflow_stub/flags.py +124 -0
- tensorbored/compat/tensorflow_stub/io/__init__.py +17 -0
- tensorbored/compat/tensorflow_stub/io/gfile.py +1011 -0
- tensorbored/compat/tensorflow_stub/pywrap_tensorflow.py +285 -0
- tensorbored/compat/tensorflow_stub/tensor_shape.py +1035 -0
- tensorbored/context.py +129 -0
- tensorbored/data/__init__.py +0 -0
- tensorbored/data/grpc_provider.py +365 -0
- tensorbored/data/ingester.py +46 -0
- tensorbored/data/proto/__init__.py +0 -0
- tensorbored/data/proto/data_provider_pb2.py +517 -0
- tensorbored/data/proto/data_provider_pb2_grpc.py +374 -0
- tensorbored/data/provider.py +1365 -0
- tensorbored/data/server_ingester.py +301 -0
- tensorbored/data_compat.py +159 -0
- tensorbored/dataclass_compat.py +224 -0
- tensorbored/default.py +124 -0
- tensorbored/errors.py +130 -0
- tensorbored/lazy.py +99 -0
- tensorbored/main.py +48 -0
- tensorbored/main_lib.py +62 -0
- tensorbored/manager.py +487 -0
- tensorbored/notebook.py +441 -0
- tensorbored/plugin_util.py +266 -0
- tensorbored/plugins/__init__.py +0 -0
- tensorbored/plugins/audio/__init__.py +0 -0
- tensorbored/plugins/audio/audio_plugin.py +229 -0
- tensorbored/plugins/audio/metadata.py +69 -0
- tensorbored/plugins/audio/plugin_data_pb2.py +37 -0
- tensorbored/plugins/audio/summary.py +230 -0
- tensorbored/plugins/audio/summary_v2.py +124 -0
- tensorbored/plugins/base_plugin.py +367 -0
- tensorbored/plugins/core/__init__.py +0 -0
- tensorbored/plugins/core/core_plugin.py +981 -0
- tensorbored/plugins/custom_scalar/__init__.py +0 -0
- tensorbored/plugins/custom_scalar/custom_scalars_plugin.py +320 -0
- tensorbored/plugins/custom_scalar/layout_pb2.py +85 -0
- tensorbored/plugins/custom_scalar/metadata.py +35 -0
- tensorbored/plugins/custom_scalar/summary.py +79 -0
- tensorbored/plugins/debugger_v2/__init__.py +0 -0
- tensorbored/plugins/debugger_v2/debug_data_multiplexer.py +631 -0
- tensorbored/plugins/debugger_v2/debug_data_provider.py +634 -0
- tensorbored/plugins/debugger_v2/debugger_v2_plugin.py +504 -0
- tensorbored/plugins/distribution/__init__.py +0 -0
- tensorbored/plugins/distribution/compressor.py +158 -0
- tensorbored/plugins/distribution/distributions_plugin.py +116 -0
- tensorbored/plugins/distribution/metadata.py +19 -0
- tensorbored/plugins/graph/__init__.py +0 -0
- tensorbored/plugins/graph/graph_util.py +129 -0
- tensorbored/plugins/graph/graphs_plugin.py +336 -0
- tensorbored/plugins/graph/keras_util.py +328 -0
- tensorbored/plugins/graph/metadata.py +42 -0
- tensorbored/plugins/histogram/__init__.py +0 -0
- tensorbored/plugins/histogram/histograms_plugin.py +144 -0
- tensorbored/plugins/histogram/metadata.py +63 -0
- tensorbored/plugins/histogram/plugin_data_pb2.py +34 -0
- tensorbored/plugins/histogram/summary.py +234 -0
- tensorbored/plugins/histogram/summary_v2.py +292 -0
- tensorbored/plugins/hparams/__init__.py +14 -0
- tensorbored/plugins/hparams/_keras.py +93 -0
- tensorbored/plugins/hparams/api.py +130 -0
- tensorbored/plugins/hparams/api_pb2.py +208 -0
- tensorbored/plugins/hparams/backend_context.py +606 -0
- tensorbored/plugins/hparams/download_data.py +158 -0
- tensorbored/plugins/hparams/error.py +26 -0
- tensorbored/plugins/hparams/get_experiment.py +71 -0
- tensorbored/plugins/hparams/hparams_plugin.py +206 -0
- tensorbored/plugins/hparams/hparams_util_pb2.py +69 -0
- tensorbored/plugins/hparams/json_format_compat.py +38 -0
- tensorbored/plugins/hparams/list_metric_evals.py +57 -0
- tensorbored/plugins/hparams/list_session_groups.py +1040 -0
- tensorbored/plugins/hparams/metadata.py +125 -0
- tensorbored/plugins/hparams/metrics.py +41 -0
- tensorbored/plugins/hparams/plugin_data_pb2.py +69 -0
- tensorbored/plugins/hparams/summary.py +205 -0
- tensorbored/plugins/hparams/summary_v2.py +597 -0
- tensorbored/plugins/image/__init__.py +0 -0
- tensorbored/plugins/image/images_plugin.py +232 -0
- tensorbored/plugins/image/metadata.py +65 -0
- tensorbored/plugins/image/plugin_data_pb2.py +34 -0
- tensorbored/plugins/image/summary.py +159 -0
- tensorbored/plugins/image/summary_v2.py +130 -0
- tensorbored/plugins/mesh/__init__.py +14 -0
- tensorbored/plugins/mesh/mesh_plugin.py +292 -0
- tensorbored/plugins/mesh/metadata.py +152 -0
- tensorbored/plugins/mesh/plugin_data_pb2.py +37 -0
- tensorbored/plugins/mesh/summary.py +251 -0
- tensorbored/plugins/mesh/summary_v2.py +214 -0
- tensorbored/plugins/metrics/__init__.py +0 -0
- tensorbored/plugins/metrics/metadata.py +17 -0
- tensorbored/plugins/metrics/metrics_plugin.py +623 -0
- tensorbored/plugins/pr_curve/__init__.py +0 -0
- tensorbored/plugins/pr_curve/metadata.py +75 -0
- tensorbored/plugins/pr_curve/plugin_data_pb2.py +34 -0
- tensorbored/plugins/pr_curve/pr_curves_plugin.py +241 -0
- tensorbored/plugins/pr_curve/summary.py +574 -0
- tensorbored/plugins/profile_redirect/__init__.py +0 -0
- tensorbored/plugins/profile_redirect/profile_redirect_plugin.py +49 -0
- tensorbored/plugins/projector/__init__.py +67 -0
- tensorbored/plugins/projector/metadata.py +26 -0
- tensorbored/plugins/projector/projector_config_pb2.py +54 -0
- tensorbored/plugins/projector/projector_plugin.py +795 -0
- tensorbored/plugins/projector/tf_projector_plugin/index.js +32 -0
- tensorbored/plugins/projector/tf_projector_plugin/projector_binary.html +524 -0
- tensorbored/plugins/projector/tf_projector_plugin/projector_binary.js +15536 -0
- tensorbored/plugins/scalar/__init__.py +0 -0
- tensorbored/plugins/scalar/metadata.py +60 -0
- tensorbored/plugins/scalar/plugin_data_pb2.py +34 -0
- tensorbored/plugins/scalar/scalars_plugin.py +181 -0
- tensorbored/plugins/scalar/summary.py +109 -0
- tensorbored/plugins/scalar/summary_v2.py +124 -0
- tensorbored/plugins/text/__init__.py +0 -0
- tensorbored/plugins/text/metadata.py +62 -0
- tensorbored/plugins/text/plugin_data_pb2.py +34 -0
- tensorbored/plugins/text/summary.py +114 -0
- tensorbored/plugins/text/summary_v2.py +124 -0
- tensorbored/plugins/text/text_plugin.py +288 -0
- tensorbored/plugins/wit_redirect/__init__.py +0 -0
- tensorbored/plugins/wit_redirect/wit_redirect_plugin.py +49 -0
- tensorbored/program.py +910 -0
- tensorbored/summary/__init__.py +35 -0
- tensorbored/summary/_output.py +124 -0
- tensorbored/summary/_tf/__init__.py +14 -0
- tensorbored/summary/_tf/summary/__init__.py +178 -0
- tensorbored/summary/_writer.py +105 -0
- tensorbored/summary/v1.py +51 -0
- tensorbored/summary/v2.py +25 -0
- tensorbored/summary/writer/__init__.py +13 -0
- tensorbored/summary/writer/event_file_writer.py +291 -0
- tensorbored/summary/writer/record_writer.py +50 -0
- tensorbored/util/__init__.py +0 -0
- tensorbored/util/encoder.py +116 -0
- tensorbored/util/grpc_util.py +311 -0
- tensorbored/util/img_mime_type_detector.py +40 -0
- tensorbored/util/io_util.py +20 -0
- tensorbored/util/lazy_tensor_creator.py +110 -0
- tensorbored/util/op_evaluator.py +104 -0
- tensorbored/util/platform_util.py +20 -0
- tensorbored/util/tb_logging.py +24 -0
- tensorbored/util/tensor_util.py +617 -0
- tensorbored/util/timing.py +122 -0
- tensorbored/version.py +21 -0
- tensorbored/webfiles.zip +0 -0
- tensorbored-2.21.0rc1769983804.dist-info/METADATA +49 -0
- tensorbored-2.21.0rc1769983804.dist-info/RECORD +271 -0
- tensorbored-2.21.0rc1769983804.dist-info/WHEEL +5 -0
- tensorbored-2.21.0rc1769983804.dist-info/entry_points.txt +6 -0
- tensorbored-2.21.0rc1769983804.dist-info/licenses/LICENSE +739 -0
- tensorbored-2.21.0rc1769983804.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,623 @@
|
|
|
1
|
+
# Copyright 2020 The TensorFlow Authors. All Rights Reserved.
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
# ==============================================================================
|
|
15
|
+
"""The TensorBoard metrics plugin."""
|
|
16
|
+
|
|
17
|
+
import collections
|
|
18
|
+
import json
|
|
19
|
+
|
|
20
|
+
from werkzeug import wrappers
|
|
21
|
+
|
|
22
|
+
from tensorbored import errors
|
|
23
|
+
from tensorbored import plugin_util
|
|
24
|
+
from tensorbored.backend import http_util
|
|
25
|
+
from tensorbored.data import provider
|
|
26
|
+
from tensorbored.plugins import base_plugin
|
|
27
|
+
from tensorbored.plugins.histogram import metadata as histogram_metadata
|
|
28
|
+
from tensorbored.plugins.image import metadata as image_metadata
|
|
29
|
+
from tensorbored.plugins.metrics import metadata
|
|
30
|
+
from tensorbored.plugins.scalar import metadata as scalar_metadata
|
|
31
|
+
from tensorbored.util import img_mime_type_detector
|
|
32
|
+
|
|
33
|
+
_SINGLE_RUN_PLUGINS = frozenset(
|
|
34
|
+
[histogram_metadata.PLUGIN_NAME, image_metadata.PLUGIN_NAME]
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
_SAMPLED_PLUGINS = frozenset([image_metadata.PLUGIN_NAME])
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def _get_tag_description_info(mapping):
|
|
41
|
+
"""Gets maps from tags to descriptions, and descriptions to runs.
|
|
42
|
+
|
|
43
|
+
Args:
|
|
44
|
+
mapping: a nested map `d` such that `d[run][tag]` is a time series
|
|
45
|
+
produced by DataProvider's `list_*` methods.
|
|
46
|
+
|
|
47
|
+
Returns:
|
|
48
|
+
A tuple containing
|
|
49
|
+
tag_to_descriptions: A map from tag strings to a set of description
|
|
50
|
+
strings.
|
|
51
|
+
description_to_runs: A map from description strings to a set of run
|
|
52
|
+
strings.
|
|
53
|
+
"""
|
|
54
|
+
tag_to_descriptions = collections.defaultdict(set)
|
|
55
|
+
description_to_runs = collections.defaultdict(set)
|
|
56
|
+
for run, tag_to_content in mapping.items():
|
|
57
|
+
for tag, metadatum in tag_to_content.items():
|
|
58
|
+
description = metadatum.description
|
|
59
|
+
if len(description):
|
|
60
|
+
tag_to_descriptions[tag].add(description)
|
|
61
|
+
description_to_runs[description].add(run)
|
|
62
|
+
|
|
63
|
+
return tag_to_descriptions, description_to_runs
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def _build_combined_description(descriptions, description_to_runs):
|
|
67
|
+
"""Creates a single description from a set of descriptions.
|
|
68
|
+
|
|
69
|
+
Descriptions may be composites when a single tag has different descriptions
|
|
70
|
+
across multiple runs.
|
|
71
|
+
|
|
72
|
+
Args:
|
|
73
|
+
descriptions: A list of description strings.
|
|
74
|
+
description_to_runs: A map from description strings to a set of run
|
|
75
|
+
strings.
|
|
76
|
+
|
|
77
|
+
Returns:
|
|
78
|
+
The combined description string.
|
|
79
|
+
"""
|
|
80
|
+
prefixed_descriptions = []
|
|
81
|
+
for description in descriptions:
|
|
82
|
+
runs = sorted(description_to_runs[description])
|
|
83
|
+
run_or_runs = "runs" if len(runs) > 1 else "run"
|
|
84
|
+
run_header = "## For " + run_or_runs + ": " + ", ".join(runs)
|
|
85
|
+
description_html = run_header + "\n" + description
|
|
86
|
+
prefixed_descriptions.append(description_html)
|
|
87
|
+
|
|
88
|
+
header = "# Multiple descriptions\n"
|
|
89
|
+
return header + "\n".join(prefixed_descriptions)
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def _get_tag_to_description(mapping):
|
|
93
|
+
"""Returns a map of tags to descriptions.
|
|
94
|
+
|
|
95
|
+
Args:
|
|
96
|
+
mapping: a nested map `d` such that `d[run][tag]` is a time series
|
|
97
|
+
produced by DataProvider's `list_*` methods.
|
|
98
|
+
|
|
99
|
+
Returns:
|
|
100
|
+
A map from tag strings to description HTML strings. E.g.
|
|
101
|
+
{
|
|
102
|
+
"loss": "<h1>Multiple descriptions</h1><h2>For runs: test, train
|
|
103
|
+
</h2><p>...</p>",
|
|
104
|
+
"loss2": "<p>The lossy details</p>",
|
|
105
|
+
}
|
|
106
|
+
"""
|
|
107
|
+
tag_to_descriptions, description_to_runs = _get_tag_description_info(
|
|
108
|
+
mapping
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
result = {}
|
|
112
|
+
for tag in tag_to_descriptions:
|
|
113
|
+
descriptions = sorted(tag_to_descriptions[tag])
|
|
114
|
+
if len(descriptions) == 1:
|
|
115
|
+
description = descriptions[0]
|
|
116
|
+
else:
|
|
117
|
+
description = _build_combined_description(
|
|
118
|
+
descriptions, description_to_runs
|
|
119
|
+
)
|
|
120
|
+
result[tag] = plugin_util.markdown_to_safe_html(description)
|
|
121
|
+
|
|
122
|
+
return result
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def _get_run_tag_info(mapping):
|
|
126
|
+
"""Returns a map of run names to a list of tag names.
|
|
127
|
+
|
|
128
|
+
Args:
|
|
129
|
+
mapping: a nested map `d` such that `d[run][tag]` is a time series
|
|
130
|
+
produced by DataProvider's `list_*` methods.
|
|
131
|
+
|
|
132
|
+
Returns:
|
|
133
|
+
A map from run strings to a list of tag strings. E.g.
|
|
134
|
+
{"loss001a": ["actor/loss", "critic/loss"], ...}
|
|
135
|
+
"""
|
|
136
|
+
return {run: sorted(mapping[run]) for run in mapping}
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def _format_basic_mapping(mapping):
|
|
140
|
+
"""Prepares a scalar or histogram mapping for client consumption.
|
|
141
|
+
|
|
142
|
+
Args:
|
|
143
|
+
mapping: a nested map `d` such that `d[run][tag]` is a time series
|
|
144
|
+
produced by DataProvider's `list_*` methods.
|
|
145
|
+
|
|
146
|
+
Returns:
|
|
147
|
+
A dict with the following fields:
|
|
148
|
+
runTagInfo: the return type of `_get_run_tag_info`
|
|
149
|
+
tagDescriptions: the return type of `_get_tag_to_description`
|
|
150
|
+
"""
|
|
151
|
+
return {
|
|
152
|
+
"runTagInfo": _get_run_tag_info(mapping),
|
|
153
|
+
"tagDescriptions": _get_tag_to_description(mapping),
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
def _format_image_blob_sequence_datum(sorted_datum_list, sample):
|
|
158
|
+
"""Formats image metadata from a list of BlobSequenceDatum's for clients.
|
|
159
|
+
|
|
160
|
+
This expects that frontend clients need to access images based on the
|
|
161
|
+
run+tag+sample.
|
|
162
|
+
|
|
163
|
+
Args:
|
|
164
|
+
sorted_datum_list: a list of DataProvider's `BlobSequenceDatum`, sorted by
|
|
165
|
+
step. This can be produced via DataProvider's `read_blob_sequences`.
|
|
166
|
+
sample: zero-indexed integer for the requested sample.
|
|
167
|
+
|
|
168
|
+
Returns:
|
|
169
|
+
A list of `ImageStepDatum` (see http_api.md).
|
|
170
|
+
"""
|
|
171
|
+
# For images, ignore the first 2 items of a BlobSequenceDatum's values, which
|
|
172
|
+
# correspond to width, height.
|
|
173
|
+
index = sample + 2
|
|
174
|
+
step_data = []
|
|
175
|
+
for datum in sorted_datum_list:
|
|
176
|
+
if len(datum.values) <= index:
|
|
177
|
+
continue
|
|
178
|
+
|
|
179
|
+
step_data.append(
|
|
180
|
+
{
|
|
181
|
+
"step": datum.step,
|
|
182
|
+
"wallTime": datum.wall_time,
|
|
183
|
+
"imageId": datum.values[index].blob_key,
|
|
184
|
+
}
|
|
185
|
+
)
|
|
186
|
+
return step_data
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
def _get_tag_run_image_info(mapping):
|
|
190
|
+
"""Returns a map of tag names to run information.
|
|
191
|
+
|
|
192
|
+
Args:
|
|
193
|
+
mapping: the result of DataProvider's `list_blob_sequences`.
|
|
194
|
+
|
|
195
|
+
Returns:
|
|
196
|
+
A nested map from run strings to tag string to image info, where image
|
|
197
|
+
info is an object of form {"maxSamplesPerStep": num}. For example,
|
|
198
|
+
{
|
|
199
|
+
"reshaped": {
|
|
200
|
+
"test": {"maxSamplesPerStep": 1},
|
|
201
|
+
"train": {"maxSamplesPerStep": 1}
|
|
202
|
+
},
|
|
203
|
+
"convolved": {"test": {"maxSamplesPerStep": 50}},
|
|
204
|
+
}
|
|
205
|
+
"""
|
|
206
|
+
tag_run_image_info = collections.defaultdict(dict)
|
|
207
|
+
for run, tag_to_content in mapping.items():
|
|
208
|
+
for tag, metadatum in tag_to_content.items():
|
|
209
|
+
tag_run_image_info[tag][run] = {
|
|
210
|
+
"maxSamplesPerStep": metadatum.max_length - 2 # width, height
|
|
211
|
+
}
|
|
212
|
+
return dict(tag_run_image_info)
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
def _format_image_mapping(mapping):
|
|
216
|
+
"""Prepares an image mapping for client consumption.
|
|
217
|
+
|
|
218
|
+
Args:
|
|
219
|
+
mapping: the result of DataProvider's `list_blob_sequences`.
|
|
220
|
+
|
|
221
|
+
Returns:
|
|
222
|
+
A dict with the following fields:
|
|
223
|
+
tagRunSampledInfo: the return type of `_get_tag_run_image_info`
|
|
224
|
+
tagDescriptions: the return type of `_get_tag_description_info`
|
|
225
|
+
"""
|
|
226
|
+
return {
|
|
227
|
+
"tagDescriptions": _get_tag_to_description(mapping),
|
|
228
|
+
"tagRunSampledInfo": _get_tag_run_image_info(mapping),
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
class MetricsPlugin(base_plugin.TBPlugin):
|
|
233
|
+
"""Metrics Plugin for TensorBoard."""
|
|
234
|
+
|
|
235
|
+
plugin_name = metadata.PLUGIN_NAME
|
|
236
|
+
|
|
237
|
+
def __init__(self, context):
|
|
238
|
+
"""Instantiates MetricsPlugin.
|
|
239
|
+
|
|
240
|
+
Args:
|
|
241
|
+
context: A base_plugin.TBContext instance. MetricsLoader checks that
|
|
242
|
+
it contains a valid `data_provider`.
|
|
243
|
+
"""
|
|
244
|
+
self._data_provider = context.data_provider
|
|
245
|
+
|
|
246
|
+
# For histograms, use a round number + 1 since sampling includes both start
|
|
247
|
+
# and end steps, so N+1 samples corresponds to dividing the step sequence
|
|
248
|
+
# into N intervals.
|
|
249
|
+
sampling_hints = context.sampling_hints or {}
|
|
250
|
+
self._plugin_downsampling = {
|
|
251
|
+
"scalars": sampling_hints.get(scalar_metadata.PLUGIN_NAME, 1000),
|
|
252
|
+
"histograms": sampling_hints.get(
|
|
253
|
+
histogram_metadata.PLUGIN_NAME, 51
|
|
254
|
+
),
|
|
255
|
+
"images": sampling_hints.get(image_metadata.PLUGIN_NAME, 10),
|
|
256
|
+
}
|
|
257
|
+
self._scalar_version_checker = plugin_util._MetadataVersionChecker(
|
|
258
|
+
data_kind="scalar time series",
|
|
259
|
+
latest_known_version=0,
|
|
260
|
+
)
|
|
261
|
+
self._histogram_version_checker = plugin_util._MetadataVersionChecker(
|
|
262
|
+
data_kind="histogram time series",
|
|
263
|
+
latest_known_version=0,
|
|
264
|
+
)
|
|
265
|
+
self._image_version_checker = plugin_util._MetadataVersionChecker(
|
|
266
|
+
data_kind="image time series",
|
|
267
|
+
latest_known_version=0,
|
|
268
|
+
)
|
|
269
|
+
|
|
270
|
+
def frontend_metadata(self):
|
|
271
|
+
return base_plugin.FrontendMetadata(
|
|
272
|
+
is_ng_component=True, tab_name="Time Series"
|
|
273
|
+
)
|
|
274
|
+
|
|
275
|
+
def get_plugin_apps(self):
|
|
276
|
+
return {
|
|
277
|
+
"/tags": self._serve_tags,
|
|
278
|
+
"/timeSeries": self._serve_time_series,
|
|
279
|
+
"/imageData": self._serve_image_data,
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
def data_plugin_names(self):
|
|
283
|
+
return (
|
|
284
|
+
scalar_metadata.PLUGIN_NAME,
|
|
285
|
+
histogram_metadata.PLUGIN_NAME,
|
|
286
|
+
image_metadata.PLUGIN_NAME,
|
|
287
|
+
)
|
|
288
|
+
|
|
289
|
+
def is_active(self):
|
|
290
|
+
return False # 'data_plugin_names' suffices.
|
|
291
|
+
|
|
292
|
+
@wrappers.Request.application
|
|
293
|
+
def _serve_tags(self, request):
|
|
294
|
+
ctx = plugin_util.context(request.environ)
|
|
295
|
+
experiment = plugin_util.experiment_id(request.environ)
|
|
296
|
+
index = self._tags_impl(ctx, experiment=experiment)
|
|
297
|
+
return http_util.Respond(request, index, "application/json")
|
|
298
|
+
|
|
299
|
+
def _tags_impl(self, ctx, experiment=None):
|
|
300
|
+
"""Returns tag metadata for a given experiment's logged metrics.
|
|
301
|
+
|
|
302
|
+
Args:
|
|
303
|
+
ctx: A `tensorboard.context.RequestContext` value.
|
|
304
|
+
experiment: optional string ID of the request's experiment.
|
|
305
|
+
|
|
306
|
+
Returns:
|
|
307
|
+
A nested dict 'd' with keys in ("scalars", "histograms", "images")
|
|
308
|
+
and values being the return type of _format_*mapping.
|
|
309
|
+
"""
|
|
310
|
+
scalar_mapping = self._data_provider.list_scalars(
|
|
311
|
+
ctx,
|
|
312
|
+
experiment_id=experiment,
|
|
313
|
+
plugin_name=scalar_metadata.PLUGIN_NAME,
|
|
314
|
+
)
|
|
315
|
+
scalar_mapping = self._filter_by_version(
|
|
316
|
+
scalar_mapping,
|
|
317
|
+
scalar_metadata.parse_plugin_metadata,
|
|
318
|
+
self._scalar_version_checker,
|
|
319
|
+
)
|
|
320
|
+
|
|
321
|
+
histogram_mapping = self._data_provider.list_tensors(
|
|
322
|
+
ctx,
|
|
323
|
+
experiment_id=experiment,
|
|
324
|
+
plugin_name=histogram_metadata.PLUGIN_NAME,
|
|
325
|
+
)
|
|
326
|
+
if histogram_mapping is None:
|
|
327
|
+
histogram_mapping = {}
|
|
328
|
+
histogram_mapping = self._filter_by_version(
|
|
329
|
+
histogram_mapping,
|
|
330
|
+
histogram_metadata.parse_plugin_metadata,
|
|
331
|
+
self._histogram_version_checker,
|
|
332
|
+
)
|
|
333
|
+
|
|
334
|
+
image_mapping = self._data_provider.list_blob_sequences(
|
|
335
|
+
ctx,
|
|
336
|
+
experiment_id=experiment,
|
|
337
|
+
plugin_name=image_metadata.PLUGIN_NAME,
|
|
338
|
+
)
|
|
339
|
+
if image_mapping is None:
|
|
340
|
+
image_mapping = {}
|
|
341
|
+
image_mapping = self._filter_by_version(
|
|
342
|
+
image_mapping,
|
|
343
|
+
image_metadata.parse_plugin_metadata,
|
|
344
|
+
self._image_version_checker,
|
|
345
|
+
)
|
|
346
|
+
|
|
347
|
+
result = {}
|
|
348
|
+
result["scalars"] = _format_basic_mapping(scalar_mapping)
|
|
349
|
+
result["histograms"] = _format_basic_mapping(histogram_mapping)
|
|
350
|
+
result["images"] = _format_image_mapping(image_mapping)
|
|
351
|
+
return result
|
|
352
|
+
|
|
353
|
+
def _filter_by_version(self, mapping, parse_metadata, version_checker):
|
|
354
|
+
"""Filter `DataProvider.list_*` output by summary metadata version."""
|
|
355
|
+
result = {run: {} for run in mapping}
|
|
356
|
+
for run, tag_to_content in mapping.items():
|
|
357
|
+
for tag, metadatum in tag_to_content.items():
|
|
358
|
+
md = parse_metadata(metadatum.plugin_content)
|
|
359
|
+
if not version_checker.ok(md.version, run, tag):
|
|
360
|
+
continue
|
|
361
|
+
result[run][tag] = metadatum
|
|
362
|
+
return result
|
|
363
|
+
|
|
364
|
+
@wrappers.Request.application
|
|
365
|
+
def _serve_time_series(self, request):
|
|
366
|
+
ctx = plugin_util.context(request.environ)
|
|
367
|
+
experiment = plugin_util.experiment_id(request.environ)
|
|
368
|
+
if request.method == "POST":
|
|
369
|
+
series_requests_string = request.form.get("requests")
|
|
370
|
+
else:
|
|
371
|
+
series_requests_string = request.args.get("requests")
|
|
372
|
+
if not series_requests_string:
|
|
373
|
+
raise errors.InvalidArgumentError("Missing 'requests' field")
|
|
374
|
+
try:
|
|
375
|
+
series_requests = json.loads(series_requests_string)
|
|
376
|
+
except ValueError:
|
|
377
|
+
raise errors.InvalidArgumentError(
|
|
378
|
+
"Unable to parse 'requests' as JSON"
|
|
379
|
+
)
|
|
380
|
+
|
|
381
|
+
response = self._time_series_impl(ctx, experiment, series_requests)
|
|
382
|
+
return http_util.Respond(request, response, "application/json")
|
|
383
|
+
|
|
384
|
+
def _time_series_impl(self, ctx, experiment, series_requests):
|
|
385
|
+
"""Constructs a list of responses from a list of series requests.
|
|
386
|
+
|
|
387
|
+
Args:
|
|
388
|
+
ctx: A `tensorboard.context.RequestContext` value.
|
|
389
|
+
experiment: string ID of the request's experiment.
|
|
390
|
+
series_requests: a list of `TimeSeriesRequest` dicts (see http_api.md).
|
|
391
|
+
|
|
392
|
+
Returns:
|
|
393
|
+
A list of `TimeSeriesResponse` dicts (see http_api.md).
|
|
394
|
+
"""
|
|
395
|
+
responses = [
|
|
396
|
+
self._get_time_series(ctx, experiment, request)
|
|
397
|
+
for request in series_requests
|
|
398
|
+
]
|
|
399
|
+
return responses
|
|
400
|
+
|
|
401
|
+
def _create_base_response(self, series_request):
|
|
402
|
+
tag = series_request.get("tag")
|
|
403
|
+
run = series_request.get("run")
|
|
404
|
+
plugin = series_request.get("plugin")
|
|
405
|
+
sample = series_request.get("sample")
|
|
406
|
+
response = {"plugin": plugin, "tag": tag}
|
|
407
|
+
if isinstance(run, str):
|
|
408
|
+
response["run"] = run
|
|
409
|
+
if isinstance(sample, int):
|
|
410
|
+
response["sample"] = sample
|
|
411
|
+
|
|
412
|
+
return response
|
|
413
|
+
|
|
414
|
+
def _get_invalid_request_error(self, series_request):
|
|
415
|
+
tag = series_request.get("tag")
|
|
416
|
+
plugin = series_request.get("plugin")
|
|
417
|
+
run = series_request.get("run")
|
|
418
|
+
sample = series_request.get("sample")
|
|
419
|
+
|
|
420
|
+
if not isinstance(tag, str):
|
|
421
|
+
return "Missing tag"
|
|
422
|
+
|
|
423
|
+
if (
|
|
424
|
+
plugin != scalar_metadata.PLUGIN_NAME
|
|
425
|
+
and plugin != histogram_metadata.PLUGIN_NAME
|
|
426
|
+
and plugin != image_metadata.PLUGIN_NAME
|
|
427
|
+
):
|
|
428
|
+
return "Invalid plugin"
|
|
429
|
+
|
|
430
|
+
if plugin in _SINGLE_RUN_PLUGINS and not isinstance(run, str):
|
|
431
|
+
return "Missing run"
|
|
432
|
+
|
|
433
|
+
if plugin in _SAMPLED_PLUGINS and not isinstance(sample, int):
|
|
434
|
+
return "Missing sample"
|
|
435
|
+
|
|
436
|
+
return None
|
|
437
|
+
|
|
438
|
+
def _get_time_series(self, ctx, experiment, series_request):
|
|
439
|
+
"""Returns time series data for a given tag, plugin.
|
|
440
|
+
|
|
441
|
+
Args:
|
|
442
|
+
ctx: A `tensorboard.context.RequestContext` value.
|
|
443
|
+
experiment: string ID of the request's experiment.
|
|
444
|
+
series_request: a `TimeSeriesRequest` (see http_api.md).
|
|
445
|
+
|
|
446
|
+
Returns:
|
|
447
|
+
A `TimeSeriesResponse` dict (see http_api.md).
|
|
448
|
+
"""
|
|
449
|
+
tag = series_request.get("tag")
|
|
450
|
+
run = series_request.get("run")
|
|
451
|
+
plugin = series_request.get("plugin")
|
|
452
|
+
sample = series_request.get("sample")
|
|
453
|
+
response = self._create_base_response(series_request)
|
|
454
|
+
request_error = self._get_invalid_request_error(series_request)
|
|
455
|
+
if request_error:
|
|
456
|
+
response["error"] = request_error
|
|
457
|
+
return response
|
|
458
|
+
|
|
459
|
+
runs = [run] if run else None
|
|
460
|
+
run_to_series = None
|
|
461
|
+
if plugin == scalar_metadata.PLUGIN_NAME:
|
|
462
|
+
run_to_series = self._get_run_to_scalar_series(
|
|
463
|
+
ctx, experiment, tag, runs
|
|
464
|
+
)
|
|
465
|
+
|
|
466
|
+
if plugin == histogram_metadata.PLUGIN_NAME:
|
|
467
|
+
run_to_series = self._get_run_to_histogram_series(
|
|
468
|
+
ctx, experiment, tag, runs
|
|
469
|
+
)
|
|
470
|
+
|
|
471
|
+
if plugin == image_metadata.PLUGIN_NAME:
|
|
472
|
+
run_to_series = self._get_run_to_image_series(
|
|
473
|
+
ctx, experiment, tag, sample, runs
|
|
474
|
+
)
|
|
475
|
+
|
|
476
|
+
response["runToSeries"] = run_to_series
|
|
477
|
+
return response
|
|
478
|
+
|
|
479
|
+
def _get_run_to_scalar_series(self, ctx, experiment, tag, runs):
|
|
480
|
+
"""Builds a run-to-scalar-series dict for client consumption.
|
|
481
|
+
|
|
482
|
+
Args:
|
|
483
|
+
ctx: A `tensorboard.context.RequestContext` value.
|
|
484
|
+
experiment: a string experiment id.
|
|
485
|
+
tag: string of the requested tag.
|
|
486
|
+
runs: optional list of run names as strings.
|
|
487
|
+
|
|
488
|
+
Returns:
|
|
489
|
+
A map from string run names to `ScalarStepDatum` (see http_api.md).
|
|
490
|
+
"""
|
|
491
|
+
mapping = self._data_provider.read_scalars(
|
|
492
|
+
ctx,
|
|
493
|
+
experiment_id=experiment,
|
|
494
|
+
plugin_name=scalar_metadata.PLUGIN_NAME,
|
|
495
|
+
downsample=self._plugin_downsampling["scalars"],
|
|
496
|
+
run_tag_filter=provider.RunTagFilter(runs=runs, tags=[tag]),
|
|
497
|
+
)
|
|
498
|
+
|
|
499
|
+
run_to_series = {}
|
|
500
|
+
for result_run, tag_data in mapping.items():
|
|
501
|
+
if tag not in tag_data:
|
|
502
|
+
continue
|
|
503
|
+
values = [
|
|
504
|
+
{
|
|
505
|
+
"wallTime": datum.wall_time,
|
|
506
|
+
"step": datum.step,
|
|
507
|
+
"value": datum.value,
|
|
508
|
+
}
|
|
509
|
+
for datum in tag_data[tag]
|
|
510
|
+
]
|
|
511
|
+
run_to_series[result_run] = values
|
|
512
|
+
|
|
513
|
+
return run_to_series
|
|
514
|
+
|
|
515
|
+
def _format_histogram_datum_bins(self, datum):
|
|
516
|
+
"""Formats a histogram datum's bins for client consumption.
|
|
517
|
+
|
|
518
|
+
Args:
|
|
519
|
+
datum: a DataProvider's TensorDatum.
|
|
520
|
+
|
|
521
|
+
Returns:
|
|
522
|
+
A list of `HistogramBin`s (see http_api.md).
|
|
523
|
+
"""
|
|
524
|
+
numpy_list = datum.numpy.tolist()
|
|
525
|
+
bins = [{"min": x[0], "max": x[1], "count": x[2]} for x in numpy_list]
|
|
526
|
+
return bins
|
|
527
|
+
|
|
528
|
+
def _get_run_to_histogram_series(self, ctx, experiment, tag, runs):
|
|
529
|
+
"""Builds a run-to-histogram-series dict for client consumption.
|
|
530
|
+
|
|
531
|
+
Args:
|
|
532
|
+
ctx: A `tensorboard.context.RequestContext` value.
|
|
533
|
+
experiment: a string experiment id.
|
|
534
|
+
tag: string of the requested tag.
|
|
535
|
+
runs: optional list of run names as strings.
|
|
536
|
+
|
|
537
|
+
Returns:
|
|
538
|
+
A map from string run names to `HistogramStepDatum` (see http_api.md).
|
|
539
|
+
"""
|
|
540
|
+
mapping = self._data_provider.read_tensors(
|
|
541
|
+
ctx,
|
|
542
|
+
experiment_id=experiment,
|
|
543
|
+
plugin_name=histogram_metadata.PLUGIN_NAME,
|
|
544
|
+
downsample=self._plugin_downsampling["histograms"],
|
|
545
|
+
run_tag_filter=provider.RunTagFilter(runs=runs, tags=[tag]),
|
|
546
|
+
)
|
|
547
|
+
|
|
548
|
+
run_to_series = {}
|
|
549
|
+
for result_run, tag_data in mapping.items():
|
|
550
|
+
if tag not in tag_data:
|
|
551
|
+
continue
|
|
552
|
+
values = [
|
|
553
|
+
{
|
|
554
|
+
"wallTime": datum.wall_time,
|
|
555
|
+
"step": datum.step,
|
|
556
|
+
"bins": self._format_histogram_datum_bins(datum),
|
|
557
|
+
}
|
|
558
|
+
for datum in tag_data[tag]
|
|
559
|
+
]
|
|
560
|
+
run_to_series[result_run] = values
|
|
561
|
+
|
|
562
|
+
return run_to_series
|
|
563
|
+
|
|
564
|
+
def _get_run_to_image_series(self, ctx, experiment, tag, sample, runs):
|
|
565
|
+
"""Builds a run-to-image-series dict for client consumption.
|
|
566
|
+
|
|
567
|
+
Args:
|
|
568
|
+
ctx: A `tensorboard.context.RequestContext` value.
|
|
569
|
+
experiment: a string experiment id.
|
|
570
|
+
tag: string of the requested tag.
|
|
571
|
+
sample: zero-indexed integer for the requested sample.
|
|
572
|
+
runs: optional list of run names as strings.
|
|
573
|
+
|
|
574
|
+
Returns:
|
|
575
|
+
A `RunToSeries` dict (see http_api.md).
|
|
576
|
+
"""
|
|
577
|
+
mapping = self._data_provider.read_blob_sequences(
|
|
578
|
+
ctx,
|
|
579
|
+
experiment_id=experiment,
|
|
580
|
+
plugin_name=image_metadata.PLUGIN_NAME,
|
|
581
|
+
downsample=self._plugin_downsampling["images"],
|
|
582
|
+
run_tag_filter=provider.RunTagFilter(runs, tags=[tag]),
|
|
583
|
+
)
|
|
584
|
+
|
|
585
|
+
run_to_series = {}
|
|
586
|
+
for result_run, tag_data in mapping.items():
|
|
587
|
+
if tag not in tag_data:
|
|
588
|
+
continue
|
|
589
|
+
blob_sequence_datum_list = tag_data[tag]
|
|
590
|
+
series = _format_image_blob_sequence_datum(
|
|
591
|
+
blob_sequence_datum_list, sample
|
|
592
|
+
)
|
|
593
|
+
if series:
|
|
594
|
+
run_to_series[result_run] = series
|
|
595
|
+
|
|
596
|
+
return run_to_series
|
|
597
|
+
|
|
598
|
+
@wrappers.Request.application
|
|
599
|
+
def _serve_image_data(self, request):
|
|
600
|
+
"""Serves an individual image."""
|
|
601
|
+
ctx = plugin_util.context(request.environ)
|
|
602
|
+
blob_key = request.args["imageId"]
|
|
603
|
+
if not blob_key:
|
|
604
|
+
raise errors.InvalidArgumentError("Missing 'imageId' field")
|
|
605
|
+
|
|
606
|
+
data, mime_type = self._image_data_impl(ctx, blob_key)
|
|
607
|
+
return http_util.Respond(request, data, mime_type)
|
|
608
|
+
|
|
609
|
+
def _image_data_impl(self, ctx, blob_key):
|
|
610
|
+
"""Gets the image data for a blob key.
|
|
611
|
+
|
|
612
|
+
Args:
|
|
613
|
+
ctx: A `tensorboard.context.RequestContext` value.
|
|
614
|
+
blob_key: a string identifier for a DataProvider blob.
|
|
615
|
+
|
|
616
|
+
Returns:
|
|
617
|
+
A tuple containing:
|
|
618
|
+
data: a raw bytestring of the requested image's contents.
|
|
619
|
+
content_type: a string HTTP content type.
|
|
620
|
+
"""
|
|
621
|
+
data = self._data_provider.read_blob(ctx, blob_key=blob_key)
|
|
622
|
+
mime_type = img_mime_type_detector.from_bytes(data)
|
|
623
|
+
return (data, mime_type)
|
|
File without changes
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# Copyright 2017 The TensorFlow Authors. All Rights Reserved.
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
# ==============================================================================
|
|
15
|
+
"""Internal information about the pr_curves plugin."""
|
|
16
|
+
|
|
17
|
+
from tensorbored.compat.proto import summary_pb2
|
|
18
|
+
from tensorbored.plugins.pr_curve import plugin_data_pb2
|
|
19
|
+
|
|
20
|
+
PLUGIN_NAME = "pr_curves"
|
|
21
|
+
|
|
22
|
+
# Indices for obtaining various values from the tensor stored in a summary.
|
|
23
|
+
TRUE_POSITIVES_INDEX = 0
|
|
24
|
+
FALSE_POSITIVES_INDEX = 1
|
|
25
|
+
TRUE_NEGATIVES_INDEX = 2
|
|
26
|
+
FALSE_NEGATIVES_INDEX = 3
|
|
27
|
+
PRECISION_INDEX = 4
|
|
28
|
+
RECALL_INDEX = 5
|
|
29
|
+
|
|
30
|
+
# The most recent value for the `version` field of the
|
|
31
|
+
# `PrCurvePluginData` proto.
|
|
32
|
+
PROTO_VERSION = 0
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def create_summary_metadata(display_name, description, num_thresholds):
|
|
36
|
+
"""Create a `summary_pb2.SummaryMetadata` proto for pr_curves plugin data.
|
|
37
|
+
|
|
38
|
+
Arguments:
|
|
39
|
+
display_name: The display name used in TensorBoard.
|
|
40
|
+
description: The description to show in TensorBoard.
|
|
41
|
+
num_thresholds: The number of thresholds to use for PR curves.
|
|
42
|
+
|
|
43
|
+
Returns:
|
|
44
|
+
A `summary_pb2.SummaryMetadata` protobuf object.
|
|
45
|
+
"""
|
|
46
|
+
pr_curve_plugin_data = plugin_data_pb2.PrCurvePluginData(
|
|
47
|
+
version=PROTO_VERSION, num_thresholds=num_thresholds
|
|
48
|
+
)
|
|
49
|
+
content = pr_curve_plugin_data.SerializeToString()
|
|
50
|
+
return summary_pb2.SummaryMetadata(
|
|
51
|
+
display_name=display_name,
|
|
52
|
+
summary_description=description,
|
|
53
|
+
plugin_data=summary_pb2.SummaryMetadata.PluginData(
|
|
54
|
+
plugin_name=PLUGIN_NAME, content=content
|
|
55
|
+
),
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def parse_plugin_metadata(content):
|
|
60
|
+
"""Parse summary metadata to a Python object.
|
|
61
|
+
|
|
62
|
+
Arguments:
|
|
63
|
+
content: The `content` field of a `SummaryMetadata` proto
|
|
64
|
+
corresponding to the pr_curves plugin.
|
|
65
|
+
|
|
66
|
+
Returns:
|
|
67
|
+
A `PrCurvesPlugin` protobuf object.
|
|
68
|
+
"""
|
|
69
|
+
if not isinstance(content, bytes):
|
|
70
|
+
raise TypeError("Content type must be bytes")
|
|
71
|
+
result = plugin_data_pb2.PrCurvePluginData.FromString(content)
|
|
72
|
+
if result.version == 0:
|
|
73
|
+
return result
|
|
74
|
+
# No other versions known at this time, so no migrations to do.
|
|
75
|
+
return result
|