tensor_stream 0.1.3 → 0.1.4

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: adb3c1e45383170db2d5c9f5f3568c251cdddb64
4
- data.tar.gz: 99cd0f23353e8f72af2bda3bb84cf4b61c615445
3
+ metadata.gz: a25008802340137903308c5ec471a1ff4bedfc42
4
+ data.tar.gz: ba3549b627248f16d1afcea678d844473765d399
5
5
  SHA512:
6
- metadata.gz: 62eb911e080ba894d6c349870d86286fecbd02f8ad0b762fe33ca311ffbd500f18f78f80520d5e875dc821b1c0f8d556f45fe913142bc02ae33d931af0fb840c
7
- data.tar.gz: d599f839bd0d6ba706d3c7fafb3e903a17070af7dfd5dbe733ce21e2641bc4428f8e0d204f649f7f80c843aa48ff2e3c98880c36822a071279de5cd62a39b3dc
6
+ metadata.gz: 912fc4724db50b4b45a4cf7af11cd28a1df8dc9589a2347fb9c5374ada51cc34c11c179c70fc392ef0bd5c8c6dc41802d8ee4457df47cf7776ee78d92d1f0b74
7
+ data.tar.gz: 506e86fbd0877c10b51b6106529284c33c6158576e53f2fe720b85213f2ec1577156c614e85a587320e2d8a063dcefee51a59f6325cc1e24a27e8a2e99114ebf
data/README.md CHANGED
@@ -1,3 +1,5 @@
1
+ [![Gem Version](https://badge.fury.io/rb/tensor_stream.svg)](https://badge.fury.io/rb/tensor_stream)
2
+
1
3
  # TensorStream
2
4
 
3
5
  A reimplementation of TensorFlow for ruby. This is a ground up implementation with no dependency on TensorFlow. Effort has been made to make the programming style as near to TensorFlow as possible, comes with a pure ruby evaluator by default as well with support for an opencl evaluator.
@@ -94,7 +96,11 @@ tf.session do |sess|
94
96
  end
95
97
  ```
96
98
 
97
- ## python to ruby guide
99
+ You can take a look at spec/tensor_stream/operation_spec.rb for a list of supported ops and various examples and test cases used. Of course these contain only a
100
+ sliver of what TensorFlow can do, so feel free to file a PR to add requested
101
+ ops and test cases.
102
+
103
+ ## Python to Ruby guide
98
104
 
99
105
  Not all ops are available. Available ops are defined in lib/tensor_stream/ops.rb, corresponding gradients are found at lib/tensor_stream/math_gradients.
100
106
 
@@ -122,7 +128,7 @@ w = tf.Variable(0, 'weights')
122
128
  Ruby
123
129
 
124
130
  ```ruby
125
- w =tf.variable(0, name: 'weights')
131
+ w = tf.variable(0, name: 'weights')
126
132
  ```
127
133
 
128
134
  # Shapes
@@ -150,7 +156,7 @@ W = tf.variable(rand, name: "weight")
150
156
  b = tf.variable(rand, name: "bias")
151
157
  pred = X * W + b
152
158
  cost = tf.reduce_sum(tf.pow(pred - Y, 2)) / ( 2 * 10)
153
- cost.to_math # "(reduce_sum(|((((Placeholder: * weight) + bias) - Placeholder_2:)^2)|) / 10.0)"
159
+ cost.to_math # "(reduce_sum(|((((Placeholder: * weight) + bias) - Placeholder_2:)^2)|) / 20.0)"
154
160
  ```
155
161
 
156
162
  breakpoints can also be set, block will be evaluated during computation
@@ -0,0 +1,100 @@
1
+ module TensorStream
2
+ # varoius utility functions for array processing
3
+ module ArrayOpsHelper
4
+ def broadcast(input_a, input_b)
5
+ sa = shape_eval(input_a)
6
+ sb = shape_eval(input_b)
7
+
8
+ return [input_a, input_b] if sa == sb
9
+
10
+ # descalar
11
+ if sa.empty?
12
+ input_a = [input_a]
13
+ sa = [1]
14
+ end
15
+
16
+ if sb.empty?
17
+ input_b = [input_b]
18
+ sb = [1]
19
+ end
20
+
21
+ target_shape = shape_diff(sa, sb)
22
+
23
+ if target_shape
24
+ input_b = broadcast_dimensions(input_b, target_shape)
25
+ else
26
+ target_shape = shape_diff(sb, sa)
27
+ raise "Incompatible shapes for op #{shape_eval(input_a)} vs #{shape_eval(input_a)}" if target_shape.nil?
28
+
29
+ input_a = broadcast_dimensions(input_a, target_shape)
30
+ end
31
+
32
+ [input_a, input_b]
33
+ end
34
+
35
+ # explicit broadcasting helper
36
+ def broadcast_dimensions(input, dims = [])
37
+ return input if dims.empty?
38
+
39
+ d = dims.shift
40
+
41
+ if input.is_a?(Array) && (get_rank(input) - 1) == dims.size
42
+ row_to_dup = input.collect do |item|
43
+ broadcast_dimensions(item, dims.dup)
44
+ end
45
+
46
+ row_to_dup + Array.new(d) { row_to_dup }.flatten(1)
47
+ elsif input.is_a?(Array)
48
+ Array.new(d) { broadcast_dimensions(input, dims.dup) }
49
+ else
50
+ Array.new(d + 1) { input }
51
+ end
52
+ end
53
+
54
+ # handle 2 tensor math operations
55
+ def vector_op(vector, vector2, op = ->(a, b) { a + b }, switch = false)
56
+ if get_rank(vector) < get_rank(vector2) # upgrade rank of A
57
+ duplicated = Array.new(vector2.size) do
58
+ vector
59
+ end
60
+ return vector_op(duplicated, vector2, op, switch)
61
+ end
62
+
63
+ return op.call(vector, vector2) unless vector.is_a?(Array)
64
+
65
+ vector.each_with_index.collect do |item, index|
66
+ next vector_op(item, vector2, op, switch) if item.is_a?(Array) && get_rank(vector) > get_rank(vector2)
67
+
68
+ z = if vector2.is_a?(Array)
69
+ if index < vector2.size
70
+ vector2[index]
71
+ else
72
+ raise 'incompatible tensor shapes used during op' if vector2.size != 1
73
+ vector2[0]
74
+ end
75
+ else
76
+ vector2
77
+ end
78
+
79
+ if item.is_a?(Array)
80
+ vector_op(item, z, op, switch)
81
+ else
82
+ switch ? op.call(z, item) : op.call(item, z)
83
+ end
84
+ end
85
+ end
86
+
87
+ def shape_diff(shape_a, shape_b)
88
+ return nil if shape_b.size > shape_a.size
89
+
90
+ reversed_a = shape_a.reverse
91
+ reversed_b = shape_b.reverse
92
+
93
+ reversed_a.each_with_index.collect do |s, index|
94
+ next s if index >= reversed_b.size
95
+ return nil if reversed_b[index] > s
96
+ s - reversed_b[index]
97
+ end.reverse
98
+ end
99
+ end
100
+ end
@@ -1,4 +1,5 @@
1
1
  require 'tensor_stream/evaluator/operation_helpers/random_gaussian'
2
+ require 'tensor_stream/evaluator/operation_helpers/array_ops_helper'
2
3
  require 'tensor_stream/math_gradients'
3
4
 
4
5
  module TensorStream
@@ -25,6 +26,7 @@ module TensorStream
25
26
  attr_accessor :retain
26
27
 
27
28
  include TensorStream::OpHelper
29
+ include TensorStream::ArrayOpsHelper
28
30
 
29
31
  def initialize(session, context, thread_pool: nil)
30
32
  @session = session
@@ -37,6 +39,8 @@ module TensorStream
37
39
  return tensor.map { |t| run(t, execution_context) } if tensor.is_a?(Array)
38
40
 
39
41
  return tensor if retain.include?(tensor) # if var is in retain don't eval to value
42
+
43
+ tensor = tensor.call() if tensor.is_a?(Proc)
40
44
 
41
45
  child_context = execution_context.dup
42
46
  res = if tensor.is_a?(Operation)
@@ -107,6 +111,11 @@ module TensorStream
107
111
  }
108
112
 
109
113
  call_op(:sign, a, child_context, func)
114
+ when :logical_and
115
+ a = complete_eval(a, child_context)
116
+ b = complete_eval(b, child_context)
117
+
118
+ call_vector_op(:greater, a, b, child_context, ->(t, u) { t && u })
110
119
  when :equal
111
120
  a = complete_eval(a, child_context)
112
121
  b = complete_eval(b, child_context)
@@ -188,35 +197,40 @@ module TensorStream
188
197
  tensor.items[0].value
189
198
  when :reduce_mean
190
199
  c = fp_type?(tensor.data_type) ? 0.0 : 0
191
- func = lambda { |v|
192
- if v.is_a?(Array)
193
- v.empty? ? c : (v.reduce(:+) / v.size)
194
- else
195
- v
200
+ func = lambda do |arr|
201
+ return c if arr.nil?
202
+
203
+ reduced_val = arr[0]
204
+ arr[1..arr.size].each do |v|
205
+ reduced_val = vector_op(reduced_val, v, ->(a, b) { a + b })
196
206
  end
197
- }
207
+
208
+ vector_op(reduced_val, nil, ->(a, _b) { a / arr.size })
209
+ end
198
210
 
199
211
  reduction(child_context, tensor, func)
200
212
  when :reduce_sum
201
213
  c = fp_type?(tensor.data_type) ? 0.0 : 0
202
- func = lambda { |v|
203
- if v.is_a?(Array)
204
- v.empty? ? c : v.reduce(:+)
205
- else
206
- v
214
+ func = lambda do |arr|
215
+ reduced_val = arr[0]
216
+ arr[1..arr.size].each do |v|
217
+ reduced_val = vector_op(reduced_val, v, ->(t, u) { t + u })
207
218
  end
208
- }
219
+ reduced_val
220
+ end
209
221
 
210
222
  reduction(child_context, tensor, func)
211
223
  when :reduce_prod
212
224
  c = fp_type?(tensor.data_type) ? 1.0 : 1
213
- func = lambda { |v|
214
- if v.is_a?(Array)
215
- v.empty? ? c : v.reduce(:*)
216
- else
217
- v
225
+ func = lambda do |arr|
226
+ return c if arr.nil?
227
+
228
+ reduced_val = arr[0]
229
+ arr[1..arr.size].each do |v|
230
+ reduced_val = vector_op(reduced_val, v, ->(a, b) { a * b })
218
231
  end
219
- }
232
+ reduced_val
233
+ end
220
234
 
221
235
  reduction(child_context, tensor, func)
222
236
  when :transpose
@@ -316,6 +330,10 @@ module TensorStream
316
330
  (Matrix[*matrix_a] * Matrix[*matrix_b]).to_a
317
331
  when :gradients
318
332
  raise 'not implemented in evaluator' # see TensorStream.gradients instead.
333
+ when :broadcast_transform
334
+ a = complete_eval(a, child_context)
335
+ b = complete_eval(b, child_context)
336
+ broadcast(a, b)
319
337
  when :identity
320
338
  complete_eval(a, child_context)
321
339
  when :print
@@ -348,6 +366,11 @@ module TensorStream
348
366
  b = complete_eval(b, child_context)
349
367
 
350
368
  call_vector_op(:max, a, b, child_context, ->(t, u) { [t, u].max })
369
+ when :broadcast_gradient_args
370
+ a = complete_eval(a, child_context)
371
+ b = complete_eval(b, child_context)
372
+
373
+ get_broadcast_gradient_args(a, b)
351
374
  else
352
375
  raise "unknown op #{tensor.operation}"
353
376
  end.tap do |result|
@@ -362,15 +385,15 @@ module TensorStream
362
385
  rescue EvaluatorExcecutionException => e
363
386
  raise e
364
387
  rescue StandardError => e
365
- # a = complete_eval(a, child_context)
366
- # b = complete_eval(b, child_context)
367
- # puts "name: #{tensor.given_name}"
368
- # puts "op: #{tensor.to_math(true, 1)}"
369
- # puts "A: #{a}" if a
370
- # puts "B: #{b}" if b
371
- # binding.pry
388
+ a = complete_eval(a, child_context)
389
+ b = complete_eval(b, child_context)
390
+ puts "name: #{tensor.given_name}"
391
+ puts "op: #{tensor.to_math(true, 1)}"
392
+ puts "A: #{a}" if a
393
+ puts "B: #{b}" if b
394
+
372
395
  puts e.backtrace.join("\n")
373
- raise EvaluatorExcecutionException.new(e, tensor), "error #{e.message} while evaluating #{tensor.name} : #{tensor.to_math} defined at #{tensor.source}"
396
+ raise EvaluatorExcecutionException.new(e, tensor), "error #{e.message} while evaluating #{tensor.name} : #{tensor.to_math(true,1)} defined at #{tensor.source}"
374
397
  end
375
398
 
376
399
  def eval_tensor(tensor, child_context)
@@ -425,19 +448,22 @@ module TensorStream
425
448
 
426
449
  def reduction(child_context, tensor, func)
427
450
  val = complete_eval(tensor.items[0], child_context)
428
- axis = tensor.options[:axis]
429
- keep_dims = tensor.options[:keepdims]
451
+ axis = complete_eval(tensor.options[:axis], child_context)
452
+ keep_dims = complete_eval(tensor.options[:keepdims], child_context)
453
+ rank = get_rank(val)
454
+ return val if axis && axis.is_a?(Array) && axis.empty?
430
455
 
431
- res = if axis.is_a?(Array)
432
- axis.each do |x|
433
- val = reduce_axis(x, val, keep_dims, child_context, func)
434
- end
456
+ axis = if axis.nil?
457
+ nil
458
+ elsif axis.is_a?(Array)
459
+ return val if axis.empty?
435
460
 
436
- func.call(val.flatten)
437
- else
438
- reduce_axis(axis, val, keep_dims, child_context, func)
439
- end
440
- res
461
+ axis.map { |a| a < 0 ? rank - a.abs : a }
462
+ else
463
+ axis < 0 ? rank - axis.abs : axis
464
+ end
465
+
466
+ reduce_axis(0, axis, val, keep_dims, func)
441
467
  end
442
468
 
443
469
  def arr_pad(arr, paddings, data_type = :float32, rank = 0)
@@ -537,14 +563,41 @@ module TensorStream
537
563
  if get_rank(eval_b).zero?
538
564
  op.call(eval_a, eval_b)
539
565
  else
540
- constant_op(eval_b, eval_a, child_context, op, true)
566
+ vector_op(eval_b, eval_a, op, true)
541
567
  end
542
568
  elsif get_rank(eval_a) > 0
543
- if get_rank(eval_b) > 0
544
- vector_op(eval_a, eval_b, child_context, op)
545
- else
546
- constant_op(eval_a, eval_b, child_context, op)
547
- end
569
+ vector_op(eval_a, eval_b, op)
570
+ end
571
+ end
572
+
573
+ # determine possible reduction axis to be used
574
+ def _broadcast_gradient_op(vector_shape1, vector_shape2, level)
575
+ va_rank = _rank_from_shape(vector_shape1)
576
+ vb_rank = _rank_from_shape(vector_shape2)
577
+ return [] if vector_shape1 == vector_shape2 # same shape so no reductions
578
+
579
+ shape2_r = vector_shape2.reverse
580
+
581
+ vector_shape1.reverse.each_with_index.collect do |s, index|
582
+ next va_rank - index - 1 if index >= shape2_r.size
583
+ next nil if shape2_r[index] == s
584
+ next nil if shape2_r[index] > s
585
+ va_rank - index - 1
586
+ end.compact
587
+ end
588
+
589
+ def _rank_from_shape(shape)
590
+ shape.is_a?(Array) ? shape.size : 0
591
+ end
592
+
593
+ def get_broadcast_gradient_args(input_a, input_b)
594
+ return [] if get_rank(input_b).zero? && get_rank(input_a).zero?
595
+ return nil if get_rank(input_b).zero?
596
+ # ruby scalar
597
+ if get_rank(input_a).zero?
598
+ _broadcast_gradient_op(input_b, input_a, 0, true)
599
+ elsif get_rank(input_a) > 0
600
+ _broadcast_gradient_op(input_a, input_b, 0)
548
601
  end
549
602
  end
550
603
 
@@ -578,7 +631,7 @@ module TensorStream
578
631
  def process_function_op(a, child_context, op)
579
632
  # ruby scalar
580
633
  if (a.is_a?(Tensor) && a.shape.rank > 0) || a.is_a?(Array)
581
- constant_op(a, 0, child_context, op)
634
+ vector_op(a, 0, op)
582
635
  elsif !a.is_a?(Tensor) || a.shape.rank.zero?
583
636
  v = run(a, child_context)
584
637
  raise FullEvalNotPossible.new, "full eval not possible for #{v.name}" if v.is_a?(Tensor) && !v.is_const
@@ -605,54 +658,29 @@ module TensorStream
605
658
  Tensor.cast_dtype(var, placeholder.data_type)
606
659
  end
607
660
 
608
- def reduce_axis(axis, val, keep_dims, child_context, op = ->(v) { v.is_a?(Array) ? v.reduce(:+) : v })
609
- val = run(val, child_context)
610
- return val.is_a?(Array) ? op.call(val.flatten) : val if axis.nil?
611
- return val.transpose.collect { |v| keep_dims ? [op.call(v)] : op.call(v) } if axis.zero?
612
- return val.collect { |v| keep_dims ? [op.call(v)] : op.call(v) } if axis == 1
613
-
614
- raise "can't handle with axis > 1 :("
615
- end
661
+ def reduce_axis(current_axis, axis, val, keep_dims, f = ->(a, b) { a + b })
662
+ return val unless val.is_a?(Array)
616
663
 
617
- def constant_add(vector, constant)
618
- run(vector).collect do |item|
619
- if item.is_a?(Array)
620
- constant_add(item, constant)
621
- elsif item.respond_to?(:value)
622
- item.value + constant
623
- else
624
- item + constant
625
- end
664
+ r = val.collect do |v|
665
+ reduce_axis(current_axis + 1, axis, v, keep_dims, f)
626
666
  end
627
- end
628
667
 
629
- def constant_op(vector, constant, child_context, op = ->(a, b) { a + b }, switch = false)
630
- eval_vector = complete_eval(vector, child_context)
631
- constant = complete_eval(constant, child_context)
668
+ should_reduce_axis = axis.nil? || (axis.is_a?(Array) && axis.include?(current_axis)) || (current_axis == axis)
632
669
 
633
- raise FullEvalNotPossible.new, "full eval not possible for #{eval_vector.name}" if eval_vector.is_a?(Tensor) || constant.is_a?(Tensor)
634
-
635
- eval_vector.each_with_index.collect do |item, index|
636
- c = if constant.is_a?(Array)
637
- if index < constant.size
638
- constant[index]
639
- else
640
- raise "incompatible tensor shapes used during op" if constant.size != 1
641
- constant[0]
642
- end
643
- else
644
- constant
645
- end
646
- if item.is_a?(Array)
647
- constant_op(item, c, child_context, op, switch)
648
- elsif item.respond_to?(:value)
649
- switch ? op.call(c, item.value) : op.call(item.value, c)
650
- else
651
- switch ? op.call(c, item) : op.call(item, c)
670
+ if should_reduce_axis
671
+ reduced_val = r[0]
672
+ if r.size > 1
673
+ reduced_val = f.call(r[0..val.size])
674
+ elsif r.size == 0
675
+ reduced_val = f.call(nil)
652
676
  end
677
+ keep_dims ? [ reduced_val ] : reduced_val
678
+ else
679
+ r
653
680
  end
654
681
  end
655
682
 
683
+ # handle 3 tensor math operations
656
684
  def call_3way_vector_op(v_a, v_b, v_c, child_context, op = ->(a, b, c) { a + b + c })
657
685
  return op.call(v_a, v_b, v_c) unless v_a.is_a?(Array)
658
686
 
@@ -667,30 +695,6 @@ module TensorStream
667
695
  end
668
696
  end
669
697
 
670
- def vector_op(vector, vector2, child_context, op = ->(a, b) { a + b })
671
- v_a = run(vector, child_context)
672
- v_b = run(vector2, child_context)
673
-
674
- if get_rank(v_a) < get_rank(v_b) # upgrade rank of A
675
- duplicated = Array.new(v_b.size) do
676
- v_a
677
- end
678
- return vector_op(duplicated, v_b, child_context, op)
679
- end
680
-
681
- v_a.each_with_index.collect do |item, index|
682
- next vector_op(item, v_b, child_context, op) if item.is_a?(Array) && get_rank(v_a) > get_rank(v_b)
683
-
684
- z = index < v_b.size ? v_b[index] : v_b[0]
685
-
686
- if item.is_a?(Array)
687
- constant_op(item, z, child_context, op)
688
- else
689
- item.respond_to?(:value) ? op.call(item.value, z.value) : op.call(item, z)
690
- end
691
- end
692
- end
693
-
694
698
  def all_true?(arr)
695
699
  if arr.is_a?(Array)
696
700
  arr.each do |a|
@@ -702,21 +706,6 @@ module TensorStream
702
706
  !!arr
703
707
  end
704
708
 
705
- def vector_add(vector, vector2, child_context)
706
- v_a = run(vector, child_context)
707
- v_b = run(vector2, child_context)
708
-
709
- v_a.each_with_index.collect do |item, index|
710
- if item.is_a?(Array)
711
- constant_add(item, constant)
712
- elsif item.respond_to?(:value)
713
- item.value + v_b[index].value
714
- else
715
- item + v_b[index]
716
- end
717
- end
718
- end
719
-
720
709
  def generate_vector(shape, dtype: :float32, generator:)
721
710
  if shape.is_a?(Integer)
722
711
  Array.new(shape) do
@@ -12,6 +12,11 @@ module TensorStream
12
12
  end
13
13
 
14
14
  def reset
15
+ @placeholder_counter = 0
16
+ @const_counter = 0
17
+ @var_counter = 0
18
+ @op_counter = 0
19
+
15
20
  @nodes = {}
16
21
  @collections = {
17
22
  :"#{GraphKeys::GLOBAL_VARIABLES}" => []
@@ -39,6 +44,7 @@ module TensorStream
39
44
  raise 'Placeholder cannot be used when eager_execution is enabled' if @eager_execution && node.is_a?(Placeholder)
40
45
  node.name = uniqunify(node.name) if @nodes[node.name]
41
46
  @nodes[node.name] = node
47
+ node.send(:propagate_consumer, node)
42
48
  node.value = node.eval if @eager_execution
43
49
  end
44
50
 
@@ -41,17 +41,31 @@ module TensorStream
41
41
  when :log
42
42
  (i_cons(1, constant_options_1) / _ds(tensor.items[0])) * grad
43
43
  when :tanh
44
- (i_cons(1, constant_options_1) - (i_op(:tanh, _ds(tensor.items[0]))**2)) * grad
44
+ i_op(:mul, (i_cons(1, constant_options_1) - (i_op(:tanh, _ds(tensor.items[0]))**2)), grad, name: 'grad_tanh')
45
45
  when :tan
46
46
  (i_cons(1, constant_options_1) / (i_op(:cos, _ds(tensor.items[0]))**2)) * grad
47
47
  when :sin
48
- i_op(:cos, tensor.items[0]) * grad
48
+ i_op(:mul, i_op(:cos, tensor.items[0]), grad, name: 'grad_sin')
49
49
  when :sqrt
50
50
  i_cons(1, constant_options_1) / (i_cons(2, constant_options_1) * i_op(:sqrt, _ds(tensor.items[0]))) * grad
51
51
  when :cos
52
52
  -i_op(:sin, tensor.items[0]) * grad
53
53
  when :add
54
- _grad_with_broadcast(tensor, wrt_dx, ->(a, b) { i_op(:add, a, b, name: 'grad_sum') }, options)
54
+ # rx = op(:shape, tensor.items[0])
55
+ # ry = op(:shape, tensor.items[1])
56
+
57
+ # ones_a = op(:ones_like, tensor.items[0])
58
+ # ones_b = op(:ones_like, tensor.items[1])
59
+ # inputs = _broadcast_transform(grad * ones_a, grad2 * ones_b)
60
+ # sx, sy = _broadcast_gradient_args(rx, ry)
61
+
62
+ # keep_dims_x = op(:rank, inputs[0]) == op(:rank, tensor.items[0])
63
+ # keep_dims_y = op(:rank, inputs[1]) == op(:rank, tensor.items[1])
64
+
65
+ # add_x = op(:reduce_sum, inputs[0], nil, axis: sy, keepdims: keep_dims_x)
66
+ # add_y = op(:reduce_sum, inputs[1], nil, axis: sx, keepdims: keep_dims_y)
67
+ # _filtered_sum(add_x, add_y, wrt_dx)
68
+ _grad_with_broadcast(tensor, wrt_dx, ->(a, b) { i_op(:add, a, b, name: 'grad_add') }, options)
55
69
  when :sub
56
70
  _grad_with_broadcast(tensor, wrt_dx, ->(a, b) { i_op(:sub, a, b, name: 'grad_sub') }, options)
57
71
  when :pow
@@ -69,7 +83,15 @@ module TensorStream
69
83
  _reduce_when_necessary(gx + gy, wrt_dx)
70
84
  when :mul
71
85
  # apply the product rule
72
- _reduce_when_necessary(grad * _ds(tensor.items[1]) + _ds(tensor.items[0]) * grad2, wrt_dx)
86
+ rx = op(:shape, tensor.items[0])
87
+ ry = op(:shape, tensor.items[1])
88
+ sx, sy = _broadcast_gradient_args(rx, ry)
89
+ inputs = _broadcast_transform(tensor.items[0], tensor.items[1])
90
+ keep_dims_x = op(:rank, inputs[0]) == op(:rank, tensor.items[0])
91
+ keep_dims_y = op(:rank, inputs[1]) == op(:rank, tensor.items[1])
92
+
93
+ _filtered_sum(op(:reduce_sum, grad * _ds(inputs[1]), nil, axis: sy, keepdims: keep_dims_x),
94
+ op(:reduce_sum, _ds(inputs[0]) * grad2, nil, axis: sx, keepdims: keep_dims_y), wrt_dx)
73
95
  when :reduce_mean
74
96
  input_size = i_op(:reduce_prod, i_op(:shape, tensor.items[0]))
75
97
  output_size = i_op(:reduce_prod, i_op(:shape, tensor))
@@ -96,9 +118,6 @@ module TensorStream
96
118
  matmul_db = i_op(:matmul, tensor.items[0], identity_1, transpose_a: true,
97
119
  pad_zeros: true,
98
120
  name: 'matrix_dy')
99
-
100
- zero_vect = i_op(:zeros_like, wrt_dx, nil, name: 'zero_vect')
101
-
102
121
  # matmul_db = op(:transpose, matmul_db, nil).first
103
122
 
104
123
  # begin_a = op(:zeros, op(:rank, matmul_db), nil, data_type: :int32, name: 'begin_a')
@@ -113,9 +132,7 @@ module TensorStream
113
132
 
114
133
  # norm_a = i_op(:cond, norm_a[0], norm_a, pred: i_op(:rank, matmul_da) > i_op(:rank, derivative_a))
115
134
  # norm_b = i_op(:cond, norm_b[0], norm_b, pred: i_op(:rank, matmul_db) > i_op(:rank, derivative_b))
116
-
117
- i_op(:cond, norm_a, zero_vect, pred: i_op(:reduce_sum, norm_a) != 0) + i_op(:cond, norm_b, zero_vect, pred: i_op(:reduce_sum, norm_b) != 0)
118
- # m.breakpoint! { |t, a, b, v| binding.pry }
135
+ _filtered_sum(norm_a, norm_b, wrt_dx)
119
136
  else
120
137
  raise "no derivative implementation found for op #{tensor.operation}"
121
138
  end
@@ -161,5 +178,19 @@ module TensorStream
161
178
  reduced = op(:reduce_sum, tensor, nil, axis: 0)
162
179
  op(:cond, ->{ reduced }, tensor, pred: rank > dx_rank)
163
180
  end
181
+
182
+ def self._broadcast_gradient_args(input_a, input_b)
183
+ [op(:broadcast_gradient_args, input_a, input_b), op(:broadcast_gradient_args, input_b, input_a)]
184
+ end
185
+
186
+ def self._broadcast_transform(input_a, input_b)
187
+ op(:broadcast_transform, input_a, input_b)
188
+ end
189
+
190
+ # filter out zero arrays
191
+ def self._filtered_sum(input_a, input_b, wrt_dx)
192
+ zero_vect = op(:zeros_like, wrt_dx)
193
+ (i_op(:cond, input_a, zero_vect, pred: i_op(:reduce_sum, input_a) != 0) + i_op(:cond, input_b, zero_vect, pred: i_op(:reduce_sum, input_b) != 0))
194
+ end
164
195
  end
165
196
  end
@@ -2,6 +2,7 @@ module TensorStream
2
2
  # TensorStream class that defines an operation
3
3
  class Operation < Tensor
4
4
  attr_accessor :name, :operation, :items, :rank, :options
5
+ attr_reader :outputs
5
6
 
6
7
  def initialize(operation, input_a, input_b, options = {})
7
8
  @graph = options[:graph] || TensorStream.get_default_graph
@@ -51,7 +52,7 @@ module TensorStream
51
52
 
52
53
  def set_data_type(passed_data_type)
53
54
  case operation
54
- when :greater, :less, :equal
55
+ when :greater, :less, :equal, :not_equal, :greater_equal, :less_equal
55
56
  :boolean
56
57
  when :shape, :rank
57
58
  :int32
@@ -150,6 +151,8 @@ module TensorStream
150
151
  "#{sub_item} == #{auto_math(items[1], name_only, max_depth - 1)}"
151
152
  when :not_equal
152
153
  "#{sub_item} != #{auto_math(items[1], name_only, max_depth - 1)}"
154
+ when :logical_and
155
+ "#{sub_item} && #{auto_math(items[1], name_only, max_depth - 1)}"
153
156
  when :sqrt
154
157
  "sqrt(#{sub_item})"
155
158
  when :zeros_like
@@ -161,7 +164,7 @@ module TensorStream
161
164
  when :cast
162
165
  "cast(#{auto_math(sub_item)}, #{data_type})"
163
166
  else
164
- raise "math form for #{operation}"
167
+ raise "no math form for #{operation} defined"
165
168
  end
166
169
  end
167
170
 
@@ -171,6 +174,14 @@ module TensorStream
171
174
 
172
175
  private
173
176
 
177
+ def propagate_consumer(consumer)
178
+ super(consumer)
179
+
180
+ @items.compact.each do |item|
181
+ item.send(:propagate_consumer, consumer) if item.name!=self.name
182
+ end
183
+ end
184
+
174
185
  def set_name
175
186
  "#{@operation}#{graph.get_operation_counter}:#{@rank}"
176
187
  end
@@ -49,7 +49,7 @@ module TensorStream
49
49
  end
50
50
 
51
51
  def eye(num_rows, num_columns: nil, dtype: :float32, name: nil)
52
- op(:eye, num_rows, num_columns || num_rows, data_type: dtype, name: name, preserve_params_type: true)
52
+ op(:eye, num_rows, num_columns || num_rows, data_type: dtype, name: name)
53
53
  end
54
54
 
55
55
  def shape(input, name: nil, out_type: :int32)
@@ -80,6 +80,10 @@ module TensorStream
80
80
  op(:less, input_a, input_b, name: name)
81
81
  end
82
82
 
83
+ def logical_and(input_a, input_b, name: nil)
84
+ op(:logical_and, input_a, input_b, name: name)
85
+ end
86
+
83
87
  def greater(input_a, input_b, name: nil)
84
88
  op(:greater, input_a, input_b, name: name)
85
89
  end
@@ -5,7 +5,9 @@ module TensorStream
5
5
  class Tensor
6
6
  include OpHelper
7
7
 
8
- attr_accessor :name, :data_type, :shape, :rank, :native_buffer, :is_const, :value, :breakpoint, :internal, :source, :given_name, :graph
8
+ attr_accessor :name, :data_type, :shape, :rank, :native_buffer, :is_const,
9
+ :value, :breakpoint, :internal, :source, :given_name, :graph,
10
+ :consumers
9
11
 
10
12
  def initialize(data_type, rank, shape, options = {})
11
13
  @data_type = data_type
@@ -104,6 +106,14 @@ module TensorStream
104
106
  op(:less_equal, self, other)
105
107
  end
106
108
 
109
+ def and(other)
110
+ op(:logical_and, self, other)
111
+ end
112
+
113
+ def matmul(other)
114
+ op(:matmul, self, other)
115
+ end
116
+
107
117
  def collect(&block)
108
118
  @value.collect(&block)
109
119
  end
@@ -204,12 +214,24 @@ module TensorStream
204
214
  end
205
215
 
206
216
  def breakpoint!(&block)
207
- @breakpoint = block
208
217
  self
209
218
  end
210
219
 
220
+ def print!(message)
221
+ op(:print, self, self, message: message)
222
+ end
223
+
211
224
  protected
212
225
 
226
+ def add_consumer(consumer)
227
+ @consumers ||= []
228
+ @consumers << consumer.name if !@consumers.include?(consumer.name) && consumer.name!=self.name
229
+ end
230
+
231
+ def propagate_consumer(consumer)
232
+ add_consumer(consumer)
233
+ end
234
+
213
235
  def format_source(trace)
214
236
  trace.reject { |c| c.to_s.include?(File.join('lib', 'tensor_stream')) }.first
215
237
  end
@@ -1,5 +1,5 @@
1
1
  module TensorStream
2
- VERSION = '0.1.3'.freeze
2
+ VERSION = '0.1.4'.freeze
3
3
 
4
4
  def self.version
5
5
  VERSION
@@ -0,0 +1,44 @@
1
+ import tensorflow as tf
2
+
3
+ test_inputs = [
4
+ [0.5937, 0.2343, 1.4332, 0.4395],
5
+ [-1.0227, -0.6915, 1.2367, 0.3452],
6
+ [-0.5675, 1.0374, 1.0429, 0.8839],
7
+ [-0.1066, -0.0469, -1.6317, -1.4836],
8
+ [0.7835, -3.0105, 1.713, -0.4536],
9
+ [-0.3076, 1.3662, -0.6537, 0.0905],
10
+ [-0.2459, 0.2243, -2.7048, 0.848],
11
+ ]
12
+
13
+ num_inputs = 4
14
+ num_neurons = 5
15
+ inputs = tf.placeholder("float", shape=(None, num_inputs))
16
+ biases = tf.constant([0.5012, 1.302, -1.6217, 0.669, 0.1494], name='b1')
17
+ biases2 = tf.constant([0.2012, 1.102, -1.5217, 0.469, 0.0494], name='b2')
18
+
19
+ weights = tf.constant([
20
+ [-0.9135, 1.0376, 0.8537, 0.4376, 1.3255],
21
+ [-0.5921, -1.4081, 1.0614, -0.5283, 1.1832],
22
+ [0.7285, -0.7844, 0.1793, -0.5275, -0.4426],
23
+ [-1.4976, 0.4433, 2.2317, -2.0479, 0.7791]], name='w')
24
+
25
+ weights_layer2 = tf.constant([
26
+ [-1.0465, -0.8766, 1.6849, -0.6625, 0.7928],
27
+ [2.0412, 1.3564, 0.7905, 0.6434, -2.5495],
28
+ [2.4276, -0.6893, -1.5917, 0.0911, 0.9112],
29
+ [-0.012, 0.0794, 1.3829, -1.018, -0.9328],
30
+ [0.061, 0.9791, -2.1727, -0.9553, -1.434]], name='w2')
31
+
32
+
33
+ sess = tf.Session()
34
+
35
+ layer_1 = tf.matmul(inputs, weights) + biases
36
+ neural_net = tf.matmul(layer_1, weights_layer2) + biases2
37
+
38
+ output = sess.run(neural_net, feed_dict={ inputs: test_inputs })
39
+
40
+ g = tf.gradients(neural_net, [weights, biases])
41
+ g2 = tf.gradients(neural_net, [weights_layer2, biases2])
42
+
43
+ weight_gradient, biases_gradient = sess.run(g, feed_dict = { inputs: test_inputs })
44
+ weight_gradient2, biases_gradient2 = sess.run(g2, feed_dict: { inputs => test_inputs })
@@ -36,6 +36,8 @@ Gem::Specification.new do |spec|
36
36
  spec.add_development_dependency "awesome_print"
37
37
  spec.add_development_dependency "rubocop"
38
38
  spec.add_development_dependency "pry-byebug"
39
+ spec.add_development_dependency "byepry"
40
+ spec.add_development_dependency "colorize"
39
41
  spec.add_dependency "deep_merge"
40
42
  spec.add_dependency "concurrent-ruby"
41
43
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tensor_stream
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.3
4
+ version: 0.1.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Joseph Emmanuel Dayo
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2018-05-12 00:00:00.000000000 Z
11
+ date: 2018-05-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -94,6 +94,34 @@ dependencies:
94
94
  - - ">="
95
95
  - !ruby/object:Gem::Version
96
96
  version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: byepry
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: colorize
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
97
125
  - !ruby/object:Gem::Dependency
98
126
  name: deep_merge
99
127
  requirement: !ruby/object:Gem::Requirement
@@ -147,6 +175,7 @@ files:
147
175
  - lib/tensor_stream.rb
148
176
  - lib/tensor_stream/control_flow.rb
149
177
  - lib/tensor_stream/evaluator/evaluator.rb
178
+ - lib/tensor_stream/evaluator/operation_helpers/array_ops_helper.rb
150
179
  - lib/tensor_stream/evaluator/operation_helpers/random_gaussian.rb
151
180
  - lib/tensor_stream/evaluator/ruby_evaluator.rb
152
181
  - lib/tensor_stream/graph.rb
@@ -171,6 +200,7 @@ files:
171
200
  - samples/iris.rb
172
201
  - samples/linear_regression.rb
173
202
  - samples/raw_neural_net_sample.rb
203
+ - samples/test.py
174
204
  - tensor_stream.gemspec
175
205
  homepage: http://www.github.com/jedld/tensor_stream
176
206
  licenses:
@@ -193,7 +223,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
193
223
  version: '0'
194
224
  requirements: []
195
225
  rubyforge_project:
196
- rubygems_version: 2.6.8
226
+ rubygems_version: 2.6.10
197
227
  signing_key:
198
228
  specification_version: 4
199
229
  summary: A Pure ruby tensorflow implementation