tensor_stream 0.1.4 → 0.1.5
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/.circleci/config.yml +57 -0
- data/README.md +2 -0
- data/lib/tensor_stream.rb +74 -10
- data/lib/tensor_stream/control_flow.rb +2 -2
- data/lib/tensor_stream/device.rb +8 -0
- data/lib/tensor_stream/evaluator/ruby_evaluator.rb +104 -40
- data/lib/tensor_stream/graph.rb +53 -5
- data/lib/tensor_stream/graph_keys.rb +1 -0
- data/lib/tensor_stream/graph_serializers/graphml.rb +91 -0
- data/lib/tensor_stream/graph_serializers/pbtext.rb +71 -0
- data/lib/tensor_stream/helpers/op_helper.rb +7 -1
- data/lib/tensor_stream/initializer.rb +16 -0
- data/lib/tensor_stream/math_gradients.rb +37 -30
- data/lib/tensor_stream/nn/nn_ops.rb +17 -0
- data/lib/tensor_stream/operation.rb +92 -31
- data/lib/tensor_stream/ops.rb +87 -53
- data/lib/tensor_stream/placeholder.rb +1 -1
- data/lib/tensor_stream/session.rb +26 -4
- data/lib/tensor_stream/tensor.rb +29 -33
- data/lib/tensor_stream/tensor_shape.rb +52 -2
- data/lib/tensor_stream/train/gradient_descent_optimizer.rb +1 -4
- data/lib/tensor_stream/variable.rb +23 -7
- data/lib/tensor_stream/version.rb +1 -1
- data/samples/logistic_regression.rb +76 -0
- data/tensor_stream.gemspec +3 -0
- metadata +50 -2
@@ -1,10 +1,14 @@
|
|
1
1
|
module TensorStream
|
2
2
|
# TensorStream class that defines a session
|
3
3
|
class Session
|
4
|
-
attr_reader :last_session_context
|
4
|
+
attr_reader :last_session_context, :closed, :target
|
5
|
+
attr_accessor :randomizer
|
6
|
+
|
5
7
|
def initialize(evaluator = :ruby_evaluator, thread_pool_class: Concurrent::ImmediateExecutor)
|
6
8
|
@evaluator_class = Object.const_get("TensorStream::Evaluator::#{camelize(evaluator.to_s)}")
|
7
9
|
@thread_pool = thread_pool_class.new
|
10
|
+
@closed = false
|
11
|
+
@randomizer = {}
|
8
12
|
end
|
9
13
|
|
10
14
|
def self.default_session
|
@@ -22,18 +26,32 @@ module TensorStream
|
|
22
26
|
# scan for placeholders and assign value
|
23
27
|
if options[:feed_dict]
|
24
28
|
options[:feed_dict].keys.each do |k|
|
25
|
-
|
29
|
+
if k.is_a?(Placeholder)
|
30
|
+
context[k.name.to_sym] = options[:feed_dict][k]
|
31
|
+
end
|
26
32
|
end
|
27
33
|
end
|
28
34
|
|
29
|
-
evaluator = @evaluator_class.new(self, context.merge!(retain: options[:retain]), thread_pool: @thread_pool)
|
35
|
+
evaluator = @evaluator_class.new(self, context.merge!(retain: options[:retain]), thread_pool: @thread_pool, log_intermediates: options[:log_intermediates])
|
30
36
|
|
31
37
|
execution_context = {}
|
32
|
-
result = args.collect { |e| evaluator.run(e, execution_context) }
|
33
38
|
@last_session_context = context
|
39
|
+
result = args.collect { |e| evaluator.run(e, execution_context) }
|
34
40
|
result.size == 1 ? result.first : result
|
35
41
|
end
|
36
42
|
|
43
|
+
def list_devices
|
44
|
+
[Device.new("cpu")]
|
45
|
+
end
|
46
|
+
|
47
|
+
def close
|
48
|
+
@closed = true
|
49
|
+
end
|
50
|
+
|
51
|
+
def closed?
|
52
|
+
@closed
|
53
|
+
end
|
54
|
+
|
37
55
|
def dump_internal_ops(tensor)
|
38
56
|
dump_ops(tensor, ->(_k, n) { n.is_a?(Tensor) && n.internal? })
|
39
57
|
end
|
@@ -50,6 +68,10 @@ module TensorStream
|
|
50
68
|
end.compact
|
51
69
|
end
|
52
70
|
|
71
|
+
def graph_ml(tensor, filename)
|
72
|
+
TensorStream::Graphml.new(self).serialize(tensor, filename)
|
73
|
+
end
|
74
|
+
|
53
75
|
private
|
54
76
|
|
55
77
|
def camelize(string, uppercase_first_letter = true)
|
data/lib/tensor_stream/tensor.rb
CHANGED
@@ -19,7 +19,7 @@ module TensorStream
|
|
19
19
|
@is_const = options[:const] || false
|
20
20
|
@internal = options[:internal]
|
21
21
|
@graph = options[:graph] || TensorStream.get_default_graph
|
22
|
-
@name = options[:name] || build_name
|
22
|
+
@name = [@graph.get_name_scope, options[:name] || build_name].compact.reject(&:empty?).join('/')
|
23
23
|
@given_name = @name
|
24
24
|
|
25
25
|
if options[:value]
|
@@ -55,7 +55,7 @@ module TensorStream
|
|
55
55
|
end
|
56
56
|
|
57
57
|
def +(other)
|
58
|
-
TensorStream::Operation.new(:add, self,
|
58
|
+
TensorStream::Operation.new(:add, self, TensorStream.convert_to_tensor(other, dtype: data_type))
|
59
59
|
end
|
60
60
|
|
61
61
|
def [](index)
|
@@ -63,19 +63,19 @@ module TensorStream
|
|
63
63
|
end
|
64
64
|
|
65
65
|
def *(other)
|
66
|
-
TensorStream::Operation.new(:mul, self,
|
66
|
+
TensorStream::Operation.new(:mul, self, TensorStream.convert_to_tensor(other, dtype: data_type))
|
67
67
|
end
|
68
68
|
|
69
69
|
def **(other)
|
70
|
-
TensorStream::Operation.new(:pow, self,
|
70
|
+
TensorStream::Operation.new(:pow, self, TensorStream.convert_to_tensor(other, dtype: data_type))
|
71
71
|
end
|
72
72
|
|
73
73
|
def /(other)
|
74
|
-
TensorStream::Operation.new(:div, self,
|
74
|
+
TensorStream::Operation.new(:div, self, TensorStream.convert_to_tensor(other, dtype: data_type))
|
75
75
|
end
|
76
76
|
|
77
77
|
def -(other)
|
78
|
-
TensorStream::Operation.new(:sub, self,
|
78
|
+
TensorStream::Operation.new(:sub, self, TensorStream.convert_to_tensor(other, dtype: data_type))
|
79
79
|
end
|
80
80
|
|
81
81
|
def -@
|
@@ -83,35 +83,40 @@ module TensorStream
|
|
83
83
|
end
|
84
84
|
|
85
85
|
def ==(other)
|
86
|
-
|
86
|
+
_op(:equal, self, other)
|
87
87
|
end
|
88
88
|
|
89
89
|
def <(other)
|
90
|
-
|
90
|
+
_op(:less, self, other)
|
91
91
|
end
|
92
92
|
|
93
93
|
def !=(other)
|
94
|
-
|
94
|
+
_op(:not_equal, self, other)
|
95
95
|
end
|
96
96
|
|
97
97
|
def >(other)
|
98
|
-
|
98
|
+
_op(:greater, self, other)
|
99
99
|
end
|
100
100
|
|
101
101
|
def >=(other)
|
102
|
-
|
102
|
+
_op(:greater_equal, self, other)
|
103
103
|
end
|
104
104
|
|
105
105
|
def <=(other)
|
106
|
-
|
106
|
+
_op(:less_equal, self, other)
|
107
107
|
end
|
108
108
|
|
109
109
|
def and(other)
|
110
|
-
|
110
|
+
_op(:logical_and, self, other)
|
111
111
|
end
|
112
112
|
|
113
113
|
def matmul(other)
|
114
|
-
|
114
|
+
_op(:matmul, self, other)
|
115
|
+
end
|
116
|
+
|
117
|
+
|
118
|
+
def dot(other)
|
119
|
+
_op(:matmul, self, other)
|
115
120
|
end
|
116
121
|
|
117
122
|
def collect(&block)
|
@@ -122,6 +127,10 @@ module TensorStream
|
|
122
127
|
@name
|
123
128
|
end
|
124
129
|
|
130
|
+
def op
|
131
|
+
is_const ? _op(:const, self, nil, name: self.name) : _op(:variable, self, nil, name: self.name)
|
132
|
+
end
|
133
|
+
|
125
134
|
def eval(options = {})
|
126
135
|
Session.default_session.run(self, options)
|
127
136
|
end
|
@@ -149,10 +158,10 @@ module TensorStream
|
|
149
158
|
end
|
150
159
|
|
151
160
|
def first
|
152
|
-
|
161
|
+
_op(:index, self, 0)
|
153
162
|
end
|
154
163
|
|
155
|
-
def to_math(name_only = false, max_depth = 99)
|
164
|
+
def to_math(name_only = false, max_depth = 99, _unused = 0)
|
156
165
|
return @name if max_depth.zero? || name_only || @value.nil?
|
157
166
|
|
158
167
|
if @value.is_a?(Array)
|
@@ -162,8 +171,8 @@ module TensorStream
|
|
162
171
|
end
|
163
172
|
end
|
164
173
|
|
165
|
-
def auto_math(tensor, name_only = false, max_depth = 99)
|
166
|
-
tensor.is_a?(Tensor) ? tensor.to_math(name_only, max_depth) : tensor
|
174
|
+
def auto_math(tensor, name_only = false, max_depth = 99, _cur_depth = 0)
|
175
|
+
tensor.is_a?(Tensor) ? tensor.to_math(name_only, max_depth, _cur_depth) : tensor
|
167
176
|
end
|
168
177
|
|
169
178
|
def self.detect_type(value)
|
@@ -181,6 +190,7 @@ module TensorStream
|
|
181
190
|
end
|
182
191
|
|
183
192
|
def self.cast_dtype(val, dtype)
|
193
|
+
return val if dtype.nil?
|
184
194
|
return val if val.is_a?(Tensor)
|
185
195
|
|
186
196
|
if val.is_a?(Array)
|
@@ -218,7 +228,7 @@ module TensorStream
|
|
218
228
|
end
|
219
229
|
|
220
230
|
def print!(message)
|
221
|
-
|
231
|
+
_op(:print, self, self, message: message)
|
222
232
|
end
|
223
233
|
|
224
234
|
protected
|
@@ -232,10 +242,6 @@ module TensorStream
|
|
232
242
|
add_consumer(consumer)
|
233
243
|
end
|
234
244
|
|
235
|
-
def format_source(trace)
|
236
|
-
trace.reject { |c| c.to_s.include?(File.join('lib', 'tensor_stream')) }.first
|
237
|
-
end
|
238
|
-
|
239
245
|
def hashify_tensor(tensor)
|
240
246
|
if tensor.is_a?(Tensor)
|
241
247
|
tensor.to_h
|
@@ -264,16 +270,6 @@ module TensorStream
|
|
264
270
|
end
|
265
271
|
end
|
266
272
|
|
267
|
-
def auto_wrap(operand)
|
268
|
-
return auto_wrap(operand.call) if operand.is_a?(Proc)
|
269
|
-
|
270
|
-
if !operand.is_a?(Tensor)
|
271
|
-
i_cons(operand, dtype: @data_type || Tensor.detect_type(operand))
|
272
|
-
else
|
273
|
-
operand
|
274
|
-
end
|
275
|
-
end
|
276
|
-
|
277
273
|
def build_name
|
278
274
|
@is_const ? "Const#{graph.get_const_counter}:#{@rank}" : "Variable#{graph.get_var_counter}:#{@rank}"
|
279
275
|
end
|
@@ -3,9 +3,9 @@ module TensorStream
|
|
3
3
|
class TensorShape
|
4
4
|
attr_accessor :rank, :shape
|
5
5
|
|
6
|
-
def initialize(shape, rank)
|
6
|
+
def initialize(shape, rank = nil)
|
7
7
|
@shape = shape
|
8
|
-
@rank = rank
|
8
|
+
@rank = rank.nil? && shape ? shape.size : rank
|
9
9
|
end
|
10
10
|
|
11
11
|
def to_s
|
@@ -22,5 +22,55 @@ module TensorStream
|
|
22
22
|
def ndims
|
23
23
|
shape.size
|
24
24
|
end
|
25
|
+
|
26
|
+
def known?
|
27
|
+
return false if shape.nil?
|
28
|
+
shape.each { |s| return false if s.nil? }
|
29
|
+
|
30
|
+
true
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.infer_shape(shape_a, shape_b)
|
34
|
+
return shape_a if shape_b.nil?
|
35
|
+
return shape_b if shape_a.nil?
|
36
|
+
return shape_a if shape_a == shape_b
|
37
|
+
return shape_b if shape_b.size > shape_a.size
|
38
|
+
return shape_a if shape_a.size > shape_b.size
|
39
|
+
|
40
|
+
reversed_a = shape_a.reverse
|
41
|
+
reversed_b = shape_b.reverse
|
42
|
+
|
43
|
+
reversed_a.each_with_index.collect do |s, index|
|
44
|
+
next s if index >= reversed_b.size
|
45
|
+
next nil if s.nil? || reversed_b[index].nil?
|
46
|
+
next nil if s.is_a?(Tensor) || reversed_b[index].is_a?(Tensor)
|
47
|
+
next reversed_b[index] if reversed_b[index] > s
|
48
|
+
s
|
49
|
+
end.reverse
|
50
|
+
end
|
51
|
+
|
52
|
+
def self.reshape(arr, new_shape)
|
53
|
+
return arr if new_shape.empty?
|
54
|
+
|
55
|
+
s = new_shape.shift
|
56
|
+
|
57
|
+
if new_shape.size.zero?
|
58
|
+
raise "reshape dimen mismatch #{arr.size} != #{s}" if arr.size != s
|
59
|
+
return arr
|
60
|
+
end
|
61
|
+
|
62
|
+
dim = (arr.size / s)
|
63
|
+
arr.each_slice(dim).collect do |slice|
|
64
|
+
reshape(slice, new_shape.dup)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def self.fix_inferred_elements(shape, total_size)
|
69
|
+
return shape if shape.empty?
|
70
|
+
|
71
|
+
current_size = shape.inject(1) { |product, n| n > 0 ? product * n : product }
|
72
|
+
inferred_size = total_size / current_size
|
73
|
+
shape.map { |s| s == -1 ? inferred_size : s }
|
74
|
+
end
|
25
75
|
end
|
26
76
|
end
|
@@ -9,10 +9,7 @@ module TensorStream
|
|
9
9
|
end
|
10
10
|
|
11
11
|
def minimize(cost)
|
12
|
-
trainable_vars = TensorStream
|
13
|
-
.get_collection(GraphKeys::GLOBAL_VARIABLES)
|
14
|
-
.select(&:trainable)
|
15
|
-
|
12
|
+
trainable_vars = TensorStream.trainable_variables
|
16
13
|
derivatives = TensorStream.gradients(cost, trainable_vars)
|
17
14
|
trainable_vars.each_with_index.collect do |var, index|
|
18
15
|
var.assign_sub(derivatives[index] * @learning_rate)
|
@@ -7,19 +7,27 @@ module TensorStream
|
|
7
7
|
|
8
8
|
@data_type = data_type
|
9
9
|
@rank = rank
|
10
|
-
@shape = TensorShape.new(shape, rank)
|
11
10
|
@value = nil
|
12
11
|
@source = format_source(caller_locations)
|
13
|
-
|
14
|
-
@
|
15
|
-
@initalizer_tensor
|
12
|
+
@name = [TensorStream.get_variable_scope, options[:name] || build_name].compact.reject(&:empty?).join('/')
|
13
|
+
@initalizer_tensor = options[:initializer] ? options[:initializer] : _variable_scope.initializer || TensorStream.glorot_uniform_initializer
|
14
|
+
if shape.nil? && @initalizer_tensor && @initalizer_tensor.shape
|
15
|
+
shape = @initalizer_tensor.shape.shape
|
16
|
+
end
|
17
|
+
@shape = TensorShape.new(shape, rank)
|
16
18
|
@trainable = options.fetch(:trainable, true)
|
17
19
|
@graph.add_variable(self, options)
|
18
20
|
end
|
19
21
|
|
22
|
+
def trainable?
|
23
|
+
@trainable
|
24
|
+
end
|
25
|
+
|
20
26
|
def initializer
|
21
|
-
|
22
|
-
|
27
|
+
init_op = @initalizer_tensor.op
|
28
|
+
init_op.shape = @shape || init_op.shape
|
29
|
+
init_op.data_type = @data_type || init_op.data_type
|
30
|
+
assign(init_op)
|
23
31
|
end
|
24
32
|
|
25
33
|
def assign(value)
|
@@ -34,7 +42,7 @@ module TensorStream
|
|
34
42
|
Operation.new(:assign_add, self, value)
|
35
43
|
end
|
36
44
|
|
37
|
-
def to_math(_tensor, _name_only = false, _max_depth = 99)
|
45
|
+
def to_math(_tensor, _name_only = false, _max_depth = 99, _unused = 0)
|
38
46
|
@name
|
39
47
|
end
|
40
48
|
|
@@ -49,5 +57,13 @@ module TensorStream
|
|
49
57
|
def self.global_variables_initializer
|
50
58
|
variables_initializer(TensorStream::GraphKeys::GLOBAL_VARIABLES)
|
51
59
|
end
|
60
|
+
|
61
|
+
private
|
62
|
+
|
63
|
+
def _variable_scope
|
64
|
+
return OpenStruct.new(name: '', reuse: false, initializer: nil) if Thread.current[:tensor_stream_variable_scope].nil? || Thread.current[:tensor_stream_variable_scope].empty?
|
65
|
+
scope = Thread.current[:tensor_stream_variable_scope].last
|
66
|
+
scope
|
67
|
+
end
|
52
68
|
end
|
53
69
|
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
require "bundler/setup"
|
2
|
+
require 'tensor_stream'
|
3
|
+
require 'pry-byebug'
|
4
|
+
|
5
|
+
tf = TensorStream
|
6
|
+
|
7
|
+
rows = File.readlines(File.join("samples","iris.data")).map {|l| l.chomp.split(',') }
|
8
|
+
|
9
|
+
iris = rows[0...100].shuffle!
|
10
|
+
|
11
|
+
transformed_data = iris.collect do |row|
|
12
|
+
row[0, 4].map(&:to_f)
|
13
|
+
end
|
14
|
+
|
15
|
+
columns = (0..3).map do |i|
|
16
|
+
transformed_data.map { |row| row[i] }
|
17
|
+
end
|
18
|
+
|
19
|
+
# Normalize data values before feeding into network
|
20
|
+
normalize = -> (val, high, low) { (val - low) / (high - low) } # maps input to float between 0 and 1
|
21
|
+
|
22
|
+
transformed_data.map! do |row|
|
23
|
+
row.map.with_index do |val, j|
|
24
|
+
max, min = columns[j].max, columns[j].min
|
25
|
+
normalize.(val, max, min)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
|
30
|
+
srand(5)
|
31
|
+
seed = 5
|
32
|
+
tf.set_random_seed(seed)
|
33
|
+
|
34
|
+
train_x = transformed_data[0..50].map { |x| x[0..3].map(&:to_f) }
|
35
|
+
train_y = iris[0..50].map { |x| x[4] == 'Iris-setosa' ? 0.0 : 1.0 }
|
36
|
+
|
37
|
+
test_x = transformed_data[51..100].map { |x| x[0..3].map(&:to_f) }
|
38
|
+
test_y = iris[51..100].map { |x| x[4] == 'Iris-setosa' ? 0.0 : 1.0 }
|
39
|
+
|
40
|
+
|
41
|
+
A = tf.variable(tf.random_normal([4, 1]))
|
42
|
+
b = tf.variable(tf.random_normal([1, 1]))
|
43
|
+
|
44
|
+
init = tf.global_variables_initializer
|
45
|
+
sess = tf.session
|
46
|
+
sess.run(init)
|
47
|
+
|
48
|
+
data = tf.placeholder(:float32, shape: [nil, 4])
|
49
|
+
target = tf.placeholder(:float32, shape: [nil, 1])
|
50
|
+
|
51
|
+
mod = data.dot(A) + b
|
52
|
+
|
53
|
+
loss = tf.reduce_mean(tf.nn.sigmoid_cross_entropy_with_logits(logits: mod, labels: target))
|
54
|
+
|
55
|
+
learning_rate = 0.003
|
56
|
+
batch_size = 30
|
57
|
+
iter_num = 1500
|
58
|
+
|
59
|
+
optimizer = TensorStream::Train::GradientDescentOptimizer.new(learning_rate)
|
60
|
+
goal = optimizer.minimize(loss)
|
61
|
+
prediction = tf.round(tf.sigmoid(mod))
|
62
|
+
# Bool into float32 type
|
63
|
+
correct = tf.cast(tf.equal(prediction, target), dtype: :float32)
|
64
|
+
# Average
|
65
|
+
accuracy = tf.reduce_mean(correct)
|
66
|
+
|
67
|
+
loss_trace = []
|
68
|
+
train_acc = []
|
69
|
+
test_acc = []
|
70
|
+
|
71
|
+
(0..iter_num).each do |epoch|
|
72
|
+
batch_train_X = train_x
|
73
|
+
batch_train_y = [train_y].transpose
|
74
|
+
sess.run(goal, feed_dict: { data => batch_train_X, target => batch_train_y })
|
75
|
+
temp_loss = sess.run(loss, feed_dict: {data => batch_train_X, target => batch_train_y})
|
76
|
+
end
|