Skip to content

This adds thaw.js#118

Merged
robertleeplummerjr merged 10 commits into
developfrom
add-thaw
Jan 27, 2018
Merged

This adds thaw.js#118
robertleeplummerjr merged 10 commits into
developfrom
add-thaw

Conversation

@freddyC

@freddyC freddyC commented Jan 11, 2018

Copy link
Copy Markdown
Contributor

A GIF to give the reviewer some spice of the internet

should solve #82

Makes training async via thaw.js

Removes reinforce option

I felt this cleans up and simplifies the network without changing functionality.

Basically when you train if it doesn't have a set size for the network it initializes and uses your options (as normal) to dictate network size, layers and all

If you have already trained the network or if the network is loaded from JSON then you don't need to set it and it trains as normal.

Potential PROBLEMS

  1. The train options training callback called with training stats test fails
    a) I think this is a problem in thaw.js but haven't been able to track it down yet.

  2. Should probably have more tests for the new train options

  3. With training being async now this may create a new problem with trying to train when currently training. I haven't tested anything on that I was just thinking and about ramifications of this change. We might want to add some sort of (is training) variable and if someone tries to train while it's currently training either push this training to the end of current training or escape out with an error or something.

Comment thread .gitignore Outdated
yarn.lock No newline at end of file
yarn.lock

/dist** No newline at end of file

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah I shouldn't have done this

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you remove the dist

@robertleeplummerjr

Copy link
Copy Markdown
Contributor

Can we alter this to have have an async method, rather than changing the established method? If we do this than the sync method stays as it is, we get a new feature, don't break backward compatibility.

@robertleeplummerjr

Copy link
Copy Markdown
Contributor

Interesting idea on reinforce, so if the net is defined, reinforce is implied?

@freddyC

freddyC commented Jan 13, 2018

Copy link
Copy Markdown
Contributor Author

Yeah, that would make the default behavior allow us to pick up and improve training at any point.

I think this will lead naturally to reinforcement especially if the user can set a required confidence level, and then things that we are less confident on the user can have it set up to trigger him and he lets the net know the correct answer and that can be used to reinforce.

I will move the training to a train async method, Are you ok with keeping the reinforcement as I set it up or do you want me to revert that change?

@robertleeplummerjr

Copy link
Copy Markdown
Contributor

We should ask the community on the thread where reinforce was added, but I think they'll love it.

@freddyC

freddyC commented Jan 14, 2018

Copy link
Copy Markdown
Contributor Author

So I made the changes but am working on the tests, and I think I broke stuff so I will try and get it updated this weekend, just wanted to keep momentum on this.

### Makes training async via thaw.js

### Removes `reinforce` option
I felt this cleans up and simplifies the network without changing functionality.

Basically when you train if it doesn't have a set size for the network it initializes and uses your options (as normal) to dictate network size, layers and all

If you have already trained the network or if the network is loaded from JSON then you don't need to set it and it trains as normal.

### PROBLEMS
1) The train options `training callback called with training stats` test fails
  - I think this is a problem in thaw.js but haven't been able to track it down yet.

2) Should probably have more tests for the new train options

3) With training being async now this may create a new problem with trying to train when currently training.  I haven't tested anything on that I was just thinking and about ramifications of this change.  We might want to add some sort of (is training) variable and if someone tries to train while it's currently training either push this training to the end of current training or escape out with an error or something.

@robertleeplummerjr robertleeplummerjr left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good job, had a few items to review. Ty ty!

Comment thread .gitignore Outdated
yarn.lock No newline at end of file
yarn.lock

/dist** No newline at end of file

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you remove the dist

Comment thread src/neural-network.js Outdated
* @param cb
* @returns {{error: number, iterations: number}}
*/
trainAsync (data, _options = {}, cb = function() {}) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we just use a Promise here?

Comment thread src/neural-network.js Outdated
};

