rb-brain 0.0.1 → 0.1.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: c16b832f4b21365c93b698c01c14fbea54eb2e74
4
- data.tar.gz: e65a2a8c1c76c8b83c00972184942d02fbbeefa3
3
+ metadata.gz: f43371c0a74e1690e8814ae7230dd12d6d0f1be0
4
+ data.tar.gz: ea374d2706bd4d54466201eb805c842d7478ba2d
5
5
  SHA512:
6
- metadata.gz: cabeba60da560694cb1343f0c0f384533e563ea4f829838783a5cea8ac30c81cf487e0cc2faede4d832ae6e1ea8da3156eca6b4dd7da66c9b29ae2c5be7d6a4f
7
- data.tar.gz: 80aab9ccc24424d6b1763c985ff693f81e09b6c41aa1d613d4dc46f431b438d8ce83a4468d78da67df16a5b75858c14747b67e0132b4ccc34df8de3438fab9f7
6
+ metadata.gz: 7c7e8ee3b5d5b07b99b5c6e972201487188f33f173a3b48025d317975602abe931163e05f1ee5ffef2327e8dd24019fb09c1609edf8dd049ca84fe69891b1a32
7
+ data.tar.gz: 86600d5d1c10f1e5fc796a19aaee8ee3019ea7040f0649b8a2930d0018d3b11c4b132920e92e0ed7be301222e7a32becfc1287df4ec3c2e0666a846118d3d0a3
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --color
2
+ --require spec_helper
3
+ --format documentation
data/Gemfile CHANGED
@@ -2,3 +2,7 @@ source 'https://rubygems.org'
2
2
 
3
3
  # Specify your gem's dependencies in carrierwave-webdav.gemspec
4
4
  gemspec
5
+ gem 'rake'
6
+ gem 'rspec'
7
+ gem 'pry'
8
+ gem 'hashr'
@@ -0,0 +1,40 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ rb-brain (0.0.1)
5
+
6
+ GEM
7
+ remote: https://rubygems.org/
8
+ specs:
9
+ coderay (1.1.0)
10
+ diff-lcs (1.2.5)
11
+ hashr (0.0.22)
12
+ method_source (0.8.2)
13
+ pry (0.10.1)
14
+ coderay (~> 1.1.0)
15
+ method_source (~> 0.8.1)
16
+ slop (~> 3.4)
17
+ rake (10.4.2)
18
+ rspec (3.1.0)
19
+ rspec-core (~> 3.1.0)
20
+ rspec-expectations (~> 3.1.0)
21
+ rspec-mocks (~> 3.1.0)
22
+ rspec-core (3.1.7)
23
+ rspec-support (~> 3.1.0)
24
+ rspec-expectations (3.1.2)
25
+ diff-lcs (>= 1.2.0, < 2.0)
26
+ rspec-support (~> 3.1.0)
27
+ rspec-mocks (3.1.3)
28
+ rspec-support (~> 3.1.0)
29
+ rspec-support (3.1.2)
30
+ slop (3.6.0)
31
+
32
+ PLATFORMS
33
+ ruby
34
+
35
+ DEPENDENCIES
36
+ hashr
37
+ pry
38
+ rake
39
+ rb-brain!
40
+ rspec
@@ -0,0 +1,5 @@
1
+ require 'bundler/gem_tasks'
2
+
3
+ require 'rspec/core/rake_task'
4
+ RSpec::Core::RakeTask.new(:spec)
5
+ task :default => :spec
@@ -1,4 +1,7 @@
1
1
  require 'brain/lookup'
2
+ require 'json'
3
+ require 'pry'
4
+ require 'hashr'
2
5
 
3
6
  module Brain
4
7
  class NeuralNetwork
@@ -187,6 +190,89 @@ module Brain
187
190
  data
188
191
  end
189
192
 
