I am done

This commit is contained in:
2024-10-30 22:14:35 +01:00
parent 720dc28c09
commit 40e2a747cf
36901 changed files with 5011519 additions and 0 deletions

View File

@ -0,0 +1,63 @@
# Copyright 2017 The TensorFlow Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ==============================================================================
"""Internal information about the text plugin."""
from tensorboard.compat.proto import summary_pb2
from tensorboard.plugins.text import plugin_data_pb2
PLUGIN_NAME = "text"
# The most recent value for the `version` field of the
# `TextPluginData` proto.
PROTO_VERSION = 0
def create_summary_metadata(display_name, description):
"""Create a `summary_pb2.SummaryMetadata` proto for text plugin data.
Returns:
A `summary_pb2.SummaryMetadata` protobuf object.
"""
content = plugin_data_pb2.TextPluginData(version=PROTO_VERSION)
metadata = summary_pb2.SummaryMetadata(
display_name=display_name,
summary_description=description,
plugin_data=summary_pb2.SummaryMetadata.PluginData(
plugin_name=PLUGIN_NAME, content=content.SerializeToString()
),
)
return metadata
def parse_plugin_metadata(content):
"""Parse summary metadata to a Python object.
Arguments:
content: The `content` field of a `SummaryMetadata` proto corresponding to
the text plugin.
Returns:
A `TextPluginData` protobuf object.
"""
if not isinstance(content, bytes):
raise TypeError("Content type must be bytes")
if content == b"{}":
# Old-style JSON format. Equivalent to an all-default proto.
return plugin_data_pb2.TextPluginData()
result = plugin_data_pb2.TextPluginData.FromString(content)
if result.version == 0:
return result
# No other versions known at this time, so no migrations to do.
return result

View File

@ -0,0 +1,34 @@
# -*- coding: utf-8 -*-
# Generated by the protocol buffer compiler. DO NOT EDIT!
# source: tensorboard/plugins/text/plugin_data.proto
"""Generated protocol buffer code."""
from google.protobuf import descriptor as _descriptor
from google.protobuf import descriptor_pool as _descriptor_pool
from google.protobuf import message as _message
from google.protobuf import reflection as _reflection
from google.protobuf import symbol_database as _symbol_database
# @@protoc_insertion_point(imports)
_sym_db = _symbol_database.Default()
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n*tensorboard/plugins/text/plugin_data.proto\x12\x0btensorboard\"!\n\x0eTextPluginData\x12\x0f\n\x07version\x18\x01 \x01(\x05\x62\x06proto3')
_TEXTPLUGINDATA = DESCRIPTOR.message_types_by_name['TextPluginData']
TextPluginData = _reflection.GeneratedProtocolMessageType('TextPluginData', (_message.Message,), {
'DESCRIPTOR' : _TEXTPLUGINDATA,
'__module__' : 'tensorboard.plugins.text.plugin_data_pb2'
# @@protoc_insertion_point(class_scope:tensorboard.TextPluginData)
})
_sym_db.RegisterMessage(TextPluginData)
if _descriptor._USE_C_DESCRIPTORS == False:
DESCRIPTOR._options = None
_TEXTPLUGINDATA._serialized_start=59
_TEXTPLUGINDATA._serialized_end=92
# @@protoc_insertion_point(module_scope)

View File

