Skip to content

Commit 4cc8db7

Browse files
committed
Starting it off
The readme is the basis of this. I have been needing to push this up for a while but wanted to get it working. This works as far as I have gotten on it. It... 1) creates a network in main.js then get's the json from it. 2) generates a set of training data. 3) passes the network json and a partition of the training data to each process 4) recreates then trains the network on each process 5) creates a json of the each network and passes them back to the main process 6) The next step to do... The next steps 1) combine the networks into 1 network (I think simple addition will work for this when parsing the network but haven't tested it yet 2) test the new combination network to see if it is actually acurate 3) stream line it into something usable by someone else.
0 parents  commit 4cc8db7

File tree

9 files changed

+572
-0
lines changed

9 files changed

+572
-0
lines changed

.gitignore

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# Mac.
2+
.DS_STORE
3+
4+
# Node.
5+
node_modules
6+
npm-debug.log
7+
8+
# Yarn
9+
yarn.lock
10+
yarn-error.log
11+
12+
# local testing files
13+
networks
14+
sendSizes

README.md

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
# brain-cluster
2+
Getting brain to train in parallel (on many machines or on many threads).
3+
4+
# Getting Started
5+
This uses [klyng](https://www.npmjs.com/package/klyng) which is also built on [fibers](https://www.npmjs.com/package/fibers).
6+
7+
Get `klyng` installed globally:
8+
```
9+
yarn global add klyng
10+
```
11+
12+
`klyng` by default runs a _beacon_ on port `:2222`. You can change the port and add a password in the config file. If you ran the above command it is found here by default `~/.config/yarn/global/node_modules/klyng/config.json`.
13+
14+
There is also the `machine.json` which describes how communication on the cluster will perform.
15+
16+
17+
# Scripts
18+
19+
#### down
20+
```
21+
yarn down
22+
```
23+
Shuts down the klyng beacon. You shouldn't have to do this unless you are running a bunch of klyng commands at once and it things start breaking and it isn't handled nicely
24+
25+
#### up
26+
```
27+
yarn up
28+
```
29+
Starts the klyng beacon. If running a klyng script and the beacon isn't up you shouldn't need to do that. This might need to be done manually when running on secondary machines.
30+
31+
#### start
32+
```
33+
yarn start
34+
```
35+
Start `cluster.js` Currently configured in the `packages.json` to start 6 local processses. Currently hard coded to train a network in solving the [likely](https://github.com/BrainJS/brain.js/blob/develop/test/base/likely.js) problem. As this gets flushed out there will be better documentation and integration with `brain.js` directly.
36+
37+
#### cluster
38+
```
39+
yarn cluster
40+
```
41+
will start `cluster.js` but will also apply the `machines.json` This is just in reference to how to run on a cluster, but hasn't been flushed out yet (also the machines.json doesn't mean anything at this point)
42+
43+
#### example
44+
```
45+
yarn example
46+
```
47+
starts a trivial example, used for concept testing on klyng. This will get removed eventually but is still useful in testing out specific functionality
48+
49+
#### test-sizes
50+
```
51+
yarn test-sizes
52+
```
53+
Like example this is used for testing specifically how large of a data set we can send back and forth from klyng instances. The documentation on klyng isn't stellar so this was a semi-stress test. This will also be removed as `brain-cluster` matures
54+
55+

cluster.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
var klyng = require('klyng');
2+
3+
function main() {
4+
var runner;
5+
if(klyng.rank() === 0) {
6+
runner = require('./src/main');
7+
runner.run(klyng);
8+
} else {
9+
runner = require('./src/helper');
10+
runner.run(klyng)
11+
.then(() => {
12+
console.log('Ending work for rank => ' + klyng.rank());
13+
klyng.end();
14+
});
15+
}
16+
}
17+
18+
klyng.init(main);

example.js

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
var klyng = require('klyng');
2+
3+
function main() {
4+
if(klyng.rank() === 0) {
5+
var list = new Array(3000);
6+
for (var i = 0; i < list.length; ++i) {
7+
list[i] = 5;
8+
}
9+
10+
var portionSize = Math.floor(list.length / klyng.size());
11+
var promises = [];
12+
for(var p = 1; p < klyng.size(); ++p) {
13+
promises.push(sendRank(klyng, p, list.slice((p - 1) * portionSize, p * portionSize)));
14+
}
15+
var sum = processData(list.slice((klyng.size() - 1) * portionSize, list.length));
16+
for(var p = 1; p < klyng.size(); ++p) {
17+
promises.push(recieveResults(klyng, p));
18+
}
19+
Promise.all(promises)
20+
.then(values => {
21+
values = values.filter(v => {return v});
22+
log('values => ' + values.join(', '));
23+
log(`The sum is ${values.reduce((prev, next) => prev + next)}`);
24+
klyng.end();
25+
})
26+
.catch(err => {
27+
log(`${klyng.rank()}, error => ${err.stack}`);
28+
klyng.end();
29+
});
30+
} else {
31+
receiveTask(klyng)
32+
.then(doBadWork)
33+
.then(sendTaskResults)
34+
.then(klyng.end)
35+
.catch(err => {
36+
log(`${klyng.rank()}, error => ${err.stack}`);
37+
klyng.end();
38+
});
39+
}
40+
}
41+
42+
function processData (portion) {
43+
return portion.reduce((prev, next) => prev + next);
44+
}
45+
46+
function receiveTask (klyng) {
47+
return new Promise ((resolve, reject) => {
48+
log(`${klyng.rank()} waiting for task`)
49+
var data = klyng.recv({from: 0});
50+
log(`${klyng.rank()} recieved task`);
51+
resolve({ klyng, data });
52+
});
53+
}
54+
55+
function doBadWork (input) {
56+
return new Promise ((resolve, reject) => {
57+
let iters = Math.floor(Math.random() * 1000000000)
58+
log(`${input.klyng.rank()} starts iterating => ${iters}`);
59+
for (var i = 0; i < iters; ++i) { var m = Math.random(); }
60+
log(`${input.klyng.rank()} ends iterating and is sending task`);
61+
resolve(input);
62+
})
63+
}
64+
65+
function sendTaskResults (input) {
66+
return new Promise ((resolve, reject) => {
67+
log(`${input.klyng.rank()} starts sending task to 0`);
68+
input.klyng.send({to:0 , data: processData(input.data) });
69+
log(`${input.klyng.rank()} sent task`);
70+
resolve();
71+
});
72+
}
73+
74+
function recieveResults (klyng, rank) {
75+
return new Promise ((resolve, reject) => {
76+
log(`\tlistening for ${rank}`);
77+
var partialSum = klyng.recv();
78+
log(`\t${rank} return recieved`);
79+
resolve(partialSum);
80+
})
81+
}
82+
83+
function sendRank (klyng, rank, portion) {
84+
return new Promise ((resolve, reject) => {
85+
log(`\tprepare to send portion to => ${rank}`);
86+
klyng.send({to: rank, data: portion});
87+
log(`\tsent portion to => ${rank}`);
88+
resolve();
89+
})
90+
}
91+
92+
function log (str) {
93+
var now = new Date();
94+
console.log(`[${now.getMinutes()}:${now.getSeconds()}:${now.getMilliseconds()}] ${str}`);
95+
}
96+
97+
klyng.init(main);

machine.json

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"local": {
3+
"max_procs": 2
4+
},
5+
6+
"192.168.1.104": {
7+
"max_procs": 4,
8+
"passwd": "dummy",
9+
"port": 9865
10+
},
11+
12+
"192.168.1.109": {
13+
"max_procs": 4
14+
}
15+
}

