tensorflow 0.1.0 → 0.1.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.
@@ -1,6 +1,6 @@
1
1
  module TensorFlow
2
2
  class Tensor
3
- def initialize(value = nil, pointer: nil, dtype: nil, shape: nil)
3
+ def initialize(value = nil, dtype: nil, shape: nil, pointer: nil)
4
4
  @status = FFI.TF_NewStatus
5
5
 
6
6
  if pointer
@@ -18,17 +18,27 @@ module TensorFlow
18
18
 
19
19
  data = data.flatten
20
20
 
21
- dtype ||= Utils.infer_type(value)
21
+ dtype ||= Utils.infer_type(data)
22
22
  type = FFI::DataType[dtype]
23
23
  case dtype
24
+ when :float, :double, :int32, :uint8, :int16, :int8, :int64, :uint16, :uint32, :uint64
25
+ data_ptr = ::FFI::MemoryPointer.new(dtype, data.size)
26
+ data_ptr.send("write_array_of_#{dtype}", data)
27
+ when :bfloat16
28
+ # https://en.wikipedia.org/wiki/Bfloat16_floating-point_format
29
+ data_ptr = ::FFI::MemoryPointer.new(:int8, data.size * 2)
30
+ data_ptr.write_bytes(data.map { |v| [v].pack("g")[0..1] }.join)
31
+ when :complex64
32
+ data_ptr = ::FFI::MemoryPointer.new(:float, data.size * 2)
33
+ data_ptr.write_array_of_float(data.flat_map { |v| [v.real, v.imaginary] })
34
+ when :complex128
35
+ data_ptr = ::FFI::MemoryPointer.new(:double, data.size * 2)
36
+ data_ptr.write_array_of_double(data.flat_map { |v| [v.real, v.imaginary] })
24
37
  when :string
25
38
  data_ptr = string_ptr(data)
26
- when :float
27
- data_ptr = ::FFI::MemoryPointer.new(:float, data.size)
28
- data_ptr.write_array_of_float(data)
29
- when :int32
30
- data_ptr = ::FFI::MemoryPointer.new(:int32, data.size)
31
- data_ptr.write_array_of_int32(data)
39
+ when :bool
40
+ data_ptr = ::FFI::MemoryPointer.new(:int8, data.size)
41
+ data_ptr.write_array_of_int8(data.map { |v| v ? 1 : 0 })
32
42
  else
33
43
  raise "Unknown type: #{dtype}"
34
44
  end
@@ -42,74 +52,41 @@ module TensorFlow
42
52
  check_status @status
43
53
  end
44
54
 
45
- # TODO fix segfault
46
- # ObjectSpace.define_finalizer(self, self.class.finalize(@pointer))
55
+ ObjectSpace.define_finalizer(self, self.class.finalize(@pointer, @status, tensor))
47
56
  end
48
57
 
49
58
  def +(other)
50
- TensorFlow.add(self, other)
59
+ Math.add(self, other)
51
60
  end
52
61
 
53
62
  def -(other)
54
- TensorFlow.subtract(self, other)
63
+ Math.subtract(self, other)
55
64
  end
56
65
 
57
66
  def *(other)
58
- TensorFlow.multiply(self, other)
67
+ Math.multiply(self, other)
59
68
  end
60
69
 
61
70
  def /(other)
62
- TensorFlow.divide(self, other)
71
+ Math.divide(self, other)
63
72
  end
64
73
 
65
74
  def %(other)
66
- TensorFlow.floormod(self, other)
67
- end
68
-
69
- def num_dims
70
- ret = FFI.TFE_TensorHandleNumDims(@pointer, @status)
71
- check_status @status
72
- ret
73
- end
74
-
75
- def dtype
76
- @dtype ||= FFI::DataType[FFI.TFE_TensorHandleDataType(@pointer)]
77
- end
78
-
79
- def element_count
80
- ret = FFI.TFE_TensorHandleNumElements(@pointer, @status)
81
- check_status @status
82
- ret
83
- end
84
-
85
- def shape
86
- @shape ||= begin
87
- shape = []
88
- num_dims.times do |i|
89
- shape << FFI.TFE_TensorHandleDim(@pointer, i, @status)
90
- check_status @status
91
- end
92
- shape
93
- end
94
- end
95
-
96
- def data_pointer
97
- tensor = FFI.TFE_TensorHandleResolve(@pointer, @status)
98
- check_status @status
99
- FFI.TF_TensorData(tensor)
100
- end
101
-
102
- def to_ptr
103
- @pointer
75
+ Math.floormod(self, other)
104
76
  end
