tensor_stream 0.8.5 → 0.8.6

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
  SHA256:
3
- metadata.gz: 1d6e9e482de719b709bfe554085718cb01a3bbfd089983eef51ace418b1b7d2d
4
- data.tar.gz: 287e63d7a7269e7143ef1016677297c44235a4ac6afb03234f72bb2b6774d348
3
+ metadata.gz: '0168aeff699a7490174f164370f6cef9dbc3413f23352a696c3a468832bd1b24'
4
+ data.tar.gz: f62926f3b919cbf5001872d6fab23095807d6c5028de5d885256449e1dd52a01
5
5
  SHA512:
6
- metadata.gz: 3ec2af3376d4cc671eb7bfc549290145d3e14167fe3b472f1c4dd1a05a6ab78ddeddc608d6c1c26a93ddc542ce605341172212e7d5d2f5beeb2542ef3790b03f
7
- data.tar.gz: 961e74c0acac0179affca04974f7933fb1162a1a88b3bcdde6c57f679db6d5e23c2e92dd4fff8454bda7ba87dc2eaad7f9424458440348151ea4ac90258a8acc
6
+ metadata.gz: ce886b38d14603f9c3f5dbfc6d96f89041350cd7cfa40a2cb2178e2845a43e22e98bca8bd689b72a2b0acceb5da8b3988d88632d601c2da701b0979375e84200
7
+ data.tar.gz: 6dac48e933a46144fb78433e17077b2c9d9fbe68e4297a0958a752276e51401279460c6ac47adc88ac0b3890a0bb535bfb0f889b64b2439c2ea38c68fb9d1665
data/CHANGELOG.md CHANGED
@@ -4,6 +4,17 @@ All notable changes to this project will be documented in this file.
4
4
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
5
5
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
6
 
7
+ ## [0.8.6] - 2018-09-11
8
+
9
+ ### Added
10
+ - [TRAINING] Added RMSPropOptimizer, AdagradOptimizer
11
+ - [NEW OP] shape_n, sparse_softmax_cross_entropy_with_logits, split, unstack
12
+ - Added RNN sample
13
+
14
+ ### Fixes
15
+ - Fixed gradient computation when passing an array of tensors to a function
16
+ - Added gradients for various other ops
17
+
7
18
  ## [0.8.5] - 2018-09-06
8
19
 
9
20
  ### Added
data/README.md CHANGED
@@ -11,10 +11,9 @@ The goal of this gem is to have a high performance machine learning and compute
11
11
  ## Features
12
12
 
13
13
  - Replicates most of the commonly used low-level tensorflow ops (tf.add, tf.constant, tf.placeholder, tf.matmul, tf.sin etc...)
14
- - Supports auto-differentiation via tf.gradients (mostly)
14
+ - Supports auto-differentiation
15
15
  - Provision to use your own opcode evaluator (opencl, sciruby and tensorflow backends planned)
16
16
  - Goal is to be as close to TensorFlow in behavior but with some freedom to add ruby specific enhancements (with lots of test cases)
17
- - eager execution (experimental)
18
17
  - (08-08-2018) Load pbtext files from tensorflow (Graph.parse_from_string)
19
18
 
20
19
  ## Compatibility
@@ -31,7 +30,7 @@ gem 'tensor_stream-opencl'
31
30
  and then (without bundler)
32
31
 
33
32
  ```ruby
34
- require 'tensor_stream-opencl'
33
+ require 'tensor_stream/opencl'
35
34
  ```
36
35
 
37
36
  OpenCL is basically a requirement for deep learning and image processing tasks as the ruby implementation is too slow even with jit speedups using latest ruby implementations.
@@ -91,8 +90,11 @@ pred = X * W + b
91
90
  # Mean squared error
92
91
  cost = ((pred - Y) ** 2).reduce(:+) / ( 2 * n_samples)
93
92
 
