def bitstring_readable(data, batch_size, model_output=None, whole_batch=False):
  """Produce a human readable representation of the sequences in data.

  Args:
    data: data to be visualised
    batch_size: size of batch
    model_output: optional model output tensor to visualize alongside data.
    whole_batch: whether to visualise the whole batch. Only the first sample
        will be visualized if False

  Returns:
    A string used to visualise the data batch
  """

  def _readable(datum):
    return '+' + ' '.join(['-' if x == 0 else '%d' % x for x in datum]) + '+'

  obs_batch = data.observations
  targ_batch = data.target

  iterate_over = range(batch_size) if whole_batch else range(1)

  batch_strings = []

  for batch_index in iterate_over:
    obs = obs_batch[batch_index, :, :]
    targ = targ_batch[batch_index, :, :]

    readable_obs = 'Observations:\n' + '\n'.join([_readable(obs_vector) for obs_vector in obs])
    readable_targ = 'Targets:\n' + '\n'.join([_readable(targ_vector) for targ_vector in targ])
    strings = [readable_obs, readable_targ]

    if model_output is not None:
      output = model_output[batch_index, :, :]
      strings.append('Model Output:\n' + '\n'.join([_readable(output_vec) for output_vec in output]))

    batch_strings.append('\n\n'.join(strings))

  return '\n' + '\n\n\n\n'.join(batch_strings)