105
77
 
106
78
  def value
107
79
  value =
108
80
  case dtype
109
- when :float
110
- data_pointer.read_array_of_float(element_count)
111
- when :int32
112
- data_pointer.read_array_of_int32(element_count)
81
+ when :float, :double, :int32, :uint8, :int16, :int8, :int64, :uint16, :uint32, :uint64
82
+ data_pointer.send("read_array_of_#{dtype}", element_count)
83
+ when :bfloat16
84
+ byte_str = data_pointer.read_bytes(element_count * 2)
85
+ element_count.times.map { |i| "#{byte_str[(2 * i)..(2 * i + 1)]}\x00\x00".unpack1("g") }
86
+ when :complex64
87
+ data_pointer.read_array_of_float(element_count * 2).each_slice(2).map { |v| Complex(*v) }
88
+ when :complex128
89
+ data_pointer.read_array_of_double(element_count * 2).each_slice(2).map { |v| Complex(*v) }
113
90
  when :string
114
91
  # string tensor format
115
92
  # https://github.com/tensorflow/tensorflow/blob/5453aee48858fd375172d7ae22fad1557e8557d6/tensorflow/c/tf_tensor.h#L57
@@ -127,6 +104,21 @@ module TensorFlow
127
104
  reshape(value, shape)
128
105
  end
129
106
 
107
+ def dtype
108
+ @dtype ||= FFI::DataType[FFI.TFE_TensorHandleDataType(@pointer)]
109
+ end
110
+
111
+ def shape
112
+ @shape ||= begin
113
+ shape = []
114
+ num_dims.times do |i|
115
+ shape << FFI.TFE_TensorHandleDim(@pointer, i, @status)
116
+ check_status @status
117
+ end
118
+ shape
119
+ end
120
+ end
121
+
130
122
  def to_s
131
123
  inspect
132
124
  end
@@ -139,18 +131,44 @@ module TensorFlow
139
131
  value
140
132
  end
141
133
 
134
+ def to_ptr
135
+ @pointer
136
+ end
137
+
142
138
  def inspect
143
139
  inspection = %w(value shape dtype).map { |v| "#{v}: #{send(v).inspect}"}
144
140
  "#<#{self.class} #{inspection.join(", ")}>"
145
141
  end
146
142
 
147
- def self.finalize(pointer)
143
+ def self.finalize(pointer, status, tensor)
148
144
  # must use proc instead of stabby lambda
149
- proc { FFI.TFE_DeleteTensorHandle(pointer) }
145
+ proc do
146
+ FFI.TFE_DeleteTensorHandle(pointer)
147
+ FFI.TFE_DeleteStatus(status)
148
+ FFI.TFE_DeleteTensor(tensor) if tensor
149
+ end
150
150
  end
151
151
 
152
152
  private
153
153
 
154
+ def num_dims
155
+ ret = FFI.TFE_TensorHandleNumDims(@pointer, @status)
156
+ check_status @status
157
+ ret
158
+ end
159
+
160
+ def element_count
161
+ ret = FFI.TFE_TensorHandleNumElements(@pointer, @status)
162
+ check_status @status
163
+ ret
164
+ end
165
+
166
+ def data_pointer
167
+ tensor = FFI.TFE_TensorHandleResolve(@pointer, @status)
168
+ check_status @status
169
+ FFI.TF_TensorData(tensor)
170
+ end
171
+
154
172
  def reshape(arr, dims)
155
173
  return arr.first if dims.empty?
156
174
  arr = arr.flatten
@@ -176,12 +194,12 @@ module TensorFlow
176
194
  start_offset_size = data.size * 8
177
195
  offsets = [0]
178
196
  data.each do |str|
179
- offsets << str.bytesize + 1
197
+ offsets << offsets.last + str.bytesize + 1
180
198
  end