if (this.sizes === null) {
this.initialize (data);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The space after a method I've not seen in javascript libraries, can we aim for justThis()

Comment thread src/neural-network.js Outdated
* @returns {{error: number, iterations: number}}
*/
trainAsync (data, _options = {}, cb = function() {}) {
if (typeof _options === "function") {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We've been in the practice of using single quotes, would you be ok migrating to such?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

absolutely

Comment thread test/base/likely.js Outdated
import likely from '../../dist/likely';

describe('likely', () => {
describe('likely', function () {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lets keep the newer () => {} syntax where possible.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One thing I found out is that mocha doesn't play well with the new () => { syntax. I like it better but I was getting some really weird crashes that I think had to do with that. That being said I probably went overboard in changing it to function () {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just use --compilers js:babel-core/register with mocha

@perkyguy perkyguy left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the use case for the training time?

Comment thread src/neural-network.js Outdated
let errorThresh = options.errorThresh;
let log = options.log === true ? console.log : options.log;
let logPeriod = options.logPeriod;
let learningRate = _options.learningRate || this.learningRate || options.learningRate;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why the triple places this could exist?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The only thing I can think of is that it gives preference to as so:

  1. the passed in learning rate
  2. the class's set learning rate
  3. the default learning rate.

It is a little odd to me but I figured it was outside the scope of this pr

Comment thread src/neural-network.js Outdated
sum += this.trainPattern (data[i].input, data[i].output, learningRate);
}

res.error = sum / data.length;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not just res.error = data.reduce(...)/data.length?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was talking to @robertleeplummerjr about it yesterday and we talked about the cost of closures. They aren't crazy expensive but there is a cost to them and I don't usually take them into consideration. I am open to either way but where it was simple to make the for loop and iterate I did that.

So I am open to either way :)

Comment thread src/neural-network.js Outdated

if (callback && (res.iterations % callbackPeriod === 0)) {
// JSON.parse/stringify to clone the object so the callback doesn't have side effects to training
callback(JSON.parse(JSON.stringify(res)));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Object.assign would be a little clearer for this?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 I didn't think of that but I will get it changed

Comment thread test/base/bitwise.js Outdated
data.forEach(d => {
var actual = net.run(d.input)
var expected = d.output;
assert.ok(actual < (expected + wiggle) && actual < (expected + wiggle), `failed to train "${op}" - expected: ${expected}, actual: ${actual}`);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy-pasta error? You have the same thing tested in this &&

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not just do a .toFixed(1)?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good idea on the .toFixed (1) and yeah that was meant to clamp that value.

Comment thread test/base/bitwise.js Outdated
data.forEach(d => {
var actual = net.run(d.input)
var expected = d.output;
assert.ok(actual < (expected + wiggle) && actual < (expected + wiggle), `failed to train "${op}" - expected: ${expected}, actual: ${actual}`);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same thing tested?

Comment thread src/neural-network.js Outdated

if (callback && (res.iterations % callbackPeriod === 0)) {
// JSON.parse/stringify to clone the object so the callback doesn't have side effects to training
callback (JSON.parse (JSON.stringify (res)));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Object.assign?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

or {...res}, which is essentially the same thing.

Comment thread src/neural-network.js
let outputSize = data[0].output.length;
let hiddenSizes = this.hiddenSizes;
if (!hiddenSizes) {
sizes.push(Math.max(3, Math.floor(inputSize / 2)));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If someone doesn't want hidden layers, the library will force it?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yep, that's how it is currently setup.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why? Can @robertleeplummerjr she's some light on this?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, I read this as "yeah I did it this way, what of it?" Not as you probably intended it, "maintaining current functionality"

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

haha no worries, it's finding the balance between not changing too much but still changing things in a good way :)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thats how it was when we got here, however one could do: { hiddenSizes: [] }

Comment thread src/neural-network.js Outdated
if (callback && (i % callbackPeriod === 0)) {
callback({ error: error, iterations: i });

if (callback && (res.iterations % callbackPeriod === 0)) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The callback is for getting periodic training results? Not the finished trained model?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah that's how it is currently setup.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logging for the most part. Most people like to see training advance. I don't see this going away any time soon, as it was part of the model when the community took over.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't that what the log option does? It's cool though, separate conversation for the community, this maintains current functionality

Comment thread src/neural-network.js Outdated
log(`iterations: ${res.iterations} training error: ${res.error}`);
}

if (callback && (res.iterations % callbackPeriod === 0)) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Like above, the callback is for partially trained nets?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that is correct as well

Comment thread src/neural-network.js Outdated
const items = new Array(iterations);
const thaw = new Thaw (items, {
delay: true,
each: () => {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the same as the standard train behavior, just delivered per iteration. This should be pulled out into its own function/method to keep it DRY

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree, that'd be more ideal.

@robertleeplummerjr

Copy link
Copy Markdown
Contributor

all on all I see some really cool things happening here.

Converts the async method to a promise, not a cb

Updates tests

Helps code be more dry
Adds back the () => in tests

Ran `npm run make` to get things
@freddyC

freddyC commented Jan 21, 2018

Copy link
Copy Markdown
Contributor Author

@robertleeplummerjr @perkyguy this I think is ready. The only thing I am not real happy with is the log tests. They pass but I don't know how to get them to log out correct.

image

this is what they look like right now. Mocha is logging something out and I don't know exactly what it's doing. Ideally I think we should be allowing the options to contain a function for log and call their function instead of just take a true or false and then using console.log. That would allow someone to plug in their custom logger. That I figured was outside the scope of this pr lol.

Simplified log test, and added in the ability to pass in custom log method.

I thought this was outside of the scope of my pr but just kinda did it because the test output bugged me lol.
@freddyC

freddyC commented Jan 22, 2018

Copy link
Copy Markdown
Contributor Author

I actually just added in the change for logging, it felt right, hopefully it is ok (a bit of scope creep but worse things have happened, I guarantee it).

Comment thread src/neural-network.js Outdated
* @param {Number[]} sizes
*/
initialize(sizes) {
initialize (sizes) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unneeded space

Comment thread src/neural-network.js Outdated
* @returns {{error: number, iterations: number}}
*/
train(data, _options = {}) {
train (data, _options = {}) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unneeded space

Comment thread src/neural-network.js Outdated
* @param data
* @returns sizes
*/
_getSizesFromData (data) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

uneeded space

Comment thread src/neural-network.js Outdated
* if false passed in nothing is logged
* @returns error
*/
_setLogMethod (log) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unneeded space

Comment thread src/neural-network.js Outdated
* @param learning Rate
* @returns error
*/
_calculateTrainingError (data, learningRate) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unneeded space

Comment thread src/neural-network.js Outdated
_calculateTrainingError (data, learningRate) {
let sum = 0;
for (let i = 0; i < data.length; ++i) {
sum += this.trainPattern (data[i].input, data[i].output, learningRate);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unneeded space

Comment thread src/neural-network.js Outdated
* @param status { iterations: number, error: number}
* @param options
*/
_checkTrainingTick (data, status, options) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unneeded space

Comment thread src/neural-network.js Outdated
*/
train(data, _options = {}) {
const options = Object.assign({}, this.constructor.trainDefaults, _options);
train (data, _options = {}) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unneeded space

Comment thread src/neural-network.js Outdated
train(data, _options = {}) {
const options = Object.assign({}, this.constructor.trainDefaults, _options);
train (data, _options = {}) {
let options = Object.assign({}, this.constructor.trainDefaults, _options);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

options is never reassigned, use a const, not a let.

Comment thread src/neural-network.js Outdated
callback: null,
callbackPeriod: 10,
reinforce: false
trainTimeMs: -Infinity

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can the trainTimeMs be put into a seperate pr? Want to keep the pr's as lean as possible.

Comment thread src/neural-network.js Outdated
* @param cb
* @returns {{error: number, iterations: number}}
*/
trainAsync (data, _options = {}) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unneeded space, this line and next

Comment thread src/neural-network.js Outdated
};

if (this.sizes === null) {
let sizes = this._getSizesFromData (data);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unneeded space, this line and next.

Comment thread src/neural-network.js Outdated
}
}
const items = new Array(options.iterations);
const thaw = new Thaw (items, {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unneeded space

Comment thread src/neural-network.js Outdated
const thaw = new Thaw (items, {
delay: true,
each: () => {
this._checkTrainingTick (data, status, options);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unneeded space

Comment thread src/neural-network.js Outdated
* @param learningRate
*/
trainPattern(input, target, learningRate) {
trainPattern (input, target, learningRate) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unneeded space

Comment thread src/neural-network.js
*/
fromJSON(json) {
this.initialize(json.sizes);
fromJSON (json) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unneeded space

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

still one space 😄

Comment thread test/base/bitwise.js Outdated

let wiggle = 0.1;

function isAround (actual, expected) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unneeded space

Comment thread test/base/bitwise.js Outdated
}

describe('bitwise functions', () => {
function testBitwiseAsync (data, op, done) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unneeded space

Comment thread test/base/bitwise.js Outdated
function testBitwiseAsync (data, op, done) {
let net = new brain.NeuralNetwork();
net
.trainAsync (data, { errorThresh: 0.003 })

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there are several more of these, can you just handle all of them?

When my friend wanted me to give him a Charmander tattoo and he got this https://timedotcom.files.wordpress.com/2015/04/screen-shot-2015-04-17-at-11-12-39-am.png
`trainTimeMs` is gone and will be in a new pr

`log` doesn't accept functions but will be in a new pr

The tests pass (ugly but they pass lol)

https://memegenerator.net/img/instances/75216624.jpg

good thing no one actually looks at commit messages amirite?
Comment thread test/base/hash.js

describe('hash input and output', () => {
it('runs correctly with array input and output', () => {
it ('runs correctly with array input and output', () => {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(space)

Comment thread test/base/likely.js
'#.....#' +
'###.##.'
), net);
let result = likely(character(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

spacing is odd here

@robertleeplummerjr robertleeplummerjr self-requested a review January 26, 2018 13:44

@robertleeplummerjr robertleeplummerjr left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks great. I had a couple suggestions, but they don't change functionality, it was just spacing. Ty for your hard work!

@freddyC freddyC changed the base branch from master to develop January 26, 2018 21:21
# Conflicts:
#	test/base/bitwise.js
#	test/base/json.js
@robertleeplummerjr robertleeplummerjr merged commit f8aa38f into develop Jan 27, 2018
@robertleeplummerjr

Copy link
Copy Markdown
Contributor

ty so much for your hard work on this! Sorry about the conflicts, I went ahead and did a merge, tested, and merged.

@robertleeplummerjr robertleeplummerjr deleted the add-thaw branch January 27, 2018 13:13
@freddyC

freddyC commented Jan 27, 2018

Copy link
Copy Markdown
Contributor Author

giphy

@mubaidr mubaidr mentioned this pull request Jul 24, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants