Skip to content

Commit 8599fd7

Browse files
fix: Merge and upgrade master into develop
for prepping master to receive develop soon
2 parents 414ff83 + cdab148 commit 8599fd7

File tree

102 files changed

+13734
-3547
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

102 files changed

+13734
-3547
lines changed

.npmignore

+1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
test/
99
coverage/
1010
.github/
11+
.cache/
1112
__tests__
1213
__coverage__
1314
.cache

README.md

+347-220
Large diffs are not rendered by default.

__tests__/cross-validate.js

+230
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,230 @@
1+
const CrossValidate = require('../src/cross-validate');
2+
const NeuralNetwork = require('../src/neural-network');
3+
const LSTMTimeStep = require('../src/recurrent/lstm-time-step');
4+
5+
describe('CrossValidate', () => {
6+
describe('.train()', () => {
7+
class FakeNN extends NeuralNetwork {
8+
constructor(run) {
9+
super();
10+
if (run) {
11+
this.run = run;
12+
}
13+
this.hiddenLayers = [1,2,3];
14+
}
15+
train() {
16+
return {
17+
iterations: 10,
18+
error: 0.05
19+
};
20+
}
21+
runInput(inputs) {
22+
return this.run(inputs);
23+
}
24+
toJSON() {
25+
return null;
26+
}
27+
}
28+
it('throws exception when training set is too small', () => {
29+
const xorTrainingData = [
30+
{ input: [0, 1], output: [1] }
31+
];
32+
const net = new CrossValidate(FakeNN);
33+
expect(() => {
34+
net.train(xorTrainingData);
35+
}).toThrow();
36+
});
37+
it('handles successful training', () => {
38+
const xorTrainingData = [
39+
{ input: [0, 1], output: [1] },
40+
{ input: [0, 0], output: [0] },
41+
{ input: [1, 1], output: [0] },
42+
{ input: [1, 0], output: [1] },
43+
44+
{ input: [0, 1], output: [1] },
45+
{ input: [0, 0], output: [0] },
46+
{ input: [1, 1], output: [0] },
47+
{ input: [1, 0], output: [1] }
48+
];
49+
const net = new CrossValidate(FakeNN, (inputs) => {
50+
if (inputs[0] === 0 && inputs[1] === 1) return [1];
51+
if (inputs[0] === 0 && inputs[1] === 0) return [0];
52+
if (inputs[0] === 1 && inputs[1] === 1) return [0];
53+
if (inputs[0] === 1 && inputs[1] === 0) return [1];
54+
throw new Error('unknown input');
55+
});
56+
net.shuffleArray = (input) => input;
57+
const result = net.train(xorTrainingData);
58+
expect(result.avgs.iterations).toBe(10);
59+
expect(result.avgs.error).toBe(0.05);
60+
expect(result.avgs.testTime >= 0).toBeTruthy();
61+
expect(result.avgs.trainTime >= 0).toBeTruthy();
62+
expect(result.stats.total).toBe(8);
63+
64+
expect(result.stats.truePos).toBe(4);
65+
expect(result.stats.trueNeg).toBe(4);
66+
expect(result.stats.falsePos).toBe(0);
67+
expect(result.stats.falseNeg).toBe(0);
68+
expect(result.stats.precision).toBe(1);
69+
expect(result.stats.accuracy).toBe(1);
70+
expect(result.stats.testSize).toBe(2);
71+
expect(result.stats.trainSize).toBe(6);
72+
73+
expect(result.sets.length).toBe(4);
74+
for (let i = 0; i < result.sets.length; i++) {
75+
const set = result.sets[0];
76+
expect(set.accuracy).toBe(1);
77+
expect(set.error).toBe(0.05);
78+
expect(set.truePos >= 1 || set.trueNeg >= 1).toBeTruthy();
79+
expect(set.falseNeg).toBe(0);
80+
expect(set.falsePos).toBe(0);
81+
expect(set.precision).toBe(1);
82+
expect(set.recall).toBe(1);
83+
expect(set.testTime >= 0).toBeTruthy();
84+
expect(set.trainTime >= 0).toBeTruthy();
85+
expect(set.total).toBe(2);
86+
expect(set.network).toBe(null);
87+
expect(set.hiddenLayers).toEqual([1,2,3]);
88+
expect(set.misclasses).toEqual([]);
89+
}
90+
});
91+
it('handles unsuccessful training', () => {
92+
const xorTrainingData = [
93+
{ input: [0, 1], output: [1] },
94+
{ input: [0, 0], output: [0] },
95+
{ input: [1, 1], output: [0] },
96+
{ input: [1, 0], output: [1] },
97+
98+
{ input: [0, 1], output: [1] },
99+
{ input: [0, 0], output: [0] },
100+
{ input: [1, 1], output: [0] },
101+
{ input: [1, 0], output: [1] }
102+
];
103+
const net = new CrossValidate(FakeNN, (inputs) => {
104+
// invert output, showing worst possible training
105+
if (inputs[0] === 0 && inputs[1] === 1) return [0];
106+
if (inputs[0] === 0 && inputs[1] === 0) return [1];
107+
if (inputs[0] === 1 && inputs[1] === 1) return [1];
108+
if (inputs[0] === 1 && inputs[1] === 0) return [0];
109+
throw new Error('unknown input');
110+
});
111+
net.shuffleArray = (input) => input;
112+
const result = net.train(xorTrainingData);
113+
expect(result.avgs.iterations).toBe(10);
114+
expect(result.avgs.error).toBe(0.05);
115+
expect(result.avgs.testTime >= 0).toBeTruthy();
116+
expect(result.avgs.trainTime >= 0).toBeTruthy();
117+
expect(result.stats.total).toBe(8);
118+
119+
expect(result.stats.truePos).toBe(0);
120+
expect(result.stats.trueNeg).toBe(0);
121+
expect(result.stats.falsePos).toBe(4);
122+
expect(result.stats.falseNeg).toBe(4);
123+
expect(result.stats.precision).toBe(0);
124+
expect(result.stats.accuracy).toBe(0);
125+
expect(result.stats.testSize).toBe(2);
126+
expect(result.stats.trainSize).toBe(6);
127+
128+
expect(result.sets.length).toBe(4);
129+
for (let i = 0; i < result.sets.length; i++) {
130+
const set = result.sets[0];
131+
expect(set.accuracy).toBe(0);
132+
expect(set.error).toBe(0.05);
133+
expect(set.truePos).toBe(0);
134+
expect(set.trueNeg).toBe(0);
135+
expect(set.falseNeg >= 1 || set.falsePos >= 1).toBeTruthy();
136+
expect(set.precision).toBe(0);
137+
expect(set.recall).toBe(0);
138+
expect(set.testTime >= 0).toBeTruthy();
139+
expect(set.trainTime >= 0).toBeTruthy();
140+
expect(set.total).toBe(2);
141+
expect(set.network).toBe(null);
142+
expect(set.hiddenLayers).toEqual([1,2,3]);
143+
expect(set.misclasses.length > 0).toBeTruthy();
144+
expect(set.misclasses[0].hasOwnProperty('input')).toBeTruthy();
145+
expect(set.misclasses[0].input.length).toBeTruthy();
146+
expect(xorTrainingData.filter(v => v.input === set.misclasses[0].input)).toBeTruthy();
147+
expect(xorTrainingData.filter(v => v.output === set.misclasses[0].output)).toBeTruthy();
148+
expect(set.misclasses[0].actual === 0 || set.misclasses[0].actual === 1).toBeTruthy();
149+
expect(set.misclasses[0].expected === 0 || set.misclasses[0].expected === 1).toBeTruthy();
150+
}
151+
});
152+
});
153+
describe('.toJSON()', () => {
154+
it('returns from this.json', () => {
155+
const fakeJson = Math.random();
156+
const json = CrossValidate.prototype.toJSON.call({ json: fakeJson });
157+
expect(json).toBe(fakeJson);
158+
});
159+
});
160+
describe('.fromJSON()', () => {
161+
class FakeNN {
162+
fromJSON(json) {
163+
this.json = json;
164+
}
165+
}
166+
it('creates a new instance of constructor from argument\'s sets.error', () => {
167+
const cv = new CrossValidate(FakeNN);
168+
const net = cv.fromJSON({ sets: [{ error: 10, network: 10 },{ error: 5, network: 5 }, { error: 1, network: 1 }] });
169+
expect(net.json).toBe(1);
170+
});
171+
});
172+
describe('.toNeuralNetwork()', () => {
173+
class FakeNN {
174+
fromJSON(json) {
175+
this.json = json;
176+
}
177+
}
178+
it('creates a new instance of constructor from top .json sets.error', () => {
179+
const cv = new CrossValidate(FakeNN);
180+
cv.json = { sets: [{ error: 10, network: 10 },{ error: 5, network: 5 }, { error: 1, network: 1 }] };
181+
const net = cv.toNeuralNetwork();
182+
expect(net.json).toBe(1);
183+
});
184+
});
185+
describe('NeuralNetwork compatibility', () => {
186+
it('handles simple xor example', () => {
187+
const xorTrainingData = [
188+
{ input: [0, 1], output: [1] },
189+
{ input: [0, 0], output: [0] },
190+
{ input: [1, 1], output: [0] },
191+
{ input: [1, 0], output: [1] },
192+
193+
{ input: [0, 1], output: [1] },
194+
{ input: [0, 0], output: [0] },
195+
{ input: [1, 1], output: [0] },
196+
{ input: [1, 0], output: [1] }
197+
];
198+
const net = new CrossValidate(NeuralNetwork);
199+
const result = net.train(xorTrainingData);
200+
for (let p in result.avgs) {
201+
expect(result.avgs[p] >= 0).toBeTruthy();
202+
}
203+
for (let p in result.stats) {
204+
expect(result.stats[p] >= 0).toBeTruthy();
205+
}
206+
});
207+
});
208+
209+
describe('RNNTimeStep compatibility', () => {
210+
it('can average error for array,array, counting forwards and backwards', () => {
211+
const trainingData = [
212+
[.1,.2,.3,.4,.5],
213+
[.2,.3,.4,.5,.6],
214+
[.3,.4,.5,.6,.7],
215+
[.4,.5,.6,.7,.8],
216+
[.5,.6,.7,.8,.9],
217+
218+
[.5,.4,.3,.2,.1],
219+
[.6,.5,.4,.3,.2],
220+
[.7,.6,.5,.4,.3],
221+
[.8,.7,.6,.5,.4],
222+
[.9,.8,.7,.6,.5],
223+
];
224+
225+
const cv = new CrossValidate(LSTMTimeStep, { inputSize: 1, hiddenLayers: [10], outputSize: 1 });
226+
const result = cv.train(trainingData, { iterations: 10 });
227+
expect(!isNaN(result.avgs.error)).toBeTruthy();
228+
});
229+
});
230+
});

