Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 33 additions & 16 deletions tensorboard/plugins/debugger/tensor_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,25 +82,42 @@ def array_view(array, slicing=None, mapping=None):
"""

dtype = str(array.dtype)
# String-type TensorFlow Tensors are represented as object-type arrays in
# numpy. We map the type name back to 'string' for clarity.
if dtype == 'object':
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wow, is this the only way to identify a numpy array that represents a TensorFlow string tensor? Do the methods in this StackOverflow post work at all?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For a tensorflow string-type tensor, the corresponding numpy array has the dtype "object", i.e., different from a regular string-type array in numpy. This is how tensorflow is and we need to live with that.

dtype = 'string'
sliced_array = (array[command_parser._parse_slices(slicing)] if slicing
else array)
shape = sliced_array.shape
if mapping == "image/png":
if len(sliced_array.shape) == 2:
return dtype, shape, array_to_base64_png(sliced_array)
elif len(sliced_array.shape) == 3:
raise NotImplementedError(
"image/png mapping for 3D array has not been implemented")
else:
raise ValueError("Invalid rank for image/png mapping: %d" %
len(sliced_array.shape))
elif mapping == 'health-pill':
health_pill = health_pill_calc.calc_health_pill(array)
return dtype, shape, health_pill
elif mapping is None or mapping == '' or mapping.lower() == 'none':
return dtype, shape, sliced_array.tolist()

if np.isscalar(sliced_array) and str(dtype) == 'string':
# When a string Tensor (for which dtype is 'object') is sliced down to only
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this logic exist because numpy.reshape(sliced_array, array.shape) could separate the string into several strings?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The logic in this special case is because in the case of a string scalar, sliced array is just a Python string, i.e., not a numpy array. This is specific to the scalar case, i.e., if you have vector of strings with length > 1, you still get a numpy array.

# one element, it becomes a string, instead of an numpy array.
# We preserve the dimensionality of original array in the returned shape
# and slice.
ndims = len(array.shape)
slice_shape = []
for _ in range(ndims):
sliced_array = [sliced_array]
slice_shape.append(1)
return dtype, tuple(slice_shape), sliced_array
else:
raise ValueError("Invalid mapping: %s" % mapping)
shape = sliced_array.shape
if mapping == "image/png":
if len(sliced_array.shape) == 2:
return dtype, shape, array_to_base64_png(sliced_array)
elif len(sliced_array.shape) == 3:
raise NotImplementedError(
"image/png mapping for 3D array has not been implemented")
else:
raise ValueError("Invalid rank for image/png mapping: %d" %
len(sliced_array.shape))
elif mapping == 'health-pill':
health_pill = health_pill_calc.calc_health_pill(array)
return dtype, shape, health_pill
elif mapping is None or mapping == '' or mapping.lower() == 'none':
return dtype, shape, sliced_array.tolist()
else:
raise ValueError("Invalid mapping: %s" % mapping)


IMAGE_COLOR_CHANNELS = 3
Expand Down
36 changes: 36 additions & 0 deletions tensorboard/plugins/debugger/tensor_helper_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,42 @@ def testImagePngMappingWorksForArrayWithInfAndNaN(self):
self.assertAllClose([255, 255, 255], decoded_x[1, 1, :]) # 3.3.
self.assertAllClose(tensor_helper.NAN_RGB, decoded_x[1, 2, :]) # nan.

def testArrayViewSlicingDownNumericTensorToOneElement(self):
x = np.array([[1.1, 2.2, np.inf], [-np.inf, 3.3, np.nan]], dtype=np.float32)
dtype, shape, data = tensor_helper.array_view(x, slicing='[0,0]')
self.assertEqual('float32', dtype)
self.assertEqual(tuple(), shape)
self.assertTrue(np.allclose(1.1, data))

def testArrayViewSlicingStringTensorToNonScalarSubArray(self):
# Construct a numpy array that corresponds to a TensorFlow string tensor
# value.
x = np.array([['foo', 'bar', 'qux'], ['baz', 'corge', 'grault']],
dtype=np.object)
dtype, shape, data = tensor_helper.array_view(x, slicing='[:2, :2]')
self.assertEqual('string', dtype)
self.assertEqual((2, 2), shape)
self.assertEqual([['foo', 'bar'], ['baz', 'corge']], data)

def testArrayViewSlicingStringTensorToScalar(self):
# Construct a numpy array that corresponds to a TensorFlow string tensor
# value.
x = np.array([['foo', 'bar', 'qux'], ['baz', 'corge', 'grault']],
dtype=np.object)
dtype, shape, data = tensor_helper.array_view(x, slicing='[1, 1]')
self.assertEqual('string', dtype)
self.assertEqual((1, 1), shape)
self.assertEqual([['corge']], data)

def testArrayViewOnScalarString(self):
# Construct a numpy scalar that corresponds to a TensorFlow string tensor
# value.
x = np.array('foo', dtype=np.object)
dtype, shape, data = tensor_helper.array_view(x)
self.assertEqual('string', dtype)
self.assertEqual(tuple(), shape)
self.assertEqual('foo', data)

def testImagePngMappingWorksForArrayWithOnlyInfAndNaN(self):
x = np.array([[np.nan, -np.inf], [np.inf, np.nan]], dtype=np.float32)
dtype, shape, data = tensor_helper.array_view(x, mapping="image/png")
Expand Down