94
- # optimizer = TensorStream::Train::MomentumOptimizer.new(0.01, 0.5, use_nesterov: true).minimize(cost)
95
- # optimizer = TensorStream::Train::AdamOptimizer.new.minimize(cost)
93
+ # optimizer = TensorStream::Train::MomentumOptimizer.new(learning_rate, momentum, use_nesterov: true).minimize(cost)
94
+ # optimizer = TensorStream::Train::AdamOptimizer.new(learning_rate).minimize(cost)
95
+ # optimizer = TensorStream::Train::AdadeltaOptimizer.new(1.0).minimize(cost)
96
+ # optimizer = TensorStream::Train::AdagradOptimizer.new(0.01).minimize(cost)
97
+ # optimizer = TensorStream::Train::RMSPropOptimizer.new(0.01, centered: true).minimize(cost)
96
98
  optimizer = TensorStream::Train::GradientDescentOptimizer.new(learning_rate).minimize(cost)
97
99
 
98
100
  # Initialize the variables (i.e. assign their default value)
@@ -212,7 +214,7 @@ gem 'tensor_stream-opencl'
212
214
  To use the opencl evaluator instead of the ruby evaluator simply require it (if using rails this should be loaded automatically).
213
215
 
214
216
  ```ruby
215
- require 'tensor_stream-opencl'
217
+ require 'tensor_stream/opencl'
216
218
  ```
217
219
 
218
220
  Adding the OpenCL evaluator should expose additional devices available to tensor_stream
@@ -227,7 +229,7 @@ By default TensorStream will determine using the given evaluators the best possi
227
229
  placement for each tensor operation
228
230
 
