tensor_stream 0.1.3 → 0.1.4

Sign up to get free protection for your applications and to get access to all the features.
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