181
199
  data_ptr = ::FFI::MemoryPointer.new(:char, start_offset_size + offsets.pop)
182
200
  data_ptr.write_array_of_uint64(offsets)
183
- data.zip(offsets) do |str, off|
184
- (data_ptr + start_offset_size + off).write_string(str)
201
+ data.zip(offsets) do |str, offset|
202
+ (data_ptr + start_offset_size + offset).write_string(str)
185
203
  end
186
204
  data_ptr
187
205
  end
@@ -1,21 +1,140 @@
1
1
  module TensorFlow
2
2
  module Utils
3
- def self.check_status(status)
4
- if FFI.TF_GetCode(status) != 0
5
- raise Error, FFI.TF_Message(status)
3
+ class << self
4
+ def check_status(status)
5
+ if FFI.TF_GetCode(status) != 0
6
+ raise Error, FFI.TF_Message(status)
7
+ end
6
8
  end
7
- end
8
9
 
9
- def self.infer_type(value)
10
- case value
11
- when String
12
- :string
13
- when Float
14
- :float
15
- when true, false
16
- :bool
17
- else
18
- :int32
10
+ def default_context
11
+ @default_context ||= Context.new
12
+ end
13
+
14
+ def execute(op_name, inputs = [], **attrs)
15
+ context = default_context
16
+ status = FFI.TF_NewStatus # TODO reuse status between ops?
17
+ op = FFI.TFE_NewOp(context, op_name, status)
18
+ check_status status
19
+
20
+ attrs.each do |attr_name, attr_value|
21
+ next if attr_value.nil?
22
+
23
+ attr_name = attr_name.to_s
24
+
25
+ is_list = ::FFI::MemoryPointer.new(:int)
26
+ type = FFI.TFE_OpGetAttrType(op, attr_name, is_list, status)
27
+ check_status status
28
+
29
+ case FFI::AttrType[type]
30
+ when :string
31
+ FFI.TFE_OpSetAttrString(op, attr_name, attr_value, attr_value.bytesize)
32
+ # when :int
33
+ # when :float
34
+ # when :bool
35
+ when :type
36
+ FFI.TFE_OpSetAttrType(op, attr_name, attr_value)
37
+ when :shape
38
+ # TODO set value properly
39
+ FFI.TFE_OpSetAttrShape(op, attr_name, nil, 0, status)
40
+ check_status status
41
+ # when :tensor
42
+ # when :placeholder
43
+ # when :func
44
+ else
45
+ raise "Unknown type: #{FFI::AttrType[type]}"
46
+ end
47
+ end
48
+
49
+ inputs.each do |input|
50
+ input = TensorFlow.convert_to_tensor(input) unless input.respond_to?(:to_ptr)
51
+ FFI.TFE_OpAddInput(op, input, status)
52
+ check_status status
53
+ end
54
+
55
+ retvals = ::FFI::MemoryPointer.new(:pointer)
56
+ num_retvals = ::FFI::MemoryPointer.new(:int)
57
+ num_retvals.write_int(retvals.size)
58
+ FFI.TFE_Execute(op, retvals, num_retvals, status)
59
+ check_status status
60
+
61
+ if num_retvals.read_int > 0
62
+ handle = retvals.read_pointer
63
+ type = FFI.TFE_TensorHandleDataType(handle)
64
+
65
+ case FFI::DataType[type]
66
+ when :resource
67
+ handle
68
+ else
69
+ Tensor.new(pointer: handle)
70
+ end
71
+ end
72
+ ensure
73
+ FFI.TF_DeleteStatus(status) if status
74
+ FFI.TFE_DeleteOp(op) if op
75
+ end
76
+
77
+ def infer_type(value)
78
+ if value.all? { |v| v.is_a?(String) }
79
+ :string
80
+ elsif value.all? { |v| v == true || v == false }
81
+ :bool
82
+ elsif value.all? { |v| v.is_a?(Integer) }
83
+ if value.all? { |v| v >= -2147483648 && v <= 2147483647 }
84
+ :int32
85
+ else
86
+ :int64
87
+ end
88
+ elsif value.all? { |v| v.is_a?(Complex) }
89
+ :complex128
90
+ elsif value.all? { |v| v.is_a?(Numeric) }
91
+ :float
92
+ else
93
+ raise Error, "Unable to infer data type"
94
+ end
95
+ end
96
+
97
+ def load_dataset(path, url)
98
+ # TODO handle this better
99
+ raise "No HOME" unless ENV["HOME"]
100
+ datasets_dir = "#{ENV["HOME"]}/.keras/datasets"
101
+ FileUtils.mkdir_p(datasets_dir)
102
+
103
+ path = "#{datasets_dir}/#{path}"
104
+ Utils.download_file(url, path) unless File.exist?(path)
105
+ Npy.load_npz(path)
106
+ end
107
+
108
+ def download_file(url, dest)
109
+ uri = URI(url)
110
+
111
+ temp_dir ||= File.dirname(Tempfile.new("tensorflow"))
112
+ temp_path = "#{temp_dir}/#{Time.now.to_f}" # TODO better name
113
+
114
+ # Net::HTTP automatically adds Accept-Encoding for compression
115
+ # of response bodies and automatically decompresses gzip
116
+ # and deflateresponses unless a Range header was sent.
117
+ # https://ruby-doc.org/stdlib-2.6.4/libdoc/net/http/rdoc/Net/HTTP.html
118
+ Net::HTTP.start(uri.host, uri.port, use_ssl: true) do |http|
119
+ request = Net::HTTP::Get.new(uri)
120
+
121
+ print("Downloading dataset")
122
+ i = 0
123
+ File.open(temp_path, "wb") do |f|
124
+ http.request(request) do |response|
125
+ response.read_body do |chunk|
126
+ f.write(chunk)
127
+
128
+ # print progress
129
+ putc "." if i % 50 == 0
130
+ i += 1
131
+ end
132
+ end
133
+ puts # newline
134
+ end
135
+ end
136
+
137
+ FileUtils.mv(temp_path, dest)
19
138
  end
