tensor_stream 1.0.4 → 1.0.9

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/CHANGELOG.md +12 -2
  4. data/Dockerfile +1 -1
  5. data/USAGE_GUIDE.md +68 -0
  6. data/lib/tensor_stream.rb +1 -0
  7. data/lib/tensor_stream/evaluator/base_evaluator.rb +21 -1
  8. data/lib/tensor_stream/evaluator/evaluator.rb +1 -0
  9. data/lib/tensor_stream/evaluator/evaluator_utils.rb +20 -0
  10. data/lib/tensor_stream/evaluator/operation_helpers/array_ops_helper.rb +60 -0
  11. data/lib/tensor_stream/evaluator/ruby/array_ops.rb +53 -1
  12. data/lib/tensor_stream/evaluator/ruby/images_ops.rb +26 -0
  13. data/lib/tensor_stream/evaluator/ruby/math_ops.rb +60 -5
  14. data/lib/tensor_stream/evaluator/ruby/nn_ops.rb +25 -29
  15. data/lib/tensor_stream/evaluator/ruby/random_ops.rb +7 -11
  16. data/lib/tensor_stream/evaluator/ruby/storage_manager.rb +40 -0
  17. data/lib/tensor_stream/evaluator/ruby/variable_ops.rb +74 -0
  18. data/lib/tensor_stream/evaluator/ruby_evaluator.rb +31 -77
  19. data/lib/tensor_stream/generated_stub/ops.rb +256 -166
  20. data/lib/tensor_stream/generated_stub/stub_file.erb +4 -4
  21. data/lib/tensor_stream/graph.rb +3 -3
  22. data/lib/tensor_stream/graph_deserializers/yaml_loader.rb +4 -6
  23. data/lib/tensor_stream/helpers/infer_shape.rb +1 -7
  24. data/lib/tensor_stream/helpers/tensor_mixins.rb +10 -1
  25. data/lib/tensor_stream/images.rb +4 -0
  26. data/lib/tensor_stream/math/math_ops.rb +22 -0
  27. data/lib/tensor_stream/math_gradients.rb +15 -1
  28. data/lib/tensor_stream/nn/embedding_lookup.rb +114 -0
  29. data/lib/tensor_stream/nn/nn_ops.rb +16 -0
  30. data/lib/tensor_stream/op_maker.rb +36 -3
  31. data/lib/tensor_stream/operation.rb +8 -20
  32. data/lib/tensor_stream/ops.rb +14 -11
  33. data/lib/tensor_stream/ops/bias_add.rb +16 -0
  34. data/lib/tensor_stream/ops/equal.rb +4 -0
  35. data/lib/tensor_stream/ops/greater.rb +4 -0
  36. data/lib/tensor_stream/ops/greater_equal.rb +4 -0
  37. data/lib/tensor_stream/ops/less.rb +19 -0
  38. data/lib/tensor_stream/ops/less_equal.rb +4 -0
  39. data/lib/tensor_stream/ops/not_equal.rb +19 -0
  40. data/lib/tensor_stream/ops/rsqrt.rb +11 -0
  41. data/lib/tensor_stream/ops/strided_slice.rb +24 -0
  42. data/lib/tensor_stream/ops/sum.rb +4 -2
  43. data/lib/tensor_stream/ops/top_k.rb +23 -0
  44. data/lib/tensor_stream/session.rb +6 -12
  45. data/lib/tensor_stream/tensor.rb +1 -0
  46. data/lib/tensor_stream/tensor_shape.rb +32 -1
  47. data/lib/tensor_stream/train/saver.rb +2 -3
  48. data/lib/tensor_stream/utils.rb +18 -13
  49. data/lib/tensor_stream/utils/freezer.rb +5 -1
  50. data/lib/tensor_stream/utils/py_ports.rb +11 -0
  51. data/lib/tensor_stream/variable.rb +9 -6
  52. data/lib/tensor_stream/version.rb +1 -1
  53. data/samples/word_embeddings/word_embedding_1.rb +192 -0
  54. data/samples/word_embeddings/word_embedding_2.rb +203 -0
  55. data/tensor_stream.gemspec +7 -2
  56. metadata +67 -10
