function decoder(opts) {
  const {originalDim, intermediateDim, latentDim} = opts;

  // The decoder model has a linear topology and hence could be constructed
  // with `tf.sequential()`. But we use the functional-model API (i.e.,
  // `tf.model()`) here nonetheless, for consistency with the encoder model
  // (see `encoder()` above).
  const input = tf.input({shape: [latentDim]});
  let y = tf.layers.dense({
    units: intermediateDim,
    activation: 'relu'
  }).apply(input);
  y = tf.layers.dense({
    units: originalDim,
    activation: 'sigmoid'
  }).apply(y);
  const dec = tf.model({inputs: input, outputs: y});

  // console.log('Decoder Summary');
  // dec.summary();
  return dec;
}
The decoder portion of the model.

@param {*} opts decoder configuration
@param {number} opts.originalDim number of dimensions in the original data
@param {number} opts.intermediateDim number of dimensions in the bottleneck
                                     of the encoder
@param {number} opts.latentDim number of dimensions in latent space