tensor_stream 0.9.1 → 0.9.2

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
- SHA1:
3
- metadata.gz: 9c6649e7cb423246d4886776bf93a8422e3be936
4
- data.tar.gz: c62ffe26455cff685925a5a9a27c49babae6d17e
2
+ SHA256:
3
+ metadata.gz: 9601653d86556739c89b591768e9d54d13d6335dd2f953fff7d91f22636e8c7b
4
+ data.tar.gz: 3db4119c9e752df77cbbf7f8f753050a012b03fd2bde04dd6bbfd01f8903af62
5
5
  SHA512:
6
- metadata.gz: 9dd8519a7c4c34fc0d0c8f4900aa2bec73e6a0dfefe15db4f4c6703188e55080cd2dcce4c8df02993beaa1d6f753b2309fabda623f5ab6a0218c3bd4ce382c0c
7
- data.tar.gz: 2e2f051ada324f4f220db702762a179f1ee20985ecbde51d5c8fc976ad50ff32a315459b97a2366c7cf9a718672b42910466b8e1e81ea388a878035807bae1e0
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
- instance_exec(execution_context, tensor, resolved_inputs, &op[:block])
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 object_id != @context[:_cache][:placement][input.name][1].object_id # tensor is on another device or evaluator
146
- perform_transition(tensor, input, @context[:_cache][:placement][input.name][1])
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
 
@@ -186,7 +186,6 @@ module TensorStream
186
186
  end
187
187
  reduced_val
188
188
  end
189
-
190
189
  reduction(context, tensor, func)
191
190
  end
192
191
 
@@ -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
@@ -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
- def session(evaluator = nil, thread_pool_class: Concurrent::ImmediateExecutor, log_device_placement: false)
133
- session = TensorStream::Session.new(evaluator, thread_pool_class: thread_pool_class, log_device_placement: log_device_placement)
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, preferred_dtype: 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?
@@ -1,5 +1,5 @@
1
1
  module TensorStream
2
- VERSION = '0.9.1'.freeze
2
+ VERSION = '0.9.2'.freeze
3
3
 
4
4
  def self.version
5
5
  VERSION
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.1
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-19 00:00:00.000000000 Z
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.6.10
340
+ rubygems_version: 2.7.7
340
341
  signing_key:
341
342
  specification_version: 4
342
343
  summary: A Pure ruby tensorflow implementation