tensor_stream 0.1.4 → 0.1.5
Sign up to get free protection for your applications and to get access to all the features.
- 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
|