@@ -70,9 +70,8 @@ module TensorStream
70
70
 
71
71
  ##
72
72
  # Outputs random values from a truncated normal distribution.
73
- def truncated_normal(shape, dtype: :float32, mean: 0.0, stddev: 1.0, seed: nil, name: nil)
74
- options = {dtype: dtype, mean: mean, stddev: stddev, seed: seed, name: name}
75
- _op(:truncated_normal, shape, options)
73
+ def truncated_normal(shape, dtype: :float32, mean: 0.0, stddev: 1.0, seed: nil, name: nil, pre_gen_table_size: nil)
74
+ _op(:truncated_normal, shape, dtype: dtype, mean: mean, stddev: stddev, seed: seed, name: name, pre_gen_table_size: pre_gen_table_size)
76
75
  end
77
76
 
78
77
  ##
@@ -163,14 +162,6 @@ module TensorStream
163
162
  _op(:ones, shape, data_type: dtype, name: name)
164
163
  end
165
164
 
166
- ##
167
- # Returns the truth value of (x < y) element-wise.
168
- # This operation supports broadcasting
169
- def less(input_a, input_b, name: nil)
170
- check_data_types(input_a, input_b)
171
- _op(:less, input_a, input_b, name: name)
172
- end
173
-
174
165
  ##
175
166
  # Returns the truth value of x AND y element-wise.
176
167
  def logical_and(input_a, input_b, name: nil)
@@ -203,6 +194,15 @@ module TensorStream
203
194
  end
204
195
  end
205
196
 
197
+ ##
198
+ # Partitions data into num_partitions tensors using indices from partitions
199
+ def dynamic_partition(data, partitions, num_partitions, name: nil)
200
+ result = _op(:dynamic_partition, data, partitions, num_partitions: num_partitions, name: nil)
201
+ num_partitions.times.map do |index|
202
+ result[index]
203
+ end
204
+ end
205
+
206
206
  def split(value, num_or_size_splits, axis: 0, num: nil, name: "split")
207
207
  value = convert_to_tensor(value)
208
208
  num_or_size_splits = convert_to_tensor(num_or_size_splits)
@@ -532,6 +532,9 @@ module TensorStream
532
532
  _op(:squeeze, value, axis: axis, name: nil)
533
533
  end
534
534
 
535
+ def clip_by_norm(tensor, clip_norm, axes: nil, name: nil)
536
+ end
537
+
535
538
  ##
536
539
  # Computes the difference between two lists of numbers or strings.
537
540
  # Given a list x and a list y, this operation returns a list out that represents all values
@@ -0,0 +1,16 @@
1
+ TensorStream::OpMaker.define_operation :bias_add do |op|
2
+ op.what_it_does "Adds bias to value."
3
+
4
+ op.parameter :value, "A Tensor", :nil, validate: 'NUMERIC_TYPES'
5
+ op.parameter :bias, "A 1 D tensor", :nil, validate: 'NUMERIC_TYPES'
6
+
7
+ op.supports_broadcasting!
8
+ op.exclude!
9
+
10
+ op.option :name, "Optional name", :nil
11
+ op.option :data_format, "A string. 'NHWC' and 'NCHW' are supported.", :nil
12
+
13
+ op.define_gradient do |grad, node, _params|
14
+ [grad, _op(:bias_add_grad, grad, data_format: node.options[:data_format])]
15
+ end
16
+ end
@@ -12,4 +12,8 @@ TensorStream::OpMaker.define_operation :equal do |op|
12
12
  op.define_gradient do |grad, node, params|
13
13
  _min_or_max_grad(node.inputs, grad, ->(a, b) { ts.equal(a, b) })
14
14
  end
15
+
16
+ op.define_data_type do
17
+ :boolean
18
+ end
15
19
  end
@@ -8,4 +8,8 @@ TensorStream::OpMaker.define_operation :greater do |op|
8
8
  op.supports_broadcasting!
9
9
 
