tensor_stream 0.7.0 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. checksums.yaml +5 -5
  2. data/.rubocop.yml +6 -1
  3. data/CHANGELOG.md +10 -0
  4. data/README.md +35 -0
  5. data/lib/tensor_stream.rb +2 -2
  6. data/lib/tensor_stream/debugging/debugging.rb +2 -1
  7. data/lib/tensor_stream/dynamic_stitch.rb +23 -24
  8. data/lib/tensor_stream/evaluator/base_evaluator.rb +27 -18
  9. data/lib/tensor_stream/evaluator/opencl/kernels/apply_momentum.cl +16 -0
  10. data/lib/tensor_stream/evaluator/opencl/kernels/pack.cl +24 -0
  11. data/lib/tensor_stream/evaluator/opencl/kernels/softmax_cross.cl +6 -1
  12. data/lib/tensor_stream/evaluator/opencl/opencl_buffer.rb +6 -6
  13. data/lib/tensor_stream/evaluator/opencl/opencl_evaluator.rb +237 -107
  14. data/lib/tensor_stream/evaluator/operation_helpers/array_ops_helper.rb +97 -7
  15. data/lib/tensor_stream/evaluator/ruby_evaluator.rb +230 -123
  16. data/lib/tensor_stream/exceptions.rb +1 -0
  17. data/lib/tensor_stream/graph_builder.rb +2 -3
  18. data/lib/tensor_stream/graph_deserializers/protobuf.rb +22 -23
  19. data/lib/tensor_stream/graph_serializers/graphml.rb +26 -29
  20. data/lib/tensor_stream/graph_serializers/pbtext.rb +22 -19
  21. data/lib/tensor_stream/helpers/string_helper.rb +4 -5
  22. data/lib/tensor_stream/math_gradients.rb +141 -77
  23. data/lib/tensor_stream/nn/nn_ops.rb +4 -6
  24. data/lib/tensor_stream/operation.rb +139 -120
  25. data/lib/tensor_stream/ops.rb +36 -3
  26. data/lib/tensor_stream/session.rb +7 -11
  27. data/lib/tensor_stream/tensor.rb +3 -3
  28. data/lib/tensor_stream/tensor_shape.rb +5 -0
  29. data/lib/tensor_stream/train/gradient_descent_optimizer.rb +4 -37
  30. data/lib/tensor_stream/train/momentum_optimizer.rb +48 -0
  31. data/lib/tensor_stream/train/optimizer.rb +129 -0
  32. data/lib/tensor_stream/train/saver.rb +0 -1
  33. data/lib/tensor_stream/train/slot_creator.rb +62 -0
  34. data/lib/tensor_stream/train/utils.rb +11 -12
  35. data/lib/tensor_stream/trainer.rb +3 -0
  36. data/lib/tensor_stream/utils.rb +18 -11
  37. data/lib/tensor_stream/variable.rb +19 -12
  38. data/lib/tensor_stream/variable_scope.rb +1 -1
  39. data/lib/tensor_stream/version.rb +1 -1
  40. data/samples/iris.rb +2 -1
  41. data/samples/linear_regression.rb +3 -1
  42. data/samples/nearest_neighbor.rb +2 -0
  43. data/test_samples/neural_network_raw.py +101 -0
  44. data/test_samples/raw_neural_net_sample.rb +6 -4
  45. data/test_samples/test2.py +73 -27
  46. metadata +9 -3
@@ -30,6 +30,10 @@ module TensorStream
30
30
  true
31
31
  end
32
32
 
33
+ def is_fully_defined?
34
+ known?
35
+ end
36
+
33
37
  def self.infer_shape(shape_a, shape_b)
34
38
  return shape_a if shape_b.nil?
35
39
  return shape_b if shape_a.nil?
@@ -69,6 +73,7 @@ module TensorStream
69
73
 
70
74
  def self.fix_inferred_elements(shape, total_size)
71
75
  return shape if shape.empty?
76
+ return nil if shape[0].is_a?(Tensor)
72
77
 
