From d6388ab74589063b6143f69cdbf65cbf70825e7e Mon Sep 17 00:00:00 2001 From: KyleAMoore Date: Thu, 23 Jul 2020 08:12:38 -0500 Subject: [PATCH] Added VisPred descriptions and fixed incorrect image --- docs/examples/redshift.rst | 19 +++++------ docs/examples/rs-tutorial.rst | 57 +++++++++++++++++++++++++++----- docs/examples/vis-pred-plot.png | Bin 0 -> 9087 bytes docs/examples/vis-pred.png | Bin 0 -> 8324 bytes 4 files changed, 56 insertions(+), 20 deletions(-) create mode 100644 docs/examples/vis-pred-plot.png create mode 100644 docs/examples/vis-pred.png diff --git a/docs/examples/redshift.rst b/docs/examples/redshift.rst index 41ed4ad54..922c52b8d 100644 --- a/docs/examples/redshift.rst +++ b/docs/examples/redshift.rst @@ -16,9 +16,7 @@ Pipeline Overview * `Test Pretrained`_ * `Download SDSS`_ * `Download Train Predict`_ - -.. * `Visualize Predictions`_ -.. * `Train Visualize`_ +* `Visualize Predictions`_ .. figure:: application-pipelines.png :align: center @@ -31,10 +29,6 @@ Train Test Single ~~~~~~~~~~~~~~~~~ Trains and evaluates a single CNN model. Uses predefined artifacts that contain the training and testing data. For this and all training pipelines, the artifacts should each contain a single numpy array. Input arrays should be a 4D array of shape **(n, y, x, c)** where n=number of images, y=image height, x=image width, and c=number of color channels. Output (label) arrays should be of shape **(n,)** . -.. Visualize Predictions -.. ~~~~~~~~~~~~~~~~~~~~~ - - Train Test Compare ~~~~~~~~~~~~~~~~~~ Trains and evaluates two CNN models and compares effectiveness of the models. @@ -55,10 +49,6 @@ Test Pretrained ~~~~~~~~~~~~~~~ Evaluates the performance of a pre-existing model that is saved as an artifact. -.. Train Visualize -.. ~~~~~~~~~~~~~~~ - - Download SDSS ~~~~~~~~~~~~~ Download SDSS images and save them as artifacts. Can be used in conjunction with the other pipelines that rely on artifacts rather than images retrieved at execution time. @@ -66,3 +56,10 @@ Download SDSS images and save them as artifacts. Can be used in conjunction with Download Train Predict ~~~~~~~~~~~~~~~~~~~~~~ Download SDSS images and use some images to train a model before using the model to predict the redshift value of the remaining galaxies. + +Visualize Predictions +~~~~~~~~~~~~~~~~~~~~~ +This pipeline produces a visualization that can be helpful for understanding the effectiveness of your redshift estimation model. It generates a set of graphs like the one below that show the output probability distribution function (pdf) for the redshift values of a set of random galaxies' images. A pair of vertical lines in each subplot indicate the actual redshift value (green) and the predicted redshift value (red) for that galaxy. This allows users to see how far the model's predictions are from the correct answers and can help with identifying biases or weak-points the model may have (for example, consistently underestimation or inaccuracy with galaxies in a specific redshift range). + +.. figure:: vis-pred-plot.png + :align: center \ No newline at end of file diff --git a/docs/examples/rs-tutorial.rst b/docs/examples/rs-tutorial.rst index 20fb93951..75ca0caee 100644 --- a/docs/examples/rs-tutorial.rst +++ b/docs/examples/rs-tutorial.rst @@ -10,9 +10,8 @@ Pipeline Overview 4. `Train CIFAR-10`_ 5. `Train-Test`_ 6. `Train-Test-Compare`_ -7. `Download-Train-Evaluate`_ - -.. 6. `Visualize Predictions`_ +7. `Train-PredVis`_ +8. `Download-Train-Evaluate`_ Pipelines --------- @@ -134,7 +133,7 @@ This pipeline gives a very basic example of how to create, train, and evaluate a Train-Test ~~~~~~~~~~ -.. figure:: train-basic.png +.. figure:: train-single.png :align: center This pipeline provides an example of how one might train and evaluate a redshift estimation model. In particular, the procedure implemented here is a simplified version of work by `Pasquet et. al. (2018) `_. For readers unfamiliar with cosmological redshift, `this article `_ provides a simple and brief introduction to the topic. For the training process, there are two primary additions that should be noted. @@ -146,10 +145,6 @@ Second, a class has been provided to give examples of how researchers may define The evaluation node has also been updated to provide metrics more in line with redshift estimation. Specifically, it calculates the fraction of outlier predictions, the model’s prediction bias, the deviation in the MAD scores of the model output, and the average Continuous Ranked Probability Score (CRPS) of the output. -.. Visualize Predictions -.. ~~~~~~~~~~~~~~~~~~~~~ - - Train-Test-Compare ~~~~~~~~~~~~~~~~~~ .. figure:: train-compare.png @@ -158,9 +153,53 @@ Train-Test-Compare This pipeline gives a more complicated example of how to create visualizations that may be helpful for understanding the effectiveness of a model. The **EvalCompare** node provides a simple comparison visualization of two models. +Train-PredVis +~~~~~~~~~~~~~ +.. figure:: vis-pred.png + :align: center + +This pipeline shows another more complex and useful visualization example that can be helpful for understanding the effectiveness of your redshift estimation model. It generates a set of graphs like the one below that show the output probability distribution function (pdf) for the redshift values of a set of random galaxies' images. A pair of vertical lines in each subplot indicate the actual redshift value (green) and the predicted redshift value (red) for that galaxy. + +As shown in this example, any visualization that can be created using the `matplotlib.pyplot `_ python library can be created and displayed by a pipeline. Displaying these visualizations can be accomplished by calling the **pyplot.show()** function after building the visualization. They can then be viewed from the `Executions view <../fundamentals/interface.rst#Executions>`_. + + +.. code-block:: python + + import numpy as np + from matplotlib import pyplot as plt + + class PredVis(): + def __init__(self, num_bins=180, num_rows=1, num_cols=1, max_val=0.4): + self.num_rows = num_rows + self.num_cols = num_cols + self.xrange = np.arange(0, max_val, max_val / num_bins) + return + + def execute(self, pt, gt, pdfs): + fig, splts = plt.subplots(self.num_rows, self.num_cols, sharex=True, sharey=True) + + num_samples = self.num_rows * self.num_cols + + random_indices = np.random.choice(list(range(len(gt))), num_samples, replace=False) + + s_pdfs = np.take(pdfs, random_indices, axis=0) + s_pt = np.take(pt, random_indices, axis=0) + s_gt = np.take(gt, random_indices, axis=0) + + for i in range(num_samples): + col = i % self.num_cols + row = i // self.num_cols + splts[row,col].plot(self.xrange, s_pdfs[i],'-') + splts[row,col].axvline(s_pt[i], color='red') + splts[row,col].axvline(s_gt[i], color='green') + plt.show() + +.. figure:: vis-pred-plot.png + :align: center + Download-Train-Evaluate ~~~~~~~~~~~~~~~~~~~~~~~ .. figure:: download.png :align: center -This pipeline provides an example of how data can be retrieved and utilized in the same pipeline. The previous pipelines use manually uploaded artifacts. In many real cases, users may desire to retrieve novel data or more specific data using SciServer’s CasJobs API. In such cases, the **DownloadSDSS** node here makes downloading data relatively simple for users. It should be noted that the data downloaded is not in a form easily usable by our models and first requires moderate preprocessing, which is performed in the **Preprocessing** node. This general structure of download-process-train is a common pattern, as data is rarely supplied in a clean, immediately usable format. +This pipeline provides an example of how data can be retrieved and utilized in the same pipeline. The previous pipelines use manually uploaded artifacts. In many real cases, users may desire to retrieve novel data or more specific data using SciServer’s CasJobs API. In such cases, the **DownloadSDSS** node here makes downloading data relatively simple for users. It should be noted that the data downloaded is not in a form easily usable by our models and first requires moderate preprocessing, which is performed in the **Preprocessing** node. This general structure of download-process-train is a common pattern, as data is rarely supplied in a clean, immediately usable format. \ No newline at end of file diff --git a/docs/examples/vis-pred-plot.png b/docs/examples/vis-pred-plot.png new file mode 100644 index 0000000000000000000000000000000000000000..0aed10ce04d423ca90d4996771aea48658d55985 GIT binary patch literal 9087 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 literal 0 HcmV?d00001 diff --git a/docs/examples/vis-pred.png b/docs/examples/vis-pred.png new file mode 100644 index 0000000000000000000000000000000000000000..9dae7ba222001477b2d9b139bbda121b0f1e588e GIT binary patch literal 8324 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 literal 0 HcmV?d00001