10
10
  op.option :name, "Optional name", :nil
11
+
12
+ op.define_data_type do
13
+ :boolean
14
+ end
11
15
  end
@@ -8,4 +8,8 @@ TensorStream::OpMaker.define_operation :greater_equal do |op|
8
8
  op.supports_broadcasting!
9
9
 
10
10
  op.option :name, "Optional name", :nil
11
+
12
+ op.define_data_type do
13
+ :boolean
14
+ end
11
15
  end
@@ -0,0 +1,19 @@
1
+ TensorStream::OpMaker.define_operation :less do |op|
2
+ op.what_it_does "Returns the truth value of (x < y) element-wise."
3
+
4
+ op.parameter :input_a, "tensor X"
5
+ op.parameter :input_b, "tensor Y"
6
+
7
+ op.apply_data_type_coercion!
8
+ op.supports_broadcasting!
9
+
10
+ op.option :name, "Optional name", :nil
11
+
12
+ op.define_gradient do |grad, node, _params|
13
+ _min_or_max_grad(node.inputs, grad, ->(a, b) { ts.less(a, b) })
14
+ end
15
+
16
+ op.define_data_type do
17
+ :boolean
18
+ end
19
+ end
@@ -12,4 +12,8 @@ TensorStream::OpMaker.define_operation :less_equal do |op|
12
12
  op.define_gradient do |grad, node, params|
13
13
  _min_or_max_grad(node.inputs, grad, ->(a, b) { ts.greater_equal(a, b) })
14
14
  end
15
+
16
+ op.define_data_type do
17
+ :boolean
18
+ end
15
19
  end
@@ -0,0 +1,19 @@
1
+ TensorStream::OpMaker.define_operation :not_equal do |op|
2
+ op.what_it_does "Returns the truth value of (x != y) element-wise."
3
+
4
+ op.parameter :input_a, "tensor X"
5
+ op.parameter :input_b, "tensor Y"
6
+
7
+ op.apply_data_type_coercion!
8
+ op.supports_broadcasting!
9
+
10
+ op.option :name, "Optional name", :nil
11
+
12
+ op.define_gradient do |grad, node, params|
13
+ _min_or_max_grad(node.inputs, grad, ->(a, b) { ts.not_equal(a, b) })
14
+ end
15
+
16
+ op.define_data_type do
17
+ :boolean
18
+ end
19
+ end
@@ -0,0 +1,11 @@
1
+ TensorStream::OpMaker.define_operation :rsqrt do |op|
2
+ op.what_it_does "Computes reciprocal of square root of x element-wise."
3
+
4
+ op.parameter :input_a, "tensor X", validate: 'FLOATING_POINT_TYPES'
5
+ op.option :name, "Optional name", :nil
6
+
7
+ op.define_gradient do |grad, node, params|
8
+ # Returns -0.5 * grad * conj(y)^3.
9
+ i_op(:rsqrt_grad, node, grad)
10
+ end
11
+ end
@@ -0,0 +1,24 @@
1
+ TensorStream::OpMaker.define_operation :strided_slice do |op|
2
+ op.what_it_does "Extracts a strided slice of a tensor "
3
+ op.what_it_does "this op extracts a slice of size `(end-begin)/stride`
4
+ from the given `input_` tensor. Starting at the location specified by `begin`
5
+ the slice continues by adding `stride` to the index until all dimensions are
6
+ not less than `end`.
7
+ Note that a stride can be negative, which causes a reverse slice."
8
+
9
+ op.parameter :input, "A tensor"
10
+ op.parameter :_begin, "start index"
11
+ op.parameter :_end, "end index"
12
+ op.parameter :strides, "end index", :nil
13
+ op.option :name, "Optional name", :nil
14
+
15
+ op.define_gradient do |grad, node, params|
16
+ input, b_index, e_index, strides = params
17
+ x = ts.shape(input, out_type: node.inputs[0].data_type)
18
+
19
+ _op(:strided_slice_grad, x, b_index, e_index, strides, grad)
20
+ end
21
+
22
+ op.define_shape do |tensor|
23
+ end
24
+ end
@@ -7,14 +7,16 @@ TensorStream::OpMaker.define_operation :sum do |op|
7
7
  op.what_it_does "If axis has no entries, all dimensions are reduced, and a tensor with a single element is returned."