73
78
  current_size = shape.inject(1) { |product, n| n > 0 ? product * n : product }
74
79
  inferred_size = total_size.nil? ? nil : total_size / current_size
@@ -1,7 +1,7 @@
1
1
  module TensorStream
2
2
  module Train
3
3
  # High Level implementation of the gradient descent algorithm
4
- class GradientDescentOptimizer
4
+ class GradientDescentOptimizer < Optimizer
5
5
  include TensorStream::OpHelper
6
6
 
7
7
  attr_accessor :learning_rate
@@ -10,43 +10,10 @@ module TensorStream
10
10
  @learning_rate = learning_rate
11
11
  end
12
12
 
13
- def minimize(loss, var_list: nil, grad_loss: nil, global_step: nil)
14
- grads_and_vars = compute_gradients(loss, var_list: var_list, grad_loss: grad_loss)
15
- apply_gradients(grads_and_vars, global_step: global_step)
16
- end
17
-
18
- ##
19
- # Apply gradients to variables.
20
- # This is the second part of minimize(). It returns an Operation that applies gradients.
21
- def apply_gradients(grads_and_vars, global_step: nil)
22
- apply_ops = grads_and_vars.map do |grad, var|
23
- i_op(:apply_gradient_descent, var, TensorStream.cast(@learning_rate, grad.data_type), grad)
24
- end
25
-
26
- if global_step.nil?
27
- apply_ops
28
- else
29
- apply_ops + [global_step.assign_add(1)]
30
- end
31
- end
32
-
33
- ##
34
- # Compute gradients of loss for the variables in var_list.
35
- #
36
- # This is the first part of minimize(). It returns a list of (gradient, variable) pairs where "gradient" is the gradient for "variable".
37
- def compute_gradients(loss, var_list: nil, grad_loss: nil)
38
- trainable_vars = if var_list
39
- raise "var_list must be an array" unless var_list.is_a?(Array)
40
- var_list.each_with_index { |var, index| raise "var #{index} not a Variable" unless var.is_a?(Variable) }
13
+ protected
41
14
 
42
- var_list
43
- else
44
- loss.graph.get_collection(TensorStream::GraphKeys::TRAINABLE_VARIABLES)
45
- end
46
- all_grads = grad_loss || TensorStream.gradients(loss, trainable_vars)
47
- trainable_vars.each_with_index.collect do |var, index|
48
- [all_grads[index], var]
49
- end
15
+ def apply_dense(grad, var)
16
+ i_op(:apply_gradient_descent, var, TensorStream.cast(@learning_rate, grad.data_type), grad)
50
17
  end
51
18
  end
52
19
  end