__tests__/cross-validate.test.js

Whitespace-only changes.

__tests__/examples.js

+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
describe('tests', () => {
2+
test('children\'s-book', () => {
3+
expect(() => {
4+
require('../examples/javascript/childrens-book');
5+
}).not.toThrow();
6+
});
7+
test('cross validation', () => {
8+
expect(() => {
9+
require('../examples/javascript/cross-validate');
10+
}).not.toThrow();
11+
});
12+
test('gpu fallback', () => {
13+
expect(() => {
14+
require('../examples/javascript/gpu-fallback');
15+
}).not.toThrow();
16+
});
17+
test('learn math', () => {
18+
expect(() => {
19+
require('../examples/javascript/learn-math');
20+
}).not.toThrow();
21+
});
22+
test('predict numbers', () => {
23+
expect(() => {
24+
require('../examples/javascript/predict-numbers');
25+
}).not.toThrow();
26+
});
27+
test('stream example', () => {
28+
expect(() => {
29+
require('../examples/javascript/stream-example');
30+
}).not.toThrow();
31+
});
32+
test('string classification', () => {
33+
expect(() => {
34+
require('../examples/javascript/string-classification');
35+
}).not.toThrow();
36+
});
37+
test('which letter simple', () => {
38+
expect(() => {
39+
require('../examples/javascript/which-letter-simple');
40+
}).not.toThrow();
41+
});
42+
});
File renamed without changes.

