trainer_lib.py 7.73 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# Copyright 2017 Google Inc. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ==============================================================================
Ivan Bogatyy's avatar
Ivan Bogatyy committed
15
16
17
18
19
20
21
22
23
24
"""Utility functions to build DRAGNN MasterSpecs and schedule model training.

Provides functions to finish a MasterSpec, building required lexicons for it and
adding them as resources, as well as setting features sizes.
"""

import random


import tensorflow as tf
Terry Koo's avatar
Terry Koo committed
25

Ivan Bogatyy's avatar
Ivan Bogatyy committed
26
27
28
29
from tensorflow.core.framework.summary_pb2 import Summary
from tensorflow.python.framework import errors
from tensorflow.python.platform import gfile

Terry Koo's avatar
Terry Koo committed
30
from syntaxnet.util import check
Ivan Bogatyy's avatar
Ivan Bogatyy committed
31
32
33
34
35
36
37
38
39
40
41
42
43


def calculate_component_accuracies(eval_res_values):
  """Transforms the DRAGNN eval_res output to float accuracies of components."""
  # The structure of eval_res_values is
  # [comp1_total, comp1_correct, comp2_total, comp2_correct, ...]
  return [
      1.0 * eval_res_values[2 * i + 1] / eval_res_values[2 * i]
      if eval_res_values[2 * i] > 0 else float('nan')
      for i in xrange(len(eval_res_values) // 2)
  ]


44
def write_summary(summary_writer, label, value, step):
Ivan Bogatyy's avatar
Ivan Bogatyy committed
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
  """Write a summary for a certain evaluation."""
  summary = Summary(value=[Summary.Value(tag=label, simple_value=float(value))])
  summary_writer.add_summary(summary, step)
  summary_writer.flush()


def annotate_dataset(sess, annotator, eval_corpus):
  """Annotate eval_corpus given a model."""
  batch_size = min(len(eval_corpus), 1024)
  processed = []
  tf.logging.info('Annotating datset: %d examples', len(eval_corpus))
  for start in range(0, len(eval_corpus), batch_size):
    end = min(start + batch_size, len(eval_corpus))
    serialized_annotations = sess.run(
        annotator['annotations'],
Terry Koo's avatar
Terry Koo committed
60
61
62
        feed_dict={
            annotator['input_batch']: eval_corpus[start:end]
        })
Ivan Bogatyy's avatar
Ivan Bogatyy committed
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
    assert len(serialized_annotations) == end - start
    processed.extend(serialized_annotations)
  tf.logging.info('Done. Produced %d annotations', len(processed))
  return processed


def get_summary_writer(tensorboard_dir):
  """Creates a directory for writing summaries and returns a writer."""
  tf.logging.info('TensorBoard directory: %s', tensorboard_dir)
  tf.logging.info('Deleting prior data if exists...')
  try:
    gfile.DeleteRecursively(tensorboard_dir)
  except errors.OpError as err:
    tf.logging.error('Directory did not exist? Error: %s', err)
  tf.logging.info('Deleted! Creating the directory again...')
  gfile.MakeDirs(tensorboard_dir)
  tf.logging.info('Created! Instatiating SummaryWriter...')
  summary_writer = tf.summary.FileWriter(tensorboard_dir)
  return summary_writer


Terry Koo's avatar
Terry Koo committed
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
def generate_target_per_step_schedule(pretrain_steps, train_steps):
  """Generates a sampled training schedule.

  Arguments:
    pretrain_steps: List, number of pre-training steps per each target.
    train_steps: List, number of sampled training steps per each target.

  Returns:
    Python list of length sum(pretrain_steps + train_steps), containing
    target numbers per step.
  """
  check.Eq(len(pretrain_steps), len(train_steps))
  # Arbitrary seed to make sure the return is deterministic.
  random.seed(0x31337)
  tf.logging.info('Determining the training schedule...')
  target_per_step = []
  for target_idx in xrange(len(pretrain_steps)):
    target_per_step += [target_idx] * pretrain_steps[target_idx]
  train_steps = list(train_steps)
  while sum(train_steps) > 0:
    step = random.randint(0, sum(train_steps) - 1)
    cumulative_steps = 0
    for target_idx in xrange(len(train_steps)):
      cumulative_steps += train_steps[target_idx]
      if step < cumulative_steps:
        break
    assert train_steps[target_idx] > 0
    train_steps[target_idx] -= 1
    target_per_step.append(target_idx)
  tf.logging.info('Training schedule defined!')
  return target_per_step


Ivan Bogatyy's avatar
Ivan Bogatyy committed
117
118
119
120
121
122
def run_training_step(sess, trainer, train_corpus, batch_size):
  """Runs a single iteration of train_op on a randomly sampled batch."""
  batch = random.sample(train_corpus, batch_size)
  sess.run(trainer['run'], feed_dict={trainer['input_batch']: batch})


Terry Koo's avatar
Terry Koo committed
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
def run_training(sess,
                 trainers,
                 annotator,
                 evaluator,
                 pretrain_steps,
                 train_steps,
                 train_corpus,
                 eval_corpus,
                 eval_gold,
                 batch_size,
                 summary_writer,
                 report_every,
                 saver,
                 checkpoint_filename,
                 checkpoint_stats=None):
Ivan Bogatyy's avatar
Ivan Bogatyy committed
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
  """Runs multi-task DRAGNN training on a single corpus.

  Arguments:
    sess: TF session to use.
    trainers: List of training ops to use.
    annotator: Annotation op.
    evaluator: Function taking two serialized corpora and returning a dict of
      scalar summaries representing evaluation metrics. The 'eval_metric'
      summary will be used for early stopping.
    pretrain_steps: List of the no. of pre-training steps for each train op.
    train_steps: List of the total no. of steps for each train op.
    train_corpus: Training corpus to use.
    eval_corpus: Holdout Corpus for early stoping.
    eval_gold: Reference of eval_corpus for computing accuracy.
      eval_corpus and eval_gold are allowed to be the same if eval_corpus
      already contains gold annotation.
      Note for segmentation eval_corpus and eval_gold are always different since
      eval_corpus contains sentences whose tokens are utf8-characters while
      eval_gold's tokens are gold words.
    batch_size: How many examples to send to the train op at a time.
    summary_writer: TF SummaryWriter to use to write summaries.
    report_every: How often to compute summaries (in steps).
    saver: TF saver op to save variables.
    checkpoint_filename: File to save checkpoints to.
    checkpoint_stats: Stats of checkpoint.
  """
  if not checkpoint_stats:
    checkpoint_stats = [0] * (len(train_steps) + 1)

Terry Koo's avatar
Terry Koo committed
167
168
  target_per_step = generate_target_per_step_schedule(pretrain_steps,
                                                      train_steps)
Ivan Bogatyy's avatar
Ivan Bogatyy committed
169
170
171
  best_eval_metric = -1.0
  tf.logging.info('Starting training...')
  actual_step = sum(checkpoint_stats[1:])
Terry Koo's avatar
Terry Koo committed
172
  for step, target_idx in enumerate(target_per_step):
Ivan Bogatyy's avatar
Ivan Bogatyy committed
173
174
175
176
177
178
179
180
181
182
    run_training_step(sess, trainers[target_idx], train_corpus, batch_size)
    checkpoint_stats[target_idx + 1] += 1
    if step % 100 == 0:
      tf.logging.info('training step: %d, actual: %d', step, actual_step + step)
    if step % report_every == 0:
      tf.logging.info('finished step: %d, actual: %d', step, actual_step + step)

      annotated = annotate_dataset(sess, annotator, eval_corpus)
      summaries = evaluator(eval_gold, annotated)
      for label, metric in summaries.iteritems():
183
        write_summary(summary_writer, label, metric, actual_step + step)
Ivan Bogatyy's avatar
Ivan Bogatyy committed
184
      eval_metric = summaries['eval_metric']
185
      tf.logging.info('Current eval metric: %.2f', eval_metric)
Ivan Bogatyy's avatar
Ivan Bogatyy committed
186
      if best_eval_metric < eval_metric:
187
        tf.logging.info('Updating best eval to %.2f, saving checkpoint.',
Ivan Bogatyy's avatar
Ivan Bogatyy committed
188
189
190
191
192
193
194
195
196
197
                        eval_metric)
        best_eval_metric = eval_metric
        saver.save(sess, checkpoint_filename)

        with gfile.GFile('%s.stats' % checkpoint_filename, 'w') as f:
          stats_str = ','.join([str(x) for x in checkpoint_stats])
          f.write(stats_str)
          tf.logging.info('Writing stats: %s', stats_str)

  tf.logging.info('Finished training!')