8
8
 
9
9
  op.parameter :input_a, "tensor X"
10
- op.parameter :axis, "tensor X", :nil, validate: 'INTEGER_TYPES'
10
+ op.parameter :axis_p, "tensor X", :nil, validate: 'INTEGER_TYPES'
11
11
 
12
+ op.option :axis, "axis", :nil, exclude: true
12
13
  op.option :name, "Optional name", :nil
13
14
  op.option :keepdims, "If true, retains reduced dimensions with length 1.", :false
14
15
 
15
16
  op.add_custom "input_a = TensorStream.convert_to_tensor(input_a)"
16
17
  op.add_custom "return input_a if input_a.shape.scalar?"
17
- op.add_custom "axis = cast_axis(input_a, axis)"
18
+ op.add_custom "axis_p = axis_p || axis"
19
+ op.add_custom "axis_p = cast_axis(input_a, axis_p)"
18
20
 
19
21
  op.define_gradient do |grad, node, params|
20
22
  x, y = params
@@ -0,0 +1,23 @@
1
+ TensorStream::OpMaker.define_operation :top_k do |op|
2
+ op.what_it_does "Finds values and indices of the `k` largest entries for the last dimension."
3
+
4
+ op.parameter :input, "1-D or higher `Tensor` with last dimension at least `k`."
5
+ op.parameter :k, "0-D `int32` `Tensor`. Number of top elements to look for along the last dimension (along each row for matrices)", 1
6
+ op.option :sorted, "If true the resulting `k` elements will be sorted by the values in descending order.", "true"
7
+ op.option :name, "Optional name", :nil
8
+
9
+ op.add_custom_post "[result[0], result[1]]"
10
+
11
+ op.define_shape do |tensor|
12
+ next nil unless tensor.inputs[0].shape.known?
13
+
14
+ input_shape = tensor.inputs[0].shape.shape.dup
15
+ k = tensor.options[:k]
16
+ input_shape[-1] = k
17
+ input_shape
18
+ end
19
+
20
+ op.define_gradient do |grad, node, params|
21
+ #TODO
22
+ end
23
+ end
@@ -18,17 +18,7 @@ module TensorStream
18
18
  end
19
19
 
20
20
  def get_evaluator_classes(evaluators)
21
- @evaluator_classes = if evaluators.is_a?(Array)
22
- if evaluators.empty?
23
- TensorStream::Evaluator.default_evaluators
24
- else
25
- evaluators.collect { |name| Object.const_get("TensorStream::Evaluator::#{camelize(name.to_s)}") }
26
- end
27
- elsif evaluators.nil?
28
- TensorStream::Evaluator.default_evaluators
29
- else
30
- [Object.const_get("TensorStream::Evaluator::#{camelize(evaluators.to_s)}")]
31
- end
21
+ @evaluator_classes = TensorStream::EvaluatorUtils.get_evaluator_classes(evaluators)
32
22
  end
33
23
 
34
24
  def clear_session_cache
@@ -58,7 +48,8 @@ module TensorStream
58
48
  # scan for placeholders and assign value
59
49
  options[:feed_dict]&.each_key do |k|
60
50
  if k.is_a?(Placeholder)
61
- context[k.name.to_sym] = options[:feed_dict][k]
51
+ ph = options[:feed_dict][k]
52
+ context[k.name.to_sym] = ph.is_a?(Tensor) ? ph.op : ph
62
53
  elsif k.is_a?(String)
63
54
  target_graph = args[0].graph
64
55
  node = target_graph.get_node(k)
@@ -98,6 +89,9 @@ module TensorStream
98
89
  end
99
90
 
100
91
  def close
92
+ # unlink resources to save memory
93
+ @last_session_context = nil
94
+ @session_cache = {}
101
95
  @closed = true
102
96
  end
103
97
 