__tests__/likely.js

+117
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
const NeuralNetwork = require('../src/neural-network');
2+
const likely = require('../src/likely');
3+
4+
describe('likely', () => {
5+
const a = character(
6+
'.#####.' +
7+
'#.....#' +
8+
'#.....#' +
9+
'#######' +
10+
'#.....#' +
11+
'#.....#' +
12+
'#.....#'
13+
);
14+
const b = character(
15+
'######.' +
16+
'#.....#' +
17+
'#.....#' +
18+
'######.' +
19+
'#.....#' +
20+
'#.....#' +
21+
'######.'
22+
);
23+
const c = character(
24+
'#######' +
25+
'#......' +
26+
'#......' +
27+
'#......' +
28+
'#......' +
29+
'#......' +
30+
'#######'
31+
);
32+
33+
/**
34+
* Learn the letters A through C.
35+
*/
36+
37+
const net = new NeuralNetwork();
38+
net.train([
39+
{ input: a, output: { a: 1 } },
40+
{ input: b, output: { b: 1 } },
41+
{ input: c, output: { c: 1 } }
42+
]);
43+
44+
it('should be able to find a "a"', () => {
45+
/**
46+
* Predict the letter A, even with a pixel off.
47+
*/
48+
let result = likely(character(
49+
'.#####.' +
50+
'#.....#' +
51+
'#.....#' +
52+
'###.###' +
53+
'#.....#' +
54+
'#.....#' +
55+
'#.....#'
56+
), net);
57+
58+
expect(result).toBe('a');
59+
});
60+
61+
it('should be able to find a "b"', () => {
62+
/**
63+
* Predict the letter B, even with a pixel off.
64+
*/
65+
let result = likely(character(
66+
'######.' +
67+
'#.....#' +
68+
'#.....#' +
69+
'######.' +
70+
'#..#..#' +
71+
'#.....#' +
72+
'###.##.'
73+
), net);
74+
75+
expect(result).toBe('b');
76+
});
77+
78+
it('should be able to find a "c"', () => {
79+
/**
80+
* Predict the letter C, even with a pixel off.
81+
*/
82+
83+
let result = likely(character(
84+
'#######' +
85+
'#......' +
86+
'#......' +
87+
'#......' +
88+
'#......' +
89+
'##.....' +
90+
'#######'
91+
), net);
92+
93+
expect(result).toBe('c');
94+
});
95+
});
96+
97+
/**
98+
* Turn the # into 1s and . into 0s. for whole string
99+
* @param string
100+
* @returns {Array}
101+
*/
102+
function character(string) {
103+
return string
104+
.trim()
105+
.split('')
106+
.map(integer);
107+
}
108+
109+
/**
110+
* Return 0 or 1 for '#'
111+
* @param character
112+
* @returns {number}
113+
*/
114+
function integer(character) {
115+
if ('#' === character) return 1;
116+
return 0;
117+
}

__tests__/likely.test.js

Whitespace-only changes.

0 commit comments

Comments
 (0)