@@ -0,0 +1,48 @@
1
+ module TensorStream
2
+ module Train
3
+ # Optimizer that implements the Momentum algorithm. loosely based on the tensorflow implementation.
4
+ class MomentumOptimizer < Optimizer
5
+ include OpHelper
6
+
7
+ ##
8
+ # Construct a new Momentum optimizer.
9
+ #
10
+ # Args:
11
+ # learning_rate: A Tensor or a floating point value that indicates the learning rate
12
+ # momentum: A Tensor or a floating point value for the momentum
13
+ # name: Optional name prefix
14
+ # use_nesterov: boolean - Flag that indicates if nesterov momentum is to be used. http://jmlr.org/proceedings/papers/v28/sutskever13.pdf
15
+ # use_locking: boolean - filler argument for compatibility, not used at the moment
16
+ def initialize(learning_rate, momentum, name: 'momentum', use_nesterov: false, use_locking: false)
17
+ @learning_rate = learning_rate
18
+ @momentum = momentum
19
+ @use_nesterov = use_nesterov
20
+ super(name: name, use_locking: use_locking)
21
+ end
22
+
23
+ protected
24
+
25
+ def prepare
26
+ @learning_rate_tensor = TensorStream.convert_to_tensor(@learning_rate, name: "learning_rate")
27
+ @momentum_tensor = TensorStream.convert_to_tensor(@momentum, name: "momentum")
28
+ end
29
+
30
+ def create_slots(var_list)
31
+ var_list.each do |v|
32
+ zeros_slot(v, "momentum", @name)
33
+ end
34
+ end
35
+
36
+ def apply_dense(grad, var)
37
+ mom = get_slot(var, "momentum")
38
+
39
+ _op(:apply_momentum, var, mom,
40
+ TensorStream.cast(@learning_rate_tensor, var.data_type),
41
+ grad,
42
+ TensorStream.cast(@momentum_tensor, var.data_type),
43
+ use_locking: @use_locking,
44
+ use_nesterov: @use_nesterov)
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,129 @@
1
+ module TensorStream
2
+ module Train
3
+ # Base class for an optimizer
4
+ # This is a straight up port from the python version
5
+ class Optimizer
6
+ include SlotCreator
7
+
8
+ attr_reader :name
9
+
10
+ def initialize(name:, use_locking:)
11
+ @name = name
12
+ @use_locking = use_locking
13
+ raise TensorStream::ValueError, "Must specify the optimizer name" unless @name
14
+ @slots = {}
15
+ end
16
+
17
+ def minimize(loss, var_list: nil, grad_loss: nil, global_step: nil, name: nil)
18
+ grads_and_vars = compute_gradients(loss, var_list: var_list, grad_loss: grad_loss)
19
+ apply_gradients(grads_and_vars, global_step: global_step, name: name)
20
+ end
21
+
22
+ ##
23
+ # Apply gradients to variables.
24
+ # This is the second part of minimize(). It returns an Operation that applies gradients.
25
+ def apply_gradients(grads_and_vars, global_step: nil, name: nil)
26
+ varlist = grads_and_vars.map { |_grad, var| var }
27
+ create_slots(varlist)
28
+ TensorStream.name_scope(name, default: @name) do
29
+ prepare
30
+ apply_ops = grads_and_vars.map do |grad, var|
31
+ TensorStream.name_scope("update_" + var.op.name) do
32
+ apply_dense(grad, var)
33
+ end
34
+ end
35
+
36
+ if global_step.nil?
37
+ finish(apply_ops, name)
38
+ else
39
+ TensorStream.control_dependencies([finish(apply_ops, "update")]) do
40
+ global_step.assign_add(1)
41
+ end
42
+ end
43
+ end
44
+ end
45
+
46
+ ##
47
+ # Compute gradients of loss for the variables in var_list.
48
+ #
49
+ # This is the first part of minimize(). It returns a list of (gradient, variable) pairs where "gradient" is the gradient for "variable".
50
+ def compute_gradients(loss, var_list: nil, grad_loss: nil)
51
+ trainable_vars = if var_list
52
+ raise "var_list must be an array" unless var_list.is_a?(Array)
53
+ var_list.each_with_index { |var, index| raise "var #{index} not a Variable" unless var.is_a?(Variable) }
54
+
55
+ var_list
56
+ else
57
+ loss.graph.get_collection(TensorStream::GraphKeys::TRAINABLE_VARIABLES)
58
+ end
59
+ all_grads = grad_loss || TensorStream.gradients(loss, trainable_vars)
60
+ trainable_vars.each_with_index.collect do |var, index|
61
+ [all_grads[index], var]
62
+ end
63
+ end
64
+
65
+ def get_slot(var, name)
66
+ named_slots = @slots.fetch(name, nil)
67
+ return nil if named_slots.nil?
68
+ named_slots.fetch(var_key(var), nil)
69
+ end
70
+
71
+ def get_slot_names
72
+ @slots.keys.sort
73
+ end
74
+
75
+ protected
76
+
77
+ def finish(update_ops, name_scope)
78
+ TensorStream.group(update_ops, name: name_scope)
79
+ end
80
+
81
+ def create_slots(var_list)
82
+ # no implementation
83
+ end
84
+
85
+ def prepare
86
+ # no implementation
87
+ end
88
+
89
+ def apply_dense(grad, var)
90
+ raise TensorStream::NotImplementedError, "not implemented"
91
+ end
92
+
93
+ ##
94
+ # Find or create a slot initialized with 0.0.
95
+ #
96
+ # Args:
97
+ # var: Variable - A Variable object
98
+ # slot_name: string - Name fot the slot
99
+ # op_name: string - Name to use when scoping the Variable that needs to be created
100
+ def zeros_slot(var, slot_name, op_name)
101
+ named_slots = slot_dict(slot_name)
102
+ if !named_slots.key?(var_key(var))
103
+ named_slots[var_key(var)] = create_zeros_slot(var, op_name)
104
+ end
105
+ named_slots[var_key(var)]
106
+ end
107
+
108
+ ##
109
+ # Returns a dict for caching slots created under the given name.
110
+ #
111
+ # Args:
112
+ # slot_name string Name for the slot
113
+ #
114
+ # Returns: A dict that maps primary 'Variable' objects to the slot created
115
+ def slot_dict(slot_name)
116
+ named_slots = @slots.fetch(slot_name, nil)
117
+ if named_slots.nil?
118
+ named_slots = {}
119
+ @slots[slot_name] = named_slots
120
+ end
121
+ named_slots
122
+ end
123
+
124
+ def var_key(var)
125
+ [var.op.graph, var.op.name]
126
+ end
127
+ end
128
+ end
129
+ end
@@ -55,7 +55,6 @@ module TensorStream
55
55
  build_save: true,