@@ -4,6 +4,7 @@ module TensorStream
4
4
  # Base class that defines a tensor like interface
5
5
  class Tensor
6
6
  include OpHelper
7
+ extend OpHelper
7
8
  include TensorMixins
8
9
 
9
10
  attr_reader :graph, :value
@@ -18,7 +18,8 @@ module TensorStream
18
18
  end
19
19
 
20
20
  def [](index)
21
- @shape[index]
21
+ new_shape = @shape[index]
22
+ TensorShape.new(@shape[index])
22
23
  end
23
24
 
24
25
  def ndims
@@ -42,6 +43,36 @@ module TensorStream
42
43
  known?
43
44
  end
44
45
 
46
+ def merge_with(other)
47
+ assert_compatible_with(other)
48
+
49
+ if @shape.nil?
50
+ TensorShape.new(other)
51
+ else
52
+ TensorShape.new(@shape)
53
+ end
54
+ end
55
+
56
+ def compatible_with?(other)
57
+ other = as_dimension(other)
58
+
59
+ shape.nil? || other.nil? || shape == other
60
+ end
61
+
62
+ def as_dimension(value)
63
+ value.is_a?(TensorShape) ? value.shape : value
64
+ end
65
+
66
+ def value
67
+ shape
68
+ end
69
+
70
+ ##
71
+ # Raises an exception if `other` is not compatible with this shape.
72
+ def assert_compatible_with(other)
73
+ raise TensorStream::ValueError, "Dimensions #{self} and #{other} are not compatible" unless compatible_with?(other)
74
+ end
75
+
45
76
  def self.infer_shape(shape_a, shape_b)
46
77
  return nil if shape_a.nil? || shape_b.nil?
47
78
  return shape_a if shape_b.empty?
@@ -7,9 +7,9 @@ module TensorStream
7
7
  class Saver
8
8
  include TensorStream::OpHelper
9
9
 
10
- def initialize
10
+ def initialize(var_list = nil)
11
11
  graph = TensorStream::Graph.get_default_graph
12
- vars = graph.get_collection(GraphKeys::GLOBAL_VARIABLES)
12
+ vars = var_list || graph.get_collection(GraphKeys::GLOBAL_VARIABLES)
13
13
 
14
14
  @filename = graph["ts_filename"] || TensorStream.placeholder(:string, name: "ts_filename", shape: [])
15
15
 
@@ -50,7 +50,6 @@ module TensorStream
50
50
  meta_data = JSON.parse(File.read(meta_file))
51
51
  gs = meta_data["gs"]
52
52
  filename = File.join(modelpath, ["model", gs, ".ckpt"].compact.join("-"))
53
-
54
53
  session.run(@restore_op, feed_dict: {@filename => filename})
55
54
  end
56
55
 
@@ -45,25 +45,22 @@ module TensorStream
45
45
  # Creates a variable
46
46
  # A variable maintains state across sessions
47
47
  def variable(value, name: nil, initializer: nil, graph: nil, dtype: nil, trainable: true)
48
- op = Graph.get_default_graph.add_op(:assign, nil, value)
49
48
  common_options = {
50
- initializer: initializer || op,
49
+ initializer: TensorStream.convert_to_tensor(initializer || value),
51
50
  name: name,
52
51
  graph: graph,
53
52
  dtype: dtype,
54
53
  trainable: trainable,
55
54
  }
56
55
  tensor = if value.is_a?(String)
57
- i_var(dtype || :string, 0, [], get_variable_scope, common_options)
58
- elsif value.is_a?(Integer)
59
- i_var(dtype || :int32, 0, [], get_variable_scope, common_options)
60
- elsif value.is_a?(Float)
61
- i_var(dtype || :float32, 0, [], get_variable_scope, common_options)
62
- else
63
- i_var(dtype || :float32, 0, nil, get_variable_scope, common_options)
64
- end
65
- op.set_input(0, tensor.op)
66
- Graph.get_default_graph.add_node(op)
56
+ i_var(dtype || :string, 0, [], get_variable_scope, common_options)
57
+ elsif value.is_a?(Integer)
58
+ i_var(dtype || :int32, 0, [], get_variable_scope, common_options)
59
+ elsif value.is_a?(Float)
60
+ i_var(dtype || :float32, 0, [], get_variable_scope, common_options)
61
+ else
62
+ i_var(dtype || :float32, 0, nil, get_variable_scope, common_options)
63
+ end
67
64
  tensor