package.json

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
{
2+
"name": "brain-cluster",
3+
"version": "0.0.0",
4+
"description": "Brain.js in a distributed system. Train in async on your local machine, in async accross many machines, or on something like a Beowulf cluster.",
5+
"main": "cluster.js",
6+
"scripts": {
7+
"down": "klyng --beacon-down",
8+
"up": "klyng --beacon-up",
9+
"start": "klyng -n 6 cluster.js",
10+
"example": "klyng -n 6 example.js",
11+
"test-sizes": "klyng -n 2 test-size.js && yarn down && klyng -n 4 test-size.js && yarn down && klyng -n 8 test-size.js && yarn down && klyng -n 16 test-size.js && yarn down && klyng -n 32 test-size.js && yarn down && klyng -n 64 test-size.js && yarn down && klyng -n 128 test-size.js",
12+
"cluster": "klyng -n 6 cluster.js -m machines.json"
13+
},
14+
"repository": {
15+
"type": "git",
16+
"url": "git+ssh://[email protected]/brainjs/brain-cluster.git"
17+
},
18+
"keywords": [
19+
"ai",
20+
"artificial-intelligence",
21+
"distributed-computing",
22+
"distributed-neural-network",
23+
"cluster",
24+
"neural-networks",
25+
"machine-learning"
26+
],
27+
"author": "The Brain.js Team",
28+
"license": "MIT",
29+
"bugs": {
30+
"url": "https://github.com/BrainJS/brain-cluster/issues"
31+
},
32+
"homepage": "https://github.com/BrainJS/brain-cluster#readme",
33+
"devDependencies": {},
34+
"dependencies": {
35+
"brain.js": "^1.1.2",
36+
"klyng": "^1.0.5"
37+
}
38+
}