56
56
  build_restore: true)
57
57
  saveables = _validate_and_slice_inputs(names_to_saveables)
58
-
59
58
  end
60
59
 
61
60
  def _validate_and_slice_inputs(names_to_saveables)
@@ -0,0 +1,62 @@
1
+ module TensorStream
2
+ module Train
3
+ module SlotCreator
4
+ include TensorStream::Utils
5
+
6
+ ##
7
+ # Helper function for creating a slot variable.
8
+ def create_slot_var(primary, val, scope)
9
+ slot = get_variable(scope, initializer: val, trainable: false,
10
+ validate_shape: val.shape.is_fully_defined?)
11
+ slot
12
+ end
13
+
14
+ ##
15
+ # Create a slot initialized to the given value
16
+ #
17
+ # Args:
18
+ # primary: Variable - The primary 'Variable' or 'Tensor'
19
+ # val: Tensor - A `Tensor` specifying the initial value of the slot
20
+ # name: String - Name to use for the slot variable
21
+ # colocate_with_primary: Boolean - If true the slot is located
22
+ # on the same device as `primary`
23
+ #
24
+ # Returns: A `Variable` object
25
+ def create_slot(primary, val, name, colocate_with_primary: true)
26
+ TensorStream.variable_scope(primary.op.name + "/" + name) do
27
+ if colocate_with_primary
28
+ TensorStream.colocate_with(primary) do
29
+ return create_slot_var(primary, val, "")
30
+ end
31
+ else
32
+ return create_slot_var(primary, val, "")
33
+ end
34
+ end
35
+ end
36
+
37
+ ##
38
+ # Create a slot initialized to 0 with same shape as the primary object.
39
+ #
40
+ # Args:
41
+ # primary: The pirmary variable or Tensor
42
+ # name: String - Name to use for the slot variable
43
+ # dtype: Symbol - Type of the slot variable
44
+ # colocate_with_primary: boolean - If true the slot is located on the same device as primary
45
+ #
46
+ # Returns:
47
+ # A `Variable` object
48
+ def create_zeros_slot(primary, name, dtype: nil, colocate_with_primary: true)
49
+ dtype = primary.data_type if dtype.nil?
50
+ slot_shape = primary.shape
51
+ slot_shape = if slot_shape.is_fully_defined?
52
+ slot_shape.shape
53
+ else
54
+ TensorStream.shape(primary.initialized_value)
55
+ end
56
+ val = TensorStream.zeros(slot_shape, dtype: dtype)
57
+ create_slot(primary, val, name,
58
+ colocate_with_primary: colocate_with_primary)
59
+ end
60
+ end
61
+ end
62
+ end
@@ -6,24 +6,23 @@ module TensorStream
6
6
  target_graph = graph || TensorStream.get_default_graph