20
139
  end
21
140
  end
@@ -1,31 +1,31 @@
1
1
  module TensorFlow
2
2
  class Variable
3
3
  def initialize(initial_value, dtype: nil)
4
- @dtype = dtype || Utils.infer_type(initial_value)
5
- @pointer = TensorFlow.var_handle_op(type_enum, nil, shared_name: TensorFlow.send(:default_context).shared_name)
4
+ @dtype = dtype || Utils.infer_type(Array(initial_value).flatten)
5
+ @pointer = RawOps.var_handle_op(dtype: type_enum, shape: [], shared_name: Utils.default_context.shared_name)
6
6
  assign(initial_value)
7
7
  end
8
8
 
9
9
  def assign(value)
10
10
  value = TensorFlow.convert_to_tensor(value, dtype: @dtype)
11
- TensorFlow.assign_variable_op(@pointer, value)
11
+ RawOps.assign_variable_op(resource: @pointer, value: value)
12
12
  self
13
13
  end
14
14
 
15
15
  def assign_add(value)
16
16
  value = TensorFlow.convert_to_tensor(value, dtype: @dtype)
17
- TensorFlow.assign_add_variable_op(@pointer, value)
17
+ RawOps.assign_add_variable_op(resource: @pointer, value: value)
18
18
  self
19
19
  end
20
20
 
21
21
  def assign_sub(value)
22
22
  value = TensorFlow.convert_to_tensor(value, dtype: @dtype)
23
- TensorFlow.assign_sub_variable_op(@pointer, value)
23
+ RawOps.assign_sub_variable_op(resource: @pointer, value: value)
24
24
  self
25
25
  end
26
26
 
27
27
  def read_value
28
- TensorFlow.read_variable_op(@pointer, type_enum)
28
+ RawOps.read_variable_op(resource: @pointer, dtype: type_enum)
29
29
  end
30
30
 
31
31
  def +(other)
@@ -1,3 +1,3 @@
1
1
  module TensorFlow
2
- VERSION = "0.1.0"
2
+ VERSION = "0.1.1"
3
3
  end
data/lib/tensorflow.rb CHANGED
@@ -1,13 +1,30 @@
1
1
  # dependencies