@ -0,0 +1,116 @@
# Copyright 2017 The TensorFlow Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ==============================================================================
"""Text summaries and TensorFlow operations to create them."""
from tensorboard.plugins.text import metadata
from tensorboard.plugins.text import summary_v2
# Export V2 versions.
text = summary_v2.text
text_pb = summary_v2.text_pb
def op(name, data, display_name=None, description=None, collections=None):
"""Create a legacy text summary op.
Text data summarized via this plugin will be visible in the Text Dashboard
in TensorBoard. The standard TensorBoard Text Dashboard will render markdown
in the strings, and will automatically organize 1D and 2D tensors into tables.
If a tensor with more than 2 dimensions is provided, a 2D subarray will be
displayed along with a warning message. (Note that this behavior is not
intrinsic to the text summary API, but rather to the default TensorBoard text
plugin.)
Args:
name: A name for the generated node. Will also serve as a series name in
TensorBoard.
data: A string-type Tensor to summarize. The text must be encoded in UTF-8.
display_name: Optional name for this summary in TensorBoard, as a
constant `str`. Defaults to `name`.
description: Optional long-form description for this summary, as a
constant `str`. Markdown is supported. Defaults to empty.
collections: Optional list of ops.GraphKeys. The collections to which to add
the summary. Defaults to [Graph Keys.SUMMARIES].
Returns:
A TensorSummary op that is configured so that TensorBoard will recognize
that it contains textual data. The TensorSummary is a scalar `Tensor` of
type `string` which contains `Summary` protobufs.
Raises:
ValueError: If tensor has the wrong type.
"""
# TODO(nickfelt): remove on-demand imports once dep situation is fixed.
import tensorflow.compat.v1 as tf
if display_name is None:
display_name = name
summary_metadata = metadata.create_summary_metadata(
display_name=display_name, description=description
)
with tf.name_scope(name):
with tf.control_dependencies([tf.assert_type(data, tf.string)]):
return tf.summary.tensor_summary(
name="text_summary",
tensor=data,
collections=collections,
summary_metadata=summary_metadata,
)
def pb(name, data, display_name=None, description=None):
"""Create a legacy text summary protobuf.
Arguments:
name: A name for the generated node. Will also serve as a series name in
TensorBoard.
data: A Python bytestring (of type bytes), or Unicode string. Or a numpy
data array of those types.
display_name: Optional name for this summary in TensorBoard, as a
`str`. Defaults to `name`.
description: Optional long-form description for this summary, as a
`str`. Markdown is supported. Defaults to empty.
Raises:
ValueError: If the type of the data is unsupported.
Returns:
A `tf.Summary` protobuf object.
"""
# TODO(nickfelt): remove on-demand imports once dep situation is fixed.
import tensorflow.compat.v1 as tf
try:
tensor = tf.make_tensor_proto(data, dtype=tf.string)
except TypeError as e:
raise ValueError(e)
if display_name is None:
display_name = name
summary_metadata = metadata.create_summary_metadata(
display_name=display_name, description=description
)
tf_summary_metadata = tf.SummaryMetadata.FromString(
summary_metadata.SerializeToString()
)
summary = tf.Summary()
summary.value.add(
tag="%s/text_summary" % name,
metadata=tf_summary_metadata,
tensor=tensor,
)
return summary

View File

@ -0,0 +1,125 @@
# Copyright 2018 The TensorFlow Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ==============================================================================
"""Text summaries and TensorFlow operations to create them, V2 versions."""
import numpy as np
from tensorboard.compat import tf2 as tf
from tensorboard.compat.proto import summary_pb2
from tensorboard.plugins.text import metadata
from tensorboard.util import tensor_util
def text(name, data, step=None, description=None):
r"""Write a text summary.
See also `tf.summary.scalar`, `tf.summary.SummaryWriter`, `tf.summary.image`.
Writes text Tensor values for later visualization and analysis in TensorBoard.
Writes go to the current default summary writer. Like `tf.summary.scalar`
points, text points are each associated with a `step` and a `name`.
All the points with the same `name` constitute a time series of text values.
For Example:
```python
test_summary_writer = tf.summary.create_file_writer('test/logdir')
with test_summary_writer.as_default():
tf.summary.text('first_text', 'hello world!', step=0)
tf.summary.text('first_text', 'nice to meet you!', step=1)
```
The text summary can also contain Markdown, and TensorBoard will render the text
as such.
```python
with test_summary_writer.as_default():
text_data = '''
| *hello* | *there* |
|---------|---------|
| this | is |
| a | table |
'''
text_data = '\n'.join(l.strip() for l in text_data.splitlines())
tf.summary.text('markdown_text', text_data, step=0)
```
Since text is Tensor valued, each text point may be a Tensor of string values.
rank-1 and rank-2 Tensors are rendered as tables in TensorBoard. For higher ranked
Tensors, you'll see just a 2D slice of the data. To avoid this, reshape the Tensor
to at most rank-2 prior to passing it to this function.
Demo notebook at
["Displaying text data in TensorBoard"](https://www.tensorflow.org/tensorboard/text_summaries).
Arguments:
name: A name for this summary. The summary tag used for TensorBoard will
be this name prefixed by any active name scopes.
data: A UTF-8 string Tensor value.
step: Explicit `int64`-castable monotonic step value for this summary. If
omitted, this defaults to `tf.summary.experimental.get_step()`, which must
not be None.
description: Optional long-form description for this summary, as a
constant `str`. Markdown is supported. Defaults to empty.
Returns:
True on success, or false if no summary was emitted because no default
summary writer was available.
Raises:
ValueError: if a default writer exists, but no step was provided and
`tf.summary.experimental.get_step()` is None.
"""
summary_metadata = metadata.create_summary_metadata(
display_name=None, description=description
)
# TODO(https://github.com/tensorflow/tensorboard/issues/2109): remove fallback
summary_scope = (
getattr(tf.summary.experimental, "summary_scope", None)
or tf.summary.summary_scope
)
with summary_scope(name, "text_summary", values=[data, step]) as (tag, _):
tf.debugging.assert_type(data, tf.string)
return tf.summary.write(
tag=tag, tensor=data, step=step, metadata=summary_metadata
)
def text_pb(tag, data, description=None):
"""Create a text tf.Summary protobuf.
Arguments:
tag: String tag for the summary.
data: A Python bytestring (of type bytes), a Unicode string, or a numpy data
array of those types.
description: Optional long-form description for this summary, as a `str`.
Markdown is supported. Defaults to empty.
Raises:
TypeError: If the type of the data is unsupported.
Returns:
A `tf.Summary` protobuf object.
"""
try:
tensor = tensor_util.make_tensor_proto(data, dtype=np.object_)
except TypeError as e:
raise TypeError("tensor must be of type string", e)
summary_metadata = metadata.create_summary_metadata(
display_name=None, description=description
)
summary = summary_pb2.Summary()
summary.value.add(tag=tag, metadata=summary_metadata, tensor=tensor)
return summary

