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