From 0f02f05913e03889bbcb85e71a6d005a8519bfb9 Mon Sep 17 00:00:00 2001 From: Yifei Feng Date: Tue, 21 Aug 2018 14:48:01 -0700 Subject: Merged commit includes the following changes: 209663919 by yifeif: Internal change. -- 209663914 by amitpatankar: Fix the topk_op_test for numpy>1.15. -- 209660476 by jdduke: Fix model lifetime for TensorFlow Lite C# bindings Ensure the model's existence for the duration of the interpreter, as per API requirements. -- 209655960 by scottzhu: Unify RNN Cell interface between TF and Keras. -- 209655731 by A. Unique TensorFlower: Added tests for PredictionOps and PartitionExamplesOps -- 209655291 by nolivia: adding rate class so that we can save global_step/sec using tf.contrib.summary. The function takes the rate in relation to any tensors provided that the numerator and denominator are broadcastable and have dtypes that can be cast to float64 -- 209654655 by kramerb: [XLA] Switch from tensorflow::gtl::InlinedVector to absl::InlinedVector This one comes with extra goodies like a move constructor. -- 209653851 by A. Unique TensorFlower: Internal build specification change -- PiperOrigin-RevId: 209663919 --- tensorflow/contrib/rate/BUILD | 48 +++++++++++ tensorflow/contrib/rate/rate.py | 151 +++++++++++++++++++++++++++++++++++ tensorflow/contrib/rate/rate_test.py | 97 ++++++++++++++++++++++ 3 files changed, 296 insertions(+) create mode 100644 tensorflow/contrib/rate/BUILD create mode 100644 tensorflow/contrib/rate/rate.py create mode 100644 tensorflow/contrib/rate/rate_test.py (limited to 'tensorflow/contrib/rate') diff --git a/tensorflow/contrib/rate/BUILD b/tensorflow/contrib/rate/BUILD new file mode 100644 index 0000000000..c461a7145e --- /dev/null +++ b/tensorflow/contrib/rate/BUILD @@ -0,0 +1,48 @@ +# Description: +# contains parts of TensorFlow that are experimental or unstable and which are not supported. + +licenses(["notice"]) # Apache 2.0 + +package(default_visibility = ["//visibility:public"]) + +exports_files(["LICENSE"]) + +load("//tensorflow:tensorflow.bzl", "py_test") + +py_library( + name = "rate", + srcs = [ + "rate.py", + ], + srcs_version = "PY2AND3", + deps = [ + "//tensorflow/python:array_ops", + "//tensorflow/python:check_ops", + "//tensorflow/python:control_flow_ops", + "//tensorflow/python:framework", + "//tensorflow/python:framework_for_generated_wrappers", + "//tensorflow/python:math_ops", + "//tensorflow/python:sparse_ops", + "//tensorflow/python:state_ops", + "//tensorflow/python:util", + "//tensorflow/python:variable_scope", + "//tensorflow/python:variables", + ], +) + +py_test( + name = "rate_test", + size = "small", + srcs = ["rate_test.py"], + deps = [ + ":rate", + "//tensorflow/python:array_ops", + "//tensorflow/python:client_testlib", + "//tensorflow/python:data_flow_ops", + "//tensorflow/python:errors", + "//tensorflow/python:framework", + "//tensorflow/python:framework_for_generated_wrappers", + "//tensorflow/python:variables", + "//tensorflow/python/eager:test", + ], +) diff --git a/tensorflow/contrib/rate/rate.py b/tensorflow/contrib/rate/rate.py new file mode 100644 index 0000000000..24d586479a --- /dev/null +++ b/tensorflow/contrib/rate/rate.py @@ -0,0 +1,151 @@ +# Copyright 2018 The TensorFlow Authors. 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. +# ============================================================================== +"""Implementation of tf.contrib.rate module.""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import re + +from tensorflow.python.eager import context +from tensorflow.python.eager import function +from tensorflow.python.framework import dtypes +from tensorflow.python.framework import ops +from tensorflow.python.ops import array_ops +from tensorflow.python.ops import math_ops +from tensorflow.python.ops import resource_variable_ops +from tensorflow.python.ops import state_ops +from tensorflow.python.ops import variable_scope + +_to_replace = re.compile("[^A-Za-z0-9.]") + + +class Rate(object): + """Computes the rate of change since the last rate call.""" + + def __init__(self, name=None): + self._built = False + self._vars = [] + self._initial_values = {} + name = name or self.__class__.__name__ + # Replace things like spaces in name to create a valid scope name. + scope_name = _to_replace.sub("_", name) + # We create the variable scope now to get the unique name that will + # be used as a variable prefix when build() calls _add_variable(). + with variable_scope.variable_scope( + scope_name, use_resource=True, reuse=False) as scope: + pos = scope.name.rfind(scope_name) + self._name = name + scope.name[pos + len(scope_name):] + self._scope = scope + + # Ensures that if the user calls build directly we still set self._built to + # True to prevent variables from being recreated. + self._build = self.build + if context.executing_eagerly(): + self._construction_scope = context.eager_mode + else: + # We make self.call() into a graph callable here, so that we can + # return a single op that performs all of the variable updates. + self._construction_scope = ops.get_default_graph().as_default + self.call = function.defun(self.call) + + def build(self, values, denominator): + """Method to create variables. + + Called by `__call__()` before `call()` for the first time. + + Args: + values: The numerator for rate. + denominator: Value to which the rate is taken with respect. + """ + self.numer = self._add_variable( + name="numer", shape=values.get_shape(), dtype=dtypes.float64) + self.denom = self._add_variable( + name="denom", shape=denominator.get_shape(), dtype=dtypes.float64) + self.prev_values = self._add_variable( + name="prev_values", shape=values.get_shape(), dtype=dtypes.float64) + self.prev_denominator = self._add_variable( + name="prev_denominator", + shape=denominator.get_shape(), + dtype=dtypes.float64) + self._built = True + + def __call__(self, *args, **kwargs): + """Returns op to execute to update. + + Returns None if eager execution is enabled. + Returns a graph-mode function if graph execution is enabled. + + Args: + *args: + **kwargs: A mini-batch of inputs to Rate, passed on to `call()`. + """ + if not self._built: + with variable_scope.variable_scope( + self._scope), self._construction_scope(): + self.build(*args, **kwargs) + self._built = True + return self.call(*args, **kwargs) + + @property + def name(self): + return self._name + + @property + def variables(self): + return self._vars + + def _safe_div(self, numerator, denominator, name): + t = math_ops.truediv(numerator, denominator) + zero = array_ops.zeros_like(t, dtype=denominator.dtype) + condition = math_ops.greater(denominator, zero) + zero = math_ops.cast(zero, t.dtype) + return array_ops.where(condition, t, zero, name=name) + + def _add_variable(self, name, shape=None, dtype=None): + """Private method for adding variables to the graph.""" + if self._built: + raise RuntimeError("Can't call add_variable() except in build().") + v = resource_variable_ops.ResourceVariable( + lambda: array_ops.zeros(shape, dtype), + trainable=False, + validate_shape=True, + name=name, + collections=[ops.GraphKeys.LOCAL_VARIABLES]) + return v + + def call(self, values, denominator): + """Computes the rate since the last call. + + Args: + values: Tensor with the per-example value. + denominator: Measure to take the rate with respect to. + + Returns: + The rate or 0 if denominator is unchanged since last call. + """ + if denominator.dtype != dtypes.float64: + denominator = math_ops.cast(denominator, dtypes.float64) + if values.dtype != dtypes.float64: + values = math_ops.cast(values, dtypes.float64) + + state_ops.assign(self.numer, math_ops.subtract(values, self.prev_values)) + state_ops.assign(self.denom, + math_ops.subtract(denominator, self.prev_denominator)) + state_ops.assign(self.prev_values, values) + state_ops.assign(self.prev_denominator, denominator) + + return self._safe_div(self.numer, self.denom, name="safe_rate") diff --git a/tensorflow/contrib/rate/rate_test.py b/tensorflow/contrib/rate/rate_test.py new file mode 100644 index 0000000000..08908104f4 --- /dev/null +++ b/tensorflow/contrib/rate/rate_test.py @@ -0,0 +1,97 @@ +# Copyright 2018 The TensorFlow Authors. 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. +# ============================================================================== +"""Tests for Rate.""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +from tensorflow.contrib.rate import rate +from tensorflow.python.framework import constant_op +from tensorflow.python.framework import dtypes +from tensorflow.python.framework import ops +from tensorflow.python.framework import test_util +from tensorflow.python.ops import array_ops +from tensorflow.python.ops import control_flow_ops +from tensorflow.python.ops import math_ops +from tensorflow.python.ops import variables +from tensorflow.python.platform import test + + +class RateTest(test.TestCase): + + @test_util.run_in_graph_and_eager_modes() + def testBuildRate(self): + m = rate.Rate() + m.build( + constant_op.constant([1], dtype=dtypes.float32), + constant_op.constant([2], dtype=dtypes.float32)) + old_numer = m.numer + m( + constant_op.constant([2], dtype=dtypes.float32), + constant_op.constant([2], dtype=dtypes.float32)) + self.assertTrue(old_numer is m.numer) + + @test_util.run_in_graph_and_eager_modes() + def testBasic(self): + with self.test_session(): + r_ = rate.Rate() + a = r_(array_ops.ones([1]), denominator=array_ops.ones([1])) + self.evaluate(variables.global_variables_initializer()) + self.evaluate(variables.local_variables_initializer()) + self.assertEqual([[1]], self.evaluate(a)) + b = r_(constant_op.constant([2]), denominator=constant_op.constant([2])) + self.assertEqual([[1]], self.evaluate(b)) + c = r_(constant_op.constant([4]), denominator=constant_op.constant([3])) + self.assertEqual([[2]], self.evaluate(c)) + d = r_(constant_op.constant([16]), denominator=constant_op.constant([3])) + self.assertEqual([[0]], self.evaluate(d)) # divide by 0 + + def testNamesWithSpaces(self): + m1 = rate.Rate(name="has space") + m1(array_ops.ones([1]), array_ops.ones([1])) + self.assertEqual(m1.name, "has space") + self.assertEqual(m1.prev_values.name, "has_space_1/prev_values:0") + + @test_util.run_in_graph_and_eager_modes() + def testWhileLoop(self): + with self.test_session(): + r_ = rate.Rate() + + def body(value, denom, i, ret_rate): + i += 1 + ret_rate = r_(value, denom) + with ops.control_dependencies([ret_rate]): + value = math_ops.add(value, 2) + denom = math_ops.add(denom, 1) + return [value, denom, i, ret_rate] + + def condition(v, d, i, r): + del v, d, r # unused vars by condition + return math_ops.less(i, 100) + + i = constant_op.constant(0) + value = constant_op.constant([1], dtype=dtypes.float64) + denom = constant_op.constant([1], dtype=dtypes.float64) + ret_rate = r_(value, denom) + self.evaluate(variables.global_variables_initializer()) + self.evaluate(variables.local_variables_initializer()) + loop = control_flow_ops.while_loop(condition, body, + [value, denom, i, ret_rate]) + self.assertEqual([[2]], self.evaluate(loop[3])) + + +if __name__ == "__main__": + test.main() -- cgit v1.2.3