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
data/lib/tensor_stream/graph.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
module TensorStream
|
2
2
|
# A class that defines a TensorStream graph
|
3
3
|
class Graph
|
4
|
-
attr_accessor :nodes, :collections, :eager_execution
|
4
|
+
attr_accessor :nodes, :collections, :eager_execution, :random_seed
|
5
5
|
|
6
6
|
def initialize
|
7
7
|
@eager_execution = false
|
@@ -16,13 +16,31 @@ module TensorStream
|
|
16
16
|
@const_counter = 0
|
17
17
|
@var_counter = 0
|
18
18
|
@op_counter = 0
|
19
|
-
|
19
|
+
@random_seed = nil
|
20
20
|
@nodes = {}
|
21
21
|
@collections = {
|
22
22
|
:"#{GraphKeys::GLOBAL_VARIABLES}" => []
|
23
23
|
}
|
24
24
|
end
|
25
25
|
|
26
|
+
def as_default
|
27
|
+
Thread.current[:tensor_stream_current_graph] = self
|
28
|
+
yield(self) if block_given?
|
29
|
+
self
|
30
|
+
end
|
31
|
+
|
32
|
+
def name_scope(name = nil)
|
33
|
+
Thread.current["ts_graph_#{object_id}"] ||= {}
|
34
|
+
Thread.current["ts_graph_#{object_id}"][:current_scope] ||= []
|
35
|
+
Thread.current["ts_graph_#{object_id}"][:current_scope] << name
|
36
|
+
|
37
|
+
begin
|
38
|
+
yield get_name_scope if block_given?
|
39
|
+
ensure
|
40
|
+
Thread.current["ts_graph_#{object_id}"][:current_scope].pop
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
26
44
|
def self.get_default_graph
|
27
45
|
Thread.current[:tensor_stream_current_graph] || create_default
|
28
46
|
end
|
@@ -42,7 +60,13 @@ module TensorStream
|
|
42
60
|
|
43
61
|
def add_node(node)
|
44
62
|
raise 'Placeholder cannot be used when eager_execution is enabled' if @eager_execution && node.is_a?(Placeholder)
|
45
|
-
|
63
|
+
|
64
|
+
node.name = if @nodes[node.name]
|
65
|
+
uniqunify(node.name)
|
66
|
+
else
|
67
|
+
node.name
|
68
|
+
end
|
69
|
+
|
46
70
|
@nodes[node.name] = node
|
47
71
|
node.send(:propagate_consumer, node)
|
48
72
|
node.value = node.eval if @eager_execution
|
@@ -62,10 +86,21 @@ module TensorStream
|
|
62
86
|
end
|
63
87
|
|
64
88
|
def add_variable(node, options = {})
|
65
|
-
|
89
|
+
scope = _variable_scope
|
66
90
|
|
67
|
-
|
91
|
+
raise "duplicate variable detected #{node.name} and reuse=false in current scope" if @nodes[node.name] && !scope.reuse
|
68
92
|
|
93
|
+
return @nodes[node.name] if @nodes[node.name]
|
94
|
+
|
95
|
+
raise "shape is not declared for #{node.name}" if node.shape.nil?
|
96
|
+
|
97
|
+
if !options[:collections].nil? && !options[:collections].empty?
|
98
|
+
options[:collections] = [options[:collections]] unless options[:collections].is_a?(Array)
|
99
|
+
options[:collections].each { |coll| add_to_collection(coll, node) }
|
100
|
+
end
|
101
|
+
|
102
|
+
add_to_collection(GraphKeys::GLOBAL_VARIABLES, node)
|
103
|
+
add_to_collection(GraphKeys::TRAINABLE_VARIABLES, node) if node.trainable?
|
69
104
|
add_node(node)
|
70
105
|
end
|
71
106
|
|
@@ -120,8 +155,21 @@ module TensorStream
|
|
120
155
|
name
|
121
156
|
end
|
122
157
|
|
158
|
+
def get_name_scope
|
159
|
+
graph_thread_storage = Thread.current["ts_graph_#{object_id}"]
|
160
|
+
return nil if graph_thread_storage.nil?
|
161
|
+
|
162
|
+
graph_thread_storage[:current_scope].join('/')
|
163
|
+
end
|
164
|
+
|
123
165
|
protected
|
124
166
|
|
167
|
+
def _variable_scope
|
168
|
+
return OpenStruct.new(name: '', reuse: false, initializer: nil) if Thread.current[:tensor_stream_variable_scope].nil? || Thread.current[:tensor_stream_variable_scope].empty?
|
169
|
+
scope = Thread.current[:tensor_stream_variable_scope].last
|
170
|
+
scope
|
171
|
+
end
|
172
|
+
|
125
173
|
def uniqunify(name)
|
126
174
|
counter = 0
|
127
175
|
new_name = name
|
@@ -0,0 +1,91 @@
|
|
1
|
+
module TensorStream
|
2
|
+
class Graphml
|
3
|
+
def initialize
|
4
|
+
end
|
5
|
+
|
6
|
+
def serialize(session, tensor, filename)
|
7
|
+
@session = session
|
8
|
+
@last_session_context = session.last_session_context
|
9
|
+
|
10
|
+
arr_buf = []
|
11
|
+
arr_buf << '<?xml version="1.0" encoding="UTF-8"?>'
|
12
|
+
arr_buf << '<graphml xmlns="http://graphml.graphdrawing.org/xmlns" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
13
|
+
xsi:schemaLocation="http://graphml.graphdrawing.org/xmlns http://graphml.graphdrawing.org/xmlns/1.0/graphml.xsd">'
|
14
|
+
arr_buf << '<key id="d0" for="node" attr.name="label" attr.type="string"/>'
|
15
|
+
arr_buf << '<key id="d1" for="node" attr.name="formula" attr.type="string"/>'
|
16
|
+
arr_buf << '<key id="d2" for="node" attr.name="color" attr.type="string"/>'
|
17
|
+
arr_buf << '<key id="d3" for="node" attr.name="value" attr.type="string"/>'
|
18
|
+
arr_buf << "<graph id=\"g_#{_gml_string(tensor.name)}\" edgedefault=\"directed\">"
|
19
|
+
arr_buf << "<node id=\"out\">"
|
20
|
+
arr_buf << "<data key=\"d0\">out</data>"
|
21
|
+
arr_buf << "<data key=\"d2\">red</data>"
|
22
|
+
arr_buf << "</node>"
|
23
|
+
to_graph_ml(tensor, arr_buf)
|
24
|
+
arr_buf << "<edge source=\"#{_gml_string(tensor.name)}\" target=\"out\"/>"
|
25
|
+
arr_buf << "</graph>"
|
26
|
+
arr_buf << "</graphml>"
|
27
|
+
File.write(filename, arr_buf.join("\n"))
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def _val(tensor)
|
33
|
+
JSON.pretty_generate(@last_session_context[tensor.name])
|
34
|
+
end
|
35
|
+
|
36
|
+
def to_graph_ml(tensor, arr_buf = [], added = {}, _id = 0)
|
37
|
+
puts tensor.name
|
38
|
+
added[tensor.name] = true
|
39
|
+
arr_buf << "<node id=\"#{_gml_string(tensor.name)}\">"
|
40
|
+
arr_buf << "<data key=\"d0\">#{tensor.operation}</data>"
|
41
|
+
arr_buf << "<data key=\"d1\">#{tensor.to_math(true, 1)}</data>"
|
42
|
+
arr_buf << "<data key=\"d2\">blue</data>"
|
43
|
+
if @last_session_context[tensor.name]
|
44
|
+
arr_buf << "<data key=\"d3\">#{_val(tensor)}</data>"
|
45
|
+
end
|
46
|
+
arr_buf << "</node>"
|
47
|
+
|
48
|
+
tensor.items.each do |item|
|
49
|
+
next unless item
|
50
|
+
next if _added[item.name]
|
51
|
+
|
52
|
+
next to_graph_ml(item, arr_buf, added) if item.is_a?(Operation)
|
53
|
+
added[item.name] = true
|
54
|
+
if item.is_a?(Variable)
|
55
|
+
arr_buf << "<node id=\"#{_gml_string(item.name)}\">"
|
56
|
+
arr_buf << "<data key=\"d0\">#{item.name}</data>"
|
57
|
+
arr_buf << "<data key=\"d2\">green</data>"
|
58
|
+
if @last_session_context[item.name]
|
59
|
+
arr_buf << "<data key=\"d3\">#{_val(tensor)}</data>"
|
60
|
+
end
|
61
|
+
arr_buf << "</node>"
|
62
|
+
elsif item.is_a?(Placeholder)
|
63
|
+
arr_buf << "<node id=\"#{_gml_string(item.name)}\">"
|
64
|
+
arr_buf << "<data key=\"d0\">#{item.name}</data>"
|
65
|
+
arr_buf << "<data key=\"d2\">yellow</data>"
|
66
|
+
if @last_session_context[item.name]
|
67
|
+
arr_buf << "<data key=\"d3\">#{_val(tensor)}</data>"
|
68
|
+
end
|
69
|
+
arr_buf << "</node>"
|
70
|
+
else
|
71
|
+
arr_buf << "<node id=\"#{_gml_string(item.name)}\">"
|
72
|
+
arr_buf << "<data key=\"d0\">#{item.name}</data>"
|
73
|
+
arr_buf << "<data key=\"d2\">black</data>"
|
74
|
+
if @last_session_context[item.name]
|
75
|
+
arr_buf << "<data key=\"d3\">#{_val(tensor)}</data>"
|
76
|
+
end
|
77
|
+
arr_buf << "</node>"
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
tensor.items.each do |item|
|
82
|
+
next unless item
|
83
|
+
arr_buf << "<edge source=\"#{_gml_string(item.name)}\" target=\"#{_gml_string(tensor.name)}\"/>"
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def _gml_string(str)
|
88
|
+
str.gsub('/','-')
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
module TensorStream
|
2
|
+
class Pbtext
|
3
|
+
def initialize
|
4
|
+
end
|
5
|
+
|
6
|
+
def serialize(session, filename, tensor)
|
7
|
+
end
|
8
|
+
|
9
|
+
def get_string(graph)
|
10
|
+
@lines = []
|
11
|
+
graph.nodes.each do |k, node|
|
12
|
+
@lines << "node {"
|
13
|
+
@lines << " name: #{node.name.to_json}"
|
14
|
+
if node.is_a?(TensorStream::Operation)
|
15
|
+
@lines << " op: #{node.operation.to_json}"
|
16
|
+
node.items.each do |input|
|
17
|
+
next unless input
|
18
|
+
@lines << " input: #{input.name.to_json}"
|
19
|
+
end
|
20
|
+
# type
|
21
|
+
pb_attr('T', sym_to_protobuf_type(node.data_type))
|
22
|
+
elsif node.is_a?(TensorStream::Tensor) && node.is_const
|
23
|
+
@lines << " op: \"Const\""
|
24
|
+
# type
|
25
|
+
pb_attr('T', sym_to_protobuf_type(node.data_type))
|
26
|
+
pb_attr('value', tensor_value(node))
|
27
|
+
end
|
28
|
+
@lines << "}"
|
29
|
+
end
|
30
|
+
@lines.join("\n")
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def tensor_value(tensor)
|
36
|
+
arr = []
|
37
|
+
arr << "tensor {"
|
38
|
+
arr << " dtype: #{sym_to_protobuf_type(tensor.data_type)}"
|
39
|
+
arr << " float_val: #{tensor.value}"
|
40
|
+
arr << "}"
|
41
|
+
arr
|
42
|
+
end
|
43
|
+
|
44
|
+
def sym_to_protobuf_type(type)
|
45
|
+
case type
|
46
|
+
when :int32
|
47
|
+
"DT_INT32"
|
48
|
+
when :float, :float32
|
49
|
+
"DT_FLOAT"
|
50
|
+
else
|
51
|
+
"DT_UNKNOWN"
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def pb_attr(key, value)
|
56
|
+
@lines << " attr {"
|
57
|
+
@lines << " key: \"#{key}\""
|
58
|
+
@lines << " value {"
|
59
|
+
if value.is_a?(Array)
|
60
|
+
value.each do |v|
|
61
|
+
@lines << " #{v}"
|
62
|
+
end
|
63
|
+
else
|
64
|
+
@lines << " #{value}"
|
65
|
+
end
|
66
|
+
@lines << " }"
|
67
|
+
@lines << " }"
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
module TensorStream
|
2
2
|
# module that contains helper functions useful for ops
|
3
3
|
module OpHelper
|
4
|
-
def
|
4
|
+
def _op(code, t_a, t_b = nil, options = {})
|
5
5
|
Operation.new(code.to_sym, t_a, t_b, options)
|
6
6
|
end
|
7
7
|
|
@@ -58,5 +58,11 @@ module TensorStream
|
|
58
58
|
def fp_type?(type)
|
59
59
|
TensorStream::Ops::FLOATING_POINT_TYPES.include?(type)
|
60
60
|
end
|
61
|
+
|
62
|
+
def format_source(trace)
|
63
|
+
grad_source = trace.select { |c| c.to_s.include?(File.join('lib', 'tensor_stream', 'math_gradients')) }.first
|
64
|
+
source = trace.reject { |c| c.to_s.include?(File.join('lib', 'tensor_stream')) }.first
|
65
|
+
[grad_source, source].compact.join("\n")
|
66
|
+
end
|
61
67
|
end
|
62
68
|
end
|
@@ -5,6 +5,7 @@ module TensorStream
|
|
5
5
|
|
6
6
|
def self.derivative(tensor, wrt_dx, options = {})
|
7
7
|
gradient_program_name = "_grad_#{tensor.name}_#{wrt_dx.name}"
|
8
|
+
|
8
9
|
return options[:graph].get_node(gradient_program_name) if options[:graph] && options[:graph].node_added?(gradient_program_name)
|
9
10
|
|
10
11
|
constant_options = { dtype: options[:dtype] }
|
@@ -18,6 +19,10 @@ module TensorStream
|
|
18
19
|
grad2 = derivative(tensor.items[1], wrt_dx, options) if tensor.items[1]
|
19
20
|
|
20
21
|
case tensor.operation
|
22
|
+
when :zeros_like
|
23
|
+
i_cons(0, constant_options)
|
24
|
+
when :log1p
|
25
|
+
grad * _op(:reciprocal, i_cons(1, constant_options_1) + tensor.items[0])
|
21
26
|
when :max
|
22
27
|
x_mask = i_op(:where, i_op(:ones_like, tensor.items[0]), i_op(:zeros_like, tensor.items[1]), pred: tensor.items[0] > tensor.items[1])
|
23
28
|
y_mask = i_op(:where, i_op(:zeros_like, tensor.items[0]), i_op(:ones_like, tensor.items[1]), pred: tensor.items[0] < tensor.items[1])
|
@@ -51,19 +56,19 @@ module TensorStream
|
|
51
56
|
when :cos
|
52
57
|
-i_op(:sin, tensor.items[0]) * grad
|
53
58
|
when :add
|
54
|
-
# rx =
|
55
|
-
# ry =
|
59
|
+
# rx = _op(:shape, tensor.items[0])
|
60
|
+
# ry = _op(:shape, tensor.items[1])
|
56
61
|
|
57
|
-
# ones_a =
|
58
|
-
# ones_b =
|
62
|
+
# ones_a = _op(:ones_like, tensor.items[0])
|
63
|
+
# ones_b = _op(:ones_like, tensor.items[1])
|
59
64
|
# inputs = _broadcast_transform(grad * ones_a, grad2 * ones_b)
|
60
65
|
# sx, sy = _broadcast_gradient_args(rx, ry)
|
61
66
|
|
62
|
-
# keep_dims_x =
|
63
|
-
# keep_dims_y =
|
67
|
+
# keep_dims_x = _op(:rank, inputs[0]) == _op(:rank, tensor.items[0])
|
68
|
+
# keep_dims_y = _op(:rank, inputs[1]) == _op(:rank, tensor.items[1])
|
64
69
|
|
65
|
-
# add_x =
|
66
|
-
# add_y =
|
70
|
+
# add_x = _op(:reduce_sum, inputs[0], nil, axis: sy, keepdims: keep_dims_x)
|
71
|
+
# add_y = _op(:reduce_sum, inputs[1], nil, axis: sx, keepdims: keep_dims_y)
|
67
72
|
# _filtered_sum(add_x, add_y, wrt_dx)
|
68
73
|
_grad_with_broadcast(tensor, wrt_dx, ->(a, b) { i_op(:add, a, b, name: 'grad_add') }, options)
|
69
74
|
when :sub
|
@@ -83,15 +88,15 @@ module TensorStream
|
|
83
88
|
_reduce_when_necessary(gx + gy, wrt_dx)
|
84
89
|
when :mul
|
85
90
|
# apply the product rule
|
86
|
-
rx =
|
87
|
-
ry =
|
91
|
+
rx = _op(:shape, tensor.items[0])
|
92
|
+
ry = _op(:shape, tensor.items[1])
|
88
93
|
sx, sy = _broadcast_gradient_args(rx, ry)
|
89
94
|
inputs = _broadcast_transform(tensor.items[0], tensor.items[1])
|
90
|
-
keep_dims_x =
|
91
|
-
keep_dims_y =
|
95
|
+
keep_dims_x = _op(:rank, inputs[0]) == _op(:rank, tensor.items[0])
|
96
|
+
keep_dims_y = _op(:rank, inputs[1]) == _op(:rank, tensor.items[1])
|
92
97
|
|
93
|
-
_filtered_sum(
|
94
|
-
|
98
|
+
_filtered_sum(_op(:reduce_sum, grad * _ds(inputs[1]), nil, axis: sy, keepdims: keep_dims_x),
|
99
|
+
_op(:reduce_sum, _ds(inputs[0]) * grad2, nil, axis: sx, keepdims: keep_dims_y), wrt_dx)
|
95
100
|
when :reduce_mean
|
96
101
|
input_size = i_op(:reduce_prod, i_op(:shape, tensor.items[0]))
|
97
102
|
output_size = i_op(:reduce_prod, i_op(:shape, tensor))
|
@@ -100,6 +105,8 @@ module TensorStream
|
|
100
105
|
(grad / i_op(:cast, factor, data_type: grad.dtype))
|
101
106
|
when :reduce_sum
|
102
107
|
grad
|
108
|
+
when :reciprocal
|
109
|
+
-grad * (i_cons(1, constant_options_1) / _ds(tensor.items[0])**2)
|
103
110
|
when :stop_gradient
|
104
111
|
return i_cons(0, constant_options)
|
105
112
|
when :matmul
|
@@ -113,20 +120,20 @@ module TensorStream
|
|
113
120
|
identity_1 = i_op(:ones, [s0[0], s1[1]], nil, data_type: tensor.items[1].data_type)
|
114
121
|
|
115
122
|
matmul_da = i_op(:matmul, identity_0, tensor.items[1], transpose_b: true,
|
116
|
-
|
117
|
-
|
123
|
+
pad_zeros: true,
|
124
|
+
name: 'matrix_dx')
|
118
125
|
matmul_db = i_op(:matmul, tensor.items[0], identity_1, transpose_a: true,
|
119
|
-
|
120
|
-
|
121
|
-
# matmul_db =
|
126
|
+
pad_zeros: true,
|
127
|
+
name: 'matrix_dy')
|
128
|
+
# matmul_db = _op(:transpose, matmul_db, nil).first
|
122
129
|
|
123
|
-
# begin_a =
|
124
|
-
# matmul_b_shape =
|
130
|
+
# begin_a = _op(:zeros, _op(:rank, matmul_db), nil, data_type: :int32, name: 'begin_a')
|
131
|
+
# matmul_b_shape = _op(:shape, matmul_db)
|
125
132
|
# end_a = [matmul_b_shape[0], 1]
|
126
133
|
|
127
|
-
matmul_da = i_op(:cond, matmul_da[0], matmul_da, pred:
|
134
|
+
matmul_da = i_op(:cond, matmul_da[0], matmul_da, pred: _op(:rank, derivative_a) > 0)
|
128
135
|
|
129
|
-
# matmul_da =
|
136
|
+
# matmul_da = _op(:cond, matmul_da[0], matmul_da, pred: _op(:rank, derivative_a) > 0)
|
130
137
|
norm_a = i_op(:mul, derivative_a, matmul_da, name: 'grad_a_norm_mul_da')
|
131
138
|
norm_b = i_op(:mul, derivative_b, matmul_db, name: 'grad_b_norm_mul_db')
|
132
139
|
|
@@ -173,23 +180,23 @@ module TensorStream
|
|
173
180
|
end
|
174
181
|
|
175
182
|
def self._reduce_when_necessary(tensor, wrt_dx)
|
176
|
-
rank =
|
177
|
-
dx_rank =
|
178
|
-
reduced =
|
179
|
-
|
183
|
+
rank = _op(:rank, tensor)
|
184
|
+
dx_rank = _op(:rank, wrt_dx)
|
185
|
+
reduced = _op(:reduce_sum, tensor, nil, axis: 0)
|
186
|
+
_op(:cond, ->{ reduced }, tensor, pred: rank > dx_rank)
|
180
187
|
end
|
181
188
|
|
182
189
|
def self._broadcast_gradient_args(input_a, input_b)
|
183
|
-
[
|
190
|
+
[_op(:broadcast_gradient_args, input_a, input_b), _op(:broadcast_gradient_args, input_b, input_a)]
|
184
191
|
end
|
185
192
|
|
186
193
|
def self._broadcast_transform(input_a, input_b)
|
187
|
-
|
194
|
+
_op(:broadcast_transform, input_a, input_b)
|
188
195
|
end
|
189
196
|
|
190
197
|
# filter out zero arrays
|
191
198
|
def self._filtered_sum(input_a, input_b, wrt_dx)
|
192
|
-
zero_vect =
|
199
|
+
zero_vect = _op(:zeros_like, wrt_dx)
|
193
200
|
(i_op(:cond, input_a, zero_vect, pred: i_op(:reduce_sum, input_a) != 0) + i_op(:cond, input_b, zero_vect, pred: i_op(:reduce_sum, input_b) != 0))
|
194
201
|
end
|
195
202
|
end
|