68
65
  end
69
66
 
@@ -219,6 +216,10 @@ module TensorStream
219
216
  TensorStream::Trainer
220
217
  end
221
218
 
219
+ def math
220
+ TensorStream::Maths
221
+ end
222
+
222
223
  def image
223
224
  TensorStream::Images
224
225
  end
@@ -242,12 +243,16 @@ module TensorStream
242
243
  return convert_to_tensor(value.call) if value.is_a?(Proc)
243
244
  # raise "Invalid tensor value" if value.nil?
244
245
 
245
- if value.is_a?(Array) && value[0].is_a?(Tensor)
246
+ if value.is_a?(Array) && value.detect { |v| v.is_a?(Tensor) }
246
247
  return TensorStream.stack(value) if value.size > 1
247
248
 
248
249
  return TensorStream.expand_dims(value[0], 0)
249
250
  end
250
251
 
252
+ if value.is_a?(TensorShape)
253
+ value = value.shape
254
+ end
255
+
251
256
  check_if_dense(value)
252
257
  i_cons(value, dtype: dtype || Tensor.detect_type(value), name: name)
253
258
  end
@@ -19,7 +19,11 @@ module TensorStream
19
19
  node = graph.get_tensor_by_name(node_key)
20
20
  case node.operation
21
21
  when :variable_v2
22
- value = node.container
22
+ value = Evaluator.read_variable(node.graph, node.options[:var_name])
23
+ if value.nil?
24
+ raise "#{node.options[:var_name]} has no value"
25
+ end
26
+
23
27
  options = {
24
28
  value: value,
25
29
  data_type: node.data_type,
@@ -0,0 +1,11 @@
1
+ module TensorStream
2
+ module PyPorts
3
+ def floor_div(a, b)
4
+ if (a.is_a?(Float))
5
+ (a.to_i / b.to_i).to_f
6
+ else
7
+ a / b
8
+ end
9
+ end
10
+ end
11
+ end
@@ -46,17 +46,16 @@ module TensorStream
46
46
 
47
47
  def assign(value, name: nil, use_locking: false)
48
48
  TensorStream.check_data_types(self, value)
49
- _op(:assign, self, value, name: name)
49
+ _op(:assign, value, name: name, var_name: @name)
50
50
  end
51
51
 
52
52
  def read_value
53
- @value = buffer.to_ruby if buffer
54
- @value
53
+ Evaluator.read_variable(@graph, @name)
55
54
  end
56
55
 
57
56
  def assign_add(value, name: nil)
58
57
  TensorStream.check_data_types(self, value)
59
- _op(:assign_add, self, value, data_type: data_type, name: name)
58
+ _op(:assign_add, value, data_type: data_type, name: name, var_name: @name)
60
59
  end
61
60
 
62
61
  def to_math(_tensor, _name_only = false, _max_depth = 99, _unused = 0)
@@ -65,11 +64,15 @@ module TensorStream
65
64
 
66
65
  def assign_sub(value)
67
66
  TensorStream.check_data_types(self, value)
68
- _op(:assign_sub, self, value)
67
+ _op(:assign_sub, value, data_type: data_type, name: name, var_name: @name)
69
68
  end
70
69
 
71
70
  def self.variables_initializer(collection)
72
- TensorStream.group(TensorStream.get_default_graph.get_collection(collection).map(&:initializer))
71
+ global_variables_ops = TensorStream.get_default_graph.get_collection(collection).map do |variable|
72
+ _op(:assign, variable.initializer, var_name: variable.name)
73
+ end
74
+
75
+ TensorStream.group(global_variables_ops)
73
76
  end
74
77
 
75
78
  def self.global_variables_initializer