aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar Nikhil Thorat <nsthorat@google.com>2016-12-14 07:46:46 -0800
committerGravatar TensorFlower Gardener <gardener@tensorflow.org>2016-12-14 08:07:36 -0800
commit72db22494e491cdf7b18ea9736b3d2fb87b6b28c (patch)
tree759e6440f125da7d648893bb62dcf1c8ce2278ff
parent5adafddfbe0628e4de9ecf72c204ae8828539dfb (diff)
Improve error messages in the embedding projector.
- When we fail to parse the bytes, give a meaningful error message. - When we fail to make requests to tensors / metadata / config, give a meaningful error message Change: 142016403
-rw-r--r--tensorflow/tensorboard/components/vz_projector/data-provider-demo.ts11
-rw-r--r--tensorflow/tensorboard/components/vz_projector/data-provider-server.ts4
-rw-r--r--tensorflow/tensorboard/components/vz_projector/data-provider.ts13
-rw-r--r--tensorflow/tensorboard/components/vz_projector/data.ts38
-rw-r--r--tensorflow/tensorboard/components/vz_projector/logging.ts4
-rw-r--r--tensorflow/tensorboard/components/vz_projector/vz-projector.ts5
6 files changed, 61 insertions, 14 deletions
diff --git a/tensorflow/tensorboard/components/vz_projector/data-provider-demo.ts b/tensorflow/tensorboard/components/vz_projector/data-provider-demo.ts
index 0d54526db1..bf1bc0b255 100644
--- a/tensorflow/tensorboard/components/vz_projector/data-provider-demo.ts
+++ b/tensorflow/tensorboard/components/vz_projector/data-provider-demo.ts
@@ -49,7 +49,14 @@ export class DemoDataProvider implements DataProvider {
let msgId = logging.setModalMessage('Fetching projector config...');
d3.json(this.projectorConfigPath, (err, projectorConfig) => {
if (err) {
- logging.setErrorMessage(err.responseText);
+ let errorMessage = err;
+ // If the error is a valid XMLHttpResponse, it's possible this is a
+ // cross-origin error.
+ if (err.responseText != null) {
+ errorMessage = 'Cannot fetch projector config, possibly a ' +
+ 'Cross-Origin request error.';
+ }
+ logging.setErrorMessage(errorMessage, 'fetching projector config');
return;
}
logging.setModalMessage(null, msgId);
@@ -71,7 +78,7 @@ export class DemoDataProvider implements DataProvider {
logging.setModalMessage('Fetching tensors...', TENSORS_MSG_ID);
d3.text(url, (error: any, dataString: string) => {
if (error) {
- logging.setErrorMessage(error.responseText);
+ logging.setErrorMessage(error.responseText, 'fetching tensors');
return;
}
dataProvider.parseTensors(dataString).then(points => {
diff --git a/tensorflow/tensorboard/components/vz_projector/data-provider-server.ts b/tensorflow/tensorboard/components/vz_projector/data-provider-server.ts
index 1ce68a956f..ff535468de 100644
--- a/tensorflow/tensorboard/components/vz_projector/data-provider-server.ts
+++ b/tensorflow/tensorboard/components/vz_projector/data-provider-server.ts
@@ -52,7 +52,7 @@ export class ServerDataProvider implements DataProvider {
let msgId = logging.setModalMessage('Fetching runs...');
d3.json(`${this.routePrefix}/runs`, (err, runs: string[]) => {
if (err) {
- logging.setErrorMessage(err.responseText);
+ logging.setErrorMessage(err.responseText, 'fetching runs');
return;
}
logging.setModalMessage(null, msgId);
@@ -71,7 +71,7 @@ export class ServerDataProvider implements DataProvider {
d3.json(`${this.routePrefix}/info?run=${run}`, (err,
config: ProjectorConfig) => {
if (err) {
- logging.setErrorMessage(err.responseText);
+ logging.setErrorMessage(err.responseText, 'fetching projector config');
return;
}
logging.setModalMessage(null, msgId);
diff --git a/tensorflow/tensorboard/components/vz_projector/data-provider.ts b/tensorflow/tensorboard/components/vz_projector/data-provider.ts
index 9c6d675fc9..b8db61cf7c 100644
--- a/tensorflow/tensorboard/components/vz_projector/data-provider.ts
+++ b/tensorflow/tensorboard/components/vz_projector/data-provider.ts
@@ -105,10 +105,17 @@ export function retrieveTensorAsBytes(
xhr.onload = () => {
if (xhr.status !== 200) {
let msg = String.fromCharCode.apply(null, new Uint8Array(xhr.response));
- logging.setErrorMessage(msg);
+ logging.setErrorMessage(msg, 'fetching tensors');
return;
}
- let data = new Float32Array(xhr.response);
+ let data: Float32Array;
+ try {
+ data = new Float32Array(xhr.response);
+ } catch (e) {
+ logging.setErrorMessage(e, 'parsing tensor bytes');
+ return;
+ }
+
let dim = embedding.tensorShape[1];
let N = data.length / dim;
if (embedding.tensorShape[0] > N) {
@@ -311,7 +318,7 @@ export function retrieveSpriteAndMetadataInfo(metadataPath: string,
logging.setModalMessage('Fetching metadata...', METADATA_MSG_ID);
d3.text(metadataPath, (err: any, rawMetadata: string) => {
if (err) {
- logging.setErrorMessage(err.responseText);
+ logging.setErrorMessage(err.responseText, 'fetching metadata');
reject(err);
return;
}
diff --git a/tensorflow/tensorboard/components/vz_projector/data.ts b/tensorflow/tensorboard/components/vz_projector/data.ts
index bae0a33e9e..f39ffb7af1 100644
--- a/tensorflow/tensorboard/components/vz_projector/data.ts
+++ b/tensorflow/tensorboard/components/vz_projector/data.ts
@@ -339,15 +339,45 @@ export class DataSet {
});
}
- mergeMetadata(metadata: SpriteAndMetadataInfo) {
+ /**
+ * Merges metadata to the dataset and returns whether it succeeded.
+ */
+ mergeMetadata(metadata: SpriteAndMetadataInfo): boolean {
if (metadata.pointsInfo.length !== this.points.length) {
- logging.setWarningMessage(
- `Number of tensors (${this.points.length}) do not match` +
- ` the number of lines in metadata (${metadata.pointsInfo.length}).`);
+ let errorMessage = `Number of tensors (${this.points.length}) do not` +
+ ` match the number of lines in metadata` +
+ ` (${metadata.pointsInfo.length}).`;
+
+ if (metadata.stats.length === 1 &&
+ this.points.length + 1 === metadata.pointsInfo.length) {
+ // If there is only one column of metadata and the number of points is
+ // exactly one less than the number of metadata lines, this is due to an
+ // unnecessary header line in the metadata and we can show a meaningful
+ // error.
+ logging.setErrorMessage(
+ errorMessage + ' Single column metadata should not have a header ' +
+ 'row.',
+ 'merging metadata');
+ return false;
+ } else if (
+ metadata.stats.length > 1 &&
+ this.points.length - 1 === metadata.pointsInfo.length) {
+ // If there are multiple columns of metadata and the number of points is
+ // exactly one greater than the number of lines in the metadata, this
+ // means there is a missing metadata header.
+ logging.setErrorMessage(
+ errorMessage + ' Multi-column metadata should have a header ' +
+ 'row with column labels.',
+ 'merging metadata');
+ return false;
+ }
+
+ logging.setWarningMessage(errorMessage);
}
this.spriteAndMetadataInfo = metadata;
metadata.pointsInfo.slice(0, this.points.length)
.forEach((m, i) => this.points[i].metadata = m);
+ return true;
}
stopTSNE() {
diff --git a/tensorflow/tensorboard/components/vz_projector/logging.ts b/tensorflow/tensorboard/components/vz_projector/logging.ts
index 7cfaf84a09..b51b702653 100644
--- a/tensorflow/tensorboard/components/vz_projector/logging.ts
+++ b/tensorflow/tensorboard/components/vz_projector/logging.ts
@@ -85,8 +85,8 @@ export function setModalMessage(
return id;
}
-export function setErrorMessage(errMsg: string) {
- setModalMessage(errMsg, null, 'Error', true);
+export function setErrorMessage(errMsg: string, task?: string) {
+ setModalMessage(errMsg, null, 'Error ' + (task != null ? task : ''), true);
}
/**
diff --git a/tensorflow/tensorboard/components/vz_projector/vz-projector.ts b/tensorflow/tensorboard/components/vz_projector/vz-projector.ts
index b559f01af3..107062ea07 100644
--- a/tensorflow/tensorboard/components/vz_projector/vz-projector.ts
+++ b/tensorflow/tensorboard/components/vz_projector/vz-projector.ts
@@ -171,7 +171,10 @@ export class Projector extends ProjectorPolymer implements
spriteAndMetadata.pointsInfo = pointsInfo;
spriteAndMetadata.stats = stats;
}
- ds.mergeMetadata(spriteAndMetadata);
+ let metadataMergeSucceeded = ds.mergeMetadata(spriteAndMetadata);
+ if (!metadataMergeSucceeded) {
+ return;
+ }
}
if (this.projectorScatterPlotAdapter != null) {
if (ds == null) {