This adds thaw.js#118
Conversation
| yarn.lock No newline at end of file | ||
| yarn.lock | ||
|
|
||
| /dist** No newline at end of file |
There was a problem hiding this comment.
yeah I shouldn't have done this
There was a problem hiding this comment.
Can you remove the dist
|
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. |
|
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? |
|
We should ask the community on the thread where |
|
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
left a comment
There was a problem hiding this comment.
Good job, had a few items to review. Ty ty!
| yarn.lock No newline at end of file | ||
| yarn.lock | ||
|
|
||
| /dist** No newline at end of file |
There was a problem hiding this comment.
Can you remove the dist
| * @param cb | ||
| * @returns {{error: number, iterations: number}} | ||
| */ | ||
| trainAsync (data, _options = {}, cb = function() {}) { |
There was a problem hiding this comment.
Can we just use a Promise here?
| }; | ||
|
|
||
| if (this.sizes === null) { | ||
| this.initialize (data); |
There was a problem hiding this comment.
The space after a method I've not seen in javascript libraries, can we aim for justThis()
| * @returns {{error: number, iterations: number}} | ||
| */ | ||
| trainAsync (data, _options = {}, cb = function() {}) { | ||
| if (typeof _options === "function") { |
There was a problem hiding this comment.
We've been in the practice of using single quotes, would you be ok migrating to such?
| import likely from '../../dist/likely'; | ||
|
|
||
| describe('likely', () => { | ||
| describe('likely', function () { |
There was a problem hiding this comment.
Lets keep the newer () => {} syntax where possible.
There was a problem hiding this comment.
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 () {
There was a problem hiding this comment.
Just use --compilers js:babel-core/register with mocha
perkyguy
left a comment
There was a problem hiding this comment.
What's the use case for the training time?
| 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; |
There was a problem hiding this comment.
Why the triple places this could exist?
There was a problem hiding this comment.
The only thing I can think of is that it gives preference to as so:
- the passed in learning rate
- the class's set learning rate
- the default learning rate.
It is a little odd to me but I figured it was outside the scope of this pr
| sum += this.trainPattern (data[i].input, data[i].output, learningRate); | ||
| } | ||
|
|
||
| res.error = sum / data.length; |
There was a problem hiding this comment.
Why not just res.error = data.reduce(...)/data.length?
There was a problem hiding this comment.
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 :)
|
|
||
| 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))); |
There was a problem hiding this comment.
Object.assign would be a little clearer for this?
There was a problem hiding this comment.
👍 I didn't think of that but I will get it changed
| 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}`); |
There was a problem hiding this comment.
Copy-pasta error? You have the same thing tested in this &&
There was a problem hiding this comment.
Why not just do a .toFixed(1)?
There was a problem hiding this comment.
Good idea on the .toFixed (1) and yeah that was meant to clamp that value.
| 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}`); |
|
|
||
| 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))); |
There was a problem hiding this comment.
or {...res}, which is essentially the same thing.
| let outputSize = data[0].output.length; | ||
| let hiddenSizes = this.hiddenSizes; | ||
| if (!hiddenSizes) { | ||
| sizes.push(Math.max(3, Math.floor(inputSize / 2))); |
There was a problem hiding this comment.
If someone doesn't want hidden layers, the library will force it?
There was a problem hiding this comment.
yep, that's how it is currently setup.
There was a problem hiding this comment.
Why? Can @robertleeplummerjr she's some light on this?
There was a problem hiding this comment.
Sorry, I read this as "yeah I did it this way, what of it?" Not as you probably intended it, "maintaining current functionality"
There was a problem hiding this comment.
haha no worries, it's finding the balance between not changing too much but still changing things in a good way :)
There was a problem hiding this comment.
Thats how it was when we got here, however one could do: { hiddenSizes: [] }
| if (callback && (i % callbackPeriod === 0)) { | ||
| callback({ error: error, iterations: i }); | ||
|
|
||
| if (callback && (res.iterations % callbackPeriod === 0)) { |
There was a problem hiding this comment.
The callback is for getting periodic training results? Not the finished trained model?
There was a problem hiding this comment.
yeah that's how it is currently setup.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Isn't that what the log option does? It's cool though, separate conversation for the community, this maintains current functionality
| log(`iterations: ${res.iterations} training error: ${res.error}`); | ||
| } | ||
|
|
||
| if (callback && (res.iterations % callbackPeriod === 0)) { |
There was a problem hiding this comment.
Like above, the callback is for partially trained nets?
There was a problem hiding this comment.
that is correct as well
| const items = new Array(iterations); | ||
| const thaw = new Thaw (items, { | ||
| delay: true, | ||
| each: () => { |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
I agree, that'd be more ideal.
|
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
|
|
| * @param {Number[]} sizes | ||
| */ | ||
| initialize(sizes) { | ||
| initialize (sizes) { |
| * @returns {{error: number, iterations: number}} | ||
| */ | ||
| train(data, _options = {}) { | ||
| train (data, _options = {}) { |
| * @param data | ||
| * @returns sizes | ||
| */ | ||
| _getSizesFromData (data) { |
| * if false passed in nothing is logged | ||
| * @returns error | ||
| */ | ||
| _setLogMethod (log) { |
| * @param learning Rate | ||
| * @returns error | ||
| */ | ||
| _calculateTrainingError (data, learningRate) { |
| _calculateTrainingError (data, learningRate) { | ||
| let sum = 0; | ||
| for (let i = 0; i < data.length; ++i) { | ||
| sum += this.trainPattern (data[i].input, data[i].output, learningRate); |
| * @param status { iterations: number, error: number} | ||
| * @param options | ||
| */ | ||
| _checkTrainingTick (data, status, options) { |
| */ | ||
| train(data, _options = {}) { | ||
| const options = Object.assign({}, this.constructor.trainDefaults, _options); | ||
| train (data, _options = {}) { |
| train(data, _options = {}) { | ||
| const options = Object.assign({}, this.constructor.trainDefaults, _options); | ||
| train (data, _options = {}) { | ||
| let options = Object.assign({}, this.constructor.trainDefaults, _options); |
There was a problem hiding this comment.
options is never reassigned, use a const, not a let.
| callback: null, | ||
| callbackPeriod: 10, | ||
| reinforce: false | ||
| trainTimeMs: -Infinity |
There was a problem hiding this comment.
Can the trainTimeMs be put into a seperate pr? Want to keep the pr's as lean as possible.
| * @param cb | ||
| * @returns {{error: number, iterations: number}} | ||
| */ | ||
| trainAsync (data, _options = {}) { |
There was a problem hiding this comment.
unneeded space, this line and next
| }; | ||
|
|
||
| if (this.sizes === null) { | ||
| let sizes = this._getSizesFromData (data); |
There was a problem hiding this comment.
unneeded space, this line and next.
| } | ||
| } | ||
| const items = new Array(options.iterations); | ||
| const thaw = new Thaw (items, { |
| const thaw = new Thaw (items, { | ||
| delay: true, | ||
| each: () => { | ||
| this._checkTrainingTick (data, status, options); |
| * @param learningRate | ||
| */ | ||
| trainPattern(input, target, learningRate) { | ||
| trainPattern (input, target, learningRate) { |
| */ | ||
| fromJSON(json) { | ||
| this.initialize(json.sizes); | ||
| fromJSON (json) { |
There was a problem hiding this comment.
still one space 😄
|
|
||
| let wiggle = 0.1; | ||
|
|
||
| function isAround (actual, expected) { |
| } | ||
|
|
||
| describe('bitwise functions', () => { | ||
| function testBitwiseAsync (data, op, done) { |
| function testBitwiseAsync (data, op, done) { | ||
| let net = new brain.NeuralNetwork(); | ||
| net | ||
| .trainAsync (data, { errorThresh: 0.003 }) |
There was a problem hiding this comment.
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?
|
|
||
| describe('hash input and output', () => { | ||
| it('runs correctly with array input and output', () => { | ||
| it ('runs correctly with array input and output', () => { |
| '#.....#' + | ||
| '###.##.' | ||
| ), net); | ||
| let result = likely(character( |
There was a problem hiding this comment.
spacing is odd here
robertleeplummerjr
left a comment
There was a problem hiding this comment.
This looks great. I had a couple suggestions, but they don't change functionality, it was just spacing. Ty for your hard work!
# Conflicts: # test/base/bitwise.js # test/base/json.js
|
ty so much for your hard work on this! Sorry about the conflicts, I went ahead and did a merge, tested, and merged. |




should solve #82
Makes training async via thaw.js
Removes
reinforceoptionI 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
The train options
training callback called with training statstest failsa) I think this is a problem in thaw.js but haven't been able to track it down yet.
Should probably have more tests for the new train options
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.