193
+ def to_json
194
+ # make json look like:
195
+ # {
196
+ # layers: [
197
+ # { x: {},
198
+ # y: {}},
199
+ # {'0': {bias: -0.98771313, weights: {x: 0.8374838, y: 1.245858},
200
+ # '1': {bias: 3.48192004, weights: {x: 1.7825821, y: -2.67899}}},
201
+ # { f: {bias: 0.27205739, weights: {'0': 1.3161821, '1': 2.00436}}}
202
+ # ]
203
+ # }
204
+ layers = []
205
+ (0..@output_layer).each do |layer|
206
+ layers[layer] = {}
207
+
208
+ if layer == 0 and @input_lookup
209
+ nodes = @input_lookup.keys
210
+ elsif layer == @output_layer and @output_lookup
211
+ nodes = @output_lookup.keys
212
+ else
213
+ nodes = (0...@sizes[layer]).to_a
214
+ end
215
+
216
+ (0...nodes.length).each do |j|
217
+ node = nodes[j]
218
+ layers[layer][node] = {}
219
+
220
+ if layer > 0
221
+ layers[layer][node][:bias] = @biases[layer][j]
222
+ layers[layer][node][:weights] = {}
223
+ layers[layer - 1].each do |k,v|
224
+ index = k
225
+ if layer == 1 and @input_lookup
226
+ index = @input_lookup[k]
227
+ end
228
+ layers[layer][node][:weights][k] = @weights[layer][j][index]
229
+ end
230
+ end
231
+ end
232
+ end
233
+
234
+ {
235
+ layers: layers,
236
+ output_lookup: !!@output_lookup,
237
+ input_lookup: !!@input_lookup
238
+ }.to_json
239
+ end
240
+
241
+ def from_json(json)
242
+ json = JSON.parse(json).deep_symbolize_keys
243
+ size = json[:layers].length
244
+
245
+
246
+ @output_layer = size - 1
247
+ @sizes = Array.new size
248
+ @weights = Array.new size
249
+ @biases = Array.new size
250
+ @outputs = Array.new size
251
+
252
+ (0..@output_layer).each do |i|
253
+ layer = json[:layers][i]
254
+ if i == 0 and (!layer[0] or json[:input_lookup])
255
+ @input_lookup = Lookup.lookup_from_hash layer
256
+ elsif i == @output_layer and (!layer[0] or json[:output_lookup])
257
+ @output_lookup = Lookup.lookup_from_hash layer
258
+ end
259
+
260
+ nodes = layer.keys
261
+ @sizes[i] = nodes.length
262
+ @weights[i] = []
263
+ @biases[i] = []
264
+ @outputs[i] = []
265
+
266
+ (0...nodes.length).each do |j|
267
+
268
+ node = nodes[j]
269
+ @biases[i][j] = layer[node][:bias]
270
+ @weights[i][j] = layer[node][:weights]
271
+ @weights[i][j] = @weights[i][j].values unless @weights[i][j].nil?
272
+ end
273
+ end
274
+ end
275
+
190
276
  private
191
277
  def random_weight
192
278
  Random.rand * 0.4 - 0.2
@@ -4,7 +4,7 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
4
 
5
5
  Gem::Specification.new do |gem|
6
6
  gem.name = "rb-brain"
7
- gem.version = "0.0.1"
7
+ gem.version = "0.1.0"
8
8
  gem.authors = ["Eric Zhang"]
9
9
  gem.email = ["i@qinix.com"]
10
10
  gem.description = %q{rb-brain is an easy-to-use neural network written in ruby}
