tensor_stream 0.9.1 → 0.9.2
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 +5 -5
- data/CHANGELOG.md +7 -2
- data/lib/tensor_stream.rb +5 -0
- data/lib/tensor_stream/evaluator/base_evaluator.rb +35 -4
- data/lib/tensor_stream/evaluator/ruby/math_ops.rb +0 -1
- data/lib/tensor_stream/evaluator/ruby_evaluator.rb +4 -2
- data/lib/tensor_stream/profile/report_tool.rb +18 -0
- data/lib/tensor_stream/session.rb +9 -5
- data/lib/tensor_stream/utils.rb +36 -3
- data/lib/tensor_stream/version.rb +1 -1
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 9601653d86556739c89b591768e9d54d13d6335dd2f953fff7d91f22636e8c7b
|
4
|
+
data.tar.gz: 3db4119c9e752df77cbbf7f8f753050a012b03fd2bde04dd6bbfd01f8903af62
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3ca3eec8ce6cc7e73a1d5e38ccaaf2f65f7a37bb84e0118766de05ec374557847a88f2f14b1e35b896010d7e68a0234589fcf3de95a9417840e2e77ab4a4ea58
|
7
|
+
data.tar.gz: 6b109a2ee9b59e286a2d06d1c3166fc5bbda9a888f0c26aa07bc4f994753ce5dbd7cb0aebb433a7446ddef653f3096ae1b60dbc4610e0dc18c9aaca8d3175f2f
|
data/CHANGELOG.md
CHANGED
@@ -4,9 +4,14 @@ 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.9.2] - 2018-10-19
|
8
|
+
- Add profiling support
|
9
|
+
- Make sure sparse ruby arrays are caught
|
10
|
+
- Enhancements to support multi-gpu execution
|
11
|
+
|
7
12
|
## [0.9.1] - 2018-10-19
|
8
13
|
- Bug fix release
|
9
|
-
|
14
|
+
|
10
15
|
## [0.9.0] - 2018-10-05
|
11
16
|
- Bug fix release for OpenCL gem
|
12
17
|
|
@@ -16,7 +21,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
16
21
|
- [TRAINING] Added RMSPropOptimizer, AdagradOptimizer
|
17
22
|
- [NEW OP] shape_n, sparse_softmax_cross_entropy_with_logits, split, unstack
|
18
23
|
- Added RNN sample
|
19
|
-
|
24
|
+
|
20
25
|
### Fixes
|
21
26
|
- Fixed gradient computation when passing an array of tensors to a function
|
22
27
|
- Added gradients for various other ops
|
data/lib/tensor_stream.rb
CHANGED
@@ -33,6 +33,7 @@ require 'tensor_stream/utils'
|
|
33
33
|
require 'tensor_stream/train/utils'
|
34
34
|
require 'tensor_stream/images'
|
35
35
|
require 'tensor_stream/trainer'
|
36
|
+
require 'tensor_stream/profile/report_tool'
|
36
37
|
|
37
38
|
# require 'tensor_stream/libraries/layers'
|
38
39
|
require 'tensor_stream/monkey_patches/integer'
|
@@ -44,4 +45,8 @@ module TensorStream
|
|
44
45
|
extend TensorStream::Ops
|
45
46
|
extend TensorStream::Debugging
|
46
47
|
extend TensorStream::Utils
|
48
|
+
|
49
|
+
def self.__version__
|
50
|
+
TensorStream::VERSION
|
51
|
+
end
|
47
52
|
end
|
@@ -19,7 +19,10 @@ module TensorStream
|
|
19
19
|
end
|
20
20
|
end
|
21
21
|
|
22
|
+
##
|
22
23
|
# Evaluator base class
|
24
|
+
#
|
25
|
+
# Base class to be used by all tensor_stream evaluators, provides support functions
|
23
26
|
class BaseEvaluator
|
24
27
|
def initialize(session, _device, thread_pool: nil, log_intermediates: false)
|
25
28
|
@session = session
|
@@ -57,6 +60,7 @@ module TensorStream
|
|
57
60
|
substrs.each do |q|
|
58
61
|
components = q.split(':')
|
59
62
|
next if components.size.zero?
|
63
|
+
|
60
64
|
if components[0] == 'device' # use tensorflow convention
|
61
65
|
device_type = components[1]
|
62
66
|
select_index = components[2].to_i
|
@@ -79,6 +83,7 @@ module TensorStream
|
|
79
83
|
evaluator_class = TensorStream::Evaluator.evaluators[components[1]][:class]
|
80
84
|
return nil unless self == evaluator_class
|
81
85
|
return evaluator_class.fetch_device(components[2..components.size]) if evaluator_class.respond_to?(:fetch_device)
|
86
|
+
|
82
87
|
return nil
|
83
88
|
end
|
84
89
|
end
|
@@ -121,14 +126,34 @@ module TensorStream
|
|
121
126
|
global_eval(tensor, i, execution_context, op_options)
|
122
127
|
end
|
123
128
|
|
124
|
-
|
129
|
+
start_time = if profile_enabled?
|
130
|
+
time = Time.now
|
131
|
+
time.to_i * (10**9) + time.nsec
|
132
|
+
end
|
133
|
+
|
134
|
+
instance_exec(execution_context, tensor, resolved_inputs, &op[:block]).tap do
|
135
|
+
if profile_enabled?
|
136
|
+
time = Time.now
|
137
|
+
end_time = time.to_i * (10**9) + time.nsec
|
138
|
+
@context[:profile] ||= { step: 0, operations: {} }
|
139
|
+
@context[:profile][:step] += 1
|
140
|
+
@context[:profile][:operations][tensor.name] = { op: tensor.operation,
|
141
|
+
step: @context[:profile][:step],
|
142
|
+
eval_time: end_time - start_time,
|
143
|
+
tensor: tensor }
|
144
|
+
end
|
145
|
+
end
|
125
146
|
end
|
126
147
|
|
127
148
|
protected
|
128
149
|
|
150
|
+
def profile_enabled?
|
151
|
+
@context[:_options][:profile_enabled]
|
152
|
+
end
|
153
|
+
|
129
154
|
##
|
130
155
|
# called when passing control to another evaluator
|
131
|
-
def perform_transition(tensor, input, _next_evaluator)
|
156
|
+
def perform_transition(tensor, input, _next_evaluator, execution_context)
|
132
157
|
cache_key = "#{tensor.graph.object_id}_#{input.name}:#{object_id}"
|
133
158
|
return @context[:_cache][cache_key] if @context[:_cache].key?(cache_key)
|
134
159
|
|
@@ -141,14 +166,20 @@ module TensorStream
|
|
141
166
|
def global_eval(tensor, input, execution_context, op_options = {})
|
142
167
|
return nil unless input
|
143
168
|
return input unless input.is_a?(Tensor)
|
169
|
+
# puts "global eval #{tensor.name}"
|
144
170
|
@context[:_cache][:placement][input.name] = @session.assign_evaluator(input) if @context[:_cache][:placement][input.name].nil?
|
145
|
-
if
|
146
|
-
|
171
|
+
if !on_same_device?(input) # tensor is on another device or evaluator
|
172
|
+
# puts "transition #{object_id} -> #{@context[:_cache][:placement][input.name][1].object_id}"
|
173
|
+
perform_transition(tensor, input, @context[:_cache][:placement][input.name][1], execution_context)
|
147
174
|
else
|
148
175
|
prepare_input(input, execution_context, op_options)
|
149
176
|
end
|
150
177
|
end
|
151
178
|
|
179
|
+
def on_same_device?(tensor)
|
180
|
+
object_id == @context[:_cache][:placement][tensor.name][1].object_id
|
181
|
+
end
|
182
|
+
|
152
183
|
def get_broadcast_gradient_args(input_a, input_b)
|
153
184
|
return [[], []] if input_a == input_b
|
154
185
|
|
@@ -282,15 +282,17 @@ module TensorStream
|
|
282
282
|
|
283
283
|
def eval_operation(tensor, child_context)
|
284
284
|
return @context[tensor.name] if @context.key?(tensor.name)
|
285
|
-
invoke(tensor, child_context).tap do |result|
|
286
285
|
|
286
|
+
# puts "ruby eval #{object_id}: #{tensor.name}"
|
287
|
+
invoke(tensor, child_context).tap do |result|
|
288
|
+
# puts "result done ruby #{object_id}: #{tensor.name}"
|
287
289
|
# assertions to make sure inferred shapes == actual evaluated shapes
|
288
290
|
if tensor.shape.known? && (result.is_a?(Array) || result.is_a?(Float) || result.is_a?(Integer))
|
289
291
|
if shape_eval(result) != tensor.shape.shape
|
290
292
|
raise "assert error #{tensor.name} #{shape_eval(result)} != #{tensor.shape.shape}"
|
291
293
|
end
|
292
294
|
end
|
293
|
-
|
295
|
+
|
294
296
|
if tensor.breakpoint
|
295
297
|
a = resolve_placeholder(tensor.inputs[0], child_context) if tensor.inputs && tensor.inputs[0]
|
296
298
|
b = resolve_placeholder(tensor.inputs[1], child_context) if tensor.inputs && tensor.inputs[1]
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module TensorStream
|
2
|
+
##
|
3
|
+
# Utiliity functions for creating performance reports
|
4
|
+
class ReportTool
|
5
|
+
def self.profile_for(session, order_by: :slowest)
|
6
|
+
context = session.last_session_context
|
7
|
+
eval_times = context[:profile][:operations].map do |name, profile|
|
8
|
+
[name, profile[:eval_time], profile[:tensor].source]
|
9
|
+
end
|
10
|
+
|
11
|
+
if order_by == :slowest
|
12
|
+
eval_times.sort_by { |a, b| b[1] <=> a[1] }
|
13
|
+
else
|
14
|
+
eval_times.sort_by { |a, b| a[1] <=> b[1] }
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -6,13 +6,13 @@ module TensorStream
|
|
6
6
|
attr_reader :last_session_context, :closed, :target, :session_cache
|
7
7
|
attr_accessor :randomizer
|
8
8
|
|
9
|
-
def initialize(evaluator = nil, thread_pool_class: Concurrent::ImmediateExecutor, log_device_placement: false, evaluator_options: {})
|
9
|
+
def initialize(evaluator = nil, thread_pool_class: Concurrent::ImmediateExecutor, log_device_placement: false, profile_enabled: false, evaluator_options: {})
|
10
10
|
@thread_pool = thread_pool_class.new
|
11
11
|
@closed = false
|
12
12
|
@session_cache = {}
|
13
13
|
@randomizer = {}
|
14
14
|
@log_device_placement = log_device_placement
|
15
|
-
@evaluator_options = evaluator_options
|
15
|
+
@evaluator_options = evaluator_options.merge(profile_enabled: profile_enabled)
|
16
16
|
get_evaluator_classes(evaluator)
|
17
17
|
@evaluators = {}
|
18
18
|
end
|
@@ -45,8 +45,13 @@ module TensorStream
|
|
45
45
|
else
|
46
46
|
{}
|
47
47
|
end
|
48
|
+
|
49
|
+
@evaluator_options[:thread_pool] = @thread_pool
|
50
|
+
@evaluator_options[:log_intermediates] = options[:log_intermediates]
|
51
|
+
|
48
52
|
context = {
|
49
|
-
_cache: @session_cache
|
53
|
+
_cache: @session_cache,
|
54
|
+
_options: options.merge(@evaluator_options)
|
50
55
|
}
|
51
56
|
|
52
57
|
# scan for placeholders and assign value
|
@@ -56,8 +61,6 @@ module TensorStream
|
|
56
61
|
end
|
57
62
|
end
|
58
63
|
|
59
|
-
@evaluator_options[:thread_pool] = @thread_pool
|
60
|
-
@evaluator_options[:log_intermediates] = options[:log_intermediates]
|
61
64
|
|
62
65
|
args.each { |t| prepare_evaluators(t, context) }
|
63
66
|
@last_session_context = context
|
@@ -69,6 +72,7 @@ module TensorStream
|
|
69
72
|
end
|
70
73
|
result = args.collect do |e|
|
71
74
|
next e.value if e.is_a?(Tensor) && e.is_const && e.value
|
75
|
+
|
72
76
|
value = delegate_to_evaluator(e, context, {})
|
73
77
|
recursive_eval(value)
|
74
78
|
end
|
data/lib/tensor_stream/utils.rb
CHANGED
@@ -129,8 +129,18 @@ module TensorStream
|
|
129
129
|
Thread.current[:tensor_stream_variable_scope].map(&:name).compact.reject(&:empty?).join('/')
|
130
130
|
end
|
131
131
|
|
132
|
-
|
133
|
-
|
132
|
+
##
|
133
|
+
# Creates a session context where operations can be executed
|
134
|
+
#
|
135
|
+
# Args:
|
136
|
+
# evaluator: Specific evaluator to use, otherwise the best evaluator will automatically be determined
|
137
|
+
#
|
138
|
+
# Options:
|
139
|
+
# thread_pool_class: Class to use to manage thread pooling
|
140
|
+
# log_device_placement: Show assigned device/evalutor for each tensor op
|
141
|
+
# profile_enabled: Log performance metrics for each operation
|
142
|
+
def session(evaluator = nil, thread_pool_class: Concurrent::ImmediateExecutor, log_device_placement: false, profile_enabled: false)
|
143
|
+
session = TensorStream::Session.new(evaluator, thread_pool_class: thread_pool_class, log_device_placement: log_device_placement, profile_enabled: profile_enabled)
|
134
144
|
yield session if block_given?
|
135
145
|
|
136
146
|
session
|
@@ -151,6 +161,7 @@ module TensorStream
|
|
151
161
|
|
152
162
|
def constant(value, dtype: nil, shape: nil, internal: false, name: 'Const')
|
153
163
|
shared_options = { const: true, value: value, name: name, internal: internal }
|
164
|
+
|
154
165
|
if value.is_a?(Float)
|
155
166
|
TensorStream::Tensor.new(dtype || :float32, 0, shape || [], shared_options)
|
156
167
|
elsif value.is_a?(Integer)
|
@@ -162,6 +173,7 @@ module TensorStream
|
|
162
173
|
elsif value.is_a?(Array)
|
163
174
|
dimension = shape || shape_eval(value)
|
164
175
|
rank = dimension.size
|
176
|
+
TensorStream.check_if_dense(value)
|
165
177
|
|
166
178
|
cur_dtype = dtype || Tensor.detect_type(value.flatten.last)
|
167
179
|
value = Tensor.cast_dtype(value, cur_dtype) unless dtype.nil?
|
@@ -224,18 +236,39 @@ module TensorStream
|
|
224
236
|
TensorStream.get_default_graph.control_dependencies(control_inputs, &block)
|
225
237
|
end
|
226
238
|
|
227
|
-
def convert_to_tensor(value, dtype: nil, name: nil
|
239
|
+
def convert_to_tensor(value, dtype: nil, name: nil)
|
228
240
|
return value if value.is_a?(Tensor)
|
229
241
|
return convert_to_tensor(value.call) if value.is_a?(Proc)
|
242
|
+
|
230
243
|
if value.is_a?(Array) && value[0].is_a?(Tensor)
|
231
244
|
return TensorStream.stack(value) if value.size > 1
|
232
245
|
|
233
246
|
return TensorStream.expand_dims(value[0], 0)
|
234
247
|
end
|
235
248
|
|
249
|
+
check_if_dense(value)
|
236
250
|
i_cons(value, dtype: dtype || Tensor.detect_type(value), name: name)
|
237
251
|
end
|
238
252
|
|
253
|
+
##
|
254
|
+
# Check to make sure passed array is dense
|
255
|
+
#
|
256
|
+
def check_if_dense(value, expected_shape = nil)
|
257
|
+
return unless value.is_a?(Array)
|
258
|
+
return if value.empty?
|
259
|
+
|
260
|
+
expected_shape ||= shape_eval(value)
|
261
|
+
|
262
|
+
s = expected_shape.shift
|
263
|
+
raise TensorStream::ValueError, "Argument must be a dense tensor: #{value}, expected size #{s} got #{value.size}" if value.size != s
|
264
|
+
|
265
|
+
return if expected_shape.empty?
|
266
|
+
|
267
|
+
value.each do |item|
|
268
|
+
check_if_dense(item, expected_shape.dup)
|
269
|
+
end
|
270
|
+
end
|
271
|
+
|
239
272
|
def check_allowed_types(input, types)
|
240
273
|
return input unless input.is_a?(Tensor)
|
241
274
|
return input if input.data_type.nil?
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: tensor_stream
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.9.
|
4
|
+
version: 0.9.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Joseph Emmanuel Dayo
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-10-
|
11
|
+
date: 2018-10-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -285,6 +285,7 @@ files:
|
|
285
285
|
- lib/tensor_stream/operation.rb
|
286
286
|
- lib/tensor_stream/ops.rb
|
287
287
|
- lib/tensor_stream/placeholder.rb
|
288
|
+
- lib/tensor_stream/profile/report_tool.rb
|
288
289
|
- lib/tensor_stream/session.rb
|
289
290
|
- lib/tensor_stream/tensor.rb
|
290
291
|
- lib/tensor_stream/tensor_shape.rb
|
@@ -336,7 +337,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
336
337
|
version: '0'
|
337
338
|
requirements: []
|
338
339
|
rubyforge_project:
|
339
|
-
rubygems_version: 2.
|
340
|
+
rubygems_version: 2.7.7
|
340
341
|
signing_key:
|
341
342
|
specification_version: 4
|
342
343
|
summary: A Pure ruby tensorflow implementation
|