graph_builder_test.py 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326
  1. # Copyright 2016 Google Inc. All Rights Reserved.
  2. #
  3. # Licensed under the Apache License, Version 2.0 (the "License");
  4. # you may not use this file except in compliance with the License.
  5. # You may obtain a copy of the License at
  6. #
  7. # http://www.apache.org/licenses/LICENSE-2.0
  8. #
  9. # Unless required by applicable law or agreed to in writing, software
  10. # distributed under the License is distributed on an "AS IS" BASIS,
  11. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. # See the License for the specific language governing permissions and
  13. # limitations under the License.
  14. # ==============================================================================
  15. """Tests for graph_builder."""
  16. # disable=no-name-in-module,unused-import,g-bad-import-order,maybe-no-member
  17. import os.path
  18. import tensorflow as tf
  19. from tensorflow.python.framework import test_util
  20. from tensorflow.python.ops import variables
  21. from tensorflow.python.platform import googletest
  22. from syntaxnet import graph_builder
  23. from syntaxnet import sparse_pb2
  24. from syntaxnet.ops import gen_parser_ops
  25. FLAGS = tf.app.flags.FLAGS
  26. if not hasattr(FLAGS, 'test_srcdir'):
  27. FLAGS.test_srcdir = ''
  28. if not hasattr(FLAGS, 'test_tmpdir'):
  29. FLAGS.test_tmpdir = tf.test.get_temp_dir()
  30. class GraphBuilderTest(test_util.TensorFlowTestCase):
  31. def setUp(self):
  32. # Creates a task context with the correct testing paths.
  33. initial_task_context = os.path.join(
  34. FLAGS.test_srcdir,
  35. 'syntaxnet/'
  36. 'testdata/context.pbtxt')
  37. self._task_context = os.path.join(FLAGS.test_tmpdir, 'context.pbtxt')
  38. with open(initial_task_context, 'r') as fin:
  39. with open(self._task_context, 'w') as fout:
  40. fout.write(fin.read().replace('SRCDIR', FLAGS.test_srcdir)
  41. .replace('OUTPATH', FLAGS.test_tmpdir))
  42. # Creates necessary term maps.
  43. with self.test_session() as sess:
  44. gen_parser_ops.lexicon_builder(task_context=self._task_context,
  45. corpus_name='training-corpus').run()
  46. self._num_features, self._num_feature_ids, _, self._num_actions = (
  47. sess.run(gen_parser_ops.feature_size(task_context=self._task_context,
  48. arg_prefix='brain_parser')))
  49. def MakeBuilder(self, use_averaging=True, **kw_args):
  50. # Set the seed and gate_gradients to ensure reproducibility.
  51. return graph_builder.GreedyParser(
  52. self._num_actions, self._num_features, self._num_feature_ids,
  53. embedding_sizes=[8, 8, 8], hidden_layer_sizes=[32, 32], seed=42,
  54. gate_gradients=True, use_averaging=use_averaging, **kw_args)
  55. def FindNode(self, name):
  56. for node in tf.get_default_graph().as_graph_def().node:
  57. if node.name == name:
  58. return node
  59. return None
  60. def NodeFound(self, name):
  61. return self.FindNode(name) is not None
  62. def testScope(self):
  63. # Set up the network topology
  64. graph = tf.Graph()
  65. with graph.as_default():
  66. parser = self.MakeBuilder()
  67. parser.AddTraining(self._task_context,
  68. batch_size=10,
  69. corpus_name='training-corpus')
  70. parser.AddEvaluation(self._task_context,
  71. batch_size=2,
  72. corpus_name='tuning-corpus')
  73. parser.AddSaver()
  74. # Check that the node ids we may rely on are there with the expected
  75. # names.
  76. self.assertEqual(parser.training['logits'].name, 'training/logits:0')
  77. self.assertTrue(self.NodeFound('training/logits'))
  78. self.assertTrue(self.NodeFound('training/feature_0'))
  79. self.assertTrue(self.NodeFound('training/feature_1'))
  80. self.assertTrue(self.NodeFound('training/feature_2'))
  81. self.assertFalse(self.NodeFound('training/feature_3'))
  82. self.assertEqual(parser.evaluation['logits'].name, 'evaluation/logits:0')
  83. self.assertTrue(self.NodeFound('evaluation/logits'))
  84. # The saver node is expected to be in the root scope.
  85. self.assertTrue(self.NodeFound('save/restore_all'))
  86. # Also check that the parameters have the scope we expect.
  87. self.assertTrue(self.NodeFound('embedding_matrix_0'))
  88. self.assertTrue(self.NodeFound('embedding_matrix_1'))
  89. self.assertTrue(self.NodeFound('embedding_matrix_2'))
  90. self.assertFalse(self.NodeFound('embedding_matrix_3'))
  91. def testNestedScope(self):
  92. # It's OK to put the whole graph in a scope of its own.
  93. graph = tf.Graph()
  94. with graph.as_default():
  95. with graph.name_scope('top'):
  96. parser = self.MakeBuilder()
  97. parser.AddTraining(self._task_context,
  98. batch_size=10,
  99. corpus_name='training-corpus')
  100. parser.AddSaver()
  101. self.assertTrue(self.NodeFound('top/training/logits'))
  102. self.assertTrue(self.NodeFound('top/training/feature_0'))
  103. # The saver node is expected to be in the root scope no matter what.
  104. self.assertFalse(self.NodeFound('top/save/restore_all'))
  105. self.assertTrue(self.NodeFound('save/restore_all'))
  106. def testUseCustomGraphs(self):
  107. batch_size = 10
  108. # Use separate custom graphs.
  109. custom_train_graph = tf.Graph()
  110. with custom_train_graph.as_default():
  111. train_parser = self.MakeBuilder()
  112. train_parser.AddTraining(self._task_context,
  113. batch_size,
  114. corpus_name='training-corpus')
  115. custom_eval_graph = tf.Graph()
  116. with custom_eval_graph.as_default():
  117. eval_parser = self.MakeBuilder()
  118. eval_parser.AddEvaluation(self._task_context,
  119. batch_size,
  120. corpus_name='tuning-corpus')
  121. # The following session runs should not fail.
  122. with self.test_session(graph=custom_train_graph) as sess:
  123. self.assertTrue(self.NodeFound('training/logits'))
  124. sess.run(train_parser.inits.values())
  125. sess.run(['training/logits:0'])
  126. with self.test_session(graph=custom_eval_graph) as sess:
  127. self.assertFalse(self.NodeFound('training/logits'))
  128. self.assertTrue(self.NodeFound('evaluation/logits'))
  129. sess.run(eval_parser.inits.values())
  130. sess.run(['evaluation/logits:0'])
  131. def testTrainingAndEvalAreIndependent(self):
  132. batch_size = 10
  133. graph = tf.Graph()
  134. with graph.as_default():
  135. parser = self.MakeBuilder(use_averaging=False)
  136. parser.AddTraining(self._task_context,
  137. batch_size,
  138. corpus_name='training-corpus')
  139. parser.AddEvaluation(self._task_context,
  140. batch_size,
  141. corpus_name='tuning-corpus')
  142. with self.test_session(graph=graph) as sess:
  143. sess.run(parser.inits.values())
  144. # Before any training updates are performed, both training and eval nets
  145. # should return the same computations.
  146. eval_logits, = sess.run([parser.evaluation['logits']])
  147. training_logits, = sess.run([parser.training['logits']])
  148. self.assertNear(abs((eval_logits - training_logits).sum()), 0, 1e-6)
  149. # After training, activations should differ.
  150. for _ in range(5):
  151. eval_logits = parser.evaluation['logits'].eval()
  152. for _ in range(5):
  153. training_logits, _ = sess.run([parser.training['logits'],
  154. parser.training['train_op']])
  155. self.assertGreater(abs((eval_logits - training_logits).sum()), 0, 1e-3)
  156. def testReproducibility(self):
  157. batch_size = 10
  158. def ComputeACost(graph):
  159. with graph.as_default():
  160. parser = self.MakeBuilder(use_averaging=False)
  161. parser.AddTraining(self._task_context,
  162. batch_size,
  163. corpus_name='training-corpus')
  164. parser.AddEvaluation(self._task_context,
  165. batch_size,
  166. corpus_name='tuning-corpus')
  167. with self.test_session(graph=graph) as sess:
  168. sess.run(parser.inits.values())
  169. for _ in range(5):
  170. cost, _ = sess.run([parser.training['cost'],
  171. parser.training['train_op']])
  172. return cost
  173. cost1 = ComputeACost(tf.Graph())
  174. cost2 = ComputeACost(tf.Graph())
  175. self.assertNear(cost1, cost2, 1e-8)
  176. def testAddTrainingAndEvalOrderIndependent(self):
  177. batch_size = 10
  178. graph1 = tf.Graph()
  179. with graph1.as_default():
  180. parser = self.MakeBuilder(use_averaging=False)
  181. parser.AddTraining(self._task_context,
  182. batch_size,
  183. corpus_name='training-corpus')
  184. parser.AddEvaluation(self._task_context,
  185. batch_size,
  186. corpus_name='tuning-corpus')
  187. with self.test_session(graph=graph1) as sess:
  188. sess.run(parser.inits.values())
  189. metrics1 = None
  190. for _ in range(500):
  191. cost1, _ = sess.run([parser.training['cost'],
  192. parser.training['train_op']])
  193. em1 = parser.evaluation['eval_metrics'].eval()
  194. metrics1 = metrics1 + em1 if metrics1 is not None else em1
  195. # Reverse the order in which Training and Eval stacks are added.
  196. graph2 = tf.Graph()
  197. with graph2.as_default():
  198. parser = self.MakeBuilder(use_averaging=False)
  199. parser.AddEvaluation(self._task_context,
  200. batch_size,
  201. corpus_name='tuning-corpus')
  202. parser.AddTraining(self._task_context,
  203. batch_size,
  204. corpus_name='training-corpus')
  205. with self.test_session(graph=graph2) as sess:
  206. sess.run(parser.inits.values())
  207. metrics2 = None
  208. for _ in range(500):
  209. cost2, _ = sess.run([parser.training['cost'],
  210. parser.training['train_op']])
  211. em2 = parser.evaluation['eval_metrics'].eval()
  212. metrics2 = metrics2 + em2 if metrics2 is not None else em2
  213. self.assertNear(cost1, cost2, 1e-8)
  214. self.assertEqual(abs(metrics1 - metrics2).sum(), 0)
  215. def testEvalMetrics(self):
  216. batch_size = 10
  217. graph = tf.Graph()
  218. with graph.as_default():
  219. parser = self.MakeBuilder()
  220. parser.AddEvaluation(self._task_context,
  221. batch_size,
  222. corpus_name='tuning-corpus')
  223. with self.test_session(graph=graph) as sess:
  224. sess.run(parser.inits.values())
  225. tokens = 0
  226. correct_heads = 0
  227. for _ in range(100):
  228. eval_metrics = sess.run(parser.evaluation['eval_metrics'])
  229. tokens += eval_metrics[0]
  230. correct_heads += eval_metrics[1]
  231. self.assertGreater(tokens, 0)
  232. self.assertGreaterEqual(tokens, correct_heads)
  233. self.assertGreaterEqual(correct_heads, 0)
  234. def MakeSparseFeatures(self, ids, weights):
  235. f = sparse_pb2.SparseFeatures()
  236. for i, w in zip(ids, weights):
  237. f.id.append(i)
  238. f.weight.append(w)
  239. return f.SerializeToString()
  240. def testEmbeddingOp(self):
  241. graph = tf.Graph()
  242. with self.test_session(graph=graph):
  243. params = tf.constant([[1.0, 2.0], [3.0, 4.0], [5.0, 6.0]],
  244. tf.float32)
  245. var = variables.Variable([self.MakeSparseFeatures([1, 2], [1.0, 1.0]),
  246. self.MakeSparseFeatures([], [])])
  247. var.initializer.run()
  248. embeddings = graph_builder.EmbeddingLookupFeatures(params, var,
  249. True).eval()
  250. self.assertAllClose([[8.0, 10.0], [0.0, 0.0]], embeddings)
  251. var = variables.Variable([self.MakeSparseFeatures([], []),
  252. self.MakeSparseFeatures([0, 2],
  253. [0.5, 2.0])])
  254. var.initializer.run()
  255. embeddings = graph_builder.EmbeddingLookupFeatures(params, var,
  256. True).eval()
  257. self.assertAllClose([[0.0, 0.0], [10.5, 13.0]], embeddings)
  258. def testOnlyTrainSomeParameters(self):
  259. batch_size = 10
  260. graph = tf.Graph()
  261. with graph.as_default():
  262. parser = self.MakeBuilder(use_averaging=False, only_train='softmax_bias')
  263. parser.AddTraining(self._task_context,
  264. batch_size,
  265. corpus_name='training-corpus')
  266. with self.test_session(graph=graph) as sess:
  267. sess.run(parser.inits.values())
  268. # Before training, save the state of two of the parameters.
  269. bias0, weight0 = sess.run([parser.params['softmax_bias'],
  270. parser.params['softmax_weight']])
  271. for _ in range(5):
  272. bias, weight, _ = sess.run([parser.params['softmax_bias'],
  273. parser.params['softmax_weight'],
  274. parser.training['train_op']])
  275. # After training, only one of the parameters should have changed.
  276. self.assertAllEqual(weight, weight0)
  277. self.assertGreater(abs(bias - bias0).sum(), 0, 1e-5)
  278. if __name__ == '__main__':
  279. googletest.main()