@@ -0,0 +1,31 @@
1
+ describe 'bitwase functions' do
2
+ it 'NOT function' do
3
+ data_not = [{input: [0], output: [1]},
4
+ {input: [1], output: [0]}]
5
+ test_bitwise data_not, 'not'
6
+ end
7
+
8
+ it 'XOR function' do
9
+ data_xor = [{input: [0, 0], output: [0]},
10
+ {input: [0, 1], output: [1]},
11
+ {input: [1, 0], output: [1]},
12
+ {input: [1, 1], output: [0]}]
13
+ test_bitwise data_xor, 'xor'
14
+ end
15
+
16
+ it 'OR function' do
17
+ data_or = [{input: [0, 0], output: [0]},
18
+ {input: [0, 1], output: [1]},
19
+ {input: [1, 0], output: [1]},
20
+ {input: [1, 1], output: [1]}]
21
+ test_bitwise data_or, 'or'
22
+ end
23
+
24
+ it 'AND function' do
25
+ data_and = [{input: [0, 0], output: [0]},
26
+ {input: [0, 1], output: [0]},
27
+ {input: [1, 0], output: [0]},
28
+ {input: [1, 1], output: [1]}]
29
+ test_bitwise data_and, 'and'
30
+ end
31
+ end
@@ -0,0 +1,70 @@
1
+ describe 'hash input and output' do
2
+ before :each do
3
+ @net = Brain::NeuralNetwork.new
4
+ end
5
+
6
+ it 'runs correctly with array input and output' do
7
+ @net.train([{input: { x: 0, y: 0 }, output: [0]},
8
+ {input: { x: 0, y: 1 }, output: [1]},
9
+ {input: { x: 1, y: 0 }, output: [1]},
10
+ {input: { x: 1, y: 1 }, output: [0]}])
11
+ output = @net.run({x: 1, y: 0})
12
+
13
+ expect(output[0]).to be > 0.9
14
+ end
15
+
16
+ it 'runs correctly with hash input' do
17
+ @net.train([{input: { x: 0, y: 0 }, output: [0]},
18
+ {input: { x: 0, y: 1 }, output: [1]},
19
+ {input: { x: 1, y: 0 }, output: [1]},
20
+ {input: { x: 1, y: 1 }, output: [0]}])
21
+ output = @net.run({x: 1, y: 0})
22
+
23
+ expect(output[0]).to be > 0.9
24
+ end
25
+
26
+ it 'runs correctly with hash output' do
27
+ @net.train([{input: [0, 0], output: { answer: 0 }},
28
+ {input: [0, 1], output: { answer: 1 }},
29
+ {input: [1, 0], output: { answer: 1 }},
30
+ {input: [1, 1], output: { answer: 0 }}])
31
+
32
+ output = @net.run([1, 0])
33
+
34
+ expect(output[:answer]).to be > 0.9
35
+ end
36
+
37
+ it 'runs correctly with hash input and output' do
38
+ @net.train([{input: { x: 0, y: 0 }, output: { answer: 0 }},
39
+ {input: { x: 0, y: 1 }, output: { answer: 1 }},
40
+ {input: { x: 1, y: 0 }, output: { answer: 1 }},
41
+ {input: { x: 1, y: 1 }, output: { answer: 0 }}])
42
+
43
+ output = @net.run({x: 1, y: 0})
44
+
45
+ expect(output[:answer]).to be > 0.9
46
+ end
47
+
48
+ it 'runs correctly with sparse hashes' do
49
+ @net.train([{input: {}, output: {}},
50
+ {input: { y: 1 }, output: { answer: 1 }},
51
+ {input: { x: 1 }, output: { answer: 1 }},
52
+ {input: { x: 1, y: 1 }, output: {}}])
53
+
54
+
55
+ output = @net.run({x: 1})
56
+
57
+ expect(output[:answer]).to be > 0.9
58
+ end
59
+
60
+ it 'runs correctly with unseen input' do
61
+ @net.train([{input: {}, output: {}},
62
+ {input: { y: 1 }, output: { answer: 1 }},
63
+ {input: { x: 1 }, output: { answer: 1 }},
64
+ {input: { x: 1, y: 1 }, output: {}}])
65
+
66
+ output = @net.run({x: 1, z: 1})
67
+
68
+ expect(output[:answer]).to be > 0.9
69
+ end
70
+ end
@@ -0,0 +1,21 @@
1
+ describe 'JSON' do
2
+ it 'to_json()/from_json()' do
3
+ net = Brain::NeuralNetwork.new
4
+
5
+ net.train([{input: {a: Random.rand, b: Random.rand},
6
+ output: {c: Random.rand, a: Random.rand}},
7
+ {input: {a: Random.rand, b: Random.rand},
8
+ output: {c: Random.rand, a: Random.rand}}])
9
+
10
+ json = net.to_json()
11
+ net2 = Brain::NeuralNetwork.new
12
+ net2.from_json json
13
+
14
+ input = {a: Random.rand, b: Random.rand}
15
+
16
+ output1 = net.run(input)
17
+ output2 = net2.run(input)
18
+
19
+ expect(output1).to eq output2
20
+ end
21
+ end
@@ -0,0 +1,32 @@
1
+ describe 'Lookup' do
2
+ it 'lookup_from_hash()' do
3
+ lup = Brain::Lookup.lookup_from_hash({ a: 6, b: 7, c: 8 })
4
+
5
+ expect(lup).to eq({ a: 0, b: 1, c: 2})
6
+ end
7
+
8
+ it 'build_lookup()' do
9
+ lup = Brain::Lookup.build_lookup([{ x: 0, y: 0 },
10
+ { x: 1, z: 0 },
11
+ { q: 0 },
12
+ { x: 1, y: 1 }])
13
+
14
+ expect(lup).to eq({ x: 0, y: 1, z: 2, q: 3 })
15
+ end
16
+
17
+ it 'to_array()' do
18
+ lup = { a: 0, b: 1, c: 2 }
19
+
20
+ array = Brain::Lookup.to_array(lup, { b: 8, notinlookup: 9 })
21
+
22
+ expect(array).to eq([0, 8, 0])
23
+ end
24
+
25
+ it 'to_hash()' do
26
+ lup = { b: 1, a: 0, c: 2 }
27
+
28
+ hash = Brain::Lookup.to_hash(lup, [0, 9, 8])
29
+
30
+ expect(hash).to eq({a: 0, b: 9, c: 8})
31
+ end
32
+ end
@@ -0,0 +1,83 @@
1
+ require 'json'
2
+ require 'hashr'
3
+
4
+ describe 'neural network options' do
5
+ it 'hiddenLayers' do
6
+ net = Brain::NeuralNetwork.new({ hidden_layers: [8, 7] })
7
+
8
+ net.train([{input: [0, 0], output: [0]},
9
+ {input: [0, 1], output: [1]},
10
+ {input: [1, 0], output: [1]},
11
+ {input: [1, 1], output: [0]}])
12
+
13
+ json = net.to_json
14
+ json = JSON.parse json
15
+ json.deep_symbolize_keys!
16
+
17
+ expect(json[:layers].length).to eq(4)
18
+ expect(json[:layers][1].length).to eq(8)
19
+ expect(json[:layers][2].length).to eq(7)
20
+ end
21
+
22
+ it 'hiddenLayers default expand to input size' do
23
+ net = Brain::NeuralNetwork.new()
24
+
25
+ net.train([{input: [0, 0, 1, 1, 1, 1, 1, 1, 1], output: [0]},
26
+ {input: [0, 1, 1, 1, 1, 1, 1, 1, 1], output: [1]},
27
+ {input: [1, 0, 1, 1, 1, 1, 1, 1, 1], output: [1]},
28
+ {input: [1, 1, 1, 1, 1, 1, 1, 1, 1], output: [0]}])
29
+
30
+ json = net.to_json
31
+ json = JSON.parse json
32
+ json.deep_symbolize_keys!
33
+
34
+ expect(json[:layers].length).to eq(3)
35
+ expect(json[:layers][1].length).to eq(4)
36
+ end
37
+
38
+
39
+ it 'learning_rate - higher learning rate should train faster' do
40
+ data = [{input: [0, 0], output: [0]},
41
+ {input: [0, 1], output: [1]},
42
+ {input: [1, 0], output: [1]},
43
+ {input: [1, 1], output: [1]}]
44
+
45
+ net1 = Brain::NeuralNetwork.new()
46
+ iters1 = net1.train(data, learning_rate: 0.5)[:iterations]
47
+
48
+ net2 = Brain::NeuralNetwork.new()
49
+ iters2 = net2.train(data, learning_rate: 0.8)[:iterations]
50
+
51
+ expect(iters1).to be > (iters2 * 1.1)
52
+ end
53
+
54
+ it 'learning_rate - backwards compatibility' do
55
+ data = [{input: [0, 0], output: [0]},
56
+ {input: [0, 1], output: [1]},
57
+ {input: [1, 0], output: [1]},
58
+ {input: [1, 1], output: [1]}]
59
+
60
+ net1 = Brain::NeuralNetwork.new(learning_rate: 0.5)
61
+ iters1 = net1.train(data)[:iterations]
62
+
63
+ net2 = Brain::NeuralNetwork.new(learning_rate: 0.8)
64
+ iters2 = net2.train(data)[:iterations]
65
+
66
+ expect(iters1).to be > (iters2 * 1.1)
67
+ end
68
+
69
+ it 'momentum - higher momentum should train faster' do
70
+ data = [{input: [0, 0], output: [0]},
71
+ {input: [0, 1], output: [1]},
72
+ {input: [1, 0], output: [1]},
73
+ {input: [1, 1], output: [1]}]
74
+
75
+ net1 = Brain::NeuralNetwork.new(momentum: 0.1)
76
+ iters1 = net1.train(data)[:iterations]
77
+
78
+ net2 = Brain::NeuralNetwork.new(momentum: 0.5)
79
+ iters2 = net2.train(data)[:iterations]
80
+
81
+ expect(iters1).to be > (iters2 * 1.1)
82
+ end
83
+ end
@@ -0,0 +1,26 @@
1
+ data = [{input: [0, 0], output: [0]},
2
+ {input: [0, 1], output: [1]},
3
+ {input: [1, 0], output: [1]},
4
+ {input: [1, 1], output: [1]}]
5
+
6
+ describe 'train() options' do
7
+ before :each do
8
+ @net = Brain::NeuralNetwork.new
9
+ end
10
+
11
+ it 'train until error threshold reached' do
12
+ error = @net.train(data,
13
+ errorThresh: 0.2,
14
+ iterations: 100000)[:error]
15
+
16
+ expect(error).to be < 0.2
17
+ end
18
+
19
+ it 'train until max iterations reached' do
20
+ stats = @net.train(data,
21
+ errorThresh: 0.001,
22
+ iterations: 1)
23
+
24
+ expect(stats[:iterations]).to eq(1)
25
+ end
26
+ end
@@ -0,0 +1,10 @@
1
+ require 'rspec'
2
+
3
+ Dir['spec/supports/**/*.rb'].each { |f| require File.expand_path(f) }
4
+
5
+ require 'brain'
6
+
7
+ RSpec.configure do |config|
8
+ config.include Helpers
9
+ end
10
+
@@ -0,0 +1,14 @@
1
+ module Helpers
2
+ def test_bitwise(data, op)
3
+ wiggle = 0.1
4
+
5
+ net = Brain::NeuralNetwork.new
6
+ net.train(data, error_thresh: 0.003)
7
+
8
+ data.each do |item|
9
+ output = net.run(item[:input])
10
+ target = item[:output]
11
+ expect(output[0]).to be_within(wiggle).of(target[0])
12
+ end
13
+ end
14
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rb-brain
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Eric Zhang
@@ -17,13 +17,24 @@ executables: []
17
17
  extensions: []