7
7
  raise TensorStream::ValueError, '"global_step" already exists.' unless get_global_step(target_graph).nil?
8
8
 
9
- TensorStream.variable_scope.get_variable(
10
- TensorStream::GraphKeys::GLOBAL_STEP, shape: [],
11
- dtype: :int64,
12
- initializer: TensorStream.zeros_initializer,
13
- trainable: false,
14
- collections: [TensorStream::GraphKeys::GLOBAL_VARIABLES,
15
- TensorStream::GraphKeys::GLOBAL_STEP])
9
+ TensorStream.variable_scope.get_variable(TensorStream::GraphKeys::GLOBAL_STEP, shape: [],
10
+ dtype: :int64,
11
+ initializer: TensorStream.zeros_initializer,
12
+ trainable: false,
13
+ collections: [TensorStream::GraphKeys::GLOBAL_VARIABLES,
14
+ TensorStream::GraphKeys::GLOBAL_STEP])
16
15
  end
17
16
 
18
17
  def get_global_step(graph = nil)
19
18
  target_graph = graph || TensorStream.get_default_graph
20
19
  global_step_tensors = target_graph.get_collection(TensorStream::GraphKeys::GLOBAL_STEP)
21
20
  global_step_tensor = if global_step_tensors.nil? || global_step_tensors.empty?
22
- begin
23
- target_graph.get_tensor_by_name('global_step:0')
24
- rescue TensorStream::KeyError
25
- nil
26
- end
21
+ begin
22
+ target_graph.get_tensor_by_name('global_step:0')
23
+ rescue TensorStream::KeyError
24
+ nil
25
+ end
27
26
  elsif global_step_tensors.size == 1
28
27
  global_step_tensors[0]
29
28
  else
@@ -1,4 +1,7 @@
1
+ require 'tensor_stream/train/slot_creator'
2
+ require 'tensor_stream/train/optimizer'
1
3
  require 'tensor_stream/train/gradient_descent_optimizer'
4
+ require 'tensor_stream/train/momentum_optimizer'
2
5
  require 'tensor_stream/train/saver'
3
6
 
4
7
  module TensorStream
@@ -54,14 +54,14 @@ module TensorStream
54
54
  trainable: trainable
55
55
  }
56
56
  tensor = if value.is_a?(String)
57
- TensorStream::Variable.new(dtype || :string, 0, [], get_variable_scope, common_options)
58
- elsif value.is_a?(Integer)
59
- TensorStream::Variable.new(dtype || :int32, 0, [], get_variable_scope, common_options)
60
- elsif value.is_a?(Float)
61
- TensorStream::Variable.new(dtype || :float32, 0, [], get_variable_scope, common_options)
62
- else
63
- TensorStream::Variable.new(dtype || :float32, 0, nil, get_variable_scope, common_options)
64
- end
57
+ TensorStream::Variable.new(dtype || :string, 0, [], get_variable_scope, common_options)
58
+ elsif value.is_a?(Integer)
59
+ TensorStream::Variable.new(dtype || :int32, 0, [], get_variable_scope, common_options)
60
+ elsif value.is_a?(Float)
61
+ TensorStream::Variable.new(dtype || :float32, 0, [], get_variable_scope, common_options)
62
+ else
63
+ TensorStream::Variable.new(dtype || :float32, 0, nil, get_variable_scope, common_options)
64
+ end
65
65
  op.inputs[0] = tensor
66
66
  tensor
67
67
  end
@@ -115,8 +115,13 @@ module TensorStream
115
115
  session
116
116
  end
117
117
 
