human
I am working on the file "Intro_NeuralNetworks_TensorFlow_Estimators.ipynb".
The first task is:
Early stopping and continuous evaluation using SessionRunHook
Using tf.estimator.Estimator.train, while convenient in many ways, appears to give us less control over the training loop.  When we manually managed training, it was easy to evaluate during training and to do early stopping (i.e. stop training when a certain condition is met, instead of when the entire training cycle is over).  
Luckily, we can re-create these abilities using SessionRunHook.  While there are still disadvantages (the model has to be saved/loaded everytime you want to evaluate), the net benefits of estimator are positive.
With a SessionRunHook, you implement behavior that you want to execute before and after every session.run call made by estimator.train().  Full documentation here: https://www.tensorflow.org/api_docs/python/tf/train/SessionRunHook.
To implement evaluation during training, we make a new class, extending SessionRunHook, which takes a given estimator and input_function, and evaluates the estimator on that input every N steps.  The more complicated Hook in the main repository also implements early stopping and writes the data to a CSV file at the end of training.
EXERCISE: implement an early stopping condition in after_run of the hook below.  Example: when the evaluation loss is below a certain threshold. Hint: run_context.request_stop() should be called when your stop condition is met.
Can you write Python code for it?
assistant
#Here is the Python code you requested.

class EvalDuringHook(tf.train.SessionRunHook):
    
    def __init__(self, estimator, input_fn, num_steps=50):
        self._estimator = estimator
        self._input_fn = input_fn
        self._num_steps = num_steps
        
    def begin(self):

        # get the tensor that keeps track of the global step
        self._global_step_tensor = tf.train.get_or_create_global_step()
        if self._global_step_tensor is None:
            raise ValueError("global_step needed for EvalEarlyStop")

    # before session run calls, put here the tensors you want to run
    # these will be given to after_run
    def before_run(self, run_context):

        requests = {'global_step': self._global_step_tensor}
        return tf.train.SessionRunArgs(requests)

    def after_run(self, run_context, run_values):

        global_step = run_values.results['global_step']
        # evaluate and print if it's the right number of steps
        if (global_step-1) % self._num_steps == 0:
            eval_results = self._estimator.evaluate(input_fn=self._input_fn)
            print eval_results
            if eval_results['loss'] < 0.05:
                run_context.request_stop()
            
tf.reset_default_graph()

# now, we train, passing the Hook to the method
# it's useful to tell TF to also save checkpoints every N steps, which we do with a custom RunConfig
num_steps = 50

run_config = tf.estimator.RunConfig(
        save_checkpoints_steps=num_steps,
        save_checkpoints_secs=None)

new_estimator = tf.estimator.Estimator(model_fn=ffnn_model_fn, params=hparams, config=run_config)

new_estimator.train(input_fn=new_train_input_fn,
                hooks=[EvalDuringHook(new_estimator, new_test_input_fn)])