18
18
  extra_rdoc_files: []
19
19
  files:
20
+ - ".rspec"
20
21
  - Gemfile
22
+ - Gemfile.lock
21
23
  - LICENSE
22
24
  - README.md
25
+ - Rakefile
23
26
  - lib/brain.rb
24
27
  - lib/brain/lookup.rb
25
28
  - lib/brain/neuralnetwork.rb
26
29
  - rb-brain.gemspec
30
+ - spec/lib/bitwise_spec.rb
31
+ - spec/lib/hash_spec.rb
32
+ - spec/lib/json_spec.rb
33
+ - spec/lib/lookup_spec.rb
34
+ - spec/lib/options_spec.rb
35
+ - spec/lib/trainopts_spec.rb
36
+ - spec/spec_helper.rb
37
+ - spec/supports/helpers.rb
27
38
  homepage: https://github.com/qinix/rb-brain
28
39
  licenses:
29
40
  - MIT
@@ -48,4 +59,12 @@ rubygems_version: 2.4.3
48
59
  signing_key:
49
60
  specification_version: 4
50
61
  summary: rb-brain is an easy-to-use neural network written in ruby
51
- test_files: []
62
+ test_files:
63
+ - spec/lib/bitwise_spec.rb
64
+ - spec/lib/hash_spec.rb
65
+ - spec/lib/json_spec.rb
66
+ - spec/lib/lookup_spec.rb
67
+ - spec/lib/options_spec.rb
68
+ - spec/lib/trainopts_spec.rb
69
+ - spec/spec_helper.rb
70
+ - spec/supports/helpers.rb