def model_function(features, labels, mode, params, embeddings):
  """A model function satisfying the tf.estimator API.

  Args:
    features: Dictionary of feature tensors with keys:
        - question: <string> [batch_size, max_question_len]
        - question_len: <int32> [batch_size]
        - question_cid: <int32> [batch_size, max_question_len, max_chars]
        - question_wid: <int32> [batch_size, max_question_len]
        - context: <string> [batch_size, max_context_len]
        - context_len: <int32> [batch_size]
        - context_cid: <int32> [batch_size, max_context_len, max_chars]
        - context_wid: <int32> [batch_size, max_context_len]
        - answer_start: <int32> [batch_size]
        - answer_end: <int32> [batch_size]
    labels: Pair of tensors containing the answer start and answer end.
    mode: One of the keys from tf.estimator.ModeKeys.
    params: Unused parameter dictionary.
    embeddings: An embedding_utils.PretrainedWordEmbeddings object.

  Returns:
    estimator_spec: A tf.estimator.EstimatorSpec object.
  """
  del params

  if mode == tf_estimator.ModeKeys.PREDICT:
    # Add a dummy batch dimension if we are exporting the predictor.
    features = {k: tf.expand_dims(v, 0) for k, v in features.items()}

  embedding_weights, embedding_scaffold = embeddings.get_params(trainable=False)

  def _embed(prefix):
    """Embed the input text based and word and character IDs."""
    word_emb = tf.nn.embedding_lookup(embedding_weights,
                                      features[prefix + "_wid"])
    char_emb = common_layers.character_cnn(
        char_ids=features[prefix + "_cid"],
        emb_size=FLAGS.char_emb_size,
        kernel_width=FLAGS.char_kernel_width,
        num_filters=FLAGS.num_char_filters)
    concat_emb = tf.concat([word_emb, char_emb], -1)

    if mode == tf_estimator.ModeKeys.TRAIN:
      concat_emb = tf.nn.dropout(concat_emb, 1.0 - FLAGS.dropout_ratio)
    return concat_emb

  with tf.variable_scope("embed"):
    # [batch_size, max_question_len, hidden_size]
    question_emb = _embed("question")

  with tf.variable_scope("embed", reuse=True):
    # [batch_size, max_context_len, hidden_size]
    context_emb = _embed("context")

  # [batch_size, max_context_len]
  start_logits, end_logits = document_reader.score_endpoints(
      question_emb=question_emb,
      question_len=features["question_len"],
      context_emb=context_emb,
      context_len=features["context_len"],
      hidden_size=FLAGS.hidden_size,
      num_layers=FLAGS.num_layers,
      dropout_ratio=FLAGS.dropout_ratio,
      mode=mode,
      use_cudnn=False if mode == tf_estimator.ModeKeys.PREDICT else None)

  if mode != tf_estimator.ModeKeys.PREDICT:
    # [batch_size]
    start_labels, end_labels = labels

    # Since we truncate long contexts, some of the labels will not be
    # recoverable. In that case, we mask these invalid labels.
    valid_start_labels = tf.less(start_labels, features["context_len"])
    valid_end_labels = tf.less(end_labels, features["context_len"])
    tf.summary.histogram("valid_start_labels", tf.to_float(valid_start_labels))
    tf.summary.histogram("valid_end_labels", tf.to_float(valid_end_labels))

    dummy_labels = tf.zeros_like(start_labels)

    # []
    start_loss = tf.losses.sparse_softmax_cross_entropy(
        labels=tf.where(valid_start_labels, start_labels, dummy_labels),
        logits=start_logits,
        weights=tf.to_float(valid_start_labels),
        reduction=tf.losses.Reduction.MEAN)
    end_loss = tf.losses.sparse_softmax_cross_entropy(
        labels=tf.where(valid_end_labels, end_labels, dummy_labels),
        logits=end_logits,
        weights=tf.to_float(valid_end_labels),
        reduction=tf.losses.Reduction.MEAN)
    loss = start_loss + end_loss
  else:
    loss = None

  if mode == tf_estimator.ModeKeys.TRAIN:
    optimizer = tf.train.AdamOptimizer()
    gradients, variables = list(zip(*optimizer.compute_gradients(loss)))
    gradients, _ = tf.clip_by_global_norm(gradients, 5.0)
    train_op = optimizer.apply_gradients(
        grads_and_vars=list(zip(gradients, variables)),
        global_step=tf.train.get_global_step())
  else:
    # Don't build the train_op unnecessarily, since the ADAM variables can cause
    # problems with loading checkpoints on CPUs.
    train_op = None

  batch_size, max_context_len = tensor_utils.shape(features["context_wid"])
  tf.summary.histogram("batch_size", batch_size)
  tf.summary.histogram("non_padding", features["context_len"] / max_context_len)

  # [batch_size], [batch_size]
  start_predictions, end_predictions, predicted_score = (
      span_utils.max_scoring_span(start_logits, end_logits))

  # [batch_size, 2]
  predictions = dict(
      start_idx=start_predictions,
      end_idx=(end_predictions + 1),
      score=predicted_score)

  if mode == tf_estimator.ModeKeys.PREDICT:
    # Remove the dummy batch dimension if we are exporting the predictor.
    predictions = {k: tf.squeeze(v, 0) for k, v in predictions.items()}

  if mode == tf_estimator.ModeKeys.EVAL:
    text_summary = get_text_summary(
        question=features["question"],
        context=features["context"],
        start_predictions=start_predictions,
        end_predictions=end_predictions)

    # TODO(kentonl): Replace this with @mingweichang's official eval script.
    exact_match = tf.logical_and(
        tf.equal(start_predictions, start_labels),
        tf.equal(end_predictions, end_labels))

    eval_metric_ops = dict(
        exact_match=tf.metrics.mean(exact_match),
        text_summary=(text_summary, tf.no_op()))
  else:
    eval_metric_ops = None

  estimator_spec = tf_estimator.EstimatorSpec(
      mode=mode,
      loss=loss,
      predictions=predictions,
      train_op=train_op,
      eval_metric_ops=eval_metric_ops,
      scaffold=embedding_scaffold)

  return estimator_spec
A model function satisfying the tf.estimator API.

  Args:
    features: Dictionary of feature tensors with keys:
        - question: <string> [batch_size, max_question_len]
        - question_len: <int32> [batch_size]
        - question_cid: <int32> [batch_size, max_question_len, max_chars]
        - question_wid: <int32> [batch_size, max_question_len]
        - context: <string> [batch_size, max_context_len]
        - context_len: <int32> [batch_size]
        - context_cid: <int32> [batch_size, max_context_len, max_chars]
        - context_wid: <int32> [batch_size, max_context_len]
        - answer_start: <int32> [batch_size]
        - answer_end: <int32> [batch_size]
    labels: Pair of tensors containing the answer start and answer end.
    mode: One of the keys from tf.estimator.ModeKeys.
    params: Unused parameter dictionary.
    embeddings: An embedding_utils.PretrainedWordEmbeddings object.

  Returns:
    estimator_spec: A tf.estimator.EstimatorSpec object.
  
language/question_answering/decatt_docreader/models/nq_short_pipeline_model.py