tensor_stream 0.8.0 → 0.8.1
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 +4 -0
- data/README.md +18 -5
- data/lib/tensor_stream/evaluator/opencl/kernels/apply_adam.cl +23 -0
- data/lib/tensor_stream/evaluator/opencl/opencl_evaluator.rb +51 -4
- data/lib/tensor_stream/evaluator/ruby/math_ops.rb +144 -0
- data/lib/tensor_stream/evaluator/ruby/nn_ops.rb +99 -0
- data/lib/tensor_stream/evaluator/ruby_evaluator.rb +6 -253
- data/lib/tensor_stream/ops.rb +2 -1
- data/lib/tensor_stream/session.rb +17 -8
- data/lib/tensor_stream/train/adam_optimizer.rb +87 -0
- data/lib/tensor_stream/train/gradient_descent_optimizer.rb +2 -1
- data/lib/tensor_stream/train/optimizer.rb +25 -2
- data/lib/tensor_stream/train/slot_creator.rb +1 -1
- data/lib/tensor_stream/trainer.rb +1 -0
- data/lib/tensor_stream/utils.rb +25 -4
- data/lib/tensor_stream/variable.rb +1 -1
- data/lib/tensor_stream/variable_scope.rb +7 -1
- data/lib/tensor_stream/version.rb +1 -1
- data/samples/iris.rb +9 -6
- data/samples/linear_regression.rb +6 -4
- data/samples/nearest_neighbor.rb +2 -2
- data/{test_samples → samples}/raw_neural_net_sample.rb +17 -20
- metadata +7 -8
- data/test_samples/error.graphml +0 -120
- data/test_samples/gradient_sample.graphml +0 -1255
- data/test_samples/neural_network_raw.py +0 -101
- data/test_samples/test.py +0 -46
- data/test_samples/test2.py +0 -87
data/lib/tensor_stream/ops.rb
CHANGED
@@ -71,7 +71,7 @@ module TensorStream
|
|
71
71
|
value = delegate_to_evaluator(e, context, {})
|
72
72
|
recursive_eval(value)
|
73
73
|
end
|
74
|
-
|
74
|
+
args.size == 1 ? result.first : result
|
75
75
|
end
|
76
76
|
|
77
77
|
def list_devices
|
@@ -111,17 +111,26 @@ module TensorStream
|
|
111
111
|
end
|
112
112
|
|
113
113
|
def delegate_to_evaluator(tensor_arr, session_context, context)
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
114
|
+
if tensor_arr.is_a?(Array)
|
115
|
+
tensor_arr.collect do |tensor|
|
116
|
+
if tensor.is_a?(Array)
|
117
|
+
delegate_to_evaluator(tensor, session_context, context)
|
118
|
+
else
|
119
|
+
run_with_session_context(tensor, session_context, context)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
else
|
123
|
+
run_with_session_context(tensor_arr, session_context, context)
|
119
124
|
end
|
120
|
-
result.size == 1 ? result.first : result
|
121
125
|
end
|
122
126
|
|
123
127
|
protected
|
124
128
|
|
129
|
+
def run_with_session_context(tensor, session_context, context)
|
130
|
+
session_context[:_cache][:placement][tensor.name] = assign_evaluator(tensor) if session_context[:_cache][:placement][tensor.name].nil?
|
131
|
+
session_context[:_cache][:placement][tensor.name][1].run_with_buffer(tensor, session_context, context)
|
132
|
+
end
|
133
|
+
|
125
134
|
def recursive_eval(value, depth = 2)
|
126
135
|
if value.is_a?(Array) && depth > 0
|
127
136
|
value.collect { |v| recursive_eval(v, depth - 1) }
|
@@ -151,7 +160,7 @@ module TensorStream
|
|
151
160
|
def prepare_evaluators(tensor_arr, context)
|
152
161
|
context[:_cache][:placement] ||= {}
|
153
162
|
|
154
|
-
tensor_arr = tensor_arr.is_a?(Array) ? tensor_arr : [tensor_arr]
|
163
|
+
tensor_arr = tensor_arr.is_a?(Array) ? tensor_arr.flatten : [tensor_arr]
|
155
164
|
tensor_arr.each do |tensor|
|
156
165
|
next if context[:_cache][:placement][tensor.name]
|
157
166
|
|
@@ -0,0 +1,87 @@
|
|
1
|
+
module TensorStream
|
2
|
+
module Train
|
3
|
+
# High Level implementation of the gradient descent algorithm
|
4
|
+
class AdamOptimizer < Optimizer
|
5
|
+
include TensorStream::OpHelper
|
6
|
+
|
7
|
+
attr_accessor :learning_rate
|
8
|
+
|
9
|
+
def initialize(learning_rate = 0.001, beta1=0.9, beta2=0.999, epsilon = 1e-8,
|
10
|
+
use_locking: false, name: "Adam")
|
11
|
+
@learning_rate = learning_rate
|
12
|
+
@beta1 = beta1
|
13
|
+
@beta2 = beta2
|
14
|
+
@epsilon = epsilon
|
15
|
+
|
16
|
+
# Tensor versions of the constructor arguments, created in _prepare().
|
17
|
+
@lr_t = nil
|
18
|
+
@beta1_t = nil
|
19
|
+
@beta2_t = nil
|
20
|
+
@epsilon_t = nil
|
21
|
+
|
22
|
+
# Created in SparseApply if needed.
|
23
|
+
@updated_lr = nil
|
24
|
+
super(name: name, use_locking: use_locking)
|
25
|
+
end
|
26
|
+
|
27
|
+
protected
|
28
|
+
|
29
|
+
def get_beta_accumulators
|
30
|
+
graph = TensorStream.get_default_graph
|
31
|
+
[ get_non_slot_variable("beta1_power", graph: graph),
|
32
|
+
get_non_slot_variable("beta2_power", graph: graph)]
|
33
|
+
end
|
34
|
+
|
35
|
+
def prepare
|
36
|
+
lr = call_if_callable(@learning_rate)
|
37
|
+
beta1 = call_if_callable(@beta1)
|
38
|
+
beta2 = call_if_callable(@beta2)
|
39
|
+
epsilon = call_if_callable(@epsilon)
|
40
|
+
|
41
|
+
@lr_t = TensorStream.convert_to_tensor(lr, name: "learning_rate")
|
42
|
+
@beta1_t = TensorStream.convert_to_tensor(beta1, name: "beta1")
|
43
|
+
@beta2_t = TensorStream.convert_to_tensor(beta2, name: "beta2")
|
44
|
+
@epsilon_t = TensorStream.convert_to_tensor(epsilon, name: "epsilon")
|
45
|
+
end
|
46
|
+
|
47
|
+
def create_slots(var_list)
|
48
|
+
first_var = var_list.min_by(&:name)
|
49
|
+
create_non_slot_variable(@beta1, "beta1_power", first_var)
|
50
|
+
create_non_slot_variable(@beta2, "beta2_power", first_var)
|
51
|
+
|
52
|
+
# Create slots for the first and second moments.
|
53
|
+
var_list.each do |v|
|
54
|
+
zeros_slot(v, "m", @name)
|
55
|
+
zeros_slot(v, "v", @name)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def apply_dense(grad, var)
|
60
|
+
m = get_slot(var, "m")
|
61
|
+
v = get_slot(var, "v")
|
62
|
+
beta1_power, beta2_power = get_beta_accumulators
|
63
|
+
_op(:apply_adam,
|
64
|
+
var, m, v,
|
65
|
+
TensorStream.cast(beta1_power, var.data_type),
|
66
|
+
TensorStream.cast(beta2_power, var.data_type),
|
67
|
+
TensorStream.cast(@lr_t, var.data_type),
|
68
|
+
TensorStream.cast(@beta1_t, var.data_type),
|
69
|
+
TensorStream.cast(@beta2_t, var.data_type),
|
70
|
+
TensorStream.cast(@epsilon_t, var.data_type),
|
71
|
+
grad, use_locking: @use_locking)
|
72
|
+
end
|
73
|
+
|
74
|
+
def finish(update_ops, name_scope)
|
75
|
+
TensorStream.control_dependencies(update_ops) do
|
76
|
+
beta1_power, beta2_power = get_beta_accumulators
|
77
|
+
update_beta1 = nil, update_beta2 = nil
|
78
|
+
TensorStream.colocate_with(beta1_power) do
|
79
|
+
update_beta1 = beta1_power.assign(beta1_power * @beta1_t, use_locking: @use_locking)
|
80
|
+
update_beta2 = beta2_power.assign(beta2_power * @beta2_t, use_locking: @use_locking)
|
81
|
+
end
|
82
|
+
TensorStream.group(update_ops + [update_beta1, update_beta2], name: name_scope)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
@@ -6,8 +6,9 @@ module TensorStream
|
|
6
6
|
|
7
7
|
attr_accessor :learning_rate
|
8
8
|
|
9
|
-
def initialize(learning_rate,
|
9
|
+
def initialize(learning_rate, use_locking: false, name: "GradientDescent")
|
10
10
|
@learning_rate = learning_rate
|
11
|
+
super(name: name, use_locking: use_locking)
|
11
12
|
end
|
12
13
|
|
13
14
|
protected
|
@@ -12,6 +12,7 @@ module TensorStream
|
|
12
12
|
@use_locking = use_locking
|
13
13
|
raise TensorStream::ValueError, "Must specify the optimizer name" unless @name
|
14
14
|
@slots = {}
|
15
|
+
@non_slots = {}
|
15
16
|
end
|
16
17
|
|
17
18
|
def minimize(loss, var_list: nil, grad_loss: nil, global_step: nil, name: nil)
|
@@ -86,7 +87,7 @@ module TensorStream
|
|
86
87
|
# no implementation
|
87
88
|
end
|
88
89
|
|
89
|
-
def apply_dense(
|
90
|
+
def apply_dense(_grad, _var)
|
90
91
|
raise TensorStream::NotImplementedError, "not implemented"
|
91
92
|
end
|
92
93
|
|
@@ -95,7 +96,7 @@ module TensorStream
|
|
95
96
|
#
|
96
97
|
# Args:
|
97
98
|
# var: Variable - A Variable object
|
98
|
-
# slot_name: string - Name
|
99
|
+
# slot_name: string - Name for the slot
|
99
100
|
# op_name: string - Name to use when scoping the Variable that needs to be created
|
100
101
|
def zeros_slot(var, slot_name, op_name)
|
101
102
|
named_slots = slot_dict(slot_name)
|
@@ -124,6 +125,28 @@ module TensorStream
|
|
124
125
|
def var_key(var)
|
125
126
|
[var.op.graph, var.op.name]
|
126
127
|
end
|
128
|
+
|
129
|
+
def get_non_slot_variable(name, graph: nil)
|
130
|
+
non_slot = @non_slots.fetch([name, graph], nil)
|
131
|
+
non_slot
|
132
|
+
end
|
133
|
+
|
134
|
+
def call_if_callable(param)
|
135
|
+
param.kind_of?(Proc) ? param.call : param
|
136
|
+
end
|
137
|
+
|
138
|
+
|
139
|
+
def create_non_slot_variable(initial_value, name, colocate_with)
|
140
|
+
graph = colocate_with.graph
|
141
|
+
|
142
|
+
key = [name, graph]
|
143
|
+
v = @non_slots.fetch(key, nil)
|
144
|
+
if v.nil?
|
145
|
+
v = TensorStream.variable(initial_value, name: name, trainable: false)
|
146
|
+
@non_slots[key] = v
|
147
|
+
end
|
148
|
+
v
|
149
|
+
end
|
127
150
|
end
|
128
151
|
end
|
129
152
|
end
|
@@ -23,7 +23,7 @@ module TensorStream
|
|
23
23
|
#
|
24
24
|
# Returns: A `Variable` object
|
25
25
|
def create_slot(primary, val, name, colocate_with_primary: true)
|
26
|
-
TensorStream.variable_scope(primary.op.name + "/" + name) do
|
26
|
+
TensorStream.variable_scope(nil, primary.op.name + "/" + name) do
|
27
27
|
if colocate_with_primary
|
28
28
|
TensorStream.colocate_with(primary) do
|
29
29
|
return create_slot_var(primary, val, "")
|
@@ -2,6 +2,7 @@ require 'tensor_stream/train/slot_creator'
|
|
2
2
|
require 'tensor_stream/train/optimizer'
|
3
3
|
require 'tensor_stream/train/gradient_descent_optimizer'
|
4
4
|
require 'tensor_stream/train/momentum_optimizer'
|
5
|
+
require 'tensor_stream/train/adam_optimizer'
|
5
6
|
require 'tensor_stream/train/saver'
|
6
7
|
|
7
8
|
module TensorStream
|
data/lib/tensor_stream/utils.rb
CHANGED
@@ -66,9 +66,25 @@ module TensorStream
|
|
66
66
|
tensor
|
67
67
|
end
|
68
68
|
|
69
|
-
|
70
|
-
|
69
|
+
##
|
70
|
+
# Defines a variable context manager
|
71
|
+
def variable_scope(scope = nil, default_name = nil, reuse: nil, initializer: nil)
|
72
|
+
Thread.current[:tensor_stream_variable_scope] ||= [ VariableScope.new ]
|
73
|
+
|
74
|
+
# uniquenifier
|
75
|
+
if scope.nil? && default_name
|
76
|
+
same_names = get_variable_scope.used_names.select { |s| s.start_with?(default_name) }
|
77
|
+
new_name = default_name
|
78
|
+
index = 1
|
79
|
+
while same_names.include?(new_name)
|
80
|
+
new_name = "#{default_name}_#{index}"
|
81
|
+
index += 1
|
82
|
+
end
|
83
|
+
scope = new_name
|
84
|
+
end
|
85
|
+
|
71
86
|
variable_scope = VariableScope.new(name: scope, reuse: reuse, initializer: initializer)
|
87
|
+
get_variable_scope.register_name(scope || "")
|
72
88
|
Thread.current[:tensor_stream_variable_scope] << variable_scope
|
73
89
|
scope_name = __v_scope_name
|
74
90
|
if block_given?
|
@@ -100,8 +116,13 @@ module TensorStream
|
|
100
116
|
end
|
101
117
|
|
102
118
|
def get_variable_scope
|
103
|
-
|
104
|
-
|
119
|
+
if !Thread.current[:tensor_stream_variable_scope]
|
120
|
+
variable_scope = VariableScope.new
|
121
|
+
Thread.current[:tensor_stream_variable_scope] = [variable_scope]
|
122
|
+
return variable_scope
|
123
|
+
end
|
124
|
+
|
125
|
+
Thread.current[:tensor_stream_variable_scope].last
|
105
126
|
end
|
106
127
|
|
107
128
|
def __v_scope_name
|
@@ -1,15 +1,21 @@
|
|
1
1
|
module TensorStream
|
2
2
|
class VariableScope
|
3
3
|
attr_accessor :name, :reuse, :initializer
|
4
|
+
attr_reader :used_names
|
4
5
|
|
5
|
-
def initialize(name:
|
6
|
+
def initialize(name: nil, reuse: nil, initializer: nil)
|
6
7
|
@name = name
|
7
8
|
@reuse = reuse
|
8
9
|
@initializer = initializer
|
10
|
+
@used_names = []
|
9
11
|
end
|
10
12
|
|
11
13
|
def get_variable(name, dtype: nil, shape: nil, initializer: nil, trainable: true, collections: nil, validate_shape: false)
|
12
14
|
TensorStream::Variable.new(dtype || :float32, nil, shape, self, collections: collections, name: name, initializer: initializer, trainable: trainable)
|
13
15
|
end
|
16
|
+
|
17
|
+
def register_name(name)
|
18
|
+
@used_names << name unless @used_names.include?(name)
|
19
|
+
end
|
14
20
|
end
|
15
21
|
end
|
data/samples/iris.rb
CHANGED
@@ -1,6 +1,5 @@
|
|
1
|
-
require "bundler/setup"
|
2
1
|
require 'tensor_stream'
|
3
|
-
|
2
|
+
require 'tensor_stream/evaluator/opencl/opencl_evaluator'
|
4
3
|
|
5
4
|
# This neural network will predict the species of an iris based on sepal and petal size
|
6
5
|
# Dataset: http://en.wikipedia.org/wiki/Iris_flower_data_set
|
@@ -66,8 +65,8 @@ end
|
|
66
65
|
x_size = x_train[0].size
|
67
66
|
y_size = y_train[0].size
|
68
67
|
h_size = 256
|
69
|
-
X = tf.placeholder(:
|
70
|
-
y = tf.placeholder(:
|
68
|
+
X = tf.placeholder(:float32, shape: [nil, x_size])
|
69
|
+
y = tf.placeholder(:float32, shape: [nil, y_size])
|
71
70
|
|
72
71
|
# Weight initializations
|
73
72
|
w_1 = init_weights([x_size, h_size])
|
@@ -79,13 +78,17 @@ predict = tf.argmax(yhat, 1)
|
|
79
78
|
|
80
79
|
# Backward propagation
|
81
80
|
cost = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(labels: y, logits: yhat))
|
82
|
-
|
81
|
+
|
82
|
+
updates = TensorStream::Train::GradientDescentOptimizer.new(0.01).minimize(cost)
|
83
|
+
# updates = TensorStream::Train::MomentumOptimizer.new(0.01, 0.5, use_nesterov: true).minimize(cost)
|
84
|
+
# updates = TensorStream::Train::AdamOptimizer.new.minimize(cost)
|
83
85
|
|
84
86
|
# Run SGD
|
85
87
|
sess = tf.session
|
86
88
|
init = tf.global_variables_initializer
|
87
89
|
sess.run(init)
|
88
|
-
|
90
|
+
loss = sess.run(cost, feed_dict: { X => x_test, y => y_test })
|
91
|
+
puts "loss test data set #{loss}"
|
89
92
|
loss = sess.run(cost, feed_dict: { X => x_train, y => y_train })
|
90
93
|
puts "Testing the untrained network..."
|
91
94
|
puts loss
|
@@ -1,8 +1,8 @@
|
|
1
|
-
|
1
|
+
# Linear Regression sample, using SGD and auto-differentiation
|
2
|
+
|
2
3
|
require 'tensor_stream'
|
3
|
-
require 'benchmark'
|
4
4
|
|
5
|
-
tf = TensorStream
|
5
|
+
tf = TensorStream # use tf to make it look like TensorFlow
|
6
6
|
|
7
7
|
learning_rate = 0.01
|
8
8
|
momentum = 0.5
|
@@ -29,7 +29,9 @@ pred = X * W + b
|
|
29
29
|
# Mean squared error
|
30
30
|
cost = ((pred - Y) ** 2).reduce(:+) / ( 2 * n_samples)
|
31
31
|
|
32
|
-
#
|
32
|
+
# Other possible Optimizers
|
33
|
+
# optimizer = TensorStream::Train::MomentumOptimizer.new(learning_rate, momentum, use_nesterov: true).minimize(cost)
|
34
|
+
# optimizer = TensorStream::Train::AdamOptimizer.new(learning_rate).minimize(cost)
|
33
35
|
optimizer = TensorStream::Train::GradientDescentOptimizer.new(learning_rate).minimize(cost)
|
34
36
|
|
35
37
|
# Initialize the variables (i.e. assign their default value)
|
data/samples/nearest_neighbor.rb
CHANGED
@@ -6,9 +6,9 @@ This example is using the MNIST database of handwritten digits
|
|
6
6
|
Author: Aymeric Damien
|
7
7
|
Project: https://github.com/aymericdamien/TensorFlow-Examples/
|
8
8
|
|
9
|
-
Make sure to install the mnist-learn gem
|
9
|
+
Make sure to install the mnist-learn gem !!
|
10
10
|
'''
|
11
|
-
|
11
|
+
|
12
12
|
require 'tensor_stream'
|
13
13
|
require 'mnist-learn'
|
14
14
|
require 'tensor_stream/evaluator/opencl/opencl_evaluator'
|
@@ -9,12 +9,12 @@ Links:
|
|
9
9
|
|
10
10
|
Author: Aymeric Damien
|
11
11
|
Project: https://github.com/aymericdamien/TensorFlow-Examples/
|
12
|
+
|
13
|
+
The mnist-learn gem is required as well as an OpenCL compatible device with drivers correctly installed
|
12
14
|
"""
|
13
|
-
require "bundler/setup"
|
14
15
|
require 'tensor_stream'
|
15
16
|
require 'mnist-learn'
|
16
17
|
require 'tensor_stream/evaluator/opencl/opencl_evaluator'
|
17
|
-
require 'pry-byebug'
|
18
18
|
|
19
19
|
tf = TensorStream
|
20
20
|
# Import MNIST data
|
@@ -23,8 +23,9 @@ mnist = Mnist.read_data_sets('/tmp/data', one_hot: true)
|
|
23
23
|
puts "downloading finished"
|
24
24
|
|
25
25
|
# Parameters
|
26
|
-
learning_rate = 0.
|
27
|
-
|
26
|
+
learning_rate = 0.001
|
27
|
+
momentum = 0.01
|
28
|
+
num_steps = 100
|
28
29
|
batch_size = 128
|
29
30
|
display_step = 5
|
30
31
|
|
@@ -71,7 +72,7 @@ prediction = tf.nn.softmax(logits)
|
|
71
72
|
loss_op = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits_v2(
|
72
73
|
logits: logits, labels: Y))
|
73
74
|
|
74
|
-
optimizer = TensorStream::Train::
|
75
|
+
optimizer = TensorStream::Train::MomentumOptimizer.new(learning_rate, momentum, use_nesterov: true)
|
75
76
|
train_op = optimizer.minimize(loss_op)
|
76
77
|
|
77
78
|
# Evaluate model
|
@@ -88,28 +89,24 @@ tf.session do |sess|
|
|
88
89
|
# Run the initializer
|
89
90
|
sess.run(init)
|
90
91
|
|
91
|
-
|
92
|
-
|
93
|
-
|
92
|
+
print("Testing Accuracy:", \
|
93
|
+
sess.run(accuracy, feed_dict: { X => mnist.test.images,
|
94
|
+
Y => mnist.test.labels}))
|
94
95
|
|
95
96
|
(1..num_steps+1).each do |step|
|
96
97
|
batch_x, batch_y = mnist.train.next_batch(batch_size)
|
97
98
|
# Run optimization op (backprop)
|
98
|
-
puts "."
|
99
99
|
sess.run(train_op, feed_dict: { X => batch_x, Y => batch_y })
|
100
100
|
if step % display_step == 0 || step == 1
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
101
|
+
# Calculate batch loss and accuracy
|
102
|
+
loss, acc = sess.run([loss_op, accuracy], feed_dict: { X => batch_x, Y => batch_y})
|
103
|
+
print("\nStep " + step.to_s + ", Minibatch Loss= " + \
|
104
|
+
loss.to_s + ", Training Accuracy= " + \
|
105
|
+
acc.to_s)
|
106
106
|
end
|
107
107
|
end
|
108
|
-
|
109
|
-
print("
|
110
|
-
|
111
|
-
# Calculate accuracy for MNIST test images
|
112
|
-
print("Testing Accuracy:", \
|
113
|
-
sess.run(accuracy, feed_dict: { X => mnist.test.images,
|
108
|
+
print("\nOptimization Finished!")
|
109
|
+
print("\nTesting Accuracy after optimization:", \
|
110
|
+
sess.run(accuracy, feed_dict: { X => mnist.test.images,
|
114
111
|
Y => mnist.test.labels}))
|
115
112
|
end
|