118
- def program(&block)
119
- block.call(self)
118
+ def colocate_with(op, ignore_existing: false)
119
+ # noop for now
120
+ yield
121
+ end
122
+
123
+ def program
124
+ yield self
120
125
  end
121
126
 
122
127
  def layers
@@ -153,7 +158,7 @@ module TensorStream
153
158
  TensorStream::DynamicStitch.new(:dynamic_stitch, [indices, data], name: name)
154
159
  end
155
160
 
156
- def get_variable(name, dtype: nil, shape: nil, initializer: nil, trainable: true, collections: nil)
161
+ def get_variable(name, dtype: nil, shape: nil, initializer: nil, trainable: true, collections: nil, validate_shape: false)
157
162
  get_variable_scope.get_variable(name, dtype: dtype, shape: shape, initializer: initializer, trainable: trainable, collections: collections)
158
163
  end
159
164
 
@@ -184,6 +189,8 @@ module TensorStream
184
189
  TensorStream.get_default_graph.get_collection(TensorStream::GraphKeys::TRAINABLE_VARIABLES)
185
190
  end
186
191
 
192
+ ##
193
+ # Sets random seed to use for the default graph
187
194
  def set_random_seed(seed)
188
195
  TensorStream.get_default_graph.random_seed = seed
189
196
  end
@@ -14,10 +14,9 @@ module TensorStream
14
14
  scope_name = variable_scope ? variable_scope.name : nil
15
15
  variable_scope_initializer = variable_scope ? variable_scope.initializer : nil
16
16
  @name = [scope_name, options[:name] || build_name].compact.reject(&:empty?).join('/')
17
- @initalizer_tensor = options[:initializer] ? options[:initializer] : variable_scope_initializer || TensorStream.glorot_uniform_initializer
18
- if shape.nil? && @initalizer_tensor && @initalizer_tensor.shape
19
- shape = @initalizer_tensor.shape.shape
20
- end
17
+ @initalizer_tensor = options[:initializer] || variable_scope_initializer || TensorStream.glorot_uniform_initializer
18
+ shape = @initalizer_tensor.shape.shape if shape.nil? && @initalizer_tensor && @initalizer_tensor.shape
19
+
21
20
  @shape = TensorShape.new(shape, rank)
22
21
  @trainable = options.fetch(:trainable, true)
23
22
  @graph.add_variable(self, options)
@@ -34,22 +33,30 @@ module TensorStream
34
33
  assign(init_op)
35
34
  end
36
35
 
36
+ def initialized_value
37
+ init_op = @initalizer_tensor.op
38
+ init_op.shape = @shape || init_op.shape
39
+ init_op.data_type = @data_type || init_op.data_type
40
+ init_op
41
+ end
42
+
37
43
  def assign(value, name: nil)
38
44
  _a, value = TensorStream.check_data_types(self, value)
39
- Operation.new(:assign, self, value, name: name)
45
+ _op(:assign, self, value, name: name)
40
46
  end
41
47
 
42
48
  def read_value
43
- if buffer
44
- @value = buffer.to_ruby
45
- end
46
-
49
+ @value = buffer.to_ruby if buffer
47
50
  @value
48
51
  end
49
52
 
50
- def assign_add(value)
53
+ def assign_add(value, name: nil)
51
54
  _a, value = TensorStream.check_data_types(self, value)
52
- Operation.new(:assign_add, self, value, data_type: data_type)
55
+ _op(:assign_add, self, value, data_type: data_type, name: name)
56
+ end
57
+
58
+ def op
59
+ @op ||= _op(:variable, self, data_type: data_type)
53
60
  end
54
61
 
55
62
  def to_math(_tensor, _name_only = false, _max_depth = 99, _unused = 0)
@@ -58,7 +65,7 @@ module TensorStream
58
65
 
59
66
  def assign_sub(value)
60
67
  _a, value = TensorStream.check_data_types(self, value)
61
- Operation.new(:assign_sub, self, value)
68
+ _op(:assign_sub, self, value)
62
69
  end
63
70
 
64
71
  def self.variables_initializer(collection)