Create a Keras model with the given hyperparameters.

  Args:
    hparams: A dict mapping hyperparameters in `HPARAMS` to values.
    seed: A hashable object to be used as a random seed (e.g., to
      construct dropout layers in the model).

  Returns:
    A compiled Keras model.

def model_fn(hparams, seed):
  """Create a Keras model with the given hyperparameters.

  Args:
    hparams: A dict mapping hyperparameters in `HPARAMS` to values.
    seed: A hashable object to be used as a random seed (e.g., to
      construct dropout layers in the model).

  Returns:
    A compiled Keras model.
  """
  rng = random.Random(seed)

  model = tf.keras.models.Sequential()
  model.add(tf.keras.layers.Input(INPUT_SHAPE))
  model.add(tf.keras.layers.Reshape(INPUT_SHAPE + (1,)))  # grayscale channel

  # Add convolutional layers.
  conv_filters = 8
  for _ in xrange(hparams[HP_CONV_LAYERS]):
    model.add(tf.keras.layers.Conv2D(
        filters=conv_filters,
        kernel_size=hparams[HP_CONV_KERNEL_SIZE],
        padding="same",
        activation="relu",
    ))
    model.add(tf.keras.layers.MaxPool2D(pool_size=2, padding="same"))
    conv_filters *= 2

  model.add(tf.keras.layers.Flatten())
  model.add(tf.keras.layers.Dropout(hparams[HP_DROPOUT], seed=rng.random()))

  # Add fully connected layers.
  dense_neurons = 32
  for _ in xrange(hparams[HP_DENSE_LAYERS]):
    model.add(tf.keras.layers.Dense(dense_neurons, activation="relu"))
    dense_neurons *= 2

  # Add the final output layer.
  model.add(tf.keras.layers.Dense(OUTPUT_CLASSES, activation="softmax"))

  model.compile(
      loss="sparse_categorical_crossentropy",
      optimizer=hparams[HP_OPTIMIZER],
      metrics=["accuracy"],
  )
  return model