2
2
  require "ffi"
3
+ require "npy"
4
+
5
+ # stdlib
6
+ require "fileutils"
7
+ require "forwardable"
8
+ require "net/http"
9
+ require "tempfile"
3
10
 
4
11
  # modules
5
12
  require "tensorflow/utils"
6
13
  require "tensorflow/context"
14
+ require "tensorflow/math"
15
+ require "tensorflow/ops"
16
+ require "tensorflow/raw_ops"
7
17
  require "tensorflow/tensor"
8
18
  require "tensorflow/variable"
9
19
  require "tensorflow/version"
10
20
 
21
+ # keras
22
+ require "tensorflow/keras/datasets/mnist"
23
+ require "tensorflow/keras/layers/dense"
24
+ require "tensorflow/keras/layers/dropout"
25
+ require "tensorflow/keras/layers/flatten"
26
+ require "tensorflow/keras/models/sequential"
27
+
11
28
  module TensorFlow
12
29
  class Error < StandardError; end
13
30
 
@@ -20,8 +37,12 @@ module TensorFlow
20
37
  autoload :FFI, "tensorflow/ffi"
21
38
 
22
39
  class << self
40
+ include Ops
23
41
  include Utils
24
42
 
43
+ extend Forwardable
44
+ def_delegators Math, :abs, :acos, :acosh, :add, :add_n, :argmax, :argmin, :asin, :asinh, :atan, :atan2, :atanh, :cos, :cosh, :cumsum, :divide, :equal, :exp, :floor, :greater, :greater_equal, :less, :less_equal, :logical_and, :logical_not, :logical_or, :maximum, :minimum, :multiply, :negative, :not_equal, :pow, :reduce_all, :reduce_any, :reduce_logsumexp, :reduce_max, :reduce_mean, :reduce_min, :reduce_prod, :reduce_sum, :round, :scalar_mul, :sigmoid, :sign, :sin, :sinh, :sqrt, :square, :subtract, :tan, :tanh, :truediv
45
+
25
46
  def library_version
26
47
  FFI.TF_Version
27
48
  end
@@ -34,153 +55,6 @@ module TensorFlow
34
55
  value = Tensor.new(value, dtype: dtype) unless value.is_a?(Tensor)
35
56
  value
36
57
  end