View File

@ -0,0 +1,289 @@
# Copyright 2017 The TensorFlow Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ==============================================================================
"""The TensorBoard Text plugin."""
import textwrap
# pylint: disable=g-bad-import-order
# Necessary for an internal test with special behavior for numpy.
import numpy as np
# pylint: enable=g-bad-import-order
from werkzeug import wrappers
from tensorboard import plugin_util
from tensorboard.backend import http_util
from tensorboard.data import provider
from tensorboard.plugins import base_plugin
from tensorboard.plugins.text import metadata
# HTTP routes
TAGS_ROUTE = "/tags"
TEXT_ROUTE = "/text"
WARNING_TEMPLATE = textwrap.dedent(
"""\
**Warning:** This text summary contained data of dimensionality %d, but only \
2d tables are supported. Showing a 2d slice of the data instead."""
)
_DEFAULT_DOWNSAMPLING = 100 # text tensors per time series
def make_table_row(contents, tag="td"):
"""Given an iterable of string contents, make a table row.
Args:
contents: An iterable yielding strings.
tag: The tag to place contents in. Defaults to 'td', you might want 'th'.
Returns:
A string containing the content strings, organized into a table row.
Example: make_table_row(['one', 'two', 'three']) == '''
<tr>
<td>one</td>
<td>two</td>
<td>three</td>
</tr>'''
"""
columns = ("<%s>%s</%s>\n" % (tag, s, tag) for s in contents)
return "<tr>\n" + "".join(columns) + "</tr>\n"
def make_table(contents, headers=None):
"""Given a numpy ndarray of strings, concatenate them into a html table.
Args:
contents: A np.ndarray of strings. May be 1d or 2d. In the 1d case, the
table is laid out vertically (i.e. row-major).
headers: A np.ndarray or list of string header names for the table.
Returns:
A string containing all of the content strings, organized into a table.
Raises:
ValueError: If contents is not a np.ndarray.
ValueError: If contents is not 1d or 2d.
ValueError: If contents is empty.
ValueError: If headers is present and not a list, tuple, or ndarray.
ValueError: If headers is not 1d.
ValueError: If number of elements in headers does not correspond to number
of columns in contents.
"""
if not isinstance(contents, np.ndarray):
raise ValueError("make_table contents must be a numpy ndarray")
if contents.ndim not in [1, 2]:
raise ValueError(
"make_table requires a 1d or 2d numpy array, was %dd"
% contents.ndim
)
if headers:
if isinstance(headers, (list, tuple)):
headers = np.array(headers)
if not isinstance(headers, np.ndarray):
raise ValueError(
"Could not convert headers %s into np.ndarray" % headers
)
if headers.ndim != 1:
raise ValueError("Headers must be 1d, is %dd" % headers.ndim)
expected_n_columns = contents.shape[1] if contents.ndim == 2 else 1
if headers.shape[0] != expected_n_columns:
raise ValueError(
"Number of headers %d must match number of columns %d"
% (headers.shape[0], expected_n_columns)
)
header = "<thead>\n%s</thead>\n" % make_table_row(headers, tag="th")
else:
header = ""
n_rows = contents.shape[0]
if contents.ndim == 1:
# If it's a vector, we need to wrap each element in a new list, otherwise
# we would turn the string itself into a row (see test code)
rows = (make_table_row([contents[i]]) for i in range(n_rows))
else:
rows = (make_table_row(contents[i, :]) for i in range(n_rows))
return "<table>\n%s<tbody>\n%s</tbody>\n</table>" % (header, "".join(rows))
def reduce_to_2d(arr):
"""Given a np.npdarray with nDims > 2, reduce it to 2d.
It does this by selecting the zeroth coordinate for every dimension greater
than two.
Args:
arr: a numpy ndarray of dimension at least 2.
Returns:
A two-dimensional subarray from the input array.
Raises:
ValueError: If the argument is not a numpy ndarray, or the dimensionality
is too low.
"""
if not isinstance(arr, np.ndarray):
raise ValueError("reduce_to_2d requires a numpy.ndarray")
ndims = len(arr.shape)
if ndims < 2:
raise ValueError("reduce_to_2d requires an array of dimensionality >=2")
# slice(None) is equivalent to `:`, so we take arr[0,0,...0,:,:]
slices = ([0] * (ndims - 2)) + [slice(None), slice(None)]
return arr[tuple(slices)]
def text_array_to_html(text_arr, enable_markdown):
"""Take a numpy.ndarray containing strings, and convert it into html.
If the ndarray contains a single scalar string, that string is converted to
html via our sanitized markdown parser. If it contains an array of strings,
the strings are individually converted to html and then composed into a table
using make_table. If the array contains dimensionality greater than 2,
all but two of the dimensions are removed, and a warning message is prefixed
to the table.
Args:
text_arr: A numpy.ndarray containing strings.
enable_markdown: boolean, whether to enable Markdown
Returns:
The array converted to html.
"""
if not text_arr.shape:
# It is a scalar. No need to put it in a table.
if enable_markdown:
return plugin_util.markdown_to_safe_html(text_arr.item())
else:
return plugin_util.safe_html(text_arr.item())
warning = ""
if len(text_arr.shape) > 2:
warning = plugin_util.markdown_to_safe_html(
WARNING_TEMPLATE % len(text_arr.shape)
)
text_arr = reduce_to_2d(text_arr)
if enable_markdown:
table = plugin_util.markdowns_to_safe_html(
text_arr.reshape(-1),
lambda xs: make_table(np.array(xs).reshape(text_arr.shape)),
)
else:
# Convert utf-8 bytes to str. The built-in np.char.decode doesn't work on
# object arrays, and converting to an numpy chararray is lossy.
decode = lambda bs: bs.decode("utf-8") if isinstance(bs, bytes) else bs
text_arr_str = np.array(
[decode(bs) for bs in text_arr.reshape(-1)]
).reshape(text_arr.shape)
table = plugin_util.safe_html(make_table(text_arr_str))
return warning + table
def process_event(wall_time, step, string_ndarray, enable_markdown):
"""Convert a text event into a JSON-compatible response."""
html = text_array_to_html(string_ndarray, enable_markdown)
return {
"wall_time": wall_time,
"step": step,
"text": html,
}
class TextPlugin(base_plugin.TBPlugin):
"""Text Plugin for TensorBoard."""
plugin_name = metadata.PLUGIN_NAME
def __init__(self, context):
"""Instantiates TextPlugin via TensorBoard core.
Args:
context: A base_plugin.TBContext instance.
"""
self._downsample_to = (context.sampling_hints or {}).get(
self.plugin_name, _DEFAULT_DOWNSAMPLING
)
self._data_provider = context.data_provider
self._version_checker = plugin_util._MetadataVersionChecker(
data_kind="text",
latest_known_version=0,
)
def is_active(self):
return False # `list_plugins` as called by TB core suffices
def frontend_metadata(self):
return base_plugin.FrontendMetadata(element_name="tf-text-dashboard")
def index_impl(self, ctx, experiment):
mapping = self._data_provider.list_tensors(
ctx,
experiment_id=experiment,
plugin_name=metadata.PLUGIN_NAME,
)
result = {run: [] for run in mapping}
for run, tag_to_content in mapping.items():
for tag, metadatum in tag_to_content.items():
md = metadata.parse_plugin_metadata(metadatum.plugin_content)
if not self._version_checker.ok(md.version, run, tag):
continue
result[run].append(tag)
return result
@wrappers.Request.application
def tags_route(self, request):
ctx = plugin_util.context(request.environ)
experiment = plugin_util.experiment_id(request.environ)
index = self.index_impl(ctx, experiment)
return http_util.Respond(request, index, "application/json")
def text_impl(self, ctx, run, tag, experiment, enable_markdown):
all_text = self._data_provider.read_tensors(
ctx,
experiment_id=experiment,
plugin_name=metadata.PLUGIN_NAME,
downsample=self._downsample_to,
run_tag_filter=provider.RunTagFilter(runs=[run], tags=[tag]),
)
text = all_text.get(run, {}).get(tag, None)
if text is None:
return []
return [
process_event(d.wall_time, d.step, d.numpy, enable_markdown)
for d in text
]
@wrappers.Request.application
def text_route(self, request):
ctx = plugin_util.context(request.environ)
experiment = plugin_util.experiment_id(request.environ)
run = request.args.get("run")
tag = request.args.get("tag")
markdown_arg = request.args.get("markdown")
enable_markdown = markdown_arg != "false" # Default to enabled.
response = self.text_impl(ctx, run, tag, experiment, enable_markdown)
return http_util.Respond(request, response, "application/json")
def get_plugin_apps(self):
return {
TAGS_ROUTE: self.tags_route,
TEXT_ROUTE: self.text_route,
}