229
231
  ```ruby
230
- require 'tensor_stream/evaluator/opencl/opencl_evaluator'
232
+ require 'tensor_stream/opencl'
231
233
 
232
234
  # set session to use the opencl evaluator
233
235
  sess = ts.session
@@ -1,12 +1,27 @@
1
1
  module TensorStream
2
2
  # varoius utility functions for array processing
3
3
  module ArrayOpsHelper
4
+ def split_tensor(input, begin_index, end_index, axis = 0)
5
+ if axis.zero?
6
+ input[begin_index...end_index]
7
+ else
8
+ input.collect do |item|
9
+ split_tensor(item, begin_index, end_index, axis - 1)
10
+ end
11
+ end
12
+ end
13
+
4
14
  def slice_tensor(input, start, size)
5
15
  return input if size.empty?
6
16
  start_index = start.shift
7
- dimen_size = start_index + size.shift
17
+ current_size = size.shift
18
+ dimen_size = if current_size == -1
19
+ input.size - 1
20
+ else
21
+ start_index + current_size - 1
22
+ end
8
23
 
9
- input[start_index...dimen_size].collect do |item|
24
+ input[start_index..dimen_size].collect do |item|
10
25
  if item.is_a?(Array)
11
26
  slice_tensor(item, start.dup, size.dup)
12
27
  else
@@ -7,7 +7,8 @@ module TensorStream
7
7
  start = inputs[1]
8
8
  size = complete_eval(tensor.options[:size], context)
9
9
  raise "start index and size not of the same shape #{start.size} != #{size.size}" if start.size != size.size
10
- slice_tensor(input, start, size)
10
+
11
+ slice_tensor(input, start.dup, size.dup)
11
12
  end
12
13
 
13
14
  register_op %i[flow_dynamic_stitch dynamic_stitch] do |_context, _tensor, inputs|
@@ -22,8 +23,9 @@ module TensorStream
22
23
  gather(params, indexes)
23
24
  end
24
25
 
25
- register_op %i[concat concat_v2] do |_context, tensor, inputs|
26
- concat_array(inputs, tensor.options[:axis])
26
+ register_op %i[concat concat_v2] do |_context, _tensor, inputs|
27
+ axis = inputs.shift
28
+ concat_array(inputs, axis)
27
29
  end
28
30
 
29
31
  register_op :stack do |_context, tensor, inputs|
@@ -74,6 +76,55 @@ module TensorStream
74
76
  TensorShape.reshape(output_buffer, new_shape)
75
77
  end
76
78
 
79
+ register_op :unstack do |_context, tensor, inputs|
80
+ value = inputs[0]
81
+
82
+ axis = tensor.options[:axis] || 0
83
+ new_shape = shape_eval(inputs[0])
84
+ rank = new_shape.size - 1
85
+
86
+ divisors = new_shape.dup.drop(1).reverse.inject([1]) do |a, s|
87
+ a << s * a.last
88
+ end.reverse
89
+
90
+ axis = rank + axis if axis < 0
91
+ rotated_shape = Array.new(axis + 1) { new_shape.shift }
92
+ new_shape = rotated_shape.rotate!(-1) + new_shape
93
+ output_buffer = Array.new(new_shape.reduce(:*)) { 0 }
94
+
95
+ multipliers = new_shape.dup.drop(1).reverse.inject([1]) do |a, s|
96
+ a << s * a.last
97
+ end.reverse
98
+
99
+ inputs.each_with_index do |input, index|
100
+ raw_input = input.is_a?(Array) ? input.flatten : [input]
101
+ start = index * divisors.first
102
+
103
+ raw_input.each_with_index do |x, index2|
104
+ index_map = []
105
+ ptr = start + index2
106
+ divisors.each_with_object(index_map) do |div, a|
107
+ a << (ptr / div.to_f).floor
108
+ ptr = ptr % div
109
+ end
110
+
111
+ rotated_index = Array.new(axis + 1) { index_map.shift }
112
+ index_map = rotated_index.rotate!(-1) + index_map
113
+
114
+ ptr2 = 0
115
+ multipliers.each_with_index do |m, idx|
116
+ ptr2 += index_map[idx] * m
117
+ end
118
+
119
+ output_buffer[ptr2] = x
120
+ end
121
+ end
122
+
123
+ res = TensorShape.reshape(output_buffer, new_shape)
124
+
125
+ TensorStream::Evaluator::OutputGroup.new(res)
126
+ end
127
+
77
128
  register_op :squeeze do |_context, tensor, inputs|
78
129
  val = inputs[0]
79
130
  shape = shape_eval(val)
@@ -81,10 +132,9 @@ module TensorStream
81
132
  axis = !tensor.options[:axis].is_a?(Array) ? [tensor.options[:axis]] : tensor.options[:axis]
82
133
 
83
134
  if !axis.empty?
84
-
85
135
  axis.each do |axis|
86
136
  if shape[axis] == 1
87
- shape[axis] = nil
137
+ shape[axis] = nil
88
138
  else
89
139
  raise TensorStream::ValueError, "unable to squeeze dimension that does not have a size of 1"
90
140
  end
@@ -93,7 +143,7 @@ module TensorStream
93
143
  shape = shape.map { |s| s == 1 ? nil : s }
94
144
  end
95
145
 
96
- TensorShape.reshape(val.flatten, shape.compact)
146
+ TensorShape.reshape(val, shape.compact)
97
147
  end
98
148
 
99
149
  register_op :expand_dims do |_context, _tensor, inputs|
@@ -105,7 +155,7 @@ module TensorStream
105
155
 
106
156
  new_shape = shape.dup.insert(axis, 1).compact
107
157
 
108
- TensorShape.reshape([val].flatten, new_shape)
158
+ TensorShape.reshape([val], new_shape)
109
159
  end
110
160
 
111
161
  register_op :fill do |_context, _tensor, inputs|
@@ -207,7 +257,6 @@ module TensorStream
207
257
  else
208
258
  -> { int_type?(tensor.data_type) ? 1 : 1.0 }
209
259
  end
210
-
211
260
  if shape.is_a?(Array) && shape.size.zero?
212
261
  func.call
213
262
  else
@@ -232,16 +281,38 @@ module TensorStream
232
281
  get_rank(inputs[0])
233
282
  end
234
283
 
284
+ register_op :split do |context, tensor, inputs|
285
+ value, num_split, axis = inputs
286
+
287
+ value_shape = shape_eval(value)
288
+ res = if num_split.is_a?(Array)
289
+ begin_index = 0
290
+ num_split.collect do |num|
291
+ end_index = begin_index + num
292
+ arr = split_tensor(value, begin_index, end_index, axis)
293
+ begin_index = end_index
294
+ arr
295
+ end
296
+ else
297
+ raise TensorStream::ValueError, "#{num_split} does not divide #{value_shape[axis]} evenly" if value_shape[axis] % num_split != 0
298
+ piece_sizes = value_shape[axis] / num_split
299
+ Array.new(num_split) do |num|
300
+ begin_index = num * piece_sizes
301
+ end_index = begin_index + piece_sizes
302
+ split_tensor(value, begin_index, end_index, axis)
303
+ end
304
+ end
305
+ TensorStream::Evaluator::OutputGroup.new(res)
306
+ end
307
+
235
308
  register_op :reshape do |_context, _tensor, inputs|
236
309
  arr, new_shape = inputs
237
-
238
310
  arr = [arr] unless arr.is_a?(Array)
239
311
 
240
312
  flat_arr = arr.flatten
241
313
  if new_shape.size.zero? && flat_arr.size == 1
242
314
  flat_arr[0]
243
315
  else
244
- new_shape = TensorShape.fix_inferred_elements(new_shape, flat_arr.size)
245
316
  TensorShape.reshape(flat_arr, new_shape)
246
317
  end
247
318
  end
@@ -276,6 +347,17 @@ module TensorStream
276
347
  pred = complete_eval(tensor.options[:pred], context)
277
348
  call_3way_vector_op(pred, inputs[0], inputs[1], context, ->(t, u, v) { t ? u : v })
278
349
  end
350
+
351
+ register_op :shape do |_context, tensor, inputs|
352
+ shape_eval(inputs[0], tensor.options[:out_type])
353
+ end
354
+
355
+ register_op :shape_n do |_context, _tensor, inputs|
356
+ shapes = inputs.collect do |input|
357
+ shape_eval(input)
358
+ end
359
+ TensorStream::Evaluator::OutputGroup.new(shapes)
360
+ end
279
361
  end
280
362
  end
281
363
  end
@@ -0,0 +1,9 @@
1
+ module TensorStream
2
+ module CheckOps
3
+ def CheckOps.included(klass)
4
+ klass.class_eval do
5
+
6
+ end
7
+ end
8
+ end
9
+ end
@@ -34,7 +34,7 @@ module TensorStream
34
34
 
35
35
  color_values
36
36
  end
37
- TensorShape.reshape(image_data.flatten, [image.height, image.width, channels])
37
+ TensorShape.reshape(image_data, [image.height, image.width, channels])
38
38
  end
39
39
 
40
40
  register_op :encode_png do |_context, tensor, inputs|
@@ -2,24 +2,24 @@ module TensorStream
2
2
  module MathOps
3
3
  def MathOps.included(klass)
4
4
  klass.class_eval do
5
- register_op :tanh, no_eval: true do |context, _tensor, inputs|
6
- call_op(:tanh, inputs[0], context, ->(t, _b) { Math.tanh(t) })
5
+ register_op :tanh, no_eval: true do |context, tensor, inputs|
6
+ call_op(tensor, inputs[0], context, ->(t, _b) { Math.tanh(t) })
7
7
  end
8
8
 
9
- register_op :tan, no_eval: true do |context, _tensor, inputs|
10
- call_op(:tan, inputs[0], context, ->(t, _b) { Math.tan(t) })
9
+ register_op :tan, no_eval: true do |context, tensor, inputs|
10
+ call_op(tensor, inputs[0], context, ->(t, _b) { Math.tan(t) })
11
11
  end
12
12
 
13
- register_op :atan, no_eval: true do |context, _tensor, inputs|
14
- call_op(:atan, inputs[0], context, ->(t, _b) { Math.atan(t) })
13
+ register_op :atan, no_eval: true do |context, tensor, inputs|
14
+ call_op(tensor, inputs[0], context, ->(t, _b) { Math.atan(t) })
15
15
  end
16
16
 
17
- register_op :sec, no_eval: true do |context, _tensor, inputs|
18
- call_op(:sec, inputs[0], context, ->(t, _b) { Math.sec(t) })
17
+ register_op :sec, no_eval: true do |context, tensor, inputs|
18
+ call_op(tensor, inputs[0], context, ->(t, _b) { Math.sec(t) })
19
19
  end
20
20
 
21
- register_op :sin, no_eval: true do |context, _tensor, inputs|
22
- call_op(:sin, inputs[0], context, ->(t, _b) { Math.sin(t) })
21
+ register_op :sin, no_eval: true do |context, tensor, inputs|
22
+ call_op(tensor, inputs[0], context, ->(t, _b) { Math.sin(t) })
23
23
  end
24
24
 
25
25
  register_op :add, no_eval: true do |context, tensor, inputs|
@@ -79,64 +79,64 @@ module TensorStream
79
79
  call_op(:round, inputs[0], context, ->(t, _b) { t.round })
80
80
  end
81
81
 
82
- register_op :abs, no_eval: true do |context, _tensor, inputs|
83
- call_op(:abs, inputs[0], context, ->(t, _b) { t.abs })
82
+ register_op :abs, no_eval: true do |context, tensor, inputs|
83
+ call_op(tensor, inputs[0], context, ->(t, _b) { t.abs })
84
84
  end
85
85
 
86
- register_op :asin, no_eval: true do |context, _tensor, inputs|
87
- call_op(:asin, inputs[0], context, ->(t, _b) { Math.asin(t) })
86
+ register_op :asin, no_eval: true do |context, tensor, inputs|
87
+ call_op(tensor, inputs[0], context, ->(t, _b) { Math.asin(t) })
88
88
  end
89
89
 
90
- register_op :acos, no_eval: true do |context, _tensor, inputs|
91
- call_op(:acos, inputs[0], context, ->(t, _b) { Math.acos(t) })
90
+ register_op :acos, no_eval: true do |context, tensor, inputs|
91
+ call_op(tensor, inputs[0], context, ->(t, _b) { Math.acos(t) })
92
92
  end
93
93
 
94
- register_op :cos, no_eval: true do |context, _tensor, inputs|
95
- call_op(:cos, inputs[0], context, ->(t, _b) { Math.cos(t) })
94
+ register_op :cos, no_eval: true do |context, tensor, inputs|
95
+ call_op(tensor, inputs[0], context, ->(t, _b) { Math.cos(t) })
96
96
  end
97
97
 
98
- register_op :log1p, no_eval: true do |context, _tensor, inputs|
99
- call_op(:log1p, inputs[0], context, ->(t, _b) { Math.log(1 + t) })
98
+ register_op :log1p, no_eval: true do |context, tensor, inputs|
99
+ call_op(tensor, inputs[0], context, ->(t, _b) { Math.log(1 + t) })
100
100
  end
101
101
 
102
- register_op :log, no_eval: true do |context, _tensor, inputs|
103
- call_op(:log, inputs[0], context, ->(t, _b) { t < 0 ? Float::NAN : Math.log(t) })
102
+ register_op :log, no_eval: true do |context, tensor, inputs|
103
+ call_op(tensor, inputs[0], context, ->(t, _b) { t < 0 ? Float::NAN : Math.log(t) })
104
104
  end
105
105
 
106
- register_op :exp, no_eval: true do |context, _tensor, inputs|
107
- call_op(:exp, inputs[0], context, ->(t, _b) { Math.exp(t) })
106
+ register_op :exp, no_eval: true do |context, tensor, inputs|
107
+ call_op(tensor, inputs[0], context, ->(t, _b) { Math.exp(t) })
108
108
  end
109
109
 
110
- register_op :sigmoid, no_eval: true do |context, _tensor, inputs|
111
- call_op(:sigmoid, inputs[0], context, ->(t, _b) { sigmoid(t) })
110
+ register_op :sigmoid, no_eval: true do |context, tensor, inputs|
111
+ call_op(tensor, inputs[0], context, ->(t, _b) { sigmoid(t) })
112
112
  end
113
113
 
114
- register_op :sqrt, no_eval: true do |context, _tensor, inputs|
115
- call_op(:sqrt, inputs[0], context, ->(t, _b) { Math.sqrt(t) })
114
+ register_op :sqrt, no_eval: true do |context, tensor, inputs|
115
+ call_op(tensor, inputs[0], context, ->(t, _b) { Math.sqrt(t) })
116
116
  end
117
117
 
118
- register_op :floor, no_eval: true do |context, _tensor, inputs|
119
- call_op(:floor, inputs[0], context, ->(t, _b) { t.floor })
118
+ register_op :floor, no_eval: true do |context, tensor, inputs|
119
+ call_op(tensor, inputs[0], context, ->(t, _b) { t.floor })
120
120
  end
121
121
 
122
- register_op :ceil, no_eval: true do |context, _tensor, inputs|
123
- call_op(:ceil, inputs[0], context, ->(t, _b) { t.ceil })
122
+ register_op :ceil, no_eval: true do |context, tensor, inputs|
123
+ call_op(tensor, inputs[0], context, ->(t, _b) { t.ceil })
124
124
  end
125
125
 
126
- register_op :square, no_eval: true do |context, _tensor, inputs|
127
- call_op(:square, inputs[0], context, ->(t, _b) { t * t })
126
+ register_op :square, no_eval: true do |context, tensor, inputs|
127
+ call_op(tensor, inputs[0], context, ->(t, _b) { t * t })
128
128
  end
129
129
 
130
- register_op :reciprocal, no_eval: true do |context, _tensor, inputs|
131
- call_op(:reciprocal, inputs[0], context, ->(t, _b) { 1 / t })
130
+ register_op :reciprocal, no_eval: true do |context, tensor, inputs|
131
+ call_op(tensor, inputs[0], context, ->(t, _b) { 1 / t })
132
132
  end
133
133
 
134
134
  register_op %i[neg negate], no_eval: true do |context, tensor, inputs|
135
135
  call_vector_op(tensor, :negate, inputs[0], nil, context, ->(t, _u) { -t })
136
136
  end
137
137
 
138
- register_op :tanh_grad, no_eval: true do |context, _tensor, inputs|
139
- call_op(:tanh_grad, inputs[0], context, ->(t, _b) { 1 - Math.tanh(t) * Math.tanh(t) })
138
+ register_op :tanh_grad, no_eval: true do |context, tensor, inputs|
139
+ call_op(tensor, inputs[0], context, ->(t, _b) { 1 - Math.tanh(t) * Math.tanh(t) })
140
140
  end
141
141
 
142
142
  register_op(%i[argmax arg_max]) do |_context, tensor, inputs|
@@ -15,46 +15,78 @@ module TensorStream
15
15
  target_var, momentum_var, learning_rate, grad, momentum = inputs
16
16
  assign = tensor.inputs[0] || tensor
17
17
  assign_acc = tensor.inputs[1]
18
- assign_acc.value = multi_array_op(->(t, u) { t * momentum + u }, momentum_var, grad )
18
+ assign_acc.value = multi_array_op(->(t, u) { t * momentum + u }, momentum_var, grad)
19
19
  if tensor.options[:use_nesterov]
20
- assign.value = multi_array_op(->(v, g, acc) { v - (g * learning_rate + acc * momentum * learning_rate) } , target_var, grad, momentum_var)
20
+ assign.value = multi_array_op(->(v, g, acc) { v - (g * learning_rate + acc * momentum * learning_rate) }, target_var, grad, momentum_var)
21
21
  else
22
22
  assign.value = multi_array_op(->(v, acc) { v - acc * learning_rate }, target_var, momentum_var)
23
23
  end
24
24
  assign.value
25
25
  end
26
26
 
27
- register_op :apply_adadelta do |context, tensor, inputs|
27
+ register_op :apply_adadelta do |_context, tensor, inputs|
28
28
  target_var, accum, accum_update, lr, rho, epsilon, grad = inputs
29
29
  assign = tensor.inputs[0] || tensor
30
30
  assign_acc = tensor.inputs[1]
31
31
  assign_acc_update = tensor.inputs[2]
32
- assign_acc.value = multi_array_op(->(acc_t, grad_t) { acc_t * rho + (grad_t * grad_t) * (1.0 - rho ) }, accum, grad)
32
+ assign_acc.value = multi_array_op(->(acc_t, grad_t) { acc_t * rho + (grad_t * grad_t) * (1.0 - rho) }, accum, grad)
33
33
  update = multi_array_op(->(acc_update_t, acc_t, grad_t) { Math.sqrt(acc_update_t + epsilon) * (1.0 / Math.sqrt(acc_t + epsilon)) * grad_t }, accum_update, assign_acc.value, grad)
34
34
  assign.value = multi_array_op(->(v, u) { v - (u * lr) }, target_var, update)
35
- assign_acc_update.value = multi_array_op(->(acc_update_t, u) { acc_update_t * rho + (u * u) * (1.0 - rho) }, accum_update, update)
36
-
35
+ assign_acc_update.value = multi_array_op(->(acc_update_t, u) { acc_update_t * rho + (u * u) * (1.0 - rho) }, accum_update, update)
36
+
37
+ assign.value
38
+ end
39
+
40
+ register_op :apply_adagrad do |_context, tensor, inputs|
41
+ target_var, accum, lr, grad = inputs
42
+ assign = tensor.inputs[0] || tensor
43
+ assign.value = multi_array_op(->(v, a, g) { v - (g * lr * (1.0 / Math.sqrt(a))) }, target_var, accum, grad)
37
44
  assign.value
38
45
  end
39
46
 
40
- register_op :apply_adam do |context, tensor, inputs|
47
+ register_op :apply_adam do |_context, tensor, inputs|
41
48
  target_var, m, v, beta1_power, beta2_power, lr_t, beta1_t, beta2_t, epsilon_t, grad = inputs
42
- alpha = lr_t * Math.sqrt( 1.0 - beta2_power) / (1.0 - beta1_power)
49
+ alpha = lr_t * Math.sqrt(1.0 - beta2_power) / (1.0 - beta1_power)
43
50
  assign = tensor.inputs[0]
44
51
  assign_m = tensor.inputs[1]
45
52
  assign_v = tensor.inputs[2]
46
53
 
47
54
  assign_m.value = multi_array_op(->(u_d , g) { u_d + (g - u_d) * (1.0 - beta1_t) }, m, grad)
48
- assign_v.value = multi_array_op(->(u_d , v_d) { u_d + (v_d ** 2 - u_d) * (1.0 - beta2_t)}, v, grad)
49
- assign.value = multi_array_op( ->(t, m_d , v_d) { t - ((m_d * alpha) / (Math.sqrt(v_d) + epsilon_t)) }, target_var, assign_m.value, assign_v.value)
55
+ assign_v.value = multi_array_op(->(u_d , v_d) { u_d + (v_d**2 - u_d) * (1.0 - beta2_t)}, v, grad)
56
+ assign.value = multi_array_op(->(t, m_d , v_d) { t - ((m_d * alpha) / (Math.sqrt(v_d) + epsilon_t)) }, target_var, assign_m.value, assign_v.value)
50
57
  assign.value
51
58
  end
52
59
 
60
+ register_op :apply_rms_prop do |_context, tensor, inputs|
61
+ var, ms, mom, lr, rho, momentum, epsilon, grad = inputs
62
+ assign = tensor.inputs[0]
63
+ assign_ms = tensor.inputs[1]
64
+ assign_mom = tensor.inputs[2]
65
+ assign_ms.value = multi_array_op(->(g, m) { m + (g * g - m) * (1.0 - rho)}, grad, ms)
66
+ assign_mom.value = multi_array_op(->(mom_t, g, m) { mom_t * momentum + (g * lr) / Math.sqrt(m + epsilon)}, mom, grad, assign_ms.value)
67
+ assign.value = multi_array_op(->(v, m) { v - m }, var, assign_mom.value)
68
+ end
69
+
70
+ register_op :apply_centered_rms_prop do |_context, tensor, inputs|
71
+ var, mg, ms, mom, lr, rho, momentum, epsilon, grad = inputs
72
+ assign = tensor.inputs[0]
73
+ assign_mg = tensor.inputs[1]
74
+ assign_ms = tensor.inputs[2]
75
+ assign_mom = tensor.inputs[3]
76
+
77
+ assign_ms.value = multi_array_op(->(g, m) { m + (g * g - m) * (1.0 - rho) }, grad, ms)
78
+ assign_mg.value = multi_array_op(->(g, mg_t) { (g - mg_t) * (1.0 - rho) }, grad, mg)
79
+ denom = multi_array_op(->(s, mg_t) { (s - mg_t * mg_t) + epsilon }, assign_ms.value, mg)
80
+ assign_mom.value = multi_array_op(->(mom_t, g, d) { mom_t * momentum + (g * lr) / Math.sqrt(d)}, mom, grad, denom)
81
+ assign.value = multi_array_op(->(v, m) { v - m }, var, assign_mom.value)
82
+ end
83
+
53
84
  register_op %i[softmax_cross_entropy_with_logits_v2 softmax_cross_entropy_with_logits] do |_context, tensor, inputs|
54
85
  last_dimen_list = last_axis(inputs[0])
55
86
  input_shape = shape_eval(inputs[0])
56
87
  rank = input_shape.size - 1
57
88
  labels = last_axis(inputs[1])
89
+
58
90
  func = lambda { |logits, label|
59
91
  c = logits.max
60
92
  transformed_logits = logits.map { |l| l - c }
@@ -83,6 +115,49 @@ module TensorStream
83
115
  end
84
116
  end
85
117
 
118
+ register_op :sparse_softmax_cross_entropy_with_logits do |context, tensor, inputs|
119
+ last_dimen_list = last_axis(inputs[0])
120
+ input_shape = shape_eval(inputs[0])
121
+ rank = input_shape.size - 1
122
+ labels = last_axis(inputs[1])
123
+ num_classes = input_shape.last
124
+
125
+ labels = labels.map do |l|
126
+ one_hot = Array.new(num_classes) { 0 }
127
+ one_hot[l] = 1
128
+ one_hot
129
+ end
130
+
131
+ func = lambda { |logits, label|
132
+ c = logits.max
133
+ transformed_logits = logits.map { |l| l - c }
134
+ sum = transformed_logits.map { |x| Math.exp(x) }.reduce(:+)
135
+ losses = transformed_logits.zip(label).map { |x, y| (Math.log(sum) - x) * y }
136
+ probs = transformed_logits.zip(label).map { |x, y| (Math.exp(x) / sum) - y }
137
+ [losses, probs]
138
+ }
139
+
140
+ if input_shape.size == 1
141
+ loss, prob = func.call(last_dimen_list, labels)
142
+ loss = reduce(loss, rank, false)
143
+
144
+ TensorStream::Evaluator::OutputGroup.new([loss, prob], [tensor.inputs[0].data_type, tensor.inputs[0].data_type])
145
+ else
146
+ losses = []
147
+ backprobs = []
148
+ arr = last_dimen_list.zip(labels).each do |list, label|
149
+ loss, prob = func.call(list, label)
150
+ losses << loss
151
+ backprobs << prob
152
+ end
153
+ reshaped_losses = TensorShape.reshape(losses, input_shape)
154
+ reshaped_backprops = TensorShape.reshape(backprobs, input_shape)
155
+ reshaped_losses = reduce(reshaped_losses, rank, false)
156
+
157
+ TensorStream::Evaluator::OutputGroup.new([reshaped_losses, reshaped_backprops], [tensor.inputs[0].data_type, tensor.inputs[0].data_type])
158
+ end
159
+ end
160
+
86
161
  register_op :log_softmax do |_context, _tensor, inputs|
87
162
  input_shape = shape_eval(inputs[0])
88
163
  last_dimen_list = last_axis(inputs[0])
@@ -100,7 +175,7 @@ module TensorStream
100
175
  arr = last_dimen_list.collect do |list|
101
176
  func.call(list)
102
177
  end
103
- TensorShape.reshape(arr.flatten, input_shape)
178
+ TensorShape.reshape(arr, input_shape)
104
179
  end
105
180
  end
106
181
 
@@ -129,7 +204,7 @@ module TensorStream
129
204
  arr = last_dimen_list.zip(last_grad_list).collect do |list, last_grad|
130
205
  func.call(list, last_grad)
131
206
  end
132
- TensorShape.reshape(arr.flatten, input_shape)
207
+ TensorShape.reshape(arr, input_shape)
133
208
  end
134
209
  end
135
210
  end