37
-
38
- def add(x, y)
39
- execute("Add", [x, y])
40
- end
41
-
42
- def subtract(x, y)
43
- execute("Sub", [x, y])
44
- end
45
-
46
- def multiply(x, y)
47
- execute("Mul", [x, y])
48
- end
49
-
50
- def divide(x, y)
51
- execute("Div", [x, y])
52
- end
53
-
54
- def abs(x)
55
- execute("Abs", [x])
56
- end
57
-
58
- def sqrt(x)
59
- execute("Sqrt", [x])
60
- end
61
-
62
- def matmul(x, y)
63
- execute("MatMul", [x, y])
64
- end
65
-
66
- def floormod(x, y)
67
- execute("Mod", [x, y])
68
- end
69
-
70
- def range(start, limit, delta)
71
- execute("Range", [start, limit, delta])
72
- end
73
-
74
- def transpose(x, perm: [1, 0])
75
- execute("Transpose", [x, perm])
76
- end
77
-
78
- def equal(x, y)
79
- execute("Equal", [x, y])
80
- end
81
-
82
- def zeros_like(x)
83
- execute("ZerosLike", [x])
84
- end
85
-
86
- def fill(dims, value)
87
- execute("Fill", [dims, value])
88
- end
89
-
90
- def zeros(dims)
91
- fill(dims, 0)
92
- end
93
-
94
- def ones(dims)
95
- fill(dims, 1)
96
- end
97
-
98
- def assign_add_variable_op(resource, value)
99
- execute("AssignAddVariableOp", [resource, value])
100
- end
101
-
102
- def assign_sub_variable_op(resource, value)
103
- execute("AssignSubVariableOp", [resource, value])
104
- end
105
-
106
- def assign_variable_op(resource, value)
107
- execute("AssignVariableOp", [resource, value])
108
- end
109
-
110
- def read_variable_op(resource, dtype)
111
- execute("ReadVariableOp", [resource], dtype: dtype)
112
- end
113
-
114
- def var_handle_op(dtype, shape, container: "", shared_name: "")
115
- execute("VarHandleOp", [], container: container, shared_name: shared_name, dtype: dtype, shape: shape)
116
- end
117
-
118
- def var_is_initialized_op(resource)
119
- execute("VarIsInitializedOp", [resource])
120
- end
121
-
122
- private
123
-
124
- def default_context
125
- @default_context ||= Context.new
126
- end
127
-
128
- def execute(op_name, inputs = [], **attrs)
129
- context = default_context
130
- status = FFI.TF_NewStatus
131
- op = FFI.TFE_NewOp(context, op_name, status)
132
- check_status status
133
- # TODO clean up status and op
134
-
135
- attrs.each do |attr_name, attr_value|
136
- attr_name = attr_name.to_s
137
-
138
- is_list = ::FFI::MemoryPointer.new(:int)
139
- type = FFI.TFE_OpGetAttrType(op, attr_name, is_list, status)
140
- check_status status
141
-
142
- case FFI::AttrType[type]
143
- when :type
144
- FFI.TFE_OpSetAttrType(op, attr_name, attr_value)
145
- when :shape
146
- # TODO set value properly
147
- FFI.TFE_OpSetAttrShape(op, attr_name, attr_value, 0, status)
148
- check_status status
149
- when :string
150
- FFI.TFE_OpSetAttrString(op, attr_name, attr_value, attr_value.bytesize)
151
- else
152
- raise "Unknown type: #{FFI::AttrType[type]}"
153
- end
154
- end
155
-
156
- inputs.each do |input|
157
- input = convert_to_tensor(input) unless input.respond_to?(:to_ptr)
158
- FFI.TFE_OpAddInput(op, input, status)
159
- check_status status
160
- end
161
-
162
- retvals = ::FFI::MemoryPointer.new(:pointer)
163
- num_retvals = ::FFI::MemoryPointer.new(:int)
164
- num_retvals.write_int(retvals.size)
165
- FFI.TFE_Execute(op, retvals, num_retvals, status)
166
- check_status status
167
-
168
- if num_retvals.read_int > 0
169
- handle = retvals.read_pointer
170
- type = FFI.TFE_TensorHandleDataType(handle)
171
-
172
- case FFI::DataType[type]
173
- when :resource
174
- handle
175
- else
176
- Tensor.new(pointer: handle)
177
- end
178
- end
179
- end
180
-
181
- def check_status(status)
182
- Utils.check_status(status)
183
- end
184
58
  end
185
59
  end
186
60
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tensorflow
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrew Kane
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-09-18 00:00:00.000000000 Z
11
+ date: 2019-09-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ffi
@@ -24,6 +24,20 @@ dependencies:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: npy
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
27
41
  - !ruby/object:Gem::Dependency
28
42
  name: bundler
29
43
  requirement: !ruby/object:Gem::Requirement
@@ -66,6 +80,34 @@ dependencies:
66
80
  - - ">="
67
81
  - !ruby/object:Gem::Version
68
82
  version: '5'
83
+ - !ruby/object:Gem::Dependency
84
+ name: google-protobuf
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: nokogiri
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
69
111
  description:
70
112
  email: andrew@chartkick.com
71
113
  executables: []
@@ -78,6 +120,14 @@ files:
78
120
  - lib/tensorflow.rb
79
121
  - lib/tensorflow/context.rb
80
122
  - lib/tensorflow/ffi.rb
123
+ - lib/tensorflow/keras/datasets/mnist.rb
124
+ - lib/tensorflow/keras/layers/dense.rb
125
+ - lib/tensorflow/keras/layers/dropout.rb
126
+ - lib/tensorflow/keras/layers/flatten.rb
127
+ - lib/tensorflow/keras/models/sequential.rb
128
+ - lib/tensorflow/math.rb
129
+ - lib/tensorflow/ops.rb
130
+ - lib/tensorflow/raw_ops.rb
81
131
  - lib/tensorflow/tensor.rb
82
132
  - lib/tensorflow/utils.rb
83
133
  - lib/tensorflow/variable.rb