tensor_stream 0.8.5 → 0.8.6

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
  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