src/helper.js

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
var brain = require('brain.js');
2+
var fs = require('fs');
3+
4+
module.exports = {
5+
run: function (klyng) {
6+
log(`${klyng.rank()}, is running`);
7+
return listeningForTask(klyng)
8+
.then(runTraining)
9+
.then(writeResutlsToFile)
10+
.then(sendResults)
11+
.then(() => {
12+
return new Promise ((resolve, reject) => {
13+
setTimeout(resolve, 3000) // delaying 3 seconds for 0 to recieve before close
14+
});
15+
})
16+
.catch((err) => {
17+
log(`${klyng.rank()} ERROR -> ${err}`);
18+
});
19+
}
20+
};
21+
22+
function listeningForTask (klyng) {
23+
return new Promise((resolve, reject) => {
24+
let data = klyng.recv({ from: 0 });
25+
data = JSON.parse(data);
26+
log(`${klyng.rank()}, recieved ${data.trainingData.length} object to train`);
27+
resolve({
28+
klyng: klyng,
29+
data: data
30+
});
31+
});
32+
}
33+
34+
function runTraining (opts) {
35+
return new Promise ((resolve, reject) => {
36+
opts.net = new brain.NeuralNetwork().fromJSON(opts.data.json);
37+
opts.data.trainRes = opts.net.train(opts.data.trainingData, {
38+
callback: (res) => { log(`${opts.klyng.rank()}, training => [${res.iterations}, ${res.error}]`); }
39+
});
40+
log(`${opts.klyng.rank()}, training => finished: [${opts.data.trainRes.iterations}, ${opts.data.trainRes.error}]`);
41+
resolve(opts);
42+
});
43+
}
44+
45+
function writeResutlsToFile (opts) {
46+
return new Promise ((resolve, reject) => {
47+
if (!fs.existsSync('./networks')){
48+
fs.mkdirSync('./networks');
49+
}
50+
const err = fs.writeFileSync(`./networks/net-${opts.klyng.rank()}.json`, JSON.stringify(opts.data));
51+
if (err) reject (err);
52+
log(`${opts.klyng.rank()} wrote to file`);
53+
resolve(opts);
54+
});
55+
}
56+
57+
function sendResults (opts) {
58+
return new Promise ((resolve, reject) => {
59+
log(`${opts.klyng.rank()} started sending back results`);
60+
delete opts.data.trainingData;
61+
opts.data.rank = opts.klyng.rank();
62+
opts.klyng.send({
63+
to: 0,
64+
data: opts.data
65+
})
66+
log(`${opts.klyng.rank()} finished sending back results`);
67+
resolve(opts);
68+
});
69+
}
70+
71+
function log (str) {
72+
var now = new Date();
73+
console.log(`[${now.getMinutes()}:${now.getSeconds()}:${now.getMilliseconds()}] ${str}`);
74+
}

0 commit comments

Comments
 (0)