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 +4 -4
- data/CHANGELOG.md +11 -0
- data/README.md +9 -7
- data/lib/tensor_stream/evaluator/operation_helpers/array_ops_helper.rb +17 -2
- data/lib/tensor_stream/evaluator/ruby/array_ops.rb +92 -10
- data/lib/tensor_stream/evaluator/ruby/check_ops.rb +9 -0
- data/lib/tensor_stream/evaluator/ruby/images_ops.rb +1 -1
- data/lib/tensor_stream/evaluator/ruby/math_ops.rb +38 -38
- data/lib/tensor_stream/evaluator/ruby/nn_ops.rb +87 -12
- data/lib/tensor_stream/evaluator/ruby_evaluator.rb +16 -13
- data/lib/tensor_stream/graph.rb +2 -0
- data/lib/tensor_stream/helpers/op_helper.rb +1 -0
- data/lib/tensor_stream/math_gradients.rb +86 -5
- data/lib/tensor_stream/nn/nn_ops.rb +47 -0
- data/lib/tensor_stream/operation.rb +25 -4
- data/lib/tensor_stream/ops.rb +160 -6
- data/lib/tensor_stream/session.rb +1 -0
- data/lib/tensor_stream/tensor.rb +4 -7
- data/lib/tensor_stream/tensor_shape.rb +10 -1
- data/lib/tensor_stream/train/adagrad_optimizer.rb +46 -0
- data/lib/tensor_stream/train/optimizer.rb +12 -0
- data/lib/tensor_stream/train/rmsprop_optimizer.rb +84 -0
- data/lib/tensor_stream/train/slot_creator.rb +14 -9
- data/lib/tensor_stream/trainer.rb +2 -0
- data/lib/tensor_stream/utils.rb +6 -4
- data/lib/tensor_stream/version.rb +1 -1
- data/samples/iris.rb +4 -3
- data/samples/linear_regression.rb +3 -0
- data/samples/rnn.rb +105 -0
- metadata +6 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: '0168aeff699a7490174f164370f6cef9dbc3413f23352a696c3a468832bd1b24'
|
4
|
+
data.tar.gz: f62926f3b919cbf5001872d6fab23095807d6c5028de5d885256449e1dd52a01
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
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
|
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 =
|
95
|
-
# optimizer =
|
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
|
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/
|
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
|
-
|
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
|
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
|
-
|
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,
|
26
|
-
|
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
|
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]
|
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
|
@@ -34,7 +34,7 @@ module TensorStream
|
|
34
34
|
|
35
35
|
color_values
|
36
36
|
end
|
37
|
-
TensorShape.reshape(image_data
|
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,
|
6
|
-
call_op(
|
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,
|
10
|
-
call_op(
|
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,
|
14
|
-
call_op(
|
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,
|
18
|
-
call_op(
|
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,
|
22
|
-
call_op(
|
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,
|
83
|
-
call_op(
|
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,
|
87
|
-
call_op(
|
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,
|
91
|
-
call_op(
|
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,
|
95
|
-
call_op(
|
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,
|
99
|
-
call_op(
|
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,
|
103
|
-
call_op(
|
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,
|
107
|
-
call_op(
|
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,
|
111
|
-
call_op(
|
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,
|
115
|
-
call_op(
|
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,
|
119
|
-
call_op(
|
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,
|
123
|
-
call_op(
|
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,
|
127
|
-
call_op(
|
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,
|
131
|
-
call_op(
|
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,
|
139
|
-
call_op(
|
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) }
|
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 |
|
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
|
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) {
|
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 |
|
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(
|
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
|
49
|
-
assign.value = multi_array_op(
|
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
|
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
|
207
|
+
TensorShape.reshape(arr, input_shape)
|
133
208
|
end
|
134
209
|
end
|
135
210
|
end
|